From eac6dc62eeb4bb636f4306d5335d1b2a4feb37ef Mon Sep 17 00:00:00 2001 From: Rose Date: Sat, 17 Dec 2022 11:24:31 -0800 Subject: [PATCH 0001/2222] Update windows build scripts --- build/.gitignore | 2 ++ build/win64/build-debug.bat | 4 ++-- build/win64/build-release.bat | 4 ++-- build/win64/generate-MSVC-all.bat | 6 +++--- build/win64/generate-MSVC-gui.bat | 6 +++--- build/win64/generate-MSVC-minimal.bat | 6 +++--- build/win64/generate-MSVC-release.bat | 6 +++--- build/win64/install-debug.bat | 4 ++-- build/win64/install-release.bat | 4 ++-- build/win64/package-debug.bat | 4 ++-- build/win64/package-release.bat | 4 ++-- 11 files changed, 26 insertions(+), 24 deletions(-) diff --git a/build/.gitignore b/build/.gitignore index 47745bc1e..256e18872 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,6 +1,8 @@ VC2010 VC2015 VC2015_32 +VC2022 +VC2022_32 DF_PATH.txt _CPack_Packages *.tar.* diff --git a/build/win64/build-debug.bat b/build/win64/build-debug.bat index 08ef6d3a9..a04cb9984 100644 --- a/build/win64/build-debug.bat +++ b/build/win64/build-debug.bat @@ -1,4 +1,4 @@ -call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 -cd VC2015 +call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +cd VC2022 msbuild /m /p:Platform=x64 /p:Configuration=RelWithDebInfo ALL_BUILD.vcxproj cd .. diff --git a/build/win64/build-release.bat b/build/win64/build-release.bat index dfeb108b3..8068e5074 100644 --- a/build/win64/build-release.bat +++ b/build/win64/build-release.bat @@ -1,5 +1,5 @@ -call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 -cd VC2015 +call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +cd VC2022 msbuild /m /p:Platform=x64 /p:Configuration=Release ALL_BUILD.vcxproj cd .. pause diff --git a/build/win64/generate-MSVC-all.bat b/build/win64/generate-MSVC-all.bat index d3261fa77..ea8db34c0 100644 --- a/build/win64/generate-MSVC-all.bat +++ b/build/win64/generate-MSVC-all.bat @@ -1,6 +1,6 @@ IF EXIST DF_PATH.txt SET /P _DF_PATH= Date: Mon, 19 Dec 2022 22:01:21 -0400 Subject: [PATCH 0002/2222] Adds Dependencies.rst --- docs/dev/building/Dependencies.rst | 365 +++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 docs/dev/building/Dependencies.rst diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst new file mode 100644 index 000000000..584d36b5e --- /dev/null +++ b/docs/dev/building/Dependencies.rst @@ -0,0 +1,365 @@ +.. _dependencies: + +################### +DFHack Dependencies +################### + +.. + DFHack is quite large, so I've attempted to + leave some sort of bread crumbs for each + mentionable aspect. + +Many of DFHack's build dependencies are included in the repository as git submodules, +however there are some system dependencies as well. They are as follows: + +System packages: +* SDL (libsdl 1.2, not sdl2). +* zlib +* OpenGL headers (optional: to build `stonesense`) + +**SDL** is used as an injection point which you can see more about in DFHack's `architecture` documentation & diagrams. +The **zlib** dependency is a compression library which is part of a chain of dependencies ultimately used by `quickfort` and `xlsxioreader`. + +Perl packages: +* XML::LibXML +* XML::LibXSLT + +These perl packages are used in code generation. DFHack has many structures that are reverse engineered, we use xml +files to define and update these structures. Then during the configuration process [running cmake] these xml files are +used to generate C++ source code to define these structures for use in plugins and scripts. + +.. contents:: Contents + :local: + :depth: 2 + +.. _linux-dependency-instructions: + +Linux +----- + +Here are some package install commands for various distributions: + +* On Arch linux:: + + pacman -Sy gcc cmake ninja git dwarffortress zlib perl-xml-libxml perl-xml-libxslt + + * The ``dwarffortress`` package provides the necessary SDL packages. + * For the required Perl modules: ``perl-xml-libxml`` and ``perl-xml-libxslt`` (or through ``cpan``) + +* On Ubuntu:: + + apt-get install gcc cmake ninja-build git zlib1g-dev libsdl1.2-dev libxml-libxml-perl libxml-libxslt-perl + + * Other Debian-based distributions should have similar requirements. + +* On Fedora:: + + yum install gcc-c++ cmake ninja-build git zlib-devel SDL-devel perl-core perl-XML-LibXML perl-XML-LibXSLT ruby + +Building 32-bit Binaries +======================== +If you want to compile 32-bit DFHack on 64-bit distributions, you'll need the +multilib development tools and libraries: + +* ``gcc-multilib`` and ``g++-multilib`` +* If you have installed a non-default version of GCC - for example, GCC 4.8 on a + distribution that defaults to 5.x - you may need to add the version number to + the multilib packages. + + * For example, ``gcc-4.8-multilib`` and ``g++-4.8-multilib`` if installing for GCC 4.8 + on a system that uses a later GCC version. + * This is definitely required on Ubuntu/Debian, check if using a different distribution. + +* ``zlib1g-dev:i386`` (or a similar i386 zlib-dev package) + +Note that installing a 32-bit GCC on 64-bit systems (e.g. ``gcc:i386`` on +Debian) will typically *not* work, as it depends on several other 32-bit +libraries that conflict with system libraries. Alternatively, you might be able +to use ``lxc`` to +:forums:`create a virtual 32-bit environment <139553.msg5435310#msg5435310>`. + +.. _linux-incompatible-libstdcxx: + +Incompatible libstdc++ +====================== +When compiling DFHack yourself, it builds against your system libstdc++. When +Dwarf Fortress runs, it uses a libstdc++ shipped in the ``libs`` folder, which +comes from GCC 4.8 and is incompatible with code compiled with newer GCC +versions. As of DFHack 0.42.05-alpha1, the ``dfhack`` launcher script attempts +to fix this by automatically removing the DF-provided libstdc++ on startup. +In rare cases, this may fail and cause errors such as: + +.. code-block:: text + + ./libs/Dwarf_Fortress: /pathToDF/libs/libstdc++.so.6: version + `GLIBCXX_3.4.18' not found (required by ./hack/libdfhack.so) + +The easiest way to fix this is generally removing the libstdc++ shipped with +DF, which causes DF to use your system libstdc++ instead:: + + cd /path/to/DF/ + rm libs/libstdc++.so.6 + +Note that distributing binaries compiled with newer GCC versions may result in +the opposite compatibility issue: users with *older* GCC versions may encounter +similar errors. This is why DFHack distributes both GCC 4.8 and GCC 7 builds. If +you are planning on distributing binaries to other users, we recommend using an +older GCC (but still at least 4.8) version if possible. + +.. _mac-dependency-instructions: + +macOS +----- + +DFHack can officially be built on macOS only with GCC 4.8 or 7. Anything newer than 7 +will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-notes`), +and your build will likely not be redistributable. + +.. _osx-setup: + +Dependencies and system set-up +============================== + +#. Download and unpack a copy of the latest DF +#. Install Xcode from the Mac App Store + +#. Install the XCode Command Line Tools by running the following command:: + + xcode-select --install + +#. Install dependencies + + It is recommended to use Homebrew instead of MacPorts, as it is generally + cleaner, quicker, and smarter. For example, installing MacPort's GCC will + install more than twice as many dependencies as Homebrew's will, and all in + both 32-bit and 64-bit variants. Homebrew also doesn't require constant use + of ``sudo``. + + Using `Homebrew `_ (recommended):: + + brew tap homebrew/versions + brew install git + brew install cmake + brew install ninja + brew install gcc@7 + + Using `MacPorts `_:: + + sudo port install gcc7 +universal cmake +universal git-core +universal ninja +universal + + Macports will take some time - maybe hours. At some point it may ask + you to install a Java environment; let it do so. + +#. Install Perl dependencies + + * Using system Perl + + * ``sudo cpan`` + + If this is the first time you've run cpan, you will need to go through the setup + process. Just stick with the defaults for everything and you'll be fine. + + If you are running OS X 10.6 (Snow Leopard) or earlier, good luck! + You'll need to open a separate Terminal window and run:: + + sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml + + * ``install XML::LibXML`` + * ``install XML::LibXSLT`` + + * In a separate, local Perl install + + Rather than using system Perl, you might also want to consider + the Perl manager, `Perlbrew `_. + + This manages Perl 5 locally under ``~/perl5/``, providing an easy + way to install Perl and run CPAN against it without ``sudo``. + It can maintain multiple Perl installs and being local has the + benefit of easy migration and insulation from OS issues and upgrades. + + See https://perlbrew.pl/ for more details. + +.. _windows-dependency-instructions: + +Windows +------- + +On Windows, DFHack replaces the SDL library distributed with DF. +For ABI compatibility with recent releases of Dwarf Fortress, DFHack requires the ``v140`` or ``v140_xp`` +toolchain to build for windows. + +What you'll need is as follows: + +* Microsoft Visual C++ 2022, 2019, 2017, or 2015 (optional) +* ``v140`` or ``v140_xp`` toolchain + + * i.e. Microsoft Visual C++ 2015 Build Tools +* Git (optional) +* CMake +* StrawberryPerl (optional, see nested) + + * Perl (required) + * XML:LibXML (required) + * XML:LibXLST (required) +* Python (required for documentation, optional otherwise) + +Releases of Dwarf Fortress since roughly 2016 have been compiled for Windows using +Microsoft's Visual Studio 2015 C++ compiler. In order to guarantee ABI and STL compatibility +with Dwarf Fortress, DFHack has to be compiled with the same compiler. + +Visual Studio 2015 is no longer supported by Microsoft and it can be difficult to obtain +working installers for this product today. As of 2022, the recommended approach +is to use Visual Studio 2022 or Visual Studio 2019, installing additional optional +Visual Studio components which provide the required support for using +Visual Studio 2015's toolchain. All of the required tools are available from Microsoft as part of +Visual Studio's Community Edition at no charge. + +You can also download just the Visual C++ 2015 `build tools`_ if you aren't going to use +Visual Studio to edit code. + +Installing Dependencies +======================= + +It is recommended to use the package manager ``choco`` on windows for satisfying dependencies. +Install instructions for this command line tool can be found at https://chocolatey.org/install + +.. + Perhaps when the windows package manager + is more mature we can switch to it. + +.. contents:: Windows + :local: + :depth: 2 + +Common Dependencies +~~~~~~~~~~~~~~~~~~~ + +Installing with Choco ++++++++++++++++++++++ +To install the common dependencies:: + + choco install cmake + choco install strawberryperl + choco install python + choco install sphinx + +Installing Manually ++++++++++++++++++++ +If you prefer to install manually rather than using Chocolatey, details and +requirements are as below. If you do install manually, please ensure you +have all executables searchable in your PATH variable. + +CMake +^^^^^ +You can get the win32 installer version from +`the official site `_. +It has the usual installer wizard. Make sure you let it add its binary folder +to your binary search PATH so the tool can be later run from anywhere. + +Perl / Strawberry Perl +^^^^^^^^^^^^^^^^^^^^^^ +For the code generation stage of the build process, you'll need Perl 5 with +XML::LibXML and XML::LibXSLT. `Strawberry Perl `_ is +recommended as it includes all of the required packages in a single, easy +install. + +After install, ensure Perl is in your user's PATH. This can be edited from +``Control Panel -> System -> Advanced System Settings -> Environment Variables``. + +The following directories must be in your PATH, in this order: + +* ``\c\bin`` +* ``\perl\site\bin`` +* ``\perl\bin`` +* ``\perl\vendor\lib\auto\XML\LibXML`` (may only be required on some systems) + +Be sure to close and re-open any existing ``cmd.exe`` windows after updating +your PATH. + +If you already have a different version of Perl installed (for example, from Cygwin), +you can run into some trouble. Either remove the other Perl install from PATH, or +install XML::LibXML and XML::LibXSLT for it using CPAN. + +Python +^^^^^^ +See ``python-install``. + +.. _python-install: https://www.python.org/downloads/ + +Sphinx +^^^^^^ +See ``sphinx-install`` at https://www.sphinx-doc.org/ + +.. _sphinx-install: https://www.sphinx-doc.org/en/master/usage/installation.html + +Visual Studio +~~~~~~~~~~~~~ + +Click Visual Studio 2022_ or 2019_ to download an installer wizard that will prompt you +to select the optional tools you want to download alongside the IDE. You may need to log into +(or create) a Microsoft account in order to download Visual Studio. + +**OR** + +To install this toolchain in your visual studio, you'll need to have first installed visual studio. +For example:: + + choco install visualstudio2022community + +If Visual Studio is installed follow these next steps: + +1. Open **Visual Studio Installer**. +2. Select modify, for whichever version you've chosen to utilize. +3. Check the boxes for the following components: + +* "Desktop Development with C++" +* "C++ Windows XP Support for VS 2017 (v141) tools [Deprecated]" +* "MSVC v140 - VS 2015 C++ build tools (v14.00)" + +Yes, this is unintuitive. Installing XP Support for VS 2017 installs XP Support for VS 2015 +if the 2015 toolchain is installed. + +.. _2022: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false +.. _2019: https://my.visualstudio.com/Downloads?q=visual%20studio%202019&wt.mc_id=o~msft~vscom~older-downloads + +Build Tools Only +~~~~~~~~~~~~~~~~ +Click `build tools`_ and you will be prompted to login to your Microsoft account. +Then you should be redirected to a page with various download options with 2015 +in their name. If this redirect doesn't occur, just copy, paste, and enter the +download link again and you should see the options. You need to get: +Visual C++ Build Tools for Visual Studio 2015 with Update 3. +Click the download button next to it and a dropdown of download formats will appear. +Select the DVD format to download an ISO file. When the download is complete, +click on the ISO file and a folder will popup with the following contents: + +* packages (folder) +* VCPlusPlusBuildTools2015Update3_x64_Files.cat +* VisualCppBuildTools_Full.exe + +The packages folder contains the dependencies that are required by the build tools. +These include: + +* Microsoft .NET Framework 4.6.1 Developer Pack +* Microsoft Visual C++ 2015 Redistributable (x64) - 14.0.24210 +* Windows 10 Universal SDK - 10.0.10240 +* Windows 8.1 SDK + +Click VisualCppBuildTools_Full.exe and use the default options provided by the installer +wizard that appears. After the installation is completed, add the path where MSBuild.exe +was installed to your PATH environment variable. The path should be: + +* ``C:\Program Files (x86)\MSBuild\14.0\Bin`` + +Note that this process may install only the ``v140`` toolchain, not the ``v140_xp`` toolchain that +is normally used to compile build releases of DFHack. Due to a bug in the Microsoft-provided libraries used with +the ``v140_xp`` toolchain that Microsoft has never fixed, DFHack (and probably also Dwarf Fortress itself) +doesn't run reliably on 64-bit XP. Investigations have so far suggested that ``v140`` and +``v140_xp`` are ABI-compatible. As such, there should be no harm in using ``v140`` instead of +``v140_xp`` as the build toolchain, at least on 64-bit platforms. However, it is our policy to use +``v140_xp`` for release builds for both 32-bit and 64-bit Windows, +since 32-bit releases of Dwarf Fortress work on XP and ``v140_xp`` is required for compatibility with +XP. + +The ``v141`` toolchain, in Visual Studio 2017, has been empirically documented to be incompatible with +released versions of Dwarf Fortress and cannot be used to make usable builds of DFHack. From 6a135d0b2b78d87e3f43e0b226a693d03fb1befe Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 19 Dec 2022 22:05:43 -0400 Subject: [PATCH 0003/2222] Fixes formatting mistake --- docs/dev/building/Dependencies.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index 584d36b5e..e5b62b8e8 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -13,6 +13,7 @@ Many of DFHack's build dependencies are included in the repository as git submod however there are some system dependencies as well. They are as follows: System packages: + * SDL (libsdl 1.2, not sdl2). * zlib * OpenGL headers (optional: to build `stonesense`) @@ -21,6 +22,7 @@ System packages: The **zlib** dependency is a compression library which is part of a chain of dependencies ultimately used by `quickfort` and `xlsxioreader`. Perl packages: + * XML::LibXML * XML::LibXSLT From 86ed5ae17a57c15a2b3eb24bce3c2d5a7c893dae Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 19 Dec 2022 22:12:00 -0400 Subject: [PATCH 0004/2222] Fixes link syntax --- docs/dev/building/Dependencies.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index e5b62b8e8..e065432e4 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -284,13 +284,13 @@ install XML::LibXML and XML::LibXSLT for it using CPAN. Python ^^^^^^ -See ``python-install``. +See `python-install`. .. _python-install: https://www.python.org/downloads/ Sphinx ^^^^^^ -See ``sphinx-install`` at https://www.sphinx-doc.org/ +See `sphinx-install` at https://www.sphinx-doc.org/ .. _sphinx-install: https://www.sphinx-doc.org/en/master/usage/installation.html From e60030e85e41dd18d3d8a651652aa66dbf67132e Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 21 Dec 2022 18:09:37 -0400 Subject: [PATCH 0005/2222] a small screen commit message about a lot of organization changes --- .gitignore | 1 + LICENSE.rst | 2 + docs/dev/Dev-intro.rst | 6 ++ docs/dev/Documentation.rst | 30 +--------- docs/dev/{ => building}/Compile.rst | 90 +++++------------------------ docs/dev/building/Dependencies.rst | 72 +++++++++++++++++------ docs/dev/building/Options.rst | 79 +++++++++++++++++++++++++ docs/dev/building/index.rst | 14 +++++ docs/dev/index.rst | 4 +- 9 files changed, 175 insertions(+), 123 deletions(-) rename docs/dev/{ => building}/Compile.rst (90%) create mode 100644 docs/dev/building/Options.rst create mode 100644 docs/dev/building/index.rst diff --git a/.gitignore b/.gitignore index 9b78fa31f..a421d40ac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # any build folders build*/ +!docs/dev/building/ nix buntu build/VC2010 diff --git a/LICENSE.rst b/LICENSE.rst index 68678dfb3..529814423 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -36,6 +36,7 @@ tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann xlsxio_ MIT \(c\) 2016-2020, Brecht Sanders alt-getopt_ MIT \(c\) 2009 Aleksey Cheusov +googletest_ BSD 3-Clause \(c\) 2008, Google Inc. =============== ============= ================================================= .. _DFHack: https://github.com/DFHack/dfhack @@ -56,6 +57,7 @@ alt-getopt_ MIT \(c\) 2009 Aleksey Cheusov .. _UTF-8-decoder: http://bjoern.hoehrmann.de/utf-8/decoder/dfa .. _xlsxio: https://github.com/brechtsanders/xlsxio .. _alt-getopt: https://github.com/LuaDist/alt-getopt +.. _googletest: https://github.com/google/googletest .. _CC-BY-SA: http://creativecommons.org/licenses/by/3.0/deed.en_US diff --git a/docs/dev/Dev-intro.rst b/docs/dev/Dev-intro.rst index 25861862e..212b2888a 100644 --- a/docs/dev/Dev-intro.rst +++ b/docs/dev/Dev-intro.rst @@ -8,6 +8,7 @@ likely the most straightforward choice. Other pages that may be relevant include: +- `building-dfhack-index` - `contributing` - `documentation` - `license` @@ -16,6 +17,8 @@ Other pages that may be relevant include: .. contents:: Contents :local: +.. _architecture + Architecture diagrams --------------------- @@ -28,6 +31,9 @@ together: :target: https://drive.google.com/file/d/1-2yeNMC7WHgMfZ9iQsDQ0dEbLukd_xyU :align: center +As seen in the diagram Dwarf Fortress utilizes the SDL library, this provides us with an easy to isolate +injection point for DFHack. + .. image:: https://drive.google.com/uc?export=download&id=1--JoEQbzKpVUOkRKDD9HxvuCqtom780F :alt: DFHack tool call graph :target: https://drive.google.com/file/d/1--JoEQbzKpVUOkRKDD9HxvuCqtom780F diff --git a/docs/dev/Documentation.rst b/docs/dev/Documentation.rst index 6ac5ef382..d649b8a15 100644 --- a/docs/dev/Documentation.rst +++ b/docs/dev/Documentation.rst @@ -434,35 +434,7 @@ Sphinx to build the docs: Using CMake ----------- -Enabling the ``BUILD_DOCS`` CMake option will cause the documentation to be built -whenever it changes as part of the normal DFHack build process. There are several -ways to do this: - -* When initially running CMake, add ``-DBUILD_DOCS:bool=ON`` to your ``cmake`` - command. For example:: - - cmake .. -DCMAKE_BUILD_TYPE:string=Release -DBUILD_DOCS:bool=ON -DCMAKE_INSTALL_PREFIX= - -* If you have already run CMake, you can simply run it again from your build - folder to update your configuration:: - - cmake .. -DBUILD_DOCS:bool=ON - -* You can edit the ``BUILD_DOCS`` setting in CMakeCache.txt directly - -* You can use the CMake GUI or ``ccmake`` to change the ``BUILD_DOCS`` setting - -* On Windows, if you prefer to use the batch scripts, you can run - ``generate-msvc-gui.bat`` and set ``BUILD_DOCS`` through the GUI. If you are - running another file, such as ``generate-msvc-all.bat``, you will need to edit - the batch script to add the flag. You can also run ``cmake`` on the command line, - similar to other platforms. - -By default, both HTML and text docs are built by CMake. The generated -documentation is stored in ``docs/html`` and ``docs/text`` (respectively) in the -root DFHack folder, and they will both be installed to ``hack/docs`` when you -install DFHack. The html and txt files will intermingle, but will not interfere -with one another. +See our page on `build options ` Running Sphinx manually ----------------------- diff --git a/docs/dev/Compile.rst b/docs/dev/building/Compile.rst similarity index 90% rename from docs/dev/Compile.rst rename to docs/dev/building/Compile.rst index 6a2e5d029..f3f1cb0ed 100644 --- a/docs/dev/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -9,7 +9,7 @@ Compiling DFHack DFHack builds are available for all supported platforms; see `installing` for installation instructions. If you are a DFHack end-user, modder, or plan on writing scripts (not plugins), it is generally recommended (and easier) to use -these builds instead of compiling DFHack from source. +these `builds` instead of compiling DFHack from source. However, if you are looking to develop plugins, work on the DFHack core, make complex changes to DF-structures, or anything else that requires compiling @@ -17,9 +17,11 @@ DFHack from source, this document will walk you through the build process. Note that some steps may be unconventional compared to other projects, so be sure to pay close attention if this is your first time compiling DFHack. +.. _build-releases:https://github.com/DFHack/dfhack/releases + .. contents:: Contents :local: - :depth: 1 + :depth: 2 .. _compile-how-to-get-the-code: @@ -86,6 +88,12 @@ assistance. are also able to help with any submodule-related (or Git-related) issues you may encounter. +Dependencies +------------ + +If you haven't already checked, this would be a good point to ensure you have +all the `dependencies `. + Contributing to DFHack ---------------------- @@ -101,6 +109,8 @@ This section describes build configuration options that apply to all platforms. If you don't have a working build environment set up yet, follow the instructions in the platform-specific sections below first, then come back here. +Be sure to check out common `compile options `. + Generator --------- @@ -160,10 +170,8 @@ automatically. Other settings -------------- -There are a variety of other settings which you can find in CMakeCache.txt in -your build folder or by running ``ccmake`` (or another CMake GUI). Most -DFHack-specific settings begin with ``BUILD_`` and control which parts of DFHack -are built. +See our page on `build options` + .. _compile-linux: @@ -174,29 +182,7 @@ On Linux, DFHack acts as a library that shadows parts of the SDL API using LD_PR Dependencies ------------ -DFHack is meant to be installed into an existing DF folder, so get one ready. - -We assume that any Linux platform will have ``git`` available (though it may -need to be installed with your package manager.) - -To build DFHack, you need GCC 4.8 or newer. GCC 4.8 has the benefit of avoiding -`libstdc++ compatibility issues `, but can be hard -to obtain on modern distributions, and working around these issues is done -automatically by the ``dfhack`` launcher script. As long as your system-provided -GCC is new enough, it should work. Note that extremely new GCC versions may not -have been used to build DFHack yet, so if you run into issues with these, please -let us know (e.g. by opening a GitHub issue). - -Before you can build anything, you'll also need ``cmake``. It is advisable to -also get ``ccmake`` on distributions that split the cmake package into multiple -parts. As mentioned above, ``ninja`` is recommended (many distributions call -this package ``ninja-build``). - -You will need pthread; most systems should have this already. Note that older -CMake versions may have trouble detecting pthread, so if you run into -pthread-related errors and pthread is installed, you may need to upgrade CMake, -either by downloading it from `cmake.org `_ or -through your package manager, if possible. + You also need zlib, libsdl (1.2, not sdl2, like DF), perl, and the XML::LibXML and XML::LibXSLT perl packages (for the code generation parts). You should be @@ -609,50 +595,6 @@ manually uninstall the version you have already and re-install via Chocolatey, which will ensure the PATH are set up right and will allow Chocolatey to manage that program for you in future. -Additional dependencies: installing manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you prefer to install manually rather than using Chocolatey, details and -requirements are as below. If you do install manually, please ensure you -have all PATHs set up correctly. - -Git -^^^ -Some examples: - -* `Git for Windows `_ (command-line and GUI) -* `tortoisegit `_ (GUI and File Explorer integration) - -CMake -^^^^^ -You can get the win32 installer version from -`the official site `_. -It has the usual installer wizard. Make sure you let it add its binary folder -to your binary search PATH so the tool can be later run from anywhere. - -Perl / Strawberry Perl -^^^^^^^^^^^^^^^^^^^^^^ -For the code generation stage of the build process, you'll need Perl 5 with -XML::LibXML and XML::LibXSLT. `Strawberry Perl `_ is -recommended as it includes all of the required packages in a single, easy -install. - -After install, ensure Perl is in your user's PATH. This can be edited from -``Control Panel -> System -> Advanced System Settings -> Environment Variables``. - -The following directories must be in your PATH, in this order: - -* ``\c\bin`` -* ``\perl\site\bin`` -* ``\perl\bin`` -* ``\perl\vendor\lib\auto\XML\LibXML`` (may only be required on some systems) - -Be sure to close and re-open any existing ``cmd.exe`` windows after updating -your PATH. - -If you already have a different version of Perl installed (for example, from Cygwin), -you can run into some trouble. Either remove the other Perl install from PATH, or -install XML::LibXML and XML::LibXSLT for it using CPAN. - Build ----- There are several different batch files in the ``win32`` and ``win64`` @@ -825,7 +767,7 @@ Building the documentation ========================== The steps above will not build DFHack's documentation by default. If you are -editing documentation, see `documentation` for details on how to build it. +changing documentation, see `documentation` for details on how to build it. Misc. Notes =========== diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index e065432e4..ad065caa4 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -1,8 +1,18 @@ -.. _dependencies: +.. _build-dependencies: -################### -DFHack Dependencies -################### +############ +Dependencies +############ + +The most immediate consideration is of course that DFHack is meant to be installed into an **existing DF folder**. +So it is prudent that one is ready. + +.. contents:: Contents + :local: + :depth: 2 + +Build Dependencies +------------------ .. DFHack is quite large, so I've attempted to @@ -15,11 +25,16 @@ however there are some system dependencies as well. They are as follows: System packages: * SDL (libsdl 1.2, not sdl2). -* zlib -* OpenGL headers (optional: to build `stonesense`) +* cmake +* ccache (**optional**, but recommended to improve build times) +* OpenGL headers (**optional**: to build `stonesense`) +* zlib (compression library used for `xlsxioreader`) +* build system + +.. + maybe the below should be talked about next to the bullets **SDL** is used as an injection point which you can see more about in DFHack's `architecture` documentation & diagrams. -The **zlib** dependency is a compression library which is part of a chain of dependencies ultimately used by `quickfort` and `xlsxioreader`. Perl packages: @@ -30,10 +45,6 @@ These perl packages are used in code generation. DFHack has many structures that files to define and update these structures. Then during the configuration process [running cmake] these xml files are used to generate C++ source code to define these structures for use in plugins and scripts. -.. contents:: Contents - :local: - :depth: 2 - .. _linux-dependency-instructions: Linux @@ -43,7 +54,7 @@ Here are some package install commands for various distributions: * On Arch linux:: - pacman -Sy gcc cmake ninja git dwarffortress zlib perl-xml-libxml perl-xml-libxslt + pacman -Sy gcc cmake ccmake ninja git dwarffortress zlib perl-xml-libxml perl-xml-libxslt * The ``dwarffortress`` package provides the necessary SDL packages. * For the required Perl modules: ``perl-xml-libxml`` and ``perl-xml-libxslt`` (or through ``cpan``) @@ -58,6 +69,25 @@ Here are some package install commands for various distributions: yum install gcc-c++ cmake ninja-build git zlib-devel SDL-devel perl-core perl-XML-LibXML perl-XML-LibXSLT ruby +To build DFHack, you need GCC 4.8 or newer. GCC 4.8 has the benefit of avoiding +`libstdc++ compatibility issues `, but can be hard +to obtain on modern distributions, and working around these issues is done +automatically by the ``dfhack`` launcher script. As long as your system-provided +GCC is new enough, it should work. Note that extremely new GCC versions may not +have been used to build DFHack yet, so if you run into issues with these, please +let us know (e.g. by opening a GitHub issue). + +Before you can build anything, you'll also need ``cmake``. It is advisable to +also get ``ccmake`` on distributions that split the cmake package into multiple +parts. As mentioned above, ``ninja`` is recommended (many distributions call +this package ``ninja-build``). + +You will need pthread; most systems should have this already. Note that older +CMake versions may have trouble detecting pthread, so if you run into +pthread-related errors and pthread is installed, you may need to upgrade CMake, +either by downloading it from `cmake.org `_ or +through your package manager, if possible. + Building 32-bit Binaries ======================== If you want to compile 32-bit DFHack on 64-bit distributions, you'll need the @@ -198,27 +228,31 @@ What you'll need is as follows: * i.e. Microsoft Visual C++ 2015 Build Tools * Git (optional) * CMake -* StrawberryPerl (optional, see nested) +* StrawberryPerl, OR CPAN (optional, see nested) * Perl (required) * XML:LibXML (required) * XML:LibXLST (required) -* Python (required for documentation, optional otherwise) +* `Python ` (required for documentation, optional otherwise) + + * `Sphinx ` Releases of Dwarf Fortress since roughly 2016 have been compiled for Windows using Microsoft's Visual Studio 2015 C++ compiler. In order to guarantee ABI and STL compatibility with Dwarf Fortress, DFHack has to be compiled with the same compiler. Visual Studio 2015 is no longer supported by Microsoft and it can be difficult to obtain -working installers for this product today. As of 2022, the recommended approach -is to use Visual Studio 2022 or Visual Studio 2019, installing additional optional -Visual Studio components which provide the required support for using -Visual Studio 2015's toolchain. All of the required tools are available from Microsoft as part of -Visual Studio's Community Edition at no charge. +working installers for this product today. The recommended approach is to use a modern community +version of Visual Studio such as 2022 or 2019, installing additional optional Visual Studio +components which provide the required support for using Visual Studio 2015's toolchain. +All of the required tools are available from Microsoft as part of Visual Studio's Community +Edition at no charge. You can also download just the Visual C++ 2015 `build tools`_ if you aren't going to use Visual Studio to edit code. +.. _build tools: https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downloads + Installing Dependencies ======================= diff --git a/docs/dev/building/Options.rst b/docs/dev/building/Options.rst new file mode 100644 index 000000000..91ad51778 --- /dev/null +++ b/docs/dev/building/Options.rst @@ -0,0 +1,79 @@ +.. _build-options: + +############# +Build Options +############# + +There are a variety of other settings which you can find in CMakeCache.txt in +your build folder or by running ``ccmake`` (or another CMake GUI). Most +DFHack-specific settings begin with ``BUILD_`` and control which parts of DFHack +are built. + +Typical usage may look like:: + + # Plugin development with updated documentation + cmake . -G Ninja -B builds/debug-info/ -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE:string=RelWithDebInfo -DBUILD_DOCS:bool=ON -DBUILD_PLUGINS=1 + # Core DFHack only + cmake ../ -G Ninja -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE:string=RelWithDebInfo -DBUILD_TESTS -DBUILD_DOCS:0 -DBUILD_PLUGINS=0 + +Generator +--------- +For the uninitiated, the generator is what allows cmake to, of course, generate +visual studio solution & project files, a makefile, or anything else. +Your selection of generator comes down to preference and availability. + +Visual Studio +============= +To generate visual studio project files, you'll need to select a particular version of +visual studio, and match that to your system's generator list viewed with ``cmake --help`` + +example:: + + cmake . -G "Visual Studio 17 2022" + +Ninja +===== +The generally preferred build system where available. + +Core +---- +todo: + +Plugins +------- +todo: + +.. _building-documentation: + +Documentation +------------- + +Enabling the ``BUILD_DOCS`` CMake option will cause the documentation to be built +whenever it changes as part of the normal DFHack build process. There are several +ways to do this: + +* When initially running CMake, add ``-DBUILD_DOCS:bool=ON`` to your ``cmake`` + command. For example:: + + cmake .. -DCMAKE_BUILD_TYPE:string=Release -DBUILD_DOCS:bool=ON -DCMAKE_INSTALL_PREFIX= + +* If you have already run CMake, you can simply run it again from your build + folder to update your configuration:: + + cmake .. -DBUILD_DOCS:bool=ON + +* You can edit the ``BUILD_DOCS`` setting in CMakeCache.txt directly + +* You can use the CMake GUI or ``ccmake`` to change the ``BUILD_DOCS`` setting + +* On Windows, if you prefer to use the batch scripts, you can run + ``generate-msvc-gui.bat`` and set ``BUILD_DOCS`` through the GUI. If you are + running another file, such as ``generate-msvc-all.bat``, you will need to edit + the batch script to add the flag. You can also run ``cmake`` on the command line, + similar to other platforms. + +By default, both HTML and text docs are built by CMake. The generated +documentation is stored in ``docs/html`` and ``docs/text`` (respectively) in the +root DFHack folder, and they will both be installed to ``hack/docs`` when you +install DFHack. The html and txt files will intermingle, but will not interfere +with one another. diff --git a/docs/dev/building/index.rst b/docs/dev/building/index.rst new file mode 100644 index 000000000..0a5758adf --- /dev/null +++ b/docs/dev/building/index.rst @@ -0,0 +1,14 @@ +.. _building-dfhack-index: + +=============== +Building DFHack +=============== + +Those seeking to compile the source code for DFHack, and or plugins, can refer to the following help pages. + +.. toctree:: + :maxdepth: 2 + + /docs/dev/building/Dependencies + /docs/dev/building/Compile + /docs/dev/building/Options diff --git a/docs/dev/index.rst b/docs/dev/index.rst index 26d094ec8..c96024055 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -1,3 +1,5 @@ + + ======================== DFHack development guide ======================== @@ -8,7 +10,7 @@ These are pages relevant to people developing for DFHack. :maxdepth: 1 /docs/dev/Dev-intro - /docs/dev/Compile + /docs/dev/building/index /docs/dev/Contributing /docs/dev/Documentation /docs/api/index From c0d582c406c89ef2106a508be3953325bec54cc8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 21 Dec 2022 20:35:40 -0400 Subject: [PATCH 0006/2222] more small screen changes --- docs/dev/Contributing.rst | 31 +- docs/dev/building/Compile.rst | 593 +++++++++-------------------- docs/dev/building/Dependencies.rst | 259 ++++++------- 3 files changed, 332 insertions(+), 551 deletions(-) diff --git a/docs/dev/Contributing.rst b/docs/dev/Contributing.rst index f503d9126..66be801f4 100644 --- a/docs/dev/Contributing.rst +++ b/docs/dev/Contributing.rst @@ -36,11 +36,24 @@ The sections below cover some guidelines that contributions should follow: .. contents:: :local: +General contribution guidelines +------------------------------- +* If convenient, compile on multiple platforms when changing anything that + compiles. Our CI should catch anything that fails to build, but checking in + advance can sometimes let you know of any issues sooner. +* Update documentation when applicable - see `docs-standards` for details. +* Update ``docs/changelog.txt`` and ``docs/about/Authors.rst`` when applicable. See + `build-changelog` for more information on the changelog format. +* Submit ideas and bug reports as :issue:`issues on GitHub <>`. + Posts in the forum thread can easily get missed or forgotten. +* Work on :issue:`reported problems ` + will take priority over ideas or suggestions. + Code format ----------- * Four space indents for C++. Never use tabs for indentation in any language. * LF (Unix style) line terminators -* Avoid trailing whitespace +* No trailing whitespace * UTF-8 encoding * For C++: @@ -86,27 +99,13 @@ Pull request guidelines or add "WIP" to the title. Otherwise, your pull request may be reviewed and/or merged prematurely. -General contribution guidelines -------------------------------- -* If convenient, compile on multiple platforms when changing anything that - compiles. Our CI should catch anything that fails to build, but checking in - advance can sometimes let you know of any issues sooner. -* Update documentation when applicable - see `docs-standards` for details. -* Update ``docs/changelog.txt`` and ``docs/about/Authors.rst`` when applicable. See - `build-changelog` for more information on the changelog format. -* Submit ideas and bug reports as :issue:`issues on GitHub <>`. - Posts in the forum thread can easily get missed or forgotten. -* Work on :issue:`reported problems ` - will take priority over ideas or suggestions. - - Other ways to help ================== DFHack is a software project, but there's a lot more to it than programming. If you're not comfortable programming, you can help by: * reporting bugs and incomplete documentation -* improving the documentation +* improving the documentation (C++ api is rife) * finding third-party scripts to add * writing tutorials for newbies diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index f3f1cb0ed..a0a9b070c 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -2,13 +2,13 @@ .. _compile: -################ -Compiling DFHack -################ +########### +Compilation +########### DFHack builds are available for all supported platforms; see `installing` for installation instructions. If you are a DFHack end-user, modder, or plan on -writing scripts (not plugins), it is generally recommended (and easier) to use +writing scripts [lua] (not plugins), it is generally recommended (and easier) to use these `builds` instead of compiling DFHack from source. However, if you are looking to develop plugins, work on the DFHack core, make @@ -173,17 +173,17 @@ Other settings See our page on `build options` +Instructions +============ .. _compile-linux: Linux -===== +----- On Linux, DFHack acts as a library that shadows parts of the SDL API using LD_PRELOAD. Dependencies ------------- - - +~~~~~~~~~~~~ You also need zlib, libsdl (1.2, not sdl2, like DF), perl, and the XML::LibXML and XML::LibXSLT perl packages (for the code generation parts). You should be able to find them in your distribution's repositories. @@ -206,9 +206,93 @@ Here are some package install commands for various distributions: yum install gcc-c++ cmake ninja-build git zlib-devel SDL-devel perl-core perl-XML-LibXML perl-XML-LibXSLT ruby +Windows cross compiling from Linux +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. highlight:: bash + +If you are on Linux but need to produce a Windows build (for example, because the +DF version you're working on isn't out for Linux yet), here is how you can build +and run a Windows binary on Linux. + +.. contents:: + :local: + :depth: 1 + +Step 1: prepare a docker image +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On your Linux host, install and run the docker daemon and then run these commands:: + + xhost +local:root + git clone https://github.com/BenLubar/build-env.git + cd build-env/msvc + docker build . + docker image ls + IMAGE_ID= + docker run -it --env="DISPLAY" --env="QT_X11_NO_MITSHM=1" --volume=/tmp/.X11-unix:/tmp/.X11-unix --user buildmaster --name dfhack-win $IMAGE_ID + +The ``xhost`` command and ``--env`` parameters are there so you can eventually +run Dwarf Fortress from the container and have it display on your host. + +Step 2: build DFHack +^^^^^^^^^^^^^^^^^^^^ + +The ``docker run`` command above will give you a shell prompt (as root) in the +container. Inside the container, run the following commands:: + + git clone https://github.com/DFHack/dfhack.git + cd dfhack + git submodule update --init + cd build + dfhack-configure windows 64 Release + dfhack-make + +Inside the ``dfhack-*`` scripts there are several commands that set up the wine +server. Each invocation of a windows tool will cause wine to run in the container. +Preloading the wineserver and telling it not to exit will speed configuration and +compilation up considerably (approx. 10x). You can configure and build DFHack +with regular ``cmake`` and ``ninja`` commands, but your build will go much slower. + +Step 3: copy Dwarf Fortress to the container +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +First, create a directory in the container to house the Dwarf Fortress binary and +assets:: + + mkdir ~/df + +If you can just downlaod Dwarf Fortress directly into the container, then that's fine. +Otherwise, you can do something like this in your host Linux environment to copy an +installed version to the container:: + + cd ~/.steam/steam/steamapps/common/Dwarf\ Fortress/ + docker cp . dfhack-win:df/ + +Step 4: install DFHack and run DF +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Back in the container, run the following commands:: + + cd dfhack/build + cmake .. -DCMAKE_INSTALL_PREFIX=/home/buildmaster/df + ninja install + cd ~/df + wine64 "Dwarf Fortress.exe" + +Other notes +^^^^^^^^^^^ + +Closing your shell will kick you out of the container. Run this command on your Linux +host when you want to reattach:: + + docker start -ai dfhack-win + +If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. +That will handle all the wineserver management for you. Multilib dependencies ---------------------- +~~~~~~~~~~~~~~~~~~~~~ If you want to compile 32-bit DFHack on 64-bit distributions, you'll need the multilib development tools and libraries: @@ -230,7 +314,7 @@ to use ``lxc`` to :forums:`create a virtual 32-bit environment <139553.msg5435310#msg5435310>`. Build ------ +~~~~~ Building is fairly straightforward. Enter the ``build`` folder (or create an empty folder in the DFHack directory to use instead) and start the build like this:: @@ -280,323 +364,12 @@ similar errors. This is why DFHack distributes both GCC 4.8 and GCC 7 builds. If you are planning on distributing binaries to other users, we recommend using an older GCC (but still at least 4.8) version if possible. - -.. _compile-macos: - -macOS -===== -DFHack functions similarly on macOS and Linux, and the majority of the -information above regarding the build process (CMake and Ninja) applies here -as well. - -DFHack can officially be built on macOS only with GCC 4.8 or 7. Anything newer than 7 -will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-notes`), -and your build will likely not be redistributable. - -.. _osx-new-gcc-notes: - -Notes for GCC 8+ or OS X 10.10+ users -------------------------------------- - -If none of these situations apply to you, skip to `osx-setup`. - -If you have issues building on OS X 10.10 (Yosemite) or above, try defining -the following environment variable:: - - export MACOSX_DEPLOYMENT_TARGET=10.9 - -If you build with a GCC version newer than 7, DFHack will probably crash -immediately on startup, or soon after. To fix this, you will need to replace -``hack/libstdc++.6.dylib`` with a symlink to the ``libstdc++.6.dylib`` included -in your version of GCC:: - - cd /hack && mv libstdc++.6.dylib libstdc++.6.dylib.orig && - ln -s [PATH_TO_LIBSTDC++] . - -For example, with GCC 6.3.0, ``PATH_TO_LIBSTDC++`` would be:: - - /usr/local/Cellar/gcc@6/6.3.0/lib/gcc/6/libstdc++.6.dylib # for 64-bit DFHack - /usr/local/Cellar/gcc@6/6.3.0/lib/gcc/6/i386/libstdc++.6.dylib # for 32-bit DFHack - -**Note:** If you build with a version of GCC that requires this, your DFHack -build will *not* be redistributable. (Even if you copy the ``libstdc++.6.dylib`` -from your GCC version and distribute that too, it will fail on older OS X -versions.) For this reason, if you plan on distributing DFHack, it is highly -recommended to use GCC 4.8 or 7. - -.. _osx-m1-notes: - -Notes for M1 users ------------------- - -Alongside the above, you will need to follow these additional steps to get it -running on Apple silicon. - -Install an x86 copy of ``homebrew`` alongside your existing one. `This -stackoverflow answer `__ describes the -process. - -Follow the normal macOS steps to install ``cmake`` and ``gcc`` via your x86 copy of -``homebrew``. Note that this will install a GCC version newer than 7, so see -`osx-new-gcc-notes`. - -In your terminal, ensure you have your path set to the correct homebrew in -addition to the normal ``CC`` and ``CXX`` flags above:: - - export PATH=/usr/local/bin:$PATH - -.. _osx-setup: - -Dependencies and system set-up ------------------------------- - -#. Download and unpack a copy of the latest DF -#. Install Xcode from the Mac App Store - -#. Install the XCode Command Line Tools by running the following command:: - - xcode-select --install - -#. Install dependencies - - It is recommended to use Homebrew instead of MacPorts, as it is generally - cleaner, quicker, and smarter. For example, installing MacPort's GCC will - install more than twice as many dependencies as Homebrew's will, and all in - both 32-bit and 64-bit variants. Homebrew also doesn't require constant use - of ``sudo``. - - Using `Homebrew `_ (recommended):: - - brew tap homebrew/versions - brew install git - brew install cmake - brew install ninja - brew install gcc@7 - - Using `MacPorts `_:: - - sudo port install gcc7 +universal cmake +universal git-core +universal ninja +universal - - Macports will take some time - maybe hours. At some point it may ask - you to install a Java environment; let it do so. - -#. Install Perl dependencies - - * Using system Perl - - * ``sudo cpan`` - - If this is the first time you've run cpan, you will need to go through the setup - process. Just stick with the defaults for everything and you'll be fine. - - If you are running OS X 10.6 (Snow Leopard) or earlier, good luck! - You'll need to open a separate Terminal window and run:: - - sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml - - * ``install XML::LibXML`` - * ``install XML::LibXSLT`` - - * In a separate, local Perl install - - Rather than using system Perl, you might also want to consider - the Perl manager, `Perlbrew `_. - - This manages Perl 5 locally under ``~/perl5/``, providing an easy - way to install Perl and run CPAN against it without ``sudo``. - It can maintain multiple Perl installs and being local has the - benefit of easy migration and insulation from OS issues and upgrades. - - See https://perlbrew.pl/ for more details. - -Building --------- - -* Get the DFHack source as per section `compile-how-to-get-the-code`, above. -* Set environment variables - - Homebrew (if installed elsewhere, replace /usr/local with ``$(brew --prefix)``):: - - export CC=/usr/local/bin/gcc-7 - export CXX=/usr/local/bin/g++-7 - - Macports:: - - export CC=/opt/local/bin/gcc-mp-7 - export CXX=/opt/local/bin/g++-mp-7 - - Change the version numbers appropriately if you installed a different version of GCC. - - If you are confident that you have GCC in your path, you can omit the absolute paths:: - - export CC=gcc-7 - export CXX=g++-7 - - (adjust as needed for different GCC installations) - -* Build DFHack:: - - mkdir build-osx - cd build-osx - cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX= - ninja install # or ninja -jX install to specify the number of cores (X) to use - - should be a path to a copy of Dwarf Fortress, of the appropriate - version for the DFHack you are building. - - .. _compile-windows: Windows -======= -On Windows, DFHack replaces the SDL library distributed with DF. - -Dependencies ------------- -You will need the following: - -* Microsoft Visual C++ 2022, 2019, 2017, or 2015 (optional) -* Microsoft Visual C++ 2015 Build Tools -* Git -* CMake -* Perl with XML::LibXML and XML::LibXSLT - - * It is recommended to install StrawberryPerl, which includes both. - -* Python (for documentation; optional, except for release builds) - -Microsoft Visual Studio -~~~~~~~~~~~~~~~~~~~~~~~ -Releases of Dwarf Fortress since roughly 2016 have been compiled for Windows using -Microsoft's Visual Studio 2015 C++ compiler. In order to guarantee ABI and STL compatibility -with Dwarf Fortress, DFHack has to be compiled with the same compiler. - -Visual Studio 2015 is no longer supported by Microsoft and it can be difficult to obtain -working installers for this product today. As of 2022, the recommended approach -is to use Visual Studio 2022 or Visual Studio 2019, installing additional optional -Visual Studio components which provide the required support for using -Visual Studio 2015's toolchain. All of the required tools are available from Microsoft as part of -Visual Studio's Community Edition at no charge. - -You can also download just the Visual C++ 2015 `build tools`_ if you aren't going to use -Visual Studio to edit code. - -Option 1: Build Tools Only -^^^^^^^^^^^^^^^^^^^^^^^^^^ -Click `build tools`_ and you will be prompted to login to your Microsoft account. -Then you should be redirected to a page with various download options with 2015 -in their name. If this redirect doesn't occur, just copy, paste, and enter the -download link again and you should see the options. You need to get: -Visual C++ Build Tools for Visual Studio 2015 with Update 3. -Click the download button next to it and a dropdown of download formats will appear. -Select the DVD format to download an ISO file. When the download is complete, -click on the ISO file and a folder will popup with the following contents: - -* packages (folder) -* VCPlusPlusBuildTools2015Update3_x64_Files.cat -* VisualCppBuildTools_Full.exe - -The packages folder contains the dependencies that are required by the build tools. -These include: - -* Microsoft .NET Framework 4.6.1 Developer Pack -* Microsoft Visual C++ 2015 Redistributable (x64) - 14.0.24210 -* Windows 10 Universal SDK - 10.0.10240 -* Windows 8.1 SDK - -Click VisualCppBuildTools_Full.exe and use the default options provided by the installer -wizard that appears. After the installation is completed, add the path where MSBuild.exe -was installed to your PATH environment variable. The path should be: - -* ``C:\Program Files (x86)\MSBuild\14.0\Bin`` - -Note that this process may install only the ``v140`` toolchain, not the ``v140_xp`` toolchain that -is normally used to compile build releases of DFHack. Due to a bug in the Microsoft-provided libraries used with -the ``v140_xp`` toolchain that Microsoft has never fixed, DFHack (and probably also Dwarf Fortress itself) -doesn't run reliably on 64-bit XP. Investigations have so far suggested that ``v140`` and -``v140_xp`` are ABI-compatible. As such, there should be no harm in using ``v140`` instead of -``v140_xp`` as the build toolchain, at least on 64-bit platforms. However, it is our policy to use -``v140_xp`` for release builds for both 32-bit and 64-bit Windows, -since 32-bit releases of Dwarf Fortress work on XP and ``v140_xp`` is required for compatibility with -XP. - -The ``v141`` toolchain, in Visual Studio 2017, has been empirically documented to be incompatible with -released versions of Dwarf Fortress and cannot be used to make usable builds of DFHack. - -Option 2: IDE + Build Tools -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Click Visual Studio 2022_ or 2019_ to download an installer wizard that will prompt you -to select the optional tools you want to download alongside the IDE. You may need to log into -(or create) a Microsoft account in order to download Visual Studio. - -In addition to selecting the workload for "Desktop Development with C++", -you will also need to go to the "Individual Components" tab in the Installer and -select the following additional components to get the "``v140_xp``" toolchain that DFHack -requires for ABI compatibility with recent releases of Dwarf Fortress: -* MSVC v140 - VS 2015 C++ build tools (v14.00) -* C++ Windows XP Support for VS 2017 (v141) tools [Deprecated] - -Yes, this is unintuitive. Installing XP Support for VS 2017 installs XP Support for VS 2015 -if the 2015 toolchain is installed. - -.. _2022: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false -.. _2019: https://my.visualstudio.com/Downloads?q=visual%20studio%202019&wt.mc_id=o~msft~vscom~older-downloads -.. _build tools: https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downloads - -Additional dependencies: installing with the Chocolatey Package Manager -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The remainder of dependencies - Git, CMake, StrawberryPerl, and Python - can be -most easily installed using the Chocolatey Package Manager. Chocolatey is a -\*nix-style package manager for Windows. It's fast, small (8-20MB on disk) -and very capable. Think "``apt-get`` for Windows." - -Chocolatey is a recommended way of installing the required dependencies -as it's quicker, requires less effort, and will install known-good utilities -guaranteed to have the correct setup (especially PATH). - -To install Chocolatey and the required dependencies: - -* Go to https://chocolatey.org in a web browser -* At the top of the page it will give you the install command to copy - - * Copy the first one, which starts ``@powershell ...`` - * It won't be repeated here in case it changes in future Chocolatey releases. - -* Open an elevated (Admin) ``cmd.exe`` window - - * On Windows 8 and later this can be easily achieved by: - - * right-clicking on the Start Menu, or pressing Win+X. - * choosing "Command Prompt (Admin)" - - * On earlier Windows: find ``cmd.exe`` in Start Menu, right click - and choose Open As Administrator. - -* Paste in the Chocolatey install command and hit enter -* Close this ``cmd.exe`` window and open another Admin ``cmd.exe`` in the same way -* Run the following command:: - - choco install git cmake.portable strawberryperl -y - -* Close the Admin ``cmd.exe`` window; you're done! - -You can now use all of these utilities from any normal ``cmd.exe`` window. -You only need Admin/elevated ``cmd.exe`` for running ``choco install`` commands; -for all other purposes, including compiling DFHack, you should use -a normal ``cmd.exe`` (or, better, an improved terminal like `Cmder `_; -details below, under Build.) - -**NOTE**: you can run the above ``choco install`` command even if you already have -Git, CMake or StrawberryPerl installed. Chocolatey will inform you if any software -is already installed and won't re-install it. In that case, please check the PATHs -are correct for that utility as listed in the manual instructions below. Or, better, -manually uninstall the version you have already and re-install via Chocolatey, -which will ensure the PATH are set up right and will allow Chocolatey to manage -that program for you in future. - +------- Build ------ +~~~~~ There are several different batch files in the ``win32`` and ``win64`` subfolders in the ``build`` folder, along with a script that's used for picking the DF path. Use the subfolder corresponding to the architecture that you want @@ -622,10 +395,26 @@ solution file(s): release builds of DFHack. Note that this includes documentation, which requires Python. -Then you can either open the solution with MSVC or use one of the msbuild scripts: +Then you can either open the solution with MSVC or use one of the msbuild scripts. + +Building/installing from the Visual Studio IDE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +After running the CMake generate script you will have a new folder called VC2015 +or VC2015_32, depending on the architecture you specified. Open the file +``dfhack.sln`` inside that folder. If you have multiple versions of Visual +Studio installed, make sure you open with Visual Studio 2015. + +The first thing you must then do is change the build type. It defaults to Debug, +but this cannot be used on Windows. Debug is not binary-compatible with DF. +If you try to use a debug build with DF, you'll only get crashes and for this +reason the Windows "debug" scripts actually do RelWithDebInfo builds. +After loading the Solution, change the Build Type to either ``Release`` +or ``RelWithDebInfo``. -Building/installing from the command line: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Then build the ``INSTALL`` target listed under ``CMakePredefinedTargets``. + +Building/installing from the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In the build directory you will find several ``.bat`` files: * Scripts with ``build`` prefix will only build DFHack. @@ -666,117 +455,109 @@ You don't need to do anything special to compile from Bash. As long as your PATH are set up correctly, you can run the same generate- and build/install/package- bat files as detailed above. -Building/installing from the Visual Studio IDE: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -After running the CMake generate script you will have a new folder called VC2015 -or VC2015_32, depending on the architecture you specified. Open the file -``dfhack.sln`` inside that folder. If you have multiple versions of Visual -Studio installed, make sure you open with Visual Studio 2015. +.. _compile-macos: -The first thing you must then do is change the build type. It defaults to Debug, -but this cannot be used on Windows. Debug is not binary-compatible with DF. -If you try to use a debug build with DF, you'll only get crashes and for this -reason the Windows "debug" scripts actually do RelWithDebInfo builds. -After loading the Solution, change the Build Type to either ``Release`` -or ``RelWithDebInfo``. +macOS +----- +DFHack functions similarly on macOS and Linux, and the majority of the +information above regarding the build process (CMake and Ninja) applies here +as well. -Then build the ``INSTALL`` target listed under ``CMakePredefinedTargets``. +DFHack can officially be built on macOS only with GCC 4.8 or 7. Anything newer than 7 +will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-notes`), +and your build will likely not be redistributable. -Windows cross compiling from Linux -================================== +Building +~~~~~~~~ -.. highlight:: bash +* Get the DFHack source as per section `compile-how-to-get-the-code`, above. +* Set environment variables -If you are on Linux but need to produce a Windows build (for example, because the -DF version you're working on isn't out for Linux yet), here is how you can build -and run a Windows binary on Linux. + Homebrew (if installed elsewhere, replace /usr/local with ``$(brew --prefix)``):: -Step 1: prepare a docker image ------------------------------- + export CC=/usr/local/bin/gcc-7 + export CXX=/usr/local/bin/g++-7 -On your Linux host, install and run the docker daemon and then run these commands:: + Macports:: - xhost +local:root - git clone https://github.com/BenLubar/build-env.git - cd build-env/msvc - docker build . - docker image ls - IMAGE_ID= - docker run -it --env="DISPLAY" --env="QT_X11_NO_MITSHM=1" --volume=/tmp/.X11-unix:/tmp/.X11-unix --user buildmaster --name dfhack-win $IMAGE_ID + export CC=/opt/local/bin/gcc-mp-7 + export CXX=/opt/local/bin/g++-mp-7 -The ``xhost`` command and ``--env`` parameters are there so you can eventually -run Dwarf Fortress from the container and have it display on your host. + Change the version numbers appropriately if you installed a different version of GCC. -Step 2: build DFHack --------------------- + If you are confident that you have GCC in your path, you can omit the absolute paths:: -The ``docker run`` command above will give you a shell prompt (as root) in the -container. Inside the container, run the following commands:: + export CC=gcc-7 + export CXX=g++-7 - git clone https://github.com/DFHack/dfhack.git - cd dfhack - git submodule update --init - cd build - dfhack-configure windows 64 Release - dfhack-make + (adjust as needed for different GCC installations) -Inside the ``dfhack-*`` scripts there are several commands that set up the wine -server. Each invocation of a windows tool will cause wine to run in the container. -Preloading the wineserver and telling it not to exit will speed configuration and -compilation up considerably (approx. 10x). You can configure and build DFHack -with regular ``cmake`` and ``ninja`` commands, but your build will go much slower. +* Build DFHack:: -Step 3: copy Dwarf Fortress to the container --------------------------------------------- + mkdir build-osx + cd build-osx + cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX= + ninja install # or ninja -jX install to specify the number of cores (X) to use -First, create a directory in the container to house the Dwarf Fortress binary and -assets:: + should be a path to a copy of Dwarf Fortress, of the appropriate + version for the DFHack you are building. - mkdir ~/df +.. _osx-new-gcc-notes: -If you can just downlaod Dwarf Fortress directly into the container, then that's fine. -Otherwise, you can do something like this in your host Linux environment to copy an -installed version to the container:: +Notes for GCC 8+ or OS X 10.10+ users +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - cd ~/.steam/steam/steamapps/common/Dwarf\ Fortress/ - docker cp . dfhack-win:df/ +If none of these situations apply to you, skip to `osx-setup`. -Step 4: install DFHack and run DF ---------------------------------- +If you have issues building on OS X 10.10 (Yosemite) or above, try defining +the following environment variable:: -Back in the container, run the following commands:: + export MACOSX_DEPLOYMENT_TARGET=10.9 - cd dfhack/build - cmake .. -DCMAKE_INSTALL_PREFIX=/home/buildmaster/df - ninja install - cd ~/df - wine64 "Dwarf Fortress.exe" +If you build with a GCC version newer than 7, DFHack will probably crash +immediately on startup, or soon after. To fix this, you will need to replace +``hack/libstdc++.6.dylib`` with a symlink to the ``libstdc++.6.dylib`` included +in your version of GCC:: -Other notes ------------ + cd /hack && mv libstdc++.6.dylib libstdc++.6.dylib.orig && + ln -s [PATH_TO_LIBSTDC++] . -Closing your shell will kick you out of the container. Run this command on your Linux -host when you want to reattach:: +For example, with GCC 6.3.0, ``PATH_TO_LIBSTDC++`` would be:: - docker start -ai dfhack-win + /usr/local/Cellar/gcc@6/6.3.0/lib/gcc/6/libstdc++.6.dylib # for 64-bit DFHack + /usr/local/Cellar/gcc@6/6.3.0/lib/gcc/6/i386/libstdc++.6.dylib # for 32-bit DFHack -If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. -That will handle all the wineserver management for you. +**Note:** If you build with a version of GCC that requires this, your DFHack +build will *not* be redistributable. (Even if you copy the ``libstdc++.6.dylib`` +from your GCC version and distribute that too, it will fail on older OS X +versions.) For this reason, if you plan on distributing DFHack, it is highly +recommended to use GCC 4.8 or 7. -Building the documentation -========================== +.. _osx-m1-notes: -The steps above will not build DFHack's documentation by default. If you are -changing documentation, see `documentation` for details on how to build it. +Notes for M1 users +~~~~~~~~~~~~~~~~~~ -Misc. Notes -=========== +Alongside the above, you will need to follow these additional steps to get it +running on Apple silicon. -.. _note-offline-builds: +Install an x86 copy of ``homebrew`` alongside your existing one. `This +stackoverflow answer `__ describes the +process. + +Follow the normal macOS steps to install ``cmake`` and ``gcc`` via your x86 copy of +``homebrew``. Note that this will install a GCC version newer than 7, so see +`osx-new-gcc-notes`. -Note on building DFHack offline -------------------------------- +In your terminal, ensure you have your path set to the correct homebrew in +addition to the normal ``CC`` and ``CXX`` flags above:: + + export PATH=/usr/local/bin:$PATH + +.. _note-offline-builds: +Building DFHack Offline +----------------------- As of 0.43.05, DFHack downloads several files during the build process, depending on your target OS and architecture. If your build machine's internet connection is unreliable, or nonexistent, you can download these files in advance. diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index ad065caa4..c6f69cc1b 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -26,10 +26,11 @@ System packages: * SDL (libsdl 1.2, not sdl2). * cmake +* git (required for `contributions`) * ccache (**optional**, but recommended to improve build times) * OpenGL headers (**optional**: to build `stonesense`) -* zlib (compression library used for `xlsxioreader`) -* build system +* zlib (compression library used for `xlsxioreader` -> `quickfort`) +* build system (e.g. gcc & ninja, or Visual Studio) .. maybe the below should be talked about next to the bullets @@ -45,10 +46,19 @@ These perl packages are used in code generation. DFHack has many structures that files to define and update these structures. Then during the configuration process [running cmake] these xml files are used to generate C++ source code to define these structures for use in plugins and scripts. +.. _pr-link: https://github.com/DFHack/dfhack/pulls + +Installing +---------- + +.. contents:: Install Instructions + :local: + :depth: 2 + .. _linux-dependency-instructions: Linux ------ +===== Here are some package install commands for various distributions: @@ -89,7 +99,7 @@ either by downloading it from `cmake.org `_ or through your package manager, if possible. Building 32-bit Binaries -======================== +~~~~~~~~~~~~~~~~~~~~~~~~ If you want to compile 32-bit DFHack on 64-bit distributions, you'll need the multilib development tools and libraries: @@ -113,7 +123,7 @@ to use ``lxc`` to .. _linux-incompatible-libstdcxx: Incompatible libstdc++ -====================== +~~~~~~~~~~~~~~~~~~~~~~ When compiling DFHack yourself, it builds against your system libstdc++. When Dwarf Fortress runs, it uses a libstdc++ shipped in the ``libs`` folder, which comes from GCC 4.8 and is incompatible with code compiled with newer GCC @@ -138,83 +148,10 @@ similar errors. This is why DFHack distributes both GCC 4.8 and GCC 7 builds. If you are planning on distributing binaries to other users, we recommend using an older GCC (but still at least 4.8) version if possible. -.. _mac-dependency-instructions: - -macOS ------ - -DFHack can officially be built on macOS only with GCC 4.8 or 7. Anything newer than 7 -will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-notes`), -and your build will likely not be redistributable. - -.. _osx-setup: - -Dependencies and system set-up -============================== - -#. Download and unpack a copy of the latest DF -#. Install Xcode from the Mac App Store - -#. Install the XCode Command Line Tools by running the following command:: - - xcode-select --install - -#. Install dependencies - - It is recommended to use Homebrew instead of MacPorts, as it is generally - cleaner, quicker, and smarter. For example, installing MacPort's GCC will - install more than twice as many dependencies as Homebrew's will, and all in - both 32-bit and 64-bit variants. Homebrew also doesn't require constant use - of ``sudo``. - - Using `Homebrew `_ (recommended):: - - brew tap homebrew/versions - brew install git - brew install cmake - brew install ninja - brew install gcc@7 - - Using `MacPorts `_:: - - sudo port install gcc7 +universal cmake +universal git-core +universal ninja +universal - - Macports will take some time - maybe hours. At some point it may ask - you to install a Java environment; let it do so. - -#. Install Perl dependencies - - * Using system Perl - - * ``sudo cpan`` - - If this is the first time you've run cpan, you will need to go through the setup - process. Just stick with the defaults for everything and you'll be fine. - - If you are running OS X 10.6 (Snow Leopard) or earlier, good luck! - You'll need to open a separate Terminal window and run:: - - sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml - - * ``install XML::LibXML`` - * ``install XML::LibXSLT`` - - * In a separate, local Perl install - - Rather than using system Perl, you might also want to consider - the Perl manager, `Perlbrew `_. - - This manages Perl 5 locally under ``~/perl5/``, providing an easy - way to install Perl and run CPAN against it without ``sudo``. - It can maintain multiple Perl installs and being local has the - benefit of easy migration and insulation from OS issues and upgrades. - - See https://perlbrew.pl/ for more details. - .. _windows-dependency-instructions: Windows -------- +======= On Windows, DFHack replaces the SDL library distributed with DF. For ABI compatibility with recent releases of Dwarf Fortress, DFHack requires the ``v140`` or ``v140_xp`` @@ -223,9 +160,7 @@ toolchain to build for windows. What you'll need is as follows: * Microsoft Visual C++ 2022, 2019, 2017, or 2015 (optional) -* ``v140`` or ``v140_xp`` toolchain - - * i.e. Microsoft Visual C++ 2015 Build Tools +* ``v140`` or ``v140_xp`` toolchain (Microsoft Visual C++ 2015 Build Tools) * Git (optional) * CMake * StrawberryPerl, OR CPAN (optional, see nested) @@ -243,7 +178,7 @@ with Dwarf Fortress, DFHack has to be compiled with the same compiler. Visual Studio 2015 is no longer supported by Microsoft and it can be difficult to obtain working installers for this product today. The recommended approach is to use a modern community -version of Visual Studio such as 2022 or 2019, installing additional optional Visual Studio +version of Visual Studio such as 2022_ or 2019_, installing additional optional Visual Studio components which provide the required support for using Visual Studio 2015's toolchain. All of the required tools are available from Microsoft as part of Visual Studio's Community Edition at no charge. @@ -253,37 +188,51 @@ Visual Studio to edit code. .. _build tools: https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downloads -Installing Dependencies -======================= - -It is recommended to use the package manager ``choco`` on windows for satisfying dependencies. -Install instructions for this command line tool can be found at https://chocolatey.org/install - -.. - Perhaps when the windows package manager - is more mature we can switch to it. - -.. contents:: Windows - :local: - :depth: 2 +With Choco +~~~~~~~~~~ +Many of the dependencies are simple enough to download and install via the +`chocolatey` package manager on the command line. -Common Dependencies -~~~~~~~~~~~~~~~~~~~ - -Installing with Choco -+++++++++++++++++++++ -To install the common dependencies:: +Here are some package install commands:: choco install cmake + choco install ccache choco install strawberryperl choco install python choco install sphinx + choco install visualstudio2022community + +.. _chocolatey-link: https://chocolatey.org/install -Installing Manually -+++++++++++++++++++ +Visual Studio +~~~~~~~~~~~~~ +You could install visual studio `manually`, perhaps even the build tools as well. +You could also just run a ``choco`` command. For example:: + + choco install visualstudio2022community + +If Visual Studio is installed follow these next steps for the build tools: + +1. Open **Visual Studio Installer**. +2. Select modify, for whichever version you've chosen to utilize. +3. Check the boxes for the following components: + +* "Desktop Development with C++" +* "C++ Windows XP Support for VS 2017 (v141) tools [Deprecated]" +* "MSVC v140 - VS 2015 C++ build tools (v14.00)" + +Yes, this is unintuitive. Installing XP Support for VS 2017 installs XP Support for VS 2015 +if the 2015 toolchain is installed. + +Manually +~~~~~~~~ If you prefer to install manually rather than using Chocolatey, details and requirements are as below. If you do install manually, please ensure you -have all executables searchable in your PATH variable. +have all **executables searchable in your PATH variable**. + +.. contents:: Windows + :local: + :depth: 1 CMake ^^^^^ @@ -328,42 +277,24 @@ See `sphinx-install` at https://www.sphinx-doc.org/ .. _sphinx-install: https://www.sphinx-doc.org/en/master/usage/installation.html -Visual Studio -~~~~~~~~~~~~~ +.. _install-visual-studio: +Visual Studio +^^^^^^^^^^^^^ Click Visual Studio 2022_ or 2019_ to download an installer wizard that will prompt you to select the optional tools you want to download alongside the IDE. You may need to log into (or create) a Microsoft account in order to download Visual Studio. -**OR** - -To install this toolchain in your visual studio, you'll need to have first installed visual studio. -For example:: - - choco install visualstudio2022community - -If Visual Studio is installed follow these next steps: - -1. Open **Visual Studio Installer**. -2. Select modify, for whichever version you've chosen to utilize. -3. Check the boxes for the following components: - -* "Desktop Development with C++" -* "C++ Windows XP Support for VS 2017 (v141) tools [Deprecated]" -* "MSVC v140 - VS 2015 C++ build tools (v14.00)" - -Yes, this is unintuitive. Installing XP Support for VS 2017 installs XP Support for VS 2015 -if the 2015 toolchain is installed. - .. _2022: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false .. _2019: https://my.visualstudio.com/Downloads?q=visual%20studio%202019&wt.mc_id=o~msft~vscom~older-downloads -Build Tools Only -~~~~~~~~~~~~~~~~ +Build Tools [Without Visual Studio] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Click `build tools`_ and you will be prompted to login to your Microsoft account. Then you should be redirected to a page with various download options with 2015 in their name. If this redirect doesn't occur, just copy, paste, and enter the download link again and you should see the options. You need to get: + Visual C++ Build Tools for Visual Studio 2015 with Update 3. Click the download button next to it and a dropdown of download formats will appear. Select the DVD format to download an ISO file. When the download is complete, @@ -399,3 +330,73 @@ XP. The ``v141`` toolchain, in Visual Studio 2017, has been empirically documented to be incompatible with released versions of Dwarf Fortress and cannot be used to make usable builds of DFHack. + +.. _mac-dependency-instructions: + +macOS +===== + +DFHack can officially be built on macOS only with GCC 4.8 or 7. Anything newer than 7 +will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-notes`), +and your build will likely not be redistributable. + +.. _osx-setup: + +#. Download and unpack a copy of the latest DF +#. Install Xcode from the Mac App Store + +#. Install the XCode Command Line Tools by running the following command:: + + xcode-select --install + +#. Install dependencies + + It is recommended to use Homebrew instead of MacPorts, as it is generally + cleaner, quicker, and smarter. For example, installing MacPort's GCC will + install more than twice as many dependencies as Homebrew's will, and all in + both 32-bit and 64-bit variants. Homebrew also doesn't require constant use + of ``sudo``. + + Using `Homebrew `_ (recommended):: + + brew tap homebrew/versions + brew install git + brew install cmake + brew install ninja + brew install gcc@7 + + Using `MacPorts `_:: + + sudo port install gcc7 +universal cmake +universal git-core +universal ninja +universal + + Macports will take some time - maybe hours. At some point it may ask + you to install a Java environment; let it do so. + +#. Install Perl dependencies + + * Using system Perl + + * ``sudo cpan`` + + If this is the first time you've run cpan, you will need to go through the setup + process. Just stick with the defaults for everything and you'll be fine. + + If you are running OS X 10.6 (Snow Leopard) or earlier, good luck! + You'll need to open a separate Terminal window and run:: + + sudo ln -s /usr/include/libxml2/libxml /usr/include/libxml + + * ``install XML::LibXML`` + * ``install XML::LibXSLT`` + + * In a separate, local Perl install + + Rather than using system Perl, you might also want to consider + the Perl manager, `Perlbrew `_. + + This manages Perl 5 locally under ``~/perl5/``, providing an easy + way to install Perl and run CPAN against it without ``sudo``. + It can maintain multiple Perl installs and being local has the + benefit of easy migration and insulation from OS issues and upgrades. + + See https://perlbrew.pl/ for more details. From a9467f76ef4f0fe3017323f6ec16c71b5c934f86 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 00:43:10 -0400 Subject: [PATCH 0007/2222] Fixing links, mostly --- docs/dev/Dev-intro.rst | 2 +- docs/dev/building/Compile.rst | 120 ++++++----------------------- docs/dev/building/Dependencies.rst | 29 ++++--- 3 files changed, 37 insertions(+), 114 deletions(-) diff --git a/docs/dev/Dev-intro.rst b/docs/dev/Dev-intro.rst index 212b2888a..8ed7badc7 100644 --- a/docs/dev/Dev-intro.rst +++ b/docs/dev/Dev-intro.rst @@ -17,7 +17,7 @@ Other pages that may be relevant include: .. contents:: Contents :local: -.. _architecture +.. _architectural-diagrams: Architecture diagrams --------------------- diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index a0a9b070c..8b4ea0b78 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -9,7 +9,7 @@ Compilation DFHack builds are available for all supported platforms; see `installing` for installation instructions. If you are a DFHack end-user, modder, or plan on writing scripts [lua] (not plugins), it is generally recommended (and easier) to use -these `builds` instead of compiling DFHack from source. +these `builds `_ instead of compiling DFHack from source. However, if you are looking to develop plugins, work on the DFHack core, make complex changes to DF-structures, or anything else that requires compiling @@ -17,8 +17,6 @@ DFHack from source, this document will walk you through the build process. Note that some steps may be unconventional compared to other projects, so be sure to pay close attention if this is your first time compiling DFHack. -.. _build-releases:https://github.com/DFHack/dfhack/releases - .. contents:: Contents :local: :depth: 2 @@ -109,7 +107,7 @@ This section describes build configuration options that apply to all platforms. If you don't have a working build environment set up yet, follow the instructions in the platform-specific sections below first, then come back here. -Be sure to check out common `compile options `. +Be sure to check out common `build options `. Generator --------- @@ -176,35 +174,38 @@ See our page on `build options` Instructions ============ +.. contents:: + :local: + :depth: 1 + .. _compile-linux: Linux ----- On Linux, DFHack acts as a library that shadows parts of the SDL API using LD_PRELOAD. -Dependencies -~~~~~~~~~~~~ -You also need zlib, libsdl (1.2, not sdl2, like DF), perl, and the XML::LibXML -and XML::LibXSLT perl packages (for the code generation parts). You should be -able to find them in your distribution's repositories. - -To build `stonesense`, you'll also need OpenGL headers. - -Here are some package install commands for various distributions: - -* On Arch linux: - - * For the required Perl modules: ``perl-xml-libxml`` and ``perl-xml-libxslt`` (or through ``cpan``) +Build +~~~~~ +Building is fairly straightforward. Enter the ``build`` folder (or create an +empty folder in the DFHack directory to use instead) and start the build like this:: -* On Ubuntu:: + cd build + cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX= + ninja install # or ninja -jX install to specify the number of cores (X) to use - apt-get install gcc cmake ninja-build git zlib1g-dev libsdl1.2-dev libxml-libxml-perl libxml-libxslt-perl + should be a path to a copy of Dwarf Fortress, of the appropriate +version for the DFHack you are building. This will build the library along +with the normal set of plugins and install them into your DF folder. - * Other Debian-based distributions should have similar requirements. +Alternatively, you can use ccmake instead of cmake:: -* On Fedora:: + cd build + ccmake .. -G Ninja + ninja install - yum install gcc-c++ cmake ninja-build git zlib-devel SDL-devel perl-core perl-XML-LibXML perl-XML-LibXSLT ruby +This will show a curses-based interface that lets you set all of the +extra options. You can also use a cmake-friendly IDE like KDevelop 4 +or the cmake-gui program. Windows cross compiling from Linux ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -291,79 +292,6 @@ host when you want to reattach:: If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. That will handle all the wineserver management for you. -Multilib dependencies -~~~~~~~~~~~~~~~~~~~~~ -If you want to compile 32-bit DFHack on 64-bit distributions, you'll need the -multilib development tools and libraries: - -* ``gcc-multilib`` and ``g++-multilib`` -* If you have installed a non-default version of GCC - for example, GCC 4.8 on a - distribution that defaults to 5.x - you may need to add the version number to - the multilib packages. - - * For example, ``gcc-4.8-multilib`` and ``g++-4.8-multilib`` if installing for GCC 4.8 - on a system that uses a later GCC version. - * This is definitely required on Ubuntu/Debian, check if using a different distribution. - -* ``zlib1g-dev:i386`` (or a similar i386 zlib-dev package) - -Note that installing a 32-bit GCC on 64-bit systems (e.g. ``gcc:i386`` on -Debian) will typically *not* work, as it depends on several other 32-bit -libraries that conflict with system libraries. Alternatively, you might be able -to use ``lxc`` to -:forums:`create a virtual 32-bit environment <139553.msg5435310#msg5435310>`. - -Build -~~~~~ -Building is fairly straightforward. Enter the ``build`` folder (or create an -empty folder in the DFHack directory to use instead) and start the build like this:: - - cd build - cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX= - ninja install # or ninja -jX install to specify the number of cores (X) to use - - should be a path to a copy of Dwarf Fortress, of the appropriate -version for the DFHack you are building. This will build the library along -with the normal set of plugins and install them into your DF folder. - -Alternatively, you can use ccmake instead of cmake:: - - cd build - ccmake .. -G Ninja - ninja install - -This will show a curses-based interface that lets you set all of the -extra options. You can also use a cmake-friendly IDE like KDevelop 4 -or the cmake-gui program. - -.. _linux-incompatible-libstdcxx: - -Incompatible libstdc++ -~~~~~~~~~~~~~~~~~~~~~~ -When compiling DFHack yourself, it builds against your system libstdc++. When -Dwarf Fortress runs, it uses a libstdc++ shipped in the ``libs`` folder, which -comes from GCC 4.8 and is incompatible with code compiled with newer GCC -versions. As of DFHack 0.42.05-alpha1, the ``dfhack`` launcher script attempts -to fix this by automatically removing the DF-provided libstdc++ on startup. -In rare cases, this may fail and cause errors such as: - -.. code-block:: text - - ./libs/Dwarf_Fortress: /pathToDF/libs/libstdc++.so.6: version - `GLIBCXX_3.4.18' not found (required by ./hack/libdfhack.so) - -The easiest way to fix this is generally removing the libstdc++ shipped with -DF, which causes DF to use your system libstdc++ instead:: - - cd /path/to/DF/ - rm libs/libstdc++.so.6 - -Note that distributing binaries compiled with newer GCC versions may result in -the opposite compatibility issue: users with *older* GCC versions may encounter -similar errors. This is why DFHack distributes both GCC 4.8 and GCC 7 builds. If -you are planning on distributing binaries to other users, we recommend using an -older GCC (but still at least 4.8) version if possible. - .. _compile-windows: Windows @@ -507,8 +435,6 @@ Building Notes for GCC 8+ or OS X 10.10+ users ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If none of these situations apply to you, skip to `osx-setup`. - If you have issues building on OS X 10.10 (Yosemite) or above, try defining the following environment variable:: diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index c6f69cc1b..a85110c70 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -26,16 +26,16 @@ System packages: * SDL (libsdl 1.2, not sdl2). * cmake -* git (required for `contributions`) +* git (required for `contributions `_) * ccache (**optional**, but recommended to improve build times) * OpenGL headers (**optional**: to build `stonesense`) -* zlib (compression library used for `xlsxioreader` -> `quickfort`) +* zlib (compression library used for `xlsxreader-api` -> `quickfort`) * build system (e.g. gcc & ninja, or Visual Studio) .. maybe the below should be talked about next to the bullets -**SDL** is used as an injection point which you can see more about in DFHack's `architecture` documentation & diagrams. +**SDL** is used as an injection point which you can see more about in DFHack's `architectural ` documentation & diagrams. Perl packages: @@ -46,12 +46,11 @@ These perl packages are used in code generation. DFHack has many structures that files to define and update these structures. Then during the configuration process [running cmake] these xml files are used to generate C++ source code to define these structures for use in plugins and scripts. -.. _pr-link: https://github.com/DFHack/dfhack/pulls Installing ---------- -.. contents:: Install Instructions +.. contents:: :local: :depth: 2 @@ -168,9 +167,9 @@ What you'll need is as follows: * Perl (required) * XML:LibXML (required) * XML:LibXLST (required) -* `Python ` (required for documentation, optional otherwise) +* `Python`_ (required for documentation, optional otherwise) - * `Sphinx ` + * `Sphinx`_ Releases of Dwarf Fortress since roughly 2016 have been compiled for Windows using Microsoft's Visual Studio 2015 C++ compiler. In order to guarantee ABI and STL compatibility @@ -191,7 +190,7 @@ Visual Studio to edit code. With Choco ~~~~~~~~~~ Many of the dependencies are simple enough to download and install via the -`chocolatey` package manager on the command line. +`chocolatey`_ package manager on the command line. Here are some package install commands:: @@ -202,7 +201,7 @@ Here are some package install commands:: choco install sphinx choco install visualstudio2022community -.. _chocolatey-link: https://chocolatey.org/install +.. _chocolatey: https://chocolatey.org/install Visual Studio ~~~~~~~~~~~~~ @@ -230,7 +229,7 @@ If you prefer to install manually rather than using Chocolatey, details and requirements are as below. If you do install manually, please ensure you have all **executables searchable in your PATH variable**. -.. contents:: Windows +.. contents:: :local: :depth: 1 @@ -267,15 +266,15 @@ install XML::LibXML and XML::LibXSLT for it using CPAN. Python ^^^^^^ -See `python-install`. +See the `Python`_ website. -.. _python-install: https://www.python.org/downloads/ +.. _Python: https://www.python.org/downloads/ Sphinx ^^^^^^ -See `sphinx-install` at https://www.sphinx-doc.org/ +See the `Sphinx`_ website. -.. _sphinx-install: https://www.sphinx-doc.org/en/master/usage/installation.html +.. _Sphinx: https://www.sphinx-doc.org/en/master/usage/installation.html .. _install-visual-studio: @@ -340,8 +339,6 @@ DFHack can officially be built on macOS only with GCC 4.8 or 7. Anything newer t will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-notes`), and your build will likely not be redistributable. -.. _osx-setup: - #. Download and unpack a copy of the latest DF #. Install Xcode from the Mac App Store From 66f8a0207c1c71a412920847ed8af02908822dcd Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 01:05:31 -0400 Subject: [PATCH 0008/2222] Updates dependency doc --- docs/dev/building/Dependencies.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index a85110c70..4da7afce7 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -26,6 +26,10 @@ System packages: * SDL (libsdl 1.2, not sdl2). * cmake +* Perl +* Python + + * Sphinx * git (required for `contributions `_) * ccache (**optional**, but recommended to improve build times) * OpenGL headers (**optional**: to build `stonesense`) @@ -33,7 +37,7 @@ System packages: * build system (e.g. gcc & ninja, or Visual Studio) .. - maybe the below should be talked about next to the bullets + maybe the below should be talked about next to the bullet point?? **SDL** is used as an injection point which you can see more about in DFHack's `architectural ` documentation & diagrams. @@ -152,24 +156,15 @@ older GCC (but still at least 4.8) version if possible. Windows ======= -On Windows, DFHack replaces the SDL library distributed with DF. For ABI compatibility with recent releases of Dwarf Fortress, DFHack requires the ``v140`` or ``v140_xp`` toolchain to build for windows. -What you'll need is as follows: +Of course all dependencies are listed above, but here are some things you'll likely want on Windows +to avoid risk of wading into uncharted waters: * Microsoft Visual C++ 2022, 2019, 2017, or 2015 (optional) * ``v140`` or ``v140_xp`` toolchain (Microsoft Visual C++ 2015 Build Tools) -* Git (optional) -* CMake -* StrawberryPerl, OR CPAN (optional, see nested) - - * Perl (required) - * XML:LibXML (required) - * XML:LibXLST (required) -* `Python`_ (required for documentation, optional otherwise) - - * `Sphinx`_ +* StrawberryPerl (perl + perl packages) Releases of Dwarf Fortress since roughly 2016 have been compiled for Windows using Microsoft's Visual Studio 2015 C++ compiler. In order to guarantee ABI and STL compatibility @@ -201,6 +196,11 @@ Here are some package install commands:: choco install sphinx choco install visualstudio2022community +You may have noticed this list **does not include** the build tools, one of the build tool packages +in the chocolatey `package repository `_ may work for our purposes +but the tried and true method is just below in the **next section**. If you verify a package works feel free +to open an issue, or update this documentation. + .. _chocolatey: https://chocolatey.org/install Visual Studio From 77b6dd2f7a09ce397231f12bb38df42f1797c65d Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 12:58:43 -0400 Subject: [PATCH 0009/2222] Updates minor things --- docs/dev/building/Dependencies.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index 4da7afce7..e3cc08fbf 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -243,8 +243,8 @@ to your binary search PATH so the tool can be later run from anywhere. Perl / Strawberry Perl ^^^^^^^^^^^^^^^^^^^^^^ For the code generation stage of the build process, you'll need Perl 5 with -XML::LibXML and XML::LibXSLT. `Strawberry Perl `_ is -recommended as it includes all of the required packages in a single, easy +**XML::LibXML** and **XML::LibXSLT**. `Strawberry Perl `_ is +recommended as it includes all of the required packages in a single easy install. After install, ensure Perl is in your user's PATH. This can be edited from @@ -255,7 +255,7 @@ The following directories must be in your PATH, in this order: * ``\c\bin`` * ``\perl\site\bin`` * ``\perl\bin`` -* ``\perl\vendor\lib\auto\XML\LibXML`` (may only be required on some systems) +* ``\perl\vendor\lib\auto\XML\LibXML`` (path may only be required on some systems) Be sure to close and re-open any existing ``cmd.exe`` windows after updating your PATH. From 98badfbd72f32d5ba5f944a95ad110b545f903c9 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 15:22:24 -0400 Subject: [PATCH 0010/2222] possibly the last expected changes --- docs/dev/Memory-research.rst | 4 +- docs/dev/building/Compile.rst | 62 ---------------- docs/dev/building/Options.rst | 133 ++++++++++++++++++++++++++-------- 3 files changed, 105 insertions(+), 94 deletions(-) diff --git a/docs/dev/Memory-research.rst b/docs/dev/Memory-research.rst index c4c7aeb63..2ee47bb05 100644 --- a/docs/dev/Memory-research.rst +++ b/docs/dev/Memory-research.rst @@ -50,7 +50,7 @@ Plugins There are a few development plugins useful for low-level memory research. They are not built by default, but can be built by setting the ``BUILD_DEVEL`` -`CMake option `. These include: +`CMake option `. These include: - ``check-structures-sanity``, which performs sanity checks on the given DF object. Note that this will crash in several cases, some intentional, so using @@ -85,7 +85,7 @@ You should not count on DF being stable when using this. DFHack's implementation of sizecheck is currently only tested on Linux, although it probably also works on macOS. It can be built with the ``BUILD_SIZECHECK`` -`CMake option `, which produces a ``libsizecheck`` +`CMake option `, which produces a ``libsizecheck`` library installed in the ``hack`` folder. On Linux, passing ``--sc`` as the first argument to the ``dfhack`` launcher script will load this library on startup. On other platforms, or when passing a different argument to the diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index 8b4ea0b78..cbc4326c0 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -109,68 +109,6 @@ in the platform-specific sections below first, then come back here. Be sure to check out common `build options `. -Generator ---------- - -The ``Ninja`` CMake build generator is the preferred build method on Linux and -macOS, instead of ``Unix Makefiles``, which is the default. You can select Ninja -by passing ``-G Ninja`` to CMake. Incremental builds using Unix Makefiles can be -much slower than Ninja builds. Note that you will probably need to install -Ninja; see the platform-specific sections for details. - -:: - - cmake .. -G Ninja - -.. warning:: - - Most other CMake settings can be changed by running ``cmake`` again, but the - generator cannot be changed after ``cmake`` has been run without creating a - new build folder. Do not forget to specify this option. - - CMake versions 3.6 and older, and possibly as recent as 3.9, are known to - produce project files with dependency cycles that fail to build - (see :issue:`1369`). Obtaining a recent version of CMake is recommended, either from - `cmake.org `_ or through a package manager. See - the sections below for more platform-specific directions for installing CMake. - -Build type ----------- - -``cmake`` allows you to pick a build type by changing the ``CMAKE_BUILD_TYPE`` variable:: - - cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE - -Valid and useful build types include 'Release' and 'RelWithDebInfo'. The default -build type is 'Release'. - -Target architecture (32-bit vs. 64-bit) ---------------------------------------- - -Set DFHACK_BUILD_ARCH to either ``32`` or ``64`` to build a 32-bit or 64-bit -version of DFHack (respectively). The default is currently ``64``, so you will -need to specify this explicitly for 32-bit builds. Specifying it is a good idea -in any case. - -:: - - cmake .. -DDFHACK_BUILD_ARCH=32 - -*or* -:: - - cmake .. -DDFHACK_BUILD_ARCH=64 - -Note that the scripts in the "build" folder on Windows will set the architecture -automatically. - -.. _compile-build-options: - -Other settings --------------- -See our page on `build options` - - Instructions ============ diff --git a/docs/dev/building/Options.rst b/docs/dev/building/Options.rst index 91ad51778..483f02434 100644 --- a/docs/dev/building/Options.rst +++ b/docs/dev/building/Options.rst @@ -4,6 +4,10 @@ Build Options ############# +.. contents:: Typical Options + :local: + :depth: 1 + There are a variety of other settings which you can find in CMakeCache.txt in your build folder or by running ``ccmake`` (or another CMake GUI). Most DFHack-specific settings begin with ``BUILD_`` and control which parts of DFHack @@ -12,10 +16,18 @@ are built. Typical usage may look like:: # Plugin development with updated documentation - cmake . -G Ninja -B builds/debug-info/ -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE:string=RelWithDebInfo -DBUILD_DOCS:bool=ON -DBUILD_PLUGINS=1 + cmake ./ -G Ninja -B builds/debug-info/ -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE:string=RelWithDebInfo -DBUILD_DOCS:bool=ON -DBUILD_PLUGINS=1 # Core DFHack only cmake ../ -G Ninja -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE:string=RelWithDebInfo -DBUILD_TESTS -DBUILD_DOCS:0 -DBUILD_PLUGINS=0 +.. admonition:: Modifying Build Options + + You can typically run new cmake commands from your build directory to turn on/off options. + Of course the generator selection is not something you can change, but the rest are. + + Additionally, you can edit the build settings in CMakeCache.txt. You also have cmake's + configuration utility ``ccmake``. + Generator --------- For the uninitiated, the generator is what allows cmake to, of course, generate @@ -29,51 +41,112 @@ visual studio, and match that to your system's generator list viewed with ``cmak example:: - cmake . -G "Visual Studio 17 2022" + cmake .. -G "Visual Studio 17 2022" Ninja ===== The generally preferred build system where available. -Core ----- -todo: +example:: + + cmake .. -G Ninja + +Install Location +---------------- +This of course uses the default cmake variable. + +Variable: ``CMAKE_INSTALL_PREFIX`` + +Usage:: + + cmake .. -DCMAKE_INSTALL_PREFIX= + +The path to df will of course depend on your system. If the directory exists it is +recommended to use ``~/.dwarffortress`` to avoid permission troubles. + +Build type +---------- +Release/Debug, this is the type of build you want. This controls what information +about symbols and line numbers the debugger will have available to it. + +Variable: ``CMAKE_BUILD_TYPE`` + +Usage:: + + cmake .. -DCMAKE_BUILD_TYPE:string=RelWithDebugInfo + +Options: + +* Release +* RelWithDebugInfo + +Target architecture (32/64-bit) +--------------------------------------- +If need 32-bit binaries or are looking to be explicit about building 64-bit. + +Variable: ``DFHACK_BUILD_ARCH`` + +Usage:: + + cmake .. -DDFHACK_BUILD_ARCH=32 + +Options: + +* '32' +* '64' (default option) + +Library +------- +This will only be useful if you're looking to avoid building the library core, as it builds by default. + +Variable: ``BUILD_LIBRARY`` + +Usage:: + + cmake .. -DBUILD_LIBRARY:bool=OFF + cmake .. -DBUILD_LIBRARY=0 + +Testing +------- +Regression testing will be arriving in the future, but for now there are only tests written in lua. + +Variables: + +* ``BUILD_TESTING`` (will build unit tests, in the future) +* ``BUILD_TESTS`` (installs lua tests) + +Usage:: + + cmake .. -DBUILD_TESTS:bool=ON + cmake .. -DBUILD_TESTS=1 Plugins ------- -todo: +If you're doing plugin development. + +Variable: ``BUILD_PLUGINS`` + +Usage:: + + cmake .. -DBUILD_PLUGINS:bool=ON + cmake .. -DBUILD_PLUGINS=1 .. _building-documentation: Documentation ------------- +If you need to build documentation. Documentation can be built as HTML, and PDF, +but there are also plain text files generated for in-game. -Enabling the ``BUILD_DOCS`` CMake option will cause the documentation to be built -whenever it changes as part of the normal DFHack build process. There are several -ways to do this: - -* When initially running CMake, add ``-DBUILD_DOCS:bool=ON`` to your ``cmake`` - command. For example:: - - cmake .. -DCMAKE_BUILD_TYPE:string=Release -DBUILD_DOCS:bool=ON -DCMAKE_INSTALL_PREFIX= +Variable: ``BUILD_DOCS`` -* If you have already run CMake, you can simply run it again from your build - folder to update your configuration:: +Usage:: cmake .. -DBUILD_DOCS:bool=ON + cmake .. -DBUILD_DOCS=1 -* You can edit the ``BUILD_DOCS`` setting in CMakeCache.txt directly - -* You can use the CMake GUI or ``ccmake`` to change the ``BUILD_DOCS`` setting - -* On Windows, if you prefer to use the batch scripts, you can run - ``generate-msvc-gui.bat`` and set ``BUILD_DOCS`` through the GUI. If you are - running another file, such as ``generate-msvc-all.bat``, you will need to edit - the batch script to add the flag. You can also run ``cmake`` on the command line, - similar to other platforms. -By default, both HTML and text docs are built by CMake. The generated -documentation is stored in ``docs/html`` and ``docs/text`` (respectively) in the -root DFHack folder, and they will both be installed to ``hack/docs`` when you -install DFHack. The html and txt files will intermingle, but will not interfere -with one another. +The generated documentation is stored in ``docs/html`` and ``docs/text`` (respectively) +in the root DFHack folder, and they will both be installed to ``hack/docs`` when you +install DFHack. The html and txt files will intermingle, but will not interfere with +one another. From 430917ce5dc58fbbc103e322dcb4e2e4a35a4746 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 12:06:15 -0800 Subject: [PATCH 0011/2222] Update docs/dev/building/Options.rst Co-authored-by: Myk --- docs/dev/building/Options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/building/Options.rst b/docs/dev/building/Options.rst index 483f02434..b1c1a209b 100644 --- a/docs/dev/building/Options.rst +++ b/docs/dev/building/Options.rst @@ -73,7 +73,7 @@ Variable: ``CMAKE_BUILD_TYPE`` Usage:: - cmake .. -DCMAKE_BUILD_TYPE:string=RelWithDebugInfo + cmake .. -DCMAKE_BUILD_TYPE:string=RelWithDebInfo Options: From 3fb249bc2acb4af7d61fb4637577afbb1ab4f8b5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 12:06:32 -0800 Subject: [PATCH 0012/2222] Update docs/dev/building/Options.rst Co-authored-by: Myk --- docs/dev/building/Options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/building/Options.rst b/docs/dev/building/Options.rst index b1c1a209b..8de88a0e3 100644 --- a/docs/dev/building/Options.rst +++ b/docs/dev/building/Options.rst @@ -78,7 +78,7 @@ Usage:: Options: * Release -* RelWithDebugInfo +* RelWithDebInfo Target architecture (32/64-bit) --------------------------------------- From 6baac102625856961c7ce8ddb2a5c555dde02c72 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 12:06:48 -0800 Subject: [PATCH 0013/2222] Update docs/dev/building/Compile.rst Co-authored-by: Myk --- docs/dev/building/Compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index cbc4326c0..df6023767 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -177,7 +177,7 @@ run Dwarf Fortress from the container and have it display on your host. Step 2: build DFHack ^^^^^^^^^^^^^^^^^^^^ -The ``docker run`` command above will give you a shell prompt (as root) in the +The ``docker run`` command above will give you a shell prompt (as the `buildmaster` user) in the container. Inside the container, run the following commands:: git clone https://github.com/DFHack/dfhack.git From e57ef4315dc1038cd53a69cb29526162564a1eb3 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 12:07:01 -0800 Subject: [PATCH 0014/2222] Update docs/dev/building/Compile.rst Co-authored-by: Myk --- docs/dev/building/Compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index df6023767..c5740d094 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -201,7 +201,7 @@ assets:: mkdir ~/df -If you can just downlaod Dwarf Fortress directly into the container, then that's fine. +If you can just download Dwarf Fortress directly into the container, then that's fine. Otherwise, you can do something like this in your host Linux environment to copy an installed version to the container:: From 994cb30f4dbf78d47d05bb3164d05fdb63860419 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 12:07:12 -0800 Subject: [PATCH 0015/2222] Update docs/dev/building/Compile.rst Co-authored-by: Myk --- docs/dev/building/Compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index c5740d094..2de2735b2 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -227,7 +227,7 @@ host when you want to reattach:: docker start -ai dfhack-win -If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. +If you edit code and need to rebuild, run ``dfhack-make install``. That will handle all the wineserver management for you. .. _compile-windows: From 1993291a873338c833c42a1e497bcf89af1dfcbe Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 17:33:43 -0400 Subject: [PATCH 0016/2222] stuff --- docs/dev/building/Compile.rst | 230 +++++++++++++++------------------- 1 file changed, 103 insertions(+), 127 deletions(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index 2de2735b2..cedf7e210 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -86,44 +86,23 @@ assistance. are also able to help with any submodule-related (or Git-related) issues you may encounter. -Dependencies ------------- - -If you haven't already checked, this would be a good point to ensure you have -all the `dependencies `. - - -Contributing to DFHack ----------------------- - -For details on contributing to DFHack, including pull requests, code -format, and more, please see `contributing-code`. - +All Platforms +============= +Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this, +but from command line is the usual way to do this; thought the windows section below points out some +windows batch files that can be used to avoid opening a terminal/command-prompt. -Build settings -============== - -This section describes build configuration options that apply to all platforms. -If you don't have a working build environment set up yet, follow the instructions -in the platform-specific sections below first, then come back here. - -Be sure to check out common `build options `. - -Instructions -============ - -.. contents:: - :local: - :depth: 1 +You should seek cmake's documentation online or via ``cmake --help`` to see how the command works. See +the `build-options` page for help finding the DFHack build options relevant to you. .. _compile-linux: Linux ------ +===== On Linux, DFHack acts as a library that shadows parts of the SDL API using LD_PRELOAD. Build -~~~~~ +----- Building is fairly straightforward. Enter the ``build`` folder (or create an empty folder in the DFHack directory to use instead) and start the build like this:: @@ -145,97 +124,10 @@ This will show a curses-based interface that lets you set all of the extra options. You can also use a cmake-friendly IDE like KDevelop 4 or the cmake-gui program. -Windows cross compiling from Linux -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. highlight:: bash - -If you are on Linux but need to produce a Windows build (for example, because the -DF version you're working on isn't out for Linux yet), here is how you can build -and run a Windows binary on Linux. - -.. contents:: - :local: - :depth: 1 - -Step 1: prepare a docker image -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -On your Linux host, install and run the docker daemon and then run these commands:: - - xhost +local:root - git clone https://github.com/BenLubar/build-env.git - cd build-env/msvc - docker build . - docker image ls - IMAGE_ID= - docker run -it --env="DISPLAY" --env="QT_X11_NO_MITSHM=1" --volume=/tmp/.X11-unix:/tmp/.X11-unix --user buildmaster --name dfhack-win $IMAGE_ID - -The ``xhost`` command and ``--env`` parameters are there so you can eventually -run Dwarf Fortress from the container and have it display on your host. - -Step 2: build DFHack -^^^^^^^^^^^^^^^^^^^^ - -The ``docker run`` command above will give you a shell prompt (as the `buildmaster` user) in the -container. Inside the container, run the following commands:: - - git clone https://github.com/DFHack/dfhack.git - cd dfhack - git submodule update --init - cd build - dfhack-configure windows 64 Release - dfhack-make - -Inside the ``dfhack-*`` scripts there are several commands that set up the wine -server. Each invocation of a windows tool will cause wine to run in the container. -Preloading the wineserver and telling it not to exit will speed configuration and -compilation up considerably (approx. 10x). You can configure and build DFHack -with regular ``cmake`` and ``ninja`` commands, but your build will go much slower. - -Step 3: copy Dwarf Fortress to the container -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -First, create a directory in the container to house the Dwarf Fortress binary and -assets:: - - mkdir ~/df - -If you can just download Dwarf Fortress directly into the container, then that's fine. -Otherwise, you can do something like this in your host Linux environment to copy an -installed version to the container:: - - cd ~/.steam/steam/steamapps/common/Dwarf\ Fortress/ - docker cp . dfhack-win:df/ - -Step 4: install DFHack and run DF -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Back in the container, run the following commands:: - - cd dfhack/build - cmake .. -DCMAKE_INSTALL_PREFIX=/home/buildmaster/df - ninja install - cd ~/df - wine64 "Dwarf Fortress.exe" - -Other notes -^^^^^^^^^^^ - -Closing your shell will kick you out of the container. Run this command on your Linux -host when you want to reattach:: - - docker start -ai dfhack-win - -If you edit code and need to rebuild, run ``dfhack-make install``. -That will handle all the wineserver management for you. - .. _compile-windows: Windows -------- -Build -~~~~~ +======= There are several different batch files in the ``win32`` and ``win64`` subfolders in the ``build`` folder, along with a script that's used for picking the DF path. Use the subfolder corresponding to the architecture that you want @@ -263,8 +155,8 @@ solution file(s): Then you can either open the solution with MSVC or use one of the msbuild scripts. -Building/installing from the Visual Studio IDE -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Visual Studio IDE +----------------- After running the CMake generate script you will have a new folder called VC2015 or VC2015_32, depending on the architecture you specified. Open the file ``dfhack.sln`` inside that folder. If you have multiple versions of Visual @@ -279,8 +171,8 @@ or ``RelWithDebInfo``. Then build the ``INSTALL`` target listed under ``CMakePredefinedTargets``. -Building/installing from the command line -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Command Line +------------ In the build directory you will find several ``.bat`` files: * Scripts with ``build`` prefix will only build DFHack. @@ -324,7 +216,7 @@ files as detailed above. .. _compile-macos: macOS ------ +===== DFHack functions similarly on macOS and Linux, and the majority of the information above regarding the build process (CMake and Ninja) applies here as well. @@ -334,7 +226,7 @@ will require you to perform extra steps to get DFHack to run (see `osx-new-gcc-n and your build will likely not be redistributable. Building -~~~~~~~~ +-------- * Get the DFHack source as per section `compile-how-to-get-the-code`, above. * Set environment variables @@ -371,7 +263,7 @@ Building .. _osx-new-gcc-notes: Notes for GCC 8+ or OS X 10.10+ users -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------- If you have issues building on OS X 10.10 (Yosemite) or above, try defining the following environment variable:: @@ -400,7 +292,7 @@ recommended to use GCC 4.8 or 7. .. _osx-m1-notes: Notes for M1 users -~~~~~~~~~~~~~~~~~~ +------------------ Alongside the above, you will need to follow these additional steps to get it running on Apple silicon. @@ -418,10 +310,94 @@ addition to the normal ``CC`` and ``CXX`` flags above:: export PATH=/usr/local/bin:$PATH +Docker +====== + +.. highlight:: bash + +You can use docker to build DFHack for windows. These instructions were developed +on a linux host system. + +.. contents:: + :local: + :depth: 1 + +Step 1: prepare a docker image +------------------------------ + +On your Linux host, install and run the docker daemon and then run these commands:: + + xhost +local:root + git clone https://github.com/BenLubar/build-env.git + cd build-env/msvc + docker build . + docker image ls + IMAGE_ID= + docker run -it --env="DISPLAY" --env="QT_X11_NO_MITSHM=1" --volume=/tmp/.X11-unix:/tmp/.X11-unix --user buildmaster --name dfhack-win $IMAGE_ID + +The ``xhost`` command and ``--env`` parameters are there so you can eventually +run Dwarf Fortress from the container and have it display on your host. + +Step 2: build DFHack +-------------------- + +The ``docker run`` command above will give you a shell prompt (as the `buildmaster` user) in the +container. Inside the container, run the following commands:: + + git clone https://github.com/DFHack/dfhack.git + cd dfhack + git submodule update --init + cd build + dfhack-configure windows 64 Release + dfhack-make + +Inside the ``dfhack-*`` scripts there are several commands that set up the wine +server. Each invocation of a windows tool will cause wine to run in the container. +Preloading the wineserver and telling it not to exit will speed configuration and +compilation up considerably (approx. 10x). You can configure and build DFHack +with regular ``cmake`` and ``ninja`` commands, but your build will go much slower. + +Step 3: copy Dwarf Fortress to the container +-------------------------------------------- + +First, create a directory in the container to house the Dwarf Fortress binary and +assets:: + + mkdir ~/df + +If you can just download Dwarf Fortress directly into the container, then that's fine. +Otherwise, you can do something like this in your host Linux environment to copy an +installed version to the container:: + + cd ~/.steam/steam/steamapps/common/Dwarf\ Fortress/ + docker cp . dfhack-win:df/ + +Step 4: install DFHack and run DF +--------------------------------- + +Back in the container, run the following commands:: + + cd dfhack/build + cmake .. -DCMAKE_INSTALL_PREFIX=/home/buildmaster/df + ninja install + cd ~/df + wine64 "Dwarf Fortress.exe" + +Other notes +----------- + +Closing your shell will kick you out of the container. Run this command on your Linux +host when you want to reattach:: + + docker start -ai dfhack-win + +If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. +That will handle all the wineserver management for you. + .. _note-offline-builds: Building DFHack Offline ------------------------ +======================= As of 0.43.05, DFHack downloads several files during the build process, depending on your target OS and architecture. If your build machine's internet connection is unreliable, or nonexistent, you can download these files in advance. From 7433cb463d5128da7056a8c7708c8fbcd4ad9143 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 23 Dec 2022 17:38:03 -0400 Subject: [PATCH 0017/2222] more stuff --- docs/dev/building/Compile.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index cedf7e210..80e88f708 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -95,6 +95,9 @@ windows batch files that can be used to avoid opening a terminal/command-prompt. You should seek cmake's documentation online or via ``cmake --help`` to see how the command works. See the `build-options` page for help finding the DFHack build options relevant to you. +Before compiling code, you'll of course need code to compile. This **will include** the submodules, so +be sure you've read the section about getting the code. + .. _compile-linux: Linux From 947863750eb072b3591899bedce0c068c9b5e8b4 Mon Sep 17 00:00:00 2001 From: Rose Date: Mon, 26 Dec 2022 16:57:19 -0800 Subject: [PATCH 0018/2222] Comment out all adventure mode stuff. The game doesn't support it anymore anyway, and it will sureley change when it does. --- .../adventure_control.cpp | 490 +++++++++--------- 1 file changed, 245 insertions(+), 245 deletions(-) diff --git a/plugins/remotefortressreader/adventure_control.cpp b/plugins/remotefortressreader/adventure_control.cpp index 6fb592d62..ff964454b 100644 --- a/plugins/remotefortressreader/adventure_control.cpp +++ b/plugins/remotefortressreader/adventure_control.cpp @@ -39,279 +39,279 @@ void SetCoord(df::coord in, RemoteFortressReader::Coord *out) command_result MoveCommand(DFHack::color_ostream &stream, const MoveCommandParams *in) { - auto viewScreen = getCurViewscreen(); - if (!in->has_direction()) - return CR_WRONG_USAGE; - if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) - return CR_OK; - auto dir = in->direction(); - switch (dir.x()) - { - case -1: - switch (dir.y()) - { - case -1: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_NW_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_NW); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_NW_UP); - break; - } - break; - case 0: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_W_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_W); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_W_UP); - break; - } - break; - case 1: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_SW_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_SW); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_SW_UP); - break; - } - break; - } - break; - case 0: - switch (dir.y()) - { - case -1: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_N_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_N); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_N_UP); - break; - } - break; - case 0: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_DOWN); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_UP); - break; - } - break; - case 1: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_S_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_S); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_S_UP); - break; - } - break; - } - break; - case 1: - switch (dir.y()) - { - case -1: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_NE_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_NE); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_NE_UP); - break; - } - break; - case 0: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_E_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_E); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_E_UP); - break; - } - break; - case 1: - switch (dir.z()) - { - case -1: - viewScreen->feed_key(interface_key::A_MOVE_SE_DOWN); - break; - case 0: - viewScreen->feed_key(interface_key::A_MOVE_SE); - break; - case 1: - viewScreen->feed_key(interface_key::A_MOVE_SE_UP); - break; - } - break; - } - break; - } + //auto viewScreen = getCurViewscreen(); + //if (!in->has_direction()) + // return CR_WRONG_USAGE; + //if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) + // return CR_OK; + //auto dir = in->direction(); + //switch (dir.x()) + //{ + //case -1: + // switch (dir.y()) + // { + // case -1: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_NW_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_NW); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_NW_UP); + // break; + // } + // break; + // case 0: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_W_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_W); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_W_UP); + // break; + // } + // break; + // case 1: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_SW_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_SW); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_SW_UP); + // break; + // } + // break; + // } + // break; + //case 0: + // switch (dir.y()) + // { + // case -1: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_N_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_N); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_N_UP); + // break; + // } + // break; + // case 0: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_DOWN); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_UP); + // break; + // } + // break; + // case 1: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_S_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_S); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_S_UP); + // break; + // } + // break; + // } + // break; + //case 1: + // switch (dir.y()) + // { + // case -1: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_NE_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_NE); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_NE_UP); + // break; + // } + // break; + // case 0: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_E_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_E); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_E_UP); + // break; + // } + // break; + // case 1: + // switch (dir.z()) + // { + // case -1: + // viewScreen->feed_key(interface_key::A_MOVE_SE_DOWN); + // break; + // case 0: + // viewScreen->feed_key(interface_key::A_MOVE_SE); + // break; + // case 1: + // viewScreen->feed_key(interface_key::A_MOVE_SE_UP); + // break; + // } + // break; + // } + // break; + //} return CR_OK; } command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParams *in) { - if (!in->has_direction()) - return CR_WRONG_USAGE; - if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) - return CR_OK; - auto dir = in->direction(); - keyQueue.push(interface_key::A_JUMP); - int x = dir.x(); - int y = dir.y(); - if (x > 0) - { - for (int i = 0; i < x; i++) - { - keyQueue.push(interface_key::CURSOR_RIGHT); - } - } - if (x < 0) - { - for (int i = 0; i > x; i--) - { - keyQueue.push(interface_key::CURSOR_LEFT); - } - } - if (y > 0) - { - for (int i = 0; i < y; i++) - { - keyQueue.push(interface_key::CURSOR_DOWN); - } - } - if (y < 0) - { - for (int i = 0; i > y; i--) - { - keyQueue.push(interface_key::CURSOR_UP); - } - } - keyQueue.push(interface_key::SELECT); + //if (!in->has_direction()) + // return CR_WRONG_USAGE; + //if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) + // return CR_OK; + //auto dir = in->direction(); + //keyQueue.push(interface_key::A_JUMP); + //int x = dir.x(); + //int y = dir.y(); + //if (x > 0) + //{ + // for (int i = 0; i < x; i++) + // { + // keyQueue.push(interface_key::CURSOR_RIGHT); + // } + //} + //if (x < 0) + //{ + // for (int i = 0; i > x; i--) + // { + // keyQueue.push(interface_key::CURSOR_LEFT); + // } + //} + //if (y > 0) + //{ + // for (int i = 0; i < y; i++) + // { + // keyQueue.push(interface_key::CURSOR_DOWN); + // } + //} + //if (y < 0) + //{ + // for (int i = 0; i > y; i--) + // { + // keyQueue.push(interface_key::CURSOR_UP); + // } + //} + //keyQueue.push(interface_key::SELECT); return CR_OK; } command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, MenuContents *out) { - auto advUi = df::global::ui_advmode; + //auto advUi = df::global::ui_advmode; - if (advUi == NULL) - return CR_FAILURE; + //if (advUi == NULL) + // return CR_FAILURE; - out->set_current_menu((AdvmodeMenu)advUi->menu); + //out->set_current_menu((AdvmodeMenu)advUi->menu); - //Fixme: Needs a proper way to control it, but for now, this is the only way to allow Armok Vision to keep going without the user needing to switch to DF. - if (advUi->menu == ui_advmode_menu::FallAction) - { - getCurViewscreen()->feed_key(interface_key::OPTION1); - } + ////Fixme: Needs a proper way to control it, but for now, this is the only way to allow Armok Vision to keep going without the user needing to switch to DF. + //if (advUi->menu == ui_advmode_menu::FallAction) + //{ + // getCurViewscreen()->feed_key(interface_key::OPTION1); + //} - switch (advUi->menu) - { - case ui_advmode_menu::MoveCarefully: - for (size_t i = 0; i < advUi->movements.size(); i++) - { - auto movement = advUi->movements[i]; - auto send_movement = out->add_movements(); - SetCoord(movement->source, send_movement->mutable_source()); - SetCoord(movement->dest, send_movement->mutable_dest()); + //switch (advUi->menu) + //{ + //case ui_advmode_menu::MoveCarefully: + // for (size_t i = 0; i < advUi->movements.size(); i++) + // { + // auto movement = advUi->movements[i]; + // auto send_movement = out->add_movements(); + // SetCoord(movement->source, send_movement->mutable_source()); + // SetCoord(movement->dest, send_movement->mutable_dest()); - STRICT_VIRTUAL_CAST_VAR(climbMovement, df::adventure_movement_climbst, movement); - if (climbMovement) - { - SetCoord(climbMovement->grab, send_movement->mutable_grab()); - send_movement->set_movement_type(CarefulMovementType::CLIMB); - } - STRICT_VIRTUAL_CAST_VAR(holdTileMovement, df::adventure_movement_hold_tilest, movement); - if (holdTileMovement) - { - SetCoord(holdTileMovement->grab, send_movement->mutable_grab()); - send_movement->set_movement_type(CarefulMovementType::HOLD_TILE); - } - } - default: - break; - } + // STRICT_VIRTUAL_CAST_VAR(climbMovement, df::adventure_movement_climbst, movement); + // if (climbMovement) + // { + // SetCoord(climbMovement->grab, send_movement->mutable_grab()); + // send_movement->set_movement_type(CarefulMovementType::CLIMB); + // } + // STRICT_VIRTUAL_CAST_VAR(holdTileMovement, df::adventure_movement_hold_tilest, movement); + // if (holdTileMovement) + // { + // SetCoord(holdTileMovement->grab, send_movement->mutable_grab()); + // send_movement->set_movement_type(CarefulMovementType::HOLD_TILE); + // } + // } + //default: + // break; + //} return CR_OK; } command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in) { - if (!(df::global::ui_advmode->menu == ui_advmode_menu::MoveCarefully)) - return CR_OK; - int choice = in->value(); - int page = choice / 5; - int select = choice % 5; - for (int i = 0; i < page; i++) - { - keyQueue.push(interface_key::SECONDSCROLL_PAGEDOWN); - } - keyQueue.push((interface_key::interface_key)(interface_key::OPTION1 + select)); + //if (!(df::global::ui_advmode->menu == ui_advmode_menu::MoveCarefully)) + // return CR_OK; + //int choice = in->value(); + //int page = choice / 5; + //int select = choice % 5; + //for (int i = 0; i < page; i++) + //{ + // keyQueue.push(interface_key::SECONDSCROLL_PAGEDOWN); + //} + //keyQueue.push((interface_key::interface_key)(interface_key::OPTION1 + select)); return CR_OK; } command_result MiscMoveCommand(DFHack::color_ostream &stream, const MiscMoveParams *in) { - if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) - return CR_OK; + //if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) + // return CR_OK; - auto type = in->type(); + //auto type = in->type(); - switch (type) - { - case AdventureControl::SET_CLIMB: - getCurViewscreen()->feed_key(interface_key::A_HOLD); - break; - case AdventureControl::SET_STAND: - getCurViewscreen()->feed_key(interface_key::A_STANCE); - break; - case AdventureControl::SET_CANCEL: - getCurViewscreen()->feed_key(interface_key::LEAVESCREEN); - break; - default: - break; - } + //switch (type) + //{ + //case AdventureControl::SET_CLIMB: + // getCurViewscreen()->feed_key(interface_key::A_HOLD); + // break; + //case AdventureControl::SET_STAND: + // getCurViewscreen()->feed_key(interface_key::A_STANCE); + // break; + //case AdventureControl::SET_CANCEL: + // getCurViewscreen()->feed_key(interface_key::LEAVESCREEN); + // break; + //default: + // break; + //} return CR_OK; } From b1ea3e8f02abdaf5cae5dc3d8fccf13206c96b85 Mon Sep 17 00:00:00 2001 From: Rose Date: Mon, 26 Dec 2022 17:05:06 -0800 Subject: [PATCH 0019/2222] Remove a few things from building_reader that haven't been updated yet. --- .../remotefortressreader/building_reader.cpp | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/plugins/remotefortressreader/building_reader.cpp b/plugins/remotefortressreader/building_reader.cpp index a4300159e..5a0595eef 100644 --- a/plugins/remotefortressreader/building_reader.cpp +++ b/plugins/remotefortressreader/building_reader.cpp @@ -316,7 +316,8 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re material->set_mat_index(local_build->mat_index); remote_build->set_building_flags(local_build->flags.whole); - remote_build->set_is_room(local_build->is_room); + //FIXME: Figure out what to replace this with. + //remote_build->set_is_room(local_build->is_room); if (local_build->room.width > 0 && local_build->room.height > 0 && local_build->room.extents != nullptr) { auto room = remote_build->mutable_room(); @@ -607,28 +608,29 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re } case df::enums::building_type::ArcheryTarget: { - auto actual = strict_virtual_cast(local_build); - if (actual) - { - auto facing = actual->archery_direction; - switch (facing) - { - case df::building_archerytargetst::TopToBottom: - remote_build->set_direction(NORTH); - break; - case df::building_archerytargetst::BottomToTop: - remote_build->set_direction(SOUTH); - break; - case df::building_archerytargetst::LeftToRight: - remote_build->set_direction(WEST); - break; - case df::building_archerytargetst::RightToLeft: - remote_build->set_direction(EAST); - break; - default: - break; - } - } + //FIXME: Need to decode archery targets again. + //auto actual = strict_virtual_cast(local_build); + //if (actual) + //{ + // auto facing = actual->archery_direction; + // switch (facing) + // { + // case df::building_archerytargetst::TopToBottom: + // remote_build->set_direction(NORTH); + // break; + // case df::building_archerytargetst::BottomToTop: + // remote_build->set_direction(SOUTH); + // break; + // case df::building_archerytargetst::LeftToRight: + // remote_build->set_direction(WEST); + // break; + // case df::building_archerytargetst::RightToLeft: + // remote_build->set_direction(EAST); + // break; + // default: + // break; + // } + //} break; } case df::enums::building_type::Chain: From 5f70a268037ca36f4086c88ba84922bf80b9358c Mon Sep 17 00:00:00 2001 From: Rose Date: Mon, 26 Dec 2022 17:15:12 -0800 Subject: [PATCH 0020/2222] Comment out the dwarf mode UI for now. (Forever?) --- .../remotefortressreader/dwarf_control.cpp | 492 +++++++++--------- 1 file changed, 246 insertions(+), 246 deletions(-) diff --git a/plugins/remotefortressreader/dwarf_control.cpp b/plugins/remotefortressreader/dwarf_control.cpp index 3a5933464..f10d228ca 100644 --- a/plugins/remotefortressreader/dwarf_control.cpp +++ b/plugins/remotefortressreader/dwarf_control.cpp @@ -233,261 +233,261 @@ command_result SetPauseState(color_ostream &stream, const SingleBool *in) void CopyBuildMenu(DwarfControl::SidebarState * out) { - auto menus = df::global::ui_sidebar_menus; - auto build_selector = df::global::ui_build_selector; - if (build_selector->building_type == -1) - for (size_t i = 0; i < menus->building.choices_visible.size(); i++) - { - auto menu_item = menus->building.choices_visible[i]; - auto send_item = out->add_menu_items(); - STRICT_VIRTUAL_CAST_VAR(building, df::interface_button_construction_building_selectorst, menu_item); - if (building) - { - auto send_bld = send_item->mutable_building_type(); - send_bld->set_building_type(building->building_type); - send_bld->set_building_subtype(building->building_subtype); - send_bld->set_building_custom(building->custom_type); - send_item->set_existing_count(building->existing_count); - } - STRICT_VIRTUAL_CAST_VAR(sub_category, df::interface_button_construction_category_selectorst, menu_item); - if (sub_category) - { - send_item->set_build_category((DwarfControl::BuildCategory)sub_category->category_id); - } - } - else - { - auto send_selector = out->mutable_build_selector(); - auto send_bld = send_selector->mutable_building_type(); - send_bld->set_building_type(build_selector->building_type); - send_bld->set_building_subtype(build_selector->building_subtype); - send_bld->set_building_custom(build_selector->custom_type); - send_selector->set_stage((DwarfControl::BuildSelectorStage)build_selector->stage); - for (size_t i = 0; i < build_selector->errors.size(); i++) - { - if (build_selector->errors[i]) - send_selector->add_errors(*build_selector->errors[i]); - } - for (size_t i = 0; i < build_selector->choices.size(); i++) - { - auto choice = build_selector->choices[i]; - auto send_choice = send_selector->add_choices(); - send_choice->set_distance(choice->distance); - std::string name; - choice->getName(&name); - send_choice->set_name(name); - send_choice->set_num_candidates(choice->getNumCandidates()); - send_choice->set_used_count(choice->getUsedCount()); - } - int16_t x_low, y_low, x_high, y_high; - GetBuildingSize(build_selector->building_type, build_selector->building_subtype, build_selector->custom_type, x_low, y_low, x_high, y_high); - send_selector->set_radius_x_low(x_low); - send_selector->set_radius_y_low(y_low); - send_selector->set_radius_x_high(x_high); - send_selector->set_radius_y_high(y_high); - if (build_selector->stage >= 1) - { - auto send_cursor = send_selector->mutable_cursor(); - send_cursor->set_x(cursor->x); - send_cursor->set_y(cursor->y); - send_cursor->set_z(cursor->z); - } + //auto menus = df::global::ui_sidebar_menus; + //auto build_selector = df::global::ui_build_selector; + //if (build_selector->building_type == -1) + // for (size_t i = 0; i < menus->building.choices_visible.size(); i++) + // { + // auto menu_item = menus->building.choices_visible[i]; + // auto send_item = out->add_menu_items(); + // STRICT_VIRTUAL_CAST_VAR(building, df::interface_button_construction_building_selectorst, menu_item); + // if (building) + // { + // auto send_bld = send_item->mutable_building_type(); + // send_bld->set_building_type(building->building_type); + // send_bld->set_building_subtype(building->building_subtype); + // send_bld->set_building_custom(building->custom_type); + // send_item->set_existing_count(building->existing_count); + // } + // STRICT_VIRTUAL_CAST_VAR(sub_category, df::interface_button_construction_category_selectorst, menu_item); + // if (sub_category) + // { + // send_item->set_build_category((DwarfControl::BuildCategory)sub_category->category_id); + // } + // } + //else + //{ + // auto send_selector = out->mutable_build_selector(); + // auto send_bld = send_selector->mutable_building_type(); + // send_bld->set_building_type(build_selector->building_type); + // send_bld->set_building_subtype(build_selector->building_subtype); + // send_bld->set_building_custom(build_selector->custom_type); + // send_selector->set_stage((DwarfControl::BuildSelectorStage)build_selector->stage); + // for (size_t i = 0; i < build_selector->errors.size(); i++) + // { + // if (build_selector->errors[i]) + // send_selector->add_errors(*build_selector->errors[i]); + // } + // for (size_t i = 0; i < build_selector->choices.size(); i++) + // { + // auto choice = build_selector->choices[i]; + // auto send_choice = send_selector->add_choices(); + // send_choice->set_distance(choice->distance); + // std::string name; + // choice->getName(&name); + // send_choice->set_name(name); + // send_choice->set_num_candidates(choice->getNumCandidates()); + // send_choice->set_used_count(choice->getUsedCount()); + // } + // int16_t x_low, y_low, x_high, y_high; + // GetBuildingSize(build_selector->building_type, build_selector->building_subtype, build_selector->custom_type, x_low, y_low, x_high, y_high); + // send_selector->set_radius_x_low(x_low); + // send_selector->set_radius_y_low(y_low); + // send_selector->set_radius_x_high(x_high); + // send_selector->set_radius_y_high(y_high); + // if (build_selector->stage >= 1) + // { + // auto send_cursor = send_selector->mutable_cursor(); + // send_cursor->set_x(cursor->x); + // send_cursor->set_y(cursor->y); + // send_cursor->set_z(cursor->z); + // } - for (int y = 0; y < (y_low + y_high + 1); y++) - for (int x = 0; x < (x_low + x_high + 1); x++) - { - send_selector->add_tiles(build_selector->tiles[x][y]); - } - } + // for (int y = 0; y < (y_low + y_high + 1); y++) + // for (int x = 0; x < (x_low + x_high + 1); x++) + // { + // send_selector->add_tiles(build_selector->tiles[x][y]); + // } + //} } command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out) { - auto ui = df::global::ui; - out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)ui->main.mode); - auto mode = ui->main.mode; - switch (mode) - { - case ui_sidebar_mode::Default: - break; - case ui_sidebar_mode::Squads: - break; - case ui_sidebar_mode::DesignateMine: - break; - case ui_sidebar_mode::DesignateRemoveRamps: - break; - case ui_sidebar_mode::DesignateUpStair: - break; - case ui_sidebar_mode::DesignateDownStair: - break; - case ui_sidebar_mode::DesignateUpDownStair: - break; - case ui_sidebar_mode::DesignateUpRamp: - break; - case ui_sidebar_mode::DesignateChannel: - break; - case ui_sidebar_mode::DesignateGatherPlants: - break; - case ui_sidebar_mode::DesignateRemoveDesignation: - break; - case ui_sidebar_mode::DesignateSmooth: - break; - case ui_sidebar_mode::DesignateCarveTrack: - break; - case ui_sidebar_mode::DesignateEngrave: - break; - case ui_sidebar_mode::DesignateCarveFortification: - break; - case ui_sidebar_mode::Stockpiles: - break; - case ui_sidebar_mode::Build: - CopyBuildMenu(out); - break; - case ui_sidebar_mode::QueryBuilding: - break; - case ui_sidebar_mode::Orders: - break; - case ui_sidebar_mode::OrdersForbid: - break; - case ui_sidebar_mode::OrdersRefuse: - break; - case ui_sidebar_mode::OrdersWorkshop: - break; - case ui_sidebar_mode::OrdersZone: - break; - case ui_sidebar_mode::BuildingItems: - break; - case ui_sidebar_mode::ViewUnits: - break; - case ui_sidebar_mode::LookAround: - break; - case ui_sidebar_mode::DesignateItemsClaim: - break; - case ui_sidebar_mode::DesignateItemsForbid: - break; - case ui_sidebar_mode::DesignateItemsMelt: - break; - case ui_sidebar_mode::DesignateItemsUnmelt: - break; - case ui_sidebar_mode::DesignateItemsDump: - break; - case ui_sidebar_mode::DesignateItemsUndump: - break; - case ui_sidebar_mode::DesignateItemsHide: - break; - case ui_sidebar_mode::DesignateItemsUnhide: - break; - case ui_sidebar_mode::DesignateChopTrees: - break; - case ui_sidebar_mode::DesignateToggleEngravings: - break; - case ui_sidebar_mode::DesignateToggleMarker: - break; - case ui_sidebar_mode::Hotkeys: - break; - case ui_sidebar_mode::DesignateTrafficHigh: - break; - case ui_sidebar_mode::DesignateTrafficNormal: - break; - case ui_sidebar_mode::DesignateTrafficLow: - break; - case ui_sidebar_mode::DesignateTrafficRestricted: - break; - case ui_sidebar_mode::Zones: - break; - case ui_sidebar_mode::ZonesPenInfo: - break; - case ui_sidebar_mode::ZonesPitInfo: - break; - case ui_sidebar_mode::ZonesHospitalInfo: - break; - case ui_sidebar_mode::ZonesGatherInfo: - break; - case ui_sidebar_mode::DesignateRemoveConstruction: - break; - case ui_sidebar_mode::DepotAccess: - break; - case ui_sidebar_mode::NotesPoints: - break; - case ui_sidebar_mode::NotesRoutes: - break; - case ui_sidebar_mode::Burrows: - break; - case ui_sidebar_mode::Hauling: - break; - case ui_sidebar_mode::ArenaWeather: - break; - case ui_sidebar_mode::ArenaTrees: - break; - default: - break; - } + //auto ui = df::global::ui; + //out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)ui->main.mode); + //auto mode = ui->main.mode; + //switch (mode) + //{ + //case ui_sidebar_mode::Default: + // break; + //case ui_sidebar_mode::Squads: + // break; + //case ui_sidebar_mode::DesignateMine: + // break; + //case ui_sidebar_mode::DesignateRemoveRamps: + // break; + //case ui_sidebar_mode::DesignateUpStair: + // break; + //case ui_sidebar_mode::DesignateDownStair: + // break; + //case ui_sidebar_mode::DesignateUpDownStair: + // break; + //case ui_sidebar_mode::DesignateUpRamp: + // break; + //case ui_sidebar_mode::DesignateChannel: + // break; + //case ui_sidebar_mode::DesignateGatherPlants: + // break; + //case ui_sidebar_mode::DesignateRemoveDesignation: + // break; + //case ui_sidebar_mode::DesignateSmooth: + // break; + //case ui_sidebar_mode::DesignateCarveTrack: + // break; + //case ui_sidebar_mode::DesignateEngrave: + // break; + //case ui_sidebar_mode::DesignateCarveFortification: + // break; + //case ui_sidebar_mode::Stockpiles: + // break; + //case ui_sidebar_mode::Build: + // CopyBuildMenu(out); + // break; + //case ui_sidebar_mode::QueryBuilding: + // break; + //case ui_sidebar_mode::Orders: + // break; + //case ui_sidebar_mode::OrdersForbid: + // break; + //case ui_sidebar_mode::OrdersRefuse: + // break; + //case ui_sidebar_mode::OrdersWorkshop: + // break; + //case ui_sidebar_mode::OrdersZone: + // break; + //case ui_sidebar_mode::BuildingItems: + // break; + //case ui_sidebar_mode::ViewUnits: + // break; + //case ui_sidebar_mode::LookAround: + // break; + //case ui_sidebar_mode::DesignateItemsClaim: + // break; + //case ui_sidebar_mode::DesignateItemsForbid: + // break; + //case ui_sidebar_mode::DesignateItemsMelt: + // break; + //case ui_sidebar_mode::DesignateItemsUnmelt: + // break; + //case ui_sidebar_mode::DesignateItemsDump: + // break; + //case ui_sidebar_mode::DesignateItemsUndump: + // break; + //case ui_sidebar_mode::DesignateItemsHide: + // break; + //case ui_sidebar_mode::DesignateItemsUnhide: + // break; + //case ui_sidebar_mode::DesignateChopTrees: + // break; + //case ui_sidebar_mode::DesignateToggleEngravings: + // break; + //case ui_sidebar_mode::DesignateToggleMarker: + // break; + //case ui_sidebar_mode::Hotkeys: + // break; + //case ui_sidebar_mode::DesignateTrafficHigh: + // break; + //case ui_sidebar_mode::DesignateTrafficNormal: + // break; + //case ui_sidebar_mode::DesignateTrafficLow: + // break; + //case ui_sidebar_mode::DesignateTrafficRestricted: + // break; + //case ui_sidebar_mode::Zones: + // break; + //case ui_sidebar_mode::ZonesPenInfo: + // break; + //case ui_sidebar_mode::ZonesPitInfo: + // break; + //case ui_sidebar_mode::ZonesHospitalInfo: + // break; + //case ui_sidebar_mode::ZonesGatherInfo: + // break; + //case ui_sidebar_mode::DesignateRemoveConstruction: + // break; + //case ui_sidebar_mode::DepotAccess: + // break; + //case ui_sidebar_mode::NotesPoints: + // break; + //case ui_sidebar_mode::NotesRoutes: + // break; + //case ui_sidebar_mode::Burrows: + // break; + //case ui_sidebar_mode::Hauling: + // break; + //case ui_sidebar_mode::ArenaWeather: + // break; + //case ui_sidebar_mode::ArenaTrees: + // break; + //default: + // break; + //} return CR_OK; } command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in) { - auto ui = df::global::ui; - if (in->has_mode()) - { - ui_sidebar_mode::ui_sidebar_mode set_mode = (ui_sidebar_mode::ui_sidebar_mode)in->mode(); - if (ui->main.mode != set_mode) - { - ui->main.mode = ui_sidebar_mode::Default; - switch (set_mode) - { - case ui_sidebar_mode::Build: - keyQueue.push(interface_key::D_BUILDING); - break; - default: - ui->main.mode = set_mode; - break; - } - } - } - switch (ui->main.mode) - { - case ui_sidebar_mode::Build: - if (in->has_action()) - { - int index = 0; - if (in->has_menu_index()) - index = in->menu_index(); - if(ui_build_selector->building_type == -1) - df::global::ui_sidebar_menus->building.cursor = index; - if (ui_build_selector->stage == 2) - { - ui_build_selector->sel_index = index; - } - } - if (ui_build_selector->stage == 1) - { - if (in->has_selection_coord()) - { - df::global::cursor->x = in->selection_coord().x(); - df::global::cursor->y = in->selection_coord().y(); - df::global::cursor->z = in->selection_coord().z(); - getCurViewscreen()->feed_key(interface_key::CURSOR_LEFT); - getCurViewscreen()->feed_key(interface_key::CURSOR_RIGHT); - } - } - break; - default: - break; - } - if (in->has_action()) - { - switch (in->action()) - { - case DwarfControl::MenuSelect: - keyQueue.push(interface_key::SELECT); - break; - case DwarfControl::MenuCancel: - keyQueue.push(interface_key::LEAVESCREEN); - break; - default: - break; - } - } + //auto ui = df::global::ui; + //if (in->has_mode()) + //{ + // ui_sidebar_mode::ui_sidebar_mode set_mode = (ui_sidebar_mode::ui_sidebar_mode)in->mode(); + // if (ui->main.mode != set_mode) + // { + // ui->main.mode = ui_sidebar_mode::Default; + // switch (set_mode) + // { + // case ui_sidebar_mode::Build: + // keyQueue.push(interface_key::D_BUILDING); + // break; + // default: + // ui->main.mode = set_mode; + // break; + // } + // } + //} + //switch (ui->main.mode) + //{ + //case ui_sidebar_mode::Build: + // if (in->has_action()) + // { + // int index = 0; + // if (in->has_menu_index()) + // index = in->menu_index(); + // if(ui_build_selector->building_type == -1) + // df::global::ui_sidebar_menus->building.cursor = index; + // if (ui_build_selector->stage == 2) + // { + // ui_build_selector->sel_index = index; + // } + // } + // if (ui_build_selector->stage == 1) + // { + // if (in->has_selection_coord()) + // { + // df::global::cursor->x = in->selection_coord().x(); + // df::global::cursor->y = in->selection_coord().y(); + // df::global::cursor->z = in->selection_coord().z(); + // getCurViewscreen()->feed_key(interface_key::CURSOR_LEFT); + // getCurViewscreen()->feed_key(interface_key::CURSOR_RIGHT); + // } + // } + // break; + //default: + // break; + //} + //if (in->has_action()) + //{ + // switch (in->action()) + // { + // case DwarfControl::MenuSelect: + // keyQueue.push(interface_key::SELECT); + // break; + // case DwarfControl::MenuCancel: + // keyQueue.push(interface_key::LEAVESCREEN); + // break; + // default: + // break; + // } + //} return CR_OK; } From 6783075ff294ce45060b4c956ed8a432e259880c Mon Sep 17 00:00:00 2001 From: Rose Date: Tue, 27 Dec 2022 00:46:04 -0800 Subject: [PATCH 0021/2222] Get RFR to actually compile. Many structure changes so it doesn't work yet. --- plugins/remotefortressreader/item_reader.cpp | 3 +- .../remotefortressreader.cpp | 68 ++++++++++--------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/plugins/remotefortressreader/item_reader.cpp b/plugins/remotefortressreader/item_reader.cpp index 56ec9019b..c791852f3 100644 --- a/plugins/remotefortressreader/item_reader.cpp +++ b/plugins/remotefortressreader/item_reader.cpp @@ -271,7 +271,8 @@ void CopyItem(RemoteFortressReader::Item * NetItem, df::item * DfItem) case df::enums::item_type::GLOVES: break; case df::enums::item_type::BOX: - type->set_mat_index(DfItem->isBag()); + //FIXME: Figure out how things change with this. Possibly make sure the item types are mutable. + //type->set_mat_index(DfItem->isBag()); break; case df::enums::item_type::BIN: break; diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index 643e92b21..acf7f27ad 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -319,14 +319,14 @@ uint16_t fletcher16(uint8_t const *data, size_t bytes) void ConvertDfColor(int16_t index, RemoteFortressReader::ColorDefinition * out) { - if (!df::global::enabler) + if (!df::global::gps) return; - auto enabler = df::global::enabler; + auto gps = df::global::gps; - out->set_red((int)(enabler->ccolor[index][0] * 255)); - out->set_green((int)(enabler->ccolor[index][1] * 255)); - out->set_blue((int)(enabler->ccolor[index][2] * 255)); + out->set_red(gps->uccolor[index][0]); + out->set_green(gps->uccolor[index][1]); + out->set_blue(gps->uccolor[index][2]); } void ConvertDfColor(int16_t in[3], RemoteFortressReader::ColorDefinition * out) @@ -1854,18 +1854,19 @@ static command_result GetViewInfo(color_ostream &stream, const EmptyMessage *in, Gui::getViewCoords(x, y, z); Gui::getCursorCoords(cx, cy, cz); -#if DF_VERSION_INT > 34011 - auto embark = Gui::getViewscreenByType(0); - if (embark) - { - df::embark_location location = embark->location; - df::world_data * data = df::global::world->world_data; - if (data && data->region_map) - { - z = data->region_map[location.region_pos.x][location.region_pos.y].elevation; - } - } -#endif + //FIXME: Get get this info from the new embark screen. +//#if DF_VERSION_INT > 34011 +// auto embark = Gui::getViewscreenByType(0); +// if (embark) +// { +// df::embark_location location = embark->location; +// df::world_data * data = df::global::world->world_data; +// if (data && data->region_map) +// { +// z = data->region_map[location.region_pos.x][location.region_pos.y].elevation; +// } +// } +//#endif auto dims = Gui::getDwarfmodeViewDims(); @@ -1915,22 +1916,23 @@ static command_result GetMapInfo(color_ostream &stream, const EmptyMessage *in, DFCoord GetMapCenter() { DFCoord output; -#if DF_VERSION_INT > 34011 - auto embark = Gui::getViewscreenByType(0); - if (embark) - { - df::embark_location location = embark->location; - output.x = (location.region_pos.x * 16) + 8; - output.y = (location.region_pos.y * 16) + 8; - output.z = 100; - df::world_data * data = df::global::world->world_data; - if (data && data->region_map) - { - output.z = data->region_map[location.region_pos.x][location.region_pos.y].elevation; - } - } - else -#endif +//FIXME: Does this even still exist? +//#if DF_VERSION_INT > 34011 +// auto embark = Gui::getViewscreenByType(0); +// if (embark) +// { +// df::embark_location location = embark->location; +// output.x = (location.region_pos.x * 16) + 8; +// output.y = (location.region_pos.y * 16) + 8; +// output.z = 100; +// df::world_data * data = df::global::world->world_data; +// if (data && data->region_map) +// { +// output.z = data->region_map[location.region_pos.x][location.region_pos.y].elevation; +// } +// } +// else +//#endif if (Maps::IsValid()) { int x, y, z; From 1b359bddf24737e76b5d6ea5caa2a0c0b63feb75 Mon Sep 17 00:00:00 2001 From: Rose Date: Wed, 28 Dec 2022 11:13:07 -0800 Subject: [PATCH 0022/2222] update xmls --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index c4d78c229..23663bc10 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c4d78c229aa0edd68a69cd5b19d5ad35a5b71098 +Subproject commit 23663bc10ed7794d3f5bb1ce99b2a84130dbe166 From fb99ef5d46985f419326593d671c785edd75dc5e Mon Sep 17 00:00:00 2001 From: Rose Date: Wed, 28 Dec 2022 16:08:36 -0800 Subject: [PATCH 0023/2222] update xml --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 23663bc10..7debb9411 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 23663bc10ed7794d3f5bb1ce99b2a84130dbe166 +Subproject commit 7debb9411b6df0709b295a8bb85b8dbada14b45d From feeefcf14900b795ff5667f5f85bd2e61791af4b Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 30 Dec 2022 00:45:00 -0500 Subject: [PATCH 0024/2222] Add read-only container_identity for std::map --- library/include/DataIdentity.h | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 711712af7..fd47429b9 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -394,6 +394,7 @@ namespace df template class ro_stl_container_identity : public container_identity { + protected: const char *name; public: @@ -419,6 +420,30 @@ namespace df } }; + template + class ro_stl_assoc_container_identity : public ro_stl_container_identity { + type_identity *key_identity; + type_identity *item_identity; + + public: + ro_stl_assoc_container_identity(const char *name, type_identity *key, type_identity *item) + : ro_stl_container_identity(name, item), + key_identity(key), + item_identity(item) + {} + + virtual std::string getFullName(type_identity*) override { + return std::string(ro_stl_assoc_container_identity::name) + "<" + key_identity->getFullName() + ", " + item_identity->getFullName() + ">"; + } + + protected: + virtual void *item_pointer(type_identity *item, void *ptr, int idx) override { + auto iter = (*(T*)ptr).begin(); + for (; idx > 0; idx--) ++iter; + return (void*)&iter->second; + } + }; + class bit_array_identity : public bit_container_identity { public: /* @@ -609,6 +634,10 @@ namespace df static container_identity *get(); }; + template struct identity_traits> { + static container_identity *get(); + }; + template<> struct identity_traits > { static bit_array_identity identity; static bit_container_identity *get() { return &identity; } @@ -679,6 +708,13 @@ namespace df return &identity; } + template + inline container_identity *identity_traits>::get() { + typedef std::map container; + static ro_stl_assoc_container_identity identity("map", identity_traits::get(), identity_traits::get()); + return &identity; + } + template inline bit_container_identity *identity_traits >::get() { static bit_array_identity identity(identity_traits::get()); From 9fd3ef7b4b07ce8444884ae8c899861e4dd2f7d7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 3 Jan 2023 14:52:49 -0800 Subject: [PATCH 0025/2222] correct mouse down behavior without hosing enabler before, we inhibited multiple mouse button down events by overwriting the values in enabler. now we keep state internally and inhibit multiple events on our own. also add events and state tracking for middle mouse button --- library/LuaTools.cpp | 30 +++++++++++++++++++++++++----- library/lua/gui.lua | 2 ++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 5b080ce43..084e5a21b 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -131,9 +131,13 @@ void DFHack::Lua::GetVector(lua_State *state, std::vector &pvec) } } +static bool inhibit_l_down = false; +static bool inhibit_r_down = false; +static bool inhibit_m_down = false; + void DFHack::Lua::PushInterfaceKeys(lua_State *L, const std::set &keys) { - lua_createtable(L, 0, keys.size() + 5); + lua_createtable(L, 0, keys.size() + 7); for (auto &key : keys) { @@ -154,23 +158,32 @@ void DFHack::Lua::PushInterfaceKeys(lua_State *L, } if (df::global::enabler) { - if (df::global::enabler->mouse_lbut_down) { + if (!inhibit_l_down && df::global::enabler->mouse_lbut_down) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_L_DOWN"); + inhibit_l_down = true; } - if (df::global::enabler->mouse_rbut_down) { + if (!inhibit_r_down && df::global::enabler->mouse_rbut_down) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_R_DOWN"); + inhibit_r_down = true; + } + if (!inhibit_m_down && df::global::enabler->mouse_mbut_down) { + lua_pushboolean(L, true); + lua_setfield(L, -2, "_MOUSE_M_DOWN"); + inhibit_m_down = true; } if (df::global::enabler->mouse_lbut) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_L"); - df::global::enabler->mouse_lbut_down = 0; } if (df::global::enabler->mouse_rbut) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_R"); - df::global::enabler->mouse_rbut_down = 0; + } + if (df::global::enabler->mouse_mbut) { + lua_pushboolean(L, true); + lua_setfield(L, -2, "_MOUSE_M"); } } } @@ -2134,4 +2147,11 @@ void DFHack::Lua::Core::Reset(color_ostream &out, const char *where) out.printerr("Common lua context stack top left at %d after %s.\n", top, where); lua_settop(State, 0); } + + if (!df::global::enabler->mouse_lbut) + inhibit_l_down = false; + if (!df::global::enabler->mouse_rbut) + inhibit_r_down = false; + if (!df::global::enabler->mouse_mbut) + inhibit_m_down = false; } diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 31631b469..1742ee342 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -14,8 +14,10 @@ CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0} local FAKE_INPUT_KEYS = { _MOUSE_L = true, _MOUSE_R = true, + _MOUSE_M = true, _MOUSE_L_DOWN = true, _MOUSE_R_DOWN = true, + _MOUSE_M_DOWN = true, _STRING = true, } From 021b24fa6589171caa672d6a5cb362fa83420cc1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 3 Jan 2023 14:56:49 -0800 Subject: [PATCH 0026/2222] update docs --- docs/dev/Lua API.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index e6cd2f9e8..de8f9c0c1 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2392,13 +2392,13 @@ Supported callbacks and fields are: ``_STRING`` Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience. - ``_MOUSE_L, _MOUSE_R`` - If the left or right mouse button is being pressed. + ``_MOUSE_L, _MOUSE_R, _MOUSE_M`` + If the left, right, and/or middle mouse button is being pressed. - ``_MOUSE_L_DOWN, _MOUSE_R_DOWN`` - If the left or right mouse button was just pressed. + ``_MOUSE_L_DOWN, _MOUSE_R_DOWN, _MOUSE_M_DOWN`` + If the left, right, and/or middle mouse button was just pressed. - If this method is omitted, the screen is dismissed on receival of the ``LEAVESCREEN`` key. + If this method is omitted, the screen is dismissed on reception of the ``LEAVESCREEN`` key. * ``function screen:onGetSelectedUnit()`` * ``function screen:onGetSelectedItem()`` From 9e38101593f47d92bd474bd3aacf3ef5e2ad3ce2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 12:09:21 -0800 Subject: [PATCH 0027/2222] enable the overlay by default now that gps->enabler is unharmed upon crossing the Lua boundary --- data/init/dfhack.tools.init | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index a910a8e34..741298109 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -78,6 +78,9 @@ # Display DFHack version on title screen #enable title-version +# Allow DFHack tools to overlay functionality and information on the DF screen +enable overlay + # Dwarf Manipulator (simple in-game Dwarf Therapist replacement) #enable manipulator @@ -89,7 +92,6 @@ # Other interface improvement tools #enable \ -# overlay \ # confirm \ # dwarfmonitor \ # mousequery \ From 5d04b9c4cbce49a55276a70b0e1bcc7ebf5b6bf0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 3 Jan 2023 01:01:30 -0800 Subject: [PATCH 0028/2222] add Textures module and load DFHack logo undocumented for now since it's internal. in the future, perhaps this could morph into a dynamic texture loading facility for tools --- data/CMakeLists.txt | 3 + data/art/dfhack.png | Bin 0 -> 1480 bytes docs/changelog.txt | 1 + library/CMakeLists.txt | 2 + library/Core.cpp | 24 +++- library/LuaApi.cpp | 9 ++ library/include/modules/Graphic.h | 22 ++- library/include/modules/Textures.h | 35 +++++ library/modules/Textures.cpp | 208 +++++++++++++++++++++++++++++ 9 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 data/art/dfhack.png create mode 100644 library/include/modules/Textures.h create mode 100644 library/modules/Textures.cpp diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 412ffa347..8991f1c24 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -10,6 +10,9 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/quickfort/ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/orders/ DESTINATION dfhack-config/orders/library) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/art/ + DESTINATION "${DFHACK_DATA_DESTINATION}/data/art") + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/examples/ DESTINATION "${DFHACK_DATA_DESTINATION}/examples") diff --git a/data/art/dfhack.png b/data/art/dfhack.png new file mode 100644 index 0000000000000000000000000000000000000000..6f146dc2fa4a8272e9c941b2640a0d80e6d63ce3 GIT binary patch literal 1480 zcmV;(1vmPMP)tWUTKeUsN(Bak0dzVYtgWp?d=7b5-KHg`}S?YKq-rl zkB6wJDERd06K69rG6HdNaj?9+EWBS>SO~|*$6#w~ON+s5iiwGV=;-KQ2Fe)t@Zp1S zhJAf~+!+oG3>iL|sd!KSLJ>dNg(&7 zot+)g%gf8Gyu6&~>FH@M1~>;0wY9Zmn3|d@5A5yjE$fYCK~`24(VLr_th>9L= z{RD$lqtOU~!R^4%(2%f7jYdPd2M-<)zp=5wDk>`E?N#nZcz8I=&CMk`KR;jI&C=3R zq8~kabo~)5E-sQG79Q!M>Eq+WZ9gIh+pYvUf^vhwAipm;CpjQ|8AB|Veo)QxiiSF<3 zho+_`*=}rXjDDrSL|j~4@DnOy0M9TuIGE1L!oorh9DK5~vt|0^~b}K6@init(); @@ -1765,12 +1768,7 @@ bool Core::Init() cerr << "DFHack is running.\n"; - { - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); - onStateChange(con, SC_CORE_INITIALIZED); - } + onStateChange(con, SC_CORE_INITIALIZED); return true; } @@ -2138,6 +2136,13 @@ void Core::onStateChange(color_ostream &out, state_change_event event) switch (event) { + case SC_CORE_INITIALIZED: + { + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); + } + break; case SC_WORLD_LOADED: case SC_WORLD_UNLOADED: case SC_MAP_LOADED: @@ -2171,6 +2176,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event) evtlog << std::endl; } } + break; + case SC_VIEWSCREEN_CHANGED: + Textures::init(out); + break; default: break; } @@ -2248,6 +2257,7 @@ int Core::Shutdown ( void ) } // invalidate all modules allModules.clear(); + Textures::cleanup(); memset(&(s_mods), 0, sizeof(s_mods)); d.reset(); return -1; diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 1352b7f1a..2f1d612b2 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -57,6 +57,7 @@ distribution. #include "modules/Materials.h" #include "modules/Random.h" #include "modules/Screen.h" +#include "modules/Textures.h" #include "modules/Translation.h" #include "modules/Units.h" #include "modules/World.h" @@ -1670,6 +1671,13 @@ static const luaL_Reg dfhack_job_funcs[] = { { NULL, NULL } }; +/***** Textures module *****/ + +static const LuaWrapper::FunctionReg dfhack_textures_module[] = { + WRAPM(Textures, getDfhackLogoTexposStart), + { NULL, NULL } +}; + /***** Units module *****/ static const LuaWrapper::FunctionReg dfhack_units_module[] = { @@ -3357,6 +3365,7 @@ void OpenDFHackApi(lua_State *state) luaL_setfuncs(state, dfhack_funcs, 0); OpenModule(state, "gui", dfhack_gui_module, dfhack_gui_funcs); OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs); + OpenModule(state, "textures", dfhack_textures_module); OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs); OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); diff --git a/library/include/modules/Graphic.h b/library/include/modules/Graphic.h index fa2cc30c6..ba5bf98c3 100644 --- a/library/include/modules/Graphic.h +++ b/library/include/modules/Graphic.h @@ -45,9 +45,29 @@ namespace DFHack uint16_t w, h; } DFSDL_Rect; typedef struct + { + void *palette; // SDL_Palette* + uint8_t BitsPerPixel; + uint8_t BytesPerPixel; + uint8_t Rloss; + uint8_t Gloss; + uint8_t Bloss; + uint8_t Aloss; + uint8_t Rshift; + uint8_t Gshift; + uint8_t Bshift; + uint8_t Ashift; + uint32_t Rmask; + uint32_t Gmask; + uint32_t Bmask; + uint32_t Amask; + uint32_t colorkey; + uint8_t alpha; + } DFSDL_PixelFormat; + typedef struct { uint32_t flags; - void* format; // PixelFormat* + DFSDL_PixelFormat* format; int w, h; int pitch; void* pixels; diff --git a/library/include/modules/Textures.h b/library/include/modules/Textures.h new file mode 100644 index 000000000..390bbeed6 --- /dev/null +++ b/library/include/modules/Textures.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Export.h" + +#include "ColorText.h" + +namespace DFHack { + +/** + * The Textures module - loads and provides access to DFHack textures + * \ingroup grp_modules + * \ingroup grp_textures + */ +namespace Textures { + +/** + * Call this on DFHack init and on every viewscreen change so we can reload + * and reindex textures as needed. + */ +void init(DFHack::color_ostream &out); + +/** + * Call this when DFHack is being unloaded. + * + */ +void cleanup(); + +/** + * Get first texpos for the DFHack logo. This texpos and the next 11 make up the + * 4x3 grid of logo textures that can be displayed on the UI layer. + */ +DFHACK_EXPORT long getDfhackLogoTexposStart(); + +} +} diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp new file mode 100644 index 000000000..efbd1cdf4 --- /dev/null +++ b/library/modules/Textures.cpp @@ -0,0 +1,208 @@ +#include "Internal.h" + +#include "modules/Textures.h" + +#include "Debug.h" +#include "PluginManager.h" + +#include "df/enabler.h" + +using df::global::enabler; +using namespace DFHack; + +namespace DFHack { + DBG_DECLARE(core, textures, DebugCategory::LINFO); +} + +static bool g_loaded = false; +static long g_num_dfhack_textures = 0; +static long g_dfhack_logo_texpos_start = -1; + +static DFLibrary *g_sdl_handle = nullptr; +static DFLibrary *g_sdl_image_handle = nullptr; +static const std::vector SDL_LIBS { + "SDLreal.dll", + "SDL.framework/Versions/A/SDL", + "SDL.framework/SDL", + "libSDL-1.2.so.0" +}; +static const std::vector SDL_IMAGE_LIBS { + "SDL_image.dll", + "SDL_image.framework/Versions/A/SDL_image", + "SDL_image.framework/SDL_image", + "libSDL_image-1.2.so.0" +}; + +DFSDL_Surface * (*g_IMG_Load)(const char *) = nullptr; +int (*g_SDL_SetAlpha)(DFSDL_Surface *, uint32_t, uint8_t) = nullptr; +DFSDL_Surface * (*g_SDL_CreateRGBSurface)(uint32_t, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t); +int (*g_SDL_UpperBlit)(DFSDL_Surface *, DFSDL_Rect *, DFSDL_Surface *, DFSDL_Rect *); +DFSDL_Surface * (*g_SDL_ConvertSurface)(DFSDL_Surface *, const DFSDL_PixelFormat *, uint32_t); + +void (*g_SDL_FreeSurface)(DFSDL_Surface *); + +// Converts an arbitrary Surface to something like the display format +// (32-bit RGBA), and converts magenta to transparency if convert_magenta is set +// and the source surface didn't already have an alpha channel. +// It also deletes the source surface. +// +// It uses the same pixel format (RGBA, R at lowest address) regardless of +// hardware. +DFSDL_Surface * canonicalize_format(DFSDL_Surface *src) { + DFSDL_PixelFormat fmt; + fmt.palette = NULL; + fmt.BitsPerPixel = 32; + fmt.BytesPerPixel = 4; + fmt.Rloss = fmt.Gloss = fmt.Bloss = fmt.Aloss = 0; +//#if SDL_BYTEORDER == SDL_BIG_ENDIAN +// fmt.Rshift = 24; fmt.Gshift = 16; fmt.Bshift = 8; fmt.Ashift = 0; +//#else + fmt.Rshift = 0; fmt.Gshift = 8; fmt.Bshift = 16; fmt.Ashift = 24; +//#endif + fmt.Rmask = 255 << fmt.Rshift; + fmt.Gmask = 255 << fmt.Gshift; + fmt.Bmask = 255 << fmt.Bshift; + fmt.Amask = 255 << fmt.Ashift; + fmt.colorkey = 0; + fmt.alpha = 255; + + DFSDL_Surface *tgt = g_SDL_ConvertSurface(src, &fmt, 0); // SDL_SWSURFACE + g_SDL_FreeSurface(src); + return tgt; +} + +const uint32_t TILE_WIDTH_PX = 8; +const uint32_t TILE_HEIGHT_PX = 12; + +static size_t load_textures(color_ostream & out, const char * fname, + long *texpos_start) { + if (!g_sdl_handle || !g_sdl_image_handle || !g_IMG_Load || !g_SDL_SetAlpha + || !g_SDL_CreateRGBSurface || !g_SDL_UpperBlit + || !g_SDL_ConvertSurface || !g_SDL_FreeSurface) + return 0; + + DFSDL_Surface *s = g_IMG_Load(fname); + if (!s) { + out.printerr("unable to load textures from '%s'\n", fname); + return 0; + } + + s = canonicalize_format(s); + g_SDL_SetAlpha(s, 0, 255); + int dimx = s->w / TILE_WIDTH_PX; + int dimy = s->h / TILE_HEIGHT_PX; + long count = 0; + for (int y = 0; y < dimy; y++) { + for (int x = 0; x < dimx; x++) { + DFSDL_Surface *tile = g_SDL_CreateRGBSurface(0, // SDL_SWSURFACE + TILE_WIDTH_PX, TILE_HEIGHT_PX, 32, + s->format->Rmask, s->format->Gmask, s->format->Bmask, + s->format->Amask); + g_SDL_SetAlpha(tile, 0,255); + DFSDL_Rect vp; + vp.x = TILE_WIDTH_PX * x; + vp.y = TILE_HEIGHT_PX * y; + vp.w = TILE_WIDTH_PX; + vp.h = TILE_HEIGHT_PX; + g_SDL_UpperBlit(s, &vp, tile, NULL); + if (!count++) + *texpos_start = enabler->textures.raws.size(); + enabler->textures.raws.push_back(tile); + } + } + g_SDL_FreeSurface(s); + + DEBUG(textures,out).print("loaded %zd textures from '%s'\n", count, fname); + return count; +} + +// DFHack could conceivably be loaded at any time, so we need to be able to +// handle loading textures before or after a world is loaded. +// If a world is already loaded, then append our textures to the raws. they'll +// be freed when the world is unloaded and we'll reload when we get to the title +// screen. If it's pre-world, append our textures and then adjust the "init" +// texture count so our textures will no longer be freed when worlds are +// unloaded. +// +void Textures::init(color_ostream &out) { + auto & textures = enabler->textures; + long num_textures = textures.raws.size(); + if (num_textures <= g_dfhack_logo_texpos_start) + g_loaded = false; + + if (g_loaded) + return; + + for (auto &lib_str : SDL_LIBS) { + if ((g_sdl_handle = OpenPlugin(lib_str.c_str()))) + break; + } + for (auto &lib_str : SDL_IMAGE_LIBS) { + if ((g_sdl_image_handle = OpenPlugin(lib_str.c_str()))) + break; + } + + if (!g_sdl_handle) { + out.printerr("Could not find SDL; DFHack textures not loaded.\n"); + } else if (!g_sdl_image_handle) { + out.printerr("Could not find SDL_image; DFHack textures not loaded.\n"); + } else { + #define bind(handle, name) \ + g_##name = (decltype(g_##name))LookupPlugin(handle, #name); \ + if (!g_##name) { \ + out.printerr("Could not find: " #name "; DFHack textures not loaded\n"); \ + } + + bind(g_sdl_image_handle, IMG_Load); + bind(g_sdl_handle, SDL_SetAlpha); + bind(g_sdl_handle, SDL_CreateRGBSurface); + bind(g_sdl_handle, SDL_UpperBlit); + bind(g_sdl_handle, SDL_ConvertSurface); + bind(g_sdl_handle, SDL_FreeSurface); + #undef bind + } + + bool is_pre_world = num_textures == textures.init_texture_size; + + g_num_dfhack_textures = load_textures(out, "hack/data/art/dfhack.png", + &g_dfhack_logo_texpos_start); + + DEBUG(textures,out).print("loaded %zd textures\n", g_num_dfhack_textures); + + if (is_pre_world) + textures.init_texture_size += g_num_dfhack_textures; + + // NOTE: when GL modes are supported, we'll have to re-upload textures here + + g_loaded = true; +} + +// It's ok to leave NULLs in the raws list (according to usage in g_src) +void Textures::cleanup() { + if (!g_loaded) + return; + + auto & textures = enabler->textures; + auto &raws = textures.raws; + size_t texpos_end = g_dfhack_logo_texpos_start + g_num_dfhack_textures; + for (size_t idx = g_dfhack_logo_texpos_start; idx <= texpos_end; ++idx) { + SDL_FreeSurface(raws[idx]); + raws[idx] = NULL; + } + + if (g_dfhack_logo_texpos_start == textures.init_texture_size - g_num_dfhack_textures) + textures.init_texture_size -= g_num_dfhack_textures; + + g_loaded = false; + g_num_dfhack_textures = 0; + g_dfhack_logo_texpos_start = -1; + + if (g_sdl_handle) { + ClosePlugin(g_sdl_handle); + g_sdl_handle = nullptr; + } +} + +long Textures::getDfhackLogoTexposStart() { + return g_dfhack_logo_texpos_start; +} From 8e62a46009750bf018cba9061a126fa0df0c0d28 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 3 Jan 2023 01:34:58 -0800 Subject: [PATCH 0029/2222] display new logo for the hotkeys overlay widget --- plugins/lua/hotkeys.lua | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 999527d0d..bfe55e0a7 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -19,7 +19,6 @@ HotspotMenuWidget.ATTRS{ } function HotspotMenuWidget:init() - self:addviews{widgets.Label{text={'!DF!', NEWLINE, '!Ha!', NEWLINE, '!ck!'}}} self.mouseover = false end @@ -41,6 +40,34 @@ function HotspotMenuWidget:overlay_trigger() mouseover=self.mouseover}:show() end +local dscreen = dfhack.screen + +function HotspotMenuWidget:onRenderBody(dc) + local tpos = dfhack.textures.getDfhackLogoTexposStart() + local x, y = dc.x, dc.y + + if tpos == -1 then + dscreen.paintString(COLOR_WHITE, x, y+0, '!DF!') + dscreen.paintString(COLOR_WHITE, x, y+1, '!Ha!') + dscreen.paintString(COLOR_WHITE, x, y+2, '!ck!') + else + dscreen.paintTile(COLOR_WHITE, x+0, y+0, '!', tpos+0) + dscreen.paintTile(COLOR_WHITE, x+1, y+0, 'D', tpos+1) + dscreen.paintTile(COLOR_WHITE, x+2, y+0, 'F', tpos+2) + dscreen.paintTile(COLOR_WHITE, x+3, y+0, '!', tpos+3) + + dscreen.paintTile(COLOR_WHITE, x+0, y+1, '!', tpos+4) + dscreen.paintTile(COLOR_WHITE, x+1, y+1, 'H', tpos+5) + dscreen.paintTile(COLOR_WHITE, x+2, y+1, 'a', tpos+6) + dscreen.paintTile(COLOR_WHITE, x+3, y+1, '!', tpos+7) + + dscreen.paintTile(COLOR_WHITE, x+0, y+2, '!', tpos+8) + dscreen.paintTile(COLOR_WHITE, x+1, y+2, 'c', tpos+9) + dscreen.paintTile(COLOR_WHITE, x+2, y+2, 'k', tpos+10) + dscreen.paintTile(COLOR_WHITE, x+3, y+2, '!', tpos+11) + end +end + -- register the menu hotspot with the overlay OVERLAY_WIDGETS = {menu=HotspotMenuWidget} From 67b95bf35f3a3405f93f4f1f237a4df27bf11196 Mon Sep 17 00:00:00 2001 From: Rose Date: Wed, 4 Jan 2023 15:01:46 -0800 Subject: [PATCH 0030/2222] Compile Remote Fortress Reader --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index dd5000ab5..19c73698b 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -148,7 +148,7 @@ dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) #dfhack_plugin(prospector prospector.cpp LINK_LIBRARIES lua) #dfhack_plugin(power-meter power-meter.cpp LINK_LIBRARIES lua) #dfhack_plugin(regrass regrass.cpp) -#add_subdirectory(remotefortressreader) +add_subdirectory(remotefortressreader) #dfhack_plugin(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) #add_subdirectory(rendermax) #dfhack_plugin(reveal reveal.cpp LINK_LIBRARIES lua) From 4e51e02924e2c2c183445f6f540856b3365c1c01 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 17:15:32 -0800 Subject: [PATCH 0031/2222] move SDL shims to a new DFSDL module --- library/CMakeLists.txt | 2 + library/Core.cpp | 7 ++ library/include/modules/DFSDL.h | 92 ++++++++++++++++++++++ library/include/modules/Graphic.h | 54 +------------ library/include/modules/Textures.h | 1 - library/modules/DFSDL.cpp | 120 +++++++++++++++++++++++++++++ library/modules/Textures.cpp | 82 +++----------------- 7 files changed, 233 insertions(+), 125 deletions(-) create mode 100644 library/include/modules/DFSDL.h create mode 100644 library/modules/DFSDL.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 0edf10c0b..87f869493 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -124,6 +124,7 @@ set(MODULE_HEADERS include/modules/Buildings.h include/modules/Burrows.h include/modules/Constructions.h + include/modules/DFSDL.h include/modules/Designations.h include/modules/EventManager.h include/modules/Filesystem.h @@ -151,6 +152,7 @@ set(MODULE_SOURCES modules/Buildings.cpp modules/Burrows.cpp modules/Constructions.cpp + modules/DFSDL.cpp modules/Designations.cpp modules/EventManager.cpp modules/Filesystem.cpp diff --git a/library/Core.cpp b/library/Core.cpp index 69e033c02..a3979ee2f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -49,6 +49,7 @@ using namespace std; #include "VersionInfo.h" #include "PluginManager.h" #include "ModuleFactory.h" +#include "modules/DFSDL.h" #include "modules/EventManager.h" #include "modules/Filesystem.h" #include "modules/Gui.h" @@ -1671,6 +1672,11 @@ bool Core::Init() return false; } + cerr << "Binding to SDL.\n"; + if (!DFSDL::init(con)) { + fatal("cannot bind SDL libraries"); + return false; + } cerr << "Initializing textures.\n"; Textures::init(con); // create mutex for syncing with interactive tasks @@ -2258,6 +2264,7 @@ int Core::Shutdown ( void ) // invalidate all modules allModules.clear(); Textures::cleanup(); + DFSDL::cleanup(); memset(&(s_mods), 0, sizeof(s_mods)); d.reset(); return -1; diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h new file mode 100644 index 000000000..b5fd119f7 --- /dev/null +++ b/library/include/modules/DFSDL.h @@ -0,0 +1,92 @@ +#pragma once + +#include "Export.h" +#include "ColorText.h" + +namespace DFHack +{ + // SDL stand-in type definitions + typedef signed short SINT16; + typedef void DFSDL_sem; + + typedef struct + { + int16_t x, y; + uint16_t w, h; + } DFSDL_Rect; + typedef struct + { + void *palette; // SDL_Palette* + uint8_t BitsPerPixel; + uint8_t BytesPerPixel; + uint8_t Rloss; + uint8_t Gloss; + uint8_t Bloss; + uint8_t Aloss; + uint8_t Rshift; + uint8_t Gshift; + uint8_t Bshift; + uint8_t Ashift; + uint32_t Rmask; + uint32_t Gmask; + uint32_t Bmask; + uint32_t Amask; + uint32_t colorkey; + uint8_t alpha; + } DFSDL_PixelFormat; + typedef struct + { + uint32_t flags; + DFSDL_PixelFormat* format; + int w, h; + int pitch; + void* pixels; + void* userdata; // as far as i could see DF doesnt use this + int locked; + void* lock_data; + DFSDL_Rect clip_rect; + void* map; + int refcount; + } DFSDL_Surface; + + // ========= + struct DFTileSurface + { + bool paintOver; // draw over original tile? + DFSDL_Surface* surface; // from where it should be drawn + DFSDL_Rect* rect; // from which coords (NULL to draw whole surface) + DFSDL_Rect* dstResize; // if not NULL dst rect will be resized (x/y/w/h will be added to original dst) + }; + +/** + * The DFSDL module - provides access to SDL functions without actually + * requiring build-time linkage to SDL + * \ingroup grp_modules + * \ingroup grp_dfsdl + */ +namespace DFSDL +{ + +/** + * Call this on DFHack init so we can load the SDL functions. Returns false on + * failure. + */ +bool init(DFHack::color_ostream &out); + +/** + * Call this when DFHack is being unloaded. + */ +void cleanup(); + +DFHACK_EXPORT DFSDL_Surface * DFIMG_Load(const char *file); +DFHACK_EXPORT int DFSDL_SetAlpha(DFSDL_Surface *surface, uint32_t flag, uint8_t alpha); +DFHACK_EXPORT DFSDL_Surface * DFSDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask); +DFHACK_EXPORT int DFSDL_UpperBlit(DFSDL_Surface *src, const DFSDL_Rect *srcrect, DFSDL_Surface *dst, DFSDL_Rect *dstrect); +DFHACK_EXPORT DFSDL_Surface * DFSDL_ConvertSurface(DFSDL_Surface *src, const DFSDL_PixelFormat *fmt, uint32_t flags); +DFHACK_EXPORT void DFSDL_FreeSurface(DFSDL_Surface *surface); +DFHACK_EXPORT int DFSDL_SemWait(DFSDL_sem *sem); +DFHACK_EXPORT int DFSDL_SemPost(DFSDL_sem *sem); + +} + +} diff --git a/library/include/modules/Graphic.h b/library/include/modules/Graphic.h index ba5bf98c3..22a5d4913 100644 --- a/library/include/modules/Graphic.h +++ b/library/include/modules/Graphic.h @@ -33,62 +33,11 @@ distribution. #include #include "Export.h" #include "Module.h" +#include "DFSDL.h" #include namespace DFHack { - // SDL stuff - typedef signed short SINT16; - typedef struct - { - int16_t x, y; - uint16_t w, h; - } DFSDL_Rect; - typedef struct - { - void *palette; // SDL_Palette* - uint8_t BitsPerPixel; - uint8_t BytesPerPixel; - uint8_t Rloss; - uint8_t Gloss; - uint8_t Bloss; - uint8_t Aloss; - uint8_t Rshift; - uint8_t Gshift; - uint8_t Bshift; - uint8_t Ashift; - uint32_t Rmask; - uint32_t Gmask; - uint32_t Bmask; - uint32_t Amask; - uint32_t colorkey; - uint8_t alpha; - } DFSDL_PixelFormat; - typedef struct - { - uint32_t flags; - DFSDL_PixelFormat* format; - int w, h; - int pitch; - void* pixels; - void* userdata; // as far as i could see DF doesnt use this - int locked; - void* lock_data; - DFSDL_Rect clip_rect; - void* map; - int refcount; - } DFSDL_Surface; - - // ========= - struct DFTileSurface - { - bool paintOver; // draw over original tile? - DFSDL_Surface* surface; // from where it should be drawn - DFSDL_Rect* rect; // from which coords (NULL to draw whole surface) - DFSDL_Rect* dstResize; // if not NULL dst rect will be resized (x/y/w/h will be added to original dst) - }; - - class DFHACK_EXPORT Graphic : public Module { public: @@ -103,7 +52,6 @@ namespace DFHack private: std::vector funcs; }; - } #endif diff --git a/library/include/modules/Textures.h b/library/include/modules/Textures.h index 390bbeed6..2ef172bff 100644 --- a/library/include/modules/Textures.h +++ b/library/include/modules/Textures.h @@ -1,7 +1,6 @@ #pragma once #include "Export.h" - #include "ColorText.h" namespace DFHack { diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp new file mode 100644 index 000000000..a7847fdb5 --- /dev/null +++ b/library/modules/DFSDL.cpp @@ -0,0 +1,120 @@ +#include "Internal.h" + +#include "modules/DFSDL.h" + +#include "Debug.h" +#include "PluginManager.h" + +namespace DFHack { + DBG_DECLARE(core, dfsdl, DebugCategory::LINFO); +} + +using namespace DFHack; + +static DFLibrary *g_sdl_handle = nullptr; +static DFLibrary *g_sdl_image_handle = nullptr; +static const std::vector SDL_LIBS { + "SDLreal.dll", // TODO: change to SDL.dll once we move to dfhooks + "SDL.framework/Versions/A/SDL", + "SDL.framework/SDL", + "libSDL-1.2.so.0" +}; +static const std::vector SDL_IMAGE_LIBS { + "SDL_image.dll", + "SDL_image.framework/Versions/A/SDL_image", + "SDL_image.framework/SDL_image", + "libSDL_image-1.2.so.0" +}; + +DFSDL_Surface * (*g_IMG_Load)(const char *) = nullptr; +int (*g_SDL_SetAlpha)(DFSDL_Surface *, uint32_t, uint8_t) = nullptr; +DFSDL_Surface * (*g_SDL_CreateRGBSurface)(uint32_t, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t); +int (*g_SDL_UpperBlit)(DFSDL_Surface *, const DFSDL_Rect *, DFSDL_Surface *, DFSDL_Rect *); +DFSDL_Surface * (*g_SDL_ConvertSurface)(DFSDL_Surface *, const DFSDL_PixelFormat *, uint32_t); +void (*g_SDL_FreeSurface)(DFSDL_Surface *); +int (*g_SDL_SemWait)(DFSDL_sem *); +int (*g_SDL_SemPost)(DFSDL_sem *); + +bool DFSDL::init(color_ostream &out) { + for (auto &lib_str : SDL_LIBS) { + if ((g_sdl_handle = OpenPlugin(lib_str.c_str()))) + break; + } + if (!g_sdl_handle) { + out.printerr("DFHack could not find SDL\n"); + return false; + } + + for (auto &lib_str : SDL_IMAGE_LIBS) { + if ((g_sdl_image_handle = OpenPlugin(lib_str.c_str()))) + break; + } + if (!g_sdl_image_handle) { + out.printerr("DFHack could not find SDL_image\n"); + return false; + } + + #define bind(handle, name) \ + g_##name = (decltype(g_##name))LookupPlugin(handle, #name); \ + if (!g_##name) { \ + out.printerr("DFHack could not find: " #name "\n"); \ + return false; \ + } + + bind(g_sdl_image_handle, IMG_Load); + bind(g_sdl_handle, SDL_SetAlpha); + bind(g_sdl_handle, SDL_CreateRGBSurface); + bind(g_sdl_handle, SDL_UpperBlit); + bind(g_sdl_handle, SDL_ConvertSurface); + bind(g_sdl_handle, SDL_FreeSurface); + bind(g_sdl_handle, SDL_SemWait); + bind(g_sdl_handle, SDL_SemPost); + #undef bind + + DEBUG(dfsdl,out).print("sdl successfully loaded\n"); + return true; +} + +// It's ok to leave NULLs in the raws list (according to usage in g_src) +void DFSDL::cleanup() { + if (g_sdl_handle) { + ClosePlugin(g_sdl_handle); + g_sdl_handle = nullptr; + } + if (g_sdl_image_handle) { + ClosePlugin(g_sdl_image_handle); + g_sdl_image_handle = nullptr; + } +} + +DFSDL_Surface * DFSDL::DFIMG_Load(const char *file) { + return g_IMG_Load(file); +} + +int DFSDL::DFSDL_SetAlpha(DFSDL_Surface *surface, uint32_t flag, uint8_t alpha) { + return g_SDL_SetAlpha(surface, flag, alpha); +} + +DFSDL_Surface * DFSDL::DFSDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) { + return g_SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask); +} + +int DFSDL::DFSDL_UpperBlit(DFSDL_Surface *src, const DFSDL_Rect *srcrect, DFSDL_Surface *dst, DFSDL_Rect *dstrect) { + return g_SDL_UpperBlit(src, srcrect, dst, dstrect); +} + +DFSDL_Surface * DFSDL::DFSDL_ConvertSurface(DFSDL_Surface *src, const DFSDL_PixelFormat *fmt, uint32_t flags) { + return g_SDL_ConvertSurface(src, fmt, flags); +} + +void DFSDL::DFSDL_FreeSurface(DFSDL_Surface *surface) { + g_SDL_FreeSurface(surface); +} + +int DFSDL::DFSDL_SemWait(DFSDL_sem *sem) { + return g_SDL_SemWait(sem); +} + +int DFSDL::DFSDL_SemPost(DFSDL_sem *sem) { + return g_SDL_SemPost(sem); +} diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index efbd1cdf4..32ed6453c 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -1,5 +1,6 @@ #include "Internal.h" +#include "modules/DFSDL.h" #include "modules/Textures.h" #include "Debug.h" @@ -9,6 +10,7 @@ using df::global::enabler; using namespace DFHack; +using namespace DFHack::DFSDL; namespace DFHack { DBG_DECLARE(core, textures, DebugCategory::LINFO); @@ -18,29 +20,6 @@ static bool g_loaded = false; static long g_num_dfhack_textures = 0; static long g_dfhack_logo_texpos_start = -1; -static DFLibrary *g_sdl_handle = nullptr; -static DFLibrary *g_sdl_image_handle = nullptr; -static const std::vector SDL_LIBS { - "SDLreal.dll", - "SDL.framework/Versions/A/SDL", - "SDL.framework/SDL", - "libSDL-1.2.so.0" -}; -static const std::vector SDL_IMAGE_LIBS { - "SDL_image.dll", - "SDL_image.framework/Versions/A/SDL_image", - "SDL_image.framework/SDL_image", - "libSDL_image-1.2.so.0" -}; - -DFSDL_Surface * (*g_IMG_Load)(const char *) = nullptr; -int (*g_SDL_SetAlpha)(DFSDL_Surface *, uint32_t, uint8_t) = nullptr; -DFSDL_Surface * (*g_SDL_CreateRGBSurface)(uint32_t, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t); -int (*g_SDL_UpperBlit)(DFSDL_Surface *, DFSDL_Rect *, DFSDL_Surface *, DFSDL_Rect *); -DFSDL_Surface * (*g_SDL_ConvertSurface)(DFSDL_Surface *, const DFSDL_PixelFormat *, uint32_t); - -void (*g_SDL_FreeSurface)(DFSDL_Surface *); - // Converts an arbitrary Surface to something like the display format // (32-bit RGBA), and converts magenta to transparency if convert_magenta is set // and the source surface didn't already have an alpha channel. @@ -66,8 +45,8 @@ DFSDL_Surface * canonicalize_format(DFSDL_Surface *src) { fmt.colorkey = 0; fmt.alpha = 255; - DFSDL_Surface *tgt = g_SDL_ConvertSurface(src, &fmt, 0); // SDL_SWSURFACE - g_SDL_FreeSurface(src); + DFSDL_Surface *tgt = DFSDL_ConvertSurface(src, &fmt, 0); // SDL_SWSURFACE + DFSDL_FreeSurface(src); return tgt; } @@ -76,41 +55,36 @@ const uint32_t TILE_HEIGHT_PX = 12; static size_t load_textures(color_ostream & out, const char * fname, long *texpos_start) { - if (!g_sdl_handle || !g_sdl_image_handle || !g_IMG_Load || !g_SDL_SetAlpha - || !g_SDL_CreateRGBSurface || !g_SDL_UpperBlit - || !g_SDL_ConvertSurface || !g_SDL_FreeSurface) - return 0; - - DFSDL_Surface *s = g_IMG_Load(fname); + DFSDL_Surface *s = DFIMG_Load(fname); if (!s) { out.printerr("unable to load textures from '%s'\n", fname); return 0; } s = canonicalize_format(s); - g_SDL_SetAlpha(s, 0, 255); + DFSDL_SetAlpha(s, 0, 255); int dimx = s->w / TILE_WIDTH_PX; int dimy = s->h / TILE_HEIGHT_PX; long count = 0; for (int y = 0; y < dimy; y++) { for (int x = 0; x < dimx; x++) { - DFSDL_Surface *tile = g_SDL_CreateRGBSurface(0, // SDL_SWSURFACE + DFSDL_Surface *tile = DFSDL_CreateRGBSurface(0, // SDL_SWSURFACE TILE_WIDTH_PX, TILE_HEIGHT_PX, 32, s->format->Rmask, s->format->Gmask, s->format->Bmask, s->format->Amask); - g_SDL_SetAlpha(tile, 0,255); + DFSDL_SetAlpha(tile, 0,255); DFSDL_Rect vp; vp.x = TILE_WIDTH_PX * x; vp.y = TILE_HEIGHT_PX * y; vp.w = TILE_WIDTH_PX; vp.h = TILE_HEIGHT_PX; - g_SDL_UpperBlit(s, &vp, tile, NULL); + DFSDL_UpperBlit(s, &vp, tile, NULL); if (!count++) *texpos_start = enabler->textures.raws.size(); enabler->textures.raws.push_back(tile); } } - g_SDL_FreeSurface(s); + DFSDL_FreeSurface(s); DEBUG(textures,out).print("loaded %zd textures from '%s'\n", count, fname); return count; @@ -133,35 +107,6 @@ void Textures::init(color_ostream &out) { if (g_loaded) return; - for (auto &lib_str : SDL_LIBS) { - if ((g_sdl_handle = OpenPlugin(lib_str.c_str()))) - break; - } - for (auto &lib_str : SDL_IMAGE_LIBS) { - if ((g_sdl_image_handle = OpenPlugin(lib_str.c_str()))) - break; - } - - if (!g_sdl_handle) { - out.printerr("Could not find SDL; DFHack textures not loaded.\n"); - } else if (!g_sdl_image_handle) { - out.printerr("Could not find SDL_image; DFHack textures not loaded.\n"); - } else { - #define bind(handle, name) \ - g_##name = (decltype(g_##name))LookupPlugin(handle, #name); \ - if (!g_##name) { \ - out.printerr("Could not find: " #name "; DFHack textures not loaded\n"); \ - } - - bind(g_sdl_image_handle, IMG_Load); - bind(g_sdl_handle, SDL_SetAlpha); - bind(g_sdl_handle, SDL_CreateRGBSurface); - bind(g_sdl_handle, SDL_UpperBlit); - bind(g_sdl_handle, SDL_ConvertSurface); - bind(g_sdl_handle, SDL_FreeSurface); - #undef bind - } - bool is_pre_world = num_textures == textures.init_texture_size; g_num_dfhack_textures = load_textures(out, "hack/data/art/dfhack.png", @@ -186,7 +131,7 @@ void Textures::cleanup() { auto &raws = textures.raws; size_t texpos_end = g_dfhack_logo_texpos_start + g_num_dfhack_textures; for (size_t idx = g_dfhack_logo_texpos_start; idx <= texpos_end; ++idx) { - SDL_FreeSurface(raws[idx]); + DFSDL_FreeSurface((DFSDL_Surface *)raws[idx]); raws[idx] = NULL; } @@ -196,11 +141,6 @@ void Textures::cleanup() { g_loaded = false; g_num_dfhack_textures = 0; g_dfhack_logo_texpos_start = -1; - - if (g_sdl_handle) { - ClosePlugin(g_sdl_handle); - g_sdl_handle = nullptr; - } } long Textures::getDfhackLogoTexposStart() { From e9a5ec13af4f1f6b5a4905c0d3f916e130a64477 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 17:55:01 -0800 Subject: [PATCH 0032/2222] update logo image --- data/art/dfhack.png | Bin 1480 -> 1465 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/art/dfhack.png b/data/art/dfhack.png index 6f146dc2fa4a8272e9c941b2640a0d80e6d63ce3..17719f084a45a3b037bab5fd4355d5f76f674fbd 100644 GIT binary patch delta 1447 zcmV;Y1z7sX3%LuBB!7TOL_t(o!|j({NRwe3$A8=Mi(DqfiZHMO3z6s`BM8@uYLG~D z5JdO^QXX`cCuYzWs}4K}%b-A_15YCGEKHCViD*Uy>UO;c@4Fl4 zvsouEopiwi&+}Z@eck`(y8rk8x*wg02!GM^{AK$GU=4uJ=YONGuaBvzDaOagNhXun z?RFJpwOY~Z|BKaRGI8+WL7JMHShHpg0Po+w=k43Kgu`Lx=H`e*BE;iy{CDM)#c35+1V+Ljg49FoKB~R zh$sLeB5iGLnt#C_k4F|47v<*7n;KtVU*GcK^7!#%P0WiIFO+|Fc2*uedL$PvT#!T} zp#^a7-aSo%nVA_85sAfOa^l1ZjsL@k4{E&A>D2gx!Jw>WStQ)DWs7F53eZ_ne^*x*OG`@t7>!1ae|~;`brvp1<8U}MUl55zlz%^!N=ZvgOWrED-ENJqwY62A zJ$oh&heI3=hq&EtNvG2)XBL3huU~5t1OkDql0A9yBrg|6M@Ms#aOE{MHI;h_3JVK0 z6-p!$Swu!(jjx78W$V!NEa9gwfGaj7B5f-Q8^3v?*H$lq%``Dm6QJwfMhbM z?$hZsgM))vev`>WO-)T+0P%SI&wD{I7+kdiE|-hm-rg+#_3PKw#JQFy7K{BkfKQ)3 zX#%`nF93eOpZ4~4jlaIWK4-NuR!~sz`(^~NxPQ3Fz`%eeprfOM+1XhJ1_l@&9?tST zc<>-=R|En9&G@;wx!()X*w~nyKY#wW``2VL$;p!^<>JMQVz=96X=zDBM0`G<^!NA6 z?%lfuki&-$OLcX%8rRd)BOgD0%o3oC=|n{4=jT_I%)-J#3_6Ztu? zyd@tyc8u!kYNe8)P)PB_?RGOXG^FAi8XA;bFSqjYay2GX`jyJ40gyRVBoYY)CLWKg zPWkfXOUA~=sHmt=+qJQ=QT-p}I!`8(N!6&L(dg;`GQ|7(^{XOZC=^mEXn(O-Xl`!i z)TvVd{QC6^v)PQ%XynqROMLnAMdcbF9|z#)&z}m+^z?K#$ZOZGU0beT_wV0lWMqVN zI?dG76h%cvq*5uu;jofgtJTV_Tet9fy&O7ph}zm(EEWqQg57Q>91gR2^JYdzM`>ti zz+$mbQBgr@X({jCy~AuaD` delta 1462 zcmV;n1xfn33&;zQB!7=dL_t(og~eD&NL61L|I}wrXyxovoGA4u*A z7DkGKC=ocI8^K`_feUe=f+#pVbfF?fCFTsJf|PtWUTKeUsN(Bak0dzVYtgWpfKPVaTr@;RMt>5|IS?8eDh%-b`*)ch92^w% zWo2bD?eFg|=+4h#$kHum=RqPgO=8nKvpkXml!q?YV zu)*y`tgfyKIxISf$B!RFad9ym9UXCFat@fAn+pT%@9%TGRLztK3kxGotyT*e>PiQ`eSiCQtugM#$B!Qcd2DPf{aRaF3%bR{ z#XonTv$In$7#$szX-`j2L9f+nubsfb!QomxmxduBAx6{Y`Sa(@*Vp%d`vI2M%ld?b z1a=MdSD?V=!S#`T6+?sfp=lVPQe}jq%{(;zG6I@bHj&K2}PsfX0r) zgtp-y-H5Ott*oqokB^TeI^gO6Mr?a~JKb(DqMtr}3cbC(Fh4&JPEJmcnVAW%U%#f+ z*x1;}D}4F#CA@j_2HM)%Xp+{}R@%v&oPQkn`t>VyOuRK=FPWH_P&&W|;nvm`3kwUQ zbBl_KBAKzSRM9ns6n%dEV-oapK4X)Xph2N1Qj zwPcu@nkoh=jb%YrRu<8lo13h=yPN3k?QMB11UmHa;X^hxH6;e25*Tm@ynjea zz;=qj?L)Y`yVDlW&dyS^YG`O6IUZeeb2HJ>r4$tX1cOwg(FlRT?ZD8`kg!UPMnk#> z4;~P|v9ZA_Dk|jdRqjT3csR?=%_TZNKVRO>($Z3*A3b_>{ShoKE|MV@9_ga#B|Veo)QxiiSF<3ho+_`*=}rXjDDrSM1NddT<{Yr zV*t-EI5?Qj%EH1z4jg>4v$JLT Date: Wed, 4 Jan 2023 10:36:28 -0800 Subject: [PATCH 0033/2222] move dfhack-config into data --- {dfhack-config => data/dfhack-config}/autonick.txt | 0 {dfhack-config => data/dfhack-config}/dfstatus.lua | 0 {dfhack-config => data/dfhack-config}/dwarfmonitor.json | 0 {dfhack-config => data/dfhack-config}/init/default.dfhack.init | 0 {dfhack-config => data/dfhack-config}/init/default.onLoad.init | 0 {dfhack-config => data/dfhack-config}/init/default.onMapLoad.init | 0 .../dfhack-config}/init/default.onMapUnload.init | 0 {dfhack-config => data/dfhack-config}/init/default.onUnload.init | 0 {dfhack-config => data/dfhack-config}/init/dfhack.init | 0 {dfhack-config => data/dfhack-config}/init/onLoad.init | 0 {dfhack-config => data/dfhack-config}/init/onMapLoad.init | 0 {dfhack-config => data/dfhack-config}/init/onMapUnload.init | 0 {dfhack-config => data/dfhack-config}/init/onUnload.init | 0 {dfhack-config => data/dfhack-config}/overlay.json | 0 {dfhack-config => data/dfhack-config}/quickfort/aliases.txt | 0 {dfhack-config => data/dfhack-config}/quickfort/quickfort.txt | 0 {dfhack-config => data/dfhack-config}/script-paths.txt | 0 {dfhack-config => data/dfhack-config}/scripts/README.md | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename {dfhack-config => data/dfhack-config}/autonick.txt (100%) rename {dfhack-config => data/dfhack-config}/dfstatus.lua (100%) rename {dfhack-config => data/dfhack-config}/dwarfmonitor.json (100%) rename {dfhack-config => data/dfhack-config}/init/default.dfhack.init (100%) rename {dfhack-config => data/dfhack-config}/init/default.onLoad.init (100%) rename {dfhack-config => data/dfhack-config}/init/default.onMapLoad.init (100%) rename {dfhack-config => data/dfhack-config}/init/default.onMapUnload.init (100%) rename {dfhack-config => data/dfhack-config}/init/default.onUnload.init (100%) rename {dfhack-config => data/dfhack-config}/init/dfhack.init (100%) rename {dfhack-config => data/dfhack-config}/init/onLoad.init (100%) rename {dfhack-config => data/dfhack-config}/init/onMapLoad.init (100%) rename {dfhack-config => data/dfhack-config}/init/onMapUnload.init (100%) rename {dfhack-config => data/dfhack-config}/init/onUnload.init (100%) rename {dfhack-config => data/dfhack-config}/overlay.json (100%) rename {dfhack-config => data/dfhack-config}/quickfort/aliases.txt (100%) rename {dfhack-config => data/dfhack-config}/quickfort/quickfort.txt (100%) rename {dfhack-config => data/dfhack-config}/script-paths.txt (100%) rename {dfhack-config => data/dfhack-config}/scripts/README.md (100%) diff --git a/dfhack-config/autonick.txt b/data/dfhack-config/autonick.txt similarity index 100% rename from dfhack-config/autonick.txt rename to data/dfhack-config/autonick.txt diff --git a/dfhack-config/dfstatus.lua b/data/dfhack-config/dfstatus.lua similarity index 100% rename from dfhack-config/dfstatus.lua rename to data/dfhack-config/dfstatus.lua diff --git a/dfhack-config/dwarfmonitor.json b/data/dfhack-config/dwarfmonitor.json similarity index 100% rename from dfhack-config/dwarfmonitor.json rename to data/dfhack-config/dwarfmonitor.json diff --git a/dfhack-config/init/default.dfhack.init b/data/dfhack-config/init/default.dfhack.init similarity index 100% rename from dfhack-config/init/default.dfhack.init rename to data/dfhack-config/init/default.dfhack.init diff --git a/dfhack-config/init/default.onLoad.init b/data/dfhack-config/init/default.onLoad.init similarity index 100% rename from dfhack-config/init/default.onLoad.init rename to data/dfhack-config/init/default.onLoad.init diff --git a/dfhack-config/init/default.onMapLoad.init b/data/dfhack-config/init/default.onMapLoad.init similarity index 100% rename from dfhack-config/init/default.onMapLoad.init rename to data/dfhack-config/init/default.onMapLoad.init diff --git a/dfhack-config/init/default.onMapUnload.init b/data/dfhack-config/init/default.onMapUnload.init similarity index 100% rename from dfhack-config/init/default.onMapUnload.init rename to data/dfhack-config/init/default.onMapUnload.init diff --git a/dfhack-config/init/default.onUnload.init b/data/dfhack-config/init/default.onUnload.init similarity index 100% rename from dfhack-config/init/default.onUnload.init rename to data/dfhack-config/init/default.onUnload.init diff --git a/dfhack-config/init/dfhack.init b/data/dfhack-config/init/dfhack.init similarity index 100% rename from dfhack-config/init/dfhack.init rename to data/dfhack-config/init/dfhack.init diff --git a/dfhack-config/init/onLoad.init b/data/dfhack-config/init/onLoad.init similarity index 100% rename from dfhack-config/init/onLoad.init rename to data/dfhack-config/init/onLoad.init diff --git a/dfhack-config/init/onMapLoad.init b/data/dfhack-config/init/onMapLoad.init similarity index 100% rename from dfhack-config/init/onMapLoad.init rename to data/dfhack-config/init/onMapLoad.init diff --git a/dfhack-config/init/onMapUnload.init b/data/dfhack-config/init/onMapUnload.init similarity index 100% rename from dfhack-config/init/onMapUnload.init rename to data/dfhack-config/init/onMapUnload.init diff --git a/dfhack-config/init/onUnload.init b/data/dfhack-config/init/onUnload.init similarity index 100% rename from dfhack-config/init/onUnload.init rename to data/dfhack-config/init/onUnload.init diff --git a/dfhack-config/overlay.json b/data/dfhack-config/overlay.json similarity index 100% rename from dfhack-config/overlay.json rename to data/dfhack-config/overlay.json diff --git a/dfhack-config/quickfort/aliases.txt b/data/dfhack-config/quickfort/aliases.txt similarity index 100% rename from dfhack-config/quickfort/aliases.txt rename to data/dfhack-config/quickfort/aliases.txt diff --git a/dfhack-config/quickfort/quickfort.txt b/data/dfhack-config/quickfort/quickfort.txt similarity index 100% rename from dfhack-config/quickfort/quickfort.txt rename to data/dfhack-config/quickfort/quickfort.txt diff --git a/dfhack-config/script-paths.txt b/data/dfhack-config/script-paths.txt similarity index 100% rename from dfhack-config/script-paths.txt rename to data/dfhack-config/script-paths.txt diff --git a/dfhack-config/scripts/README.md b/data/dfhack-config/scripts/README.md similarity index 100% rename from dfhack-config/scripts/README.md rename to data/dfhack-config/scripts/README.md From c70c4131bd9c9a19b6e331a3f2b286c102025883 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 11:35:50 -0800 Subject: [PATCH 0034/2222] don't include dfhack-config/ in release tarball create it at runtime move files around so no dfhack-owned files are in there --- CMakeLists.txt | 2 - data/CMakeLists.txt | 12 ++-- .../init/examples}/onMapLoad_dreamfort.init | 0 data/examples/README.md | 9 --- library/Core.cpp | 63 ++++++++++--------- 5 files changed, 38 insertions(+), 48 deletions(-) rename data/{examples/init => dfhack-config/init/examples}/onMapLoad_dreamfort.init (100%) delete mode 100644 data/examples/README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 5877f69c6..2b518f809 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,8 +448,6 @@ endif() file(WRITE "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" ${DFHACK_SETARCH}) install(FILES "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" DESTINATION "${DFHACK_DATA_DESTINATION}") -install(DIRECTORY dfhack-config/ DESTINATION dfhack-config/default) - # build the plugins if(BUILD_PLUGINS) add_subdirectory(plugins) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 8991f1c24..8cbd910cb 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -1,23 +1,23 @@ +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dfhack-config/ + DESTINATION "${DFHACK_DATA_DESTINATION}/data/dfhack-config-defaults") + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/init/ DESTINATION "${DFHACK_DATA_DESTINATION}/init") install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/base_command_counts.json - DESTINATION "${DFHACK_DATA_DESTINATION}/data/base_command_counts.json") + DESTINATION "${DFHACK_DATA_DESTINATION}/data") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/quickfort/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/quickfort") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/orders/ - DESTINATION dfhack-config/orders/library) + DESTINATION "${DFHACK_DATA_DESTINATION}/data/orders") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/art/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/art") -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/examples/ - DESTINATION "${DFHACK_DATA_DESTINATION}/examples") - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/professions/ - DESTINATION dfhack-config/professions/library) + DESTINATION "${DFHACK_DATA_DESTINATION}/data/professions") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/blueprints/ DESTINATION blueprints diff --git a/data/examples/init/onMapLoad_dreamfort.init b/data/dfhack-config/init/examples/onMapLoad_dreamfort.init similarity index 100% rename from data/examples/init/onMapLoad_dreamfort.init rename to data/dfhack-config/init/examples/onMapLoad_dreamfort.init diff --git a/data/examples/README.md b/data/examples/README.md deleted file mode 100644 index fb2b8e3a1..000000000 --- a/data/examples/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# The DFHack Example Configuration File Library - -This folder contains ready-to-use examples of various DFHack configuration -files. You can use them by copying them to appropriate folders where DFHack -and its plugins can find them. You can use them unmodified, or you can -customize them to better suit your preferences. - -For information on each of the files in this library, see the -[DFHack Example Configuration File Guide](https://docs.dfhack.org/en/stable/docs/guides/examples-guide.html). diff --git a/library/Core.cpp b/library/Core.cpp index a3979ee2f..df6ddbe36 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -106,6 +106,9 @@ namespace DFHack { DBG_DECLARE(core,keybinding,DebugCategory::LINFO); DBG_DECLARE(core,script,DebugCategory::LINFO); +static const std::string CONFIG_PATH = "dfhack-config/"; +static const std::string CONFIG_DEFAULTS_PATH = "hack/data/dfhack-config-defaults/"; + class MainThread { public: //! MainThread::suspend keeps the main DF thread suspended from Core::Init to @@ -486,10 +489,10 @@ void Core::getScriptPaths(std::vector *dest) { lock_guard lock(script_path_mutex); dest->clear(); - string df_path = this->p->getPath(); + string df_path = this->p->getPath() + "/"; for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it) dest->push_back(*it); - dest->push_back(df_path + "/dfhack-config/scripts"); + dest->push_back(df_path + CONFIG_PATH + "scripts"); if (df::global::world && isWorldLoaded()) { string save = World::ReadWorldFolder(); if (save.size()) @@ -518,7 +521,7 @@ string Core::findScript(string name) bool loadScriptPaths(color_ostream &out, bool silent = false) { using namespace std; - string filename("dfhack-config/script-paths.txt"); + string filename(CONFIG_PATH + "script-paths.txt"); ifstream file(filename); if (!file) { @@ -1311,11 +1314,11 @@ static void run_dfhack_init(color_ostream &out, Core *core) } // load baseline defaults - core->loadScriptFile(out, "dfhack-config/init/default.dfhack.init", false); + core->loadScriptFile(out, CONFIG_PATH + "init/default.dfhack.init", false); // load user overrides std::vector prefixes(1, "dfhack"); - loadScriptFiles(core, out, prefixes, "dfhack-config/init"); + loadScriptFiles(core, out, prefixes, CONFIG_PATH + "init"); } // Load dfhack.init in a dedicated thread (non-interactive console mode) @@ -1331,14 +1334,14 @@ void fInitthread(void * iodata) // A thread function... for the interactive console. void fIOthread(void * iodata) { - static const char * HISTORY_FILE = "dfhack-config/dfhack.history"; + static const std::string HISTORY_FILE = CONFIG_PATH + "dfhack.history"; IODATA * iod = ((IODATA*) iodata); Core * core = iod->core; PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr; CommandHistory main_history; - main_history.load(HISTORY_FILE); + main_history.load(HISTORY_FILE.c_str()); Console & con = core->getConsole(); if (plug_mgr == 0) @@ -1379,7 +1382,7 @@ void fIOthread(void * iodata) { // a proper, non-empty command was entered main_history.add(command); - main_history.save(HISTORY_FILE); + main_history.save(HISTORY_FILE.c_str()); } auto rv = core->runCommand(con, command); @@ -1614,46 +1617,44 @@ bool Core::Init() // initialize data defs virtual_identity::Init(this); + // create config directory if it doesn't already exist + if (!Filesystem::mkdir_recursive(CONFIG_PATH)) + con.printerr("Failed to create config directory: '%s'\n", CONFIG_PATH.c_str()); + // copy over default config files if necessary std::map config_files; std::map default_config_files; - if (Filesystem::listdir_recursive("dfhack-config", config_files, 10, false) != 0) - con.printerr("Failed to list directory: dfhack-config"); - else if (Filesystem::listdir_recursive("dfhack-config/default", default_config_files, 10, false) != 0) - con.printerr("Failed to list directory: dfhack-config/default"); + if (Filesystem::listdir_recursive(CONFIG_PATH, config_files, 10, false) != 0) + con.printerr("Failed to list directory: '%s'\n", CONFIG_PATH.c_str()); + else if (Filesystem::listdir_recursive(CONFIG_DEFAULTS_PATH, default_config_files, 10, false) != 0) + con.printerr("Failed to list directory: '%s'\n", CONFIG_DEFAULTS_PATH.c_str()); else { // ensure all config file directories exist before we start copying files - for (auto it = default_config_files.begin(); it != default_config_files.end(); ++it) - { + for (auto &entry : default_config_files) { // skip over files - if (!it->second) + if (!entry.second) continue; - std::string dirname = "dfhack-config/" + it->first; + std::string dirname = CONFIG_PATH + entry.first; if (!Filesystem::mkdir_recursive(dirname)) - { con.printerr("Failed to create config directory: '%s'\n", dirname.c_str()); - } } // copy files from the default tree that don't already exist in the config tree - for (auto it = default_config_files.begin(); it != default_config_files.end(); ++it) - { + for (auto &entry : default_config_files) { // skip over directories - if (it->second) + if (entry.second) continue; - std::string filename = it->first; - if (config_files.find(filename) == config_files.end()) - { - std::string src_file = std::string("dfhack-config/default/") + filename; + std::string filename = entry.first; + if (!config_files.count(filename)) { + std::string src_file = CONFIG_DEFAULTS_PATH + filename; if (!Filesystem::isfile(src_file)) continue; - std::string dest_file = std::string("dfhack-config/") + filename; + std::string dest_file = CONFIG_PATH + filename; std::ifstream src(src_file, std::ios::binary); std::ofstream dest(dest_file, std::ios::binary); - if (!src.good() || !dest.good()) - { - con.printerr("Copy failed: %s\n", filename.c_str()); + if (!src.good() || !dest.good()) { + con.printerr("Copy failed: '%s'\n", filename.c_str()); continue; } dest << src.rdbuf(); @@ -2090,9 +2091,9 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve const std::vector& set = i->second; // load baseline defaults - this->loadScriptFile(out, "dfhack-config/init/default." + set[0] + ".init", false); + this->loadScriptFile(out, CONFIG_PATH + "init/default." + set[0] + ".init", false); - loadScriptFiles(this, out, set, "dfhack-config/init"); + loadScriptFiles(this, out, set, CONFIG_PATH + "init"); loadScriptFiles(this, out, set, rawFolder); loadScriptFiles(this, out, set, rawFolder + "objects/"); } From 9cbcd81f630d35bd43b1c180d2b803fd9a37948c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 21:24:52 -0800 Subject: [PATCH 0035/2222] fix format strings for vars that were size_t --- library/modules/Textures.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index 32ed6453c..928329f83 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -86,7 +86,7 @@ static size_t load_textures(color_ostream & out, const char * fname, } DFSDL_FreeSurface(s); - DEBUG(textures,out).print("loaded %zd textures from '%s'\n", count, fname); + DEBUG(textures,out).print("loaded %d textures from '%s'\n", count, fname); return count; } @@ -112,7 +112,7 @@ void Textures::init(color_ostream &out) { g_num_dfhack_textures = load_textures(out, "hack/data/art/dfhack.png", &g_dfhack_logo_texpos_start); - DEBUG(textures,out).print("loaded %zd textures\n", g_num_dfhack_textures); + DEBUG(textures,out).print("loaded %d textures\n", g_num_dfhack_textures); if (is_pre_world) textures.init_texture_size += g_num_dfhack_textures; From 412531bf038a758f8fd29800b0bd907b29e34e93 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 21:29:45 -0800 Subject: [PATCH 0036/2222] really fix the format string this time --- library/modules/Textures.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index 928329f83..70011b627 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -86,7 +86,7 @@ static size_t load_textures(color_ostream & out, const char * fname, } DFSDL_FreeSurface(s); - DEBUG(textures,out).print("loaded %d textures from '%s'\n", count, fname); + DEBUG(textures,out).print("loaded %ld textures from '%s'\n", count, fname); return count; } @@ -112,7 +112,7 @@ void Textures::init(color_ostream &out) { g_num_dfhack_textures = load_textures(out, "hack/data/art/dfhack.png", &g_dfhack_logo_texpos_start); - DEBUG(textures,out).print("loaded %d textures\n", g_num_dfhack_textures); + DEBUG(textures,out).print("loaded %ld textures\n", g_num_dfhack_textures); if (is_pre_world) textures.init_texture_size += g_num_dfhack_textures; From 5e5775f5e353e57d6ccb4f92110b10fc45c7a85a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 17:11:01 -0800 Subject: [PATCH 0037/2222] rename globals according to structures update --- library/Core.cpp | 30 +-- library/DataStatics.cpp | 2 +- library/RemoteTools.cpp | 24 +-- library/include/DebugManager.h | 4 +- library/include/RemoteClient.h | 2 +- library/include/modules/Gui.h | 2 +- library/include/modules/Kitchen.h | 2 +- library/modules/Buildings.cpp | 14 +- library/modules/Burrows.cpp | 24 +-- library/modules/EventManager.cpp | 12 +- library/modules/Gui.cpp | 100 ++++----- library/modules/Items.cpp | 12 +- library/modules/Job.cpp | 2 +- library/modules/Kitchen.cpp | 94 ++++----- library/modules/Materials.cpp | 6 +- library/modules/Screen.cpp | 2 +- library/modules/Units.cpp | 20 +- plugins/.gitignore | 1 + plugins/add-spatter.cpp | 4 +- plugins/autochop.cpp | 8 +- plugins/autofarm.cpp | 6 +- plugins/autogems.cpp | 4 +- plugins/autolabor/autohauler.cpp | 6 +- plugins/autolabor/autolabor.cpp | 8 +- plugins/autolabor/joblabormapper.cpp | 2 +- plugins/autolabor/labormanager.cpp | 22 +- plugins/automaterial.cpp | 14 +- plugins/automelt.cpp | 4 +- plugins/autotrade.cpp | 4 +- plugins/buildingplan/buildingplan-planner.cpp | 2 +- plugins/buildingplan/buildingplan.cpp | 14 +- plugins/burrows.cpp | 14 +- plugins/confirm.cpp | 2 +- plugins/createitem.cpp | 6 +- plugins/debug.cpp | 2 +- plugins/devel/kittens.cpp | 8 +- plugins/devel/stockcheck.cpp | 6 +- plugins/devel/stripcaged.cpp | 4 +- plugins/dig-now.cpp | 6 +- plugins/dig.cpp | 6 +- plugins/diggingInvaders/assignJob.cpp | 4 +- plugins/diggingInvaders/diggingInvaders.cpp | 2 +- plugins/dwarfmonitor.cpp | 4 +- plugins/dwarfvet.cpp | 6 +- plugins/embark-assistant/finder_ui.cpp | 198 +++++++++--------- plugins/eventful.cpp | 2 +- plugins/getplants.cpp | 6 +- plugins/jobutils.cpp | 12 +- plugins/manipulator.cpp | 8 +- plugins/misery.cpp | 4 +- plugins/mousequery.cpp | 30 +-- plugins/nestboxes.cpp | 6 +- plugins/power-meter.cpp | 6 +- .../adventure_control.cpp | 12 +- .../remotefortressreader/dwarf_control.cpp | 26 +-- .../remotefortressreader.cpp | 10 +- plugins/rename.cpp | 22 +- plugins/rendermax/renderer_light.cpp | 2 +- plugins/rendermax/rendermax.cpp | 2 +- plugins/search.cpp | 14 +- plugins/siege-engine.cpp | 8 +- plugins/sort.cpp | 12 +- plugins/spectate/pause.cpp | 6 +- plugins/spectate/spectate.cpp | 8 +- plugins/steam-engine.cpp | 8 +- plugins/stockflow.cpp | 8 +- plugins/stockpiles/StockpileSerializer.h | 2 +- plugins/stockpiles/stockpiles.cpp | 20 +- plugins/stocks.cpp | 2 +- plugins/strangemood.cpp | 16 +- plugins/tailor.cpp | 10 +- plugins/trackstop.cpp | 6 +- plugins/tweak/tweak.cpp | 16 +- plugins/tweak/tweaks/block-labors.h | 4 +- plugins/tweak/tweaks/burrow-name-cancel.h | 12 +- plugins/tweak/tweaks/cage-butcher.h | 4 +- plugins/tweak/tweaks/eggs-fertile.h | 10 +- plugins/tweak/tweaks/farm-plot-select.h | 10 +- plugins/tweak/tweaks/hide-priority.h | 12 +- plugins/tweak/tweaks/hotkey-clear.h | 10 +- plugins/tweak/tweaks/max-wheelbarrow.h | 2 +- plugins/tweak/tweaks/shift-8-scroll.h | 4 +- plugins/tweak/tweaks/stable-cursor.h | 4 +- plugins/uicommon.h | 8 +- plugins/workflow.cpp | 8 +- plugins/zone.cpp | 12 +- 86 files changed, 552 insertions(+), 551 deletions(-) create mode 100644 plugins/.gitignore diff --git a/library/Core.cpp b/library/Core.cpp index df6ddbe36..d54218b5c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -65,8 +65,8 @@ using namespace std; using namespace DFHack; -#include "df/ui.h" -#include "df/ui_sidebar_menus.h" +#include "df/plotinfost.h" +#include "df/gamest.h" #include "df/world.h" #include "df/world_data.h" #include "df/interfacest.h" @@ -1307,7 +1307,7 @@ bool Core::loadScriptFile(color_ostream &out, string fname, bool silent) static void run_dfhack_init(color_ostream &out, Core *core) { CoreSuspender lock; - if (!df::global::world || !df::global::ui || !df::global::gview) + if (!df::global::world || !df::global::plotinfo || !df::global::gview) { out.printerr("Key globals are missing, skipping loading dfhack.init.\n"); return; @@ -1712,10 +1712,10 @@ bool Core::Init() if (!listen.get()) cerr << "TCP listen failed.\n"; - if (df::global::ui_sidebar_menus) + if (df::global::game) { vector args; - const string & raw = df::global::ui_sidebar_menus->command_line.original; + const string & raw = df::global::game->command_line.original; size_t offset = 0; while (offset < raw.size()) { @@ -1879,7 +1879,7 @@ void Core::doUpdate(color_ostream &out) strict_virtual_cast(screen); // save data (do this before updating last_world_data_ptr and triggering unload events) - if ((df::global::ui->main.autosave_request && !d->last_autosave_request) || + if ((df::global::plotinfo->main.autosave_request && !d->last_autosave_request) || (is_load_save && !d->was_load_save && strict_virtual_cast(screen))) { doSaveData(out); @@ -1941,7 +1941,7 @@ void Core::doUpdate(color_ostream &out) // Execute per-frame handlers onUpdate(out); - d->last_autosave_request = df::global::ui->main.autosave_request; + d->last_autosave_request = df::global::plotinfo->main.autosave_request; d->was_load_save = is_load_save; out << std::flush; @@ -2288,14 +2288,14 @@ bool Core::ncurses_wgetch(int in, int & out) /* TODO: understand how this changes for v50 int idx = in - KEY_F(1); // FIXME: copypasta, push into a method! - if(df::global::ui && df::global::gview) + if(df::global::plotinfo && df::global::gview) { df::viewscreen * ws = Gui::getCurViewscreen(); if (strict_virtual_cast(ws) && - df::global::ui->main.mode != ui_sidebar_mode::Hotkeys && - df::global::ui->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None) + df::global::plotinfo->main.mode != ui_sidebar_mode::Hotkeys && + df::global::plotinfo->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None) { - setHotkeyCmd(df::global::ui->main.hotkeys[idx].name); + setHotkeyCmd(df::global::plotinfo->main.hotkeys[idx].name); return false; } else @@ -2422,7 +2422,7 @@ int Core::DFH_SDL_Event(SDL::Event* ev) bool Core::SelectHotkey(int sym, int modifiers) { // Find the topmost viewscreen - if (!df::global::gview || !df::global::ui) + if (!df::global::gview || !df::global::plotinfo) return false; df::viewscreen *screen = &df::global::gview->view; @@ -2477,10 +2477,10 @@ bool Core::SelectHotkey(int sym, int modifiers) idx += 8; if (strict_virtual_cast(screen) && - df::global::ui->main.mode != ui_sidebar_mode::Hotkeys && - df::global::ui->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None) + df::global::plotinfo->main.mode != ui_sidebar_mode::Hotkeys && + df::global::plotinfo->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None) { - cmd = df::global::ui->main.hotkeys[idx].name; + cmd = df::global::plotinfo->main.hotkeys[idx].name; } */ } diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp index 275cae584..fe60e5454 100644 --- a/library/DataStatics.cpp +++ b/library/DataStatics.cpp @@ -6,7 +6,7 @@ #include "df/world.h" #include "df/world_data.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "DataIdentity.h" diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index 1d45a19c0..c8fc810c8 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -59,8 +59,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "LuaTools.h" #include "DataDefs.h" -#include "df/ui.h" -#include "df/ui_advmode.h" +#include "df/plotinfost.h" +#include "df/adventurest.h" #include "df/world.h" #include "df/world_data.h" #include "df/unit.h" @@ -375,11 +375,11 @@ static command_result GetDFVersion(color_ostream &stream, static command_result GetWorldInfo(color_ostream &stream, const EmptyMessage *, GetWorldInfoOut *out) { - using df::global::ui; - using df::global::ui_advmode; + using df::global::plotinfo; + using df::global::adventure; using df::global::world; - if (!ui || !world || !Core::getInstance().isWorldLoaded()) + if (!plotinfo || !world || !Core::getInstance().isWorldLoaded()) return CR_NOT_FOUND; df::game_type gt = game_type::DWARF_MAIN; @@ -397,10 +397,10 @@ static command_result GetWorldInfo(color_ostream &stream, case game_type::DWARF_RECLAIM: case game_type::DWARF_UNRETIRE: out->set_mode(GetWorldInfoOut::MODE_DWARF); - out->set_civ_id(ui->civ_id); - out->set_site_id(ui->site_id); - out->set_group_id(ui->group_id); - out->set_race_id(ui->race_id); + out->set_civ_id(plotinfo->civ_id); + out->set_site_id(plotinfo->site_id); + out->set_group_id(plotinfo->group_id); + out->set_race_id(plotinfo->race_id); break; case game_type::ADVENTURE_MAIN: @@ -410,10 +410,10 @@ static command_result GetWorldInfo(color_ostream &stream, if (auto unit = vector_get(world->units.active, 0)) out->set_player_unit_id(unit->id); - if (!ui_advmode) + if (!adventure) break; - if (auto nemesis = vector_get(world->nemesis.all, ui_advmode->player_id)) + if (auto nemesis = vector_get(world->nemesis.all, adventure->player_id)) { if (nemesis->figure) out->set_player_histfig_id(nemesis->figure->id); @@ -613,7 +613,7 @@ static command_result ListUnits(color_ostream &stream, static command_result ListSquads(color_ostream &stream, const ListSquadsIn *in, ListSquadsOut *out) { - auto entity = df::historical_entity::find(df::global::ui->group_id); + auto entity = df::historical_entity::find(df::global::plotinfo->group_id); if (!entity) return CR_NOT_FOUND; diff --git a/library/include/DebugManager.h b/library/include/DebugManager.h index 57de0f279..d910efd4a 100644 --- a/library/include/DebugManager.h +++ b/library/include/DebugManager.h @@ -42,13 +42,13 @@ class DebugCategory; /*! * \brief Container holding all registered runtime debug categories * Singleton DebugManager is a minor extension to std::vector that allows signal - * callbacks to be attached from ui code that manages. + * callbacks to be attached from plotinfo code that manages. * * To avoid parallel plugin unload causing issues access to DebugManager must be * protected by mutex. The access mutex will be taken when * DFHack::DebugCategory::~DebugCategory performs unregister calls to * DFHack::DebugManager. The mutex will protect from memory disappearing while - * ui code is accessing or changing the runtime state. + * plotinfo code is accessing or changing the runtime state. * * Signal emitting happens from a locked contexts. Taking the * DFHack::DebugManager::access_mutex_ in a signal callback will results to a diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index e71b985cd..1948ea754 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -46,7 +46,7 @@ namespace DFHack CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded CR_OK = 0, // Success CR_FAILURE = 1, // Failure - CR_WRONG_USAGE = 2, // Wrong arguments or ui state + CR_WRONG_USAGE = 2, // Wrong arguments or plotinfo state CR_NOT_FOUND = 3 // Target object not found (for RPC mainly) }; diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 3ce2df0eb..33ea32939 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -33,7 +33,7 @@ distribution. #include "DataDefs.h" #include "df/init.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/announcement_type.h" #include "df/announcement_flags.h" #include "df/report_init.h" diff --git a/library/include/modules/Kitchen.h b/library/include/modules/Kitchen.h index ec161178e..3fde8edf8 100644 --- a/library/include/modules/Kitchen.h +++ b/library/include/modules/Kitchen.h @@ -75,7 +75,7 @@ DFHACK_EXPORT void clearLimits(); DFHACK_EXPORT std::size_t size(); -// Finds the index of a kitchen exclusion in ui.kitchen.exc_types. Returns -1 if not found. +// Finds the index of a kitchen exclusion in plotinfo.kitchen.exc_types. Returns -1 if not found. DFHACK_EXPORT int findExclusion(df::kitchen_exc_type type, df::item_type item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_index); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 4ec3f11d7..cd0924a36 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -81,14 +81,14 @@ using namespace DFHack; #include "df/job_item.h" #include "df/map_block.h" #include "df/tile_occupancy.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/ui_look_list.h" #include "df/unit.h" #include "df/unit_relationship_type.h" #include "df/world.h" using namespace df::enums; -using df::global::ui; +using df::global::plotinfo; using df::global::world; using df::global::d_init; using df::global::building_next_id; @@ -425,7 +425,7 @@ df::building *Buildings::allocInstance(df::coord pos, df::building_type type, in bld->y1 = bld->y2 = bld->centery = pos.y; bld->z = pos.z; - bld->race = ui->race_id; + bld->race = plotinfo->race_id; if (subtype != -1) bld->setSubtype(subtype); @@ -989,7 +989,7 @@ static void linkRooms(df::building *bld) } if (changed) - df::global::ui->equipment.update.bits.buildings = true; + df::global::plotinfo->equipment.update.bits.buildings = true; */ } @@ -1219,7 +1219,7 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorjob_items, 0, items[i]); if (items[i]->item_type == item_type::BOULDER) @@ -1238,7 +1238,7 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectortax_collection.rooms, linear_index(ui->tax_collection.rooms, bld->id)); + vector_erase_at(plotinfo->tax_collection.rooms, linear_index(plotinfo->tax_collection.rooms, bld->id)); // Assume: not used in punishment // Assume: not used in non-own jobs // Assume: does not affect pathfinding diff --git a/library/modules/Burrows.cpp b/library/modules/Burrows.cpp index 52e550960..c2fe45eae 100644 --- a/library/modules/Burrows.cpp +++ b/library/modules/Burrows.cpp @@ -42,14 +42,14 @@ using namespace std; #include "df/block_burrow_link.h" #include "df/burrow.h" #include "df/map_block.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; df::burrow *Burrows::findByName(std::string name) { @@ -77,11 +77,11 @@ void Burrows::clearUnits(df::burrow *burrow) burrow->units.clear(); /* TODO: understand how this changes for v50 - // Sync ui if active - if (ui && ui->main.mode == ui_sidebar_mode::Burrows && - ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id) + // Sync plotinfo if active + if (plotinfo && plotinfo->main.mode == ui_sidebar_mode::Burrows && + plotinfo->burrows.in_add_units_mode && plotinfo->burrows.sel_id == burrow->id) { - auto &sel = ui->burrows.sel_units; + auto &sel = plotinfo->burrows.sel_units; for (size_t i = 0; i < sel.size(); i++) sel[i] = false; @@ -99,7 +99,7 @@ bool Burrows::isAssignedUnit(df::burrow *burrow, df::unit *unit) void Burrows::setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable) { - using df::global::ui; + using df::global::plotinfo; CHECK_NULL_POINTER(unit); CHECK_NULL_POINTER(burrow); @@ -116,13 +116,13 @@ void Burrows::setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable) } /* TODO: understand how this changes for v50 - // Sync ui if active - if (ui && ui->main.mode == ui_sidebar_mode::Burrows && - ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id) + // Sync plotinfo if active + if (plotinfo && plotinfo->main.mode == ui_sidebar_mode::Burrows && + plotinfo->burrows.in_add_units_mode && plotinfo->burrows.sel_id == burrow->id) { - int idx = linear_index(ui->burrows.list_units, unit); + int idx = linear_index(plotinfo->burrows.list_units, unit); if (idx >= 0) - ui->burrows.sel_units[idx] = enable; + plotinfo->burrows.sel_units[idx] = enable; } */ } diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index e882febf8..02b1892e9 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -27,7 +27,7 @@ #include "df/job.h" #include "df/job_list_link.h" #include "df/report.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_flags1.h" #include "df/unit_inventory_item.h" @@ -297,14 +297,14 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event return; if (!df::global::job_next_id) return; - if (!df::global::ui) + if (!df::global::plotinfo) return; if (!df::global::world) return; nextItem = *df::global::item_next_id; nextBuilding = *df::global::building_next_id; - nextInvasion = df::global::ui->invasions.next_id; + nextInvasion = df::global::plotinfo->invasions.next_id; lastJobId = -1 + *df::global::job_next_id; constructions.clear(); @@ -807,13 +807,13 @@ static void manageSyndromeEvent(color_ostream& out) { } static void manageInvasionEvent(color_ostream& out) { - if (!df::global::ui) + if (!df::global::plotinfo) return; multimap copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end()); - if ( df::global::ui->invasions.next_id <= nextInvasion ) + if ( df::global::plotinfo->invasions.next_id <= nextInvasion ) return; - nextInvasion = df::global::ui->invasions.next_id; + nextInvasion = df::global::plotinfo->invasions.next_id; for (auto &key_value : copy) { EventHandler &handle = key_value.second; diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index f88331347..f59cdcb8b 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -74,10 +74,10 @@ using namespace DFHack; #include "df/report_zoom_type.h" #include "df/route_stockpile_link.h" #include "df/stop_depart_condition.h" -#include "df/ui_advmode.h" -#include "df/ui_build_selector.h" +#include "df/adventurest.h" +#include "df/buildreq.h" #include "df/ui_look_list.h" -#include "df/ui_sidebar_menus.h" +#include "df/gamest.h" #include "df/ui_unit_view_mode.h" #include "df/unit.h" #include "df/unit_inventory_item.h" @@ -101,9 +101,9 @@ using df::global::gps; using df::global::gview; using df::global::init; using df::global::selection_rect; -using df::global::ui; +using df::global::plotinfo; using df::global::ui_menu_width; -using df::global::ui_sidebar_menus; +using df::global::game; using df::global::world; /* TODO: understand how this changes for v50 @@ -158,9 +158,9 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) using df::global::ui_building_assign_items; using df::global::ui_building_in_assign; - focus += "/" + enum_item_key(ui->main.mode); + focus += "/" + enum_item_key(plotinfo->main.mode); - switch (ui->main.mode) + switch (plotinfo->main.mode) { case QueryBuilding: if (df::building *selected = world->selected_building) @@ -304,38 +304,38 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) break; case Burrows: - if (ui->burrows.in_confirm_delete) + if (plotinfo->burrows.in_confirm_delete) focus += "/ConfirmDelete"; - else if (ui->burrows.in_add_units_mode) + else if (plotinfo->burrows.in_add_units_mode) focus += "/AddUnits"; - else if (ui->burrows.in_edit_name_mode) + else if (plotinfo->burrows.in_edit_name_mode) focus += "/EditName"; - else if (ui->burrows.in_define_mode) + else if (plotinfo->burrows.in_define_mode) focus += "/Define"; else focus += "/List"; break; case Hauling: - if (ui->hauling.in_assign_vehicle) + if (plotinfo->hauling.in_assign_vehicle) { - auto vehicle = vector_get(ui->hauling.vehicles, ui->hauling.cursor_vehicle); + auto vehicle = vector_get(plotinfo->hauling.vehicles, plotinfo->hauling.cursor_vehicle); focus += "/AssignVehicle/" + std::string(vehicle ? "Some" : "None"); } else { - int idx = ui->hauling.cursor_top; - auto route = vector_get(ui->hauling.view_routes, idx); - auto stop = vector_get(ui->hauling.view_stops, idx); + int idx = plotinfo->hauling.cursor_top; + auto route = vector_get(plotinfo->hauling.view_routes, idx); + auto stop = vector_get(plotinfo->hauling.view_stops, idx); std::string tag = stop ? "Stop" : (route ? "Route" : "None"); - if (ui->hauling.in_name) + if (plotinfo->hauling.in_name) focus += "/Rename/" + tag; - else if (ui->hauling.in_stop) + else if (plotinfo->hauling.in_stop) { - int sidx = ui->hauling.cursor_stop; - auto cond = vector_get(ui->hauling.stop_conditions, sidx); - auto link = vector_get(ui->hauling.stop_links, sidx); + int sidx = plotinfo->hauling.cursor_stop; + auto cond = vector_get(plotinfo->hauling.stop_conditions, sidx); + auto link = vector_get(plotinfo->hauling.stop_links, sidx); focus += "/DefineStop"; @@ -364,12 +364,12 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) /* TODO: understand how this changes for v50 DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) { - using df::global::ui_advmode; + using df::global::adventure; - if (!ui_advmode) + if (!adventure) return; - focus += "/" + enum_item_key(ui_advmode->menu); + focus += "/" + enum_item_key(adventure->menu); } DEFINE_GET_FOCUS_STRING_HANDLER(unitlist) @@ -675,7 +675,7 @@ bool Gui::workshop_job_hotkey(df::viewscreen *top) using df::global::ui_workshop_in_add; using df::global::ui_workshop_job_cursor; - switch (ui->main.mode) { + switch (plotinfo->main.mode) { case QueryBuilding: { if (!ui_workshop_job_cursor) // allow missing @@ -712,7 +712,7 @@ bool Gui::build_selector_hotkey(df::viewscreen *top) using namespace ui_sidebar_mode; using df::global::ui_build_selector; - switch (ui->main.mode) { + switch (plotinfo->main.mode) { case Build: { if (!ui_build_selector) // allow missing @@ -739,7 +739,7 @@ bool Gui::view_unit_hotkey(df::viewscreen *top) /* TODO: understand how this changes for v50 using df::global::ui_selected_unit; - if (ui->main.mode != ui_sidebar_mode::ViewUnits) + if (plotinfo->main.mode != ui_sidebar_mode::ViewUnits) return false; if (!ui_selected_unit) // allow missing return false; @@ -1017,19 +1017,19 @@ df::unit *Gui::getAnyUnit(df::viewscreen *top) if (!Gui::dwarfmode_hotkey(top)) return NULL; - if (!ui) + if (!plotinfo) return NULL; // general assigning units in building, i.e. (q)uery cage -> (a)ssign if (ui_building_in_assign && *ui_building_in_assign && ui_building_assign_units && ui_building_item_cursor - && ui->main.mode != Zones) // dont show for (i) zone + && plotinfo->main.mode != Zones) // dont show for (i) zone return vector_get(*ui_building_assign_units, *ui_building_item_cursor); - if (ui->follow_unit != -1) - return df::unit::find(ui->follow_unit); + if (plotinfo->follow_unit != -1) + return df::unit::find(plotinfo->follow_unit); - switch (ui->main.mode) { + switch (plotinfo->main.mode) { case ViewUnits: { if (!ui_selected_unit || !world) @@ -1047,8 +1047,8 @@ df::unit *Gui::getAnyUnit(df::viewscreen *top) } case Burrows: { - if (ui->burrows.in_add_units_mode) - return vector_get(ui->burrows.list_units, ui->burrows.unit_cursor_pos); + if (plotinfo->burrows.in_add_units_mode) + return vector_get(plotinfo->burrows.list_units, plotinfo->burrows.unit_cursor_pos); return NULL; } @@ -1196,16 +1196,16 @@ df::item *Gui::getAnyItem(df::viewscreen *top) if (!Gui::dwarfmode_hotkey(top)) return NULL; - switch (ui->main.mode) { + switch (plotinfo->main.mode) { case ViewUnits: { - if (!ui_unit_view_mode || !ui_look_cursor || !ui_sidebar_menus) + if (!ui_unit_view_mode || !ui_look_cursor || !game) return NULL; if (ui_unit_view_mode->value != df::ui_unit_view_mode::Inventory) return NULL; - auto inv_item = vector_get(ui_sidebar_menus->unit.inv_items, *ui_look_cursor); + auto inv_item = vector_get(game->unit.inv_items, *ui_look_cursor); return inv_item ? inv_item->item : NULL; } case LookAround: @@ -1271,7 +1271,7 @@ df::building *Gui::getAnyBuilding(df::viewscreen *top) if (!Gui::dwarfmode_hotkey(top)) return NULL; - switch (ui->main.mode) { + switch (plotinfo->main.mode) { case LookAround: { if (!ui_look_list || !ui_look_cursor) @@ -1293,8 +1293,8 @@ df::building *Gui::getAnyBuilding(df::viewscreen *top) case ZonesPitInfo: case ZonesHospitalInfo: { - if (ui_sidebar_menus) - return ui_sidebar_menus->zone.selected; + if (game) + return game->zone.selected; return NULL; } default: @@ -1327,11 +1327,11 @@ df::plant *Gui::getAnyPlant(df::viewscreen *top) if (Gui::dwarfmode_hotkey(top)) { - if (!cursor || !ui || !world) + if (!cursor || !plotinfo || !world) return nullptr; /* TODO: understand how this changes for v50 - if (ui->main.mode == ui_sidebar_mode::LookAround) + if (plotinfo->main.mode == ui_sidebar_mode::LookAround) { return Maps::getPlantAtTile(cursor->x, cursor->y, cursor->z); } @@ -1949,12 +1949,12 @@ void Gui::resetDwarfmodeView(bool pause) { using df::global::cursor; - if (ui) + if (plotinfo) { - ui->follow_unit = -1; - ui->follow_item = -1; + plotinfo->follow_unit = -1; + plotinfo->follow_item = -1; /* TODO: understand how this changes for v50 - ui->main.mode = ui_sidebar_mode::Default; + plotinfo->main.mode = ui_sidebar_mode::Default; */ } @@ -2011,8 +2011,8 @@ bool Gui::revealInDwarfmodeMap(int32_t x, int32_t y, int32_t z, bool center) *window_x = clip_range(new_win_x, 0, (world->map.x_count - w)); *window_y = clip_range(new_win_y, 0, (world->map.y_count - h)); *window_z = clip_range(new_win_z, 0, (world->map.z_count - 1)); - ui_sidebar_menus->minimap.need_render = true; - ui_sidebar_menus->minimap.need_scan = true; + game->minimap.need_render = true; + game->minimap.need_scan = true; return true; } @@ -2058,10 +2058,10 @@ bool Gui::refreshSidebar() bool Gui::inRenameBuilding() { - if (!ui_sidebar_menus) + if (!game) return false; /* TODO: understand how this changes for v50 - return ui_sidebar_menus->barracks.in_rename; + return game->barracks.in_rename; */ return false; } diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index ceb0224e8..11d170725 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -87,7 +87,7 @@ using namespace std; #include "df/reaction_product_itemst.h" #include "df/tool_uses.h" #include "df/trapcomp_flags.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/vehicle.h" @@ -100,7 +100,7 @@ using namespace std; using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; using df::global::ui_selected_unit; using df::global::proj_next_id; @@ -924,7 +924,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item) { /* TODO: understand how this changes for v50 // Unit view sidebar holds inventory item pointers - if (ui->main.mode == ui_sidebar_mode::ViewUnits && + if (plotinfo->main.mode == ui_sidebar_mode::ViewUnits && (!ui_selected_unit || vector_get(world->units.active, *ui_selected_unit) == unit)) return false; @@ -1534,7 +1534,7 @@ int32_t Items::createItem(df::item_type item_type, int16_t item_subtype, int16_t df::enums::game_type::game_type type = *df::global::gametype; prod->produce(unit, &out_products, &out_items, &in_reag, &in_items, 1, job_skill::NONE, 0, df::historical_entity::find(unit->civ_id), - ((type == df::enums::game_type::DWARF_MAIN) || (type == df::enums::game_type::DWARF_RECLAIM)) ? df::world_site::find(df::global::ui->site_id) : NULL, + ((type == df::enums::game_type::DWARF_MAIN) || (type == df::enums::game_type::DWARF_RECLAIM)) ? df::world_site::find(df::global::plotinfo->site_id) : NULL, NULL); if ( out_items.size() != 1 ) return -1; @@ -1643,9 +1643,9 @@ bool Items::isRouteVehicle(df::item *item) bool Items::isSquadEquipment(df::item *item) { CHECK_NULL_POINTER(item); - if (!ui) + if (!plotinfo) return false; - auto &vec = ui->equipment.items_assigned[item->getType()]; + auto &vec = plotinfo->equipment.items_assigned[item->getType()]; return binsearch_index(vec, &df::item::id, item->id) >= 0; } diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index ff158caa0..cd574684f 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -43,7 +43,7 @@ using namespace std; #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/building.h" #include "df/job.h" diff --git a/library/modules/Kitchen.cpp b/library/modules/Kitchen.cpp index ac560c687..65e47f528 100644 --- a/library/modules/Kitchen.cpp +++ b/library/modules/Kitchen.cpp @@ -20,13 +20,13 @@ using namespace DFHack; #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/item_type.h" #include "df/plant_raw.h" using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; // Special values used by "seedwatch" plugin to store seed limits const df::enums::item_type::item_type SEEDLIMIT_ITEMTYPE = df::enums::item_type::BAR; @@ -41,12 +41,12 @@ void Kitchen::debug_print(color_ostream &out) { out.print("%2zu: IT:%2i IS:%i MT:%3i MI:%2i ET:%i %s\n", i, - ui->kitchen.item_types[i], - ui->kitchen.item_subtypes[i], - ui->kitchen.mat_types[i], - ui->kitchen.mat_indices[i], - ui->kitchen.exc_types[i], - (ui->kitchen.mat_types[i] >= 419 && ui->kitchen.mat_types[i] <= 618) ? world->raws.plants.all[ui->kitchen.mat_indices[i]]->id.c_str() : "n/a" + plotinfo->kitchen.item_types[i], + plotinfo->kitchen.item_subtypes[i], + plotinfo->kitchen.mat_types[i], + plotinfo->kitchen.mat_indices[i], + plotinfo->kitchen.exc_types[i], + (plotinfo->kitchen.mat_types[i] >= 419 && plotinfo->kitchen.mat_types[i] <= 618) ? world->raws.plants.all[plotinfo->kitchen.mat_indices[i]]->id.c_str() : "n/a" ); } out.print("\n"); @@ -83,11 +83,11 @@ void Kitchen::fillWatchMap(std::map& watchMap) watchMap.clear(); for (std::size_t i = 0; i < size(); ++i) { - if (ui->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMTYPE && - ui->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && - ui->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) + if (plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMTYPE && + plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && + plotinfo->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) { - watchMap[ui->kitchen.mat_indices[i]] = ui->kitchen.mat_types[i]; + watchMap[plotinfo->kitchen.mat_indices[i]] = plotinfo->kitchen.mat_types[i]; } } } @@ -96,10 +96,10 @@ int Kitchen::findLimit(int32_t plant_id) { for (size_t i = 0; i < size(); ++i) { - if (ui->kitchen.item_types[i] == SEEDLIMIT_ITEMTYPE && - ui->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && - ui->kitchen.mat_indices[i] == plant_id && - ui->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) + if (plotinfo->kitchen.item_types[i] == SEEDLIMIT_ITEMTYPE && + plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && + plotinfo->kitchen.mat_indices[i] == plant_id && + plotinfo->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) { return int(i); } @@ -113,11 +113,11 @@ bool Kitchen::removeLimit(int32_t plant_id) if (i < 0) return false; - ui->kitchen.item_types.erase(ui->kitchen.item_types.begin() + i); - ui->kitchen.item_subtypes.erase(ui->kitchen.item_subtypes.begin() + i); - ui->kitchen.mat_types.erase(ui->kitchen.mat_types.begin() + i); - ui->kitchen.mat_indices.erase(ui->kitchen.mat_indices.begin() + i); - ui->kitchen.exc_types.erase(ui->kitchen.exc_types.begin() + i); + plotinfo->kitchen.item_types.erase(plotinfo->kitchen.item_types.begin() + i); + plotinfo->kitchen.item_subtypes.erase(plotinfo->kitchen.item_subtypes.begin() + i); + plotinfo->kitchen.mat_types.erase(plotinfo->kitchen.mat_types.begin() + i); + plotinfo->kitchen.mat_indices.erase(plotinfo->kitchen.mat_indices.begin() + i); + plotinfo->kitchen.exc_types.erase(plotinfo->kitchen.exc_types.begin() + i); return true; } @@ -129,15 +129,15 @@ bool Kitchen::setLimit(int32_t plant_id, int16_t limit) int i = findLimit(plant_id); if (i < 0) { - ui->kitchen.item_types.push_back(SEEDLIMIT_ITEMTYPE); - ui->kitchen.item_subtypes.push_back(SEEDLIMIT_ITEMSUBTYPE); - ui->kitchen.mat_types.push_back(limit); - ui->kitchen.mat_indices.push_back(plant_id); - ui->kitchen.exc_types.push_back(SEEDLIMIT_EXCTYPE); + plotinfo->kitchen.item_types.push_back(SEEDLIMIT_ITEMTYPE); + plotinfo->kitchen.item_subtypes.push_back(SEEDLIMIT_ITEMSUBTYPE); + plotinfo->kitchen.mat_types.push_back(limit); + plotinfo->kitchen.mat_indices.push_back(plant_id); + plotinfo->kitchen.exc_types.push_back(SEEDLIMIT_EXCTYPE); } else { - ui->kitchen.mat_types[i] = limit; + plotinfo->kitchen.mat_types[i] = limit; } return true; } @@ -146,11 +146,11 @@ void Kitchen::clearLimits() { for (size_t i = 0; i < size(); ++i) { - if (ui->kitchen.item_types[i] == SEEDLIMIT_ITEMTYPE && - ui->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && - ui->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) + if (plotinfo->kitchen.item_types[i] == SEEDLIMIT_ITEMTYPE && + plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && + plotinfo->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) { - removeLimit(ui->kitchen.mat_indices[i]); + removeLimit(plotinfo->kitchen.mat_indices[i]); --i; } } @@ -158,7 +158,7 @@ void Kitchen::clearLimits() size_t Kitchen::size() { - return ui->kitchen.item_types.size(); + return plotinfo->kitchen.item_types.size(); } int Kitchen::findExclusion(df::kitchen_exc_type type, @@ -167,11 +167,11 @@ int Kitchen::findExclusion(df::kitchen_exc_type type, { for (size_t i = 0; i < size(); i++) { - if (ui->kitchen.item_types[i] == item_type && - ui->kitchen.item_subtypes[i] == item_subtype && - ui->kitchen.mat_types[i] == mat_type && - ui->kitchen.mat_indices[i] == mat_index && - ui->kitchen.exc_types[i] == type) + if (plotinfo->kitchen.item_types[i] == item_type && + plotinfo->kitchen.item_subtypes[i] == item_subtype && + plotinfo->kitchen.mat_types[i] == mat_type && + plotinfo->kitchen.mat_indices[i] == mat_index && + plotinfo->kitchen.exc_types[i] == type) { return int(i); } @@ -186,11 +186,11 @@ bool Kitchen::addExclusion(df::kitchen_exc_type type, if (findExclusion(type, item_type, item_subtype, mat_type, mat_index) >= 0) return false; - ui->kitchen.item_types.push_back(item_type); - ui->kitchen.item_subtypes.push_back(item_subtype); - ui->kitchen.mat_types.push_back(mat_type); - ui->kitchen.mat_indices.push_back(mat_index); - ui->kitchen.exc_types.push_back(type); + plotinfo->kitchen.item_types.push_back(item_type); + plotinfo->kitchen.item_subtypes.push_back(item_subtype); + plotinfo->kitchen.mat_types.push_back(mat_type); + plotinfo->kitchen.mat_indices.push_back(mat_index); + plotinfo->kitchen.exc_types.push_back(type); return true; } @@ -202,10 +202,10 @@ bool Kitchen::removeExclusion(df::kitchen_exc_type type, if (i < 0) return false; - ui->kitchen.item_types.erase(ui->kitchen.item_types.begin() + i); - ui->kitchen.item_subtypes.erase(ui->kitchen.item_subtypes.begin() + i); - ui->kitchen.mat_types.erase(ui->kitchen.mat_types.begin() + i); - ui->kitchen.mat_indices.erase(ui->kitchen.mat_indices.begin() + i); - ui->kitchen.exc_types.erase(ui->kitchen.exc_types.begin() + i); + plotinfo->kitchen.item_types.erase(plotinfo->kitchen.item_types.begin() + i); + plotinfo->kitchen.item_subtypes.erase(plotinfo->kitchen.item_subtypes.begin() + i); + plotinfo->kitchen.mat_types.erase(plotinfo->kitchen.mat_types.begin() + i); + plotinfo->kitchen.mat_indices.erase(plotinfo->kitchen.mat_indices.begin() + i); + plotinfo->kitchen.exc_types.erase(plotinfo->kitchen.exc_types.begin() + i); return true; } diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index a52579059..0854a85ce 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -42,7 +42,7 @@ using namespace std; #include "MiscUtils.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/item.h" #include "df/creature_raw.h" #include "df/caste_raw.h" @@ -68,7 +68,7 @@ using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; bool MaterialInfo::decode(df::item *item) { @@ -516,7 +516,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &ma TEST(fire_safe, material->heat.melting_point > 11000); TEST(magma_safe, material->heat.melting_point > 12000); TEST(deep_material, FLAG(inorganic, inorganic_flags::SPECIAL)); - TEST(non_economic, !inorganic || !(ui && vector_get(ui->economic_stone, index))); + TEST(non_economic, !inorganic || !(plotinfo && vector_get(plotinfo->economic_stone, index))); TEST(plant, plant); TEST(silk, MAT_FLAG(SILK)); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index e7f6cea93..ab7031308 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -83,7 +83,7 @@ namespace DFHack { * Screen painting API. */ -// returns ui grid coordinates, even if the game map is scaled differently +// returns plotinfo grid coordinates, even if the game map is scaled differently df::coord2d Screen::getMousePos() { if (!gps) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 20badb1b5..6d5ec7fde 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -73,7 +73,7 @@ using namespace std; #include "df/nemesis_record.h" #include "df/squad.h" #include "df/tile_occupancy.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit_inventory_item.h" #include "df/unit_misc_trait.h" #include "df/unit_relationship_type.h" @@ -87,7 +87,7 @@ using namespace std; using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; using df::global::gamemode; using df::global::gametype; @@ -178,7 +178,7 @@ bool Units::isFortControlled(df::unit *unit) unit->flags2.bits.resident) return false; - return unit->civ_id != -1 && unit->civ_id == ui->civ_id; + return unit->civ_id != -1 && unit->civ_id == plotinfo->civ_id; } // check if creature belongs to the player's civilization @@ -186,7 +186,7 @@ bool Units::isFortControlled(df::unit *unit) bool Units::isOwnCiv(df::unit* unit) { CHECK_NULL_POINTER(unit); - return unit->civ_id == ui->civ_id; + return unit->civ_id == plotinfo->civ_id; } // check if creature belongs to the player's group @@ -199,7 +199,7 @@ bool Units::isOwnGroup(df::unit* unit) for (size_t i = 0; i < histfig->entity_links.size(); i++) { auto link = histfig->entity_links[i]; - if (link->entity_id == ui->group_id && link->getType() == df::histfig_entity_link_type::MEMBER) + if (link->entity_id == plotinfo->group_id && link->getType() == df::histfig_entity_link_type::MEMBER) return true; } return false; @@ -210,7 +210,7 @@ bool Units::isOwnGroup(df::unit* unit) bool Units::isOwnRace(df::unit* unit) { CHECK_NULL_POINTER(unit); - return unit->race == ui->race_id; + return unit->race == plotinfo->race_id; } @@ -622,8 +622,8 @@ bool Units::isDwarf(df::unit *unit) { CHECK_NULL_POINTER(unit); - return unit->race == ui->race_id || - unit->enemy.normal_race == ui->race_id; + return unit->race == plotinfo->race_id || + unit->enemy.normal_race == plotinfo->race_id; } bool Units::isAnimal(df::unit* unit) { @@ -1301,7 +1301,7 @@ bool Units::setLaborValidity(df::unit_labor labor, bool isValid) return false; if (labor == df::unit_labor::NONE) return false; - df::historical_entity *entity = df::historical_entity::find(ui->civ_id); + df::historical_entity *entity = df::historical_entity::find(plotinfo->civ_id); if (!entity || !entity->entity_raw) return false; entity->entity_raw->jobs.permitted_labor[labor] = isValid; @@ -1704,7 +1704,7 @@ std::string Units::getCasteProfessionName(int race, int casteid, df::profession if (pid < (df::profession)0 || !is_valid_enum_item(pid)) return ""; - int16_t current_race = df::global::ui->race_id; + int16_t current_race = df::global::plotinfo->race_id; if (df::global::gamemode && *df::global::gamemode == df::game_mode::ADVENTURE) current_race = world->units.active[0]->race; bool use_race_prefix = (race >= 0 && race != current_race); diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 000000000..5b0a52473 --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1 @@ +!buildingplan/ diff --git a/plugins/add-spatter.cpp b/plugins/add-spatter.cpp index 459e28f25..451fffabf 100644 --- a/plugins/add-spatter.cpp +++ b/plugins/add-spatter.cpp @@ -24,7 +24,7 @@ #include "df/job.h" #include "df/job_item.h" #include "df/job_item_ref.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/report.h" #include "df/reaction.h" #include "df/reaction_reagent_itemst.h" @@ -45,7 +45,7 @@ DFHACK_PLUGIN("add-spatter"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); typedef df::reaction_product_item_improvementst improvement_product; diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index acb8959b9..415a6d322 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -22,7 +22,7 @@ #include "df/plant_tree_tile.h" #include "df/plant_raw.h" #include "df/tile_dig_designation.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/viewscreen_dwarfmodest.h" #include "df/world.h" @@ -45,7 +45,7 @@ using namespace df::enums; #define PLUGIN_VERSION 0.3 DFHACK_PLUGIN("autochop"); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); static int get_log_count(); @@ -499,7 +499,7 @@ public: burrows_column.clear(); - for (df::burrow *burrow : ui->burrows.list) + for (df::burrow *burrow : plotinfo->burrows.list) { string name = burrow->name; if (name.empty()) @@ -820,7 +820,7 @@ struct autochop_hook : public df::viewscreen_dwarfmodest bool isInDesignationMenu() { using namespace df::enums::ui_sidebar_mode; - return (ui->main.mode == DesignateChopTrees); + return (plotinfo->main.mode == DesignateChopTrees); } void sendKey(const df::interface_key &key) diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index 2f8762425..6a4078b56 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -5,7 +5,7 @@ #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/building_type.h" #include "df/building_farmplotst.h" #include "df/buildings_other_id.h" @@ -30,7 +30,7 @@ using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; static command_result autofarm(color_ostream& out, std::vector& parameters); @@ -348,7 +348,7 @@ static std::unique_ptr autofarmInstance; DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) { - if (world && ui) { + if (world && plotinfo) { commands.push_back( PluginCommand("autofarm", "Automatically manage farm crop selection.", diff --git a/plugins/autogems.cpp b/plugins/autogems.cpp index d296caf7f..8cf419aba 100644 --- a/plugins/autogems.cpp +++ b/plugins/autogems.cpp @@ -33,7 +33,7 @@ using namespace DFHack; DFHACK_PLUGIN("autogems"); DFHACK_PLUGIN_IS_ENABLED(enabled); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); typedef int32_t item_id; @@ -242,7 +242,7 @@ struct autogem_hook : public df::viewscreen_dwarfmodest { bool in_menu() { // Determines whether we're looking at the Workshop Orders screen. - return ui->main.mode == ui_sidebar_mode::OrdersWorkshop; + return plotinfo->main.mode == ui_sidebar_mode::OrdersWorkshop; } bool handleInput(std::set *input) { diff --git a/plugins/autolabor/autohauler.cpp b/plugins/autolabor/autohauler.cpp index e7c2530e1..3bb2534f8 100644 --- a/plugins/autolabor/autohauler.cpp +++ b/plugins/autolabor/autohauler.cpp @@ -12,7 +12,7 @@ // DF data structure definition headers #include "DataDefs.h" -#include +#include #include #include #include @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -47,7 +47,7 @@ using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("autohauler"); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 76108ddd0..0af4be5e2 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -12,7 +12,7 @@ // DF data structure definition headers #include "DataDefs.h" -#include +#include #include #include #include @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -47,7 +47,7 @@ using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("autolabor"); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) @@ -813,7 +813,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) // identify dwarfs who are needed for meetings and mark them for exclusion - for (auto& act : ui->activities) + for (auto& act : plotinfo->activities) { if (!act) continue; bool p1 = act->unit_actor == dwarfs[dwarf]; diff --git a/plugins/autolabor/joblabormapper.cpp b/plugins/autolabor/joblabormapper.cpp index 2917b8d3a..690e80881 100644 --- a/plugins/autolabor/joblabormapper.cpp +++ b/plugins/autolabor/joblabormapper.cpp @@ -56,7 +56,7 @@ using std::string; using std::endl; using namespace DFHack; using namespace df::enums; -using df::global::ui; +using df::global::plotinfo; using df::global::world; #include "labormanager.h" diff --git a/plugins/autolabor/labormanager.cpp b/plugins/autolabor/labormanager.cpp index 44817e405..4b43b09c1 100644 --- a/plugins/autolabor/labormanager.cpp +++ b/plugins/autolabor/labormanager.cpp @@ -25,7 +25,7 @@ #include "DataDefs.h" #include -#include +#include #include #include #include @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -64,7 +64,7 @@ #include #include #include -#include +#include #include #include #include @@ -82,7 +82,7 @@ using std::string; using std::endl; using namespace DFHack; using namespace df::enums; -using df::global::ui; +using df::global::plotinfo; using df::global::world; #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) @@ -948,9 +948,9 @@ private: // identify dwarfs who are needed for meetings and mark them for exclusion - for (size_t i = 0; i < ui->activities.size(); ++i) + for (size_t i = 0; i < plotinfo->activities.size(); ++i) { - df::activity_info *act = ui->activities[i]; + df::activity_info *act = plotinfo->activities[i]; if (!act) continue; bool p1 = act->unit_actor == dwarf->dwarf; @@ -996,11 +996,11 @@ private: for (size_t j = 0; j < dwarf->dwarf->inventory.size(); j++) { - df::unit_inventory_item* ui = dwarf->dwarf->inventory[j]; - if (ui->mode == df::unit_inventory_item::Weapon && ui->item->isWeapon()) + df::unit_inventory_item* plotinfo = dwarf->dwarf->inventory[j]; + if (plotinfo->mode == df::unit_inventory_item::Weapon && plotinfo->item->isWeapon()) { dwarf->armed = true; - df::itemdef_weaponst* weapondef = ((df::item_weaponst*)(ui->item))->subtype; + df::itemdef_weaponst* weapondef = ((df::item_weaponst*)(plotinfo->item))->subtype; df::job_skill weaponsk = (df::job_skill) weapondef->skill_melee; df::job_skill rangesk = (df::job_skill) weapondef->skill_ranged; if (weaponsk == df::job_skill::AXE) @@ -1408,8 +1408,8 @@ public: (isOptionEnabled(CF_ALLOW_HUNTING) && has_butchers) ? 1 : 0; /* add animal trainers */ - for (auto a = df::global::ui->equipment.training_assignments.begin(); - a != df::global::ui->equipment.training_assignments.end(); + for (auto a = df::global::plotinfo->equipment.training_assignments.begin(); + a != df::global::plotinfo->equipment.training_assignments.end(); a++) { labor_needed[df::unit_labor::ANIMALTRAIN]++; diff --git a/plugins/automaterial.cpp b/plugins/automaterial.cpp index 24f2d8e2a..9783d28dc 100644 --- a/plugins/automaterial.cpp +++ b/plugins/automaterial.cpp @@ -21,8 +21,8 @@ #include "df/build_req_choice_specst.h" #include "df/construction_type.h" #include "df/item.h" -#include "df/ui.h" -#include "df/ui_build_selector.h" +#include "df/plotinfost.h" +#include "df/buildreq.h" #include "df/viewscreen_dwarfmodest.h" #include "df/items_other_id.h" #include "df/job.h" @@ -50,7 +50,7 @@ using namespace df::enums; DFHACK_PLUGIN("automaterial"); REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); namespace DFHack { @@ -115,14 +115,14 @@ static inline bool in_material_choice_stage() { return Gui::build_selector_hotkey(Core::getTopViewscreen()) && ui_build_selector->building_type == df::building_type::Construction && - ui->main.mode == ui_sidebar_mode::Build && + plotinfo->main.mode == ui_sidebar_mode::Build && ui_build_selector->stage == 2; } static inline bool in_placement_stage() { return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - ui->main.mode == ui_sidebar_mode::Build && + plotinfo->main.mode == ui_sidebar_mode::Build && ui_build_selector && ui_build_selector->building_type == df::building_type::Construction && ui_build_selector->stage == 1; @@ -131,7 +131,7 @@ static inline bool in_placement_stage() static inline bool in_type_choice_stage() { return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - ui->main.mode == ui_sidebar_mode::Build && + plotinfo->main.mode == ui_sidebar_mode::Build && ui_build_selector && ui_build_selector->building_type < 0; } @@ -713,7 +713,7 @@ struct jobutils_hook : public df::viewscreen_dwarfmodest if (!box_select_enabled) return; - if (ui->main.mode != df::ui_sidebar_mode::Build || + if (plotinfo->main.mode != df::ui_sidebar_mode::Build || ui_build_selector->building_type != df::building_type::Construction) return; diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 4bbb727d7..9ca20e9ea 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -9,7 +9,7 @@ #include "df/building_stockpilest.h" #include "modules/Buildings.h" #include "modules/Items.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "modules/Maps.h" #include "modules/World.h" #include "df/item_quality.h" @@ -21,7 +21,7 @@ DFHACK_PLUGIN("automelt"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(cursor); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); static const string PERSISTENCE_KEY = "automelt/stockpiles"; diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp index 623fd5c1e..333e34e95 100644 --- a/plugins/autotrade.cpp +++ b/plugins/autotrade.cpp @@ -15,7 +15,7 @@ #include "df/job.h" #include "df/job_item_ref.h" #include "modules/Job.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/mandate.h" #include "modules/Maps.h" @@ -25,7 +25,7 @@ DFHACK_PLUGIN("autotrade"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(cursor); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); static const string PERSISTENCE_KEY = "autotrade/stockpiles"; diff --git a/plugins/buildingplan/buildingplan-planner.cpp b/plugins/buildingplan/buildingplan-planner.cpp index 4ac11c4e1..07f23150a 100644 --- a/plugins/buildingplan/buildingplan-planner.cpp +++ b/plugins/buildingplan/buildingplan-planner.cpp @@ -6,7 +6,7 @@ #include "df/building_type.h" #include "df/general_ref_building_holderst.h" #include "df/job_item.h" -#include "df/ui_build_selector.h" +#include "df/buildreq.h" #include "modules/Buildings.h" #include "modules/Gui.h" diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index e0bc6dd69..cd4e84a6e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -1,7 +1,7 @@ #include "df/construction_type.h" #include "df/entity_position.h" #include "df/interface_key.h" -#include "df/ui_build_selector.h" +#include "df/buildreq.h" #include "df/viewscreen_dwarfmodest.h" #include "modules/Gui.h" @@ -18,7 +18,7 @@ DFHACK_PLUGIN("buildingplan"); #define PLUGIN_VERSION "2.0" -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); REQUIRE_GLOBAL(world); // used in buildingplan library @@ -455,8 +455,8 @@ struct buildingplan_query_hook : public df::viewscreen_dwarfmodest bool isInPlannedBuildingQueryMode() { - return (ui->main.mode == df::ui_sidebar_mode::QueryBuilding || - ui->main.mode == df::ui_sidebar_mode::BuildingItems) && + return (plotinfo->main.mode == df::ui_sidebar_mode::QueryBuilding || + plotinfo->main.mode == df::ui_sidebar_mode::BuildingItems) && planner.getPlannedBuilding(world->selected_building); } @@ -595,7 +595,7 @@ struct buildingplan_place_hook : public df::viewscreen_dwarfmodest bool isInPlannedBuildingPlacementMode() { - return ui->main.mode == ui_sidebar_mode::Build && + return plotinfo->main.mode == ui_sidebar_mode::Build && df::global::ui_build_selector && df::global::ui_build_selector->stage < 2 && planner.isPlannableBuilding(toBuildingTypeKey(ui_build_selector)); @@ -861,7 +861,7 @@ struct buildingplan_room_hook : public df::viewscreen_dwarfmodest std::vector getNoblePositionOfSelectedBuildingOwner() { std::vector np; - if (ui->main.mode != df::ui_sidebar_mode::QueryBuilding || + if (plotinfo->main.mode != df::ui_sidebar_mode::QueryBuilding || !world->selected_building || !world->selected_building->owner) { @@ -1084,7 +1084,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan static bool is_paused() { return World::ReadPauseState() || - ui->main.mode > df::ui_sidebar_mode::Squads || + plotinfo->main.mode > df::ui_sidebar_mode::Squads || !strict_virtual_cast(Gui::getCurViewscreen(true)); } diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index 029b3c715..c39253488 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -17,7 +17,7 @@ #include "TileTypes.h" #include "DataDefs.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" #include "df/unit.h" #include "df/burrow.h" @@ -38,7 +38,7 @@ using namespace df::enums; using namespace dfproto; DFHACK_PLUGIN("burrows"); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(gamemode); @@ -102,11 +102,11 @@ DEFINE_LUA_EVENT_1(onBurrowRename, handle_burrow_rename, df::burrow*); static void detect_burrow_renames(color_ostream &out) { - if (ui->main.mode == ui_sidebar_mode::Burrows && - ui->burrows.in_edit_name_mode && - ui->burrows.sel_id >= 0) + if (plotinfo->main.mode == ui_sidebar_mode::Burrows && + plotinfo->burrows.in_edit_name_mode && + plotinfo->burrows.sel_id >= 0) { - name_burrow_id = ui->burrows.sel_id; + name_burrow_id = plotinfo->burrows.sel_id; } else if (name_burrow_id >= 0) { @@ -222,7 +222,7 @@ static std::map name_lookup; static void parse_names() { - auto &list = ui->burrows.list; + auto &list = plotinfo->burrows.list; grow_burrows.clear(); name_lookup.clear(); diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 5cef6376f..499d3f08f 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -36,7 +36,7 @@ using std::vector; DFHACK_PLUGIN("confirm"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); typedef std::set ikey_set; command_result df_confirm (color_ostream &out, vector & parameters); diff --git a/plugins/createitem.cpp b/plugins/createitem.cpp index 57dbf7244..499814adc 100644 --- a/plugins/createitem.cpp +++ b/plugins/createitem.cpp @@ -16,7 +16,7 @@ #include "DataDefs.h" #include "df/game_type.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/historical_entity.h" #include "df/world_site.h" @@ -38,7 +38,7 @@ using namespace df::enums; DFHACK_PLUGIN("createitem"); REQUIRE_GLOBAL(cursor); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(gametype); REQUIRE_GLOBAL(cur_year_tick); @@ -78,7 +78,7 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it prod->produce(unit, &out_products, &out_items, &in_reag, &in_items, 1, job_skill::NONE, 0, df::historical_entity::find(unit->civ_id), - (World::isFortressMode()) ? df::world_site::find(ui->site_id) : NULL, NULL); + (World::isFortressMode()) ? df::world_site::find(plotinfo->site_id) : NULL, NULL); if (!out_items.size()) return false; // if we asked to make shoes and we got twice as many as we asked, then we're okay diff --git a/plugins/debug.cpp b/plugins/debug.cpp index f4a22b8d1..a48fd2a05 100644 --- a/plugins/debug.cpp +++ b/plugins/debug.cpp @@ -43,7 +43,7 @@ namespace DFHack { DBG_DECLARE(debug,filter); DBG_DECLARE(debug,init); DBG_DECLARE(debug,command); -DBG_DECLARE(debug,ui); +DBG_DECLARE(debug,plotinfo); DBG_DECLARE(debug,example,DebugCategory::LINFO); } diff --git a/plugins/devel/kittens.cpp b/plugins/devel/kittens.cpp index 89ece51b6..ec1114576 100644 --- a/plugins/devel/kittens.cpp +++ b/plugins/devel/kittens.cpp @@ -33,7 +33,7 @@ using namespace DFHack; DFHACK_PLUGIN("kittens"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); namespace DFHack { @@ -119,9 +119,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } if(trackmenu_flg) { - if (last_menu != ui->main.mode) + if (last_menu != plotinfo->main.mode) { - last_menu = ui->main.mode; + last_menu = plotinfo->main.mode; out.print("Menu: %d\n",last_menu); } } @@ -157,7 +157,7 @@ command_result trackmenu (color_ostream &out, vector & parameters) else { is_enabled = true; - last_menu = ui->main.mode; + last_menu = plotinfo->main.mode; out.print("Menu: %d\n",last_menu); trackmenu_flg = true; return CR_OK; diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp index 619fda9fd..796bc2fc9 100644 --- a/plugins/devel/stockcheck.cpp +++ b/plugins/devel/stockcheck.cpp @@ -5,7 +5,7 @@ #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/building_stockpilest.h" #include "df/global_objects.h" #include "df/item.h" @@ -24,7 +24,7 @@ using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; using df::global::selection_rect; using df::building_stockpilest; @@ -35,7 +35,7 @@ DFHACK_PLUGIN("stockcheck"); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - if (world && ui) { + if (world && plotinfo) { commands.push_back( PluginCommand("stockcheck", "Check for unprotected rottable items.", stockcheck, false, diff --git a/plugins/devel/stripcaged.cpp b/plugins/devel/stripcaged.cpp index 30d0b0d2b..e96e63fb1 100644 --- a/plugins/devel/stripcaged.cpp +++ b/plugins/devel/stripcaged.cpp @@ -20,7 +20,7 @@ using namespace std; #include "modules/World.h" #include "MiscUtils.h" -#include +#include #include "df/world.h" #include "df/world_raws.h" #include "df/building_def.h" @@ -34,7 +34,7 @@ using namespace DFHack; using namespace df::enums; using df::global::world; using df::global::cursor; -using df::global::ui; +using df::global::plotinfo; using namespace DFHack::Gui; diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 1bca6784c..9945ee2b0 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -20,14 +20,14 @@ #include #include #include -#include +#include #include #include #include #include DFHACK_PLUGIN("dig-now"); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); using namespace DFHack; @@ -678,7 +678,7 @@ static void create_boulders(color_ostream &out, df::unit *unit = world->units.active[0]; df::historical_entity *civ = df::historical_entity::find(unit->civ_id); df::world_site *site = World::isFortressMode() ? - df::world_site::find(ui->site_id) : NULL; + df::world_site::find(plotinfo->site_id) : NULL; std::vector in_reag; std::vector in_items; diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 945bf3613..371dd588f 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -16,7 +16,7 @@ #include "modules/Maps.h" #include "modules/Materials.h" -#include "df/ui_sidebar_menus.h" +#include "df/gamest.h" using std::vector; using std::string; @@ -33,7 +33,7 @@ command_result digcircle (color_ostream &out, vector & parameters); command_result digtype (color_ostream &out, vector & parameters); DFHACK_PLUGIN("dig"); -REQUIRE_GLOBAL(ui_sidebar_menus); +REQUIRE_GLOBAL(game); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(window_z); @@ -213,7 +213,7 @@ bool lineY (MapExtras::MapCache & MCache, int32_t parse_priority(color_ostream &out, vector ¶meters) { - int32_t default_priority = ui_sidebar_menus->designation.priority; + int32_t default_priority = game->designation.priority; for (auto it = parameters.begin(); it != parameters.end(); ++it) { diff --git a/plugins/diggingInvaders/assignJob.cpp b/plugins/diggingInvaders/assignJob.cpp index 2eaad9eb5..eb74433b8 100644 --- a/plugins/diggingInvaders/assignJob.cpp +++ b/plugins/diggingInvaders/assignJob.cpp @@ -25,7 +25,7 @@ #include "df/job_type.h" #include "df/reaction_product_itemst.h" #include "df/reaction_reagent.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/world.h" @@ -252,7 +252,7 @@ int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map in_items; prod->produce(firstInvader, &out_products, &out_items, &in_reag, &in_items, 1, df::job_skill::NONE, 0, df::historical_entity::find(firstInvader->civ_id), - df::world_site::find(df::global::ui->site_id), NULL); + df::world_site::find(df::global::plotinfo->site_id), NULL); if ( out_items.size() != 1 ) { out.print("%s, %d: wrong size: %zu.\n", __FILE__, __LINE__, out_items.size()); diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 457a00e50..cf0047cd8 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -47,7 +47,7 @@ #include "df/tiletype_material.h" #include "df/tiletype_shape.h" #include "df/tiletype_shape_basic.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/world.h" diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 4355104a7..2b546bfad 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -4,7 +4,7 @@ #include "DataDefs.h" #include "df/job.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/viewscreen_dwarfmodest.h" #include "df/world.h" @@ -53,7 +53,7 @@ using std::deque; DFHACK_PLUGIN("dwarfmonitor"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); typedef int16_t activity_type; diff --git a/plugins/dwarfvet.cpp b/plugins/dwarfvet.cpp index 356fda238..75f015975 100644 --- a/plugins/dwarfvet.cpp +++ b/plugins/dwarfvet.cpp @@ -39,7 +39,7 @@ #include "df/job.h" #include "df/general_ref_unit_workerst.h" #include "df/profession.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_health_info.h" #include "df/unit_health_flags.h" @@ -58,7 +58,7 @@ using namespace std; DFHACK_PLUGIN("dwarfvet"); DFHACK_PLUGIN_IS_ENABLED(dwarfvet_enabled); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); static unordered_set tracked_units; @@ -407,7 +407,7 @@ void tickHandler(color_ostream& out, void* data) { if ( !dwarfvet_enabled ) return; CoreSuspender suspend; - int32_t own_race_id = df::global::ui->race_id; + int32_t own_race_id = df::global::plotinfo->race_id; /** * Generate a list of animal hospitals on the map diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 1fb845d15..6501f7f21 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -123,7 +123,7 @@ namespace embark_assist { struct states { embark_assist::defs::find_callbacks find_callback; - uis ui; + uis plotinfo; display_maps finder_list; // Don't need the element key, but it's easier to use the same type. uint16_t finder_list_focus; bool finder_list_active; @@ -171,13 +171,13 @@ namespace embark_assist { size_t civ = 0; while (true) { - for (size_t k = 0; k < state->ui[static_cast(i)]->list.size(); k++) { - if (state->ui[static_cast(i) + civ]->current_value == state->ui[static_cast(i) + civ]->list[k].key) { - fprintf(outfile, "[%s:%s]\n", state->finder_list[static_cast(i) + civ].text.c_str(), state->ui[static_cast(i) + civ]->list[k].text.c_str()); + for (size_t k = 0; k < state->plotinfo[static_cast(i)]->list.size(); k++) { + if (state->plotinfo[static_cast(i) + civ]->current_value == state->plotinfo[static_cast(i) + civ]->list[k].key) { + fprintf(outfile, "[%s:%s]\n", state->finder_list[static_cast(i) + civ].text.c_str(), state->plotinfo[static_cast(i) + civ]->list[k].text.c_str()); break; } } -// fprintf(outfile, "[%s:%i]\n", state->finder_list[static_cast(i)].text.c_str(), state->ui[static_cast(i)]->current_value); +// fprintf(outfile, "[%s:%i]\n", state->finder_list[static_cast(i)].text.c_str(), state->plotinfo[static_cast(i)]->current_value); if (i == last_fields) { civ++; @@ -229,10 +229,10 @@ namespace embark_assist { found = false; - for (size_t l = 0; l < state->ui[static_cast(i) + civ]->list.size(); l++) { + for (size_t l = 0; l < state->plotinfo[static_cast(i) + civ]->list.size(); l++) { for (int m = k + 1; m < count; m++) { - if (state->ui[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] != line[m]) { - if (state->ui[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] == '\0' && + if (state->plotinfo[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] != line[m]) { + if (state->plotinfo[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] == '\0' && line[m] == ']') { found = true; } @@ -291,13 +291,13 @@ namespace embark_assist { found = false; - for (size_t l = 0; l < state->ui[static_cast(i) + civ]->list.size(); l++) { + for (size_t l = 0; l < state->plotinfo[static_cast(i) + civ]->list.size(); l++) { for (int m = k + 1; m < count; m++) { - if (state->ui[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] != line[m]) { - if (state->ui[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] == '\0' && + if (state->plotinfo[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] != line[m]) { + if (state->plotinfo[static_cast(i) + civ]->list[l].text.c_str()[m - (k + 1)] == '\0' && line[m] == ']') { - state->ui[static_cast(i) + civ]->current_value = state->ui[static_cast(i) + civ]->list[l].key; - state->ui[static_cast(i) + civ]->current_display_value = l; + state->plotinfo[static_cast(i) + civ]->current_value = state->plotinfo[static_cast(i) + civ]->list[l].key; + state->plotinfo[static_cast(i) + civ]->current_display_value = l; found = true; } @@ -1095,7 +1095,7 @@ namespace embark_assist { if (l < state->civs.size() - 1) { element->current_value = element->list[0].key; - state->ui.push_back(element); + state->plotinfo.push_back(element); element = new ui_lists; element->current_display_value = 0; element->current_index = 0; @@ -1108,7 +1108,7 @@ namespace embark_assist { } element->current_value = element->list[0].key; - state->ui.push_back(element); + state->plotinfo.push_back(element); switch (i) { case fields::x_dim: @@ -1333,21 +1333,21 @@ namespace embark_assist { // off to compensate for the list starting with 1 at index 0. // auto screen = Gui::getViewscreenByType(0); - state->ui[static_cast(fields::x_dim)]->current_display_value = + state->plotinfo[static_cast(fields::x_dim)]->current_display_value = screen->location.embark_pos_max.x - screen->location.embark_pos_min.x; - state->ui[static_cast(fields::x_dim)]->current_index = - state->ui[static_cast(fields::x_dim)]->current_display_value; - state->ui[static_cast(fields::x_dim)]->current_value = - state->ui[static_cast(fields::x_dim)]->current_display_value + 1; + state->plotinfo[static_cast(fields::x_dim)]->current_index = + state->plotinfo[static_cast(fields::x_dim)]->current_display_value; + state->plotinfo[static_cast(fields::x_dim)]->current_value = + state->plotinfo[static_cast(fields::x_dim)]->current_display_value + 1; - state->ui[static_cast(fields::y_dim)]->current_display_value = + state->plotinfo[static_cast(fields::y_dim)]->current_display_value = screen->location.embark_pos_max.y - screen->location.embark_pos_min.y; - state->ui[static_cast(fields::y_dim)]->current_index = - state->ui[static_cast(fields::y_dim)]->current_display_value; - state->ui[static_cast(fields::y_dim)]->current_value = - state->ui[static_cast(fields::y_dim)]->current_display_value + 1; + state->plotinfo[static_cast(fields::y_dim)]->current_index = + state->plotinfo[static_cast(fields::y_dim)]->current_display_value; + state->plotinfo[static_cast(fields::y_dim)]->current_value = + state->plotinfo[static_cast(fields::y_dim)]->current_display_value + 1; } //========================================================================================================== @@ -1360,234 +1360,234 @@ namespace embark_assist { while (true) { switch (i) { case fields::x_dim: - finder.x_dim = state->ui[static_cast(i)]->current_value; + finder.x_dim = state->plotinfo[static_cast(i)]->current_value; break; case fields::y_dim: - finder.y_dim = state->ui[static_cast(i)]->current_value; + finder.y_dim = state->plotinfo[static_cast(i)]->current_value; break; case fields::savagery_calm: finder.savagery[0] = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::savagery_medium: finder.savagery[1] = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::savagery_savage: finder.savagery[2] = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::good: finder.evilness[0] = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::neutral: finder.evilness[1] = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::evil: finder.evilness[2] = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::aquifer: finder.aquifer = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::min_river: finder.min_river = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::max_river: finder.max_river = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::min_waterfall: - finder.min_waterfall = state->ui[static_cast(i)]->current_value; + finder.min_waterfall = state->plotinfo[static_cast(i)]->current_value; break; case fields::flat: finder.flat = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::soil_min_everywhere: finder.soil_min_everywhere = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::freezing: finder.freezing = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::blood_rain: finder.blood_rain = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::syndrome_rain: finder.syndrome_rain = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::reanimation: finder.reanimation = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::clay: finder.clay = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::sand: finder.sand = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::flux: finder.flux = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::coal: finder.coal = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::soil_min: finder.soil_min = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::soil_max: finder.soil_max = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::spire_count_min: - finder.spire_count_min = state->ui[static_cast(i)]->current_value; + finder.spire_count_min = state->plotinfo[static_cast(i)]->current_value; break; case fields::spire_count_max: - finder.spire_count_max = state->ui[static_cast(i)]->current_value; + finder.spire_count_max = state->plotinfo[static_cast(i)]->current_value; break; case fields::magma_min: finder.magma_min = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::magma_max: finder.magma_max = - static_cast(state->ui[static_cast(i)]->current_value); + static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::biome_count_min: - finder.biome_count_min = state->ui[static_cast(i)]->current_value; + finder.biome_count_min = state->plotinfo[static_cast(i)]->current_value; break; case fields::biome_count_max: - finder.biome_count_max = state->ui[static_cast(i)]->current_value; + finder.biome_count_max = state->plotinfo[static_cast(i)]->current_value; break; case fields::region_type_1: - finder.region_type_1 = state->ui[static_cast(i)]->current_value; + finder.region_type_1 = state->plotinfo[static_cast(i)]->current_value; break; case fields::region_type_2: - finder.region_type_2 = state->ui[static_cast(i)]->current_value; + finder.region_type_2 = state->plotinfo[static_cast(i)]->current_value; break; case fields::region_type_3: - finder.region_type_3 = state->ui[static_cast(i)]->current_value; + finder.region_type_3 = state->plotinfo[static_cast(i)]->current_value; break; case fields::biome_1: - finder.biome_1 = state->ui[static_cast(i)]->current_value; + finder.biome_1 = state->plotinfo[static_cast(i)]->current_value; break; case fields::biome_2: - finder.biome_2 = state->ui[static_cast(i)]->current_value; + finder.biome_2 = state->plotinfo[static_cast(i)]->current_value; break; case fields::biome_3: - finder.biome_3 = state->ui[static_cast(i)]->current_value; + finder.biome_3 = state->plotinfo[static_cast(i)]->current_value; break; case fields::min_trees: - finder.min_trees = static_cast(state->ui[static_cast(i)]->current_value); + finder.min_trees = static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::max_trees: - finder.max_trees = static_cast(state->ui[static_cast(i)]->current_value); + finder.max_trees = static_cast(state->plotinfo[static_cast(i)]->current_value); break; case fields::metal_1: - finder.metal_1 = state->ui[static_cast(i)]->current_value; + finder.metal_1 = state->plotinfo[static_cast(i)]->current_value; break; case fields::metal_2: - finder.metal_2 = state->ui[static_cast(i)]->current_value; + finder.metal_2 = state->plotinfo[static_cast(i)]->current_value; break; case fields::metal_3: - finder.metal_3 = state->ui[static_cast(i)]->current_value; + finder.metal_3 = state->plotinfo[static_cast(i)]->current_value; break; case fields::economic_1: - finder.economic_1 = state->ui[static_cast(i)]->current_value; + finder.economic_1 = state->plotinfo[static_cast(i)]->current_value; break; case fields::economic_2: - finder.economic_2 = state->ui[static_cast(i)]->current_value; + finder.economic_2 = state->plotinfo[static_cast(i)]->current_value; break; case fields::economic_3: - finder.economic_3 = state->ui[static_cast(i)]->current_value; + finder.economic_3 = state->plotinfo[static_cast(i)]->current_value; break; case fields::mineral_1: - finder.mineral_1 = state->ui[static_cast(i)]->current_value; + finder.mineral_1 = state->plotinfo[static_cast(i)]->current_value; break; case fields::mineral_2: - finder.mineral_2 = state->ui[static_cast(i)]->current_value; + finder.mineral_2 = state->plotinfo[static_cast(i)]->current_value; break; case fields::mineral_3: - finder.mineral_3 = state->ui[static_cast(i)]->current_value; + finder.mineral_3 = state->plotinfo[static_cast(i)]->current_value; break; case fields::min_necro_neighbors: - finder.min_necro_neighbors = state->ui[static_cast(i)]->current_value; + finder.min_necro_neighbors = state->plotinfo[static_cast(i)]->current_value; break; case fields::max_necro_neighbors: - finder.max_necro_neighbors = state->ui[static_cast(i)]->current_value; + finder.max_necro_neighbors = state->plotinfo[static_cast(i)]->current_value; break; case fields::min_civ_neighbors: - finder.min_civ_neighbors = state->ui[static_cast(i)]->current_value; + finder.min_civ_neighbors = state->plotinfo[static_cast(i)]->current_value; break; case fields::max_civ_neighbors: - finder.max_civ_neighbors = state->ui[static_cast(i)]->current_value; + finder.max_civ_neighbors = state->plotinfo[static_cast(i)]->current_value; break; case fields::neighbors: for (size_t k = 0; k < state->civs.size(); k++) { - finder.neighbors.push_back({ state->civs[k].id, static_cast(state->ui[static_cast(i) + k]->current_value) }); + finder.neighbors.push_back({ state->civs[k].id, static_cast(state->plotinfo[static_cast(i) + k]->current_value) }); } break; } @@ -1641,10 +1641,10 @@ namespace embark_assist { } } else { - if (state->ui[state->finder_list_focus]->current_index > 0) { - state->ui[state->finder_list_focus]->current_index--; + if (state->plotinfo[state->finder_list_focus]->current_index > 0) { + state->plotinfo[state->finder_list_focus]->current_index--; } else { - state->ui[state->finder_list_focus]->current_index = static_cast(state->ui[state->finder_list_focus]->list.size()) - 1; + state->plotinfo[state->finder_list_focus]->current_index = static_cast(state->plotinfo[state->finder_list_focus]->list.size()) - 1; } } @@ -1657,17 +1657,17 @@ namespace embark_assist { } } else { - if (state->ui[state->finder_list_focus]->current_index < state->ui[state->finder_list_focus]->list.size() - 1) { - state->ui[state->finder_list_focus]->current_index++; + if (state->plotinfo[state->finder_list_focus]->current_index < state->plotinfo[state->finder_list_focus]->list.size() - 1) { + state->plotinfo[state->finder_list_focus]->current_index++; } else { - state->ui[state->finder_list_focus]->current_index = 0; + state->plotinfo[state->finder_list_focus]->current_index = 0; } } } else if (input->count(df::interface_key::SELECT)) { if (!state->finder_list_active) { - state->ui[state->finder_list_focus]->current_display_value = state->ui[state->finder_list_focus]->current_index; - state->ui[state->finder_list_focus]->current_value = state->ui[state->finder_list_focus]->list[state->ui[state->finder_list_focus]->current_index].key; + state->plotinfo[state->finder_list_focus]->current_display_value = state->plotinfo[state->finder_list_focus]->current_index; + state->plotinfo[state->finder_list_focus]->current_value = state->plotinfo[state->finder_list_focus]->list[state->plotinfo[state->finder_list_focus]->current_index].key; state->finder_list_active = true; } @@ -1743,7 +1743,7 @@ namespace embark_assist { embark_assist::screen::paintString(active_pen, 21, top_row + i - offset, - state->ui[i]->list[state->ui[i]->current_display_value].text); + state->plotinfo[i]->list[state->plotinfo[i]->current_display_value].text); } else { embark_assist::screen::paintString(normal_pen, 1, top_row + i - offset, state->finder_list[i].text); @@ -1751,38 +1751,38 @@ namespace embark_assist { embark_assist::screen::paintString(white_pen, 21, top_row + i - offset, - state->ui[i]->list[state->ui[i]->current_display_value].text); + state->plotinfo[i]->list[state->plotinfo[i]->current_display_value].text); } } // Implement scrolling lists if they don't fit on the screen. offset = 0; - if (int32_t(state->ui[state->finder_list_focus]->list.size()) > screen_size.y - top_row - 1) { + if (int32_t(state->plotinfo[state->finder_list_focus]->list.size()) > screen_size.y - top_row - 1) { offset = (screen_size.y - top_row - 1) / 2; - if (state->ui[state->finder_list_focus]->current_index < offset) { + if (state->plotinfo[state->finder_list_focus]->current_index < offset) { offset = 0; } else { - offset = state->ui[state->finder_list_focus]->current_index - offset; + offset = state->plotinfo[state->finder_list_focus]->current_index - offset; } - if (int32_t(state->ui[state->finder_list_focus]->list.size() - offset) < screen_size.y - top_row - 1) { - offset = static_cast(state->ui[state->finder_list_focus]->list.size()) - (screen_size.y - top_row - 1); + if (int32_t(state->plotinfo[state->finder_list_focus]->list.size() - offset) < screen_size.y - top_row - 1) { + offset = static_cast(state->plotinfo[state->finder_list_focus]->list.size()) - (screen_size.y - top_row - 1); } } - for (uint16_t i = offset; i < state->ui[state->finder_list_focus]->list.size(); i++) { - if (i == state->ui[state->finder_list_focus]->current_index) { + for (uint16_t i = offset; i < state->plotinfo[state->finder_list_focus]->list.size(); i++) { + if (i == state->plotinfo[state->finder_list_focus]->current_index) { if (!state->finder_list_active) { // Negated expression to get the display lines in the same order as above. - embark_assist::screen::paintString(active_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text); + embark_assist::screen::paintString(active_pen, list_column, top_row + i - offset, state->plotinfo[state->finder_list_focus]->list[i].text); } else { - embark_assist::screen::paintString(passive_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text); + embark_assist::screen::paintString(passive_pen, list_column, top_row + i - offset, state->plotinfo[state->finder_list_focus]->list[i].text); } } else { - embark_assist::screen::paintString(normal_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text); + embark_assist::screen::paintString(normal_pen, list_column, top_row + i - offset, state->plotinfo[state->finder_list_focus]->list[i].text); } } @@ -1823,8 +1823,8 @@ void embark_assist::finder_ui::activate() { void embark_assist::finder_ui::shutdown() { if (embark_assist::finder_ui::state) { - for (uint16_t i = 0; i < embark_assist::finder_ui::state->ui.size(); i++) { - delete embark_assist::finder_ui::state->ui[i]; + for (uint16_t i = 0; i < embark_assist::finder_ui::state->plotinfo.size(); i++) { + delete embark_assist::finder_ui::state->plotinfo[i]; } delete embark_assist::finder_ui::state; diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp index 980b2db9c..dfddab9e8 100644 --- a/plugins/eventful.cpp +++ b/plugins/eventful.cpp @@ -39,7 +39,7 @@ using namespace df::enums; DFHACK_PLUGIN("eventful"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); typedef df::reaction_product_itemst item_product; diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 6819b6191..4fc82cb97 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -14,7 +14,7 @@ #include "df/plant_growth.h" #include "df/plant_raw.h" #include "df/tile_dig_designation.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" #include "df/world_data.h" #include "df/world_object_data.h" @@ -32,7 +32,7 @@ using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("getplants"); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(cur_year); REQUIRE_GLOBAL(cur_year_tick); @@ -242,7 +242,7 @@ bool ripe(int32_t x, int32_t y, int32_t start, int32_t end) { // Looks in the picked growths vector to see if a matching growth has been marked as picked. bool picked(const df::plant *plant, int32_t growth_subtype) { df::world_data *world_data = world->world_data; - df::world_site *site = df::world_site::find(ui->site_id); + df::world_site *site = df::world_site::find(plotinfo->site_id); int32_t pos_x = site->global_min_x + plant->pos.x / 48; int32_t pos_y = site->global_min_y + plant->pos.y / 48; size_t id = pos_x + pos_y * 16 * world_data->world_width; diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 5f5994435..35b6f2cb5 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -11,8 +11,8 @@ #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" -#include "df/ui_build_selector.h" +#include "df/plotinfost.h" +#include "df/buildreq.h" #include "df/ui_build_item_req.h" #include "df/build_req_choice_genst.h" #include "df/build_req_choice_specst.h" @@ -33,7 +33,7 @@ using namespace df::enums; DFHACK_PLUGIN("jobutils"); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); REQUIRE_GLOBAL(ui_workshop_job_cursor); REQUIRE_GLOBAL(job_next_id); @@ -48,7 +48,7 @@ static command_result job_cmd(color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - if (!world || !ui) + if (!world || !plotinfo) return CR_FAILURE; commands.push_back( @@ -232,9 +232,9 @@ static command_result job_material(color_ostream &out, vector & paramet else return CR_WRONG_USAGE; - if (ui->main.mode == ui_sidebar_mode::QueryBuilding) + if (plotinfo->main.mode == ui_sidebar_mode::QueryBuilding) return job_material_in_job(out, new_mat); - if (ui->main.mode == ui_sidebar_mode::Build) + if (plotinfo->main.mode == ui_sidebar_mode::Build) return job_material_in_build(out, new_mat); return CR_WRONG_USAGE; diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 2d3f3bc0a..6731aa513 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -19,7 +19,7 @@ #include "df/activity_event.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/graphic.h" #include "df/enabler.h" #include "df/viewscreen_unitlistst.h" @@ -49,7 +49,7 @@ using namespace df::enums; DFHACK_PLUGIN("manipulator"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(enabler); @@ -1972,9 +1972,9 @@ void viewscreen_unitlaborsst::render() Screen::paintTile(Screen::Pen(columns[col_offset].label[0], fg, bg), col_offsets[DISP_COLUMN_LABORS] + col, 1); Screen::paintTile(Screen::Pen(columns[col_offset].label[1], fg, bg), col_offsets[DISP_COLUMN_LABORS] + col, 2); df::profession profession = columns[col_offset].profession; - if ((profession != profession::NONE) && (ui->race_id != -1)) + if ((profession != profession::NONE) && (plotinfo->race_id != -1)) { - auto graphics = world->raws.creatures.all[ui->race_id]->graphics; + auto graphics = world->raws.creatures.all[plotinfo->race_id]->graphics; Screen::paintTile( Screen::Pen(' ', fg, 0, graphics.profession_add_color[creature_graphics_role::DEFAULT][profession], diff --git a/plugins/misery.cpp b/plugins/misery.cpp index 224060c6b..870c4480c 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -10,7 +10,7 @@ #include "modules/Units.h" #include "df/emotion_type.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_personality.h" #include "df/unit_soul.h" @@ -24,7 +24,7 @@ DFHACK_PLUGIN("misery"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(cur_year); REQUIRE_GLOBAL(cur_year_tick); diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 124664fa2..1a088141a 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -6,8 +6,8 @@ #include "df/viewscreen_dwarfmodest.h" #include "df/world.h" #include "df/items_other_id.h" -#include "df/ui_build_selector.h" -#include "df/ui_sidebar_menus.h" +#include "df/buildreq.h" +#include "df/gamest.h" #include "modules/Gui.h" #include "modules/World.h" @@ -26,7 +26,7 @@ DFHACK_PLUGIN("mousequery"); REQUIRE_GLOBAL(enabler); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); using namespace df::enums::ui_sidebar_mode; @@ -196,7 +196,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest bool isInDesignationMenu() { - switch (ui->main.mode) + switch (plotinfo->main.mode) { case DesignateMine: case DesignateRemoveRamps: @@ -230,7 +230,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest return true; case Burrows: - return ui->burrows.in_define_mode; + return plotinfo->burrows.in_define_mode; default: return false; @@ -242,7 +242,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest if (isInDesignationMenu()) return box_designation_enabled; - switch (ui->main.mode) + switch (plotinfo->main.mode) { case DesignateItemsClaim: case DesignateItemsForbid: @@ -281,8 +281,8 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest { bool selectableMode = isInDesignationMenu() || - ui->main.mode == Stockpiles || - ui->main.mode == Zones; + plotinfo->main.mode == Stockpiles || + plotinfo->main.mode == Zones; if (selectableMode) { @@ -314,7 +314,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest } else { - switch (ui->main.mode) + switch (plotinfo->main.mode) { case QueryBuilding: if (cursor_still_here) @@ -369,8 +369,8 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest if (mx < 1 || mx > dims.map_x2 || my < 1 || my > dims.map_y2) return false; - if (ui->main.mode == df::ui_sidebar_mode::Zones || - ui->main.mode == df::ui_sidebar_mode::Stockpiles) + if (plotinfo->main.mode == df::ui_sidebar_mode::Zones || + plotinfo->main.mode == df::ui_sidebar_mode::Stockpiles) { int32_t x, y, z; if (Gui::getDesignationCoords(x, y, z)) @@ -417,8 +417,8 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest enabler->mouse_rbut = 0; using namespace df::enums::ui_sidebar_mode; - if ((ui->main.mode == QueryBuilding || ui->main.mode == BuildingItems || - ui->main.mode == ViewUnits || ui->main.mode == LookAround) || + if ((plotinfo->main.mode == QueryBuilding || plotinfo->main.mode == BuildingItems || + plotinfo->main.mode == ViewUnits || plotinfo->main.mode == LookAround) || (isInTrackableMode() && tracking_enabled)) { sendKey(df::interface_key::LEAVESCREEN); @@ -707,8 +707,8 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest if (Gui::getDesignationCoords(x, y, z)) { color = COLOR_WHITE; - if (ui->main.mode == df::ui_sidebar_mode::Zones || - ui->main.mode == df::ui_sidebar_mode::Stockpiles) + if (plotinfo->main.mode == df::ui_sidebar_mode::Zones || + plotinfo->main.mode == df::ui_sidebar_mode::Stockpiles) { auto dX = abs(x - mpos.x); if (dX > 30) diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 403a8cb37..1908684d4 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -5,7 +5,7 @@ #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/building_nest_boxst.h" #include "df/building_type.h" #include "df/buildings_other_id.h" @@ -26,7 +26,7 @@ using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; static command_result nestboxes(color_ostream &out, vector & parameters); @@ -67,7 +67,7 @@ static void eggscan(color_ostream &out) DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - if (world && ui) { + if (world && plotinfo) { commands.push_back( PluginCommand( "nestboxes", diff --git a/plugins/power-meter.cpp b/plugins/power-meter.cpp index c61c3c8ee..f5f4ac63f 100644 --- a/plugins/power-meter.cpp +++ b/plugins/power-meter.cpp @@ -29,8 +29,8 @@ #include "df/machine_info.h" #include "df/report.h" #include "df/tile_designation.h" -#include "df/ui.h" -#include "df/ui_build_selector.h" +#include "df/plotinfost.h" +#include "df/buildreq.h" #include "df/viewscreen_dwarfmodest.h" #include "df/world.h" @@ -43,7 +43,7 @@ using namespace df::enums; DFHACK_PLUGIN("power-meter"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); static const uint32_t METER_BIT = 0x80000000U; diff --git a/plugins/remotefortressreader/adventure_control.cpp b/plugins/remotefortressreader/adventure_control.cpp index 6fb592d62..2dbf2abc1 100644 --- a/plugins/remotefortressreader/adventure_control.cpp +++ b/plugins/remotefortressreader/adventure_control.cpp @@ -7,7 +7,7 @@ #include "df/adventure_movement_hold_itemst.h" #include "df/adventure_movement_hold_tilest.h" #include "df/adventure_movement_optionst.h" -#include "df/ui_advmode.h" +#include "df/adventurest.h" #include "df/viewscreen.h" #include "modules/Gui.h" @@ -42,7 +42,7 @@ command_result MoveCommand(DFHack::color_ostream &stream, const MoveCommandParam auto viewScreen = getCurViewscreen(); if (!in->has_direction()) return CR_WRONG_USAGE; - if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) + if (!df::global::adventure->menu == ui_advmode_menu::Default) return CR_OK; auto dir = in->direction(); switch (dir.x()) @@ -193,7 +193,7 @@ command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParam { if (!in->has_direction()) return CR_WRONG_USAGE; - if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) + if (!df::global::adventure->menu == ui_advmode_menu::Default) return CR_OK; auto dir = in->direction(); keyQueue.push(interface_key::A_JUMP); @@ -233,7 +233,7 @@ command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParam command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, MenuContents *out) { - auto advUi = df::global::ui_advmode; + auto advUi = df::global::adventure; if (advUi == NULL) return CR_FAILURE; @@ -278,7 +278,7 @@ command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in) { - if (!(df::global::ui_advmode->menu == ui_advmode_menu::MoveCarefully)) + if (!(df::global::adventure->menu == ui_advmode_menu::MoveCarefully)) return CR_OK; int choice = in->value(); int page = choice / 5; @@ -293,7 +293,7 @@ command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfprot command_result MiscMoveCommand(DFHack::color_ostream &stream, const MiscMoveParams *in) { - if (!df::global::ui_advmode->menu == ui_advmode_menu::Default) + if (!df::global::adventure->menu == ui_advmode_menu::Default) return CR_OK; auto type = in->type(); diff --git a/plugins/remotefortressreader/dwarf_control.cpp b/plugins/remotefortressreader/dwarf_control.cpp index 3a5933464..f0147670f 100644 --- a/plugins/remotefortressreader/dwarf_control.cpp +++ b/plugins/remotefortressreader/dwarf_control.cpp @@ -12,9 +12,9 @@ #include "df/job_list_link.h" #include "df/interface_button_construction_building_selectorst.h" #include "df/interface_button_construction_category_selectorst.h" -#include "df/ui.h" -#include "df/ui_build_selector.h" -#include "df/ui_sidebar_menus.h" +#include "df/plotinfost.h" +#include "df/buildreq.h" +#include "df/gamest.h" #include "df/viewscreen.h" #include "df/world.h" @@ -233,7 +233,7 @@ command_result SetPauseState(color_ostream &stream, const SingleBool *in) void CopyBuildMenu(DwarfControl::SidebarState * out) { - auto menus = df::global::ui_sidebar_menus; + auto menus = df::global::game; auto build_selector = df::global::ui_build_selector; if (build_selector->building_type == -1) for (size_t i = 0; i < menus->building.choices_visible.size(); i++) @@ -303,9 +303,9 @@ void CopyBuildMenu(DwarfControl::SidebarState * out) command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out) { - auto ui = df::global::ui; - out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)ui->main.mode); - auto mode = ui->main.mode; + auto plotinfo = df::global::plotinfo; + out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)plotinfo->main.mode); + auto mode = plotinfo->main.mode; switch (mode) { case ui_sidebar_mode::Default: @@ -427,25 +427,25 @@ command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMe command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in) { - auto ui = df::global::ui; + auto plotinfo = df::global::plotinfo; if (in->has_mode()) { ui_sidebar_mode::ui_sidebar_mode set_mode = (ui_sidebar_mode::ui_sidebar_mode)in->mode(); - if (ui->main.mode != set_mode) + if (plotinfo->main.mode != set_mode) { - ui->main.mode = ui_sidebar_mode::Default; + plotinfo->main.mode = ui_sidebar_mode::Default; switch (set_mode) { case ui_sidebar_mode::Build: keyQueue.push(interface_key::D_BUILDING); break; default: - ui->main.mode = set_mode; + plotinfo->main.mode = set_mode; break; } } } - switch (ui->main.mode) + switch (plotinfo->main.mode) { case ui_sidebar_mode::Build: if (in->has_action()) @@ -454,7 +454,7 @@ command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::Si if (in->has_menu_index()) index = in->menu_index(); if(ui_build_selector->building_type == -1) - df::global::ui_sidebar_menus->building.cursor = index; + df::global::game->building.cursor = index; if (ui_build_selector->stage == 2) { ui_build_selector->sel_index = index; diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index 643e92b21..536aa3b0d 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -89,7 +89,7 @@ #endif #include "df/tissue.h" #include "df/tissue_style_raw.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/unit_wound.h" @@ -135,9 +135,9 @@ using namespace df::global; #else REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(gamemode); -REQUIRE_GLOBAL(ui_advmode); +REQUIRE_GLOBAL(adventure); #endif // Here go all the command declarations... @@ -1887,8 +1887,8 @@ static command_result GetViewInfo(color_ostream &stream, const EmptyMessage *in, if (gamemode && *gamemode == GameMode::ADVENTURE) out->set_follow_unit_id(world->units.active[0]->id); else - out->set_follow_unit_id(ui->follow_unit); - out->set_follow_item_id(ui->follow_item); + out->set_follow_unit_id(plotinfo->follow_unit); + out->set_follow_item_id(plotinfo->follow_item); return CR_OK; } diff --git a/plugins/rename.cpp b/plugins/rename.cpp index 7c46fc908..68186cb74 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -13,8 +13,8 @@ #include "modules/Screen.h" #include -#include "df/ui.h" -#include "df/ui_sidebar_menus.h" +#include "df/plotinfost.h" +#include "df/gamest.h" #include "df/world.h" #include "df/squad.h" #include "df/unit.h" @@ -49,8 +49,8 @@ using namespace dfproto; DFHACK_PLUGIN("rename"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(ui); -REQUIRE_GLOBAL(ui_sidebar_menus); +REQUIRE_GLOBAL(plotinfo); +REQUIRE_GLOBAL(game); REQUIRE_GLOBAL(world); DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event); @@ -59,7 +59,7 @@ static command_result rename(color_ostream &out, vector & parameters); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - if (world && ui) { + if (world && plotinfo) { commands.push_back(PluginCommand( "rename", "Easily rename things.", @@ -135,9 +135,9 @@ struct dwarf_render_zone_hook : df::viewscreen_dwarfmodest { { INTERPOSE_NEXT(render)(); - if (ui->main.mode == ui_sidebar_mode::Zones && - ui_sidebar_menus && ui_sidebar_menus->zone.selected && - !ui_sidebar_menus->zone.selected->name.empty()) + if (plotinfo->main.mode == ui_sidebar_mode::Zones && + game && game->zone.selected && + !game->zone.selected->name.empty()) { auto dims = Gui::getDwarfmodeViewDims(); int width = dims.menu_x2 - dims.menu_x1 - 1; @@ -146,7 +146,7 @@ struct dwarf_render_zone_hook : df::viewscreen_dwarfmodest { Screen::fillRect(pen, dims.menu_x1, dims.y1+1, dims.menu_x2, dims.y1+1); std::string name; - ui_sidebar_menus->zone.selected->getName(&name); + game->zone.selected->getName(&name); Screen::paintString(pen, dims.menu_x1+1, dims.y1+1, name.substr(0, width)); } } @@ -258,7 +258,7 @@ static bool renameBuilding(df::building *bld, std::string name) static df::squad *getSquadByIndex(unsigned idx) { - auto entity = df::historical_entity::find(ui->group_id); + auto entity = df::historical_entity::find(plotinfo->group_id); if (!entity) return NULL; @@ -361,7 +361,7 @@ static command_result rename(color_ostream &out, vector ¶meters) return CR_WRONG_USAGE; } - ui->main.hotkeys[id-1].name = parameters[2]; + plotinfo->main.hotkeys[id-1].name = parameters[2]; } else if (cmd == "unit") { diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index cc5d569c0..428de3a25 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -94,7 +94,7 @@ rect2d getMapViewport() { view_rb=area_x2; } - if (menu_posmain.mode!=0) + if (menu_posmain.mode!=0) { if (menu_pos >= area_pos) menu_pos = area_pos-1; diff --git a/plugins/rendermax/rendermax.cpp b/plugins/rendermax/rendermax.cpp index b7c703e09..6ed22fe77 100644 --- a/plugins/rendermax/rendermax.cpp +++ b/plugins/rendermax/rendermax.cpp @@ -31,7 +31,7 @@ REQUIRE_GLOBAL(cursor); REQUIRE_GLOBAL(enabler); REQUIRE_GLOBAL(gametype); REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(window_x); REQUIRE_GLOBAL(window_y); REQUIRE_GLOBAL(window_z); diff --git a/plugins/search.cpp b/plugins/search.cpp index 9ecae6460..b69480778 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -52,7 +52,7 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(gview); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_building_assign_units); REQUIRE_GLOBAL(ui_building_in_assign); REQUIRE_GLOBAL(ui_building_item_cursor); @@ -1878,7 +1878,7 @@ class look_menu_search : public look_menu_search_base public: bool can_init(df::viewscreen_dwarfmodest *screen) { - if (ui->main.mode == df::ui_sidebar_mode::LookAround) + if (plotinfo->main.mode == df::ui_sidebar_mode::LookAround) { return look_menu_search_base::can_init(screen); } @@ -1986,7 +1986,7 @@ class burrow_search : public burrow_search_base public: bool can_init(df::viewscreen_dwarfmodest *screen) { - if (ui->main.mode == df::ui_sidebar_mode::Burrows && ui->burrows.in_add_units_mode) + if (plotinfo->main.mode == df::ui_sidebar_mode::Burrows && plotinfo->burrows.in_add_units_mode) { return burrow_search_base::can_init(screen); } @@ -2011,17 +2011,17 @@ public: vector *get_primary_list() { - return &ui->burrows.list_units; + return &plotinfo->burrows.list_units; } vector *get_secondary_list() { - return &ui->burrows.sel_units; + return &plotinfo->burrows.sel_units; } virtual int32_t * get_viewscreen_cursor() { - return &ui->burrows.unit_cursor_pos; + return &plotinfo->burrows.unit_cursor_pos; } @@ -2055,7 +2055,7 @@ class room_assign_search : public room_assign_search_base public: bool can_init(df::viewscreen_dwarfmodest *screen) { - if (ui->main.mode == df::ui_sidebar_mode::QueryBuilding && *ui_building_in_assign) + if (plotinfo->main.mode == df::ui_sidebar_mode::QueryBuilding && *ui_building_in_assign) { return room_assign_search_base::can_init(screen); } diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 55ea4eeab..2fbb1e8c8 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -47,8 +47,8 @@ #include "df/report.h" #include "df/stockpile_links.h" #include "df/strain_type.h" -#include "df/ui.h" -#include "df/ui_build_selector.h" +#include "df/plotinfost.h" +#include "df/buildreq.h" #include "df/unit.h" #include "df/unit_misc_trait.h" #include "df/unit_relationship_type.h" @@ -72,7 +72,7 @@ DFHACK_PLUGIN("siege-engine"); REQUIRE_GLOBAL(gamemode); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); REQUIRE_GLOBAL(process_jobs); @@ -1477,7 +1477,7 @@ static bool isTired(df::unit *worker) static void releaseTiredWorker(EngineInfo *engine, df::job *job, df::unit *worker) { // If not in siege - auto &sieges = ui->invasions.list; + auto &sieges = plotinfo->invasions.list; for (size_t i = 0; i < sieges.size(); i++) if (sieges[i]->flags.bits.active) diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 630ac9e53..62af2c416 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -11,7 +11,7 @@ #include "LuaTools.h" #include "DataDefs.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" #include "df/viewscreen_joblistst.h" #include "df/viewscreen_unitlistst.h" @@ -38,7 +38,7 @@ using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("sort"); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(ui_building_in_assign); REQUIRE_GLOBAL(ui_building_item_cursor); @@ -379,11 +379,11 @@ DEFINE_SORT_HANDLER(unit_sorters, dwarfmode, "/Burrows/AddUnits", screen) { PARSE_SPEC("units", parameters); - if (compute_order(*pout, L, top, &order, ui->burrows.list_units)) + if (compute_order(*pout, L, top, &order, plotinfo->burrows.list_units)) { - reorder_cursor(&ui->burrows.unit_cursor_pos, order); - reorder_vector(&ui->burrows.list_units, order); - reorder_vector(&ui->burrows.sel_units, order); + reorder_cursor(&plotinfo->burrows.unit_cursor_pos, order); + reorder_vector(&plotinfo->burrows.list_units, order); + reorder_vector(&plotinfo->burrows.sel_units, order); } } diff --git a/plugins/spectate/pause.cpp b/plugins/spectate/pause.cpp index 5c60e2194..0e4f1707d 100644 --- a/plugins/spectate/pause.cpp +++ b/plugins/spectate/pause.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -25,12 +25,12 @@ namespace pausing { bool locked_states[announcement_flag_arr_size]; // locked state (re-applied each frame) bool allow_player_pause = true; // toggles player pause ability - using df::global::ui; + using df::global::plotinfo; using namespace df::enums; struct player_pause_hook : df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set* input)) { - if ((ui->main.mode == ui_sidebar_mode::Default) && !allow_player_pause) { + if ((plotinfo->main.mode == ui_sidebar_mode::Default) && !allow_player_pause) { input->erase(interface_key::D_PAUSE); } INTERPOSE_NEXT(feed)(input); diff --git a/plugins/spectate/spectate.cpp b/plugins/spectate/spectate.cpp index 73574c136..7bf3e1ec2 100644 --- a/plugins/spectate/spectate.cpp +++ b/plugins/spectate/spectate.cpp @@ -37,7 +37,7 @@ namespace DFHack { DFHACK_PLUGIN("spectate"); DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(pause_state); REQUIRE_GLOBAL(d_init); @@ -106,7 +106,7 @@ namespace SP { out.print(" SETTINGS:\n"); out.print(" %-20s\t%" PRIi32 "\n", "tick-threshold: ", config.tick_threshold); if (following_dwarf) - out.print(" %-21s\t%s[id: %d]\n","FOLLOWING:", our_dorf ? our_dorf->name.first_name.c_str() : "nullptr", df::global::ui->follow_unit); + out.print(" %-21s\t%s[id: %d]\n","FOLLOWING:", our_dorf ? our_dorf->name.first_name.c_str() : "nullptr", df::global::plotinfo->follow_unit); } void SetUnpauseState(bool state) { @@ -311,7 +311,7 @@ namespace SP { // if you're looking at a warning about a local address escaping, it means the unit* from units (which aren't local) size_t idx = follow_any(RNG); our_dorf = units[idx]; - df::global::ui->follow_unit = our_dorf->id; + df::global::plotinfo->follow_unit = our_dorf->id; timestamp = df::global::world->frame_counter; return true; } else { @@ -362,7 +362,7 @@ namespace SP { if (!World::ReadPauseState() && tick - last_tick >= 1) { last_tick = tick; // validate follow state - if (!following_dwarf || !our_dorf || df::global::ui->follow_unit < 0 || tick - timestamp >= config.tick_threshold) { + if (!following_dwarf || !our_dorf || df::global::plotinfo->follow_unit < 0 || tick - timestamp >= config.tick_threshold) { // we're not following anyone following_dwarf = false; if (!config.disengage) { diff --git a/plugins/steam-engine.cpp b/plugins/steam-engine.cpp index 6e3c317d3..ac7803b95 100644 --- a/plugins/steam-engine.cpp +++ b/plugins/steam-engine.cpp @@ -31,8 +31,8 @@ #include "df/power_info.h" #include "df/report.h" #include "df/tile_designation.h" -#include "df/ui.h" -#include "df/ui_build_selector.h" +#include "df/plotinfost.h" +#include "df/buildreq.h" #include "df/viewscreen_dwarfmodest.h" #include "df/workshop_type.h" #include "df/world.h" @@ -48,7 +48,7 @@ DFHACK_PLUGIN("steam-engine"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); REQUIRE_GLOBAL(cursor); @@ -748,7 +748,7 @@ struct dwarfmode_hook : df::viewscreen_dwarfmodest steam_engine_workshop *get_steam_engine() { - if (ui->main.mode == ui_sidebar_mode::Build && + if (plotinfo->main.mode == ui_sidebar_mode::Build && ui_build_selector->stage == 1 && ui_build_selector->building_type == building_type::Workshop && ui_build_selector->building_subtype == workshop_type::Custom) diff --git a/plugins/stockflow.cpp b/plugins/stockflow.cpp index 65223f603..49d56816b 100644 --- a/plugins/stockflow.cpp +++ b/plugins/stockflow.cpp @@ -26,7 +26,7 @@ DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); bool fast = false; @@ -56,15 +56,15 @@ public: if (found) { // Entice the bookkeeper to spend less time update records. - ui->nobles.bookkeeper_precision += ui->nobles.bookkeeper_precision >> 3; + plotinfo->nobles.bookkeeper_precision += plotinfo->nobles.bookkeeper_precision >> 3; if (!bookkeeping) { command_method("start_bookkeeping", out); bookkeeping = true; } } else { // Entice the bookkeeper to update records more often. - ui->nobles.bookkeeper_precision -= ui->nobles.bookkeeper_precision >> 5; - ui->nobles.bookkeeper_cooldown -= ui->nobles.bookkeeper_cooldown >> 2; + plotinfo->nobles.bookkeeper_precision -= plotinfo->nobles.bookkeeper_precision >> 5; + plotinfo->nobles.bookkeeper_cooldown -= plotinfo->nobles.bookkeeper_cooldown >> 2; if (bookkeeping) { command_method("finish_bookkeeping", out); bookkeeping = false; diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index ff0a38241..478b403f2 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -105,7 +105,7 @@ private: */ void write(); - // parse serialized data into ui indices + // parse serialized data into plotinfo indices void read (); /** diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index aa36e20bd..4b61ff175 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -21,7 +21,7 @@ #include "df/world_data.h" #include "DataDefs.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/building_stockpilest.h" #include "df/stockpile_settings.h" #include "df/global_objects.h" @@ -42,7 +42,7 @@ using namespace dfstockpiles; DFHACK_PLUGIN ( "stockpiles" ); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(selection_rect); using df::building_stockpilest; @@ -59,7 +59,7 @@ static bool loadstock_guard ( df::viewscreen *top ); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands ) { - if ( world && ui ) + if ( world && plotinfo ) { commands.push_back(PluginCommand( "copystock", @@ -106,7 +106,7 @@ static bool copystock_guard ( df::viewscreen *top ) if ( !Gui::dwarfmode_hotkey ( top ) ) return false; - switch ( ui->main.mode ) + switch ( plotinfo->main.mode ) { case Stockpiles: return true; @@ -123,10 +123,10 @@ static command_result copystock ( color_ostream &out, vector & paramete // HOTKEY COMMAND: CORE ALREADY SUSPENDED // For convenience: when used in the stockpiles mode, switch to 'q' - if ( ui->main.mode == ui_sidebar_mode::Stockpiles ) + if ( plotinfo->main.mode == ui_sidebar_mode::Stockpiles ) { world->selected_building = NULL; // just in case it contains some kind of garbage - ui->main.mode = ui_sidebar_mode::QueryBuilding; + plotinfo->main.mode = ui_sidebar_mode::QueryBuilding; selection_rect->start_x = -30000; out << "Switched back to query building." << endl; @@ -140,8 +140,8 @@ static command_result copystock ( color_ostream &out, vector & paramete return CR_WRONG_USAGE; } - ui->stockpile.custom_settings = sp->settings; - ui->main.mode = ui_sidebar_mode::Stockpiles; + plotinfo->stockpile.custom_settings = sp->settings; + plotinfo->main.mode = ui_sidebar_mode::Stockpiles; world->selected_stockpile_type = stockpile_category::Custom; out << "Stockpile options copied." << endl; @@ -156,7 +156,7 @@ static bool savestock_guard ( df::viewscreen *top ) if ( !Gui::dwarfmode_hotkey ( top ) ) return false; - switch ( ui->main.mode ) + switch ( plotinfo->main.mode ) { case Stockpiles: return true; @@ -175,7 +175,7 @@ static bool loadstock_guard ( df::viewscreen *top ) if ( !Gui::dwarfmode_hotkey ( top ) ) return false; - switch ( ui->main.mode ) + switch ( plotinfo->main.mode ) { case Stockpiles: return true; diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 62c13dcd5..8cc27067c 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -27,7 +27,7 @@ #include "modules/Maps.h" #include "modules/Units.h" #include "df/building_cagest.h" -#include "df/ui_advmode.h" +#include "df/adventurest.h" DFHACK_PLUGIN("stocks"); #define PLUGIN_VERSION 0.13 diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp index a5b8e74a4..f6dd1f747 100644 --- a/plugins/strangemood.cpp +++ b/plugins/strangemood.cpp @@ -23,7 +23,7 @@ #include "df/job.h" #include "df/job_item.h" #include "df/map_block.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_preference.h" #include "df/unit_relationship_type.h" @@ -39,7 +39,7 @@ using namespace df::enums; DFHACK_PLUGIN("strangemood"); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(d_init); REQUIRE_GLOBAL(created_item_count); REQUIRE_GLOBAL(created_item_type); @@ -394,7 +394,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) out.printerr("Strange moods disabled via debug flag!\n"); return CR_FAILURE; } - if (ui->mood_cooldown && !force) + if (plotinfo->mood_cooldown && !force) { out.printerr("Last strange mood happened too recently!\n"); return CR_FAILURE; @@ -406,7 +406,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) df::unit *cur = world->units.active[i]; if (Units::isCitizen(cur) && cur->flags1.bits.has_mood) { - ui->mood_cooldown = 1000; + plotinfo->mood_cooldown = 1000; out.printerr("A strange mood is already in progress!\n"); return CR_FAILURE; } @@ -466,12 +466,12 @@ command_result df_strangemood (color_ostream &out, vector & parameters) if (blk->designation[x][y].bits.subterranean && !blk->designation[x][y].bits.hidden) num_revealed_tiles++; } - if (num_revealed_tiles / 2304 < ui->tasks.num_artifacts) + if (num_revealed_tiles / 2304 < plotinfo->tasks.num_artifacts) { out.printerr("Fortress is not eligible for a strange mood at this time - not enough subterranean tiles revealed.\n"); return CR_FAILURE; } - if (num_items / 200 < ui->tasks.num_artifacts) + if (num_items / 200 < plotinfo->tasks.num_artifacts) { out.printerr("Fortress is not eligible for a strange mood at this time - not enough items created\n"); return CR_FAILURE; @@ -544,7 +544,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) return CR_FAILURE; } - ui->mood_cooldown = 1000; + plotinfo->mood_cooldown = 1000; // If no mood type was specified, pick one randomly if (type == mood_type::None) { @@ -1034,7 +1034,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) (job->job_type == job_type::StrangeMoodFell) )) { - int extra_items = std::min(rng.df_trandom((ui->tasks.num_artifacts * 20 + moodable_units.size()) / 20 + 1), 7); + int extra_items = std::min(rng.df_trandom((plotinfo->tasks.num_artifacts * 20 + moodable_units.size()) / 20 + 1), 7); df::item_type avoid_type = item_type::NONE; int avoid_glass = 0; switch (skill) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index bd11988d0..0a8a82f5b 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -20,7 +20,7 @@ #include "df/job.h" #include "df/job_type.h" #include "df/manager_order.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" #include "modules/Maps.h" @@ -32,14 +32,14 @@ using namespace DFHack; using namespace std; using df::global::world; -using df::global::ui; +using df::global::plotinfo; DFHACK_PLUGIN("tailor"); #define AUTOENABLE false DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); class Tailor { // ARMOR, SHOES, HELM, GLOVES, PANTS @@ -261,7 +261,7 @@ private: void create_orders() { - auto entity = world->entities.all[ui->civ_id]; + auto entity = world->entities.all[plotinfo->civ_id]; for (auto& a : needed) { @@ -325,7 +325,7 @@ private: void place_orders() { - auto entity = world->entities.all[ui->civ_id]; + auto entity = world->entities.all[plotinfo->civ_id]; for (auto& o : orders) { diff --git a/plugins/trackstop.cpp b/plugins/trackstop.cpp index d21df0e36..43807ea8c 100644 --- a/plugins/trackstop.cpp +++ b/plugins/trackstop.cpp @@ -26,7 +26,7 @@ DFHACK_PLUGIN("trackstop"); DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); /* @@ -44,7 +44,7 @@ struct trackstop_hook : public df::viewscreen_dwarfmodest { }; building_trapst *get_selected_trackstop() { - if (ui->main.mode != ui_sidebar_mode::QueryBuilding) { + if (plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) { // Not in a building's 'q' menu. return nullptr; } @@ -200,7 +200,7 @@ struct roller_hook : public df::viewscreen_dwarfmodest { }; building_rollersst *get_selected_roller() { - if (ui->main.mode != ui_sidebar_mode::QueryBuilding) { + if (plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) { // Not in a building's 'q' menu. return nullptr; } diff --git a/plugins/tweak/tweak.cpp b/plugins/tweak/tweak.cpp index 6d2a46fa8..323f5f932 100644 --- a/plugins/tweak/tweak.cpp +++ b/plugins/tweak/tweak.cpp @@ -21,7 +21,7 @@ #include "DataDefs.h" #include #include "../uicommon.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" #include "df/unit.h" #include "df/unit_soul.h" @@ -36,8 +36,8 @@ #include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_kitchenprefst.h" #include "df/viewscreen_layer_unit_actionst.h" -#include "df/ui_build_selector.h" -#include "df/ui_sidebar_menus.h" +#include "df/buildreq.h" +#include "df/gamest.h" #include "df/building_trapst.h" #include "df/building_workshopst.h" #include "df/item_actual.h" @@ -78,7 +78,7 @@ #include "tweaks/block-labors.h" #include "tweaks/burrow-name-cancel.h" #include "tweaks/cage-butcher.h" -#include "tweaks/civ-agreement-ui.h" +#include "tweaks/civ-agreement-plotinfo.h" #include "tweaks/condition-material.h" #include "tweaks/craft-age-wear.h" #include "tweaks/do-job-now.h" @@ -117,14 +117,14 @@ DFHACK_PLUGIN("tweak"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(enabler); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_build_selector); REQUIRE_GLOBAL(ui_building_in_assign); REQUIRE_GLOBAL(ui_building_in_resize); REQUIRE_GLOBAL(ui_building_item_cursor); REQUIRE_GLOBAL(ui_look_cursor); REQUIRE_GLOBAL(ui_menu_width); -REQUIRE_GLOBAL(ui_sidebar_menus); +REQUIRE_GLOBAL(game); REQUIRE_GLOBAL(ui_unit_view_mode); REQUIRE_GLOBAL(ui_workshop_in_add); REQUIRE_GLOBAL(world); @@ -449,7 +449,7 @@ static command_result tweak(color_ostream &out, vector ¶meters) // if it happens that the player has 'foreign' units of the same race // (vanilla df: dwarves not from mountainhome) on his map, just grab them if(!Units::isOwnCiv(unit)) - unit->civ_id = ui->civ_id; + unit->civ_id = plotinfo->civ_id; return fix_clothing_ownership(out, unit); } @@ -468,7 +468,7 @@ static command_result tweak(color_ostream &out, vector ¶meters) if(Units::isForest(unit)) unit->flags1.bits.forest = 0; if(!Units::isOwnCiv(unit)) - unit->civ_id = ui->civ_id; + unit->civ_id = plotinfo->civ_id; if(unit->profession == df::profession::MERCHANT) unit->profession = df::profession::TRADER; if(unit->profession2 == df::profession::MERCHANT) diff --git a/plugins/tweak/tweaks/block-labors.h b/plugins/tweak/tweaks/block-labors.h index bc407ed19..df2474400 100644 --- a/plugins/tweak/tweaks/block-labors.h +++ b/plugins/tweak/tweaks/block-labors.h @@ -6,7 +6,7 @@ #include "df/viewscreen_dwarfmodest.h" using namespace DFHack; -using df::global::ui; +using df::global::plotinfo; using df::global::ui_look_cursor; using df::global::ui_unit_view_mode; @@ -15,7 +15,7 @@ struct block_labors_hook : df::viewscreen_dwarfmodest { inline bool valid_mode() { - return ui->main.mode == df::ui_sidebar_mode::ViewUnits && + return plotinfo->main.mode == df::ui_sidebar_mode::ViewUnits && ui_unit_view_mode->value == df::ui_unit_view_mode::T_value::PrefLabor && Gui::getAnyUnit(this); } diff --git a/plugins/tweak/tweaks/burrow-name-cancel.h b/plugins/tweak/tweaks/burrow-name-cancel.h index 2ee84508e..f49e7ae1b 100644 --- a/plugins/tweak/tweaks/burrow-name-cancel.h +++ b/plugins/tweak/tweaks/burrow-name-cancel.h @@ -1,6 +1,6 @@ #include "df/burrow.h" -using df::global::ui; +using df::global::plotinfo; struct burrow_name_cancel_hook : df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; @@ -9,15 +9,15 @@ struct burrow_name_cancel_hook : df::viewscreen_dwarfmodest { DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input)) { - if (ui->main.mode == df::ui_sidebar_mode::Burrows) + if (plotinfo->main.mode == df::ui_sidebar_mode::Burrows) { - bool was_naming = ui->burrows.in_edit_name_mode; + bool was_naming = plotinfo->burrows.in_edit_name_mode; INTERPOSE_NEXT(feed)(input); - df::burrow *burrow = vector_get(ui->burrows.list, ui->burrows.sel_index); + df::burrow *burrow = vector_get(plotinfo->burrows.list, plotinfo->burrows.sel_index); if (!burrow) return; - if (ui->burrows.in_edit_name_mode) + if (plotinfo->burrows.in_edit_name_mode) { if (!was_naming) { @@ -27,7 +27,7 @@ struct burrow_name_cancel_hook : df::viewscreen_dwarfmodest { if (input->count(df::interface_key::LEAVESCREEN)) { // Cancel and restore the old name - ui->burrows.in_edit_name_mode = false; + plotinfo->burrows.in_edit_name_mode = false; burrow->name = old_name; } } diff --git a/plugins/tweak/tweaks/cage-butcher.h b/plugins/tweak/tweaks/cage-butcher.h index 61a989d50..7290746a9 100644 --- a/plugins/tweak/tweaks/cage-butcher.h +++ b/plugins/tweak/tweaks/cage-butcher.h @@ -6,7 +6,7 @@ #include "df/unit.h" using namespace DFHack; -using df::global::ui; +using df::global::plotinfo; using df::global::ui_building_in_assign; using df::global::ui_building_in_resize; using df::global::ui_building_item_cursor; @@ -19,7 +19,7 @@ struct cage_butcher_hook : df::viewscreen_dwarfmodest { if (*ui_building_in_assign || *ui_building_in_resize) return nullptr; - if (ui->main.mode != df::ui_sidebar_mode::QueryBuilding) + if (plotinfo->main.mode != df::ui_sidebar_mode::QueryBuilding) return nullptr; auto cage = virtual_cast(Gui::getAnyBuilding(this)); diff --git a/plugins/tweak/tweaks/eggs-fertile.h b/plugins/tweak/tweaks/eggs-fertile.h index f4fe29569..b264b227f 100644 --- a/plugins/tweak/tweaks/eggs-fertile.h +++ b/plugins/tweak/tweaks/eggs-fertile.h @@ -6,15 +6,15 @@ using namespace DFHack; using namespace df::enums; using df::global::world; -using df::global::ui; +using df::global::plotinfo; struct egg_fertile_hook : df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; df::building_nest_boxst* getNestBox() { - if (ui->main.mode != ui_sidebar_mode::QueryBuilding && - ui->main.mode != ui_sidebar_mode::BuildingItems) + if (plotinfo->main.mode != ui_sidebar_mode::QueryBuilding && + plotinfo->main.mode != ui_sidebar_mode::BuildingItems) return NULL; return virtual_cast(world->selected_building); } @@ -38,7 +38,7 @@ struct egg_fertile_hook : df::viewscreen_dwarfmodest { has_eggs = true; if (egg->egg_flags.bits.fertile) fertile = true; - if (ui->main.mode == ui_sidebar_mode::BuildingItems) + if (plotinfo->main.mode == ui_sidebar_mode::BuildingItems) { Screen::paintString( Screen::Pen(' ', fertile ? COLOR_LIGHTGREEN : COLOR_LIGHTRED), @@ -50,7 +50,7 @@ struct egg_fertile_hook : df::viewscreen_dwarfmodest { } ++idx; } - if (has_eggs && ui->main.mode == ui_sidebar_mode::QueryBuilding) + if (has_eggs && plotinfo->main.mode == ui_sidebar_mode::QueryBuilding) { Screen::paintString( Screen::Pen(' ', fertile ? COLOR_LIGHTGREEN : COLOR_LIGHTRED), diff --git a/plugins/tweak/tweaks/farm-plot-select.h b/plugins/tweak/tweaks/farm-plot-select.h index 696c7ca2e..7b2e440e1 100644 --- a/plugins/tweak/tweaks/farm-plot-select.h +++ b/plugins/tweak/tweaks/farm-plot-select.h @@ -4,7 +4,7 @@ using namespace df::enums; -using df::global::ui; +using df::global::plotinfo; using df::global::ui_building_item_cursor; using df::global::world; @@ -13,7 +13,7 @@ struct farm_select_hook : df::viewscreen_dwarfmodest { df::building_farmplotst* getFarmPlot() { - if (ui->main.mode != ui_sidebar_mode::QueryBuilding) + if (plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) return NULL; VIRTUAL_CAST_VAR(farm_plot, df::building_farmplotst, world->selected_building); return farm_plot; @@ -44,12 +44,12 @@ struct farm_select_hook : df::viewscreen_dwarfmodest { return false; } - inline int32_t getSelectedCropId() { return ui->selected_farm_crops[*ui_building_item_cursor]; } + inline int32_t getSelectedCropId() { return plotinfo->selected_farm_crops[*ui_building_item_cursor]; } DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) { df::building_farmplotst* farm_plot = getFarmPlot(); - if (farm_plot && ui->selected_farm_crops.size() > 0) + if (farm_plot && plotinfo->selected_farm_crops.size() > 0) { if (input->count(interface_key::SELECT_ALL)) { @@ -77,7 +77,7 @@ struct farm_select_hook : df::viewscreen_dwarfmodest { { INTERPOSE_NEXT(render)(); auto farm_plot = getFarmPlot(); - if (!farm_plot || !ui->selected_farm_crops.size()) + if (!farm_plot || !plotinfo->selected_farm_crops.size()) return; if (farm_plot->getBuildStage() != farm_plot->getMaxBuildStage()) return; diff --git a/plugins/tweak/tweaks/hide-priority.h b/plugins/tweak/tweaks/hide-priority.h index ef84a5831..6f096ee82 100644 --- a/plugins/tweak/tweaks/hide-priority.h +++ b/plugins/tweak/tweaks/hide-priority.h @@ -3,7 +3,7 @@ using namespace DFHack; using df::global::gps; -using df::global::ui_sidebar_menus; +using df::global::game; struct hide_priority_hook : df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; @@ -14,7 +14,7 @@ struct hide_priority_hook : df::viewscreen_dwarfmodest { inline bool valid_mode () { - switch (ui->main.mode) + switch (plotinfo->main.mode) { case df::ui_sidebar_mode::DesignateMine: case df::ui_sidebar_mode::DesignateRemoveRamps: @@ -41,7 +41,7 @@ struct hide_priority_hook : df::viewscreen_dwarfmodest { DEFINE_VMETHOD_INTERPOSE(void, render, ()) { if (!was_valid_mode && valid_mode() && toggled_manually) { - ui_sidebar_menus->designation.priority_set = last_show_priorities_setting; + game->designation.priority_set = last_show_priorities_setting; } INTERPOSE_NEXT(render)(); if (valid_mode()) @@ -51,7 +51,7 @@ struct hide_priority_hook : df::viewscreen_dwarfmodest { { int x = dims.menu_x1 + 1, y = gps->dimy - (gps->dimy > 26 ? 8 : 7); OutputToggleString(x, y, "Show priorities", df::interface_key::CUSTOM_ALT_P, - ui_sidebar_menus->designation.priority_set, true, 0, + game->designation.priority_set, true, 0, COLOR_WHITE, COLOR_LIGHTRED); } } @@ -61,9 +61,9 @@ struct hide_priority_hook : df::viewscreen_dwarfmodest { { if (valid_mode() && input->count(df::interface_key::CUSTOM_ALT_P)) { - ui_sidebar_menus->designation.priority_set = !ui_sidebar_menus->designation.priority_set; + game->designation.priority_set = !game->designation.priority_set; toggled_manually = true; - last_show_priorities_setting = ui_sidebar_menus->designation.priority_set; + last_show_priorities_setting = game->designation.priority_set; } else INTERPOSE_NEXT(feed)(input); diff --git a/plugins/tweak/tweaks/hotkey-clear.h b/plugins/tweak/tweaks/hotkey-clear.h index bc85c112e..8d683a1bd 100644 --- a/plugins/tweak/tweaks/hotkey-clear.h +++ b/plugins/tweak/tweaks/hotkey-clear.h @@ -1,6 +1,6 @@ #include "df/viewscreen_dwarfmodest.h" -using df::global::ui; +using df::global::plotinfo; struct hotkey_clear_hook : df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; @@ -8,7 +8,7 @@ struct hotkey_clear_hook : df::viewscreen_dwarfmodest { DEFINE_VMETHOD_INTERPOSE(void, render, ()) { INTERPOSE_NEXT(render)(); - if (ui->main.mode == df::ui_sidebar_mode::Hotkeys) + if (plotinfo->main.mode == df::ui_sidebar_mode::Hotkeys) { auto dims = Gui::getDwarfmodeViewDims(); int x = dims.menu_x1 + 1, y = 19; @@ -18,11 +18,11 @@ struct hotkey_clear_hook : df::viewscreen_dwarfmodest { DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) { - if (ui->main.mode == df::ui_sidebar_mode::Hotkeys && + if (plotinfo->main.mode == df::ui_sidebar_mode::Hotkeys && input->count(df::interface_key::CUSTOM_C) && - !ui->main.in_rename_hotkey) + !plotinfo->main.in_rename_hotkey) { - auto &hotkey = ui->main.hotkeys[ui->main.selected_hotkey]; + auto &hotkey = plotinfo->main.hotkeys[plotinfo->main.selected_hotkey]; hotkey.name = ""; hotkey.cmd = df::ui_hotkey::T_cmd::None; hotkey.x = 0; diff --git a/plugins/tweak/tweaks/max-wheelbarrow.h b/plugins/tweak/tweaks/max-wheelbarrow.h index a7af186ef..38d704b24 100644 --- a/plugins/tweak/tweaks/max-wheelbarrow.h +++ b/plugins/tweak/tweaks/max-wheelbarrow.h @@ -22,7 +22,7 @@ struct max_wheelbarrow_hook : df::viewscreen_dwarfmodest { df::building_stockpilest* getStockpile() { - if (ui->main.mode != ui_sidebar_mode::QueryBuilding) + if (plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) return NULL; return virtual_cast(world->selected_building); } diff --git a/plugins/tweak/tweaks/shift-8-scroll.h b/plugins/tweak/tweaks/shift-8-scroll.h index 86f7e9fc7..2c1eea23d 100644 --- a/plugins/tweak/tweaks/shift-8-scroll.h +++ b/plugins/tweak/tweaks/shift-8-scroll.h @@ -1,13 +1,13 @@ #include "df/viewscreen_dwarfmodest.h" using namespace df::enums; -using df::global::ui; +using df::global::plotinfo; struct shift_8_scroll_hook : df::viewscreen_dwarfmodest { typedef df::viewscreen_dwarfmodest interpose_base; DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set* input)) { - if (ui->main.mode != ui_sidebar_mode::Default && + if (plotinfo->main.mode != ui_sidebar_mode::Default && input->count(interface_key::CURSOR_UP_FAST) && input->count(interface_key::SECONDSCROLL_PAGEDOWN) ) diff --git a/plugins/tweak/tweaks/stable-cursor.h b/plugins/tweak/tweaks/stable-cursor.h index b08346410..3b628ef04 100644 --- a/plugins/tweak/tweaks/stable-cursor.h +++ b/plugins/tweak/tweaks/stable-cursor.h @@ -6,7 +6,7 @@ using namespace std; using namespace DFHack; using namespace df::enums; -using df::global::ui; +using df::global::plotinfo; using df::global::ui_build_selector; using df::global::ui_menu_width; @@ -18,7 +18,7 @@ struct stable_cursor_hook : df::viewscreen_dwarfmodest bool check_default() { - switch (ui->main.mode) { + switch (plotinfo->main.mode) { case ui_sidebar_mode::Default: return true; diff --git a/plugins/uicommon.h b/plugins/uicommon.h index e7a586585..03914b461 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -24,7 +24,7 @@ #include "df/dfhack_material_category.h" #include "df/enabler.h" #include "df/item_quality.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/world.h" using namespace std; @@ -217,7 +217,7 @@ static inline char get_string_input(const std::set *input) static inline df::building_stockpilest *get_selected_stockpile() { if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || - df::global::ui->main.mode != ui_sidebar_mode::QueryBuilding) + df::global::plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) { return nullptr; } @@ -227,10 +227,10 @@ static inline df::building_stockpilest *get_selected_stockpile() static inline bool can_trade() { - if (df::global::ui->caravans.size() == 0) + if (df::global::plotinfo->caravans.size() == 0) return false; - for (auto it = df::global::ui->caravans.begin(); it != df::global::ui->caravans.end(); it++) + for (auto it = df::global::plotinfo->caravans.begin(); it != df::global::plotinfo->caravans.end(); it++) { typedef df::caravan_state::T_trade_state state; auto caravan = *it; diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 3f132616b..a8f11dec0 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -15,7 +15,7 @@ #include "DataDefs.h" #include "df/world.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/building_workshopst.h" #include "df/building_furnacest.h" #include "df/job.h" @@ -51,7 +51,7 @@ using namespace df::enums; DFHACK_PLUGIN("workflow"); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_workshop_job_cursor); REQUIRE_GLOBAL(job_next_id); @@ -64,7 +64,7 @@ static void cleanup_state(color_ostream &out); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { - if (!world || !ui) + if (!world || !plotinfo) return CR_FAILURE; if (ui_workshop_job_cursor && job_next_id) { @@ -1684,7 +1684,7 @@ static command_result workflow_cmd(color_ostream &out, vector & paramet //df::job *job = NULL; if (Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - ui->main.mode == ui_sidebar_mode::QueryBuilding) + plotinfo->main.mode == ui_sidebar_mode::QueryBuilding) { workshop = world->selected_building; //job = Gui::getSelectedWorkshopJob(out, true); diff --git a/plugins/zone.cpp b/plugins/zone.cpp index a3a0f2431..878014bb0 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -24,7 +24,7 @@ #include "df/building_chainst.h" #include "df/building_civzonest.h" #include "df/general_ref_building_civzone_assignedst.h" -#include "df/ui.h" +#include "df/plotinfost.h" #include "df/unit.h" #include "df/unit_relationship_type.h" #include "df/viewscreen_dwarfmodest.h" @@ -56,7 +56,7 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(cursor); REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(ui); +REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(ui_building_item_cursor); REQUIRE_GLOBAL(ui_building_assign_type); REQUIRE_GLOBAL(ui_building_assign_is_marked); @@ -1565,7 +1565,7 @@ static command_result df_zone(color_ostream &out, vector & parameters) if(!race_filter_set && (building_assign || cagezone_assign || unit_slaughter)) { - string own_race_name = Units::getRaceNameById(ui->race_id); + string own_race_name = Units::getRaceNameById(plotinfo->race_id); out.color(COLOR_BROWN); out << "Default filter for " << parameters[0] << ": 'not (race " << own_race_name << " or own civilization)'; use 'race " @@ -2128,14 +2128,14 @@ struct zone_hook : public df::viewscreen_dwarfmodest DEFINE_VMETHOD_INTERPOSE(void, render, ()) { - if ( ( (ui->main.mode == ui_sidebar_mode::ZonesPenInfo || ui->main.mode == ui_sidebar_mode::ZonesPitInfo) && + if ( ( (plotinfo->main.mode == ui_sidebar_mode::ZonesPenInfo || plotinfo->main.mode == ui_sidebar_mode::ZonesPitInfo) && ui_building_assign_type && ui_building_assign_units && ui_building_assign_is_marked && ui_building_assign_items && ui_building_assign_type->size() == ui_building_assign_units->size() && ui_building_item_cursor) // allow mode QueryBuilding, but only for cages (bedrooms will crash DF with this code, chains don't work either etc) || - ( ui->main.mode == ui_sidebar_mode::QueryBuilding && + ( plotinfo->main.mode == ui_sidebar_mode::QueryBuilding && ui_building_in_assign && *ui_building_in_assign && ui_building_assign_type && ui_building_assign_units && ui_building_assign_type->size() == ui_building_assign_units->size() && @@ -2146,7 +2146,7 @@ struct zone_hook : public df::viewscreen_dwarfmodest ) { if (vector_get(*ui_building_assign_units, *ui_building_item_cursor)) - filter.initialize(ui->main.mode); + filter.initialize(plotinfo->main.mode); } else { From c4a2bdd08efc2905980dbe6d0c3926ae2335453c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 17:35:33 -0800 Subject: [PATCH 0038/2222] update lua too --- library/lua/gui/dwarfmode.lua | 6 +++--- library/lua/makeown.lua | 4 ++-- plugins/lua/buildingplan.lua | 2 +- plugins/lua/confirm.lua | 2 +- plugins/lua/eventful.lua | 6 +++--- plugins/lua/siege-engine.lua | 2 +- plugins/lua/stockflow.lua | 4 ++-- test/library/gui/dwarfmode.lua | 14 +++++++------- test/library/utils.lua | 4 ++-- test/quickfort/ecosystem.lua | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 7ac29e04e..bb381be24 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -49,7 +49,7 @@ function enterSidebarMode(sidebar_mode, max_esc) while remaining_esc > 0 do local screen = dfhack.gui.getCurViewscreen(true) focus_string = dfhack.gui.getFocusString(screen) - if df.global.ui.main.mode == df.ui_sidebar_mode.Default and + if df.global.plotinfo.main.mode == df.ui_sidebar_mode.Default and focus_string == 'dwarfmode/Default' then if #navkey > 0 then gui.simulateInput(screen, navkey) end if navkey == 'D_DESIGNATE' then @@ -275,7 +275,7 @@ end HOTKEY_KEYS = {} -for i,v in ipairs(df.global.ui.main.hotkeys) do +for i,v in ipairs(df.global.plotinfo.main.hotkeys) do HOTKEY_KEYS['D_HOTKEY'..(i+1)] = v end @@ -433,7 +433,7 @@ function MenuOverlay:init() end if self.sidebar_mode then - self.saved_sidebar_mode = df.global.ui.main.mode + self.saved_sidebar_mode = df.global.plotinfo.main.mode -- what mode should we restore when this window is dismissed? ideally, we'd -- restore the mode that the user has set, but we should fall back to -- restoring the default mode if either of the following conditions are diff --git a/library/lua/makeown.lua b/library/lua/makeown.lua index 2b16db1df..3d646b5fc 100644 --- a/library/lua/makeown.lua +++ b/library/lua/makeown.lua @@ -288,11 +288,11 @@ end function make_own(unit) --tweak makeown unit.flags2.resident = false; unit.flags1.merchant = false; unit.flags1.forest = false; - unit.civ_id = df.global.ui.civ_id + unit.civ_id = df.global.plotinfo.civ_id if unit.profession == df.profession.MERCHANT then unit.profession = df.profession.TRADER end if unit.profession2 == df.profession.MERCHANT then unit.profession2 = df.profession.TRADER end fix_clothing_ownership(unit) - if unit.race == df.global.ui.race_id then + if unit.race == df.global.plotinfo.race_id then make_citizen(unit) end end diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 43122a7da..a01a1d0b1 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -87,7 +87,7 @@ end -- returns a vector of constructed buildings (usually of size 1, but potentially -- more for constructions) function construct_buildings_from_ui_state() - local uibs = df.global.ui_build_selector + local uibs = df.global.buildreq local world = df.global.world local direction = world.selected_direction local _, width, height = dfhack.buildings.getCorrectSize( diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index e36a4fb86..736f13fe1 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -1,6 +1,6 @@ local _ENV = mkmodule('plugins.confirm') -local ui = df.global.ui +local ui = df.global.plotinfo local confs = {} -- Wraps df.interface_key[foo] functionality but fails with invalid keys diff --git a/plugins/lua/eventful.lua b/plugins/lua/eventful.lua index 1e3170c45..4cbcbeef2 100644 --- a/plugins/lua/eventful.lua +++ b/plugins/lua/eventful.lua @@ -54,10 +54,10 @@ local function onPostSidebar(workshop) if shop_id then if _registeredStuff.shopNonNative and _registeredStuff.shopNonNative[shop_id] then if _registeredStuff.shopNonNative[shop_id].all then - --[[for _,button in ipairs(df.global.ui_sidebar_menus.workshop_job.choices_all) do + --[[for _,button in ipairs(df.global.game.workshop_job.choices_all) do button.is_hidden=true end]] - df.global.ui_sidebar_menus.workshop_job.choices_visible:resize(0) + df.global.game.workshop_job.choices_visible:resize(0) else --todo by name end @@ -72,7 +72,7 @@ local function onPostSidebar(workshop) new_button.job_type=df.job_type.CustomReaction --could be used for other stuff too i guess... new_button.reaction_name=reaction_name new_button.is_custom=true - local wjob=df.global.ui_sidebar_menus.workshop_job + local wjob=df.global.game.workshop_job wjob.choices_all:insert("#",new_button) wjob.choices_visible:insert("#",new_button) end diff --git a/plugins/lua/siege-engine.lua b/plugins/lua/siege-engine.lua index 5d8cc9050..25f405615 100644 --- a/plugins/lua/siege-engine.lua +++ b/plugins/lua/siege-engine.lua @@ -146,7 +146,7 @@ function getBaseUnitWeight(unit) return -30 elseif flags1.diplomat or flags1.merchant or flags1.forest then return -5 - elseif flags1.tame and unit.civ_id == df.global.ui.civ_id then + elseif flags1.tame and unit.civ_id == df.global.plotinfo.civ_id then return -1 end end diff --git a/plugins/lua/stockflow.lua b/plugins/lua/stockflow.lua index 91a3284ff..d6c9fc070 100644 --- a/plugins/lua/stockflow.lua +++ b/plugins/lua/stockflow.lua @@ -234,7 +234,7 @@ function material_reactions(reactions, itemtypes, mat_info) end function clothing_reactions(reactions, mat_info, filter) - local resources = df.historical_entity.find(df.global.ui.civ_id).resources + local resources = df.historical_entity.find(df.global.plotinfo.civ_id).resources local itemdefs = df.global.world.raws.itemdefs local job_types = df.job_type resource_reactions(reactions, job_types.MakeArmor, mat_info, resources.armor_type, itemdefs.armor, {permissible = filter}) @@ -408,7 +408,7 @@ function collect_reactions() -- Reactions defined in the raws. -- Not all reactions are allowed to the civilization. -- That includes "Make sharp rock" by default. - local entity = df.historical_entity.find(df.global.ui.civ_id) + local entity = df.historical_entity.find(df.global.plotinfo.civ_id) if not entity then -- No global civilization; arena mode? -- Anyway, skip remaining reactions, since many depend on the civ. diff --git a/test/library/gui/dwarfmode.lua b/test/library/gui/dwarfmode.lua index 1f3ab6a87..960397e02 100644 --- a/test/library/gui/dwarfmode.lua +++ b/test/library/gui/dwarfmode.lua @@ -36,39 +36,39 @@ function test.enterSidebarMode() end) -- verify expected starting state - expect.eq(df.ui_sidebar_mode.Default, df.global.ui.main.mode) + expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) expect.eq('dwarfmode/Default', dfhack.gui.getCurFocus(true)) -- get into the orders screen gui.simulateInput(dfhack.gui.getCurViewscreen(true), 'D_JOBLIST') gui.simulateInput(dfhack.gui.getCurViewscreen(true), 'UNITJOB_MANAGER') - expect.eq(df.ui_sidebar_mode.Default, df.global.ui.main.mode) + expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) expect.eq('jobmanagement/Main', dfhack.gui.getCurFocus(true)) -- get back into default from some deep screen guidm.enterSidebarMode(df.ui_sidebar_mode.Default) - expect.eq(df.ui_sidebar_mode.Default, df.global.ui.main.mode) + expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) expect.eq('dwarfmode/Default', dfhack.gui.getCurFocus(true)) -- move from default to some other mode guidm.enterSidebarMode(df.ui_sidebar_mode.QueryBuilding) - expect.eq(df.ui_sidebar_mode.QueryBuilding, df.global.ui.main.mode) + expect.eq(df.ui_sidebar_mode.QueryBuilding, df.global.plotinfo.main.mode) expect.str_find('^dwarfmode/QueryBuilding', dfhack.gui.getCurFocus(true)) -- move between non-default modes guidm.enterSidebarMode(df.ui_sidebar_mode.LookAround) - expect.eq(df.ui_sidebar_mode.LookAround, df.global.ui.main.mode) + expect.eq(df.ui_sidebar_mode.LookAround, df.global.plotinfo.main.mode) expect.str_find('^dwarfmode/LookAround', dfhack.gui.getCurFocus(true)) -- get back into default from a supported mode guidm.enterSidebarMode(df.ui_sidebar_mode.Default) - expect.eq(df.ui_sidebar_mode.Default, df.global.ui.main.mode) + expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) expect.eq('dwarfmode/Default', dfhack.gui.getCurFocus(true)) -- verify that all supported modes lead where we say they'll go for k,v in pairs(guidm.SIDEBAR_MODE_KEYS) do guidm.enterSidebarMode(k) - expect.eq(k, df.global.ui.main.mode, df.ui_sidebar_mode[k]) + expect.eq(k, df.global.plotinfo.main.mode, df.ui_sidebar_mode[k]) end -- end test back in default so the test harness doesn't have to autocorrect guidm.enterSidebarMode(df.ui_sidebar_mode.Default) diff --git a/test/library/utils.lua b/test/library/utils.lua index 509fab8bc..cf2024618 100644 --- a/test/library/utils.lua +++ b/test/library/utils.lua @@ -94,7 +94,7 @@ function test.df_expr_to_ref() dfhack.with_temp_object(df.new('ptr-vector'), function(vec) fake_unit = vec vec:insert('#', df.global.world) - vec:insert('#', df.global.ui) + vec:insert('#', df.global.plotinfo) expect.eq(utils.df_expr_to_ref('unit'), vec) @@ -102,7 +102,7 @@ function test.df_expr_to_ref() expect.eq(df.reinterpret_cast(df.world, utils.df_expr_to_ref('unit[0]').value), df.global.world) expect.eq(utils.df_expr_to_ref('unit[1]'), utils.df_expr_to_ref('unit.1')) - expect.eq(df.reinterpret_cast(df.ui, utils.df_expr_to_ref('unit[1]').value), df.global.ui) + expect.eq(df.reinterpret_cast(df.ui, utils.df_expr_to_ref('unit[1]').value), df.global.plotinfo) expect.error_match('index out of bounds', function() utils.df_expr_to_ref('unit.2') end) expect.error_match('index out of bounds', function() utils.df_expr_to_ref('unit[2]') end) diff --git a/test/quickfort/ecosystem.lua b/test/quickfort/ecosystem.lua index 27dddc5d5..3a4c014e4 100644 --- a/test/quickfort/ecosystem.lua +++ b/test/quickfort/ecosystem.lua @@ -397,7 +397,7 @@ function extra_fns.gui_quantum(pos) local vehicles = assign_minecarts.get_free_vehicles() local confirm_state = confirm.isEnabled() local confirm_conf = confirm.get_conf_data() - local routes = df.global.ui.hauling.routes + local routes = df.global.plotinfo.hauling.routes local num_routes = #routes local next_order_id = df.global.world.manager_order_next_id From 845391f4e65d5231397c8451a3eed4ff95634624 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 17:46:30 -0800 Subject: [PATCH 0039/2222] fix overzealous "ui" -> "plotinfo" replacement some comments were just talking about the UI.. --- library/include/RemoteClient.h | 2 +- library/modules/Buildings.cpp | 2 +- library/modules/Screen.cpp | 2 +- plugins/stockpiles/StockpileSerializer.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/include/RemoteClient.h b/library/include/RemoteClient.h index 1948ea754..e71b985cd 100644 --- a/library/include/RemoteClient.h +++ b/library/include/RemoteClient.h @@ -46,7 +46,7 @@ namespace DFHack CR_NOT_IMPLEMENTED = -1, // Command not implemented, or plugin not loaded CR_OK = 0, // Success CR_FAILURE = 1, // Failure - CR_WRONG_USAGE = 2, // Wrong arguments or plotinfo state + CR_WRONG_USAGE = 2, // Wrong arguments or ui state CR_NOT_FOUND = 3 // Target object not found (for RPC mainly) }; diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index cd0924a36..21d63862c 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1219,7 +1219,7 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorjob_items, 0, items[i]); if (items[i]->item_type == item_type::BOULDER) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index ab7031308..e7f6cea93 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -83,7 +83,7 @@ namespace DFHack { * Screen painting API. */ -// returns plotinfo grid coordinates, even if the game map is scaled differently +// returns ui grid coordinates, even if the game map is scaled differently df::coord2d Screen::getMousePos() { if (!gps) diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 478b403f2..ff0a38241 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -105,7 +105,7 @@ private: */ void write(); - // parse serialized data into plotinfo indices + // parse serialized data into ui indices void read (); /** From 3969262e44ed741ab4a29acc0623db91ff494dc3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 17:50:49 -0800 Subject: [PATCH 0040/2222] update submodule refs --- library/xml | 2 +- plugins/stonesense | 2 +- scripts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index 1b1ffd406..a172530f9 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 1b1ffd4069652bf189ac0bc229ddf027009c110a +Subproject commit a172530f98612b787ad59d58452bddf83d2bd7e8 diff --git a/plugins/stonesense b/plugins/stonesense index 25e7452f0..7b44c9dd0 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 25e7452f01b49b0a479d2c77a922cabbb7fd1e4c +Subproject commit 7b44c9dd0e0316c3edb66393de08591c95264e1a diff --git a/scripts b/scripts index 5ec3a484e..cea267cec 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5ec3a484e243954151cf21fd288311667bf15b61 +Subproject commit cea267cec7819c03bd097485c29a24d77af29bc3 From 1934014659816b0b7704799638f0d7afeccbdfe6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 13:36:46 -0800 Subject: [PATCH 0041/2222] framework for passthru, z-order swapping windows --- library/LuaApi.cpp | 12 ++++++ library/LuaTools.cpp | 22 +++++++++-- library/include/modules/Screen.h | 1 + library/lua/gui.lua | 66 +++++++++++++++++++++++++++++++- library/lua/gui/widgets.lua | 5 +-- library/modules/Screen.cpp | 4 ++ 6 files changed, 102 insertions(+), 8 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 2f1d612b2..303a319c9 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2456,6 +2456,17 @@ static int screen_findGraphicsTile(lua_State *L) } } +static int screen_raise(lua_State *L) { + df::viewscreen *screen = dfhack_lua_viewscreen::get_pointer(L, 1, false); + + // remove screen from the stack so it doesn't get returned as an output + lua_remove(L, 1); + + Screen::raise(screen); + + return 0; +} + static int screen_hideGuard(lua_State *L) { df::viewscreen *screen = dfhack_lua_viewscreen::get_pointer(L, 1, false); luaL_checktype(L, 2, LUA_TFUNCTION); @@ -2574,6 +2585,7 @@ static const luaL_Reg dfhack_screen_funcs[] = { { "paintString", screen_paintString }, { "fillRect", screen_fillRect }, { "findGraphicsTile", screen_findGraphicsTile }, + CWRAP(raise, screen_raise), CWRAP(hideGuard, screen_hideGuard), CWRAP(show, screen_show), CWRAP(dismiss, screen_dismiss), diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 084e5a21b..c76c65651 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -131,6 +131,9 @@ void DFHack::Lua::GetVector(lua_State *state, std::vector &pvec) } } +static bool trigger_inhibit_l_down = false; +static bool trigger_inhibit_r_down = false; +static bool trigger_inhibit_m_down = false; static bool inhibit_l_down = false; static bool inhibit_r_down = false; static bool inhibit_m_down = false; @@ -161,17 +164,17 @@ void DFHack::Lua::PushInterfaceKeys(lua_State *L, if (!inhibit_l_down && df::global::enabler->mouse_lbut_down) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_L_DOWN"); - inhibit_l_down = true; + trigger_inhibit_l_down = true; } if (!inhibit_r_down && df::global::enabler->mouse_rbut_down) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_R_DOWN"); - inhibit_r_down = true; + trigger_inhibit_r_down = true; } if (!inhibit_m_down && df::global::enabler->mouse_mbut_down) { lua_pushboolean(L, true); lua_setfield(L, -2, "_MOUSE_M_DOWN"); - inhibit_m_down = true; + trigger_inhibit_m_down = true; } if (df::global::enabler->mouse_lbut) { lua_pushboolean(L, true); @@ -2148,6 +2151,19 @@ void DFHack::Lua::Core::Reset(color_ostream &out, const char *where) lua_settop(State, 0); } + if (trigger_inhibit_l_down) { + trigger_inhibit_l_down = false; + inhibit_l_down = true; + } + if (trigger_inhibit_r_down) { + trigger_inhibit_r_down = false; + inhibit_r_down = true; + } + if (trigger_inhibit_m_down) { + trigger_inhibit_m_down = false; + inhibit_m_down = true; + } + if (!df::global::enabler->mouse_lbut) inhibit_l_down = false; if (!df::global::enabler->mouse_rbut) diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 347df1011..bc8a406a0 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -222,6 +222,7 @@ namespace DFHack DFHACK_EXPORT void dismiss(df::viewscreen *screen, bool to_first = false); DFHACK_EXPORT bool isDismissed(df::viewscreen *screen); DFHACK_EXPORT bool hasActiveScreens(Plugin *p); + DFHACK_EXPORT void raise(df::viewscreen *screen); /// Retrieve the string representation of the bound key. DFHACK_EXPORT std::string getKeyDisplay(df::interface_key key); diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 1742ee342..27484541f 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -484,6 +484,10 @@ function View:getMousePos(view_rect) end end +function View:getMouseFramePos() + return self:getMousePos(ViewRect{rect=self.frame_rect}) +end + function View:computeFrame(parent_rect) return mkdims_wh(0,0,parent_rect.width,parent_rect.height) end @@ -681,9 +685,67 @@ function Screen:onRender() self:render(Painter.new()) end ------------------------- +----------------------------- +-- Z-order swapping screen -- +----------------------------- + +ZScreen = defclass(ZScreen, Screen) + +function ZScreen:onIdle() + if self._native and self._native.parent then + self._native.parent:logic() + end +end + +function ZScreen:render(dc) + self:renderParent() + ZScreen.super.render(self, dc) +end + +local function zscreen_is_top(self) + return dfhack.gui.getCurViewscreen(true) == self._native +end + +function ZScreen:onInput(keys) + if not zscreen_is_top(self) then + if keys._MOUSE_L_DOWN and self:isMouseOver() then + self:raise() + else + self:sendInputToParent(keys) + return + end + end + + if ZScreen.super.onInput(self, keys) then + return + end + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + self:dismiss() + return + end + + if not keys._MOUSE_L or not self:isMouseOver() then + self:sendInputToParent(keys) + end +end + +-- move this viewscreen to the top of the stack (if it's not there already) +function ZScreen:raise() + if self:isDismissed() or zscreen_is_top(self) then + return + end + dscreen.raise(self) +end + +-- subclasses should override this and return whether the mouse is over an +-- owned screen element +function ZScreen:isMouseOver() + return false +end + +-------------------------- -- Framed screen object -- ------------------------- +-------------------------- -- Plain grey-colored frame. GREY_FRAME = { diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index c3d89c49b..e95410fb2 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -87,7 +87,7 @@ Panel.ATTRS { function Panel:init(args) if not self.drag_anchors then - self.drag_anchors = {title=true, frame=false, body=false} + self.drag_anchors = {title=true, frame=false, body=true} end if not self.resize_anchors then self.resize_anchors = {t=false, l=true, r=true, b=true} @@ -303,8 +303,7 @@ function Panel:onInput(keys) return true end if not keys._MOUSE_L_DOWN then return end - local rect = self.frame_rect - local x,y = self:getMousePos(gui.ViewRect{rect=rect}) + local x,y = self:getMouseFramePos() if not x then return end if self.resizable and y == 0 then diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index e7f6cea93..65817aa93 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -502,6 +502,10 @@ bool Screen::hasActiveScreens(Plugin *plugin) return false; } +void Screen::raise(df::viewscreen *screen) { + Hide swapper(screen, Screen::Hide::RESTORE_AT_TOP); +} + namespace DFHack { namespace Screen { Hide::Hide(df::viewscreen* screen, int flags) : From 9eb5165ebed2964fc8f552ce4c740e93e8ab5e04 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 6 Jan 2023 02:11:27 +0000 Subject: [PATCH 0042/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index a172530f9..522b9ea01 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a172530f98612b787ad59d58452bddf83d2bd7e8 +Subproject commit 522b9ea01ac32206eaa6bfc43fed0494b84e5541 From f4d059ea3d5c1d079b599b4bbb0b07f82c02de51 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 6 Jan 2023 02:43:42 +0000 Subject: [PATCH 0043/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 522b9ea01..7714dd9cb 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 522b9ea01ac32206eaa6bfc43fed0494b84e5541 +Subproject commit 7714dd9cbe94c270f0de1ab09c797ad4ecdcaa0e diff --git a/scripts b/scripts index cea267cec..164bea616 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit cea267cec7819c03bd097485c29a24d77af29bc3 +Subproject commit 164bea61674ab786dce8ac774cb9ae9efaa1e5aa From 1540e7c2945ebba1f183988d9ac3e91631dac249 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 6 Jan 2023 07:13:54 +0000 Subject: [PATCH 0044/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 7714dd9cb..d026f34ed 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7714dd9cbe94c270f0de1ab09c797ad4ecdcaa0e +Subproject commit d026f34ed1f7ab79aebb1c5bc8a36ee9b30bd13d diff --git a/scripts b/scripts index 164bea616..55488a935 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 164bea61674ab786dce8ac774cb9ae9efaa1e5aa +Subproject commit 55488a9350190858efa2f872cda2d627ae2d0cd2 From d7495bfdf8908787226cc381df501f6d260f43a3 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Fri, 6 Jan 2023 21:14:09 +1100 Subject: [PATCH 0045/2222] update the install documentation to mention VS2022 properly --- docs/dev/building/Compile.rst | 18 ++--- docs/dev/building/Dependencies.rst | 126 ++++++++--------------------- 2 files changed, 38 insertions(+), 106 deletions(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index 80e88f708..b2f7fd7ed 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -160,10 +160,10 @@ Then you can either open the solution with MSVC or use one of the msbuild script Visual Studio IDE ----------------- -After running the CMake generate script you will have a new folder called VC2015 -or VC2015_32, depending on the architecture you specified. Open the file +After running the CMake generate script you will have a new folder called VC2022 +or VC2022_32, depending on the architecture you specified. Open the file ``dfhack.sln`` inside that folder. If you have multiple versions of Visual -Studio installed, make sure you open with Visual Studio 2015. +Studio installed, make sure you open with Visual Studio 2022. The first thing you must then do is change the build type. It defaults to Debug, but this cannot be used on Windows. Debug is not binary-compatible with DF. @@ -183,15 +183,9 @@ In the build directory you will find several ``.bat`` files: * Scripts with ``package`` prefix will build and create a .zip package of DFHack. Compiling from the command line is generally the quickest and easiest option. -However be aware that due to the limitations of ``cmd.exe`` - especially in -versions of Windows prior to Windows 10 - it can be very hard to see what happens -during a build. If you get a failure, you may miss important errors or warnings -due to the tiny window size and extremely limited scrollback. For that reason you -may prefer to compile in the IDE which will always show all build output. - -Alternatively (or additionally), consider installing an improved Windows terminal -such as `Cmder `_. Easily installed through Chocolatey with: -``choco install cmder -y``. +Modern Windows terminal emulators such as `Cmder `_ or +`Windows Terminal `_ provide a better +experience by providing more scrollback and larger window sizes. **Note for Cygwin/msysgit users**: It is also possible to compile DFHack from a Bash command line. This has three potential benefits: diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/building/Dependencies.rst index e3cc08fbf..da8728b9b 100644 --- a/docs/dev/building/Dependencies.rst +++ b/docs/dev/building/Dependencies.rst @@ -156,31 +156,8 @@ older GCC (but still at least 4.8) version if possible. Windows ======= -For ABI compatibility with recent releases of Dwarf Fortress, DFHack requires the ``v140`` or ``v140_xp`` -toolchain to build for windows. - -Of course all dependencies are listed above, but here are some things you'll likely want on Windows -to avoid risk of wading into uncharted waters: - -* Microsoft Visual C++ 2022, 2019, 2017, or 2015 (optional) -* ``v140`` or ``v140_xp`` toolchain (Microsoft Visual C++ 2015 Build Tools) -* StrawberryPerl (perl + perl packages) - -Releases of Dwarf Fortress since roughly 2016 have been compiled for Windows using -Microsoft's Visual Studio 2015 C++ compiler. In order to guarantee ABI and STL compatibility -with Dwarf Fortress, DFHack has to be compiled with the same compiler. - -Visual Studio 2015 is no longer supported by Microsoft and it can be difficult to obtain -working installers for this product today. The recommended approach is to use a modern community -version of Visual Studio such as 2022_ or 2019_, installing additional optional Visual Studio -components which provide the required support for using Visual Studio 2015's toolchain. -All of the required tools are available from Microsoft as part of Visual Studio's Community -Edition at no charge. - -You can also download just the Visual C++ 2015 `build tools`_ if you aren't going to use -Visual Studio to edit code. - -.. _build tools: https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downloads +DFHack must be built with the Microsoft Visual C++ 2022 toolchain (aka MSVC v143) +for ABI compatibility with Dwarf Fortress v50. With Choco ~~~~~~~~~~ @@ -194,34 +171,19 @@ Here are some package install commands:: choco install strawberryperl choco install python choco install sphinx - choco install visualstudio2022community - -You may have noticed this list **does not include** the build tools, one of the build tool packages -in the chocolatey `package repository `_ may work for our purposes -but the tried and true method is just below in the **next section**. If you verify a package works feel free -to open an issue, or update this documentation. - -.. _chocolatey: https://chocolatey.org/install -Visual Studio -~~~~~~~~~~~~~ -You could install visual studio `manually`, perhaps even the build tools as well. -You could also just run a ``choco`` command. For example:: - - choco install visualstudio2022community - -If Visual Studio is installed follow these next steps for the build tools: + # Visual Studio + choco install visualstudio2022community --params "--add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended" + # OR + # Build Tools for Visual Studio + choco install visualstudio2022buildtools --params "--add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended" -1. Open **Visual Studio Installer**. -2. Select modify, for whichever version you've chosen to utilize. -3. Check the boxes for the following components: +If you already have Visual Studio 2022 or the Build Tools installed, you may +need to modify the installed version to include the workload components +listed in the manual installation section, as chocolatey will not amend +the existing install. -* "Desktop Development with C++" -* "C++ Windows XP Support for VS 2017 (v141) tools [Deprecated]" -* "MSVC v140 - VS 2015 C++ build tools (v14.00)" - -Yes, this is unintuitive. Installing XP Support for VS 2017 installs XP Support for VS 2015 -if the 2015 toolchain is installed. +.. _chocolatey: https://chocolatey.org/install Manually ~~~~~~~~ @@ -280,55 +242,31 @@ See the `Sphinx`_ website. Visual Studio ^^^^^^^^^^^^^ -Click Visual Studio 2022_ or 2019_ to download an installer wizard that will prompt you -to select the optional tools you want to download alongside the IDE. You may need to log into -(or create) a Microsoft account in order to download Visual Studio. +The required toolchain can be installed as a part of either the `Visual Studio 2022 IDE`_ +or the `Build Tools for Visual Studio 2022`_. If you already have a preferred code +editor, the Build Tools will be a smaller install. You may need to log into (or create) +a Microsoft account in order to download Visual Studio. + +.. _Visual Studio 2022 IDE: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false +.. _Build Tools for Visual Studio 2022: https://my.visualstudio.com/Downloads?q=Build%20Tools%20for%20Visual%20Studio%202022 -.. _2022: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false -.. _2019: https://my.visualstudio.com/Downloads?q=visual%20studio%202019&wt.mc_id=o~msft~vscom~older-downloads Build Tools [Without Visual Studio] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Click `build tools`_ and you will be prompted to login to your Microsoft account. -Then you should be redirected to a page with various download options with 2015 +Click `Build Tools for Visual Studio 2022`_ and you will be prompted to login to your Microsoft account. +Then you should be redirected to a page with various download options with 2022 in their name. If this redirect doesn't occur, just copy, paste, and enter the -download link again and you should see the options. You need to get: - -Visual C++ Build Tools for Visual Studio 2015 with Update 3. -Click the download button next to it and a dropdown of download formats will appear. -Select the DVD format to download an ISO file. When the download is complete, -click on the ISO file and a folder will popup with the following contents: - -* packages (folder) -* VCPlusPlusBuildTools2015Update3_x64_Files.cat -* VisualCppBuildTools_Full.exe - -The packages folder contains the dependencies that are required by the build tools. -These include: - -* Microsoft .NET Framework 4.6.1 Developer Pack -* Microsoft Visual C++ 2015 Redistributable (x64) - 14.0.24210 -* Windows 10 Universal SDK - 10.0.10240 -* Windows 8.1 SDK - -Click VisualCppBuildTools_Full.exe and use the default options provided by the installer -wizard that appears. After the installation is completed, add the path where MSBuild.exe -was installed to your PATH environment variable. The path should be: - -* ``C:\Program Files (x86)\MSBuild\14.0\Bin`` - -Note that this process may install only the ``v140`` toolchain, not the ``v140_xp`` toolchain that -is normally used to compile build releases of DFHack. Due to a bug in the Microsoft-provided libraries used with -the ``v140_xp`` toolchain that Microsoft has never fixed, DFHack (and probably also Dwarf Fortress itself) -doesn't run reliably on 64-bit XP. Investigations have so far suggested that ``v140`` and -``v140_xp`` are ABI-compatible. As such, there should be no harm in using ``v140`` instead of -``v140_xp`` as the build toolchain, at least on 64-bit platforms. However, it is our policy to use -``v140_xp`` for release builds for both 32-bit and 64-bit Windows, -since 32-bit releases of Dwarf Fortress work on XP and ``v140_xp`` is required for compatibility with -XP. - -The ``v141`` toolchain, in Visual Studio 2017, has been empirically documented to be incompatible with -released versions of Dwarf Fortress and cannot be used to make usable builds of DFHack. +download link again and you should see the options. + +You want to select the most up-to-date version -- as of writing this is +"Build Tools for Visual Studio 2022 (version 17.4)". "LTSC" is an extended +support variant and is not required for our purposes. + +When installing, select the "Desktop Development with C++" workload and ensure that the following are checked: + +- MSVC v143 - VS 2022 C++ x64/x86 build tools +- C++ CMake tools for Windows +- At least one Windows SDK (for example, Windows 11 SDK 10.0.22621). .. _mac-dependency-instructions: From 79206c92a759bb3a68098bb48ef9b1c0977d160d Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Fri, 6 Jan 2023 21:15:18 +1100 Subject: [PATCH 0046/2222] modern windows terminal + powershell is, (un?)fortunately superior to cygwin's, so cut this for simplification --- docs/dev/building/Compile.rst | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index b2f7fd7ed..3b54b38f0 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -187,29 +187,6 @@ Modern Windows terminal emulators such as `Cmder `_ or `Windows Terminal `_ provide a better experience by providing more scrollback and larger window sizes. -**Note for Cygwin/msysgit users**: It is also possible to compile DFHack from a -Bash command line. This has three potential benefits: - -* When you've installed Git and are using its Bash, but haven't added Git to your path: - - * You can load Git's Bash and as long as it can access Perl and CMake, you can - use it for compile without adding Git to your system path. - -* When you've installed Cygwin and its SSH server: - - * You can now SSH in to your Windows install and compile from a remote terminal; - very useful if your Windows installation is a local VM on a \*nix host OS. - -* In general: you can use Bash as your compilation terminal, meaning you have a decent - sized window, scrollback, etc. - - * Whether you're accessing it locally as with Git's Bash, or remotely through - Cygwin's SSH server, this is far superior to using ``cmd.exe``. - -You don't need to do anything special to compile from Bash. As long as your PATHs -are set up correctly, you can run the same generate- and build/install/package- bat -files as detailed above. - .. _compile-macos: macOS From 42d572cd03ea11a059f5eb042f05ab06d58898dd Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 08:31:54 -0600 Subject: [PATCH 0047/2222] set-df-path: accept path on command line --- build/win32/set_df_path.vbs | 15 +++++++++++---- build/win64/set_df_path.vbs | 15 +++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/build/win32/set_df_path.vbs b/build/win32/set_df_path.vbs index 219d150da..0da7203b5 100644 --- a/build/win32/set_df_path.vbs +++ b/build/win32/set_df_path.vbs @@ -2,11 +2,18 @@ Option Explicit Const BIF_returnonlyfsdirs = &H0001 -Dim wsh, objDlg, objF, fso, spoFile -Set objDlg = WScript.CreateObject("Shell.Application") -Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs) +Dim wsh, objDlg, objF, fso, spoFile, args Set fso = CreateObject("Scripting.FileSystemObject") + +set args = Wscript.Arguments +if args.count > 0 Then + Set ObjF = fso.GetFolder(args.Item(0)) +else + Set objDlg = WScript.CreateObject("Shell.Application") + Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs).Self +end if + If fso.FileExists("DF_PATH.txt") Then fso.DeleteFile "DF_PATH.txt", True End If @@ -14,7 +21,7 @@ End If If IsValue(objF) Then If InStr(1, TypeName(objF), "Folder") > 0 Then Set spoFile = fso.CreateTextFile("DF_PATH.txt", True) - spoFile.WriteLine(objF.Self.Path) + spoFile.WriteLine(objF.Path) End If End If diff --git a/build/win64/set_df_path.vbs b/build/win64/set_df_path.vbs index 219d150da..0da7203b5 100644 --- a/build/win64/set_df_path.vbs +++ b/build/win64/set_df_path.vbs @@ -2,11 +2,18 @@ Option Explicit Const BIF_returnonlyfsdirs = &H0001 -Dim wsh, objDlg, objF, fso, spoFile -Set objDlg = WScript.CreateObject("Shell.Application") -Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs) +Dim wsh, objDlg, objF, fso, spoFile, args Set fso = CreateObject("Scripting.FileSystemObject") + +set args = Wscript.Arguments +if args.count > 0 Then + Set ObjF = fso.GetFolder(args.Item(0)) +else + Set objDlg = WScript.CreateObject("Shell.Application") + Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs).Self +end if + If fso.FileExists("DF_PATH.txt") Then fso.DeleteFile "DF_PATH.txt", True End If @@ -14,7 +21,7 @@ End If If IsValue(objF) Then If InStr(1, TypeName(objF), "Folder") > 0 Then Set spoFile = fso.CreateTextFile("DF_PATH.txt", True) - spoFile.WriteLine(objF.Self.Path) + spoFile.WriteLine(objF.Path) End If End If From 9de09ac7799925c9db6ed128efbc3f19223aa75d Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 08:50:10 -0600 Subject: [PATCH 0048/2222] bump version to 50.05 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b518f809..dcb5b42ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,7 +195,7 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.04") +set(DF_VERSION "50.05") set(DFHACK_RELEASE "alpha0") set(DFHACK_PRERELEASE TRUE) From 83e5bc120a2446984f97c0d57c89a442a22368c4 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 12:01:01 -0600 Subject: [PATCH 0049/2222] set_df_path: handle cancel correctly --- build/win32/set_df_path.vbs | 5 ++++- build/win64/set_df_path.vbs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/build/win32/set_df_path.vbs b/build/win32/set_df_path.vbs index 0da7203b5..85aeba2ce 100644 --- a/build/win32/set_df_path.vbs +++ b/build/win32/set_df_path.vbs @@ -11,7 +11,10 @@ if args.count > 0 Then Set ObjF = fso.GetFolder(args.Item(0)) else Set objDlg = WScript.CreateObject("Shell.Application") - Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs).Self + Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs) + if IsValue(objF) Then + set ObjF = objF.self + end if end if If fso.FileExists("DF_PATH.txt") Then diff --git a/build/win64/set_df_path.vbs b/build/win64/set_df_path.vbs index 0da7203b5..85aeba2ce 100644 --- a/build/win64/set_df_path.vbs +++ b/build/win64/set_df_path.vbs @@ -11,7 +11,10 @@ if args.count > 0 Then Set ObjF = fso.GetFolder(args.Item(0)) else Set objDlg = WScript.CreateObject("Shell.Application") - Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs).Self + Set objF = objDlg.BrowseForFolder (&H0,"Select your DF folder", BIF_returnonlyfsdirs) + if IsValue(objF) Then + set ObjF = objF.self + end if end if If fso.FileExists("DF_PATH.txt") Then From 779e159512a6506c2c07c43a893b206db4e33b6a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 13:56:19 -0600 Subject: [PATCH 0050/2222] enable reveal `reveal demon` is disabled due to double popup bug `reveal` also has guidance text that appears if the player is in graphics mode explaining the limitations of the new renderer --- plugins/CMakeLists.txt | 2 +- plugins/reveal.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index ac3f0f95a..319600439 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -151,7 +151,7 @@ dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #add_subdirectory(remotefortressreader) #dfhack_plugin(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) #add_subdirectory(rendermax) -#dfhack_plugin(reveal reveal.cpp LINK_LIBRARIES lua) +dfhack_plugin(reveal reveal.cpp LINK_LIBRARIES lua) #dfhack_plugin(search search.cpp) #dfhack_plugin(seedwatch seedwatch.cpp) #dfhack_plugin(showmood showmood.cpp) diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 8d76bd54b..e5ed7aa4e 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -12,6 +12,7 @@ #include "modules/World.h" #include "modules/MapCache.h" #include "modules/Gui.h" +#include "modules/Screen.h" #include "df/block_square_event_frozen_liquidst.h" #include "df/construction.h" @@ -184,16 +185,18 @@ command_result reveal(color_ostream &out, vector & params) else if(params[i] == "help" || params[i] == "?") return CR_WRONG_USAGE; } + auto& con = out; if(params.size() && params[0] == "hell") { no_hell = false; } if(params.size() && params[0] == "demon") { - no_hell = false; - pause = false; + con.printerr("`reveal demon` is currently disabled to prevent a hang due to a bug in the base game\n"); + return CR_FAILURE; + //no_hell = false; + //pause = false; } - auto & con = out; if(revealed != NOT_REVEALED) { con.printerr("Map is already revealed or this is a different map.\n"); @@ -256,7 +259,14 @@ command_result reveal(color_ostream &out, vector & params) revealed = DEMON_REVEALED; } is_active = nopause_state || (revealed == REVEALED); + bool graphics_mode = Screen::inGraphicsMode(); con.print("Map revealed.\n"); + if (graphics_mode) { + con.print("Note that in graphics mode, tiles that are not adjacent to open\n" + "space will not render but can still be examined by hovering over\n" + "them with the mouse. Switching to text mode (in the game settings)\n" + "will allow the display the revealed tiles.\n"); + } if(!no_hell) con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n"); con.print("Run 'unreveal' to revert to previous state.\n"); From 6f46ae672ec64f33b9d4796a42b6cc7c615bc4dc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 12:17:36 -0800 Subject: [PATCH 0051/2222] add Gui::getDFViewscreen and Lua binding --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 5 +++++ library/LuaApi.cpp | 1 + library/include/modules/Gui.h | 3 +++ library/modules/Gui.cpp | 21 +++++++++++++++++---- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bd07b58ac..bfc01b322 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,12 +48,14 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::getDwarfmodeDims``: now only returns map viewport dimensions; menu dimensions are obsolete +- ``Gui::getDFViewscreen``: returns the topmost underlying DF viewscreen ## Lua - ``gui.View``: ``visible`` and ``active`` can now be functions that return a boolean - ``widgets.Panel``: new attributes to control window dragging and resizing with mouse or keyboard - ``widgets.Window``: Panel subclass with attributes preset for top-level windows - `overlay`: ``OverlayWidget`` now inherits from ``Panel`` instead of ``Widget`` to get all the frame and mouse integration goodies +- ``dfhack.gui.getDFViewscreen()``: returns the topmost underlying DF viewscreen ## Internals diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8ec2b24ac..8962d795a 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -965,6 +965,11 @@ Screens the specified type (e.g. ``df.viewscreen_titlest``), or ``nil`` if none match. If ``depth`` is not specified or is less than 1, all viewscreens are checked. +* ``dfhack.gui.getDFViewscreen([skip_dismissed])`` + + Returns the topmost viewscreen not owned by DFHack. If ``skip_dismissed`` is + ``true``, ignores screens already marked to be removed. + General-purpose selections ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 303a319c9..fdd19783a 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1454,6 +1454,7 @@ static int gui_getMousePos(lua_State *L) static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getCurViewscreen), + WRAPM(Gui, getDFViewscreen), WRAPM(Gui, getFocusString), WRAPM(Gui, getCurFocus), WRAPM(Gui, getSelectedWorkshopJob), diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 33ea32939..25415ffa9 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -181,6 +181,9 @@ namespace DFHack DFHACK_EXPORT df::viewscreen *getViewscreenByIdentity(virtual_identity &id, int n = 1); + /// Get the top-most underlying DF viewscreen (not owned by DFHack) + DFHACK_EXPORT df::viewscreen *getDFViewscreen(bool skip_dismissed = false); + /// Get the top-most viewscreen of the given type from the top `n` viewscreens (or all viewscreens if n < 1) /// returns NULL if none match template diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index f59cdcb8b..bf2b2e745 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -1873,6 +1873,12 @@ bool Gui::autoDFAnnouncement(df::announcement_type type, df::coord pos, std::str return autoDFAnnouncement(r, message); } +static df::viewscreen * do_skip_dismissed(df::viewscreen * ws) { + while (ws && Screen::isDismissed(ws) && ws->parent) + ws = ws->parent; + return ws; +} + df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed) { if (!gview) @@ -1883,10 +1889,7 @@ df::viewscreen *Gui::getCurViewscreen(bool skip_dismissed) ws = ws->child; if (skip_dismissed) - { - while (ws && Screen::isDismissed(ws) && ws->parent) - ws = ws->parent; - } + ws = do_skip_dismissed(ws); return ws; } @@ -1906,6 +1909,16 @@ df::viewscreen *Gui::getViewscreenByIdentity (virtual_identity &id, int n) return NULL; } +df::viewscreen *Gui::getDFViewscreen(bool skip_dismissed) { + df::viewscreen *screen = Gui::getCurViewscreen(skip_dismissed); + while (screen && dfhack_viewscreen::is_instance(screen)) { + screen = screen->parent; + if (skip_dismissed) + screen = do_skip_dismissed(screen); + } + return screen; +} + df::coord Gui::getViewportPos() { if (!df::global::window_x || !df::global::window_y || !df::global::window_z) From 5e9ddd9a366bfb6b70a7bc6ba4725b5572408efd Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 14:34:59 -0600 Subject: [PATCH 0052/2222] reveal: vertical whitespaec --- plugins/reveal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index e5ed7aa4e..4a05f061f 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -260,15 +260,15 @@ command_result reveal(color_ostream &out, vector & params) } is_active = nopause_state || (revealed == REVEALED); bool graphics_mode = Screen::inGraphicsMode(); - con.print("Map revealed.\n"); + con.print("Map revealed.\n\n"); if (graphics_mode) { con.print("Note that in graphics mode, tiles that are not adjacent to open\n" "space will not render but can still be examined by hovering over\n" "them with the mouse. Switching to text mode (in the game settings)\n" - "will allow the display the revealed tiles.\n"); + "will allow the display the revealed tiles.\n\n"); } if(!no_hell) - con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n"); + con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n\n"); con.print("Run 'unreveal' to revert to previous state.\n"); return CR_OK; } From 82644157200f3f2d368bd40a1c919a17c7d44707 Mon Sep 17 00:00:00 2001 From: Myk Date: Fri, 6 Jan 2023 15:11:10 -0800 Subject: [PATCH 0053/2222] Update docs/dev/building/Compile.rst --- docs/dev/building/Compile.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/building/Compile.rst b/docs/dev/building/Compile.rst index 3b54b38f0..378ab6454 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/building/Compile.rst @@ -315,7 +315,7 @@ run Dwarf Fortress from the container and have it display on your host. Step 2: build DFHack -------------------- -The ``docker run`` command above will give you a shell prompt (as the `buildmaster` user) in the +The ``docker run`` command above will give you a shell prompt (as the ``buildmaster`` user) in the container. Inside the container, run the following commands:: git clone https://github.com/DFHack/dfhack.git From 1a8f60c03b171f3be5c00d4391c3f831a62a454b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 15:31:50 -0800 Subject: [PATCH 0054/2222] implement remaining review comments from #2517 --- .gitignore | 1 - docs/dev/{building => compile}/Compile.rst | 14 ++++----- .../{building => compile}/Dependencies.rst | 0 docs/dev/{building => compile}/Options.rst | 29 ++++++++++--------- docs/dev/{building => compile}/index.rst | 6 ++-- docs/dev/index.rst | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) rename docs/dev/{building => compile}/Compile.rst (97%) rename docs/dev/{building => compile}/Dependencies.rst (100%) rename docs/dev/{building => compile}/Options.rst (90%) rename docs/dev/{building => compile}/index.rst (69%) diff --git a/.gitignore b/.gitignore index a421d40ac..9b78fa31f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ # any build folders build*/ -!docs/dev/building/ nix buntu build/VC2010 diff --git a/docs/dev/building/Compile.rst b/docs/dev/compile/Compile.rst similarity index 97% rename from docs/dev/building/Compile.rst rename to docs/dev/compile/Compile.rst index 378ab6454..032eb7524 100644 --- a/docs/dev/building/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -89,8 +89,8 @@ assistance. All Platforms ============= Before you can compile the code you'll need to configure your build with cmake. Some IDEs can do this, -but from command line is the usual way to do this; thought the windows section below points out some -windows batch files that can be used to avoid opening a terminal/command-prompt. +but from command line is the usual way to do this; thought the Windows section below points out some +Windows batch files that can be used to avoid opening a terminal/command-prompt. You should seek cmake's documentation online or via ``cmake --help`` to see how the command works. See the `build-options` page for help finding the DFHack build options relevant to you. @@ -284,13 +284,13 @@ addition to the normal ``CC`` and ``CXX`` flags above:: export PATH=/usr/local/bin:$PATH -Docker -====== +Windows cross compiling from Linux +================================== .. highlight:: bash -You can use docker to build DFHack for windows. These instructions were developed -on a linux host system. +You can use docker to build DFHack for Windows. These instructions were developed +on a Linux host system. .. contents:: :local: @@ -326,7 +326,7 @@ container. Inside the container, run the following commands:: dfhack-make Inside the ``dfhack-*`` scripts there are several commands that set up the wine -server. Each invocation of a windows tool will cause wine to run in the container. +server. Each invocation of a Windows tool will cause wine to run in the container. Preloading the wineserver and telling it not to exit will speed configuration and compilation up considerably (approx. 10x). You can configure and build DFHack with regular ``cmake`` and ``ninja`` commands, but your build will go much slower. diff --git a/docs/dev/building/Dependencies.rst b/docs/dev/compile/Dependencies.rst similarity index 100% rename from docs/dev/building/Dependencies.rst rename to docs/dev/compile/Dependencies.rst diff --git a/docs/dev/building/Options.rst b/docs/dev/compile/Options.rst similarity index 90% rename from docs/dev/building/Options.rst rename to docs/dev/compile/Options.rst index 8de88a0e3..b314e9db2 100644 --- a/docs/dev/building/Options.rst +++ b/docs/dev/compile/Options.rst @@ -29,13 +29,13 @@ Typical usage may look like:: configuration utility ``ccmake``. Generator ---------- +========= For the uninitiated, the generator is what allows cmake to, of course, generate visual studio solution & project files, a makefile, or anything else. Your selection of generator comes down to preference and availability. Visual Studio -============= +------------- To generate visual studio project files, you'll need to select a particular version of visual studio, and match that to your system's generator list viewed with ``cmake --help`` @@ -44,7 +44,7 @@ example:: cmake .. -G "Visual Studio 17 2022" Ninja -===== +----- The generally preferred build system where available. example:: @@ -52,8 +52,8 @@ example:: cmake .. -G Ninja Install Location ----------------- -This of course uses the default cmake variable. +================ +This is the location where DFHack will be installed. Variable: ``CMAKE_INSTALL_PREFIX`` @@ -65,9 +65,9 @@ The path to df will of course depend on your system. If the directory exists it recommended to use ``~/.dwarffortress`` to avoid permission troubles. Build type ----------- -Release/Debug, this is the type of build you want. This controls what information -about symbols and line numbers the debugger will have available to it. +========== +This is the type of build you want. This controls what information about symbols and +line numbers the debugger will have available to it. Variable: ``CMAKE_BUILD_TYPE`` @@ -81,8 +81,9 @@ Options: * RelWithDebInfo Target architecture (32/64-bit) ---------------------------------------- -If need 32-bit binaries or are looking to be explicit about building 64-bit. +=============================== +You can set this if you need 32-bit binaries or are looking to be explicit about +building 64-bit. Variable: ``DFHACK_BUILD_ARCH`` @@ -96,7 +97,7 @@ Options: * '64' (default option) Library -------- +======= This will only be useful if you're looking to avoid building the library core, as it builds by default. Variable: ``BUILD_LIBRARY`` @@ -107,7 +108,7 @@ Usage:: cmake .. -DBUILD_LIBRARY=0 Testing -------- +======= Regression testing will be arriving in the future, but for now there are only tests written in lua. Variables: @@ -121,7 +122,7 @@ Usage:: cmake .. -DBUILD_TESTS=1 Plugins -------- +======= If you're doing plugin development. Variable: ``BUILD_PLUGINS`` @@ -134,7 +135,7 @@ Usage:: .. _building-documentation: Documentation -------------- +============= If you need to build documentation. Documentation can be built as HTML, and PDF, but there are also plain text files generated for in-game. diff --git a/docs/dev/building/index.rst b/docs/dev/compile/index.rst similarity index 69% rename from docs/dev/building/index.rst rename to docs/dev/compile/index.rst index 0a5758adf..0504bd8fb 100644 --- a/docs/dev/building/index.rst +++ b/docs/dev/compile/index.rst @@ -9,6 +9,6 @@ Those seeking to compile the source code for DFHack, and or plugins, can refer t .. toctree:: :maxdepth: 2 - /docs/dev/building/Dependencies - /docs/dev/building/Compile - /docs/dev/building/Options + /docs/dev/compile/Dependencies + /docs/dev/compile/Compile + /docs/dev/compile/Options diff --git a/docs/dev/index.rst b/docs/dev/index.rst index c96024055..01436033c 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -10,7 +10,7 @@ These are pages relevant to people developing for DFHack. :maxdepth: 1 /docs/dev/Dev-intro - /docs/dev/building/index + /docs/dev/compile/index /docs/dev/Contributing /docs/dev/Documentation /docs/api/index From e111a7376302faf980cfa5aeb964b69a37941178 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 11:34:15 -0800 Subject: [PATCH 0055/2222] allow color options for CycleHotkeyLabel and use the option to render `On` in green for ToggleHotkeyLabel --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 9 ++++++--- library/lua/gui/widgets.lua | 24 ++++++++++++++---------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bfc01b322..b714adac1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,6 +54,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``gui.View``: ``visible`` and ``active`` can now be functions that return a boolean - ``widgets.Panel``: new attributes to control window dragging and resizing with mouse or keyboard - ``widgets.Window``: Panel subclass with attributes preset for top-level windows +- ``widgets.CycleHotkeyLabel``: now supports rendering option labels in the color of your choice +- ``widgets.ToggleHotkeyLabel``: now renders the ``On`` option in green text - `overlay`: ``OverlayWidget`` now inherits from ``Panel`` instead of ``Widget`` to get all the frame and mouse integration goodies - ``dfhack.gui.getDFViewscreen()``: returns the topmost underlying DF viewscreen diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8962d795a..01b356a7c 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4608,8 +4608,10 @@ It has the following attributes: hotkey. :label_width: The number of spaces to allocate to the ``label`` (for use in aligning a column of ``CycleHotkeyLabel`` labels). -:options: A list of strings or tables of ``{label=string, value=string}``. - String options use the same string for the label and value. +:options: A list of strings or tables of + ``{label=string, value=string[, pen=pen]}``. String options use the same + string for the label and value and the default pen. The optional ``pen`` + element could be a color like ``COLOR_RED``. :initial_option: The value or numeric index of the initial option. :on_change: The callback to call when the selected option changes. It is called as ``on_change(new_option_value, old_option_value)``. @@ -4637,7 +4639,8 @@ ToggleHotkeyLabel ----------------- This is a specialized subclass of CycleHotkeyLabel that has two options: -``On`` (with a value of ``true``) and ``Off`` (with a value of ``false``). +``On`` (with a value of ``true``) and ``Off`` (with a value of ``false``). The +``On`` option is rendered in green. List class ---------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index e95410fb2..db5950c1f 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1416,7 +1416,8 @@ function CycleHotkeyLabel:init() {key=self.key, key_sep=': ', text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, ' ', - {text=self:callback('getOptionLabel')}, + {text=self:callback('getOptionLabel'), + pen=self:callback('getOptionPen')}, } end @@ -1433,22 +1434,25 @@ function CycleHotkeyLabel:cycle() end end -function CycleHotkeyLabel:getOptionLabel(option_idx) +local function cyclehotkeylabel_getOptionElem(self, option_idx, key) option_idx = option_idx or self.option_idx local option = self.options[option_idx] if type(option) == 'table' then - return option.label + return option[key] end return option end +function CycleHotkeyLabel:getOptionLabel(option_idx) + return cyclehotkeylabel_getOptionElem(self, option_idx, 'label') +end + function CycleHotkeyLabel:getOptionValue(option_idx) - option_idx = option_idx or self.option_idx - local option = self.options[option_idx] - if type(option) == 'table' then - return option.value - end - return option + return cyclehotkeylabel_getOptionElem(self, option_idx, 'value') +end + +function CycleHotkeyLabel:getOptionPen(option_idx) + return cyclehotkeylabel_getOptionElem(self, option_idx, 'pen') end function CycleHotkeyLabel:onInput(keys) @@ -1466,7 +1470,7 @@ end ToggleHotkeyLabel = defclass(ToggleHotkeyLabel, CycleHotkeyLabel) ToggleHotkeyLabel.ATTRS{ - options={{label='On', value=true}, + options={{label='On', value=true, pen=COLOR_GREEN}, {label='Off', value=false}}, } From f15e9e357760617e115ce701e66bf326ed02e34c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 12:15:38 -0800 Subject: [PATCH 0056/2222] don't revert to plain strings for pens --- library/lua/gui/widgets.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index db5950c1f..bc5dff9da 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1452,7 +1452,9 @@ function CycleHotkeyLabel:getOptionValue(option_idx) end function CycleHotkeyLabel:getOptionPen(option_idx) - return cyclehotkeylabel_getOptionElem(self, option_idx, 'pen') + local pen = cyclehotkeylabel_getOptionElem(self, option_idx, 'pen') + if type(pen) == 'string' then return nil end + return pen end function CycleHotkeyLabel:onInput(keys) From 574be3fe73223d75114dd94bcc99395da293daf6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 21:50:43 -0800 Subject: [PATCH 0057/2222] provide a useful default impl of isMouseOver --- library/lua/gui.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 27484541f..a7f68dd25 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -737,10 +737,10 @@ function ZScreen:raise() dscreen.raise(self) end --- subclasses should override this and return whether the mouse is over an --- owned screen element +-- subclasses should either annotate their viewable panel with view_id='main' +-- or override this and return whether the mouse is over an owned screen element function ZScreen:isMouseOver() - return false + return self.subviews.main and self.subviews.main:getMouseFramePos() or false end -------------------------- From fbf895fe0cd7bdb0596f9a0e95f722c3c2b1aac2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 23:20:45 -0800 Subject: [PATCH 0058/2222] document ZScreen (and view:getMouseFramePos()) --- docs/dev/Lua API.rst | 62 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8962d795a..ca3a6c9ed 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -3921,8 +3921,13 @@ The class has the following methods: * ``view:getMousePos([view_rect])`` Returns the mouse *x,y* in coordinates local to the given ViewRect (or - ``frame_body`` if no ViewRect is passed) if it is within its clip area, - or nothing otherwise. + ``frame_body`` if no ViewRect is passed) if it is within its clip area, or + nothing otherwise. + +* ``view:getMouseFramePos()`` + + Returns the mouse *x,y* in coordinates local to ``frame_rect`` if it is + within its clip area, or nothing otherwise. * ``view:updateLayout([parent_rect])`` @@ -4005,7 +4010,7 @@ The class has the following methods: Screen class ------------ -This is a View subclass intended for use as a stand-alone dialog or screen. +This is a View subclass intended for use as a stand-alone modal dialog or screen. It adds the following methods: * ``screen:isShown()`` @@ -4073,6 +4078,57 @@ It adds the following methods: Defined as callbacks for native code. +ZScreen class +------------- + +A screen subclass that allows the underlying viewscreens to be interacted with. +For example, a DFHack GUI tool implemented as a ZScreen can allow the player to +interact with the underlying map. That is, even when the DFHack tool window is +visible, players will be able to use vanilla designation tools, select units, or +scan/drag the map around. + +If multiple ZScreens are on the stack and the player clicks on a visible element +of a non-top ZScreen, that ZScreen will be raised to the top of the viewscreen +stack. This allows multiple DFHack gui tools to be usable at the same time. +Clicks that are not over any visible ZScreen element, of course, are passed +through to the underlying viewscreen. + +If :kbd:`Esc` or the right mouse button is pressed, and the ZScreen widgets +don't otherwise handle them, then the top ZScreen is dismissed. + +Keyboard input goes to the top ZScreen, as usual. If the subviews of the top +ZScreen don't handle the input (i.e. they all return something falsey), the +input is passed directly to the first underlying non-ZScreen. + +All this behavior is implemented in ``ZScreen:onInput()``, which subclasses +should *not* override. Instead, ZScreen subclasses should delegate all input +processing to subviews. Consider using a `Window class`_ widget as your top +level input processor. + +When rendering, the parent viewscreen is automatically rendered first, so +subclasses do not have to call ``self:renderParent()``. Calls to ``logic()`` +(a world "tick" when playing the game) are also passed through, so the game +progresses normally and can be paused/unpaused as normal by the player. +ZScreens that handle the :kbd:`Space` key may want to provide an alternate way +to pause. Note that passing ``logic()`` calls through to the underlying map is +required for allowing the player to drag the map with the mouse. + +ZScreen provides the following functions: + +* ``zscreen:raise()`` + + Raises the ZScreen to the top of the viewscreen stack. Note that this is + handled automatically for common cases (e.g. player clicks on a Window + belonging to a ZScreen that is not the top viewscreen). + +* ``zscreen:isMouseOver()`` + + If the ZScreen subclass has a subview with a ``view_id`` equal to "main", + then the mouse will be considered to be over the visible viewscreen elements + when ``self.subviews.main:getMouseFramePos()`` returns a position. Subclasses + can override this function if that logic is not appropriate, for example if + there are multiple independent windows being shown and this function should + return true if the mouse is over any of them. FramedScreen class ------------------ From 96f19621c9ed5bb6492ce68b55e6fb8e9ad3656a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 5 Jan 2023 23:24:44 -0800 Subject: [PATCH 0059/2222] update changelog --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index bfc01b322..cd4191c81 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -56,6 +56,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.Window``: Panel subclass with attributes preset for top-level windows - `overlay`: ``OverlayWidget`` now inherits from ``Panel`` instead of ``Widget`` to get all the frame and mouse integration goodies - ``dfhack.gui.getDFViewscreen()``: returns the topmost underlying DF viewscreen +- ``gui.ZScreen``: Screen subclass that implements window raising, multi-viewscreen input handling, and viewscreen event pass-through so the underlying map can be interacted with and dragged around while DFHack screens are visible +- ``gui.View``: new function: ``view:getMouseFramePos()`` for detecting whether the mouse is within (or over) the exterior frame of a view ## Internals From f43358002dfe4cf69e24776ca1c5a51db6ceecaf Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 10:35:22 -0800 Subject: [PATCH 0060/2222] Allow dialogs to close on r-click --- library/lua/gui/dialogs.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index e5448986f..e7228db1e 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -59,11 +59,11 @@ function MessageBox:onDestroy() end function MessageBox:onInput(keys) - if keys.SELECT or keys.LEAVESCREEN then + if keys.SELECT or keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() if keys.SELECT and self.on_accept then self.on_accept() - elseif keys.LEAVESCREEN and self.on_cancel then + elseif (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) and self.on_cancel then self.on_cancel() end return true @@ -130,7 +130,7 @@ function InputBox:onInput(keys) self.on_input(self.subviews.edit.text) end return true - elseif keys.LEAVESCREEN then + elseif keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() if self.on_cancel then self.on_cancel() @@ -231,7 +231,7 @@ function ListBox:getWantedFrameSize() end function ListBox:onInput(keys) - if keys.LEAVESCREEN then + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() if self.on_cancel then self.on_cancel() From fccefd1155499cc5ab437a91d78a10822706204e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 10:42:07 -0800 Subject: [PATCH 0061/2222] don't pass through handled r-clicks --- library/lua/gui.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index a7f68dd25..044d5a0fd 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -721,6 +721,9 @@ function ZScreen:onInput(keys) end if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() + -- ensure underlying DF screens don't also react to the click + df.global.enabler.mouse_rbut_down = 0 + df.global.enabler.mouse_rbut = 0 return end From 1f5ae4165f6eec58cf01962279c2df73782923af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 11:11:11 -0800 Subject: [PATCH 0062/2222] return self from raise, update docs --- docs/dev/Lua API.rst | 44 +++++++++++++++++++++++++++++++++++++++++--- library/lua/gui.lua | 9 +++++---- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ca3a6c9ed..8c9655b69 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4117,9 +4117,10 @@ ZScreen provides the following functions: * ``zscreen:raise()`` - Raises the ZScreen to the top of the viewscreen stack. Note that this is - handled automatically for common cases (e.g. player clicks on a Window - belonging to a ZScreen that is not the top viewscreen). + Raises the ZScreen to the top of the viewscreen stack and returns a reference + to ``self``. A common pattern is to check if a tool dialog is already active + when the tool command is run and raise the existing dialog if it exists or + show a new dialog if it doesn't. See the sample code below for an example. * ``zscreen:isMouseOver()`` @@ -4130,6 +4131,43 @@ ZScreen provides the following functions: there are multiple independent windows being shown and this function should return true if the mouse is over any of them. +Here is an example skeleton for a ZScreen tool dialog:: + + local gui = require('gui') + local widgets = require('gui.widgets') + + MyWindow = defclass(MyWindow, widgets.Window) + MyWindow.ATTRS { + frame_title='My Window', + frame={w=50, h=45}, + resizable=true, -- if resizing makes sense for your dialog + } + + function MyWindow:init() + self:addviews{ + -- add subviews here + } + end + + function MyWindow:onInput(keys) + -- if required + end + + MyScreen = defclass(MyScreen, gui.ZScreen) + MyScreen.ATTRS { + focus_path='myscreen', + } + + function MyScreen:init() + self:addviews{MyWindow{view_id='main'}} + end + + function MyScreen:onDismiss() + view = nil + end + + view = view and view:raise() or MyScreen{}:show() + FramedScreen class ------------------ diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 044d5a0fd..4777f8186 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -702,12 +702,12 @@ function ZScreen:render(dc) ZScreen.super.render(self, dc) end -local function zscreen_is_top(self) +function ZScreen:isOnTop() return dfhack.gui.getCurViewscreen(true) == self._native end function ZScreen:onInput(keys) - if not zscreen_is_top(self) then + if not self:isOnTop() then if keys._MOUSE_L_DOWN and self:isMouseOver() then self:raise() else @@ -734,10 +734,11 @@ end -- move this viewscreen to the top of the stack (if it's not there already) function ZScreen:raise() - if self:isDismissed() or zscreen_is_top(self) then - return + if self:isDismissed() or self:isOnTop() then + return self end dscreen.raise(self) + return self end -- subclasses should either annotate their viewable panel with view_id='main' From 810430f1a2f217288ed40d9328b0b727622f46eb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 18:48:53 -0800 Subject: [PATCH 0063/2222] make windows lockable (can ignore r-click and esc) --- docs/dev/Lua API.rst | 33 ++++++++++++++++++++++++--------- library/lua/gui.lua | 31 +++++++++++++++++++++++++------ library/lua/gui/widgets.lua | 19 ++++++++++++++++++- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8c9655b69..a3b91af18 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4094,7 +4094,15 @@ Clicks that are not over any visible ZScreen element, of course, are passed through to the underlying viewscreen. If :kbd:`Esc` or the right mouse button is pressed, and the ZScreen widgets -don't otherwise handle them, then the top ZScreen is dismissed. +don't otherwise handle them, then the top ZScreen is dismissed. If the ZScreen +is "locked", then the screen is not dismissed and the input is passed on to the +underlying DF viewscreen. :kbd:`Alt`:kbd:`L` toggles the locked status if the +ZScreen widgets don't otherwise handle that key sequence. If you have a +``Panel`` with the ``lockable`` attribute set and a frame that has pens defined +for the lock icon (like ``Window`` widgets have by default), then a lock icon +will appear in the upper right corner of the frame. Clicking on this icon will +toggle the ZScreen ``locked`` status just as if :kbd:`Alt`:kbd:`L` had been +pressed. Keyboard input goes to the top ZScreen, as usual. If the subviews of the top ZScreen don't handle the input (i.e. they all return something falsey), the @@ -4122,14 +4130,16 @@ ZScreen provides the following functions: when the tool command is run and raise the existing dialog if it exists or show a new dialog if it doesn't. See the sample code below for an example. +* ``zscreen:toggleLocked()`` + + Toggles whether the window closes on :kbd:`ESC` or r-click (unlocked) or not + (locked). + * ``zscreen:isMouseOver()`` - If the ZScreen subclass has a subview with a ``view_id`` equal to "main", - then the mouse will be considered to be over the visible viewscreen elements - when ``self.subviews.main:getMouseFramePos()`` returns a position. Subclasses - can override this function if that logic is not appropriate, for example if - there are multiple independent windows being shown and this function should - return true if the mouse is over any of them. + The default implementation iterates over the direct subviews of the ZScreen + subclass and sees if ``getMouseFramePos()`` returns a position for any of + them. Subclasses can override this function if that logic is not appropriate. Here is an example skeleton for a ZScreen tool dialog:: @@ -4159,7 +4169,7 @@ Here is an example skeleton for a ZScreen tool dialog:: } function MyScreen:init() - self:addviews{MyWindow{view_id='main'}} + self:addviews{MyWindow{}} end function MyScreen:onDismiss() @@ -4315,6 +4325,11 @@ Has attributes: hitting :kbd:`Esc` (while resizing with the mouse or keyboard), or by calling ``Panel:setKeyboardResizeEnabled(false)`` (while resizing with the keyboard). +* ``lockable = bool`` (default: ``false``) + + Determines whether the panel will draw a lock icon in its frame. See + `ZScreen class`_ for details. + * ``autoarrange_subviews = bool`` (default: ``false``) * ``autoarrange_gap = int`` (default: ``0``) @@ -4376,7 +4391,7 @@ Window class ------------ Subclass of Panel; sets Panel attributes to useful defaults for a top-level -framed, draggable window. +framed, lockable, draggable window. ResizingPanel class ------------------- diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 4777f8186..426458159 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -706,6 +706,10 @@ function ZScreen:isOnTop() return dfhack.gui.getCurViewscreen(true) == self._native end +function ZScreen:toggleLocked() + self.locked = not self.locked +end + function ZScreen:onInput(keys) if not self:isOnTop() then if keys._MOUSE_L_DOWN and self:isMouseOver() then @@ -719,7 +723,13 @@ function ZScreen:onInput(keys) if ZScreen.super.onInput(self, keys) then return end - if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + + if keys.CUSTOM_ALT_L then + self:toggleLocked() + return + end + + if not self.locked and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then self:dismiss() -- ensure underlying DF screens don't also react to the click df.global.enabler.mouse_rbut_down = 0 @@ -732,7 +742,6 @@ function ZScreen:onInput(keys) end end --- move this viewscreen to the top of the stack (if it's not there already) function ZScreen:raise() if self:isDismissed() or self:isOnTop() then return self @@ -741,10 +750,10 @@ function ZScreen:raise() return self end --- subclasses should either annotate their viewable panel with view_id='main' --- or override this and return whether the mouse is over an owned screen element function ZScreen:isMouseOver() - return self.subviews.main and self.subviews.main:getMouseFramePos() or false + for _,sv in ipairs(self.subviews) do + if sv:getMouseFramePos() then return true end + end end -------------------------- @@ -777,9 +786,11 @@ GREY_LINE_FRAME = { rb_frame_pen = to_pen{ tile=917, ch=188, fg=COLOR_GREY, bg=COLOR_BLACK }, title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, + locked_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, + unlocked_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, } -function paint_frame(dc,rect,style,title) +function paint_frame(dc,rect,style,title,show_lock,locked) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 dscreen.paintTile(style.lt_frame_pen or pen, x1, y1) @@ -802,6 +813,14 @@ function paint_frame(dc,rect,style,title) end dscreen.paintString(style.title_pen or pen, x, y1, tstr) end + + if show_lock then + if locked and style.locked_pen then + dscreen.paintTile(style.locked_pen, x2-1, y1) + elseif not locked and style.unlocked_pen then + dscreen.paintTile(style.unlocked_pen, x2-1, y1) + end + end end FramedScreen = defclass(FramedScreen, Screen) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index e95410fb2..4df6d07ff 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -81,6 +81,7 @@ Panel.ATTRS { resize_min = DEFAULT_NIL, on_resize_begin = DEFAULT_NIL, on_resize_end = DEFAULT_NIL, + lockable = false, autoarrange_subviews = false, -- whether to automatically lay out subviews autoarrange_gap = 0, -- how many blank lines to insert between widgets } @@ -464,7 +465,11 @@ end function Panel:onRenderFrame(dc, rect) Panel.super.onRenderFrame(self, dc, rect) if not self.frame_style then return end - gui.paint_frame(dc, rect, self.frame_style, self.frame_title) + local locked = nil + if self.lockable then + locked = self.parent_view and self.parent_view.locked + end + gui.paint_frame(dc, rect, self.frame_style, self.frame_title, self.lockable, locked) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} @@ -487,8 +492,20 @@ Window.ATTRS { frame_background = gui.CLEAR_PEN, frame_inset = 1, draggable = true, + lockable = true, } +function Window:onInput(keys) + if keys._MOUSE_L_DOWN and self.parent_view and self.parent_view.toggleLocked then + local x,y = dscreen.getMousePos() + local frame_rect = self.frame_rect + if x == frame_rect.x2-1 and y == frame_rect.y1 then + self.parent_view:toggleLocked() + end + end + return Window.super.onInput(self, keys) +end + ------------------- -- ResizingPanel -- ------------------- From 093eac3eb2fc3ff05467684655b22472839c8058 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 18:58:08 -0800 Subject: [PATCH 0064/2222] use a black background for non-top ZScreen titles --- library/lua/gui.lua | 6 ++++-- library/lua/gui/widgets.lua | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 426458159..661351d31 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -785,12 +785,13 @@ GREY_LINE_FRAME = { rt_frame_pen = to_pen{ tile=903, ch=187, fg=COLOR_GREY, bg=COLOR_BLACK }, rb_frame_pen = to_pen{ tile=917, ch=188, fg=COLOR_GREY, bg=COLOR_BLACK }, title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, + inactive_title_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, locked_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, unlocked_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, } -function paint_frame(dc,rect,style,title,show_lock,locked) +function paint_frame(dc,rect,style,title,show_lock,locked,inactive) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 dscreen.paintTile(style.lt_frame_pen or pen, x1, y1) @@ -811,7 +812,8 @@ function paint_frame(dc,rect,style,title,show_lock,locked) if #tstr > x2-x1-1 then tstr = string.sub(tstr,1,x2-x1-1) end - dscreen.paintString(style.title_pen or pen, x, y1, tstr) + dscreen.paintString(inactive and style.inactive_title_pen or style.title_pen or pen, + x, y1, tstr) end if show_lock then diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 4df6d07ff..9d8e30d18 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -469,7 +469,10 @@ function Panel:onRenderFrame(dc, rect) if self.lockable then locked = self.parent_view and self.parent_view.locked end - gui.paint_frame(dc, rect, self.frame_style, self.frame_title, self.lockable, locked) + local inactive = self.parent_view and self.parent_view.isOnTop + and not self.parent_view:isOnTop() + gui.paint_frame(dc, rect, self.frame_style, self.frame_title, + self.lockable, locked, inactive) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} From 3e4861b54bbd8ff0b76c9d60110a3ee50487a29b Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 21:24:01 -0600 Subject: [PATCH 0065/2222] fix persistence for changed file structure `data/save` -> `save` --- library/modules/Persistence.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Persistence.cpp b/library/modules/Persistence.cpp index 05afa77ed..975ba0bea 100644 --- a/library/modules/Persistence.cpp +++ b/library/modules/Persistence.cpp @@ -387,7 +387,7 @@ static std::string filterSaveFileName(std::string s) static std::string getSaveFilePath(const std::string &world, const std::string &name) { - return "data/save/" + world + "/dfhack-" + filterSaveFileName(name) + ".dat"; + return "save/" + world + "/dfhack-" + filterSaveFileName(name) + ".dat"; } #if defined(__GNUC__) && __GNUC__ < 5 From cc4a42a901ebf5b7b30128e00b241fc15cef9f44 Mon Sep 17 00:00:00 2001 From: Rose Date: Fri, 6 Jan 2023 19:43:54 -0800 Subject: [PATCH 0066/2222] Re-comment out the UI stuff. --- .../adventure_control.cpp | 19 +++++++++++++------ .../remotefortressreader/dwarf_control.cpp | 6 ++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/remotefortressreader/adventure_control.cpp b/plugins/remotefortressreader/adventure_control.cpp index 2dbf2abc1..0d2e97f68 100644 --- a/plugins/remotefortressreader/adventure_control.cpp +++ b/plugins/remotefortressreader/adventure_control.cpp @@ -39,6 +39,7 @@ void SetCoord(df::coord in, RemoteFortressReader::Coord *out) command_result MoveCommand(DFHack::color_ostream &stream, const MoveCommandParams *in) { +/* Removed for v50 which has no adventure mode. auto viewScreen = getCurViewscreen(); if (!in->has_direction()) return CR_WRONG_USAGE; @@ -186,12 +187,13 @@ command_result MoveCommand(DFHack::color_ostream &stream, const MoveCommandParam } break; } - return CR_OK; +*/ return CR_OK; } command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParams *in) { - if (!in->has_direction()) +/* Removed for v50 which has no adventure mode. + if (!in->has_direction()) return CR_WRONG_USAGE; if (!df::global::adventure->menu == ui_advmode_menu::Default) return CR_OK; @@ -228,12 +230,14 @@ command_result JumpCommand(DFHack::color_ostream &stream, const MoveCommandParam } } keyQueue.push(interface_key::SELECT); +*/ return CR_OK; } command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, MenuContents *out) { - auto advUi = df::global::adventure; +/* Removed for v50 which has no adventure mode. + auto advUi = df::global::adventure; if (advUi == NULL) return CR_FAILURE; @@ -272,12 +276,13 @@ command_result MenuQuery(DFHack::color_ostream &stream, const EmptyMessage *in, default: break; } - +*/ return CR_OK; } command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfproto::IntMessage *in) { + /* Removed for v50 which has no adventure mode. if (!(df::global::adventure->menu == ui_advmode_menu::MoveCarefully)) return CR_OK; int choice = in->value(); @@ -288,12 +293,14 @@ command_result MovementSelectCommand(DFHack::color_ostream &stream, const dfprot keyQueue.push(interface_key::SECONDSCROLL_PAGEDOWN); } keyQueue.push((interface_key::interface_key)(interface_key::OPTION1 + select)); +*/ return CR_OK; } command_result MiscMoveCommand(DFHack::color_ostream &stream, const MiscMoveParams *in) { - if (!df::global::adventure->menu == ui_advmode_menu::Default) + /* Removed for v50 which has no adventure mode. + if (!df::global::adventure->menu == ui_advmode_menu::Default) return CR_OK; auto type = in->type(); @@ -312,6 +319,6 @@ command_result MiscMoveCommand(DFHack::color_ostream &stream, const MiscMovePara default: break; } - +*/ return CR_OK; } diff --git a/plugins/remotefortressreader/dwarf_control.cpp b/plugins/remotefortressreader/dwarf_control.cpp index f0147670f..5b8f2b282 100644 --- a/plugins/remotefortressreader/dwarf_control.cpp +++ b/plugins/remotefortressreader/dwarf_control.cpp @@ -233,6 +233,7 @@ command_result SetPauseState(color_ostream &stream, const SingleBool *in) void CopyBuildMenu(DwarfControl::SidebarState * out) { + /* Removed in v50 because the new menu is completely changed and we may not even want to do it the same way anymore. auto menus = df::global::game; auto build_selector = df::global::ui_build_selector; if (build_selector->building_type == -1) @@ -299,10 +300,12 @@ void CopyBuildMenu(DwarfControl::SidebarState * out) send_selector->add_tiles(build_selector->tiles[x][y]); } } + */ } command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMessage *in, DwarfControl::SidebarState *out) { + /* Removed in v50 because the new menu is completely changed and we may not even want to do it the same way anymore. auto plotinfo = df::global::plotinfo; out->set_mode((proto::enums::ui_sidebar_mode::ui_sidebar_mode)plotinfo->main.mode); auto mode = plotinfo->main.mode; @@ -422,11 +425,13 @@ command_result GetSideMenu(DFHack::color_ostream &stream, const dfproto::EmptyMe default: break; } + */ return CR_OK; } command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::SidebarCommand *in) { + /* Removed in v50 because the new menu is completely changed and we may not even want to do it the same way anymore. auto plotinfo = df::global::plotinfo; if (in->has_mode()) { @@ -489,5 +494,6 @@ command_result SetSideMenu(DFHack::color_ostream &stream, const DwarfControl::Si break; } } + */ return CR_OK; } From 51bb5589dee14093748215808b1b48e4903a5b7b Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 21:55:42 -0600 Subject: [PATCH 0067/2222] change order of state change event processing this will cause persistent data storage to load _before_ `SC_WORLD_LOADED` events are sent to scripts and plugins --- library/Core.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index d54218b5c..abcba2e78 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2197,6 +2197,11 @@ void Core::onStateChange(color_ostream &out, state_change_event event) std::cerr << "loaded map in prerelease build" << std::endl; } + if (event == SC_WORLD_LOADED) + { + doLoadData(out); + } + EventManager::onStateChange(out, event); buildings_onStateChange(out, event); @@ -2211,10 +2216,6 @@ void Core::onStateChange(color_ostream &out, state_change_event event) { Persistence::Internal::clear(); } - if (event == SC_WORLD_LOADED) - { - doLoadData(out); - } } void Core::doSaveData(color_ostream &out) From cc454a30d845001c2aca3a0b5af308544a72eab4 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 7 Jan 2023 07:13:45 +0000 Subject: [PATCH 0068/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 55488a935..7c6a06cdb 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 55488a9350190858efa2f872cda2d627ae2d0cd2 +Subproject commit 7c6a06cdbd1026820e7a97fbafbbcec0dfd91793 From 72d5760ff6dfc52cc8565be77c4497c3f0970dcb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Jan 2023 01:00:40 -0800 Subject: [PATCH 0069/2222] add CycleHotkeyLabel:setOption() --- docs/changelog.txt | 3 ++- docs/dev/Lua API.rst | 12 +++++++++++ library/lua/gui/widgets.lua | 42 +++++++++++++++++++++++-------------- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2e112fd48..f94e7cc0c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,11 +55,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.Panel``: new attributes to control window dragging and resizing with mouse or keyboard - ``widgets.Window``: Panel subclass with attributes preset for top-level windows - ``widgets.CycleHotkeyLabel``: now supports rendering option labels in the color of your choice +- ``widgets.CycleHotkeyLabel``: new functions ``setOption()`` and ``getOptionPen()`` - ``widgets.ToggleHotkeyLabel``: now renders the ``On`` option in green text - `overlay`: ``OverlayWidget`` now inherits from ``Panel`` instead of ``Widget`` to get all the frame and mouse integration goodies - ``dfhack.gui.getDFViewscreen()``: returns the topmost underlying DF viewscreen - ``gui.ZScreen``: Screen subclass that implements window raising, multi-viewscreen input handling, and viewscreen event pass-through so the underlying map can be interacted with and dragged around while DFHack screens are visible -- ``gui.View``: new function: ``view:getMouseFramePos()`` for detecting whether the mouse is within (or over) the exterior frame of a view +- ``gui.View``: new function ``view:getMouseFramePos()`` for detecting whether the mouse is within (or over) the exterior frame of a view ## Internals diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 286294ebe..2b360350f 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4734,6 +4734,12 @@ The CycleHotkeyLabel widget implements the following methods: Cycles the selected option and triggers the ``on_change`` callback. +* ``cyclehotkeylabel:setOption(value_or_index, call_on_change)`` + + Sets the current option to the option with the specified value or + index. If ``call_on_change`` is set to ``true``, then the ``on_change`` + callback is triggered. + * ``cyclehotkeylabel:getOptionLabel([option_idx])`` Retrieves the option label at the given index, or the label of the @@ -4744,6 +4750,12 @@ The CycleHotkeyLabel widget implements the following methods: Retrieves the option value at the given index, or the value of the currently selected option if no index is given. +* ``cyclehotkeylabel:getOptionPen([option_idx])`` + + Retrieves the option pen at the given index, or the pen of the currently + selected option if no index is given. If an option was defined as just a + string, then this function will return ``nil`` for that option. + ToggleHotkeyLabel ----------------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 0f0d14d64..c5002ddaa 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1415,22 +1415,7 @@ CycleHotkeyLabel.ATTRS{ } function CycleHotkeyLabel:init() - -- initialize option_idx - for i in ipairs(self.options) do - if self.initial_option == self:getOptionValue(i) then - self.option_idx = i - break - end - end - if not self.option_idx then - if self.options[self.initial_option] then - self.option_idx = self.initial_option - end - end - if not self.option_idx then - error(('cannot find option with value or index: "%s"') - :format(self.initial_option)) - end + self:setOption(self.initial_option) self:setText{ {key=self.key, key_sep=': ', text=self.label, width=self.label_width, @@ -1454,6 +1439,31 @@ function CycleHotkeyLabel:cycle() end end +function CycleHotkeyLabel:setOption(value_or_index, call_on_change) + local option_idx = nil + for i in ipairs(self.options) do + if value_or_index == self:getOptionValue(i) then + option_idx = i + break + end + end + if not option_idx then + if self.options[value_or_index] then + option_idx = value_or_index + end + end + if not option_idx then + error(('cannot find option with value or index: "%s"') + :format(value_or_index)) + end + local old_option_idx = self.option_idx + self.option_idx = option_idx + if call_on_change and self.on_change then + self.on_change(self:getOptionValue(), + self:getOptionValue(old_option_idx)) + end +end + local function cyclehotkeylabel_getOptionElem(self, option_idx, key) option_idx = option_idx or self.option_idx local option = self.options[option_idx] From 95223d819761c09b8290be1cb61fc65a015aec6d Mon Sep 17 00:00:00 2001 From: Rose Date: Sat, 7 Jan 2023 14:37:44 -0800 Subject: [PATCH 0070/2222] Got archery target direction back, and cleaned up a few things. --- library/xml | 2 +- .../remotefortressreader/building_reader.cpp | 49 +++++++++---------- .../remotefortressreader.cpp | 25 ---------- 3 files changed, 24 insertions(+), 52 deletions(-) diff --git a/library/xml b/library/xml index d026f34ed..d7de31978 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d026f34ed1f7ab79aebb1c5bc8a36ee9b30bd13d +Subproject commit d7de31978634d6bd165069e5fcaffc64a0d4a91c diff --git a/plugins/remotefortressreader/building_reader.cpp b/plugins/remotefortressreader/building_reader.cpp index 5a0595eef..e1f29e358 100644 --- a/plugins/remotefortressreader/building_reader.cpp +++ b/plugins/remotefortressreader/building_reader.cpp @@ -316,8 +316,6 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re material->set_mat_index(local_build->mat_index); remote_build->set_building_flags(local_build->flags.whole); - //FIXME: Figure out what to replace this with. - //remote_build->set_is_room(local_build->is_room); if (local_build->room.width > 0 && local_build->room.height > 0 && local_build->room.extents != nullptr) { auto room = remote_build->mutable_room(); @@ -609,28 +607,29 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re case df::enums::building_type::ArcheryTarget: { //FIXME: Need to decode archery targets again. - //auto actual = strict_virtual_cast(local_build); - //if (actual) - //{ - // auto facing = actual->archery_direction; - // switch (facing) - // { - // case df::building_archerytargetst::TopToBottom: - // remote_build->set_direction(NORTH); - // break; - // case df::building_archerytargetst::BottomToTop: - // remote_build->set_direction(SOUTH); - // break; - // case df::building_archerytargetst::LeftToRight: - // remote_build->set_direction(WEST); - // break; - // case df::building_archerytargetst::RightToLeft: - // remote_build->set_direction(EAST); - // break; - // default: - // break; - // } - //} + auto actual = strict_virtual_cast(local_build); + if (actual) + { + for (size_t i = 0; i < actual->relations.size(); i++) + { + if (actual->relations[i]->getType() != df::enums::building_type::Civzone) + continue; + auto zone = strict_virtual_cast(actual->relations[i]); + if (!zone) + continue; + if (zone->type != df::civzone_type::ArcheryRange) + continue; + if(zone->dir_x < 0) + remote_build->set_direction(EAST); + else if(zone->dir_x > 0) + remote_build->set_direction(WEST); + else if (zone->dir_y < 0) + remote_build->set_direction(SOUTH); + else if (zone->dir_y > 0) + remote_build->set_direction(NORTH); + break; + } + } break; } case df::enums::building_type::Chain: @@ -851,7 +850,6 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re auto actual = strict_virtual_cast(local_build); if (actual) { -#if DF_VERSION_INT > 34011 if (actual->orient_x < 0) remote_build->set_direction(WEST); else if (actual->orient_x > 0) @@ -861,7 +859,6 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re else if (actual->orient_y > 0) remote_build->set_direction(SOUTH); else -#endif remote_build->set_direction(WEST); if (actual->machine.machine_id >= 0) { diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index c4e5b99de..d1a269d52 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -859,31 +859,6 @@ static command_result GetMaterialList(color_ostream &stream, const EmptyMessage } } } - //for (size_t i = 0; i < history->figures.size(); i++) - //{ - // df::historical_figure * figure = history->figures[i]; - // if (figure->race < 0) - // continue; - // df::creature_raw * creature = raws->creatures.all[figure->race]; - // for (int j = 0; j < creature->material.size(); j++) - // { - // mat.decode(j + MaterialInfo::FIGURE_BASE, i); - // MaterialDefinition *mat_def = out->add_material_list(); - // mat_def->mutable_mat_pair()->set_mat_type(j + MaterialInfo::FIGURE_BASE); - // mat_def->mutable_mat_pair()->set_mat_index(i); - // stringstream id; - // id << "HF" << i << mat.getToken(); - // mat_def->set_id(id.str()); - // mat_def->set_name(mat.toString()); //find the name at cave temperature; - // if (creature->material[j]->state_color[GetState(creature->material[j])] < raws->descriptors.colors.size()) - // { - // df::descriptor_color *color = raws->descriptors.colors[creature->material[j]->state_color[GetState(creature->material[j])]]; - // mat_def->mutable_state_color()->set_red(color->red * 255); - // mat_def->mutable_state_color()->set_green(color->green * 255); - // mat_def->mutable_state_color()->set_blue(color->blue * 255); - // } - // } - //} for (size_t i = 0; i < raws->plants.all.size(); i++) { df::plant_raw * plant = raws->plants.all[i]; From 60a2f615c001e46072747f3296e2da19cbdde76d Mon Sep 17 00:00:00 2001 From: Rose Date: Sat, 7 Jan 2023 21:06:47 -0800 Subject: [PATCH 0071/2222] Enabled probe, and fixed the one thing preventing it from compiling. Before this can be considered updated, it would need to be able to find the currently selected building or creature from the game, but it does work as-is when the keyboard cursor is enabled in the game. --- plugins/CMakeLists.txt | 2 +- plugins/probe.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2b2f3aad5..6e0dc7e00 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -144,7 +144,7 @@ dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #dfhack_plugin(petcapRemover petcapRemover.cpp) #dfhack_plugin(plants plants.cpp) -#dfhack_plugin(probe probe.cpp) +dfhack_plugin(probe probe.cpp) #dfhack_plugin(prospector prospector.cpp LINK_LIBRARIES lua) #dfhack_plugin(power-meter power-meter.cpp LINK_LIBRARIES lua) #dfhack_plugin(regrass regrass.cpp) diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 7781812b9..d55e4e135 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -477,8 +477,8 @@ command_result df_bprobe (color_ostream &out, vector & parameters) out.print(", subtype %i", building.subtype); break; } - if(building.origin->is_room) //isRoom()) - out << ", room"; + if(building.origin->relations.size()) //Connected to rooms. + out << ", " << building.origin->relations.size() << " rooms"; if(building.origin->getBuildStage()!=building.origin->getMaxBuildStage()) out << ", in construction"; out.print("\n"); From 0684b4a9ba8001f0c810f1a049d0c35b56be2c1a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 8 Jan 2023 07:13:26 +0000 Subject: [PATCH 0072/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index d7de31978..79ed9d4af 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d7de31978634d6bd165069e5fcaffc64a0d4a91c +Subproject commit 79ed9d4af1fc5e3c0e4693ffb0d1da1d0df771f5 diff --git a/scripts b/scripts index 7c6a06cdb..fed70e364 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7c6a06cdbd1026820e7a97fbafbbcec0dfd91793 +Subproject commit fed70e36466fdc7baed1d0424f8c4c7d1503597e From f78267351d4399656d19065fc0fb070e928728e4 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 8 Jan 2023 18:45:17 +0000 Subject: [PATCH 0073/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 79ed9d4af..82833e9ca 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 79ed9d4af1fc5e3c0e4693ffb0d1da1d0df771f5 +Subproject commit 82833e9ca2f91034237f01b76c03580e1de9a8eb From 92489ef27dfa4b4bffd439ef3de4f0d1ea0c6f00 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 8 Jan 2023 11:33:14 -0800 Subject: [PATCH 0074/2222] get things to compile with the recent structure updates --- library/modules/Buildings.cpp | 5 +++-- library/modules/Gui.cpp | 4 ++-- library/modules/Job.cpp | 22 +++++++++---------- .../remotefortressreader/dwarf_control.cpp | 3 +-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 21d63862c..2e7143533 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1263,7 +1263,8 @@ bool Buildings::deconstruct(df::building *bld) // Assume: no parties. unlinkRooms(bld); // Assume: not unit destroy target - vector_erase_at(plotinfo->tax_collection.rooms, linear_index(plotinfo->tax_collection.rooms, bld->id)); + int id = bld->id; + vector_erase_at(plotinfo->tax_collection.rooms, linear_index(plotinfo->tax_collection.rooms, id)); // Assume: not used in punishment // Assume: not used in non-own jobs // Assume: does not affect pathfinding @@ -1283,7 +1284,7 @@ bool Buildings::deconstruct(df::building *bld) { auto item = ui_look_list->items[i]; if (item->type == df::ui_look_list::T_items::Building && - item->data.Building == bld) + item->data.building.bld_id == id) { vector_erase_at(ui_look_list->items, i); delete item; diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index bf2b2e745..8a009c1dc 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -2024,8 +2024,8 @@ bool Gui::revealInDwarfmodeMap(int32_t x, int32_t y, int32_t z, bool center) *window_x = clip_range(new_win_x, 0, (world->map.x_count - w)); *window_y = clip_range(new_win_y, 0, (world->map.y_count - h)); *window_z = clip_range(new_win_z, 0, (world->map.z_count - 1)); - game->minimap.need_render = true; - game->minimap.need_scan = true; + game->minimap.update = true; + game->minimap.mustmake = true; return true; } diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index cd574684f..1976d2e12 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -593,17 +593,17 @@ std::string Job::getName(df::job *job) std::string desc; auto button = df::allocate(); - button->reaction_name = job->reaction_name; - button->hist_figure_id = job->hist_figure_id; - button->job_type = job->job_type; - button->item_type = job->item_type; - button->item_subtype = job->item_subtype; - button->mat_type = job->mat_type; - button->mat_index = job->mat_index; - button->item_category = job->item_category; - button->material_category = job->material_category; - - button->getLabel(&desc); + button->mstring = job->reaction_name; + button->spec_id = job->hist_figure_id; + button->jobtype = job->job_type; + button->itemtype = job->item_type; + button->subtype = job->item_subtype; + button->material = job->mat_type; + button->matgloss = job->mat_index; + button->specflag = job->item_category; + button->job_item_flag = job->material_category; + + button->text(&desc); delete button; return desc; diff --git a/plugins/remotefortressreader/dwarf_control.cpp b/plugins/remotefortressreader/dwarf_control.cpp index 5b8f2b282..a96cbdc04 100644 --- a/plugins/remotefortressreader/dwarf_control.cpp +++ b/plugins/remotefortressreader/dwarf_control.cpp @@ -1,3 +1,4 @@ + #include "dwarf_control.h" #include "DataDefs.h" #include "df_version_int.h" @@ -10,8 +11,6 @@ #include "df/building_def_workshopst.h" #include "df/job.h" #include "df/job_list_link.h" -#include "df/interface_button_construction_building_selectorst.h" -#include "df/interface_button_construction_category_selectorst.h" #include "df/plotinfost.h" #include "df/buildreq.h" #include "df/gamest.h" From 46a4222de6d0e14691c63809814e2710a3df16fc Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sun, 8 Jan 2023 15:57:38 -0600 Subject: [PATCH 0075/2222] fix stupid typo in reveal --- plugins/reveal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 4a05f061f..2820c9f54 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -265,7 +265,7 @@ command_result reveal(color_ostream &out, vector & params) con.print("Note that in graphics mode, tiles that are not adjacent to open\n" "space will not render but can still be examined by hovering over\n" "them with the mouse. Switching to text mode (in the game settings)\n" - "will allow the display the revealed tiles.\n\n"); + "will allow the display of the revealed tiles.\n\n"); } if(!no_hell) con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n\n"); From b3909f61a3a0929ebd6ffc0cdfe279ce65cb265d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 01:35:21 -0800 Subject: [PATCH 0076/2222] write autochop docs and proposed API --- docs/plugins/autochop.rst | 71 ++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 6b51b85cb..51f9c17df 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -4,9 +4,19 @@ autochop .. dfhack-tool:: :summary: Auto-harvest trees when low on stockpiled logs. :tags: fort auto plants - :no-command: -This plugin can designate trees for chopping when your stocks are low on logs. +This plugin can automatically designate trees for chopping when your stocks are +low on logs. It can also ensure specified burrows are kept clear of trees, so, +for example, caravans always have a path to your depot and trees won't grow +close to your walls, giving invaders an unexpected path into your fort. + +Autochop checks your stock of logs and designates appropriate trees for chopping +once every in-game day. Logs that are forbidden or inaccessible (e.g. in hidden +parts of the map, under water, etc.) are not counted towards your target. Trees +that are inaccessible are likewise never designated. Tree cutting quota +agreements that you have made are respected (by default). + +Please see `gui/autochop` for the interactive configuration dialog. Usage ----- @@ -14,14 +24,57 @@ Usage :: enable autochop + autochop [status] + autochop (designate|undesignate) + autochop target [] + autochop quota (abide|ignore) + autochop restrict (set|add|remove) [,...] + autochop clearcut (set|add|remove) [,...] + autochop (protect|unprotect) [,...] + +Examples +-------- + +Ensure we always have about 500 logs in stock, harvested from anywhere on the +map. Also ensure the caravan pathway and the area around the outer wall is +always clear (requires existence of appropriately-named burrows):: + + autochop target 500 + autochop clearcut CaravanPath,OuterWall + enable autochop + +Commands +-------- + +``status`` + Show current configuration and statistics, including tree cutting quota + agreement status. + +``designate`` + Designate trees for chopping right now according to the current + configuration. This works even if ``autochop`` is not currently enabled. + +``undesignate`` + Undesignates all trees. + +``target []`` + Set the target range for the number of logs you want to have in stock. If a + minimum amount is not specified, it defaults to 20% less than the maximum. + The default target is ``200`` (with a corresponding minimum of ``160``). -Then, open the settings menu with :kbd:`c` from the designations menu (the -option appears when you have "Chop Down Trees" selected with :kbd:`d`-:kbd:`t`). +``quota (abide|ignore)`` + Choose whether to abide by or ignore tree cutting quota agreements that you + may have signed with friendly elven civilizations. By default, ``autochop`` + will abide by any agreements you have made. -Set your desired thresholds and enable autochopping with :kbd:`a`. +``restrict (set|add|remove) [,...]`` + Instead of choosing trees across the whole game map, restrict tree cutting + to the given burrows. Burrows can be specified by name or internal ID. -You can also restrict autochopping to specific burrows. Highlight a burrow name -with the Up/Down arrow keys and hit :kbd:`Enter` to mark it as the autochop -burrrow. +``clearcut (set|add|remove) [,...]`` + Ensure the given burrows are always clear of trees. As soon as a tree + appears in any of these burrows, it is designated for chopping. -Autochop checks your stock of logs and designates trees once every in game day. +``protect [,...]``, ``unprotect [,...]`` + Choose whether to exclude trees from chopping that produce any of the given + types of food. Valid types are: ``brewable``, ``edible``, and ``cookable``. From 3e11a31070f1d49a68c49dca8b8938fa26468b2a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 9 Jan 2023 07:15:10 +0000 Subject: [PATCH 0077/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 82833e9ca..d3d87c864 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 82833e9ca2f91034237f01b76c03580e1de9a8eb +Subproject commit d3d87c864388eb44fc3f1f5d4b668ff854cd5e68 From 2877f87d78c2ffaae56b272a105b13a1a603268d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 6 Jan 2023 10:08:50 -0800 Subject: [PATCH 0078/2222] implement new autochop --- docs/plugins/autochop.rst | 48 +- plugins/CMakeLists.txt | 2 +- plugins/autochop.cpp | 1455 ++++++++++++++++++------------------- plugins/lua/autochop.lua | 111 +++ 4 files changed, 835 insertions(+), 781 deletions(-) create mode 100644 plugins/lua/autochop.lua diff --git a/docs/plugins/autochop.rst b/docs/plugins/autochop.rst index 51f9c17df..7ac95f588 100644 --- a/docs/plugins/autochop.rst +++ b/docs/plugins/autochop.rst @@ -13,8 +13,7 @@ close to your walls, giving invaders an unexpected path into your fort. Autochop checks your stock of logs and designates appropriate trees for chopping once every in-game day. Logs that are forbidden or inaccessible (e.g. in hidden parts of the map, under water, etc.) are not counted towards your target. Trees -that are inaccessible are likewise never designated. Tree cutting quota -agreements that you have made are respected (by default). +that are inaccessible are likewise never designated. Please see `gui/autochop` for the interactive configuration dialog. @@ -27,28 +26,39 @@ Usage autochop [status] autochop (designate|undesignate) autochop target [] - autochop quota (abide|ignore) - autochop restrict (set|add|remove) [,...] - autochop clearcut (set|add|remove) [,...] - autochop (protect|unprotect) [,...] + autochop (chop|nochop) [,...] + autochop (clearcut|noclearcut) [,...] + autochop (protect|unprotect) [,...] [,...] Examples -------- -Ensure we always have about 500 logs in stock, harvested from anywhere on the -map. Also ensure the caravan pathway and the area around the outer wall is -always clear (requires existence of appropriately-named burrows):: +Ensure we always have about 200 logs in stock, harvested from accessible trees +anywhere on the map:: + enable autochop + +Ensure we always have about 500 logs in stock, harvested from a burrow named +"TreeFarm". Also ensure the caravan pathway and the area around the outer wall +("CaravanPath" and "OuterWall") are always clear:: + + enable autochop autochop target 500 + autochop chop TreeFarm autochop clearcut CaravanPath,OuterWall - enable autochop + +Clear all non-food-producing trees out of a burrow ("PicnicArea") intended to +contain only food-producing trees:: + + autochop clearcut PicnicArea + autochop protect brewable,edible,cookable PicnicArea + autochop designate Commands -------- ``status`` - Show current configuration and statistics, including tree cutting quota - agreement status. + Show current configuration and relevant statistics. ``designate`` Designate trees for chopping right now according to the current @@ -62,19 +72,15 @@ Commands minimum amount is not specified, it defaults to 20% less than the maximum. The default target is ``200`` (with a corresponding minimum of ``160``). -``quota (abide|ignore)`` - Choose whether to abide by or ignore tree cutting quota agreements that you - may have signed with friendly elven civilizations. By default, ``autochop`` - will abide by any agreements you have made. - -``restrict (set|add|remove) [,...]`` +``(no)chop [,...]`` Instead of choosing trees across the whole game map, restrict tree cutting to the given burrows. Burrows can be specified by name or internal ID. -``clearcut (set|add|remove) [,...]`` +``(no)clearcut [,...]`` Ensure the given burrows are always clear of trees. As soon as a tree - appears in any of these burrows, it is designated for chopping. + appears in any of these burrows, it is designated for chopping at priority + 2. -``protect [,...]``, ``unprotect [,...]`` +``(un)protect [,...] [,...]`` Choose whether to exclude trees from chopping that produce any of the given types of food. Valid types are: ``brewable``, ``edible``, and ``cookable``. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2b2f3aad5..1087b846a 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -81,7 +81,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) #dfhack_plugin(3dveins 3dveins.cpp) #dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) -#dfhack_plugin(autochop autochop.cpp) +dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) #dfhack_plugin(autoclothing autoclothing.cpp) #dfhack_plugin(autodump autodump.cpp) #dfhack_plugin(autofarm autofarm.cpp) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 415a6d322..046038330 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -1,287 +1,329 @@ // automatically chop trees -#include "uicommon.h" -#include "listcolumn.h" - -#include "Core.h" -#include "Console.h" -#include "Export.h" +#include "Debug.h" +#include "LuaTools.h" #include "PluginManager.h" -#include "DataDefs.h" #include "TileTypes.h" +#include "modules/Burrows.h" +#include "modules/Designations.h" +#include "modules/Maps.h" +#include "modules/Persistence.h" +#include "modules/Units.h" +#include "modules/World.h" + #include "df/burrow.h" #include "df/item.h" -#include "df/item_flags.h" -#include "df/items_other_id.h" -#include "df/job.h" #include "df/map_block.h" -#include "df/material.h" #include "df/plant.h" #include "df/plant_tree_info.h" #include "df/plant_tree_tile.h" -#include "df/plant_raw.h" -#include "df/tile_dig_designation.h" #include "df/plotinfost.h" -#include "df/viewscreen_dwarfmodest.h" #include "df/world.h" -#include "modules/Burrows.h" -#include "modules/Designations.h" -#include "modules/Gui.h" -#include "modules/MapCache.h" -#include "modules/Maps.h" -#include "modules/Screen.h" -#include "modules/World.h" - -#include +#include +#include +using std::map; +using std::multimap; +using std::pair; using std::string; +using std::unordered_map; using std::vector; -using std::set; + using namespace DFHack; using namespace df::enums; -#define PLUGIN_VERSION 0.3 DFHACK_PLUGIN("autochop"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(plotinfo); -static int get_log_count(); - -static bool autochop_enabled = false; -static int min_logs, max_logs; -static const int LOG_CAP_MAX = 99999; -static bool wait_for_threshold; -struct Skip { - bool fruit_trees; - bool food_trees; - bool cook_trees; - operator int() { - return (fruit_trees ? 1 : 0) | - (food_trees ? 2 : 0) | - (cook_trees ? 4 : 0); - } - Skip &operator= (int in) { - // set all fields to false if they haven't been set in this save yet - if (in < 0) - in = 0; - fruit_trees = (in & 1); - food_trees = (in & 2); - cook_trees = (in & 4); - return *this; - } +namespace DFHack { + // for configuration-related logging + DBG_DECLARE(autochop, status, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(autochop, cycle, DebugCategory::LINFO); +} + +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static const string BURROW_CONFIG_KEY_PREFIX = string(plugin_name) + "/burrow/"; +static PersistentDataItem config; +static vector watched_burrows; +static unordered_map watched_burrows_indices; + +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_MAX_LOGS = 1, + CONFIG_MIN_LOGS = 2, + CONFIG_WAITING_FOR_MIN = 3, }; -static Skip skip; -static PersistentDataItem config_autochop; +enum BurrowConfigValues { + BURROW_CONFIG_ID = 0, + BURROW_CONFIG_CHOP = 1, + BURROW_CONFIG_CLEARCUT = 2, + BURROW_CONFIG_PROTECT_BREWABLE = 3, + BURROW_CONFIG_PROTECT_EDIBLE = 4, + BURROW_CONFIG_PROTECT_COOKABLE = 5, +}; -struct WatchedBurrow -{ - int32_t id; - df::burrow *burrow; +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} - WatchedBurrow(df::burrow *burrow) : burrow(burrow) - { - id = burrow->id; +static PersistentDataItem & ensure_burrow_config(color_ostream &out, int id) { + if (watched_burrows_indices.count(id)) + return watched_burrows[watched_burrows_indices[id]]; + string keyname = BURROW_CONFIG_KEY_PREFIX + int_to_string(id); + DEBUG(status,out).print("creating new persistent key for burrow %d\n", id); + watched_burrows.emplace_back(World::GetPersistentData(keyname, NULL)); + size_t idx = watched_burrows.size()-1; + watched_burrows_indices.emplace(id, idx); + return watched_burrows[idx]; +} +static void remove_burrow_config(color_ostream &out, int id) { + if (!watched_burrows_indices.count(id)) + return; + DEBUG(status,out).print("removing persistent key for burrow %d\n", id); + size_t idx = watched_burrows_indices[id]; + World::DeletePersistentData(watched_burrows[idx]); + watched_burrows.erase(watched_burrows.begin()+idx); + watched_burrows_indices.erase(id); +} +static void validate_burrow_configs(color_ostream &out) { + for (int32_t idx = watched_burrows.size()-1; idx >=0; --idx) { + int id = get_config_val(watched_burrows[idx], BURROW_CONFIG_ID); + if (!df::burrow::find(id)) { + remove_burrow_config(out, id); + } } -}; +} -class WatchedBurrows -{ -public: - string getSerialisedIds() - { - validate(); - stringstream burrow_ids; - bool append_started = false; - for (auto it = burrows.begin(); it != burrows.end(); it++) - { - if (append_started) - burrow_ids << " "; - burrow_ids << it->id; - append_started = true; - } +static const int32_t CYCLE_TICKS = 1200; +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - return burrow_ids.str(); - } +static command_result do_command(color_ostream &out, vector ¶meters); +static int32_t do_cycle(color_ostream &out, bool force_designate = false); - void clear() - { - burrows.clear(); - } +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(status,out).print("initializing %s\n", plugin_name); - void add(const int32_t id) - { - if (!isValidBurrow(id)) - return; + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Auto-harvest trees when low on stockpiled logs.", + do_command)); - WatchedBurrow wb(getBurrow(id)); - burrows.push_back(wb); + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; } - void add(const string burrow_ids) - { - istringstream iss(burrow_ids); - int id; - while (iss >> id) - { - add(id); - } + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(status,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } else { + DEBUG(status,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); } + return CR_OK; +} - bool isValidPos(const df::coord &plant_pos) - { - validate(); - if (!burrows.size()) - return true; +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(status,out).print("shutting down %s\n", plugin_name); - for (auto it = burrows.begin(); it != burrows.end(); it++) - { - df::burrow *burrow = it->burrow; - if (Burrows::isAssignedTile(burrow, plant_pos)) - return true; - } + return CR_OK; +} - return false; - } +DFhackCExport command_result plugin_load_data (color_ostream &out) { + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) { + DEBUG(status,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + set_config_val(config, CONFIG_MAX_LOGS, 200); + set_config_val(config, CONFIG_MIN_LOGS, 160); + set_config_bool(config, CONFIG_WAITING_FOR_MIN, false); + } + + // we have to copy our enabled flag into the global plugin variable, but + // all the other state we can directly read/modify from the persistent + // data structure. + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(status,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + World::GetPersistentData(&watched_burrows, BURROW_CONFIG_KEY_PREFIX, true); + watched_burrows_indices.clear(); + const size_t num_watched_burrows = watched_burrows.size(); + for (size_t idx = 0; idx < num_watched_burrows; ++idx) { + auto &c = watched_burrows[idx]; + watched_burrows_indices.emplace(get_config_val(c, BURROW_CONFIG_ID), idx); + } + validate_burrow_configs(out); - bool isBurrowWatched(const df::burrow *burrow) - { - validate(); - for (auto it = burrows.begin(); it != burrows.end(); it++) - { - if (it->burrow == burrow) - return true; - } + return CR_OK; +} - return false; +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(status,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; + } } + return CR_OK; +} -private: - static bool isValidBurrow(const int32_t id) - { - return getBurrow(id); +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int32_t designated = do_cycle(out); + if (0 < designated) + out.print("autochop: designated %d trees for chopping\n", designated); } + return CR_OK; +} - static df::burrow *getBurrow(const int32_t id) - { - return df::burrow::find(id); - } +static bool call_autochop_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(status).print("calling autochop lua function: '%s'\n", fn_name); - void validate() - { - for (auto it = burrows.begin(); it != burrows.end();) - { - if (!isValidBurrow(it->id)) - it = burrows.erase(it); - else - ++it; - } - } + CoreSuspender guard; - vector burrows; -}; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); -static WatchedBurrows watchedBurrows; + if (!out) + out = &Core::getInstance().getConsole(); -static void save_config() -{ - config_autochop.val() = watchedBurrows.getSerialisedIds(); - config_autochop.ival(0) = autochop_enabled; - config_autochop.ival(1) = min_logs; - config_autochop.ival(2) = max_logs; - config_autochop.ival(3) = wait_for_threshold; - config_autochop.ival(4) = skip; + return Lua::CallLuaModuleFunction(*out, L, "plugins.autochop", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); } -static void initialize() -{ - watchedBurrows.clear(); - autochop_enabled = false; - min_logs = 80; - max_logs = 100; - wait_for_threshold = false; - skip = 0; - - config_autochop = World::GetPersistentData("autochop/config"); - if (config_autochop.isValid()) - { - watchedBurrows.add(config_autochop.val()); - autochop_enabled = config_autochop.ival(0); - min_logs = config_autochop.ival(1); - max_logs = config_autochop.ival(2); - wait_for_threshold = config_autochop.ival(3); - skip = config_autochop.ival(4); +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; } - else - { - config_autochop = World::AddPersistentData("autochop/config"); - if (config_autochop.isValid()) - save_config(); + + bool show_help = false; + if (!call_autochop_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; } + + return show_help ? CR_WRONG_USAGE : CR_OK; } -static bool skip_plant(const df::plant * plant, bool *restricted) -{ - if (restricted) - *restricted = false; +///////////////////////////////////////////////////// +// cycle logic +// + +static bool is_accessible_item(const df::coord &pos, const vector &citizens) { + for (auto &unit : citizens) { + if (Maps::canWalkBetween(unit->pos, pos)) + return true; + } + return false; +} + +// at least one member of the fort can reach a position adjacent to the given pos +static bool is_accessible_tree(const df::coord &pos, const vector &citizens) { + for (auto &unit : citizens) { + if (Maps::canWalkBetween(unit->pos, df::coord(pos.x-1, pos.y-1, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x, pos.y-1, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x+1, pos.y-1, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x-1, pos.y, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x+1, pos.y, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x-1, pos.y+1, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x, pos.y+1, pos.z)) + || Maps::canWalkBetween(unit->pos, df::coord(pos.x+1, pos.y+1, pos.z))) + return true; + } + return false; +} +static bool is_valid_tree(const df::plant *plant) { // Skip all non-trees immediately. if (plant->flags.bits.is_shrub) - return true; + return false; // Skip plants with invalid tile. - df::map_block *cur = Maps::getTileBlock(plant->pos); - if (!cur) - return true; + df::map_block *block = Maps::getTileBlock(plant->pos); + if (!block) + return false; int x = plant->pos.x % 16; int y = plant->pos.y % 16; // Skip all unrevealed plants. - if (cur->designation[x][y].bits.hidden) - return true; + if (block->designation[x][y].bits.hidden) + return false; - df::tiletype_material material = tileMaterial(cur->tiletype[x][y]); - if (material != tiletype_material::TREE) - return true; + if (tileMaterial(block->tiletype[x][y]) != tiletype_material::TREE) + return false; + + return true; +} +static bool is_protected(const df::plant * plant, PersistentDataItem &c) { const df::plant_raw *plant_raw = df::plant_raw::find(plant->material); - // Skip fruit trees if set. - if (skip.fruit_trees && plant_raw->material_defs.type[plant_material_def::drink] != -1) - { - if (restricted) - *restricted = true; + bool protect_brewable = get_config_bool(c, BURROW_CONFIG_PROTECT_BREWABLE); + bool protect_edible = get_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE); + bool protect_cookable = get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE); + + if (protect_brewable && plant_raw->material_defs.type[plant_material_def::drink] != -1) return true; - } - if (skip.food_trees || skip.cook_trees) - { - for (df::material * mat : plant_raw->material) - { - if (skip.food_trees && mat->flags.is_set(material_flags::EDIBLE_RAW)) - { - if (restricted) - *restricted = true; + if (protect_edible || protect_cookable) { + for (df::material * mat : plant_raw->material) { + if (protect_edible && mat->flags.is_set(material_flags::EDIBLE_RAW)) return true; - } - if (skip.cook_trees && mat->flags.is_set(material_flags::EDIBLE_COOKED)) - { - if (restricted) - *restricted = true; + if (protect_cookable && mat->flags.is_set(material_flags::EDIBLE_COOKED)) return true; - } } } return false; } -static int estimate_logs(const df::plant *plant) -{ +static int32_t estimate_logs(const df::plant *plant) { //adapted from code by aljohnston112 @ github df::plant_tree_tile** tiles = plant->tree_info->body; df::plant_tree_tile* tilesRow; @@ -297,88 +339,130 @@ static int estimate_logs(const df::plant *plant) return trunks; } -static int do_chop_designation(bool chop, bool count_only, int *skipped = nullptr) -{ - int count = 0; - int estimated_yield = get_log_count(); - multimap> trees_by_size; - - if (skipped) - { - *skipped = 0; - } - - //get trees - for (auto plant : world->plants.all) - { - bool restricted = false; +static void bucket_tree(df::plant *plant, bool designate_clearcut, bool *designated, bool *can_chop, + map *tree_counts, map *designated_tree_counts, + map &clearcut_burrows, + map &chop_burrows) { + for (auto &burrow : plotinfo->burrows.list) { + if (!Burrows::isAssignedTile(burrow, plant->pos)) + continue; - if (skip_plant(plant, &restricted)) - { - if (restricted && skipped) - { - ++*skipped; + int id = burrow->id; + if (tree_counts) + ++(*tree_counts)[id]; + + if (*designated) { + if (designated_tree_counts) + ++(*designated_tree_counts)[id]; + } else if (clearcut_burrows.count(id) && !is_protected(plant, *clearcut_burrows[id])) { + if (designate_clearcut && Designations::markPlant(plant)) { + *designated = true; + if (designated_tree_counts) + ++(*designated_tree_counts)[id]; } - continue; + } else if (chop_burrows.count(id) && !is_protected(plant, *chop_burrows[id])) { + *can_chop = true; } - - trees_by_size.insert(pair(estimate_logs(plant), plant)); } - //designate - for (auto & entry : trees_by_size) - { - const df::plant * plant = entry.second; + if (!*designated && chop_burrows.empty()) + *can_chop = true; +} - if ((estimated_yield >= max_logs) && chop) - break; +static void get_citizens(vector &vec) { + for (auto &unit : world->units.active) { + if (Units::isCitizen(unit)) + vec.emplace_back(unit); + } +} + +static void bucket_watched_burrows(color_ostream & out, + map &clearcut_burrows, + map &chop_burrows) { + for (auto &c : watched_burrows) { + int id = get_config_val(c, BURROW_CONFIG_ID); + if (get_config_bool(c, BURROW_CONFIG_CLEARCUT)) + clearcut_burrows.emplace(id, &c); + else if (get_config_bool(c, BURROW_CONFIG_CHOP)) + chop_burrows.emplace(id, &c); + } +} - if (!count_only && !watchedBurrows.isValidPos(plant->pos)) +typedef multimap> TreesBySize; + +// returns the number of trees that were newly marked +static int32_t scan_trees(color_ostream & out, int32_t *expected_yield, + TreesBySize *designatable_trees_by_size, bool designate_clearcut, + const vector &citizens, int32_t *accessible_trees = NULL, + int32_t *inaccessible_trees = NULL, int32_t *designated_trees = NULL, + int32_t *accessible_yield = NULL, + map *tree_counts = NULL, + map *designated_tree_counts = NULL) { + int32_t newly_marked = 0; + + if (accessible_trees) + *accessible_trees = 0; + if (inaccessible_trees) + *inaccessible_trees = 0; + if (designated_trees) + *designated_trees = 0; + if (expected_yield) + *expected_yield = 0; + if (accessible_yield) + *accessible_yield = 0; + if (tree_counts) + tree_counts->clear(); + if (designated_tree_counts) + designated_tree_counts->clear(); + + map clearcut_burrows, chop_burrows; + bucket_watched_burrows(out, clearcut_burrows, chop_burrows); + + for (auto plant : world->plants.all) { + if (!is_valid_tree(plant)) continue; - if (chop && !Designations::isPlantMarked(plant)) - { - if (count_only) - { - if (Designations::canMarkPlant(plant)) - count++; - } - else - { - if (Designations::markPlant(plant)) - { - estimated_yield += entry.first; - count++; - } - } + bool accessible = is_accessible_tree(plant->pos, citizens); + int32_t yield = estimate_logs(plant); + + if (accessible) { + if (accessible_trees) + ++*accessible_trees; + if (accessible_yield) + *accessible_yield += yield; + } else { + if (inaccessible_trees) + ++*inaccessible_trees; } - if (!chop && Designations::isPlantMarked(plant)) - { - if (count_only) - { - if (Designations::canUnmarkPlant(plant)) - count++; - } - else - { - if (Designations::unmarkPlant(plant)) - count++; - } + bool can_chop = false; + bool designated = Designations::isPlantMarked(plant); + bool was_designated = designated; + bucket_tree(plant, designate_clearcut, &designated, &can_chop, tree_counts, + designated_tree_counts, clearcut_burrows, chop_burrows); + + if (designated) { + if (!was_designated) + ++newly_marked; + if (designated_trees) + ++*designated_trees; + if (expected_yield) + *expected_yield += yield; + } else if (can_chop && accessible) { + if (designatable_trees_by_size) + designatable_trees_by_size->emplace(yield, plant); } } - return count; + return newly_marked; } -static bool is_valid_item(df::item *item) -{ - for (size_t i = 0; i < item->general_refs.size(); i++) - { +// TODO: does this actually catch anything above the bad_flag check? +static bool is_valid_item(df::item *item) { + for (size_t i = 0; i < item->general_refs.size(); i++) { df::general_ref *ref = item->general_refs[i]; - switch (ref->getType()) - { + switch (ref->getType()) { case general_ref_type::CONTAINED_IN_ITEM: return false; @@ -393,12 +477,10 @@ static bool is_valid_item(df::item *item) } } - for (size_t i = 0; i < item->specific_refs.size(); i++) - { + for (size_t i = 0; i < item->specific_refs.size(); i++) { df::specific_ref *ref = item->specific_refs[i]; - if (ref->type == specific_ref_type::JOB) - { + if (ref->type == specific_ref_type::JOB) { // Ignore any items assigned to a job return false; } @@ -407,544 +489,399 @@ static bool is_valid_item(df::item *item) return true; } -static int get_log_count() +struct BadFlags { - std::vector &items = world->items.other[items_other_id::IN_PLAY]; - - // Pre-compute a bitmask with the bad flags - df::item_flags bad_flags; - bad_flags.whole = 0; + uint32_t whole; -#define F(x) bad_flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); - F(spider_web); F(owned); F(in_job); -#undef F - - size_t valid_count = 0; - for (size_t i = 0; i < items.size(); i++) + BadFlags() { - df::item *item = items[i]; + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(artifact); + F(in_job); F(owned); F(in_chest); F(removed); + F(encased); F(spider_web); + #undef F + whole = flags.whole; + } +}; - if (item->getType() != item_type::WOOD) - continue; +static void scan_logs(int32_t *usable_logs, const vector &citizens, int32_t *inaccessible_logs = NULL) { + static const BadFlags bad_flags; + + if (usable_logs) + *usable_logs = 0; + if (inaccessible_logs) + *inaccessible_logs = 0; + for (auto &item : world->items.other[items_other_id::IN_PLAY]) { if (item->flags.whole & bad_flags.whole) continue; - if (!is_valid_item(item)) + if (item->getType() != item_type::WOOD) continue; - ++valid_count; - } - - return valid_count; -} - -static void set_threshold_check(bool state) -{ - wait_for_threshold = state; - save_config(); -} - -static void do_autochop() -{ - int log_count = get_log_count(); - if (wait_for_threshold) - { - if (log_count < min_logs) - { - set_threshold_check(false); - do_chop_designation(true, false); - } - } - else - { - if (log_count >= max_logs) - { - set_threshold_check(true); - do_chop_designation(false, false); + if (!is_valid_item(item)) { + INFO(status).print("autochop is_valid_item actually caught something useful!! Please tell the DFHack team.\n"); + continue; } - else - { - do_chop_designation(true, false); + + if (!is_accessible_item(item->pos, citizens)) { + if (inaccessible_logs) + ++*inaccessible_logs; + } else if (usable_logs) { + ++*usable_logs; } } } -class ViewscreenAutochop : public dfhack_viewscreen -{ -public: - ViewscreenAutochop(): - selected_column(0), - current_log_count(0), - marked_tree_count(0), - skipped_tree_count(0) - { - edit_mode = EDIT_NONE; - burrows_column.multiselect = true; - burrows_column.setTitle("Burrows"); - burrows_column.bottom_margin = 3; - burrows_column.allow_search = false; - burrows_column.text_clip_at = 30; +static int32_t do_cycle(color_ostream &out, bool force_designate) { + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); - populateBurrowsColumn(); - message.clear(); - } + // mark that we have recently run + cycle_timestamp = world->frame_counter; - void populateBurrowsColumn() - { - selected_column = 0; - - burrows_column.clear(); - - for (df::burrow *burrow : plotinfo->burrows.list) - { - string name = burrow->name; - if (name.empty()) - name = "Burrow " + int_to_string(burrow->id + 1); - auto elem = ListEntry(name, burrow); - elem.selected = watchedBurrows.isBurrowWatched(burrow); - burrows_column.add(elem); - } + validate_burrow_configs(out); - burrows_column.fixWidth(); - burrows_column.filterDisplay(); + // scan trees and clearcut marked burrows + int32_t expected_yield; + TreesBySize designatable_trees_by_size; + vector citizens; + get_citizens(citizens); + int32_t newly_marked = scan_trees(out, &expected_yield, + &designatable_trees_by_size, true, citizens); - current_log_count = get_log_count(); - marked_tree_count = do_chop_designation(false, true, &skipped_tree_count); - } - - void change_min_logs(int delta) - { - if (!autochop_enabled) - return; + // check how many logs we have already + int32_t usable_logs; + scan_logs(&usable_logs, citizens); - min_logs += delta; - if (min_logs < 0) - min_logs = 0; - if (min_logs > max_logs) - max_logs = min_logs; + if (get_config_bool(config, CONFIG_WAITING_FOR_MIN) + && usable_logs <= get_config_val(config, CONFIG_MIN_LOGS)) { + DEBUG(cycle,out).print("minimum threshold crossed\n"); + set_config_bool(config, CONFIG_WAITING_FOR_MIN, false); } - - void change_max_logs(int delta) - { - if (!autochop_enabled) - return; - - max_logs += delta; - if (max_logs < min_logs) - min_logs = max_logs; + else if (!get_config_bool(config, CONFIG_WAITING_FOR_MIN) + && usable_logs > get_config_val(config, CONFIG_MAX_LOGS)) { + DEBUG(cycle,out).print("maximum threshold crossed\n"); + set_config_bool(config, CONFIG_WAITING_FOR_MIN, true); } - void feed(set *input) - { - if (edit_mode != EDIT_NONE) - { - string entry = int_to_string(edit_mode == EDIT_MIN ? min_logs : max_logs); - if (input->count(interface_key::LEAVESCREEN) || input->count(interface_key::SELECT)) - { - if (edit_mode == EDIT_MIN) - max_logs = std::max(min_logs, max_logs); - else if (edit_mode == EDIT_MAX) - min_logs = std::min(min_logs, max_logs); - edit_mode = EDIT_NONE; - } - else if (input->count(interface_key::STRING_A000)) - { - if (!entry.empty()) - entry.erase(entry.size() - 1); - } - else if (entry.size() < 5) - { - for (auto k = input->begin(); k != input->end(); ++k) - { - char ch = char(Screen::keyToChar(*k)); - if (ch >= '0' && ch <= '9') - entry += ch; - } - } - - switch (edit_mode) - { - case EDIT_MIN: - min_logs = string_to_int(entry); - break; - case EDIT_MAX: - max_logs = string_to_int(entry); - break; - default: break; - } - - return; - } - - bool key_processed = false; - message.clear(); - switch (selected_column) - { - case 0: - key_processed = burrows_column.feed(input); - break; - } - - if (key_processed) - { - if (input->count(interface_key::SELECT)) - updateAutochopBurrows(); - return; - } - - if (input->count(interface_key::LEAVESCREEN)) - { - save_config(); - input->clear(); - Screen::dismiss(this); - if (autochop_enabled) - do_autochop(); - return; - } - else if (input->count(interface_key::CUSTOM_A)) - { - autochop_enabled = !autochop_enabled; - } - else if (input->count(interface_key::CUSTOM_D)) - { - int count = do_chop_designation(true, false); - message = "Trees marked for chop: " + int_to_string(count); - marked_tree_count = do_chop_designation(false, true, &skipped_tree_count); - if (skipped_tree_count) - { - message += ", skipped: " + int_to_string(skipped_tree_count); - } - } - else if (input->count(interface_key::CUSTOM_U)) - { - int count = do_chop_designation(false, false); - message = "Trees unmarked: " + int_to_string(count); - marked_tree_count = do_chop_designation(false, true, &skipped_tree_count); - if (skipped_tree_count) - { - message += ", skipped: " + int_to_string(skipped_tree_count); - } - } - else if (input->count(interface_key::CUSTOM_N)) - { - edit_mode = EDIT_MIN; - } - else if (input->count(interface_key::CUSTOM_M)) - { - edit_mode = EDIT_MAX; - } - else if (input->count(interface_key::CUSTOM_SHIFT_N)) - { - min_logs = LOG_CAP_MAX + 1; - max_logs = LOG_CAP_MAX + 1; - } - else if (input->count(interface_key::CUSTOM_H)) - { - change_min_logs(-1); - } - else if (input->count(interface_key::CUSTOM_SHIFT_H)) - { - change_min_logs(-10); - } - else if (input->count(interface_key::CUSTOM_J)) - { - change_min_logs(1); - } - else if (input->count(interface_key::CUSTOM_SHIFT_J)) - { - change_min_logs(10); - } - else if (input->count(interface_key::CUSTOM_K)) - { - change_max_logs(-1); - } - else if (input->count(interface_key::CUSTOM_SHIFT_K)) - { - change_max_logs(-10); - } - else if (input->count(interface_key::CUSTOM_L)) - { - change_max_logs(1); - } - else if (input->count(interface_key::CUSTOM_SHIFT_L)) - { - change_max_logs(10); - } - else if (input->count(interface_key::CUSTOM_F)) - { - skip.fruit_trees = !skip.fruit_trees; - } - else if (input->count(interface_key::CUSTOM_E)) - { - skip.food_trees = !skip.food_trees; - } - else if (input->count(interface_key::CUSTOM_C)) - { - skip.cook_trees = !skip.cook_trees; - } - else if (enabler->tracking_on && enabler->mouse_lbut) - { - if (burrows_column.setHighlightByMouse()) - { - selected_column = 0; - } - - enabler->mouse_lbut = enabler->mouse_rbut = 0; - } + // if we already have designated enough, we're done + int32_t limit = force_designate || !get_config_bool(config, CONFIG_WAITING_FOR_MIN) ? + get_config_val(config, CONFIG_MAX_LOGS) : + get_config_val(config, CONFIG_MIN_LOGS); + if (usable_logs + expected_yield > limit) { + return newly_marked; } - void render() - { - if (Screen::isDismissed(this)) - return; - - dfhack_viewscreen::render(); - - Screen::clear(); - Screen::drawBorder(" Autochop "); - - burrows_column.display(selected_column == 0); - - int32_t y = gps->dimy - 3; - int32_t x = 2; - OutputHotkeyString(x, y, "Leave", "Esc"); - x += 3; - OutputString(COLOR_YELLOW, x, y, message); - - y = 3; - int32_t left_margin = burrows_column.getMaxItemWidth() + 3; - x = left_margin; - if (burrows_column.getSelectedElems().size() > 0) - { - OutputString(COLOR_GREEN, x, y, "Will chop in selected burrows", true, left_margin); - ++y; - } - else - { - OutputString(COLOR_YELLOW, x, y, "Will chop from whole map", true, left_margin); - OutputString(COLOR_YELLOW, x, y, "Select from left to chop in specific burrows", true, left_margin); - } - - ++y; - - using namespace df::enums::interface_key; - OutputToggleString(x, y, "Autochop", CUSTOM_A, autochop_enabled, true, left_margin); - OutputHotkeyString(x, y, "Designate Now", CUSTOM_D, true, left_margin); - OutputHotkeyString(x, y, "Undesignate Now", CUSTOM_U, true, left_margin); - OutputHotkeyString(x, y, "Toggle Burrow", "Enter", true, left_margin); - if (autochop_enabled) - { - const struct { - const char *caption; - int count; - bool in_edit; - df::interface_key key; - df::interface_key skeys[4]; - } rows[] = { - {"Min Logs: ", min_logs, edit_mode == EDIT_MIN, CUSTOM_N, {CUSTOM_H, CUSTOM_J, CUSTOM_SHIFT_H, CUSTOM_SHIFT_J}}, - {"Max Logs: ", max_logs, edit_mode == EDIT_MAX, CUSTOM_M, {CUSTOM_K, CUSTOM_L, CUSTOM_SHIFT_K, CUSTOM_SHIFT_L}} - }; - for (size_t i = 0; i < sizeof(rows) / sizeof(rows[0]); ++i) - { - auto row = rows[i]; - OutputHotkeyString(x, y, row.caption, row.key); - auto prev_x = x; - if (row.in_edit) - OutputString(COLOR_LIGHTCYAN, x, y, int_to_string(row.count) + "_"); - else if (row.count <= LOG_CAP_MAX) - OutputString(COLOR_LIGHTGREEN, x, y, int_to_string(row.count)); - else - OutputString(COLOR_LIGHTBLUE, x, y, "Unlimited"); - if (edit_mode == EDIT_NONE) - { - x = std::max(x, prev_x + 10); - for (size_t j = 0; j < sizeof(row.skeys) / sizeof(row.skeys[0]); ++j) - OutputString(COLOR_LIGHTGREEN, x, y, DFHack::Screen::getKeyDisplay(row.skeys[j])); - OutputString(COLOR_WHITE, x, y, ": Step"); - } - OutputString(COLOR_WHITE, x, y, "", true, left_margin); - } - OutputHotkeyString(x, y, "No limit", CUSTOM_SHIFT_N, true, left_margin); - OutputToggleString(x, y, "Skip Fruit Trees", CUSTOM_F, skip.fruit_trees, true, left_margin); - OutputToggleString(x, y, "Skip Edible Product Trees", CUSTOM_E, skip.food_trees, true, left_margin); - OutputToggleString(x, y, "Skip Cookable Product Trees", CUSTOM_C, skip.cook_trees, true, left_margin); + // designate until the expected yield gets us to our target or we run out + // of accessible trees + int32_t needed = get_config_val(config, CONFIG_MAX_LOGS) - + (usable_logs + expected_yield); + DEBUG(cycle,out).print("needed logs for this cycle: %d\n", needed); + for (auto & entry : designatable_trees_by_size) { + if (!Designations::markPlant(entry.second)) + continue; + ++newly_marked; + needed -= entry.first; + if (needed <= 0) { + return newly_marked; } - - ++y; - OutputString(COLOR_BROWN, x, y, "Current Counts", true, left_margin); - OutputString(COLOR_WHITE, x, y, "Current Logs: "); - OutputString(COLOR_GREEN, x, y, int_to_string(current_log_count), true, left_margin); - OutputString(COLOR_WHITE, x, y, "Marked Trees: "); - OutputString(COLOR_GREEN, x, y, int_to_string(marked_tree_count), true, left_margin); - } - - std::string getFocusString() { return "autochop"; } - - void updateAutochopBurrows() - { - watchedBurrows.clear(); - vector v = burrows_column.getSelectedElems(); - for_each_(v, [] (df::burrow *b) { watchedBurrows.add(b->id); }); - } - -private: - ListColumn burrows_column; - int selected_column; - int current_log_count; - int marked_tree_count; - int skipped_tree_count; - MapExtras::MapCache mcache; - string message; - enum { EDIT_NONE, EDIT_MIN, EDIT_MAX } edit_mode; - - void validateColumn() - { - set_to_limit(selected_column, 0); } + out.print("autochop: insufficient accessible trees to reach log target! Still need %d logs!\n", + needed); + return newly_marked; +} - void resize(int32_t x, int32_t y) - { - dfhack_viewscreen::resize(x, y); - burrows_column.resize(); - } -}; - -struct autochop_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; +///////////////////////////////////////////////////// +// Lua API +// core will already be suspended when coming in through here +// + +static const char * get_protect_str(bool protect_brewable, bool protect_edible, bool protect_cookable) { + if (!protect_brewable && !protect_edible && !protect_cookable) + return " "; + if (!protect_brewable && !protect_edible && protect_cookable) + return " c"; + if (!protect_brewable && protect_edible && !protect_cookable) + return " e "; + if (!protect_brewable && protect_edible && protect_cookable) + return " ec"; + if (protect_brewable && !protect_edible && !protect_cookable) + return "b "; + if (protect_brewable && !protect_edible && protect_cookable) + return "b c"; + if (protect_brewable && protect_edible && !protect_cookable) + return "be "; + if (protect_brewable && protect_edible && protect_cookable) + return "bec"; + return ""; +} - bool isInDesignationMenu() - { - using namespace df::enums::ui_sidebar_mode; - return (plotinfo->main.mode == DesignateChopTrees); +static void autochop_printStatus(color_ostream &out) { + DEBUG(status,out).print("entering autochop_printStatus\n"); + validate_burrow_configs(out); + out.print("autochop is %s\n\n", is_enabled ? "enabled" : "disabled"); + out.print(" keeping log counts between %d and %d\n", + get_config_val(config, CONFIG_MIN_LOGS), get_config_val(config, CONFIG_MAX_LOGS)); + if (get_config_bool(config, CONFIG_WAITING_FOR_MIN)) + out.print(" currently waiting for min threshold to be crossed before designating more trees\n"); + else + out.print(" currently designating trees until max threshold is crossed\n"); + out.print("\n"); + + int32_t usable_logs, inaccessible_logs; + int32_t accessible_trees, inaccessible_trees; + int32_t designated_trees, expected_yield, accessible_yield; + map tree_counts, designated_tree_counts; + vector citizens; + get_citizens(citizens); + scan_logs(&usable_logs, citizens, &inaccessible_logs); + scan_trees(out, &expected_yield, NULL, false, citizens, &accessible_trees, &inaccessible_trees, + &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); + + out.print("summary:\n"); + out.print(" accessible logs (usable stock): %d\n", usable_logs); + out.print(" inaccessible logs: %d\n", inaccessible_logs); + out.print(" total visible logs: %d\n", usable_logs + inaccessible_logs); + out.print("\n"); + out.print(" accessible trees: %d\n", accessible_trees); + out.print(" inaccessible trees: %d\n", inaccessible_trees); + out.print(" total visible trees: %d\n", accessible_trees + inaccessible_trees); + out.print("\n"); + out.print(" designated trees: %d\n", designated_trees); + out.print(" expected logs from designated trees: %d\n", expected_yield); + out.print(" expected logs from all accessible trees: %d\n", accessible_yield); + out.print("\n"); + out.print(" total trees harvested: %d\n", plotinfo->trees_removed); + out.print("\n"); + + if (!plotinfo->burrows.list.size()) { + out.print("no burrows defined\n"); + return; + } + + out.print("\n"); + + int name_width = 11; + for (auto &burrow : plotinfo->burrows.list) { + name_width = std::max(name_width, (int)burrow->name.size()); + } + name_width = -name_width; // left justify + + const char *fmt = "%*s %4s %4s %8s %5s %6s %7s\n"; + out.print(fmt, name_width, "burrow name", " id ", "chop", "clearcut", "trees", "marked", "protect"); + out.print(fmt, name_width, "-----------", "----", "----", "--------", "-----", "------", "-------"); + + for (auto &burrow : plotinfo->burrows.list) { + bool chop = false; + bool clearcut = false; + bool protect_brewable = false; + bool protect_edible = false; + bool protect_cookable = false; + if (watched_burrows_indices.count(burrow->id)) { + auto &c = watched_burrows[watched_burrows_indices[burrow->id]]; + chop = get_config_bool(c, BURROW_CONFIG_CHOP); + clearcut = get_config_bool(c, BURROW_CONFIG_CLEARCUT); + protect_brewable = get_config_bool(c, BURROW_CONFIG_PROTECT_BREWABLE); + protect_edible = get_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE); + protect_cookable = get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE); + } + out.print(fmt, name_width, burrow->name, int_to_string(burrow->id).c_str(), + chop ? "[x]" : "[ ]", clearcut ? "[x]" : "[ ]", + int_to_string(tree_counts[burrow->id]).c_str(), + int_to_string(designated_tree_counts[burrow->id]).c_str(), + get_protect_str(protect_brewable, protect_edible, protect_cookable)); } +} - void sendKey(const df::interface_key &key) - { - set tmp; - tmp.insert(key); - INTERPOSE_NEXT(feed)(&tmp); - } +static void autochop_designate(color_ostream &out) { + DEBUG(status,out).print("entering autochop_designate\n"); + out.print("designated %d tree(s) for chopping\n", do_cycle(out, true)); +} - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (isInDesignationMenu() && input->count(interface_key::CUSTOM_C)) - { - sendKey(interface_key::LEAVESCREEN); - Screen::show(dts::make_unique(), plugin_self); - } - else - { - INTERPOSE_NEXT(feed)(input); - } +static void autochop_undesignate(color_ostream &out) { + DEBUG(status,out).print("entering autochop_undesignate\n"); + int32_t count = 0; + for (auto plant : world->plants.all) { + if (is_valid_tree(plant) && Designations::unmarkPlant(plant)) + ++count; } + out.print("undesignated %d tree(s)\n", count); +} - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - auto dims = Gui::getDwarfmodeViewDims(); - if (dims.menu_x1 <= 0) - return; - - if (!isInDesignationMenu()) - return; - - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = 26; - OutputHotkeyString(x, y, "Autochop Dashboard", "c"); +static void autochop_setTargets(color_ostream &out, int32_t max_logs, int32_t min_logs) { + DEBUG(status,out).print("entering autochop_setTargets\n"); + if (max_logs < min_logs || min_logs < 0) { + out.printerr("max and min must be at least 0 and max must be greater than min\n"); + return; } -}; + set_config_val(config, CONFIG_MAX_LOGS, max_logs); + set_config_val(config, CONFIG_MIN_LOGS, min_logs); -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, feed, 100); -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(autochop_hook, render, 100); - - -command_result df_autochop (color_ostream &out, vector & parameters) -{ - for (size_t i = 0; i < parameters.size(); i++) - { - if (parameters[i] == "help" || parameters[i] == "?") - return CR_WRONG_USAGE; - if (parameters[i] == "debug") - save_config(); - else - return CR_WRONG_USAGE; - } - if (Maps::IsValid()) - Screen::show(dts::make_unique(), plugin_self); - return CR_OK; + // check limits and designate up to the new maximum + autochop_designate(out); } -DFhackCExport command_result plugin_onupdate (color_ostream &out) -{ - if (!autochop_enabled) - return CR_OK; - - if(!Maps::IsValid()) - return CR_OK; - - if (DFHack::World::ReadPauseState()) - return CR_OK; - - if (world->frame_counter % 1200 != 0) // Check every day - return CR_OK; - - do_autochop(); +static int autochop_getTargets(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering autochop_getTargets\n"); + Lua::Push(L, get_config_val(config, CONFIG_MAX_LOGS)); + Lua::Push(L, get_config_val(config, CONFIG_MIN_LOGS)); + return 2; +} - return CR_OK; +static int autochop_getLogCounts(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering autochop_getNumLogs\n"); + int32_t usable_logs, inaccessible_logs; + vector citizens; + get_citizens(citizens); + scan_logs(&usable_logs, citizens, &inaccessible_logs); + Lua::Push(L, usable_logs); + Lua::Push(L, inaccessible_logs); + return 2; } -DFHACK_PLUGIN_IS_ENABLED(is_enabled); +static void push_burrow_config(lua_State *L, int id, bool chop = false, + bool clearcut = false, bool protect_brewable = false, + bool protect_edible = false, bool protect_cookable = false) { + map burrow_config; + burrow_config.emplace("id", id); + burrow_config.emplace("chop", chop); + burrow_config.emplace("clearcut", clearcut); + burrow_config.emplace("protect_brewable", protect_brewable); + burrow_config.emplace("protect_edible", protect_edible); + burrow_config.emplace("protect_cookable", protect_cookable); + Lua::Push(L, burrow_config); +} -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (!gps) - return CR_FAILURE; +static void push_burrow_config(lua_State *L, PersistentDataItem &c) { + push_burrow_config(L, get_config_val(c, BURROW_CONFIG_ID), + get_config_bool(c, BURROW_CONFIG_CHOP), + get_config_bool(c, BURROW_CONFIG_CLEARCUT), + get_config_bool(c, BURROW_CONFIG_PROTECT_BREWABLE), + get_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE), + get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE)); +} - if (enable != is_enabled) - { - if (!INTERPOSE_HOOK(autochop_hook, feed).apply(enable) || - !INTERPOSE_HOOK(autochop_hook, render).apply(enable)) - return CR_FAILURE; +static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering autochop_getTreeCountsAndBurrowConfigs\n"); + validate_burrow_configs(*out); + int32_t accessible_trees, inaccessible_trees; + int32_t designated_trees, expected_yield, accessible_yield; + map tree_counts, designated_tree_counts; + vector citizens; + get_citizens(citizens); + scan_trees(*out, &expected_yield, NULL, false, citizens, &accessible_trees, &inaccessible_trees, + &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); + + map summary; + summary.emplace("accessible_trees", accessible_trees); + summary.emplace("inaccessible_trees", inaccessible_trees); + summary.emplace("designated_trees", designated_trees); + summary.emplace("expected_yield", expected_yield); + summary.emplace("accessible_yield", accessible_yield); + Lua::Push(L, summary); + + Lua::Push(L, tree_counts); + Lua::Push(L, designated_tree_counts); + + for (auto &burrow : plotinfo->burrows.list) { + int id = burrow->id; + if (watched_burrows_indices.count(id)) { + push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); + } else { + push_burrow_config(L, id); + } + } + + return 3 + plotinfo->burrows.list.size(); +} - is_enabled = enable; - initialize(); +static int autochop_getBurrowConfig(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering autochop_getBurrowConfig\n"); + validate_burrow_configs(*out); + // param can be a name or an id + int id; + if (lua_isnumber(L, -1)) { + id = lua_tointeger(L, -1); + if (!df::burrow::find(id)) + return 0; + } else { + const char * name = lua_tostring(L, -1); + if (!name) + return 0; + + string nameStr = name; + bool found = false; + for (auto &burrow : plotinfo->burrows.list) { + if (nameStr == burrow->name) { + id = burrow->id; + found = true; + break; + } + } + if (!found) + return 0; } - return CR_OK; + if (watched_burrows_indices.count(id)) { + push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); + } else { + push_burrow_config(L, id); + } + return 1; } -DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) -{ - commands.push_back(PluginCommand( - "autochop", - "Auto-harvest trees when low on stockpiled logs.", - df_autochop)); - - initialize(); - return CR_OK; -} +static void autochop_setBurrowConfig(color_ostream &out, int id, bool chop, + bool clearcut, bool protect_brewable, bool protect_edible, + bool protect_cookable) { + DEBUG(status,out).print("entering autochop_setBurrowConfig\n"); + validate_burrow_configs(out); -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} + bool isInvalidBurrow = !df::burrow::find(id); + bool hasNoData = !chop && !clearcut && !protect_brewable && !protect_edible + && !protect_cookable; -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_MAP_LOADED: - initialize(); - break; - default: - break; + if (isInvalidBurrow || hasNoData) { + remove_burrow_config(out, id); + return; } - return CR_OK; + PersistentDataItem &c = ensure_burrow_config(out, id); + set_config_val(c, BURROW_CONFIG_ID, id); + set_config_bool(c, BURROW_CONFIG_CHOP, chop); + set_config_bool(c, BURROW_CONFIG_CLEARCUT, clearcut); + set_config_bool(c, BURROW_CONFIG_PROTECT_BREWABLE, protect_brewable); + set_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE, protect_edible); + set_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE, protect_cookable); } + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(autochop_printStatus), + DFHACK_LUA_FUNCTION(autochop_designate), + DFHACK_LUA_FUNCTION(autochop_undesignate), + DFHACK_LUA_FUNCTION(autochop_setTargets), + DFHACK_LUA_FUNCTION(autochop_setBurrowConfig), + DFHACK_LUA_END +}; + +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(autochop_getTargets), + DFHACK_LUA_COMMAND(autochop_getLogCounts), + DFHACK_LUA_COMMAND(autochop_getBurrowConfig), + DFHACK_LUA_COMMAND(autochop_getTreeCountsAndBurrowConfigs), + DFHACK_LUA_END +}; diff --git a/plugins/lua/autochop.lua b/plugins/lua/autochop.lua new file mode 100644 index 000000000..e6f8e7008 --- /dev/null +++ b/plugins/lua/autochop.lua @@ -0,0 +1,111 @@ +local _ENV = mkmodule('plugins.autochop') + +local argparse = require('argparse') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +function setTargets(max, min) + max = tonumber(max) + max = max and math.floor(max) or nil + if not max or max < 0 then + qerror('target maximum must be a non-negative integer') + end + + min = tonumber(min) + min = min and math.floor(min) or nil + if min and (min < 0 or min > max) then + qerror('target minimum must be between 0 and the maximum, inclusive') + end + if not min then + min = math.floor(max * 0.8) + end + autochop_setTargets(max, min) +end + +local function do_set_burrow_config(var_name, val, burrows) + for _,bspec in ipairs(argparse.stringList(burrows)) do + local config = autochop_getBurrowConfig(bspec) + config[var_name] = val + autochop_setBurrowConfig(config.id, config.chop, config.clearcut, + config.protect_brewable, config.protect_edible, + config.protect_cookable) + end +end + +local function do_set_burrow_protect_config(types, val, burrows) + for _,tname in ipairs(argparse.stringList(types)) do + do_set_burrow_config('protect_'..tname, val, burrows) + end +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = positionals[1] + if not command or command == 'status' then + autochop_printStatus() + elseif command == 'designate' then + autochop_designate() + elseif command == 'undesignate' then + autochop_undesignate() + elseif command == 'target' then + setTarget(args[2], args[3]) + elseif command == 'chop' then + do_set_burrow_config('chop', true, args[2]) + elseif command == 'nochop' then + do_set_burrow_config('chop', false, args[2]) + elseif command == 'clearcut' or command == 'clear' then + do_set_burrow_config('clearcut', true, args[2]) + elseif command == 'noclearcut' or command == 'noclear' then + do_set_burrow_config('clearcut', false, args[2]) + elseif command == 'protect' then + do_set_burrow_protect_config(args[2], true, args[3]) + elseif command == 'unprotect' or command == 'noprotect' then + do_set_burrow_protect_config(args[2], false, args[3]) + else + return false + end + + return true +end + +-- used by gui/autochop +function setBurrowConfig(config) + autochop_setBurrowConfig(config.id, config.chop, config.clearcut, + config.protect_brewable, config.protect_edible, + config.protect_cookable) +end + +function getTreeCountsAndBurrowConfigs() + local data = {autochop_getTreeCountsAndBurrowConfigs()} + local ret = {} + ret.summary = table.remove(data, 1) + ret.tree_counts = table.remove(data, 1) + ret.designated_tree_counts = table.remove(data, 1) + ret.burrow_configs = data + for _,c in ipairs(ret.burrow_configs) do + c.name = df.burrow.find(c.id).name + c.chop = c.chop ~= 0 + c.clearcut = c.clearcut ~= 0 + c.protect_brewable = c.protect_brewable ~= 0 + c.protect_edible = c.protect_edible ~= 0 + c.protect_cookable = c.protect_cookable ~= 0 + end + return ret +end + +return _ENV From c647ae33d8361d2923ad2f984d21c439de516202 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 8 Jan 2023 23:43:02 -0800 Subject: [PATCH 0079/2222] make gcc-4.8 happy --- plugins/autochop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 046038330..2f0c4e2cb 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -688,7 +688,7 @@ static void autochop_printStatus(color_ostream &out) { protect_edible = get_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE); protect_cookable = get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE); } - out.print(fmt, name_width, burrow->name, int_to_string(burrow->id).c_str(), + out.print(fmt, name_width, burrow->name.c_str(), int_to_string(burrow->id).c_str(), chop ? "[x]" : "[ ]", clearcut ? "[x]" : "[ ]", int_to_string(tree_counts[burrow->id]).c_str(), int_to_string(designated_tree_counts[burrow->id]).c_str(), From 5310cfadcad10ac15189e8f739e4e323a9543d7a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 00:13:00 -0800 Subject: [PATCH 0080/2222] represent cookable with 'z' to match the hotkey --- plugins/autochop.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 2f0c4e2cb..59dc32a60 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -603,19 +603,19 @@ static const char * get_protect_str(bool protect_brewable, bool protect_edible, if (!protect_brewable && !protect_edible && !protect_cookable) return " "; if (!protect_brewable && !protect_edible && protect_cookable) - return " c"; + return " z"; if (!protect_brewable && protect_edible && !protect_cookable) return " e "; if (!protect_brewable && protect_edible && protect_cookable) - return " ec"; + return " ez"; if (protect_brewable && !protect_edible && !protect_cookable) return "b "; if (protect_brewable && !protect_edible && protect_cookable) - return "b c"; + return "b z"; if (protect_brewable && protect_edible && !protect_cookable) return "be "; if (protect_brewable && protect_edible && protect_cookable) - return "bec"; + return "bez"; return ""; } From 2234328a915a50810ee8654d9cbb0116d47ba771 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 00:47:13 -0800 Subject: [PATCH 0081/2222] use an actual pin texture for ZScreen pins --- data/art/green-pin.png | Bin 0 -> 397 bytes data/art/red-pin.png | Bin 0 -> 313 bytes library/LuaApi.cpp | 2 ++ library/include/modules/Textures.h | 5 +++++ library/lua/gui.lua | 20 ++++++++++++++++++-- library/lua/gui/widgets.lua | 3 ++- library/modules/Textures.cpp | 14 ++++++++++++++ 7 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 data/art/green-pin.png create mode 100644 data/art/red-pin.png diff --git a/data/art/green-pin.png b/data/art/green-pin.png new file mode 100644 index 0000000000000000000000000000000000000000..25d0e696ff266a3def8c1584ac56dd1ac2586f45 GIT binary patch literal 397 zcmV;80doF{P)Px$M@d9MR5*>Llrc}jKp2Lf!x2JUCaQ^ rE~ZTtevt|TgnZg7^eT(!nnHjt#vF+A=31eZ00000NkvXXu0mjflf$c# literal 0 HcmV?d00001 diff --git a/data/art/red-pin.png b/data/art/red-pin.png new file mode 100644 index 0000000000000000000000000000000000000000..763b83e97e88b6bd901d7877702eb71b96731395 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRm!3HEhFR0}KQjEnx?oJHr&dIz4iFvv>hFJ7o zowAnikb#KX{@Vu=93PxKq9WJ)h;xGXgTxt4*KaUS;AubFd_!6xz}a75*MzkaY*Wfo zB87$QZqJ)s{kvGxzK4~^Y29ln&KaD4q8(4!)dM*4# zJ#~R{3)uY4eg6&P4-_I+c)gmr!r4rj>q3} zA8z}gZ*xWVY-)OLT6d`QuusCure!}DHwK9cRiDn$VlS59eD|p&V|`O{d^ynj44$rj JF6*2UngC=xd2Ijy literal 0 HcmV?d00001 diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index fdd19783a..c327efe1b 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1676,6 +1676,8 @@ static const luaL_Reg dfhack_job_funcs[] = { static const LuaWrapper::FunctionReg dfhack_textures_module[] = { WRAPM(Textures, getDfhackLogoTexposStart), + WRAPM(Textures, getGreenPinTexposStart), + WRAPM(Textures, getRedPinTexposStart), { NULL, NULL } }; diff --git a/library/include/modules/Textures.h b/library/include/modules/Textures.h index 2ef172bff..e088ce477 100644 --- a/library/include/modules/Textures.h +++ b/library/include/modules/Textures.h @@ -30,5 +30,10 @@ void cleanup(); */ DFHACK_EXPORT long getDfhackLogoTexposStart(); +/** + * Get the texpos for the UI pin tiles. Each are 2x2 grids. + */ +DFHACK_EXPORT long getGreenPinTexposStart(); +DFHACK_EXPORT long getRedPinTexposStart(); } } diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 661351d31..f83a1b0cd 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -818,9 +818,25 @@ function paint_frame(dc,rect,style,title,show_lock,locked,inactive) if show_lock then if locked and style.locked_pen then - dscreen.paintTile(style.locked_pen, x2-1, y1) + local pin_texpos = dfhack.textures.getGreenPinTexposStart() + if pin_texpos == -1 then + dscreen.paintTile(style.locked_pen, x2-1, y1) + else + dscreen.paintTile(style.locked_pen, x2-2, y1-1, nil, pin_texpos+0) + dscreen.paintTile(style.locked_pen, x2-1, y1-1, nil, pin_texpos+1) + dscreen.paintTile(style.locked_pen, x2-2, y1, nil, pin_texpos+2) + dscreen.paintTile(style.locked_pen, x2-1, y1, nil, pin_texpos+3) + end elseif not locked and style.unlocked_pen then - dscreen.paintTile(style.unlocked_pen, x2-1, y1) + local pin_texpos = dfhack.textures.getRedPinTexposStart() + if pin_texpos == -1 then + dscreen.paintTile(style.unlocked_pen, x2-1, y1) + else + dscreen.paintTile(style.unlocked_pen, x2-2, y1-1, nil, pin_texpos+0) + dscreen.paintTile(style.unlocked_pen, x2-1, y1-1, nil, pin_texpos+1) + dscreen.paintTile(style.unlocked_pen, x2-2, y1, nil, pin_texpos+2) + dscreen.paintTile(style.unlocked_pen, x2-1, y1, nil, pin_texpos+3) + end end end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index c5002ddaa..c13757f22 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -502,7 +502,8 @@ function Window:onInput(keys) if keys._MOUSE_L_DOWN and self.parent_view and self.parent_view.toggleLocked then local x,y = dscreen.getMousePos() local frame_rect = self.frame_rect - if x == frame_rect.x2-1 and y == frame_rect.y1 then + if (x == frame_rect.x2-2 or x == frame_rect.x2-1) + and (y == frame_rect.y1-1 or y == frame_rect.y1) then self.parent_view:toggleLocked() end end diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index 70011b627..3ae8658c8 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -19,6 +19,8 @@ namespace DFHack { static bool g_loaded = false; static long g_num_dfhack_textures = 0; static long g_dfhack_logo_texpos_start = -1; +static long g_green_pin_texpos_start = -1; +static long g_red_pin_texpos_start = -1; // Converts an arbitrary Surface to something like the display format // (32-bit RGBA), and converts magenta to transparency if convert_magenta is set @@ -111,6 +113,10 @@ void Textures::init(color_ostream &out) { g_num_dfhack_textures = load_textures(out, "hack/data/art/dfhack.png", &g_dfhack_logo_texpos_start); + g_num_dfhack_textures += load_textures(out, "hack/data/art/green-pin.png", + &g_green_pin_texpos_start); + g_num_dfhack_textures += load_textures(out, "hack/data/art/red-pin.png", + &g_red_pin_texpos_start); DEBUG(textures,out).print("loaded %ld textures\n", g_num_dfhack_textures); @@ -146,3 +152,11 @@ void Textures::cleanup() { long Textures::getDfhackLogoTexposStart() { return g_dfhack_logo_texpos_start; } + +long Textures::getGreenPinTexposStart() { + return g_green_pin_texpos_start; +} + +long Textures::getRedPinTexposStart() { + return g_red_pin_texpos_start; +} From f8728a438893d7a6b608b27cfab9bd7991832a6e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 01:39:48 -0800 Subject: [PATCH 0082/2222] get rudimentary getAny(Unit|Item|Building) working --- library/modules/Gui.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 8a009c1dc..0fb455fc9 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -832,6 +832,15 @@ df::job *Gui::getSelectedJob(color_ostream &out, bool quiet) df::unit *Gui::getAnyUnit(df::viewscreen *top) { + using df::global::game; + + if (auto dfscreen = dfhack_viewscreen::try_cast(top)) + return dfscreen->getSelectedUnit(); + + if (game->main_interface.view_sheets.open + && game->main_interface.view_sheets.active_sheet == view_sheet_type::UNIT) + return df::unit::find(game->main_interface.view_sheets.active_id); + /* TODO: understand how this changes for v50 using namespace ui_sidebar_mode; using df::global::ui_look_cursor; @@ -1114,6 +1123,15 @@ df::unit *Gui::getSelectedUnit(color_ostream &out, bool quiet) df::item *Gui::getAnyItem(df::viewscreen *top) { + using df::global::game; + + if (auto dfscreen = dfhack_viewscreen::try_cast(top)) + return dfscreen->getSelectedItem(); + + if (game->main_interface.view_sheets.open + && game->main_interface.view_sheets.active_sheet == view_sheet_type::ITEM) + return df::item::find(game->main_interface.view_sheets.active_id); + /* TODO: understand how this changes for v50 using namespace ui_sidebar_mode; using df::global::ui_look_cursor; @@ -1254,6 +1272,15 @@ df::item *Gui::getSelectedItem(color_ostream &out, bool quiet) df::building *Gui::getAnyBuilding(df::viewscreen *top) { + using df::global::game; + + if (auto dfscreen = dfhack_viewscreen::try_cast(top)) + return dfscreen->getSelectedBuilding(); + + if (game->main_interface.view_sheets.open + && game->main_interface.view_sheets.active_sheet == view_sheet_type::BUILDING) + return df::building::find(game->main_interface.view_sheets.active_id); + /* TODO: understand how this changes for v50 using namespace ui_sidebar_mode; using df::global::ui_look_list; From 3cf3e3f5b67419704be39174dca7bf68bf846070 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 02:03:55 -0800 Subject: [PATCH 0083/2222] enable orders, no updates needed --- plugins/CMakeLists.txt | 2 +- plugins/orders.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2b2f3aad5..b74d16e96 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -139,7 +139,7 @@ dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) #dfhack_plugin(nestboxes nestboxes.cpp) -#dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static) +dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #dfhack_plugin(petcapRemover petcapRemover.cpp) diff --git a/plugins/orders.cpp b/plugins/orders.cpp index 800a0db6e..6118b3997 100644 --- a/plugins/orders.cpp +++ b/plugins/orders.cpp @@ -147,8 +147,7 @@ static command_result orders_list_command(color_ostream & out) if (files.empty()) { - out << COLOR_YELLOW << "No exported orders yet. Create manager orders and export them with 'orders export ', or copy pre-made orders .json files into " << ORDERS_DIR << "." << std::endl; - return CR_OK; + out << COLOR_YELLOW << "No exported orders yet. Create manager orders and export them with 'orders export ', or copy pre-made orders .json files into " << ORDERS_DIR << "." << std::endl << std::endl; } for (auto it : files) From f39684dbfd5030a917d67e532b9693a0381dbf83 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 09:35:55 -0800 Subject: [PATCH 0084/2222] pass getAny calls through ZScreens --- library/lua/gui.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 661351d31..02b993916 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -756,6 +756,23 @@ function ZScreen:isMouseOver() end end +local function zscreen_get_any(scr, thing) + if not scr._native or not scr._native.parent then return nil end + return dfhack.gui['getAny'..thing](scr._native.parent) +end +function ZScreen:onGetSelectedUnit() + return zscreen_get_any(self, 'Unit') +end +function ZScreen:onGetSelectedItem() + return zscreen_get_any(self, 'Item') +end +function ZScreen:onGetSelectedBuilding() + return zscreen_get_any(self, 'Building') +end +function ZScreen:onGetSelectedPlant() + return zscreen_get_any(self, 'Plant') +end + -------------------------- -- Framed screen object -- -------------------------- From fecc733192ecc78829b8b2c1555ccdce58034a38 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 12:52:26 -0800 Subject: [PATCH 0085/2222] detect clicks on part of pin that sticks up --- library/lua/gui.lua | 10 ++++++++- library/lua/gui/widgets.lua | 44 ++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index f83a1b0cd..245dec8d2 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -721,6 +721,13 @@ function ZScreen:onInput(keys) end if ZScreen.super.onInput(self, keys) then + -- ensure underlying DF screens don't also react to handled clicks + if keys._MOUSE_L_DOWN then + df.global.enabler.mouse_lbut_down = 0 + end + if keys._MOUSE_R_DOWN then + df.global.enabler.mouse_rbut_down = 0 + end return end @@ -729,7 +736,8 @@ function ZScreen:onInput(keys) return end - if not self.locked and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then + if (self:isMouseOver() or not self.locked) + and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then self:dismiss() -- ensure underlying DF screens don't also react to the click df.global.enabler.mouse_rbut_down = 0 diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index c13757f22..4a39efa8e 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -272,10 +272,30 @@ local function Panel_on_double_click(self) Panel_update_frame(self, frame, true) end +local function panel_is_on_pin(self) + local frame_rect = self.frame_rect + local x,y = dscreen.getMousePos() + return (x == frame_rect.x2-2 or x == frame_rect.x2-1) + and (y == frame_rect.y1-1 or y == frame_rect.y1) +end + +local function panel_is_pinnable(self) + return self.lockable and self.parent_view and self.parent_view.toggleLocked +end + +function Panel:getMouseFramePos() + local x,y = Panel.super.getMouseFramePos(self) + if x then return x, y end + if panel_is_pinnable(self) and panel_is_on_pin(self) then + local frame_rect = self.frame_rect + return frame_rect.width - 3, 0 + end +end + function Panel:onInput(keys) if self.kbd_get_pos then - if keys.SELECT or keys.LEAVESCREEN then - Panel_end_drag(self, keys.LEAVESCREEN and self.saved_frame or nil, + if keys.SELECT or keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + Panel_end_drag(self, not keys.SELECT and self.saved_frame or nil, not not keys.SELECT) return true end @@ -300,7 +320,13 @@ function Panel:onInput(keys) end return true end - if self:inputToSubviews(keys) then + if panel_is_pinnable(self) and keys._MOUSE_L_DOWN then + if panel_is_on_pin(self) then + self.parent_view:toggleLocked() + return true + end + end + if Panel.super.onInput(self, keys) then return true end if not keys._MOUSE_L_DOWN then return end @@ -498,18 +524,6 @@ Window.ATTRS { lockable = true, } -function Window:onInput(keys) - if keys._MOUSE_L_DOWN and self.parent_view and self.parent_view.toggleLocked then - local x,y = dscreen.getMousePos() - local frame_rect = self.frame_rect - if (x == frame_rect.x2-2 or x == frame_rect.x2-1) - and (y == frame_rect.y1-1 or y == frame_rect.y1) then - self.parent_view:toggleLocked() - end - end - return Window.super.onInput(self, keys) -end - ------------------- -- ResizingPanel -- ------------------- From 817991f8c906c7275a12011490c9574e70bb36cd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 13:48:08 -0800 Subject: [PATCH 0086/2222] remove twaterlvl from the global keybinding list since vanilla df comes with its own keybinding now --- data/init/dfhack.keybindings.init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index c6620f492..c4323ecbc 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -31,7 +31,7 @@ keybinding add Ctrl-Shift-K gui/cp437-table #keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave # toggle the display of water level as 1-7 tiles -keybinding add Ctrl-W@dwarfmode|dungeonmode twaterlvl +#keybinding add Ctrl-W@dwarfmode|dungeonmode twaterlvl # designate the whole vein for digging #keybinding add Ctrl-V@dwarfmode digv From 2da3510adebcc8f3e2548f0221cc7a2a1834adfc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 14:59:38 -0800 Subject: [PATCH 0087/2222] add stonesense to the build (if configured); docs the `overlay` command option has been removed --- docs/plugins/stonesense.rst | 6 ------ plugins/CMakeLists.txt | 2 +- plugins/stonesense | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/plugins/stonesense.rst b/docs/plugins/stonesense.rst index 23c20ef0a..714de0378 100644 --- a/docs/plugins/stonesense.rst +++ b/docs/plugins/stonesense.rst @@ -13,8 +13,6 @@ Usage ``stonesense`` or ``ssense`` Open the visualiser in a new window. -``ssense overlay`` - Overlay DF window, replacing the map area. The viewer window has read-only access to the game, and can follow the game view or be moved independently. Configuration for stonesense can be set in the @@ -24,10 +22,6 @@ unable to see the edges of the map with the overlay active, try decreasing the value for ``SEGMENTSIZE_XY`` -- normal values are ``50`` to ``80``, depending on your screen resolution. -If you replace the map section of your DF window with ``ssense overlay``, be -aware that it's not (yet) suitable for use as your only interface. Use DF's -``[PRINT_MODE:2D]`` init option (in ``data/init/init.txt``) for stability. - .. figure:: ../images/stonesense-roadtruss.jpg :align: center :target: http://www.bay12forums.com/smf/index.php?topic=48172.msg3198664#msg3198664 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index a31f89898..5b2298126 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,7 +2,7 @@ include(Plugins.cmake) option(BUILD_STONESENSE "Build stonesense (needs a checkout first)." OFF) if(BUILD_STONESENSE) - #add_subdirectory(stonesense) + add_subdirectory(stonesense) endif() option(BUILD_ISOWORLD "Build isoworld (needs a checkout first)." OFF) diff --git a/plugins/stonesense b/plugins/stonesense index 7b44c9dd0..6376bbc63 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 7b44c9dd0e0316c3edb66393de08591c95264e1a +Subproject commit 6376bbc630feaf8b4f68623cdd2c28c87ac11af4 From d918e2d9d83d649fb2bdc24ebc077eb6f4dfd39d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 15:33:04 -0800 Subject: [PATCH 0088/2222] fix example init file and related doc --- data/dfhack-config/init/examples/README.md | 9 +++++++++ .../init/examples/onMapLoad_dreamfort.init | 6 +++--- docs/guides/examples-guide.rst | 12 ++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 data/dfhack-config/init/examples/README.md diff --git a/data/dfhack-config/init/examples/README.md b/data/dfhack-config/init/examples/README.md new file mode 100644 index 000000000..99060b0a3 --- /dev/null +++ b/data/dfhack-config/init/examples/README.md @@ -0,0 +1,9 @@ +# Example DFHack init scripts + +This folder contains ready-to-use examples of DFHack init scripts. +You can use them by copying them to the dfhack-config/init folder. +You can use them unmodified, or you can customize them to better +suit your preferences. + +For information on each of the files in this library, see the +[DFHack Example Configuration File Guide](https://docs.dfhack.org/en/stable/docs/guides/examples-guide.html). diff --git a/data/dfhack-config/init/examples/onMapLoad_dreamfort.init b/data/dfhack-config/init/examples/onMapLoad_dreamfort.init index 8f341cf7e..c1d129c33 100644 --- a/data/dfhack-config/init/examples/onMapLoad_dreamfort.init +++ b/data/dfhack-config/init/examples/onMapLoad_dreamfort.init @@ -10,7 +10,7 @@ #on-new-fortress buildingplan set boulders false; buildingplan set logs false # Disable cooking of useful item types when you start a new fortress. -on-new-fortress ban-cooking tallow; ban-cooking honey; ban-cooking oil; ban-cooking seeds; ban-cooking brew; ban-cooking fruit; ban-cooking mill; ban-cooking thread; ban-cooking milk; ban-cooking booze +on-new-fortress ban-cooking all # Show a warning dialog when units are starving repeat -name warn-starving -time 10 -timeUnits days -command [ warn-starving ] @@ -40,8 +40,8 @@ enable automelt enable tailor # auto-assigns nesting birds to nestbox zones and protects fertile eggs from -# being cooked/eaten -enable zone autonestbox nestboxes +# being gathered and eaten +enable autonestbox nestboxes # manages seed stocks enable seedwatch diff --git a/docs/guides/examples-guide.rst b/docs/guides/examples-guide.rst index bcb7c77ec..d2581cf81 100644 --- a/docs/guides/examples-guide.rst +++ b/docs/guides/examples-guide.rst @@ -13,16 +13,16 @@ your preferences. The ``init/`` subfolder ----------------------- -The :source:`init/ ` subfolder contains useful DFHack -`init-files` that you can copy into your :file:`dfhack-config/init` folder -- -the same directory as ``dfhack.init``. +The :source:`init/ ` subfolder contains useful +DFHack `init-files` that you can copy into your :file:`dfhack-config/init` +folder -- the same directory as ``dfhack.init``. .. _onMapLoad-dreamfort-init: -:source:`onMapLoad_dreamfort.init ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:source:`onMapLoad_dreamfort.init ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is the config file that comes with the `dreamfort` set of blueprints, but +This is the config file that designed for the `dreamfort` set of blueprints, but it is useful (and customizable) for any fort. It includes the following config: - Calls `ban-cooking` for items that have important alternate uses and should From 3518a13deacb17d9a957db9a24183d26c01e344d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 15:45:45 -0800 Subject: [PATCH 0089/2222] add cleanowned back to the build (no changes) --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 5b2298126..6b9de7267 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -101,7 +101,7 @@ dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) #add_subdirectory(channel-safely) #dfhack_plugin(cleanconst cleanconst.cpp) #dfhack_plugin(cleaners cleaners.cpp) -#dfhack_plugin(cleanowned cleanowned.cpp) +dfhack_plugin(cleanowned cleanowned.cpp) #dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) #dfhack_plugin(createitem createitem.cpp) #dfhack_plugin(cursecheck cursecheck.cpp) From f0aa7a76de321bfffb4ccd19bc3919c0f4566c03 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 20:04:54 -0600 Subject: [PATCH 0090/2222] reenable autofarm and add persistence support note that persistence does not appear to work properly right now so this hasn't been fully tested --- plugins/CMakeLists.txt | 2 +- plugins/autofarm.cpp | 97 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6b9de7267..d0b7a3381 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -84,7 +84,7 @@ dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) #dfhack_plugin(autochop autochop.cpp) #dfhack_plugin(autoclothing autoclothing.cpp) #dfhack_plugin(autodump autodump.cpp) -#dfhack_plugin(autofarm autofarm.cpp) +dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) #add_subdirectory(autolabor) #dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index 6a4078b56..a3a2e52b2 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -1,5 +1,6 @@ #include "Core.h" #include "Console.h" +#include "Debug.h" #include "Export.h" #include "PluginManager.h" @@ -38,6 +39,13 @@ DFHACK_PLUGIN("autofarm"); DFHACK_PLUGIN_IS_ENABLED(enabled); +#define CONFIG_KEY "autofarm/config" + +namespace DFHack { + DBG_DECLARE(autofarm, cycle, DebugCategory::LINFO); + DBG_DECLARE(autofarm, config, DebugCategory::LINFO); +} + class AutoFarm { private: std::map thresholds; @@ -192,9 +200,7 @@ public: if (old_plant_id != new_plant_id) { farm->plant_id[season] = new_plant_id; - out << "autofarm: changing farm #" << farm->id << - " from " << get_plant_name(old_plant_id) << - " to " << get_plant_name(new_plant_id) << '\n'; + INFO(cycle, out).print("autofarm: changing farm #%d from %s to %s\n", farm->id, get_plant_name(old_plant_id), get_plant_name(new_plant_id)); } } @@ -341,6 +347,61 @@ public: out << std::flush; } + + void load_state(color_ostream& out) + { + initialize(); + + PersistentDataItem cfg_default_threshold = World::GetPersistentData("autofarm/default_threshold"); + if (cfg_default_threshold.isValid()) + defaultThreshold = cfg_default_threshold.ival(0); + + PersistentDataItem cfg_num_thresholds = World::GetPersistentData("autofarm/num_threshold"); + if (cfg_num_thresholds.isValid()) + { + int n = cfg_num_thresholds.ival(0); + for (int i = 0; i < n; i++) + { + const std::string keyName = "autofarm/threshold/" + std::to_string(i); + PersistentDataItem cfg_threshold = World::GetPersistentData(keyName); + if (cfg_threshold.isValid()) + { + const auto allPlants = world->raws.plants.all; + const std::string id = cfg_threshold.val(); + const int val = cfg_threshold.ival(0); + const auto plant = std::find_if(std::begin(allPlants), std::end(allPlants), [id](df::plant_raw* p) { return p->id == id; }); + if (plant != std::end(allPlants)) + { + setThreshold((*plant)->index, val); + INFO(config, out).print("threshold of %d for unknown plant %s in saved configuration loaded\n", val, id); + } + else + { + WARN(config, out).print("threshold for unknown plant %s in saved configuration ignored\n", id); + } + } + } + } + + PersistentDataItem cfg_enabled = World::GetPersistentData("autofarm/enabled"); + enabled = (cfg_enabled.isValid() && cfg_enabled.ival(0) != 0); + } + + void save_state(color_ostream& out) + { + World::AddPersistentData("autofarm/default_threshold").ival(0) = defaultThreshold; + World::AddPersistentData("autofarm/num_threshold").ival(0) = thresholds.size(); + for (const auto& t : thresholds) + { + const std::string& plantName = world->raws.plants.all[t.first]->id; + const std::string keyName = "autofarm/threshold/" + plantName; + PersistentDataItem cfgThreshold = World::AddPersistentData(keyName); + cfgThreshold.val() = plantName; + cfgThreshold.ival(0) = t.second; + } + World::AddPersistentData("autofarm/enabled").ival(0) = enabled; + } + }; static std::unique_ptr autofarmInstance; @@ -390,6 +451,26 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) { enabled = enable; + if (Core::getInstance().isWorldLoaded()) + autofarmInstance->save_state(out); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event event) +{ + if (!autofarmInstance) + return CR_OK; + + switch (event) { + case SC_WORLD_LOADED: + autofarmInstance->load_state(out); + break; + case SC_MAP_UNLOADED: + break; + default: + break; + } + return CR_OK; } @@ -417,6 +498,7 @@ static command_result setThresholds(color_ostream& out, std::vector return CR_WRONG_USAGE; } } + autofarmInstance->save_state(out); return CR_OK; } @@ -434,11 +516,16 @@ static command_result autofarm(color_ostream& out, std::vector& par plugin_enable(out, false); else if (parameters.size() == 2 && parameters[0] == "default") { - if (autofarmInstance) autofarmInstance->setDefault(atoi(parameters[1].c_str())); + if (autofarmInstance) + { + autofarmInstance->setDefault(atoi(parameters[1].c_str())); + autofarmInstance->save_state(out); + } } else if (parameters.size() >= 3 && parameters[0] == "threshold") { - if (autofarmInstance) return setThresholds(out, parameters); + if (autofarmInstance) + return setThresholds(out, parameters); } else if (parameters.size() == 0 || (parameters.size() == 1 && parameters[0] == "status")) autofarmInstance->status(out); From e649255c8eff684fc49e9cd4ef6336efb3c256ba Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 6 Jan 2023 23:45:16 -0600 Subject: [PATCH 0091/2222] rework autofarm persistence --- plugins/autofarm.cpp | 73 ++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index a3a2e52b2..cb5b5a855 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -348,58 +348,70 @@ public: out << std::flush; } +private: + + PersistentDataItem cfg_default_threshold; + PersistentDataItem cfg_enabled; + +public: void load_state(color_ostream& out) { initialize(); - PersistentDataItem cfg_default_threshold = World::GetPersistentData("autofarm/default_threshold"); + cfg_enabled = World::GetPersistentData("autofarm/enabled"); + if (cfg_enabled.isValid()) + enabled = cfg_enabled.ival(0) != 0; + else + cfg_enabled.ival(0) = enabled; + + cfg_default_threshold = World::GetPersistentData("autofarm/default_threshold"); + if (cfg_default_threshold.isValid()) defaultThreshold = cfg_default_threshold.ival(0); + else + cfg_default_threshold.ival(0) = defaultThreshold; - PersistentDataItem cfg_num_thresholds = World::GetPersistentData("autofarm/num_threshold"); - if (cfg_num_thresholds.isValid()) - { - int n = cfg_num_thresholds.ival(0); - for (int i = 0; i < n; i++) + std::vector items; + World::GetPersistentData(&items, "autofarm/threshold/", true); + for (auto& i: items) { + if (i.isValid()) { - const std::string keyName = "autofarm/threshold/" + std::to_string(i); - PersistentDataItem cfg_threshold = World::GetPersistentData(keyName); - if (cfg_threshold.isValid()) + const auto allPlants = world->raws.plants.all; + const std::string id = i.val(); + const int val = i.ival(0); + const auto plant = std::find_if(std::begin(allPlants), std::end(allPlants), [id](df::plant_raw* p) { return p->id == id; }); + if (plant != std::end(allPlants)) + { + setThreshold((*plant)->index, val); + INFO(config, out).print("threshold of %d for plant %s in saved configuration loaded\n", val, id); + } + else { - const auto allPlants = world->raws.plants.all; - const std::string id = cfg_threshold.val(); - const int val = cfg_threshold.ival(0); - const auto plant = std::find_if(std::begin(allPlants), std::end(allPlants), [id](df::plant_raw* p) { return p->id == id; }); - if (plant != std::end(allPlants)) - { - setThreshold((*plant)->index, val); - INFO(config, out).print("threshold of %d for unknown plant %s in saved configuration loaded\n", val, id); - } - else - { - WARN(config, out).print("threshold for unknown plant %s in saved configuration ignored\n", id); - } + WARN(config, out).print("threshold for unknown plant %s in saved configuration ignored\n", id); } } } - PersistentDataItem cfg_enabled = World::GetPersistentData("autofarm/enabled"); - enabled = (cfg_enabled.isValid() && cfg_enabled.ival(0) != 0); } void save_state(color_ostream& out) { - World::AddPersistentData("autofarm/default_threshold").ival(0) = defaultThreshold; - World::AddPersistentData("autofarm/num_threshold").ival(0) = thresholds.size(); + cfg_default_threshold.ival(0) = defaultThreshold; + cfg_enabled.ival(0) = enabled; + + std::vector items; + World::GetPersistentData(&items, "autofarm/threshold/", true); + for (auto& i : items) + World::DeletePersistentData(i); + for (const auto& t : thresholds) { - const std::string& plantName = world->raws.plants.all[t.first]->id; - const std::string keyName = "autofarm/threshold/" + plantName; + const std::string& plantID = world->raws.plants.all[t.first]->id; + const std::string keyName = "autofarm/threshold/" + plantID; PersistentDataItem cfgThreshold = World::AddPersistentData(keyName); - cfgThreshold.val() = plantName; + cfgThreshold.val() = plantID; cfgThreshold.ival(0) = t.second; } - World::AddPersistentData("autofarm/enabled").ival(0) = enabled; } }; @@ -416,6 +428,7 @@ DFhackCExport command_result plugin_init(color_ostream& out, std::vector ()); + autofarmInstance->load_state(out); return CR_OK; } From 29506dbd87e6435c5deb9e223ed778b44a4e6607 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 16:13:15 -0800 Subject: [PATCH 0092/2222] remove call go fix/blood-del, which no longer seems useful --- data/dfhack-config/init/examples/onMapLoad_dreamfort.init | 3 --- 1 file changed, 3 deletions(-) diff --git a/data/dfhack-config/init/examples/onMapLoad_dreamfort.init b/data/dfhack-config/init/examples/onMapLoad_dreamfort.init index c1d129c33..13d4ea8fc 100644 --- a/data/dfhack-config/init/examples/onMapLoad_dreamfort.init +++ b/data/dfhack-config/init/examples/onMapLoad_dreamfort.init @@ -25,9 +25,6 @@ repeat -name autoMilkCreature -time 14 -timeUnits days -command [ workorder "{\" # Fulfill high-volume orders before slower once-daily orders repeat -name orders-sort -time 1 -timeUnits days -command [ orders sort ] -# Don't let caravans bring barrels of blood and other useless liquids -fix/blood-del - # Manages crop assignment for farm plots enable autofarm autofarm default 30 From 3938721df0cc8ca943f3acc556c928403ec044f3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 16:25:01 -0800 Subject: [PATCH 0093/2222] enable seedwatch (no changes) --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6b9de7267..3a1797fbc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -153,7 +153,7 @@ add_subdirectory(remotefortressreader) #add_subdirectory(rendermax) dfhack_plugin(reveal reveal.cpp LINK_LIBRARIES lua) #dfhack_plugin(search search.cpp) -#dfhack_plugin(seedwatch seedwatch.cpp) +dfhack_plugin(seedwatch seedwatch.cpp) #dfhack_plugin(showmood showmood.cpp) #dfhack_plugin(siege-engine siege-engine.cpp LINK_LIBRARIES lua) #dfhack_plugin(sort sort.cpp LINK_LIBRARIES lua) From 81a8a6047be3df6bb0ce546327210cb124dc966c Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 9 Jan 2023 20:04:13 -0600 Subject: [PATCH 0094/2222] persistence fixes, other tweaks --- plugins/autofarm.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index cb5b5a855..238627c3a 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -200,7 +200,9 @@ public: if (old_plant_id != new_plant_id) { farm->plant_id[season] = new_plant_id; - INFO(cycle, out).print("autofarm: changing farm #%d from %s to %s\n", farm->id, get_plant_name(old_plant_id), get_plant_name(new_plant_id)); + INFO(cycle, out).print("autofarm: changing farm #%d from %s to %s\n", farm->id, + get_plant_name(old_plant_id).c_str(), + get_plant_name(new_plant_id).c_str()); } } @@ -357,19 +359,25 @@ public: void load_state(color_ostream& out) { initialize(); + if (!(Core::getInstance().isWorldLoaded())) + return; cfg_enabled = World::GetPersistentData("autofarm/enabled"); if (cfg_enabled.isValid()) enabled = cfg_enabled.ival(0) != 0; - else + else { + cfg_enabled = World::AddPersistentData("autofarm/enabled"); cfg_enabled.ival(0) = enabled; + } cfg_default_threshold = World::GetPersistentData("autofarm/default_threshold"); if (cfg_default_threshold.isValid()) defaultThreshold = cfg_default_threshold.ival(0); - else + else { + cfg_default_threshold = World::AddPersistentData("autofarm/default_threshold"); cfg_default_threshold.ival(0) = defaultThreshold; + } std::vector items; World::GetPersistentData(&items, "autofarm/threshold/", true); @@ -383,11 +391,11 @@ public: if (plant != std::end(allPlants)) { setThreshold((*plant)->index, val); - INFO(config, out).print("threshold of %d for plant %s in saved configuration loaded\n", val, id); + INFO(config, out).print("threshold of %d for plant %s in saved configuration loaded\n", val, id.c_str()); } else { - WARN(config, out).print("threshold for unknown plant %s in saved configuration ignored\n", id); + WARN(config, out).print("threshold for unknown plant %s in saved configuration ignored\n", id.c_str()); } } } From d725b813c750df8f0aa6abf73e1014f4e31894fb Mon Sep 17 00:00:00 2001 From: arekatir <120872617+arekatir@users.noreply.github.com> Date: Mon, 9 Jan 2023 23:04:43 -0500 Subject: [PATCH 0095/2222] Remove os.execute and io.popen --- library/LuaTools.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index c76c65651..618fb1fea 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1896,9 +1896,17 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) luaL_setfuncs(state, dfhack_coro_funcs, 0); lua_pop(state, 1); + // replace some io functions + lua_getglobal(state, "io"); + lua_pushnil(state); + lua_setfield(state, -2, "popen"); + lua_pop(state, 1); + // replace some os functions lua_getglobal(state, "os"); luaL_setfuncs(state, dfhack_os_funcs, 0); + lua_pushnil(state); + lua_setfield(state, -2, "execute"); lua_pop(state, 1); // split the global environment From 8ad1cafb07c239d383850053fb5bfd5c6c4bf0af Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 04:51:59 +0000 Subject: [PATCH 0096/2222] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- library/LuaTools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 618fb1fea..b4d009c7b 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1901,7 +1901,7 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) lua_pushnil(state); lua_setfield(state, -2, "popen"); lua_pop(state, 1); - + // replace some os functions lua_getglobal(state, "os"); luaL_setfuncs(state, dfhack_os_funcs, 0); From c6472560c610b8a761da3bd39877b89aeff2cea9 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 10 Jan 2023 07:14:21 +0000 Subject: [PATCH 0097/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index d3d87c864..a3759522c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d3d87c864388eb44fc3f1f5d4b668ff854cd5e68 +Subproject commit a3759522cab195893b244cd615cf5286fc9bea13 diff --git a/scripts b/scripts index fed70e364..49ae8f7d4 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit fed70e36466fdc7baed1d0424f8c4c7d1503597e +Subproject commit 49ae8f7d4f97b37001f430335198adeb9d3dd405 From 7a9dd02bbda9c46ec9a82d37eb95fb69c9b8c3a7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 10 Jan 2023 07:51:40 +0000 Subject: [PATCH 0098/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index a3759522c..cf4940071 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a3759522cab195893b244cd615cf5286fc9bea13 +Subproject commit cf4940071c0ac16e3af8d2f46abc6a25d3251492 diff --git a/scripts b/scripts index 49ae8f7d4..9e70e2d6e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 49ae8f7d4f97b37001f430335198adeb9d3dd405 +Subproject commit 9e70e2d6eb35ac6b6646bacdde7176b1afa28e52 From 7cf703ef2362128a235545582a94edda54890513 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 6 Dec 2022 06:40:39 -0800 Subject: [PATCH 0099/2222] turn down Ruby support --- docs/Core.rst | 2 +- docs/about/Removed.rst | 9 +- docs/changelog.txt | 1 + docs/dev/Dev-intro.rst | 7 +- docs/dev/Documentation.rst | 15 +- docs/dev/Lua API.rst | 6 +- docs/plugins/ruby.rst | 31 - library/Core.cpp | 31 - library/PluginManager.cpp | 7 - library/include/PluginManager.h | 8 - library/lua/helpdb.lua | 11 +- plugins/CMakeLists.txt | 5 - plugins/ruby/CMakeLists.txt | 93 --- plugins/ruby/README | 279 ------- plugins/ruby/building.rb | 368 --------- plugins/ruby/codegen.pl | 1166 --------------------------- plugins/ruby/item.rb | 44 - plugins/ruby/job.rb | 35 - plugins/ruby/linux32/.gitignore | 1 - plugins/ruby/linux64/.gitignore | 1 - plugins/ruby/map.rb | 344 -------- plugins/ruby/material.rb | 203 ----- plugins/ruby/osx32/.gitignore | 1 - plugins/ruby/osx64/.gitignore | 1 - plugins/ruby/plant.rb | 111 --- plugins/ruby/ruby-autogen-defs.rb | 1034 ------------------------ plugins/ruby/ruby.cpp | 1237 ----------------------------- plugins/ruby/ruby.rb | 258 ------ plugins/ruby/ui.rb | 91 --- plugins/ruby/unit.rb | 280 ------- plugins/ruby/win32/.gitignore | 1 - plugins/ruby/win64/.gitignore | 1 - 32 files changed, 24 insertions(+), 5658 deletions(-) delete mode 100644 docs/plugins/ruby.rst delete mode 100644 plugins/ruby/CMakeLists.txt delete mode 100644 plugins/ruby/README delete mode 100644 plugins/ruby/building.rb delete mode 100755 plugins/ruby/codegen.pl delete mode 100644 plugins/ruby/item.rb delete mode 100644 plugins/ruby/job.rb delete mode 100644 plugins/ruby/linux32/.gitignore delete mode 100644 plugins/ruby/linux64/.gitignore delete mode 100644 plugins/ruby/map.rb delete mode 100644 plugins/ruby/material.rb delete mode 100644 plugins/ruby/osx32/.gitignore delete mode 100644 plugins/ruby/osx64/.gitignore delete mode 100644 plugins/ruby/plant.rb delete mode 100644 plugins/ruby/ruby-autogen-defs.rb delete mode 100644 plugins/ruby/ruby.cpp delete mode 100644 plugins/ruby/ruby.rb delete mode 100644 plugins/ruby/ui.rb delete mode 100644 plugins/ruby/unit.rb delete mode 100644 plugins/ruby/win32/.gitignore delete mode 100644 plugins/ruby/win64/.gitignore diff --git a/docs/Core.rst b/docs/Core.rst index 4fb1fdb71..b82fad109 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -21,7 +21,7 @@ DFHack commands can be implemented in any of three ways: same version of DFHack. They are less flexible than scripts, but used for complex or ongoing tasks because they run faster. -:scripts: are Ruby or Lua scripts stored in ``hack/scripts/`` or other +:scripts: are Lua scripts stored in ``hack/scripts/`` or other directories in the `script-paths`. Because they don't need to be compiled, scripts are more flexible about versions, and they are easier to distribute. Most third-party DFHack addons diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 072bd32ab..0fef8cbe9 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -128,10 +128,17 @@ longer necessary. resume ====== - Allowed you to resume suspended jobs and displayed an overlay indicating suspended building construction jobs. Replaced by `unsuspend` script. +.. _ruby: +.. _rb: + +ruby +==== +Support for the Ruby language in DFHack scripts was removed due to the issues +the Ruby library causes when used as an embedded language. + .. _warn-stuck-trees: warn-stuck-trees diff --git a/docs/changelog.txt b/docs/changelog.txt index f94e7cc0c..b5e6d3f97 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -66,6 +66,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Removed - ``fix-job-postings`` from the `workflow` plugin is now obsolete since affected savegames can no longer be loaded +- Ruby is no longer a supported DFHack scripting language # 0.47.05-r8 diff --git a/docs/dev/Dev-intro.rst b/docs/dev/Dev-intro.rst index 8ed7badc7..9253795eb 100644 --- a/docs/dev/Dev-intro.rst +++ b/docs/dev/Dev-intro.rst @@ -68,10 +68,9 @@ Run `plug` at the DFHack prompt for a list of all plugins included in DFHack. Scripts ------- -DFHack scripts can currently be written in Lua or Ruby. The `Lua API ` -is more complete and currently better-documented, however. Referring to existing -scripts as well as the API documentation can be helpful when developing new -scripts. +DFHack scripts are written in Lua, with a `well-documented library `. +Referring to existing scripts as well as the API documentation can be helpful +when developing new scripts. Scripts included in DFHack live in a separate :source-scripts:`scripts repository <>`. This can be found in the ``scripts`` diff --git a/docs/dev/Documentation.rst b/docs/dev/Documentation.rst index d649b8a15..07242d698 100644 --- a/docs/dev/Documentation.rst +++ b/docs/dev/Documentation.rst @@ -289,22 +289,15 @@ it should be written in plain text. Any reStructuredText markup will not be proc if present, will be shown verbatim to the player (which is probably not what you want). For external scripts, the short description comes from a comment on the first line -(the comment marker and extra whitespace is stripped). For Lua, this would look like: +(the comment marker and extra whitespace is stripped): .. code-block:: lua -- A short description of my cool script. -and for Ruby scripts it would look like: - -.. code-block:: ruby - - # A short description of my cool script. - -The main help text for an external script needs to appear between two markers. For -Lua, these markers are ``[====[`` and ``]====]``, and for Ruby they are ``=begin`` and -``=end``. The documentation standards above still apply to external tools, but there is -no need to include backticks for links or monospaced fonts. Here is a Lua example for an +The main help text for an external script needs to appear between two markers -- ``[====[`` +and ``]====]``. The documentation standards above still apply to external tools, but there is +no need to include backticks for links or monospaced fonts. Here is an example for an entire script header:: -- Inventory management for adventurers. diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 2b360350f..90ccdb851 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5678,9 +5678,9 @@ General script API Note that the ``dfhack.run_script()`` function allows Lua errors to propagate to the caller. - To run other types of commands (such as built-in commands, plugin commands, or - Ruby scripts), see ``dfhack.run_command()``. Note that this is slightly slower - than ``dfhack.run_script()`` for Lua scripts. + To run other types of commands (i.e. built-in commands or commands provided by plugins), + see ``dfhack.run_command()``. Note that this is slightly slower than ``dfhack.run_script()`` + when running Lua scripts. * ``dfhack.script_help([name, [extension]])`` diff --git a/docs/plugins/ruby.rst b/docs/plugins/ruby.rst deleted file mode 100644 index f1fafbb36..000000000 --- a/docs/plugins/ruby.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _rb: - -ruby -==== - -.. dfhack-tool:: - :summary: Allow Ruby scripts to be executed as DFHack commands. - :tags: dev - :no-command: - -.. dfhack-command:: rb - :summary: Eval() a ruby string. - -.. dfhack-command:: rb_eval - :summary: Eval() a ruby string. - -Usage ------ - -:: - - enable ruby - rb "ruby expression" - rb_eval "ruby expression" - :rb ruby expression - -Example -------- - -``:rb puts df.unit_find(:selected).name`` - Print the name of the selected unit. diff --git a/library/Core.cpp b/library/Core.cpp index abcba2e78..78257a262 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -341,30 +341,6 @@ static command_result enableLuaScript(color_ostream &out, std::string name, bool return ok ? CR_OK : CR_FAILURE; } -static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr, std::string filename, vector &args) -{ - if (!plug_mgr->ruby || !plug_mgr->ruby->is_enabled()) - return CR_FAILURE; - - // ugly temporary patch for https://github.com/DFHack/dfhack/issues/1146 - string cwd = Filesystem::getcwd(); - if (filename.find(cwd) == 0) - { - filename = filename.substr(cwd.size()); - while (!filename.empty() && (filename[0] == '/' || filename[0] == '\\')) - filename = filename.substr(1); - } - - std::string rbcmd = "$script_args = ["; - for (size_t i = 0; i < args.size(); i++) - rbcmd += "'" + args[i] + "', "; - rbcmd += "]\n"; - - rbcmd += "catch(:script_finished) { load '" + filename + "' }"; - - return plug_mgr->ruby->eval_ruby(out, rbcmd.c_str()); -} - command_result Core::runCommand(color_ostream &out, const std::string &command) { if (!command.empty()) @@ -901,7 +877,6 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v con << parts[0]; bool builtin = is_builtin(con, parts[0]); string lua_path = findScript(parts[0] + ".lua"); - string ruby_path = findScript(parts[0] + ".rb"); Plugin *plug = plug_mgr->getPluginByCommand(parts[0]); if (builtin) { @@ -920,10 +895,6 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { con << " is a Lua script: " << lua_path << std::endl; } - else if (ruby_path.size()) - { - con << " is a Ruby script: " << ruby_path << std::endl; - } else { con << " is not a recognized command." << std::endl; @@ -1233,8 +1204,6 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } if ( lua ) res = runLuaScript(con, first, parts); - else if ( filename != "" && plug_mgr->ruby && plug_mgr->ruby->is_enabled() ) - res = runRubyScript(con, plug_mgr, filename, parts); else if ( try_autocomplete(con, first, completed) ) res = CR_NOT_IMPLEMENTED; else diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 223abfeac..3670755fd 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -199,7 +199,6 @@ Plugin::Plugin(Core * core, const std::string & path, plugin_is_enabled = 0; plugin_save_data = 0; plugin_load_data = 0; - plugin_eval_ruby = 0; state = PS_UNLOADED; access = new RefLock(); } @@ -357,7 +356,6 @@ bool Plugin::load(color_ostream &con) plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); plugin_save_data = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_save_data"); plugin_load_data = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_load_data"); - plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); index_lua(plug); plugin_lib = plug; commands.clear(); @@ -826,7 +824,6 @@ PluginManager::PluginManager(Core * core) : core(core) { plugin_mutex = new tthread::recursive_mutex(); cmdlist_mutex = new tthread::mutex(); - ruby = NULL; } PluginManager::~PluginManager() @@ -1035,8 +1032,6 @@ void PluginManager::registerCommands( Plugin * p ) } command_map[name] = p; } - if (p->plugin_eval_ruby) - ruby = p; cmdlist_mutex->unlock(); } @@ -1048,8 +1043,6 @@ void PluginManager::unregisterCommands( Plugin * p ) { command_map.erase(cmds[i].name); } - if (p->plugin_eval_ruby) - ruby = NULL; cmdlist_mutex->unlock(); } diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 6bef8a74c..78c5e5dc8 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -201,12 +201,6 @@ namespace DFHack void open_lua(lua_State *state, int table); - command_result eval_ruby(color_ostream &out, const char* cmd) { - if (!plugin_eval_ruby || !is_enabled()) - return CR_FAILURE; - return plugin_eval_ruby(out, cmd); - } - private: RefLock * access; std::vector commands; @@ -244,7 +238,6 @@ namespace DFHack command_result (*plugin_onstatechange)(color_ostream &, state_change_event); command_result (*plugin_enable)(color_ostream &, bool); RPCService* (*plugin_rpcconnect)(color_ostream &); - command_result (*plugin_eval_ruby)(color_ostream &, const char*); command_result (*plugin_save_data)(color_ostream &); command_result (*plugin_load_data)(color_ostream &); }; @@ -282,7 +275,6 @@ namespace DFHack bool CanInvokeHotkey(const std::string &command, df::viewscreen *top); Plugin* operator[] (const std::string name); std::size_t size(); - Plugin *ruby; std::map::iterator begin(); std::map::iterator end(); diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 5af41c929..711555a78 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -25,8 +25,6 @@ local TAG_DEFINITIONS = 'hack/docs/docs/Tags.txt' -- used when reading help text embedded in script sources local SCRIPT_DOC_BEGIN = '[====[' local SCRIPT_DOC_END = ']====]' -local SCRIPT_DOC_BEGIN_RUBY = '=begin' -local SCRIPT_DOC_END_RUBY = '=end' -- enums local ENTRY_TYPES = { @@ -274,11 +272,10 @@ local function make_script_entry(old_entry, entry_name, kwargs) if not ok then return entry end - local is_rb = source_path:endswith('.rb') update_entry(entry, lines, - {begin_marker=(is_rb and SCRIPT_DOC_BEGIN_RUBY or SCRIPT_DOC_BEGIN), - end_marker=(is_rb and SCRIPT_DOC_END_RUBY or SCRIPT_DOC_END), - first_line_is_short_help=(is_rb and '#' or '%-%-')}) + {begin_marker=SCRIPT_DOC_BEGIN, + end_marker=SCRIPT_DOC_END, + first_line_is_short_help='%-%-'}) return entry end @@ -364,7 +361,7 @@ local function scan_scripts(old_db) if not files then goto skip_path end for _,f in ipairs(files) do if f.isdir or - (not f.path:endswith('.lua') and not f.path:endswith('.rb')) or + not f.path:endswith('.lua') or f.path:startswith('test/') or f.path:startswith('internal/') then goto continue diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e24e6129c..06d2b5c0b 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -24,11 +24,6 @@ if(BUILD_DEV_PLUGINS) #add_subdirectory(devel) endif() -option(BUILD_RUBY "Build ruby binding." ON) -if(BUILD_RUBY) - #add_subdirectory(ruby) -endif() - install(DIRECTORY lua/ DESTINATION ${DFHACK_LUA_DESTINATION}/plugins FILES_MATCHING PATTERN "*.lua") diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt deleted file mode 100644 index 704137621..000000000 --- a/plugins/ruby/CMakeLists.txt +++ /dev/null @@ -1,93 +0,0 @@ -# Allow build system to turn off downloading of libruby.so. -option(DOWNLOAD_RUBY "Download prebuilt libruby.so for ruby plugin." ON) - -if(DOWNLOAD_RUBY) - - if(APPLE) - set(RUBYLIB ${CMAKE_CURRENT_SOURCE_DIR}/osx${DFHACK_BUILD_ARCH}/libruby.dylib) - set(RUBYLIB_INSTALL_NAME "libruby.dylib") - if(${DFHACK_BUILD_ARCH} STREQUAL 64) - # message("No ruby lib for 64-bit OS X yet") - else() - download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/osx32-libruby187.dylib.gz" - "gz" - ${RUBYLIB}.gz - "e9bc4263557e652121b055a46abb4f97" - ${RUBYLIB} - "3ee5356759f764a440be5b5b44649826") - endif() - elseif(UNIX) - set(RUBYLIB ${CMAKE_CURRENT_SOURCE_DIR}/linux${DFHACK_BUILD_ARCH}/libruby.so) - set(RUBYLIB_INSTALL_NAME "libruby.so") - if(${DFHACK_BUILD_ARCH} STREQUAL 64) - download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/linux64-libruby187.so.gz" - "gz" - ${RUBYLIB}.gz - "8eb757bb9ada08608914d8ca8906c427" - ${RUBYLIB} - "e8c36a06f031cfbf02def28169bb5f1f") - else() - download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/linux32-libruby187.so.gz" - "gz" - ${RUBYLIB}.gz - "2d06f5069ff07ea934ecd40db55a4ac5" - ${RUBYLIB} - "b00d8d7086cb39f6fde793f9d89cb2d7") - endif() - else() - set(RUBYLIB ${CMAKE_CURRENT_SOURCE_DIR}/win${DFHACK_BUILD_ARCH}/libruby.dll) - set(RUBYLIB_INSTALL_NAME "libruby.dll") - if(${DFHACK_BUILD_ARCH} STREQUAL 64) - download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-libruby200.dll.gz" - "gz" - ${RUBYLIB}.gz - "81db54a8b8b3090c94c6ae2147d30b8f" - ${RUBYLIB} - "8a8564418aebddef3dfee1e96690e713") - else() - download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-libruby187.dll.gz" - "gz" - ${RUBYLIB}.gz - "ffc0f1b5b33748e2a36128e90c97f6b2" - ${RUBYLIB} - "482c1c418f4ee1a5f04203eee1cda0ef") - endif() - endif() - -endif() - -if(APPLE OR UNIX) - set(RUBYAUTOGEN ruby-autogen-gcc.rb) -else(APPLE OR UNIX) - set(RUBYAUTOGEN ruby-autogen-win.rb) -endif(APPLE OR UNIX) - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN} - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN} ${CMAKE_SYSTEM_NAME} ${DFHACK_BUILD_ARCH} - # cmake quirk: depending on codegen.out.xml or generate_headers only is not enough, needs both - # test by manually patching any library/xml/moo.xml, run make ruby-autogen-rb -j2, and check build/plugins/ruby/ruby-autogen.rb for patched xml data - DEPENDS generate_headers ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl - COMMENT ${RUBYAUTOGEN} -) -add_custom_target(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN}) - -include_directories("${dfhack_SOURCE_DIR}/depends/tthread") - -dfhack_plugin(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) -add_dependencies(ruby ruby-autogen-rb) - -if(EXISTS ${RUBYLIB}) - install(FILES ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION} RENAME ${RUBYLIB_INSTALL_NAME}) -else() - # Only fire this warning if DOWNLOAD_RUBY was set. - if(NOT(APPLE AND ${DFHACK_BUILD_ARCH} STREQUAL 64) AND DOWNLOAD_RUBY) - message(WARNING "Ruby library not found at ${RUBYLIB} - will not be installed") - endif() -endif() - -install(DIRECTORY . - DESTINATION hack/ruby - FILES_MATCHING PATTERN "*.rb") - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN} DESTINATION hack/ruby) diff --git a/plugins/ruby/README b/plugins/ruby/README deleted file mode 100644 index 767844414..000000000 --- a/plugins/ruby/README +++ /dev/null @@ -1,279 +0,0 @@ -This plugins embeds a ruby interpreter inside DFHack (ie inside Dwarf Fortress). - -The plugin maps all the structures available in library/xml/ to ruby objects. - -These objects are described in ruby-autogen.rb, they are all in the DFHack -module. The toplevel 'df' method is a shortcut to the DFHack module. - -The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct -access to the raw DF data structures in memory is provided. - -Some library methods are stored in the various .rb file, e.g. shortcuts to read -a map block, find an unit or an item, etc. - -Global dfhack objects are accessible through the 'df' accessor (eg 'df.world'). - -DFHack structures are renamed in CamelCase in the ruby namespace. - -For a list of the structures and their methods, grep the ruby-autogen.rb file. - -All ruby code runs while the main DF process and other plugins are suspended. - - -DFHack console --------------- - -The ruby plugin defines one new dfhack console command: - rb_eval ; evaluate a ruby expression and show the result in -the console. Ex: rb_eval df.unit_find().name.first_name -You can use single-quotes for strings ; avoid double-quotes that are parsed -and removed by the dfhack console code. - -Text output from ruby code, through the standard 'puts', 'p' or 'raise' are -redirected to the dfhack console window. - -If dfhack reports 'rb_eval is not a recognized command', check stderr.log. You -need a valid 32-bit ruby library to work, and ruby1.8 is prefered (ruby1.9 may -crash DF on startup for now). Install the library in the df root folder (or -df/hack/ on linux), the library should be named 'libruby.dll' (.so on linux). -You can download a tested version at http://github.com/jjyg/dfhack/downloads/ - - -Ruby scripts ------------- - -The ruby plugin allows the creation of '.rb' scripts in df/hack/scripts/. - -If you create such a script, e.g. 'test.rb', that will add a new dfhack console -command 'test'. -The script can access the console command arguments through the global variable -'$script_args', which is an array of ruby Strings. -To exit early from a script, use 'throw :script_finished' - -The help string displayed in dfhack 'ls' command is the first line of the -script, if it is a comment (ie starts with '# '). - - -Calling dfhack commands ------------------------ - -The ruby plugin allows the calling of arbitrary dfhack commands, as if typed -directly on the dfhack prompt. -However due to locks and stuff, the dfhack command is delayed until the current -ruby command is finished, so it is restricted to interactive uses. -It is possible to call the method many times, this will queue dfhack commands -to be run in order. - - df.dfhack_run "reveal" - - -Ruby helper functions ---------------------- - -This is an excerpt of the functions defined in dfhack/plugins/ruby/*.rb. Check -the files and the comments for a complete list. - - df.same_pos?(obj1, obj2) -Returns true if both objects are at the same game coordinates. -obj1 and 2 should respond to #pos and #x #y #z. - - df.map_block_at(pos) / map_block_at(x, y, z) -Returns the MapBlock for the coordinates or nil. - - df.map_tile_at(pos) -Returns a MapTile, holding all informations wrt the map tile (read&write). -This class is a ruby specific extention, to facilitate interaction with the -DF map data. Check out hack/ruby/map.rb. - - df.each_map_block { |b| } - df.each_map_block_z(zlevel) { |b| } -Iterates over every map block (opt. on a single z-level). - - df.center_viewscreen(coords) -Centers the DF view on the given coordinates. Accepts x/y/z arguments, or a -single argument responding to pos/x/y/z, eg an Unit, Item, ... - - df.unit_find(arg) -Returns an Unit. -With no arg, returns the currently selected unit (through the (v) or (k) menus) -With a number, returns the unit with this ID -With something else, returns the first unit at the same game coordinates - - df.unit_workers -Returns a list of worker citizen: units of your race & civilization, adults, -not dead, crazy, ghosts or nobles exempted of work. - - df.unit_entitypositions(unit) -Returns the list of EntityPosition occupied by the unit. -Check the 'code' field for a readable name (MANAGER, CHIEF_MEDICAL_DWARF, ...) - - df.match_rawname(name, list) -String fuzzy matching. Returns the list entry most similar to 'name'. -First searches for an exact match, then for a case-insensitive match, and -finally for a case-insensitive substring. -Returns the element from list if there is only one match, or nil. -Most useful to allow the user to specify a raw-defined name, -eg 'gob' for 'GOBLIN' or 'coal' for 'COAL_BITUMINOUS', hence the name. - - df.building_alloc(type, subtype, customtype) - df.building_position(bld, pos, w, h) - df.building_construct(bld, item_list) -Allocates a new building in DF memory, define its position / dimensions, and -create a dwarf job to construct it from the given list of items. -See buildings.rb/buildbed for an example. - - df.each_tree(material) { |t| } -Iterates over every tree of the given material (eg 'maple'). - - df.translate_name(name, in_english=true, only_lastpart=false) -Decode the LanguageName structure as a String as displayed in the game UI. -A shortcut is available through name.to_s - - df.decode_mat(obj) -Returns a MaterialInfo definition for the given object, using its mat_type -and mat_index fields. Also works with a token string argument ('STONE:DOLOMITE') - - -DFHack callbacks ----------------- - -The plugin interfaces with dfhack 'onupdate' hook. -To register ruby code to be run every graphic frame, use: - handle = df.onupdate_register('log') { puts 'i love flooding the console' } -You can also rate-limit when your callback is called to a number of game ticks: - handle = df.onupdate_register('myname', 10) { puts '10 more in-game ticks elapsed' } -In this case, the callback is called immediately, and then every X in-game -ticks (advances only when the game is unpaused). -To stop being called, use: - df.onupdate_unregister handle - -The same mechanism is available for 'onstatechange', but the -SC_BEGIN_UNLOAD event is not propagated to the ruby handler. - -Available states: - :WORLD_LOADED, :WORLD_UNLOADED, :MAP_LOADED, :MAP_UNLOADED, - :VIEWSCREEN_CHANGED, :CORE_INITIALIZED, :PAUSED, :UNPAUSED - - -C++ object manipulation ------------------------ - -The ruby classes defined in ruby-autogen.rb are accessors to the underlying -df C++ objects in-memory. To allocate a new C++ object for use in DF, use the -RubyClass.cpp_new method (see buildings.rb for examples), works for Compounds -only. -A special Compound DFHack::StlString is available for allocating a single c++ -stl::string, so that you can call vmethods that take a string pointer argument -(eg getName). - ex: s = DFHack::StlString.cpp_new ; df.building_find.getName(s) ; p s.str - -Deallocation may work, using the compound method _cpp_delete. Use with caution, -may crash your DF session. It may be simpler to just leak the memory. -_cpp_delete will try to free all memory directly used by the compound, eg -strings and vectors. It will *not* call the class destructor, and will not free -stuff behind pointers. - -C++ std::string fields may be directly re-allocated using standard ruby strings, -e.g. some_unit.name.nickname = 'moo' -More subtle string manipulation, e.g. changing a single character, are not -supported. Read the whole string, manipulate it in ruby, and re-assign it -instead. - -C++ std::vector<> can be iterated as standard ruby Enumerable objects, using -each/map/etc. -To append data to a vector, use vector << newelement or vector.push(newelement) -To insert at a given pos, vector.insert_at(index, value) -To delete an element, vector.delete_at(index) - -You can binary search an element in a vector for a given numeric field value: - df.world.unit.all.binsearch(42, :id) -will find the entry whose 'id' field is 42 (needs the vector to be initially -sorted by this field). The binsearch 2nd argument defaults to :id. - -Any numeric field defined as being an enum value will be converted to a ruby -Symbol. This works for array indexes too. - ex: df.unit_find(:selected).status.labors[:HAUL_FOOD] = true - df.map_tile_at(df.cursor).designation.liquid_type = :Water - -Virtual method calls are supported for C++ objects, with a maximum of 6 -arguments. Arguments / return value are interpreted as Compound/Enums as -specified in the vmethod definition in the xmls. - -Pointer fields are automatically dereferenced ; so a vector of pointer to -Units will yield Units directly. NULL pointers yield the 'nil' value. - - -Examples --------- - -For more complex examples, check the dfhack/scripts/*.rb files. - -Show info on the currently selected unit ('v' or 'k' DF menu) - p df.unit_find.flags1 - -Set a custom nickname to unit with id '123' - df.unit_find(123).name.nickname = 'moo' - -Show current unit profession - p df.unit_find.profession - -Change current unit profession - df.unit_find.profession = :MASON - -Center the screen on unit ID '123' - df.center_viewscreen(df.unit_find(123)) - -Find an item under the game cursor and show its C++ classname - p df.item_find(df.cursor)._rtti_classname - -Find the raws name of the plant under cursor - plant = df.world.plants.all.find { |plt| df.at_cursor?(plt) } - p df.world.raws.plants.all[plant.mat_index].id - -Dig a channel under the cursor - df.map_tile_at(df.cursor).dig(:Channel) - -Spawn 2/7 magma on the tile of the dwarf nicknamed 'hotfeet' - hot = df.unit_citizens.find { |u| u.name.nickname == 'hotfeet' } - df.map_tile_at(hot).spawn_magma(2) - - -Plugin compilation ------------------- - -The plugin consists of the main ruby.cpp native plugin and the *.rb files. - -The native plugin handles only low-level ruby-to-df interaction (eg raw memory -read/write, and dfhack integration), and the .rb files hold end-user helper -functions. - -On dfhack start, the native plugin will initialize the ruby interpreter, and -load hack/ruby/ruby.rb. This one then loads all other .rb files. - -The DF internal structures are described in ruby-autogen.rb . -It is output by ruby/codegen.pl, from dfhack/library/include/df/codegen.out.xml -It contains architecture-specific data (eg DF internal structures field offsets, -which differ between Windows and Linux. Linux and Macosx are the same, as they -both use gcc). -It is stored inside the build directory (eg build/plugins/ruby/ruby-autogen.rb) - -For example, - - - - - -Will generate - class Unit < MemHack::Compound - field(:name, 0) { global :LanguageName } - field(:custom_profession, 60) { stl_string } - field(:profession, 64) { number 16, true } - -The syntax for the 'field' method in ruby-autogen.rb is: -1st argument = name of the method -2nd argument = offset of this field from the beginning of the current struct. - This field depends on the compiler used by Toady to generate DF. -The block argument describes the type of the field: uint32, ptr to global... - -Primitive type access is done through native methods from ruby.cpp (vector length, -raw memory access, etc) diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb deleted file mode 100644 index dae1d455b..000000000 --- a/plugins/ruby/building.rb +++ /dev/null @@ -1,368 +0,0 @@ -module DFHack - class << self - def building_find(what=:selected, y=nil, z=nil) - if what == :selected - return world.buildings.all.binsearch(df.get_selected_building_id) - elsif what.kind_of?(Integer) - # search by building.id - return world.buildings.all.binsearch(what) if not z - - # search by coordinates - x = what - world.buildings.all.find { |b| - b.z == z and - if b.room.extents - dx = x - b.room.x - dy = y - b.room.y - dx >= 0 and dx < b.room.width and - dy >= 0 and dy < b.room.height and - b.room.extents[ dy*b.room.width + dx ] > 0 - else - b.x1 <= x and b.x2 >= x and - b.y1 <= y and b.y2 >= y - end - } - - elsif what.respond_to?(:x) or what.respond_to?(:pos) - # find the building at the same position - what = what.pos if what.respond_to?(:pos) - building_find(what.x, what.y, what.z) - - else - raise "what what?" - end - end - - # allocate a new building object - def building_alloc(type, subtype=-1, custom=-1) - cls = rtti_n2c[BuildingType::Classname[type].to_sym] - raise "invalid building type #{type.inspect}" if not cls - bld = cls.cpp_new - bld.race = ui.race_id - subtype = ConstructionType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Construction - subtype = SiegeengineType.int(subtype) if subtype.kind_of?(::Symbol) and type == :SiegeEngine - subtype = WorkshopType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Workshop - subtype = FurnaceType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Furnace - subtype = CivzoneType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Civzone - subtype = TrapType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Trap - bld.setSubtype(subtype) - bld.setCustomType(custom) - case type - when :Well; bld.bucket_z = bld.z - when :Furnace; bld.melt_remainder[world.raws.inorganics.length] = 0 - when :Coffin; bld.initBurialFlags - when :Trap; bld.ready_timeout = 500 if bld.trap_type == :PressurePlate - when :Floodgate; bld.gate_flags.closed = true - when :GrateWall; bld.gate_flags.closed = true - when :GrateFloor; bld.gate_flags.closed = true - when :BarsVertical; bld.gate_flags.closed = true - when :BarsFloor; bld.gate_flags.closed = true - end - bld - end - - # used by building_setsize - def building_check_bridge_support(bld) - x1 = bld.x1-1 - x2 = bld.x2+1 - y1 = bld.y1-1 - y2 = bld.y2+1 - z = bld.z - (x1..x2).each { |x| - (y1..y2).each { |y| - next if ((x == x1 or x == x2) and - (y == y1 or y == y2)) - if mb = map_block_at(x, y, z) and tile = mb.tiletype[x%16][y%16] and TiletypeShape::BasicShape[Tiletype::Shape[tile]] != :Open - bld.gate_flags.has_support = true - return - end - } - } - bld.gate_flags.has_support = false - end - - # sets x2/centerx/y2/centery from x1/y1/bldtype - # x2/y2 preserved for :FarmPlot etc - def building_setsize(bld) - bld.x2 = bld.x1 if bld.x1 > bld.x2 - bld.y2 = bld.y1 if bld.y1 > bld.y2 - case bld.getType - when :Bridge - bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 - bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 - building_check_bridge_support(bld) - when :FarmPlot, :RoadDirt, :RoadPaved, :Stockpile, :Civzone - bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 - bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 - when :TradeDepot, :Shop - bld.x2 = bld.x1+4 - bld.y2 = bld.y1+4 - bld.centerx = bld.x1+2 - bld.centery = bld.y1+2 - when :SiegeEngine, :Windmill, :Wagon - bld.x2 = bld.x1+2 - bld.y2 = bld.y1+2 - bld.centerx = bld.x1+1 - bld.centery = bld.y1+1 - when :AxleHorizontal - if bld.is_vertical == 1 - bld.x2 = bld.centerx = bld.x1 - bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 - else - bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 - bld.y2 = bld.centery = bld.y1 - end - when :WaterWheel - if bld.is_vertical == 1 - bld.x2 = bld.centerx = bld.x1 - bld.y2 = bld.y1+2 - bld.centery = bld.y1+1 - else - bld.x2 = bld.x1+2 - bld.centerx = bld.x1+1 - bld.y2 = bld.centery = bld.y1 - end - when :Workshop, :Furnace - # Furnace = Custom or default case only - case bld.type - when :Quern, :Millstone, :Tool - bld.x2 = bld.centerx = bld.x1 - bld.y2 = bld.centery = bld.y1 - when :Siege, :Kennels - bld.x2 = bld.x1+4 - bld.y2 = bld.y1+4 - bld.centerx = bld.x1+2 - bld.centery = bld.y1+2 - when :Custom - if bdef = world.raws.buildings.all.binsearch(bld.getCustomType) - bld.x2 = bld.x1 + bdef.dim_x - 1 - bld.y2 = bld.y1 + bdef.dim_y - 1 - bld.centerx = bld.x1 + bdef.workloc_x - bld.centery = bld.y1 + bdef.workloc_y - end - else - bld.x2 = bld.x1+2 - bld.y2 = bld.y1+2 - bld.centerx = bld.x1+1 - bld.centery = bld.y1+1 - end - when :ScrewPump - case bld.direction - when :FromEast - bld.x2 = bld.centerx = bld.x1+1 - bld.y2 = bld.centery = bld.y1 - when :FromSouth - bld.x2 = bld.centerx = bld.x1 - bld.y2 = bld.centery = bld.y1+1 - when :FromWest - bld.x2 = bld.x1+1 - bld.y2 = bld.centery = bld.y1 - bld.centerx = bld.x1 - else - bld.x2 = bld.x1+1 - bld.y2 = bld.centery = bld.y1 - bld.centerx = bld.x1 - end - when :Well - bld.bucket_z = bld.z - bld.x2 = bld.centerx = bld.x1 - bld.y2 = bld.centery = bld.y1 - when :Construction - bld.x2 = bld.centerx = bld.x1 - bld.y2 = bld.centery = bld.y1 - bld.setMaterialAmount(1) - return - else - bld.x2 = bld.centerx = bld.x1 - bld.y2 = bld.centery = bld.y1 - end - bld.setMaterialAmount((bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1) - end - - # set building at position, with optional width/height - def building_position(bld, pos, w=nil, h=nil) - if pos.respond_to?(:x1) - x, y, z = pos.x1, pos.y1, pos.z - w ||= pos.x2-pos.x1+1 if pos.respond_to?(:x2) - h ||= pos.y2-pos.y1+1 if pos.respond_to?(:y2) - elsif pos.respond_to?(:x) - x, y, z = pos.x, pos.y, pos.z - else - x, y, z = pos - end - w ||= pos.w if pos.respond_to?(:w) - h ||= pos.h if pos.respond_to?(:h) - bld.x1 = x - bld.y1 = y - bld.z = z - bld.x2 = bld.x1+w-1 if w - bld.y2 = bld.y1+h-1 if h - building_setsize(bld) - end - - # set map occupancy/stockpile/etc for a building - def building_setoccupancy(bld) - stockpile = (bld.getType == :Stockpile) - complete = (bld.getBuildStage >= bld.getMaxBuildStage) - extents = (bld.room.extents and bld.isExtentShaped) - - z = bld.z - (bld.x1..bld.x2).each { |x| - (bld.y1..bld.y2).each { |y| - next if extents and bld.room.extents[bld.room.width*(y-bld.room.y)+(x-bld.room.x)] == 0 - next if not mb = map_block_at(x, y, z) - des = mb.designation[x%16][y%16] - des.pile = stockpile - des.dig = :No - if complete - bld.updateOccupancy(x, y) - else - mb.occupancy[x%16][y%16].building = :Planned - end - } - } - end - - # link bld into other rooms if it is inside their extents or vice versa - def building_linkrooms(bld) - world.buildings.other[:IN_PLAY].each { |ob| - next if ob.z != bld.z - if bld.is_room and bld.room.extents - next if ob.is_room or ob.x1 < bld.room.x or ob.x1 >= bld.room.x+bld.room.width or ob.y1 < bld.room.y or ob.y1 >= bld.room.y+bld.room.height - next if bld.room.extents[bld.room.width*(ob.y1-bld.room.y)+(ob.x1-bld.room.x)] == 0 - ui.equipment.update.buildings = true - bld.children << ob - ob.parents << bld - elsif ob.is_room and ob.room.extents - next if bld.is_room or bld.x1 < ob.room.x or bld.x1 >= ob.room.x+ob.room.width or bld.y1 < ob.room.y or bld.y1 >= ob.room.y+ob.room.height - next if ob.room.extents[ob.room.width*(bld.y1-ob.room.y)+(bld.x1-ob.room.x)].to_i == 0 - ui.equipment.update.buildings = true - ob.children << bld - bld.parents << ob - end - } - end - - # link the building into the world, set map data, link rooms, bld.id - def building_link(bld) - bld.id = df.building_next_id - df.building_next_id += 1 - - world.buildings.all << bld - bld.categorize(true) - building_setoccupancy(bld) if bld.isSettingOccupancy - building_linkrooms(bld) - end - - # set a design for the building - def building_createdesign(bld, rough=true) - job = bld.jobs[0] - job.mat_type = bld.mat_type - job.mat_index = bld.mat_index - if bld.needsDesign - bld.design = BuildingDesign.cpp_new - bld.design.flags.rough = rough - end - end - - # creates a job to build bld, return it - def building_linkforconstruct(bld) - building_link bld - ref = GeneralRefBuildingHolderst.cpp_new - ref.building_id = bld.id - job = Job.cpp_new - job.job_type = :ConstructBuilding - job.pos = [bld.centerx, bld.centery, bld.z] - job.general_refs << ref - bld.jobs << job - job_link job - job - end - - # construct a building with items or JobItems - def building_construct(bld, items) - job = building_linkforconstruct(bld) - rough = false - items.each { |item| - if items.kind_of?(JobItem) - item.quantity = (bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1 if item.quantity < 0 - job.job_items << item - else - job_attachitem(job, item, :Hauled) - end - rough = true if item.getType == :BOULDER - bld.mat_type = item.getMaterial if bld.mat_type == -1 - bld.mat_index = item.getMaterialIndex if bld.mat_index == -1 - } - building_createdesign(bld, rough) - end - - # construct an abstract building (stockpile, farmplot, ...) - def building_construct_abstract(bld) - case bld.getType - when :Stockpile - max = df.world.buildings.other[:STOCKPILE].map { |s| s.stockpile_number }.max - bld.stockpile_number = max.to_i + 1 - when :Civzone - max = df.world.buildings.other[:ANY_ZONE].map { |z| z.zone_num }.max - bld.zone_num = max.to_i + 1 - end - building_link bld - if !bld.flags.exists - bld.flags.exists = true - bld.initFarmSeasons - end - end - - def building_setowner(bld, unit) - return unless bld.is_room - return if bld.owner == unit - - if bld.owner - if idx = bld.owner.owned_buildings.index { |ob| ob.id == bld.id } - bld.owner.owned_buildings.delete_at(idx) - end - if spouse = bld.owner.relations.spouse_tg and - idx = spouse.owned_buildings.index { |ob| ob.id == bld.id } - spouse.owned_buildings.delete_at(idx) - end - end - bld.owner = unit - if unit - unit.owned_buildings << bld - if spouse = bld.owner.relations.spouse_tg and - !spouse.owned_buildings.index { |ob| ob.id == bld.id } and - bld.canUseSpouseRoom - spouse.owned_buildings << bld - end - end - end - - # creates a job to deconstruct the building - def building_deconstruct(bld) - job = Job.cpp_new - refbuildingholder = GeneralRefBuildingHolderst.cpp_new - job.job_type = :DestroyBuilding - refbuildingholder.building_id = bld.id - job.general_refs << refbuildingholder - bld.jobs << job - job_link job - job - end - - # exemple usage - def buildbed(pos=cursor) - raise 'where to ?' if pos.x < 0 - - item = world.items.all.find { |i| - i.kind_of?(ItemBedst) and - item_isfree(i) - } - raise 'no free bed, build more !' if not item - - bld = building_alloc(:Bed) - building_position(bld, pos) - building_construct(bld, [item]) - end - end -end diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl deleted file mode 100755 index 95d91c3ab..000000000 --- a/plugins/ruby/codegen.pl +++ /dev/null @@ -1,1166 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -use XML::LibXML; - -our @lines_rb; - -my $os = $ARGV[2] or die('os not provided (argv[2])'); -if ($os =~ /linux/i or $os =~ /darwin/i) { - $os = 'linux'; -} elsif ($os =~ /windows/i) { - $os = 'windows'; -} else { - die "Unknown OS: " . $ARGV[2] . "\n"; -} - -my $arch = $ARGV[3] or die('arch not provided (argv[3])'); -if ($arch =~ /64/i) { - $arch = 64; -} elsif ($arch =~ /32/i) { - $arch = 32; -} else { - die "Unknown architecture: " . $ARGV[3] . "\n"; -} - -# 32 bits on Windows and 32-bit *nix, 64 bits on 64-bit *nix -my $SIZEOF_LONG; -if ($os eq 'windows' || $arch == 32) { - $SIZEOF_LONG = 4; -} else { - $SIZEOF_LONG = 8; -} - -my $SIZEOF_PTR = ($arch == 64) ? 8 : 4; - -sub indent_rb(&) { - my ($sub) = @_; - my @lines; - { - local @lines_rb; - $sub->(); - @lines = map { " " . $_ } @lines_rb; - } - push @lines_rb, @lines -} - -sub rb_ucase { - my ($name) = @_; - return $name if ($name eq uc($name)); - return join("", map { ucfirst $_ } (split('_', $name))); -} - - -my %item_renderer = ( - 'global' => \&render_item_global, - 'number' => \&render_item_number, - 'container' => \&render_item_container, - 'compound' => \&render_item_compound, - 'pointer' => \&render_item_pointer, - 'static-array' => \&render_item_staticarray, - 'primitive' => \&render_item_primitive, - 'bytes' => \&render_item_bytes, -); - - -my %global_types; -our $current_typename; - -sub render_global_enum { - my ($name, $type) = @_; - - my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname < MemHack::Enum"; - indent_rb { - render_enum_fields($type); - }; - push @lines_rb, "end\n"; -} - -sub render_enum_fields { - my ($type) = @_; - - push @lines_rb, "ENUM = Hash.new"; - push @lines_rb, "NUME = Hash.new"; - - my %attr_type; - my %attr_list; - render_enum_initattrs($type, \%attr_type, \%attr_list); - - my $value = -1; - for my $item ($type->findnodes('child::enum-item')) - { - $value = $item->getAttribute('value') || ($value+1); - my $elemname = $item->getAttribute('name'); # || "unk_$value"; - - if ($elemname) - { - my $rbelemname = rb_ucase($elemname); - push @lines_rb, "ENUM[$value] = :$rbelemname ; NUME[:$rbelemname] = $value"; - for my $iattr ($item->findnodes('child::item-attr')) - { - my $attr = render_enum_attr($rbelemname, $iattr, \%attr_type, \%attr_list); - $lines_rb[$#lines_rb] .= ' ; ' . $attr; - } - } - } -} - -sub render_enum_initattrs { - my ($type, $attr_type, $attr_list) = @_; - - for my $attr ($type->findnodes('child::enum-attr')) - { - my $rbattr = rb_ucase($attr->getAttribute('name')); - my $typeattr = $attr->getAttribute('type-name'); - # find how we need to encode the attribute values: string, symbol (for enums), raw (number, bool) - if ($typeattr) { - if ($global_types{$typeattr}) { - $attr_type->{$rbattr} = 'symbol'; - } else { - $attr_type->{$rbattr} = 'naked'; - } - } else { - $attr_type->{$rbattr} = 'quote'; - } - - my $def = $attr->getAttribute('default-value'); - if ($attr->getAttribute('is-list')) - { - push @lines_rb, "$rbattr = Hash.new { |h, k| h[k] = [] }"; - $attr_list->{$rbattr} = 1; - } - elsif ($def) - { - $def = ":$def" if ($attr_type->{$rbattr} eq 'symbol'); - $def =~ s/'/\\'/g if ($attr_type->{$rbattr} eq 'quote'); - $def = "'$def'" if ($attr_type->{$rbattr} eq 'quote'); - push @lines_rb, "$rbattr = Hash.new($def)"; - } - else - { - push @lines_rb, "$rbattr = Hash.new"; - } - } -} - -sub render_enum_attr { - my ($rbelemname, $iattr, $attr_type, $attr_list) = @_; - - my $ian = $iattr->getAttribute('name'); - my $iav = $iattr->getAttribute('value'); - my $rbattr = rb_ucase($ian); - - my $op = ($attr_list->{$rbattr} ? '<<' : '='); - - $iav = ":$iav" if ($attr_type->{$rbattr} eq 'symbol'); - $iav =~ s/'/\\'/g if ($attr_type->{$rbattr} eq 'quote'); - $iav = "'$iav'" if ($attr_type->{$rbattr} eq 'quote'); - - return "${rbattr}[:$rbelemname] $op $iav"; -} - - -sub render_global_bitfield { - my ($name, $type) = @_; - - my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname < MemHack::Compound"; - indent_rb { - render_bitfield_fields($type); - }; - push @lines_rb, "end\n"; -} - -sub render_bitfield_fields { - my ($type) = @_; - - push @lines_rb, "field(:_whole, 0) {"; - indent_rb { - render_item_number($type); - }; - push @lines_rb, "}"; - - my $shift = 0; - for my $field ($type->findnodes('child::ld:field')) - { - my $count = $field->getAttribute('count') || 1; - my $name = $field->getAttribute('name'); - my $type = $field->getAttribute('type-name'); - my $enum = rb_ucase($type) if ($type and $global_types{$type}); - $name = $field->getAttribute('ld:anon-name') if (!$name); - print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); - - if ($name) - { - if ($enum) { - push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }"; - } elsif ($count == 1) { - push @lines_rb, "field(:$name, 0) { bit $shift }"; - } else { - push @lines_rb, "field(:$name, 0) { bits $shift, $count }"; - } - } - - $shift += $count; - } -} - - -my %seen_class; -our $compound_off; -our $compound_pointer; - -sub render_global_class { - my ($name, $type) = @_; - - my $meta = $type->getAttribute('ld:meta'); - my $rbname = rb_ucase($name); - - # ensure pre-definition of ancestors - my $parent = $type->getAttribute('inherits-from'); - render_global_class($parent, $global_types{$parent}) if ($parent and !$seen_class{$parent}); - - return if $seen_class{$name}; - $seen_class{$name}++; - - local $compound_off = 0; - $compound_off = $SIZEOF_PTR if ($meta eq 'class-type'); - $compound_off = sizeof($global_types{$parent}) if $parent; - local $current_typename = $rbname; - - my $rtti_name; - if ($meta eq 'class-type') - { - $rtti_name = $type->getAttribute('original-name') || - $type->getAttribute('type-name') || - $name; - } - - my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); - my $ienum; - if (($type->getAttribute('ld:subtype') or '') eq 'df-other-vectors-type') - { - $rbparent = 'MemHack::OtherVectors'; - $ienum = rb_ucase($type->getAttribute('index-enum')); - } - push @lines_rb, "class $rbname < $rbparent"; - indent_rb { - my $sz = sizeof($type); - # see comment is sub sizeof ; but gcc has sizeof(cls) aligned - $sz = align_field($sz, $SIZEOF_PTR) if $os eq 'linux' and $meta eq 'class-type'; - push @lines_rb, "sizeof $sz\n"; - - push @lines_rb, "rtti_classname :$rtti_name\n" if $rtti_name; - - push @lines_rb, "ienum $ienum\n" if $ienum; - - render_struct_fields($type); - - my $vms = $type->findnodes('child::virtual-methods')->[0]; - if ($vms) - { - my $voff = render_class_vmethods_voff($parent); - render_class_vmethods($vms, $voff); - } - }; - push @lines_rb, "end\n"; -} - -sub render_struct_fields { - my ($type) = @_; - - my $isunion = $type->getAttribute('is-union'); - - for my $field ($type->findnodes('child::ld:field')) - { - my $name = $field->getAttribute('name'); - $name = $field->getAttribute('ld:anon-name') if (!$name); - - if (!$name and $field->getAttribute('ld:anon-compound')) - { - render_struct_fields($field); - } - else - { - $compound_off = align_field($compound_off, get_field_align($field)); - if ($name) - { - push @lines_rb, "field(:$name, $compound_off) {"; - indent_rb { - render_item($field); - }; - push @lines_rb, "}"; - - render_struct_field_refs($type, $field, $name); - } - } - - $compound_off += sizeof($field) if (!$isunion); - } -} - -# handle generating accessor for xml attributes ref-target, refers-to etc -sub render_struct_field_refs { - my ($parent, $field, $name) = @_; - - my $reftg = $field->getAttribute('ref-target'); - - my $refto = $field->getAttribute('refers-to'); - render_field_refto($parent, $name, $refto) if ($refto); - - my $meta = $field->getAttribute('ld:meta'); - my $item = $field->findnodes('child::ld:item')->[0]; - if ($meta and $meta eq 'container' and $item) { - my $itemreftg = $item->getAttribute('ref-target'); - render_container_reftarget($parent, $item, $name, $itemreftg) if $itemreftg; - } elsif ($reftg) { - render_field_reftarget($parent, $field, $name, $reftg); - } -} - -sub render_field_reftarget { - my ($parent, $field, $name, $reftg) = @_; - - my $tg = $global_types{$reftg}; - return if (!$tg); - - my $tgname = "${name}_tg"; - $tgname =~ s/_id(.?.?)_tg/_tg$1/; - - my $aux = $field->getAttribute('aux-value'); - if ($aux) { - # minimal support (aims is unit.caste_tg) - return if $aux !~ /^\$\$\.[^_][\w\.]+$/; - $aux =~ s/\$\$\.//; - - for my $codehelper ($tg->findnodes('child::code-helper')) { - if ($codehelper->getAttribute('name') eq 'find-instance') { - my $helper = $codehelper->textContent; - $helper =~ s/\$global/df/; - $helper =~ s/\$\$/$aux/; - $helper =~ s/\$/$name/; - if ($helper =~ /^[\w\.\[\]]+$/) { - push @lines_rb, "def $tgname ; $helper ; end"; - } - } - } - return; - } - - my $tgvec = $tg->getAttribute('instance-vector'); - return if (!$tgvec); - my $idx = $tg->getAttribute('key-field'); - - $tgvec =~ s/^\$global/df/; - return if $tgvec !~ /^[\w\.]+$/; - - for my $othername (map { $_->getAttribute('name') } $parent->findnodes('child::ld:field')) { - $tgname .= '_' if ($othername and $tgname eq $othername); - } - - if ($idx) { - my $fidx = ''; - $fidx = ', :' . $idx if ($idx ne 'id'); - push @lines_rb, "def $tgname ; ${tgvec}.binsearch($name$fidx) ; end"; - } else { - push @lines_rb, "def $tgname ; ${tgvec}[$name] ; end"; - } - -} - -sub render_field_refto { - my ($parent, $name, $tgvec) = @_; - - $tgvec =~ s/^\$global/df/; - $tgvec =~ s/\[\$\]$//; - return if $tgvec !~ /^[\w\.]+$/; - - my $tgname = "${name}_tg"; - $tgname =~ s/_id(.?.?)_tg/_tg$1/; - - for my $othername (map { $_->getAttribute('name') } $parent->findnodes('child::ld:field')) { - $tgname .= '_' if ($othername and $tgname eq $othername); - } - - push @lines_rb, "def $tgname ; ${tgvec}[$name] ; end"; -} - -sub render_container_reftarget { - my ($parent, $item, $name, $reftg) = @_; - - my $aux = $item->getAttribute('aux-value'); - return if ($aux); # TODO - - my $tg = $global_types{$reftg}; - return if (!$tg); - my $tgvec = $tg->getAttribute('instance-vector'); - return if (!$tgvec); - my $idx = $tg->getAttribute('key-field'); - - $tgvec =~ s/^\$global/df/; - return if $tgvec !~ /^[\w\.]+$/; - - my $tgname = "${name}_tg"; - $tgname =~ s/_id(.?.?)_tg/_tg$1/; - - for my $othername (map { $_->getAttribute('name') } $parent->findnodes('child::ld:field')) { - $tgname .= '_' if ($othername and $tgname eq $othername); - } - - if ($idx) { - my $fidx = ''; - $fidx = ', :' . $idx if ($idx ne 'id'); - push @lines_rb, "def $tgname ; $name.map { |i| $tgvec.binsearch(i$fidx) } ; end"; - } else { - push @lines_rb, "def $tgname ; $name.map { |i| ${tgvec}[i] } ; end"; - } -} - -# return the size of the parent's vtables -sub render_class_vmethods_voff { - my ($name) = @_; - - return 0 if !$name; - - my $type = $global_types{$name}; - my $parent = $type->getAttribute('inherits-from'); - - my $voff = render_class_vmethods_voff($parent); - my $vms = $type->findnodes('child::virtual-methods')->[0]; - - for my $meth ($vms->findnodes('child::vmethod')) - { - $voff += $SIZEOF_PTR if $meth->getAttribute('is-destructor') and $os eq 'linux'; - $voff += $SIZEOF_PTR; - } - - return $voff; -} - -sub render_class_vmethods { - my ($vms, $voff) = @_; - - for my $meth ($vms->findnodes('child::vmethod')) - { - my $name = $meth->getAttribute('name'); - - if ($name) - { - my @argnames; - my @argargs; - - # check if arguments need special treatment (eg auto-convert from symbol to enum value) - for my $arg ($meth->findnodes('child::ld:field')) - { - my $nr = $#argnames + 1; - my $argname = lcfirst($arg->getAttribute('name') || "arg$nr"); - push @argnames, $argname; - - if ($arg->getAttribute('ld:meta') eq 'global' and $arg->getAttribute('ld:subtype') eq 'enum') { - push @argargs, rb_ucase($arg->getAttribute('type-name')) . ".int($argname)"; - } else { - push @argargs, $argname; - } - } - - # write vmethod ruby wrapper - push @lines_rb, "def $name(" . join(', ', @argnames) . ')'; - indent_rb { - my $args = join('', map { ", $_" } @argargs); - my $ret = $meth->findnodes('child::ret-type')->[0]; - my $call; - if (!$ret or $ret->getAttribute('ld:meta') ne 'primitive') { - $call = "DFHack.vmethod_call(self, $voff$args)"; - } else { - $call = "DFHack.vmethod_call_mem_return(self, $voff, rv$args)"; - } - render_class_vmethod_ret($call, $ret); - }; - push @lines_rb, 'end'; - } - - # on linux, the destructor uses 2 entries - $voff += $SIZEOF_PTR if $meth->getAttribute('is-destructor') and $os eq 'linux'; - $voff += $SIZEOF_PTR; - } -} - -sub render_class_vmethod_ret { - my ($call, $ret) = @_; - - if (!$ret) - { - # method returns void, hide return value - push @lines_rb, "$call ; nil"; - return; - } - - my $retmeta = $ret->getAttribute('ld:meta') || ''; - if ($retmeta eq 'global') - { - # method returns an enum value: auto-convert to symbol - my $retname = $ret->getAttribute('type-name'); - if ($retname and $global_types{$retname} and - $global_types{$retname}->getAttribute('ld:meta') eq 'enum-type') - { - push @lines_rb, rb_ucase($retname) . ".sym($call)"; - } - else - { - print "vmethod global nonenum $call\n"; - push @lines_rb, $call; - } - - } - elsif ($retmeta eq 'number') - { - # raw method call returns an int32, mask according to actual return type - my $retsubtype = $ret->getAttribute('ld:subtype'); - my $retbits = sizeof($ret) * 8; - push @lines_rb, "val = $call"; - if ($retsubtype eq 'bool') - { - push @lines_rb, "(val & 1) != 0"; - } - elsif ($ret->getAttribute('ld:unsigned')) - { - push @lines_rb, "val & ((1 << $retbits) - 1)"; - } - elsif ($retbits != 32) - { - # manual sign extension - push @lines_rb, "val &= ((1 << $retbits) - 1)"; - push @lines_rb, "((val >> ($retbits-1)) & 1) == 0 ? val : val - (1 << $retbits)"; - } - - } - elsif ($retmeta eq 'pointer') - { - # method returns a pointer to some struct, create the correct ruby wrapper - push @lines_rb, "ptr = $call"; - push @lines_rb, "class << self"; - indent_rb { - render_item($ret->findnodes('child::ld:item')->[0]); - }; - push @lines_rb, "end._at(ptr) if ptr != 0"; - } - elsif ($retmeta eq 'primitive') - { - my $subtype = $ret->getAttribute('ld:subtype'); - if ($subtype eq 'stl-string') { - push @lines_rb, "rv = DFHack::StlString.cpp_new"; - push @lines_rb, $call; - push @lines_rb, "rv"; - } else { - print "Unknown return subtype for $call\n"; - push @lines_rb, "nil"; - } - } - else - { - print "vmethod unkret $call\n"; - push @lines_rb, $call; - } -} - -sub render_global_objects { - my (@objects) = @_; - my @global_objects; - - local $compound_off = 0; - local $current_typename = 'Global'; - - # define all globals as 'fields' of a virtual globalobject wrapping the whole address space - push @lines_rb, 'class GlobalObjects < MemHack::Compound'; - indent_rb { - for my $obj (@objects) - { - my $oname = $obj->getAttribute('name'); - - # check if the symbol is defined in xml to avoid NULL deref - my $addr = "DFHack.get_global_address('$oname')"; - push @lines_rb, "addr = $addr"; - push @lines_rb, "if addr != 0"; - indent_rb { - push @lines_rb, "field(:$oname, addr) {"; - my $item = $obj->findnodes('child::ld:item')->[0]; - indent_rb { - render_item($item); - }; - push @lines_rb, "}"; - }; - push @lines_rb, "end"; - - push @global_objects, $oname; - } - }; - push @lines_rb, "end"; - - # define friendlier accessors, eg df.world -> DFHack::GlobalObjects.new._at(0).world - indent_rb { - push @lines_rb, "Global = GlobalObjects.new._at(0)"; - for my $oname (@global_objects) - { - push @lines_rb, "if DFHack.get_global_address('$oname') != 0"; - indent_rb { - push @lines_rb, "def self.$oname ; Global.$oname ; end"; - push @lines_rb, "def self.$oname=(v) ; Global.$oname = v ; end"; - }; - push @lines_rb, "end"; - } - }; -} - - -my %align_cache; -my %sizeof_cache; - -sub align_field { - my ($off, $fldalign) = @_; - my $dt = $off % $fldalign; - $off += $fldalign - $dt if $dt > 0; - return $off; -} - -sub get_field_align { - my ($field) = @_; - my $al = $SIZEOF_PTR; - my $meta = $field->getAttribute('ld:meta'); - - if ($meta eq 'number') { - $al = sizeof($field); - # linux aligns int64_t to $SIZEOF_PTR, windows to 8 - # floats are 4 bytes so no pb - $al = 4 if ($al > 4 and (($os eq 'linux' and $arch == 32) or $al != 8)); - } elsif ($meta eq 'global') { - $al = get_global_align($field); - } elsif ($meta eq 'compound') { - $al = get_compound_align($field); - } elsif ($meta eq 'static-array') { - my $tg = $field->findnodes('child::ld:item')->[0]; - $al = get_field_align($tg); - } elsif ($meta eq 'bytes') { - $al = $field->getAttribute('alignment') || 1; - } elsif ($meta eq 'primitive') { - my $subtype = $field->getAttribute('ld:subtype'); - if ($subtype eq 'stl-fstream' and $os eq 'windows') { $al = 8; } - } - - return $al; -} - -sub get_global_align { - my ($field) = @_; - - my $typename = $field->getAttribute('type-name'); - return $align_cache{$typename} if $align_cache{$typename}; - - my $g = $global_types{$typename}; - - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') - { - my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; - print "$st type $base\n" if $base !~ /int(\d+)_t/; - # dont cache, field->base-type may differ - return $1/8; - } - - my $al = 1; - for my $gf ($g->findnodes('child::ld:field')) { - my $fld_al = get_field_align($gf); - $al = $fld_al if $fld_al > $al; - } - $align_cache{$typename} = $al; - - return $al; -} - -sub get_compound_align { - my ($field) = @_; - - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum') - { - my $base = $field->getAttribute('base-type') || 'uint32_t'; - if ($base eq 'long') { - return $SIZEOF_LONG; - } - print "$st type $base\n" if $base !~ /int(\d+)_t/; - return $1/8; - } - - my $al = 1; - for my $f ($field->findnodes('child::ld:field')) { - my $fal = get_field_align($f); - $al = $fal if $fal > $al; - } - - return $al; -} - -sub get_container_count { - my ($field) = @_; - my $count = $field->getAttribute('count'); - if ($count) { - return $count; - } - my $enum = $field->getAttribute('index-enum'); - if ($enum) { - my $tag = $global_types{$enum}; - return $tag->getAttribute('last-value') + 1; - } - - return 0; -} - -sub sizeof { - my ($field) = @_; - my $meta = $field->getAttribute('ld:meta'); - - if ($meta eq 'number') { - if ($field->getAttribute('ld:subtype') eq 'long') { - return $SIZEOF_LONG; - } - - return $field->getAttribute('ld:bits')/8; - - } elsif ($meta eq 'pointer') { - return $SIZEOF_PTR; - - } elsif ($meta eq 'static-array') { - my $count = get_container_count($field); - my $tg = $field->findnodes('child::ld:item')->[0]; - return $count * sizeof($tg); - - } elsif ($meta eq 'bitfield-type' or $meta eq 'enum-type') { - my $base = $field->getAttribute('base-type') || 'uint32_t'; - print "$meta type $base\n" if $base !~ /int(\d+)_t/; - return $1/8; - - } elsif ($meta eq 'global') { - my $typename = $field->getAttribute('type-name'); - return $sizeof_cache{$typename} if $sizeof_cache{$typename}; - - my $g = $global_types{$typename}; - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum' or $g->getAttribute('ld:meta') eq 'bitfield-type') - { - my $base = $field->getAttribute('base-type') || $g->getAttribute('base-type') || 'uint32_t'; - print "$st type $base\n" if $base !~ /int(\d+)_t/; - return $1/8; - } - - return sizeof($g); - - } elsif ($meta eq 'class-type' or $meta eq 'struct-type' or $meta eq 'compound') { - return sizeof_compound($field); - - } elsif ($meta eq 'container') { - my $subtype = $field->getAttribute('ld:subtype'); - - if ($subtype eq 'stl-vector') { - if ($os eq 'linux' or $os eq 'windows') { - return ($arch == 64) ? 24 : 12; - } else { - print "sizeof stl-vector on $os\n"; - } - } elsif ($subtype eq 'stl-bit-vector') { - if ($os eq 'linux') { - return ($arch == 64) ? 40 : 20; - } elsif ($os eq 'windows') { - return ($arch == 64) ? 32 : 16; - } else { - print "sizeof stl-bit-vector on $os\n"; - } - } elsif ($subtype eq 'stl-deque') { - if ($os eq 'linux') { - return ($arch == 64) ? 80 : 40; - } elsif ($os eq 'windows') { - return ($arch == 64) ? 40 : 20; - } else { - print "sizeof stl-deque on $os\n"; - } - } elsif ($subtype eq 'df-linked-list') { - return 3 * $SIZEOF_PTR; - } elsif ($subtype eq 'df-flagarray') { - return 2 * $SIZEOF_PTR; # XXX length may be 4 on windows? - } elsif ($subtype eq 'df-static-flagarray') { - return $field->getAttribute('count'); - } elsif ($subtype eq 'df-array') { - return 4 + $SIZEOF_PTR; # XXX 4->2 ? - } else { - print "sizeof container $subtype\n"; - } - - } elsif ($meta eq 'primitive') { - my $subtype = $field->getAttribute('ld:subtype'); - - if ($subtype eq 'stl-string') { - if ($os eq 'linux') { - return ($arch == 64) ? 8 : 4; - } elsif ($os eq 'windows') { - return ($arch == 64) ? 32 : 24; - } else { - print "sizeof stl-string on $os\n"; - } - print "sizeof stl-string\n"; - } elsif ($subtype eq 'stl-fstream') { - if ($os eq 'linux') { - return 284; # TODO: fix on x64 - } elsif ($os eq 'windows') { - return ($arch == 64) ? 280 : 192; - } else { - print "sizeof stl-fstream on $os\n"; - } - print "sizeof stl-fstream\n"; - } else { - print "sizeof primitive $subtype\n"; - } - - } elsif ($meta eq 'bytes') { - return $field->getAttribute('size'); - } else { - print "sizeof $meta\n"; - } -} - -sub sizeof_compound { - my ($field) = @_; - - my $typename = $field->getAttribute('type-name'); - return $sizeof_cache{$typename} if $typename and $sizeof_cache{$typename}; - - my $meta = $field->getAttribute('ld:meta'); - - my $st = $field->getAttribute('ld:subtype') || ''; - if ($st eq 'bitfield' or $st eq 'enum') - { - my $base = $field->getAttribute('base-type') || 'uint32_t'; - if ($base eq 'long') { - $sizeof_cache{$typename} = $SIZEOF_LONG if $typename; - return $SIZEOF_LONG; - } - print "$st type $base\n" if $base !~ /int(\d+)_t/; - $sizeof_cache{$typename} = $1/8 if $typename; - return $1/8; - } - - if ($field->getAttribute('is-union')) - { - my $sz = 0; - for my $f ($field->findnodes('child::ld:field')) - { - my $fsz = sizeof($f); - $sz = $fsz if $fsz > $sz; - } - return $sz; - } - - my $parent = $field->getAttribute('inherits-from'); - my $off = 0; - $off = $SIZEOF_PTR if ($meta eq 'class-type'); - $off = sizeof($global_types{$parent}) if ($parent); - - my $al = 1; - $al = $SIZEOF_PTR if ($meta eq 'class-type'); - - for my $f ($field->findnodes('child::ld:field')) - { - my $fa = get_field_align($f); - $al = $fa if $fa > $al; - $off = align_field($off, $fa); - $off += sizeof($f); - } - - # GCC: class a { vtable; char; } ; class b:a { char c2; } -> c2 has offset 5 (Windows MSVC: offset 8) - $al = 1 if ($meta eq 'class-type' and $os eq 'linux'); - $off = align_field($off, $al); - $sizeof_cache{$typename} = $off if $typename; - - return $off; -} - - -sub render_item { - my ($item) = @_; - return if (!$item); - - my $meta = $item->getAttribute('ld:meta'); - - my $renderer = $item_renderer{$meta}; - if ($renderer) { - $renderer->($item); - } else { - print "no render item $meta\n"; - } -} - -sub render_item_global { - my ($item) = @_; - - my $typename = $item->getAttribute('type-name'); - my $subtype = $item->getAttribute('ld:subtype'); - - if ($subtype and $subtype eq 'enum') { - render_item_number($item); - } else { - my $rbname = rb_ucase($typename); - push @lines_rb, "global :$rbname"; - } -} - -sub render_item_number { - my ($item, $classname) = @_; - - my $subtype = $item->getAttribute('ld:subtype'); - my $meta = $item->getAttribute('ld:meta'); - my $initvalue = $item->getAttribute('init-value'); - $initvalue ||= -1 if $item->getAttribute('refers-to') or $item->getAttribute('ref-target'); - my $typename = $item->getAttribute('type-name'); - undef $typename if ($meta and $meta eq 'bitfield-type'); - my $g = $global_types{$typename} if ($typename); - $typename = rb_ucase($typename) if $typename; - $typename = $classname if (!$typename and $subtype and $subtype eq 'enum'); # compound enum - - $initvalue = 1 if ($initvalue and $initvalue eq 'true'); - $initvalue = ":$initvalue" if ($initvalue and $typename and $initvalue =~ /[a-zA-Z]/); - $initvalue ||= 'nil' if $typename; - - $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'bitfield' or $subtype eq 'enum'); - $subtype ||= $g->getAttribute('base-type') if ($g); - $subtype = 'int32_t' if (!$subtype); - - if ($subtype eq 'uint64_t') { - push @lines_rb, 'number 64, false'; - } elsif ($subtype eq 'int64_t') { - push @lines_rb, 'number 64, true'; - } elsif ($subtype eq 'uint32_t') { - push @lines_rb, 'number 32, false'; - } elsif ($subtype eq 'int32_t') { - push @lines_rb, 'number 32, true'; - } elsif ($subtype eq 'uint16_t') { - push @lines_rb, 'number 16, false'; - } elsif ($subtype eq 'int16_t') { - push @lines_rb, 'number 16, true'; - } elsif ($subtype eq 'uint8_t') { - push @lines_rb, 'number 8, false'; - } elsif ($subtype eq 'int8_t') { - push @lines_rb, 'number 8, true'; - } elsif ($subtype eq 'bool') { - push @lines_rb, 'number 8, true'; - $initvalue ||= 'nil'; - $typename ||= 'BooleanEnum'; - } elsif ($subtype eq 'long') { - push @lines_rb, 'number ' . $SIZEOF_LONG . ', true'; - } elsif ($subtype eq 's-float') { - push @lines_rb, 'float'; - return; - } elsif ($subtype eq 'd-float') { - push @lines_rb, 'double'; - return; - } else { - print "no render number $subtype\n"; - return; - } - - $lines_rb[$#lines_rb] .= ", $initvalue" if ($initvalue); - $lines_rb[$#lines_rb] .= ", $typename" if ($typename); -} - -sub render_item_compound { - my ($item) = @_; - - my $subtype = $item->getAttribute('ld:subtype'); - - local $compound_off = 0; - my $classname = $current_typename . '_' . rb_ucase($item->getAttribute('ld:typedef-name')); - local $current_typename = $classname; - - if (!$subtype || $subtype eq 'bitfield') - { - push @lines_rb, "compound(:$classname) {"; - indent_rb { - # declare sizeof() only for useful compound, eg the one behind pointers - # that the user may want to allocate - my $sz = sizeof($item); - push @lines_rb, "sizeof $sz\n" if $compound_pointer; - - if (!$subtype) { - local $compound_pointer = 0; - render_struct_fields($item); - } else { - render_bitfield_fields($item); - } - }; - push @lines_rb, "}" - } - elsif ($subtype eq 'enum') - { - push @lines_rb, "class ::DFHack::$classname < MemHack::Enum"; - indent_rb { - # declare constants - render_enum_fields($item); - }; - push @lines_rb, "end\n"; - - # actual field - render_item_number($item, $classname); - } - else - { - print "no render compound $subtype\n"; - } -} - -sub render_item_container { - my ($item) = @_; - - my $subtype = $item->getAttribute('ld:subtype'); - my $rbmethod = join('_', split('-', $subtype)); - my $tg = $item->findnodes('child::ld:item')->[0]; - my $indexenum = $item->getAttribute('index-enum'); - my $count = $item->getAttribute('count'); - if ($tg) - { - if ($rbmethod eq 'df_linked_list') { - push @lines_rb, "$rbmethod {"; - } else { - my $tglen = sizeof($tg) if $tg; - push @lines_rb, "$rbmethod($tglen) {"; - } - indent_rb { - render_item($tg); - }; - push @lines_rb, "}"; - } - elsif ($indexenum) - { - $indexenum = rb_ucase($indexenum); - if ($count) { - push @lines_rb, "$rbmethod($count, $indexenum)"; - } else { - push @lines_rb, "$rbmethod($indexenum)"; - } - } - else - { - if ($count) { - push @lines_rb, "$rbmethod($count)"; - } else { - push @lines_rb, "$rbmethod"; - } - } -} - -sub render_item_pointer { - my ($item) = @_; - - my $tg = $item->findnodes('child::ld:item')->[0]; - my $ary = $item->getAttribute('is-array') || ''; - - if ($ary eq 'true') { - my $tglen = sizeof($tg) if $tg; - push @lines_rb, "pointer_ary($tglen) {"; - } else { - push @lines_rb, "pointer {"; - } - indent_rb { - local $compound_pointer = 1; - render_item($tg); - }; - push @lines_rb, "}"; -} - -sub render_item_staticarray { - my ($item) = @_; - - my $count = get_container_count($item); - my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = sizeof($tg) if $tg; - my $indexenum = $item->getAttribute('index-enum'); - - if ($indexenum) { - $indexenum = rb_ucase($indexenum); - push @lines_rb, "static_array($count, $tglen, $indexenum) {"; - } else { - push @lines_rb, "static_array($count, $tglen) {"; - } - indent_rb { - render_item($tg); - }; - push @lines_rb, "}"; -} - -sub render_item_primitive { - my ($item) = @_; - - my $subtype = $item->getAttribute('ld:subtype'); - if ($subtype eq 'stl-string') { - push @lines_rb, "stl_string"; - } elsif ($subtype eq 'stl-fstream') { - } else { - print "no render primitive $subtype\n"; - } -} - -sub render_item_bytes { - my ($item) = @_; - - my $subtype = $item->getAttribute('ld:subtype'); - if ($subtype eq 'padding') { - } elsif ($subtype eq 'static-string') { - my $size = $item->getAttribute('size') || -1; - push @lines_rb, "static_string($size)"; - } else { - print "no render bytes $subtype\n"; - } -} - - - -my $input = $ARGV[0] or die "need input xml"; -my $output = $ARGV[1] or die "need output file"; - -my $doc = XML::LibXML->new()->parse_file($input); -$global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type'); - -# render enums first, this allows later code to refer to them directly -my @nonenums; -for my $name (sort { $a cmp $b } keys %global_types) -{ - my $type = $global_types{$name}; - my $meta = $type->getAttribute('ld:meta'); - if ($meta eq 'enum-type') { - render_global_enum($name, $type); - } else { - push @nonenums, $name; - } -} - -# render other structs/bitfields/classes -for my $name (@nonenums) -{ - my $type = $global_types{$name}; - my $meta = $type->getAttribute('ld:meta'); - if ($meta eq 'struct-type' or $meta eq 'class-type') { - render_global_class($name, $type); - } elsif ($meta eq 'bitfield-type') { - render_global_bitfield($name, $type); - } else { - print "no render global type $meta\n"; - } -} - -# render globals -render_global_objects($doc->findnodes('/ld:data-definition/ld:global-object')); - - -open FH, ">$output"; -print FH "module DFHack\n"; -print FH "$_\n" for @lines_rb; -print FH "end\n"; -close FH; diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb deleted file mode 100644 index 59477c382..000000000 --- a/plugins/ruby/item.rb +++ /dev/null @@ -1,44 +0,0 @@ -module DFHack - class << self - # return an Item - # arg similar to unit.rb/unit_find; no arg = 'k' menu - def item_find(what=:selected, y=nil, z=nil) - if what == :selected - return world.items.all.binsearch(df.get_selected_item_id) - elsif what.kind_of?(Integer) - # search by id - return world.items.all.binsearch(what) if not z - # search by position - x = what - world.items.all.find { |i| i.pos.x == x and i.pos.y == y and i.pos.z == z } - elsif what.respond_to?(:x) or what.respond_to?(:pos) - world.items.all.find { |i| same_pos?(what, i) } - else - raise "what what?" - end - end - - # check item flags to see if it is suitable for use as a job input material - def item_isfree(i, check_empty=true) - !i.flags.trader and - !i.flags.in_job and - !i.flags.construction and - !i.flags.removed and - !i.flags.forbid and - !i.flags.dump and - !i.flags.owned and - !i.flags.in_chest and # used as hospital supply ? - (!i.flags.container or not check_empty or - !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainsItemst) }) and - (!i.flags.in_inventory or - (!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefUnitHolderst) and # allow hauled items TODO check if holder is a thief - ir.unit_tg.inventory.find { |ii| ii.item == i and ii.mode != :Hauled } } and - !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainedInItemst) and - !item_isfree(ir.item_tg, false) })) and - (!i.flags.in_building or - !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefBuildingHolderst) and - ir.building_tg.contained_items.find { |bi| bi.use_mode == 2 and bi.item == i } }) and - (!i.flags.on_ground or !df.map_tile_at(i).designation.hidden) # i.flags.unk11? - end - end -end diff --git a/plugins/ruby/job.rb b/plugins/ruby/job.rb deleted file mode 100644 index bf033fbb3..000000000 --- a/plugins/ruby/job.rb +++ /dev/null @@ -1,35 +0,0 @@ -module DFHack - class << self - # link a job to the world - # allocate & set job.id, allocate a JobListLink, link to job & world.jobs.list - def job_link(job) - lastjob = world.jobs.list - lastjob = lastjob.next while lastjob.next - joblink = JobListLink.cpp_new - joblink.prev = lastjob - joblink.item = job - job.list_link = joblink - job.id = df.job_next_id - df.job_next_id += 1 - lastjob.next = joblink - end - - # attach an item to a job, flag item in_job - def job_attachitem(job, item, role=:Hauled, filter_idx=-1) - if role != :TargetContainer - item.flags.in_job = true - end - - itemlink = SpecificRef.cpp_new - itemlink.type = :JOB - itemlink.job = job - item.specific_refs << itemlink - - joblink = JobItemRef.cpp_new - joblink.item = item - joblink.role = role - joblink.job_item_idx = filter_idx - job.items << joblink - end - end -end diff --git a/plugins/ruby/linux32/.gitignore b/plugins/ruby/linux32/.gitignore deleted file mode 100644 index ef44e3942..000000000 --- a/plugins/ruby/linux32/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libruby* diff --git a/plugins/ruby/linux64/.gitignore b/plugins/ruby/linux64/.gitignore deleted file mode 100644 index ef44e3942..000000000 --- a/plugins/ruby/linux64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libruby* diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb deleted file mode 100644 index 5c6295946..000000000 --- a/plugins/ruby/map.rb +++ /dev/null @@ -1,344 +0,0 @@ -module DFHack - class << self - # return a map block by tile coordinates - # you can also use find_map_block(cursor) or anything that respond to x/y/z - def map_block_at(x, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - if x >= 0 and x < world.map.x_count and y >= 0 and y < world.map.y_count and z >= 0 and z < world.map.z_count - world.map.block_index[x/16][y/16][z] - end - end - - def map_designation_at(x, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - if b = map_block_at(x, y, z) - b.designation[x%16][y%16] - end - end - - def map_occupancy_at(x, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - if b = map_block_at(x, y, z) - b.occupancy[x%16][y%16] - end - end - - def map_tile_at(x=df.cursor, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - b = map_block_at(x, y, z) - MapTile.new(b, x, y, z) if b - end - - # yields every map block - def each_map_block - (0...world.map.x_count_block).each { |xb| - xl = world.map.block_index[xb] - (0...world.map.y_count_block).each { |yb| - yl = xl[yb] - (0...world.map.z_count_block).each { |z| - p = yl[z] - yield p if p - } - } - } - end - - # yields every map block for a given z level - def each_map_block_z(z) - (0...world.map.x_count_block).each { |xb| - xl = world.map.block_index[xb] - (0...world.map.y_count_block).each { |yb| - p = xl[yb][z] - yield p if p - } - } - end - end - - class MapTile - attr_accessor :x, :y, :z, :dx, :dy, :mapblock - def initialize(b, x, y, z) - @x, @y, @z = x, y, z - @dx, @dy = @x&15, @y&15 - @mapblock = b - end - - def offset(dx, dy=nil, dz=0) - if dx.respond_to?(:x) - dz = dx.z if dx.respond_to?(:z) - dx, dy = dx.x, dx.y - end - df.map_tile_at(@x+dx, @y+dy, @z+dz) - end - - def designation - @mapblock.designation[@dx][@dy] - end - - def occupancy - @mapblock.occupancy[@dx][@dy] - end - - def tiletype - @mapblock.tiletype[@dx][@dy] - end - - def tiletype=(t) - @mapblock.tiletype[@dx][@dy] = t - end - - def caption - Tiletype::Caption[tiletype] - end - - def shape - Tiletype::Shape[tiletype] - end - - def tilemat - Tiletype::Material[tiletype] - end - - def variant - Tiletype::Variant[tiletype] - end - - def special - Tiletype::Special[tiletype] - end - - def direction - Tiletype::Direction[tiletype] - end - - def shape_caption - TiletypeShape::Caption[shape] - end - - def shape_basic - TiletypeShape::BasicShape[shape] - end - - def shape_passablelow - TiletypeShape::PassableLow[shape] - end - - def shape_passablehigh - TiletypeShape::PassableHigh[shape] - end - - def shape_passableflow - TiletypeShape::PassableFlow[shape] - end - - def shape_walkable - TiletypeShape::Walkable[shape] - end - - - # return all veins for current mapblock - def all_veins - mapblock.block_events.grep(BlockSquareEventMineralst) - end - - # return the vein applicable to current tile - def vein - # last vein wins - all_veins.reverse.find { |v| - v.tile_bitmask.bits[@dy][@dx] > 0 - } - end - - # return the first BlockBurrow this tile is in (nil if none) - def burrow - mapblock.block_burrows.find { |b| - b.tile_bitmask.bits[@dy][@dx] > 0 - } - end - - # return the array of BlockBurrow this tile is in - def all_burrows - mapblock.block_burrows.find_all { |b| - b.tile_bitmask.bits[@dy][@dx] > 0 - } - end - - # return the mat_index for the current tile (if in vein) - def mat_index_vein - v = vein - v.inorganic_mat if v - end - - # return the RegionMapEntry (from designation.biome) - def region_map_entry - b = mapblock.region_offset[designation.biome] - wd = df.world.world_data - - # region coords + [[-1, -1], [0, -1], ..., [1, 1]][b] - # clipped to world dimensions - rx = df.world.map.region_x/16 - rx -= 1 if b % 3 == 0 and rx > 0 - rx += 1 if b % 3 == 2 and rx < wd.world_width-1 - - ry = df.world.map.region_y/16 - ry -= 1 if b < 3 and ry > 0 - ry += 1 if b > 5 and ry < wd.world_height-1 - - wd.region_map[rx][ry] - end - - # return the world_data.geo_biome for current tile - def geo_biome - df.world.world_data.geo_biomes[ region_map_entry.geo_index ] - end - - # return the world_data.geo_biome.layer for current tile - def stone_layer - geo_biome.layers[designation.geolayer_index] - end - - # MaterialInfo: token for current tile, based on tilemat (vein, soil, plant, lava_stone...) - def mat_info - case tilemat - when :SOIL - base = stone_layer - if !df.world.raws.inorganics[base.mat_index].flags[:SOIL_ANY] - base = geo_biome.layers.find_all { |l| df.world.raws.inorganics[l.mat_index].flags[:SOIL_ANY] }.last - end - mat_index = (base ? base.mat_index : rand(df.world.raws.inorganics.length)) - MaterialInfo.new(0, mat_index) - - when :STONE - base = stone_layer - if df.world.raws.inorganics[base.mat_index].flags[:SOIL_ANY] - base = geo_biome.layers.find { |l| !df.world.raws.inorganics[l.mat_index].flags[:SOIL_ANY] } - end - mat_index = (base ? base.mat_index : rand(df.world.raws.inorganics.length)) - MaterialInfo.new(0, mat_index) - - when :MINERAL - mat_index = (mat_index_vein || stone_layer.mat_index) - MaterialInfo.new(0, mat_index) - - when :LAVA_STONE - # XXX this is wrong - # maybe should search world.region_details.pos == biome_region_pos ? - idx = mapblock.region_offset[designation.biome] - mat_index = df.world.world_data.region_details[idx].lava_stone - MaterialInfo.new(0, mat_index) - - when :FEATURE - if designation.feature_local - mx = mapblock.region_pos.x - my = mapblock.region_pos.y - df.decode_mat(df.world.world_data.feature_map[mx/16][my/16].features.feature_init[mx%16][my%16][mapblock.local_feature]) - elsif designation.feature_global - df.decode_mat(df.world.world_data.underground_regions[mapblock.global_feature].feature_init) - else - MaterialInfo.new(-1, -1) - end - - when :FROZEN_LIQUID - MaterialInfo.new('WATER') - - # TODO - #when :PLANT - #when :GRASS_DARK, :GRASS_DEAD, :GRASS_DRY, :GRASS_LIGHT - #when :CONSTRUCTION - else # AIR ASHES BROOK CAMPFIRE DRIFTWOOD FIRE HFS MAGMA POOL RIVER - MaterialInfo.new(-1, -1) - end - end - - def mat_type - mat_info.mat_type - end - - def mat_index - mat_info.mat_index - end - - def inspect - "#" - end - - def dig(mode=:Default) - if mode == :Smooth - if (tilemat == :STONE or tilemat == :MINERAL) and caption !~ /smooth|pillar|fortification/i and # XXX caption.. - designation.smooth == 0 and (designation.hidden or not df.world.jobs.list.find { |j| - # the game removes 'smooth' designation as soon as it assigns a job, if we - # re-set it the game may queue another :DetailWall that will carve a fortification - (j.job_type == :DetailWall or j.job_type == :DetailFloor) and df.same_pos?(j, self) - }) - designation.dig = :No - designation.smooth = 1 - mapblock.flags.designated = true - end - else - return if mode != :No and designation.dig == :No and not designation.hidden and df.world.jobs.list.find { |j| - # someone already enroute to dig here, avoid 'Inappropriate dig square' spam - JobType::Type[j.job_type] == :Digging and df.same_pos?(j, self) - } - designation.dig = mode - mapblock.flags.designated = true if mode != :No - end - end - - def spawn_liquid(quantity, is_magma=false, flowing=true) - designation.flow_size = quantity - designation.liquid_type = (is_magma ? :Magma : :Water) - designation.flow_forbid = true if is_magma or quantity >= 4 - - if flowing - mapblock.flags.update_liquid = true - mapblock.flags.update_liquid_twice = true - - zf = df.world.map_extras.z_level_flags[z] - zf.update = true - zf.update_twice = true - end - end - - def spawn_water(quantity=7) - spawn_liquid(quantity) - end - - def spawn_magma(quantity=7) - spawn_liquid(quantity, true) - end - - # yield a serie of tiles until the block returns true, returns the matching tile - # the yielded tiles form a (squared) spiral centered here in the current zlevel - # eg for radius 4, yields (-4, -4), (-4, -3), .., (-4, 3), - # (-4, 4), (-3, 4), .., (4, 4), .., (4, -4), .., (-3, -4) - # then move on to radius 5 - def spiral_search(maxradius=100, minradius=0, step=1) - if minradius == 0 - return self if yield self - minradius += step - end - - sides = [[0, 1], [1, 0], [0, -1], [-1, 0]] - (minradius..maxradius).step(step) { |r| - sides.length.times { |s| - dxr, dyr = sides[(s-1) % sides.length] - dx, dy = sides[s] - (-r...r).step(step) { |v| - t = offset(dxr*r + dx*v, dyr*r + dy*v) - return t if t and yield t - } - } - } - nil - end - - # returns dx^2+dy^2+dz^2 - def distance_to(ot) - (x-ot.x)**2 + (y-ot.y)**2 + (z-ot.z)**2 - end - end -end diff --git a/plugins/ruby/material.rb b/plugins/ruby/material.rb deleted file mode 100644 index ca0a64779..000000000 --- a/plugins/ruby/material.rb +++ /dev/null @@ -1,203 +0,0 @@ -module DFHack - class MaterialInfo - attr_accessor :mat_type, :mat_index - attr_accessor :mode, :material, :creature, :figure, :plant, :inorganic - def initialize(what, idx=nil) - case what - when Integer - @mat_type, @mat_index = what, idx - decode_type_index - when String - decode_string(what) - else - @mat_type, @mat_index = what.mat_type, what.mat_index - decode_type_index - end - end - - CREATURE_BASE = 19 - FIGURE_BASE = CREATURE_BASE+200 - PLANT_BASE = FIGURE_BASE+200 - END_BASE = PLANT_BASE+200 - - # interpret the mat_type and mat_index fields - def decode_type_index - if @mat_index < 0 or @mat_type >= END_BASE - @mode = :Builtin - @material = df.world.raws.mat_table.builtin[@mat_type] - - elsif @mat_type >= PLANT_BASE - @mode = :Plant - @plant = df.world.raws.plants.all[@mat_index] - @material = @plant.material[@mat_type-PLANT_BASE] if @plant - - elsif @mat_type >= FIGURE_BASE - @mode = :Figure - @figure = df.world.history.figures.binsearch(@mat_index) - @creature = df.world.raws.creatures.all[@figure.race] if @figure - @material = @creature.material[@mat_type-FIGURE_BASE] if @creature - - elsif @mat_type >= CREATURE_BASE - @mode = :Creature - @creature = df.world.raws.creatures.all[@mat_index] - @material = @creature.material[@mat_type-CREATURE_BASE] if @creature - - elsif @mat_type > 0 - @mode = :Builtin - @material = df.world.raws.mat_table.builtin[@mat_type] - - elsif @mat_type == 0 - @mode = :Inorganic - @inorganic = df.world.raws.inorganics[@mat_index] - @material = @inorganic.material if @inorganic - end - end - - def decode_string(str) - parts = str.split(':') - case parts[0].chomp('_MAT') - when 'INORGANIC', 'STONE', 'METAL' - decode_string_inorganic(parts) - when 'PLANT' - decode_string_plant(parts) - when 'CREATURE' - if parts[3] and parts[3] != 'NONE' - decode_string_figure(parts) - else - decode_string_creature(parts) - end - when 'INVALID' - @mat_type = parts[1].to_i - @mat_index = parts[2].to_i - else - decode_string_builtin(parts) - end - end - - def decode_string_inorganic(parts) - @@inorganics_index ||= (0...df.world.raws.inorganics.length).inject({}) { |h, i| h.update df.world.raws.inorganics[i].id => i } - - @mode = :Inorganic - @mat_type = 0 - - if parts[1] and parts[1] != 'NONE' - @mat_index = @@inorganics_index[parts[1]] - raise "invalid inorganic token #{parts.join(':').inspect}" if not @mat_index - @inorganic = df.world.raws.inorganics[@mat_index] - @material = @inorganic.material - end - end - - def decode_string_builtin(parts) - @@builtins_index ||= (1...df.world.raws.mat_table.builtin.length).inject({}) { |h, i| b = df.world.raws.mat_table.builtin[i] ; b ? h.update(b.id => i) : h } - - @mode = :Builtin - @mat_index = -1 - @mat_type = @@builtins_index[parts[0]] - raise "invalid builtin token #{parts.join(':').inspect}" if not @mat_type - @material = df.world.raws.mat_table.builtin[@mat_type] - - if parts[0] == 'COAL' and parts[1] - @mat_index = ['COKE', 'CHARCOAL'].index(parts[1]) || -1 - end - end - - def decode_string_creature(parts) - @@creatures_index ||= (0...df.world.raws.creatures.all.length).inject({}) { |h, i| h.update df.world.raws.creatures.all[i].creature_id => i } - - @mode = :Creature - - if parts[1] and parts[1] != 'NONE' - @mat_index = @@creatures_index[parts[1]] - raise "invalid creature token #{parts.join(':').inspect}" if not @mat_index - @creature = df.world.raws.creatures.all[@mat_index] - end - - if @creature and parts[2] and parts[2] != 'NONE' - @mat_type = @creature.material.index { |m| m.id == parts[2] } - @material = @creature.material[@mat_type] - @mat_type += CREATURE_BASE - end - end - - def decode_string_figure(parts) - @mode = :Figure - @mat_index = parts[3].to_i - @figure = df.world.history.figures.binsearch(@mat_index) - raise "invalid creature histfig #{parts.join(':').inspect}" if not @figure - - @creature = df.world.raws.creatures.all[@figure.race] - if parts[1] and parts[1] != 'NONE' - raise "invalid histfig race #{parts.join(':').inspect}" if @creature.creature_id != parts[1] - end - - if @creature and parts[2] and parts[2] != 'NONE' - @mat_type = @creature.material.index { |m| m.id == parts[2] } - @material = @creature.material[@mat_type] - @mat_type += FIGURE_BASE - end - end - - def decode_string_plant(parts) - @@plants_index ||= (0...df.world.raws.plants.all.length).inject({}) { |h, i| h.update df.world.raws.plants.all[i].id => i } - - @mode = :Plant - - if parts[1] and parts[1] != 'NONE' - @mat_index = @@plants_index[parts[1]] - raise "invalid plant token #{parts.join(':').inspect}" if not @mat_index - @plant = df.world.raws.plants.all[@mat_index] - end - - if @plant and parts[2] and parts[2] != 'NONE' - @mat_type = @plant.material.index { |m| m.id == parts[2] } - raise "invalid plant type #{parts.join(':').inspect}" if not @mat_type - @material = @plant.material[@mat_type] - @mat_type += PLANT_BASE - end - end - - # delete the caches of raws id => index used in decode_string - def self.flush_raws_cache - @@inorganics_index = @@plants_index = @@creatures_index = @@builtins_index = nil - end - - def token - out = [] - case @mode - when :Builtin - out << (@material ? @material.id : 'NONE') - out << (['COKE', 'CHARCOAL'][@mat_index] || 'NONE') if @material and @material.id == 'COAL' and @mat_index >= 0 - when :Inorganic - out << 'INORGANIC' - out << @inorganic.id if @inorganic - when :Plant - out << 'PLANT_MAT' - out << @plant.id if @plant - out << @material.id if @plant and @material - when :Creature, :Figure - out << 'CREATURE_MAT' - out << @creature.creature_id if @creature - out << @material.id if @creature and @material - out << @figure.id.to_s if @creature and @material and @figure - else - out << 'INVALID' - out << @mat_type.to_s - out << @mat_index.to_s - end - out.join(':') - end - - def to_s ; token ; end - - def ===(other) - other.mat_index == mat_index and other.mat_type == mat_type - end - end - - class << self - def decode_mat(what, idx=nil) - MaterialInfo.new(what, idx) - end - end -end diff --git a/plugins/ruby/osx32/.gitignore b/plugins/ruby/osx32/.gitignore deleted file mode 100644 index ef44e3942..000000000 --- a/plugins/ruby/osx32/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libruby* diff --git a/plugins/ruby/osx64/.gitignore b/plugins/ruby/osx64/.gitignore deleted file mode 100644 index ef44e3942..000000000 --- a/plugins/ruby/osx64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libruby* diff --git a/plugins/ruby/plant.rb b/plugins/ruby/plant.rb deleted file mode 100644 index 2f5a1c7c4..000000000 --- a/plugins/ruby/plant.rb +++ /dev/null @@ -1,111 +0,0 @@ -module DFHack - class << self - # return a Plant - # arg similar to unit.rb/unit_find, no menu - def plant_find(what=cursor) - if what.kind_of?(Integer) - world.items.all.binsearch(what) - elsif what.respond_to?(:x) or what.respond_to?(:pos) - world.plants.all.find { |p| same_pos?(what, p) } - else - raise "what what?" - end - end - - def each_tree(material=:any) - @raws_tree_name ||= {} - if @raws_tree_name.empty? - df.world.raws.plants.all.each_with_index { |p, idx| - @raws_tree_name[idx] = p.id if p.flags[:TREE] - } - end - - if material != :any - mat = match_rawname(material, @raws_tree_name.values) - unless wantmat = @raws_tree_name.index(mat) - raise "invalid tree material #{material}" - end - end - - world.plants.all.each { |plant| - next if not @raws_tree_name[plant.material] - next if wantmat and plant.material != wantmat - yield plant - } - end - - def each_shrub(material=:any) - @raws_shrub_name ||= {} - if @raws_tree_name.empty? - df.world.raws.plants.all.each_with_index { |p, idx| - @raws_shrub_name[idx] = p.id if not p.flags[:GRASS] and not p.flags[:TREE] - } - end - - if material != :any - mat = match_rawname(material, @raws_shrub_name.values) - unless wantmat = @raws_shrub_name.index(mat) - raise "invalid shrub material #{material}" - end - end - end - - SaplingToTreeAge = 120960 - def cuttrees(material=nil, count_max=100, quiet=false) - if !material - # list trees - cnt = Hash.new(0) - each_tree { |plant| - next if plant.grow_counter < SaplingToTreeAge - next if map_designation_at(plant).hidden - cnt[plant.material] += 1 - } - cnt.sort_by { |mat, c| c }.each { |mat, c| - name = @raws_tree_name[mat] - puts " #{name} #{c}" unless quiet - } - else - cnt = 0 - each_tree(material) { |plant| - next if plant.grow_counter < SaplingToTreeAge - b = map_block_at(plant) - d = b.designation[plant.pos.x%16][plant.pos.y%16] - next if d.hidden - if d.dig == :No - d.dig = :Default - b.flags.designated = true - cnt += 1 - break if cnt == count_max - end - } - puts "Updated #{cnt} plant designations" unless quiet - end - end - - def growtrees(material=nil, count_max=100, quiet=false) - if !material - # list plants - cnt = Hash.new(0) - each_tree { |plant| - next if plant.grow_counter >= SaplingToTreeAge - next if map_designation_at(plant).hidden - cnt[plant.material] += 1 - } - cnt.sort_by { |mat, c| c }.each { |mat, c| - name = @raws_tree_name[mat] - puts " #{name} #{c}" unless quiet - } - else - cnt = 0 - each_tree(material) { |plant| - next if plant.grow_counter >= SaplingToTreeAge - next if map_designation_at(plant).hidden - plant.grow_counter = SaplingToTreeAge - cnt += 1 - break if cnt == count_max - } - puts "Grown #{cnt} saplings" unless quiet - end - end - end -end diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb deleted file mode 100644 index 162914e32..000000000 --- a/plugins/ruby/ruby-autogen-defs.rb +++ /dev/null @@ -1,1034 +0,0 @@ -# definition of classes used by ruby-autogen -$sizeof_ptr = case RUBY_PLATFORM - when /x86_64|x64/i; 64 - else 32 - end - -module DFHack - def self.memory_read_int64(addr) - (memory_read_int32(addr) & 0xffffffff) + (memory_read_int32(addr+4) << 32) - end - def self.memory_write_int64(addr, v) - memory_write_int32(addr, v & 0xffffffff) ; memory_write_int32(addr+4, v>>32) - end - if $sizeof_ptr == 64 - def self.memory_read_ptr(addr) - memory_read_int64(addr) & 0xffffffff_ffffffff - end - def self.memory_write_ptr(addr, v) - memory_write_int64(addr, v) - end - else - def self.memory_read_ptr(addr) - memory_read_int32(addr) & 0xffffffff - end - def self.memory_write_ptr(addr, v) - memory_write_int32(addr, v) - end - end - - module MemHack - INSPECT_SIZE_LIMIT=16384 - class MemStruct - attr_accessor :_memaddr - def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end - def _get ; self ; end - def _cpp_init ; end - def _cpp_delete ; end - end - - class Compound < MemStruct - class << self - attr_accessor :_fields, :_rtti_classname, :_sizeof - def field(name, offset) - struct = yield - return if not struct - @_fields ||= [] - @_fields << [name, offset, struct] - define_method(name) { struct._at(@_memaddr+offset)._get } - define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) } - end - def _fields_ancestors - if superclass.respond_to?(:_fields_ancestors) - superclass._fields_ancestors + _fields.to_a - else - _fields.to_a - end - end - - def number(bits, signed, initvalue=nil, enum=nil) - Number.new(bits, signed, initvalue, enum) - end - def float - Float.new - end - def double - Double.new - end - def bit(shift, enum=nil) - BitField.new(shift, 1, enum) - end - def bits(shift, len, enum=nil) - BitField.new(shift, len, enum) - end - def pointer - Pointer.new((yield if block_given?)) - end - def pointer_ary(tglen) - PointerAry.new(tglen, yield) - end - def static_array(len, tglen, indexenum=nil) - StaticArray.new(tglen, len, indexenum, yield) - end - def static_string(len) - StaticString.new(len) - end - - def stl_vector(tglen=nil) - tg = yield if tglen - case tglen - when 1; StlVector8.new(tg) - when 2; StlVector16.new(tg) - when 4; StlVector32.new(tg) - when 8; StlVector64.new(tg) - else StlVector32.new(tg) - end - end - def stl_string - StlString.new - end - def stl_bit_vector - StlBitVector.new - end - def stl_deque(tglen) - StlDeque.new(tglen, yield) - end - - def df_flagarray(indexenum=nil) - DfFlagarray.new(indexenum) - end - def df_static_flagarray(len, indexenum=nil) - DfStaticFlagarray.new(len, indexenum) - end - def df_array(tglen) - DfArray.new(tglen, yield) - end - def df_linked_list - DfLinkedList.new(yield) - end - - def global(glob) - Global.new(glob) - end - def compound(name=nil, &b) - m = Class.new(Compound) - DFHack.const_set(name, m) if name - m.class_eval(&b) - m.new - end - def rtti_classname(n) - DFHack.rtti_register(n, self) - @_rtti_classname = n - end - def sizeof(n) - @_sizeof = n - end - - # allocate a new c++ object, return its ruby wrapper - def cpp_new(init=nil) - ptr = DFHack.malloc(_sizeof) - if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname) - DFHack.memory_write_ptr(ptr, vt) - # TODO call constructor - end - o = new._at(ptr) - o._cpp_init - o._set(init) if init - o - end - end - def _cpp_init - _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } - end - def _cpp_delete - # cannot call delete on compound members (would call free on member address) - #_fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_delete } - DFHack.free(@_memaddr) - @_memaddr = nil # turn future segfaults in harmless ruby exceptions - end - def _set(h) - case h - when Hash; h.each { |k, v| send("#{k}=", v) } - when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) } - else _field_names.each { |n| send("#{n}=", h.send(n)) } - end - end - def _fields ; self.class._fields.to_a ; end - def _fields_ancestors ; self.class._fields_ancestors.to_a ; end - def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end - def _rtti_classname ; self.class._rtti_classname ; end - def _raw_rtti_classname ; df.get_rtti_classname(df.get_vtable_ptr(@_memaddr)) if self.class._rtti_classname ; end - def _sizeof ; self.class._sizeof ; end - def ==(o) ; o.kind_of?(Compound) and o._memaddr == _memaddr ; end - - @@inspecting = {} # avoid infinite recursion on mutually-referenced objects - def inspect - cn = self.class.name.sub(/^DFHack::/, '') - out = "#<#{cn}" - return out << ' ...>' if @@inspecting[_memaddr] - @@inspecting[_memaddr] = true - _fields_ancestors.each { |n, o, s| - out << ' ' if out.length != 0 and out[-1, 1] != ' ' - if out.length > INSPECT_SIZE_LIMIT - out << '...' - break - end - out << inspect_field(n, o, s) - } - out.chomp!(' ') - @@inspecting.delete _memaddr - out << '>' - end - def inspect_field(n, o, s) - if s.kind_of?(BitField) and s._len == 1 and not s._enum - send(n) ? n.to_s : '' - elsif s.kind_of?(Pointer) - "#{n}=#{s._at(@_memaddr+o).inspect}" - elsif n == :_whole - "_whole=0x#{_whole.to_s(16)}" - else - v = send(n).inspect - "#{n}=#{v}" - end - rescue - "#{n}=ERR(#{$!})" - end - end - - class OtherVectors < Compound - class << self - attr_accessor :_enum - def ienum(enum) - @_enum = enum - end - end - - def _indexenum - self.class._enum - end - def [](i) - self.send(self.class._enum.sym(i)) - end - def []=(i, v) - self.send((self.class._enum.sym(i).to_s + "=").to_sym, v) - end - end - - class Enum - # number -> symbol - def self.enum - # ruby weirdness, needed to make the constants 'virtual' - @enum ||= const_get(:ENUM) - end - # symbol -> number - def self.nume - @nume ||= const_get(:NUME) - end - - def self.int(i, allow_bad_sym=false) - raise ArgumentError, "invalid enum member #{i} of #{self}" if i.kind_of?(::Symbol) and not allow_bad_sym and not nume.has_key?(i) - nume[i] || i - end - def self.sym(i) - enum[i] || i - end - end - - class Number < MemStruct - attr_accessor :_bits, :_signed, :_initvalue, :_enum - def initialize(bits, signed, initvalue, enum) - @_bits = bits - @_signed = signed - @_initvalue = initvalue - @_enum = enum - end - - def _get - v = case @_bits - when 64; DFHack.memory_read_int64(@_memaddr) - when 32; DFHack.memory_read_int32(@_memaddr) - when 16; DFHack.memory_read_int16(@_memaddr) - when 8; DFHack.memory_read_int8( @_memaddr) - end - v &= (1 << @_bits) - 1 if not @_signed - v = @_enum.sym(v) if @_enum - v - end - - def _set(v) - v = @_enum.int(v) if @_enum - case @_bits - when 64; DFHack.memory_write_int64(@_memaddr, v) - when 32; DFHack.memory_write_int32(@_memaddr, v) - when 16; DFHack.memory_write_int16(@_memaddr, v) - when 8; DFHack.memory_write_int8( @_memaddr, v) - end - end - - def _cpp_init - _set(@_initvalue) if @_initvalue - end - end - class Float < MemStruct - def _get - DFHack.memory_read_float(@_memaddr) - end - - def _set(v) - DFHack.memory_write_float(@_memaddr, v) - end - - def _cpp_init - _set(0.0) - end - end - class Double < MemStruct - def _get - DFHack.memory_read_double(@_memaddr) - end - - def _set(v) - DFHack.memory_write_double(@_memaddr, v) - end - - def _cpp_init - _set(0.0) - end - end - class BitField < MemStruct - attr_accessor :_shift, :_len, :_enum - def initialize(shift, len, enum=nil) - @_shift = shift - @_len = len - @_enum = enum - end - def _mask - (1 << @_len) - 1 - end - - def _get - v = DFHack.memory_read_int32(@_memaddr) >> @_shift - if @_len == 1 and not @_enum - ((v & 1) == 0) ? false : true - else - v &= _mask - v = @_enum.sym(v) if @_enum - v - end - end - - def _set(v) - if @_len == 1 and (not @_enum or v == false or v == true) - # allow 'bit = 0' - v = (v && v != 0 ? 1 : 0) - end - v = @_enum.int(v) if @_enum - v = (v & _mask) << @_shift - - ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff - DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v) - end - end - - class Pointer < MemStruct - attr_accessor :_tg - def initialize(tg) - @_tg = tg - end - - def _getp - DFHack.memory_read_ptr(@_memaddr) - end - - def _setp(v) - DFHack.memory_write_ptr(@_memaddr, v) - end - - def _get - addr = _getp - return if addr == 0 - return addr if not @_tg - @_tg._at(addr)._get - end - - # XXX shaky... - def _set(v) - case v - when Pointer; DFHack.memory_write_ptr(@_memaddr, v._getp) - when MemStruct; DFHack.memory_write_ptr(@_memaddr, v._memaddr) - when Integer - if @_tg and @_tg.kind_of?(MemHack::Number) - if _getp == 0 - _setp(DFHack.malloc(@_tg._bits/8)) - end - @_tg._at(_getp)._set(v) - else - DFHack.memory_write_ptr(@_memaddr, v) - end - when nil; DFHack.memory_write_ptr(@_memaddr, 0) - else @_tg._at(_getp)._set(v) - end - end - - def inspect - ptr = _getp - if ptr == 0 - 'NULL' - else - cn = '' - cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg - cn = @_tg._glob if cn == 'Global' - "#" - end - end - end - class PointerAry < MemStruct - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen - @_tg = tg - end - - def _getp(i=0) - delta = (i != 0 ? i*@_tglen : 0) - DFHack.memory_read_ptr(@_memaddr) + delta - end - - def _get - addr = _getp - return if addr == 0 - self - end - - def _set(v) - case v - when Pointer; DFHack.memory_write_ptr(@_memaddr, v._getp) - when MemStruct; DFHack.memory_write_ptr(@_memaddr, v._memaddr) - when Integer; DFHack.memory_write_ptr(@_memaddr, v) - when nil; DFHack.memory_write_ptr(@_memaddr, 0) - else raise "cannot PointerAry._set(#{v.inspect})" - end - end - - def [](i) - addr = _getp(i) - return if addr == 0 - @_tg._at(addr)._get - end - def []=(i, v) - addr = _getp(i) - raise 'null pointer' if addr == 0 - @_tg._at(addr)._set(v) - end - - def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#" ; end - end - module Enumerable - include ::Enumerable - attr_accessor :_indexenum - def each ; (0...length).each { |i| yield self[i] } ; end - def inspect - out = '[' - each_with_index { |e, idx| - out << ', ' if out.length > 1 - if out.length > INSPECT_SIZE_LIMIT - out << '...' - break - end - out << "#{_indexenum.sym(idx)}=" if _indexenum - out << e.inspect - } - out << ']' - end - def empty? ; length == 0 ; end - def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end - def index(e=nil, &b) ; (0...length).find { |i| b ? b[self[i]] : self[i] == e } ; end - def map! ; (0...length).each { |i| self[i] = yield(self[i]) } ; end - def first ; self[0] ; end - def last ; self[length-1] ; end - end - class StaticArray < MemStruct - attr_accessor :_tglen, :_length, :_indexenum, :_tg - def initialize(tglen, length, indexenum, tg) - @_tglen = tglen - @_length = length - @_indexenum = indexenum - @_tg = tg - end - def _set(a) - a.each_with_index { |v, i| self[i] = v } - end - def _cpp_init - _length.times { |i| _tgat(i)._cpp_init } - end - def _cpp_delete - _length.times { |i| _tgat(i)._cpp_delete } - end - alias length _length - alias size _length - def _tgat(i) - @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length - end - def [](i) - i = _indexenum.int(i) if _indexenum - i += @_length if i < 0 - if t = _tgat(i) - t._get - end - end - def []=(i, v) - i = _indexenum.int(i) if _indexenum - i += @_length if i < 0 - if t = _tgat(i) - t._set(v) - else - raise 'index out of bounds' - end - end - - include Enumerable - end - class StaticString < MemStruct - attr_accessor :_length - def initialize(length) - @_length = length - end - def length - if @_length == -1 - maxlen = 4096 - (@_memaddr & 0xfff) - maxlen += 4096 until len = DFHack.memory_read(@_memaddr, maxlen).index("\0") - len - else - @_length - end - end - def _get - DFHack.memory_read(@_memaddr, length) - end - def _set(v) - DFHack.memory_write(@_memaddr, v[0, length]) - end - end - - class StlVector32 < MemStruct - attr_accessor :_tg - def initialize(tg) - @_tg = tg || Number.new(32, false, 0, nil) - end - - def length - DFHack.memory_vector32_length(@_memaddr) - end - def size ; length ; end # alias wouldnt work for subclasses - def valueptr_at(idx) - DFHack.memory_vector32_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector32_insertat(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector32_deleteat(@_memaddr, idx) - end - - def _set(v) - delete_at(length-1) while length > v.length # match lengthes - v.each_with_index { |e, i| self[i] = e } # patch entries - end - - def self._cpp_new - new._at DFHack.memory_vector_new - end - def _cpp_delete - DFHack.memory_vector_delete(@_memaddr) - end - def _cpp_init - DFHack.memory_vector_init(@_memaddr) - end - - def clear - delete_at(length-1) while length > 0 - end - def [](idx) - idx += length if idx < 0 - @_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length - end - def []=(idx, v) - idx += length if idx < 0 - if idx >= length - insert_at(length, 0) while idx >= length - elsif idx < 0 - raise 'index out of bounds' - end - @_tg._at(valueptr_at(idx))._set(v) - end - def push(v) - self[length] = v - self - end - def <<(v) ; push(v) ; end - def pop - l = length - if l > 0 - v = self[l-1] - delete_at(l-1) - end - v - end - - include Enumerable - # do a binary search in an ordered vector for a specific target attribute - # ex: world.history.figures.binsearch(unit.hist_figure_id) - def binsearch(target, field=:id) - o_start = 0 - o_end = length - 1 - while o_end >= o_start - o_half = o_start + (o_end-o_start)/2 - obj = self[o_half] - oval = obj.send(field) - if oval == target - return obj - elsif oval < target - o_start = o_half+1 - else - o_end = o_half-1 - end - end - end - end - class StlVector64 < StlVector32 - def length - DFHack.memory_vector64_length(@_memaddr) - end - def valueptr_at(idx) - DFHack.memory_vector64_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector64_insertat(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector64_deleteat(@_memaddr, idx) - end - end - class StlVector16 < StlVector32 - def length - DFHack.memory_vector16_length(@_memaddr) - end - def valueptr_at(idx) - DFHack.memory_vector16_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector16_insertat(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector16_deleteat(@_memaddr, idx) - end - end - class StlVector8 < StlVector32 - def length - DFHack.memory_vector8_length(@_memaddr) - end - def valueptr_at(idx) - DFHack.memory_vector8_ptrat(@_memaddr, idx) - end - def insert_at(idx, val) - DFHack.memory_vector8_insertat(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vector8_deleteat(@_memaddr, idx) - end - end - class StlBitVector < StlVector32 - def initialize ; end - def length - DFHack.memory_vectorbool_length(@_memaddr) - end - def insert_at(idx, val) - DFHack.memory_vectorbool_insertat(@_memaddr, idx, val) - end - def delete_at(idx) - DFHack.memory_vectorbool_deleteat(@_memaddr, idx) - end - def [](idx) - idx += length if idx < 0 - DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length - end - def []=(idx, v) - idx += length if idx < 0 - if idx >= length - insert_at(idx, v) - elsif idx < 0 - raise 'index out of bounds' - else - DFHack.memory_vectorbool_setat(@_memaddr, idx, v) - end - end - def self._cpp_new - new._at DFHack.memory_vectorbool_new - end - def _cpp_delete - DFHack.memory_vectorbool_delete(@_memaddr) - end - end - class StlString < MemStruct - def _get - DFHack.memory_read_stlstring(@_memaddr) - end - - def _set(v) - DFHack.memory_write_stlstring(@_memaddr, v) - end - - def self._cpp_new - new._at DFHack.memory_stlstring_new - end - def _cpp_delete - DFHack.memory_stlstring_delete(@_memaddr) - end - def _cpp_init - DFHack.memory_stlstring_init(@_memaddr) - end - end - class StlDeque < MemStruct - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen - @_tg = tg - end - # XXX DF uses stl::deque, so to have a C binding we'd need to single-case every - # possible struct size, like for StlVector. Just ignore it for now, deques are rare enough. - def inspect ; "#" ; end - end - - class DfFlagarray < MemStruct - attr_accessor :_indexenum - def initialize(indexenum) - @_indexenum = indexenum - end - def length - DFHack.memory_bitarray_length(@_memaddr) - end - # TODO _cpp_init, _cpp_delete - def size ; length ; end - def resize(len) - DFHack.memory_bitarray_resize(@_memaddr, len) - end - def [](idx) - idx = _indexenum.int(idx) if _indexenum - idx += length if idx < 0 - DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length - end - def []=(idx, v) - idx = _indexenum.int(idx) if _indexenum - idx += length if idx < 0 - if idx >= length or idx < 0 - raise 'index out of bounds' - else - DFHack.memory_bitarray_set(@_memaddr, idx, v) - end - end - def inspect - out = "#' - end - - include Enumerable - end - class DfStaticFlagarray < MemStruct - attr_accessor :_indexenum - def initialize(len, indexenum) - @len = len*8 - @_indexenum = indexenum - end - def length - @len - end - def size ; length ; end - def [](idx) - idx = _indexenum.int(idx) if _indexenum - idx += length if idx < 0 - return if idx < 0 or idx >= length - byte = DFHack.memory_read_int8(@_memaddr + idx/8) - (byte & (1 << (idx%8))) > 0 - end - def []=(idx, v) - idx = _indexenum.int(idx) if _indexenum - idx += length if idx < 0 - if idx >= length or idx < 0 - raise 'index out of bounds' - else - byte = DFHack.memory_read_int8(@_memaddr + idx/8) - if (v == nil or v == false or v == 0) - byte &= 0xff ^ (1 << (idx%8)) - else - byte |= (1 << (idx%8)) - end - DFHack.memory_write_int8(@_memaddr + idx/8, byte) - end - end - def inspect - out = "#' - end - - include Enumerable - end - class DfArray < Compound - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen - @_tg = tg - end - - field(:_ptr, 0) { number $sizeof_ptr, false } - field(:_length, $sizeof_ptr/8) { number 16, false } - - def length ; _length ; end - def size ; _length ; end - # TODO _cpp_init, _cpp_delete - def _tgat(i) - @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length - end - def [](i) - i += _length if i < 0 - if t = _tgat(i) - t._get - end - end - def []=(i, v) - i += _length if i < 0 - if t = _tgat(i) - t._set(v) - else - raise 'index out of bounds' - end - end - def _set(a) - a.each_with_index { |v, i| self[i] = v } - end - - include Enumerable - end - class DfLinkedList < Compound - attr_accessor :_tg - def initialize(tg) - @_tg = tg - end - - field(:_ptr, 0) { pointer } - field(:_prev, $sizeof_ptr/8) { pointer } - field(:_next, 2*$sizeof_ptr/8) { pointer } - - def item - # With the current xml structure, currently _tg designate - # the type of the 'next' and 'prev' fields, not 'item'. - # List head has item == NULL, so we can safely return nil. - - #addr = _ptr - #return if addr == 0 - #@_tg._at(addr)._get - end - - def item=(v) - #addr = _ptr - #raise 'null pointer' if not addr - #@_tg.at(addr)._set(v) - raise 'null pointer' - end - - def prev - addr = _prev - return if not addr - @_tg._at(addr)._get - end - - def next - addr = _next - return if not addr - @_tg._at(addr)._get - end - alias next= _next= - alias prev= _prev= - - include Enumerable - def each - o = self - while o - yield o.item if o.item - o = o.next - end - end - def inspect ; "#" ; end - end - - class Global < MemStruct - attr_accessor :_glob - def initialize(glob) - @_glob = glob - end - def _at(addr) - g = DFHack.const_get(@_glob) - g = DFHack.rtti_getclassat(g, addr) - g.new._at(addr) - end - def inspect ; "#<#{@_glob}>" ; end - end - - end - - class BooleanEnum - def self.int(v) ; ((v == true) || (v == 1)) ? 1 : 0 ; end - def self.sym(v) ; (!v || (v == 0)) ? false : true ; end - end - - class StlString < MemHack::Compound - field(:str, 0) { stl_string } - - def self.cpp_new(init=nil) - s = MemHack::StlString._cpp_new - s._set(init) if init - new._at(s._memaddr) - end - - def _cpp_delete - MemHack::StlString.new._at(@_memaddr+0)._cpp_delete - @_memaddr = nil - end - end - - class StlSet - attr_accessor :_memaddr, :_enum - def self.cpp_new(init=nil, enum=nil) - ret = new DFHack.memory_stlset_new, enum - init.each { |k| ret.set(k) } if init - ret - end - - def initialize(addr, enum=nil) - addr = nil if addr == 0 - @_memaddr = addr - @_enum = enum - end - - def isset(key) - raise unless @_memaddr - key = @_enum.int(key) if _enum - DFHack.memory_stlset_isset(@_memaddr, key) - end - alias is_set? isset - - def set(key) - raise unless @_memaddr - key = @_enum.int(key) if _enum - DFHack.memory_stlset_set(@_memaddr, key) - end - - def delete(key) - raise unless @_memaddr - key = @_enum.int(key) if _enum - DFHack.memory_stlset_deletekey(@_memaddr, key) - end - - def clear - raise unless @_memaddr - DFHack.memory_stlset_clear(@_memaddr) - end - - def _cpp_delete - raise unless @_memaddr - DFHack.memory_stlset_delete(@_memaddr) - @_memaddr = nil - end - end - - - # cpp rtti name -> rb class - @rtti_n2c = {} - @rtti_c2n = {} - - # cpp rtti name -> vtable ptr - @rtti_n2v = {} - @rtti_v2n = {} - - def self.rtti_n2c ; @rtti_n2c ; end - def self.rtti_c2n ; @rtti_c2n ; end - def self.rtti_n2v ; @rtti_n2v ; end - def self.rtti_v2n ; @rtti_v2n ; end - - # register a ruby class with a cpp rtti class name - def self.rtti_register(cppname, cls) - @rtti_n2c[cppname] = cls - @rtti_c2n[cls] = cppname - end - - # return the ruby class to use for the cpp object at address if rtti info is available - def self.rtti_getclassat(cls, addr) - if addr != 0 and @rtti_c2n[cls] - # rtti info exist for class => cpp object has a vtable - @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls - else - cls - end - end - - # try to read the rtti classname from an object vtable pointer - def self.rtti_readclassname(vptr) - unless n = @rtti_v2n[vptr] - n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym - @rtti_n2v[n] = vptr - end - n - end - - # return the vtable pointer from the cpp rtti name - def self.rtti_getvtable(cppname) - unless v = @rtti_n2v[cppname] - v = get_vtable(cppname.to_s) - @rtti_n2v[cppname] = v - @rtti_v2n[v] = cppname if v != 0 - end - v if v != 0 - end - - def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0, a5=0) - this = obj._memaddr - vt = df.get_vtable_ptr(this) - fptr = df.memory_read_ptr(vt + voff) - vmethod_do_call(this, fptr, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), - vmethod_arg(a3), vmethod_arg(a4), vmethod_arg(a5)) - end - - def self.vmethod_call_mem_return(obj, voff, r0=0, a0=0, a1=0, a2=0, a3=0, a4=0) - this = obj._memaddr - vt = df.get_vtable_ptr(this) - fptr = df.memory_read_ptr(vt + voff) - vmethod_do_call(vmethod_arg(r0), fptr, this, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), - vmethod_arg(a3), vmethod_arg(a4)) - end - - def self.vmethod_arg(arg) - case arg - when nil, false; 0 - when true; 1 - when Integer; arg - #when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer - when MemHack::Compound, StlSet; arg._memaddr - else raise "bad vmethod arg #{arg.class}" - end - end -end diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp deleted file mode 100644 index 0209806d1..000000000 --- a/plugins/ruby/ruby.cpp +++ /dev/null @@ -1,1237 +0,0 @@ -// blindly copied imports from fastdwarf -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" -#include "VersionInfo.h" -#include "MemAccess.h" -#include "DataDefs.h" - -#include "modules/Gui.h" -#include "df/global_objects.h" -#include "df/building.h" -#include "df/item.h" -#include "df/unit.h" - -#include "tinythread.h" - -using namespace DFHack; - - - -// DFHack stuff - - -static int df_loadruby(void); -static void df_unloadruby(void); -static void df_rubythread(void*); -static command_result df_rubyeval(color_ostream &out, std::vector & parameters); -static void ruby_bind_dfhack(void); - -// inter-thread communication stuff -enum RB_command { - RB_IDLE, - RB_INIT, - RB_DIE, - RB_EVAL, -}; -tthread::mutex *m_irun; -tthread::mutex *m_mutex; -static volatile RB_command r_type; -static volatile command_result r_result; -static color_ostream *r_console; // color_ostream given as argument, if NULL resort to console_proxy -static const char *r_command; -static tthread::thread *r_thread; -static int onupdate_active; -static int onupdate_minyear, onupdate_minyeartick=-1, onupdate_minyeartickadv=-1; -static color_ostream_proxy *console_proxy; -static std::vector *dfhack_run_queue; - - -DFHACK_PLUGIN("ruby") - - -DFhackDataExport bool plugin_is_enabled = true; - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - plugin_is_enabled = enable; - return CR_OK; -} - - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - onupdate_active = 0; - - // fail silently instead of spamming the console with 'failed to initialize' - // if libruby is not present, the error is still logged in stderr.log - if (!df_loadruby()) - return CR_OK; - - // the ruby thread sleeps trying to lock this - // when it gets it, it runs according to r_type - // when finished, it sets r_type to IDLE and unlocks - m_irun = new tthread::mutex(); - - // when any thread is going to request something to the ruby thread, - // lock this before anything, and release when everything is done - m_mutex = new tthread::mutex(); - - // list of dfhack commands to run when the current ruby run is done (once locks are released) - dfhack_run_queue = new std::vector; - - r_type = RB_INIT; - - // create the dedicated ruby thread - // df_rubythread starts the ruby interpreter and goes to type=IDLE when done - r_thread = new tthread::thread(df_rubythread, 0); - - // wait until init phase 1 is done - while (r_type != RB_IDLE) - tthread::this_thread::yield(); - - // ensure the ruby thread sleeps until we have a command to handle - m_irun->lock(); - - // check return value from rbinit - if (r_result == CR_FAILURE) - return CR_FAILURE; - - commands.push_back(PluginCommand("rb_eval", - "Ruby interpreter. Eval() a ruby string.", - df_rubyeval)); - - commands.push_back(PluginCommand("rb", - "Ruby interpreter. Eval() a ruby string.", - df_rubyeval)); - - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - // if dlopen failed - if (!r_thread) - return CR_OK; - - // ensure ruby thread is idle - m_mutex->lock(); - - r_type = RB_DIE; - r_command = NULL; - // start ruby thread - m_irun->unlock(); - - // wait until ruby thread ends after RB_DIE - r_thread->join(); - - // cleanup everything - delete r_thread; - r_thread = 0; - delete m_irun; - // we can release m_mutex, other users will check r_thread - m_mutex->unlock(); - delete m_mutex; - delete dfhack_run_queue; - - // dlclose libruby - df_unloadruby(); - - return CR_OK; -} - -static command_result do_plugin_eval_ruby(color_ostream &out, const char *command) -{ - command_result ret; - - // ensure ruby thread is idle - m_mutex->lock(); - if (!r_thread) - // raced with plugin_shutdown - return CR_OK; - - r_type = RB_EVAL; - r_command = command; - r_console = &out; - // wake ruby thread up - m_irun->unlock(); - - // semi-active loop until ruby thread is done - while (r_type != RB_IDLE) - tthread::this_thread::yield(); - - ret = r_result; - r_console = NULL; - - // block ruby thread - m_irun->lock(); - // let other plugin_eval_ruby run - m_mutex->unlock(); - - return ret; -} - -// send a single ruby line to be evaluated by the ruby thread -DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *command) -{ - command_result ret; - - // if dlopen failed - if (!r_thread) - { - out.printerr("Failed to load ruby library.\n"); - return CR_FAILURE; - } - - if (!strncmp(command, "nolock ", 7)) { - // debug only! - // run ruby commands without locking the main thread - // useful when the game is frozen after a segfault - ret = do_plugin_eval_ruby(out, command+7); - } else { - // wrap all ruby code inside a suspend block - // if we dont do that and rely on ruby code doing it, we'll deadlock in - // onupdate - CoreSuspender suspend; - ret = do_plugin_eval_ruby(out, command); - } - - // if any dfhack command is queued for run, do it now - while (!dfhack_run_queue->empty()) { - std::string cmd = dfhack_run_queue->at(0); - // delete before running the command, which may be ruby and cause infinite loops - dfhack_run_queue->erase(dfhack_run_queue->begin()); - Core::getInstance().runCommand(out, cmd); - } - - return ret; -} - -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - if (!r_thread) - return CR_OK; - - // ruby sets this flag when needed, to avoid lag running ruby code every - // frame if not necessary - if (!onupdate_active) - return CR_OK; - - if (df::global::cur_year && *df::global::cur_year < onupdate_minyear) - return CR_OK; - if (df::global::cur_year_tick && onupdate_minyeartick >= 0 && - *df::global::cur_year_tick < onupdate_minyeartick) - return CR_OK; - if (df::global::cur_year_tick_advmode && onupdate_minyeartickadv >= 0 && - *df::global::cur_year_tick_advmode < onupdate_minyeartickadv) - return CR_OK; - - return plugin_eval_ruby(out, "DFHack.onupdate"); -} - -DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e) -{ - if (!r_thread) - return CR_OK; - - std::string cmd = "DFHack.onstatechange "; - switch (e) { -#define SCASE(s) case SC_ ## s : cmd += ":" # s ; break - case SC_UNKNOWN : return CR_OK; - SCASE(WORLD_LOADED); - SCASE(WORLD_UNLOADED); - SCASE(MAP_LOADED); - SCASE(MAP_UNLOADED); - SCASE(VIEWSCREEN_CHANGED); - SCASE(CORE_INITIALIZED); - // if we go through plugin_eval at BEGIN_UNLOAD, it'll - // try to get the suspend lock and deadlock at df exit - case SC_BEGIN_UNLOAD : return CR_OK; - SCASE(PAUSED); - SCASE(UNPAUSED); -#undef SCASE - } - - return plugin_eval_ruby(out, cmd.c_str()); -} - -static command_result df_rubyeval(color_ostream &out, std::vector & parameters) -{ - if (parameters.size() == 1 && (parameters[0] == "help" || parameters[0] == "?")) - { - out.print("This command executes an arbitrary ruby statement.\n"); - return CR_OK; - } - - // reconstruct the text from dfhack console line - std::string full = ""; - - for (unsigned i=0 ; i> 1) -#define RUBY_METHOD_FUNC(func) ((VALUE(*)(...))func) - -void (*ruby_init_stack)(VALUE*); -void (*ruby_sysinit)(int *, const char ***); -void (*ruby_init)(void); -void (*ruby_init_loadpath)(void); -void (*ruby_script)(const char*); -int (*ruby_cleanup)(int); -ID (*rb_intern)(const char*); -VALUE (*rb_funcall)(VALUE, ID, int, ...); -VALUE (*rb_define_module)(const char*); -void (*rb_define_singleton_method)(VALUE, const char*, VALUE(*)(...), int); -VALUE (*rb_gv_get)(const char*); -VALUE (*rb_str_new)(const char*, long); -char* (*rb_string_value_ptr)(VALUE*); -VALUE (*rb_eval_string_protect)(const char*, int*); -VALUE (*rb_ary_shift)(VALUE); -VALUE (*rb_float_new)(double); -double (*rb_num2dbl)(VALUE); -VALUE (*rb_int2inum)(intptr_t); // XXX check on win64 long vs intptr_t -VALUE (*rb_uint2inum)(uintptr_t); -uintptr_t (*rb_num2ulong)(VALUE); -// end of rip(ruby.h) - -DFHack::DFLibrary *libruby_handle; - -// load the ruby library, initialize function pointers -static int df_loadruby(void) -{ - const char *libpaths[] = { -#if defined(WIN32) - "./libruby.dll", -#elif defined(__APPLE__) - "hack/libruby.dylib", - "/System/Library/Frameworks/Ruby.framework/Ruby", -#else - "hack/libruby.so", - "libruby.so", -#endif - NULL - }; - - for (const char **path = libpaths; *path; path++) { - if ((libruby_handle = OpenPlugin(*path))) - break; - else - fprintf(stderr, "ruby: warning: Failed to load %s\n", *path); - } - - if (!libruby_handle) { - Core::printerr("Cannot initialize ruby plugin: failed to load ruby library\n"); - return 0; - } - - // ruby_sysinit is optional (ruby1.9 only) - ruby_sysinit = (decltype(ruby_sysinit))LookupPlugin(libruby_handle, "ruby_sysinit"); -#define rbloadsyma(s,a) if (!(s = (decltype(s))LookupPlugin(libruby_handle, #a))) return 0 -#define rbloadsym(s) rbloadsyma(s,s) - rbloadsym(ruby_init_stack); - rbloadsym(ruby_init); - rbloadsym(ruby_init_loadpath); - rbloadsym(ruby_script); - rbloadsym(ruby_cleanup); - rbloadsym(rb_intern); - rbloadsym(rb_funcall); - rbloadsym(rb_define_module); - rbloadsym(rb_define_singleton_method); - rbloadsym(rb_gv_get); - rbloadsym(rb_str_new); - rbloadsym(rb_string_value_ptr); - rbloadsym(rb_eval_string_protect); - rbloadsym(rb_ary_shift); - rbloadsym(rb_num2dbl); - rbloadsym(rb_int2inum); -#if defined(_WIN64) - rbloadsyma(rb_uint2inum, rb_ull2inum); - rbloadsyma(rb_num2ulong, rb_num2ull); -#else - rbloadsym(rb_uint2inum); - rbloadsym(rb_num2ulong); -#endif - -#undef rbloadsym - // rb_float_new_in_heap in ruby 2 - if (!((rb_float_new = (decltype(rb_float_new))(LookupPlugin(libruby_handle, "rb_float_new"))) || - (rb_float_new = (decltype(rb_float_new))(LookupPlugin(libruby_handle, "rb_float_new_in_heap"))))) - return 0; - - return 1; -} - -static void df_unloadruby(void) -{ - if (libruby_handle) { - ClosePlugin(libruby_handle); - libruby_handle = 0; - } -} - -static void printerr(const char* fmt, const char *arg) -{ - if (r_console) - r_console->printerr(fmt, arg); - else - Core::printerr(fmt, arg); -} - -// ruby thread code -static void dump_rb_error(void) -{ - VALUE s, err; - - err = rb_gv_get("$!"); - - s = rb_funcall(err, rb_intern("class"), 0); - s = rb_funcall(s, rb_intern("name"), 0); - printerr("E: %s: ", rb_string_value_ptr(&s)); - - s = rb_funcall(err, rb_intern("message"), 0); - printerr("%s\n", rb_string_value_ptr(&s)); - - err = rb_funcall(err, rb_intern("backtrace"), 0); - for (int i=0 ; i<8 ; ++i) - if ((s = rb_ary_shift(err)) != Qnil) - printerr(" %s\n", rb_string_value_ptr(&s)); -} - -// ruby thread main loop -static void df_rubythread(void *p) -{ - int state, running; - - // may need to be run from df main thread? - VALUE foo; - ruby_init_stack(&foo); - - if (ruby_sysinit) { - // ruby1.9 specific API - static int argc; - static const char *argv[] = { "dfhack", 0 }; - ruby_sysinit(&argc, (const char ***)&argv); - } - - // initialize the ruby interpreter - ruby_init(); - ruby_init_loadpath(); - // default value for the $0 "current script name" - ruby_script("dfhack"); - - // create the ruby objects to map DFHack to ruby methods - ruby_bind_dfhack(); - - console_proxy = new color_ostream_proxy(Core::getInstance().getConsole()); - - // ensure noone bothers us while we load data defs in the background - m_mutex->lock(); - - // tell the main thread our initialization is finished - r_result = CR_OK; - r_type = RB_IDLE; - - // initialize ruby constants (may depend on libruby compilation flags/version) - Qnil = rb_eval_string_protect("nil", &state); - Qtrue = rb_eval_string_protect("true", &state); - Qfalse = rb_eval_string_protect("false", &state); - - // load the default ruby-level definitions in the background - state=0; - rb_eval_string_protect("require './hack/ruby/ruby'", &state); - if (state) - dump_rb_error(); - - // ready to go - m_mutex->unlock(); - - running = 1; - while (running) { - // sleep waiting for new command - m_irun->lock(); - - switch (r_type) { - case RB_IDLE: - case RB_INIT: - break; - - case RB_DIE: - running = 0; - ruby_cleanup(0); - break; - - case RB_EVAL: - state = 0; - rb_eval_string_protect(r_command, &state); - if (state) - dump_rb_error(); - break; - } - - r_result = CR_OK; - r_type = RB_IDLE; - m_irun->unlock(); - tthread::this_thread::yield(); - } -} - - -#define BOOL_ISFALSE(v) ((v) == Qfalse || (v) == Qnil || (v) == INT2FIX(0)) - -// main DFHack ruby module -static VALUE rb_cDFHack; - - -// DFHack module ruby methods, binds specific dfhack methods - -// df-dfhack version (eg "0.34.11-r2") -static VALUE rb_dfversion(VALUE self) -{ - const char *dfhack_version = Version::dfhack_version(); - return rb_str_new(dfhack_version, strlen(dfhack_version)); -} - -// enable/disable calls to DFHack.onupdate() -static VALUE rb_dfonupdate_active(VALUE self) -{ - if (onupdate_active) - return Qtrue; - else - return Qfalse; -} - -static VALUE rb_dfonupdate_active_set(VALUE self, VALUE val) -{ - onupdate_active = (BOOL_ISFALSE(val) ? 0 : 1); - return Qtrue; -} - -static VALUE rb_dfonupdate_minyear(VALUE self) -{ - return rb_uint2inum(onupdate_minyear); -} - -static VALUE rb_dfonupdate_minyear_set(VALUE self, VALUE val) -{ - onupdate_minyear = rb_num2ulong(val); - return Qtrue; -} - -static VALUE rb_dfonupdate_minyeartick(VALUE self) -{ - return rb_uint2inum(onupdate_minyeartick); -} - -static VALUE rb_dfonupdate_minyeartick_set(VALUE self, VALUE val) -{ - onupdate_minyeartick = rb_num2ulong(val); - return Qtrue; -} - -static VALUE rb_dfonupdate_minyeartickadv(VALUE self) -{ - return rb_uint2inum(onupdate_minyeartickadv); -} - -static VALUE rb_dfonupdate_minyeartickadv_set(VALUE self, VALUE val) -{ - onupdate_minyeartickadv = rb_num2ulong(val); - return Qtrue; -} - -static VALUE rb_dfprint_str(VALUE self, VALUE s) -{ - if (r_console) - r_console->print("%s", rb_string_value_ptr(&s)); - else - console_proxy->print("%s", rb_string_value_ptr(&s)); - return Qnil; -} - -static VALUE rb_dfprint_color(VALUE self, VALUE c, VALUE s) -{ - if (r_console) { - color_value old_col = r_console->color(); - r_console->color(color_value(rb_num2ulong(c))); - r_console->print("%s", rb_string_value_ptr(&s)); - r_console->color(old_col); - } else - console_proxy->print("%s", rb_string_value_ptr(&s)); - return Qnil; -} - -static VALUE rb_dfprint_err(VALUE self, VALUE s) -{ - printerr("%s", rb_string_value_ptr(&s)); - return Qnil; -} - -static VALUE rb_dfget_global_address(VALUE self, VALUE name) -{ - return rb_uint2inum(Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name))); -} - -static VALUE rb_dfget_vtable(VALUE self, VALUE name) -{ - return rb_uint2inum((uintptr_t)Core::getInstance().vinfo->getVTable(rb_string_value_ptr(&name))); -} - -// read the c++ class name from a vtable pointer, inspired from doReadClassName -// XXX virtual classes only! dark pointer arithmetic, use with caution ! -static VALUE rb_dfget_rtti_classname(VALUE self, VALUE vptr) -{ - char *ptr = (char*)rb_num2ulong(vptr); -#if defined(_WIN64) - // win64 - char *rtti = *(char**)(ptr - 0x8); - char *typeinfo = (char*)Core::getInstance().p->getBase() + *(uint32_t*)(rtti + 0xC); - // skip the .?AV, trim @@ from end - return rb_str_new(typeinfo+0x14, strlen(typeinfo+0x14)-2); -#elif defined(WIN32) - // win32 - char *rtti = *(char**)(ptr - 0x4); - char *typeinfo = *(char**)(rtti + 0xC); - // skip the .?AV, trim @@ from end - return rb_str_new(typeinfo+0xc, strlen(typeinfo+0xc)-2); -#else - // linux/osx 32/64 - char *typeinfo = *(char**)(ptr - sizeof(void*)); - char *typestring = *(char**)(typeinfo + sizeof(void*)); - while (*typestring >= '0' && *typestring <= '9') - typestring++; - return rb_str_new(typestring, strlen(typestring)); -#endif -} - -static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr) -{ - return rb_uint2inum(*(uintptr_t*)rb_num2ulong(objptr)); -} - -static VALUE rb_dfget_selected_building_id(VALUE self) -{ - df::building *b = Gui::getAnyBuilding(Core::getTopViewscreen()); - return rb_int2inum(b ? b->id : -1); -} - -static VALUE rb_dfget_selected_item_id(VALUE self) -{ - df::item *i = Gui::getAnyItem(Core::getTopViewscreen()); - return rb_int2inum(i ? i->id : -1); -} - -static VALUE rb_dfget_selected_unit_id(VALUE self) -{ - df::unit *u = Gui::getAnyUnit(Core::getTopViewscreen()); - return rb_int2inum(u ? u->id : -1); -} - -// run a dfhack command, as if typed from the dfhack console -static VALUE rb_dfhack_run(VALUE self, VALUE cmd) -{ - std::string s; - int strlen = FIX2INT(rb_funcall(cmd, rb_intern("bytesize"), 0)); - s.assign(rb_string_value_ptr(&cmd), strlen); - dfhack_run_queue->push_back(s); - return Qtrue; -} - - - -// raw memory access -// used by the ruby class definitions -// XXX may cause game crash ! double-check your addresses ! - -static VALUE rb_dfmalloc(VALUE self, VALUE len) -{ - char *ptr = (char*)malloc(FIX2INT(len)); - if (!ptr) - return Qnil; - memset(ptr, 0, FIX2INT(len)); - return rb_uint2inum((uintptr_t)ptr); -} - -static VALUE rb_dffree(VALUE self, VALUE ptr) -{ - free((void*)rb_num2ulong(ptr)); - return Qtrue; -} - -// memory reading (buffer) -static VALUE rb_dfmemory_read(VALUE self, VALUE addr, VALUE len) -{ - return rb_str_new((char*)rb_num2ulong(addr), rb_num2ulong(len)); -} - -// memory reading (integers/floats) -static VALUE rb_dfmemory_read_int8(VALUE self, VALUE addr) -{ - return rb_int2inum(*(char*)rb_num2ulong(addr)); -} -static VALUE rb_dfmemory_read_int16(VALUE self, VALUE addr) -{ - return rb_int2inum(*(short*)rb_num2ulong(addr)); -} -static VALUE rb_dfmemory_read_int32(VALUE self, VALUE addr) -{ - return rb_int2inum(*(int*)rb_num2ulong(addr)); -} - -static VALUE rb_dfmemory_read_float(VALUE self, VALUE addr) -{ - return rb_float_new(*(float*)rb_num2ulong(addr)); -} - -static VALUE rb_dfmemory_read_double(VALUE self, VALUE addr) -{ - return rb_float_new(*(double*)rb_num2ulong(addr)); -} - - -// memory writing (buffer) -static VALUE rb_dfmemory_write(VALUE self, VALUE addr, VALUE raw) -{ - // no stable api for raw.length between rb1.8/rb1.9 ... - int strlen = FIX2INT(rb_funcall(raw, rb_intern("bytesize"), 0)); - - memcpy((void*)rb_num2ulong(addr), rb_string_value_ptr(&raw), strlen); - - return Qtrue; -} - -// memory writing (integers/floats) -static VALUE rb_dfmemory_write_int8(VALUE self, VALUE addr, VALUE val) -{ - *(char*)rb_num2ulong(addr) = rb_num2ulong(val); - return Qtrue; -} -static VALUE rb_dfmemory_write_int16(VALUE self, VALUE addr, VALUE val) -{ - *(short*)rb_num2ulong(addr) = rb_num2ulong(val); - return Qtrue; -} -static VALUE rb_dfmemory_write_int32(VALUE self, VALUE addr, VALUE val) -{ - *(int*)rb_num2ulong(addr) = rb_num2ulong(val); - return Qtrue; -} - -static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) -{ - *(float*)rb_num2ulong(addr) = rb_num2dbl(val); - return Qtrue; -} - -static VALUE rb_dfmemory_write_double(VALUE self, VALUE addr, VALUE val) -{ - *(double*)rb_num2ulong(addr) = rb_num2dbl(val); - return Qtrue; -} - -// return memory permissions at address (eg "rx", nil if unmapped) -static VALUE rb_dfmemory_check(VALUE self, VALUE addr) -{ - void *ptr = (void*)rb_num2ulong(addr); - std::vector ranges; - Core::getInstance().p->getMemRanges(ranges); - - unsigned i = 0; - while (i < ranges.size() && ranges[i].end <= ptr) - i++; - - if (i >= ranges.size() || ranges[i].start > ptr || !ranges[i].valid) - return Qnil; - - std::string perm = ""; - if (ranges[i].read) - perm += "r"; - if (ranges[i].write) - perm += "w"; - if (ranges[i].execute) - perm += "x"; - if (ranges[i].shared) - perm += "s"; - - return rb_str_new(perm.c_str(), perm.length()); -} - -// memory write (tmp override page permissions, eg patch code) -static VALUE rb_dfmemory_patch(VALUE self, VALUE addr, VALUE raw) -{ - int strlen = FIX2INT(rb_funcall(raw, rb_intern("bytesize"), 0)); - bool ret; - - ret = Core::getInstance().p->patchMemory((void*)rb_num2ulong(addr), - rb_string_value_ptr(&raw), strlen); - - return ret ? Qtrue : Qfalse; -} - -// allocate memory pages -static VALUE rb_dfmemory_pagealloc(VALUE self, VALUE len) -{ - void *ret = Core::getInstance().p->memAlloc(rb_num2ulong(len)); - - return (ret == (void*)-1) ? Qnil : rb_uint2inum((uintptr_t)ret); -} - -// free memory from pagealloc -static VALUE rb_dfmemory_pagedealloc(VALUE self, VALUE ptr, VALUE len) -{ - int ret = Core::getInstance().p->memDealloc((void*)rb_num2ulong(ptr), rb_num2ulong(len)); - - return ret ? Qfalse : Qtrue; -} - -// change memory page permissions -// ptr must be page-aligned -// prot is a String, eg 'rwx', 'r', 'x' -static VALUE rb_dfmemory_pageprotect(VALUE self, VALUE ptr, VALUE len, VALUE prot_str) -{ - int ret, prot=0; - char *prot_p = rb_string_value_ptr(&prot_str); - - if (*prot_p == 'r') { - prot |= Process::MemProt::READ; - ++prot_p; - } - if (*prot_p == 'w') { - prot |= Process::MemProt::WRITE; - ++prot_p; - } - if (*prot_p == 'x') { - prot |= Process::MemProt::EXEC; - ++prot_p; - } - - Core::printerr("pageprot %zx %zx %x\n", rb_num2ulong(ptr), rb_num2ulong(len), prot); - ret = Core::getInstance().p->memProtect((void*)rb_num2ulong(ptr), rb_num2ulong(len), prot); - - return ret ? Qfalse : Qtrue; -} - - -// stl::string -static VALUE rb_dfmemory_stlstring_new(VALUE self) -{ - std::string *ptr = new std::string; - return rb_uint2inum((uintptr_t)ptr); -} -static VALUE rb_dfmemory_stlstring_delete(VALUE self, VALUE addr) -{ - std::string *ptr = (std::string*)rb_num2ulong(addr); - if (ptr) - delete ptr; - return Qtrue; -} -static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr) -{ - new((void*)rb_num2ulong(addr)) std::string(); - return Qtrue; -} -static VALUE rb_dfmemory_read_stlstring(VALUE self, VALUE addr) -{ - std::string *s = (std::string*)rb_num2ulong(addr); - return rb_str_new(s->c_str(), s->length()); -} -static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) -{ - std::string *s = (std::string*)rb_num2ulong(addr); - int strlen = FIX2INT(rb_funcall(val, rb_intern("bytesize"), 0)); - s->assign(rb_string_value_ptr(&val), strlen); - return Qtrue; -} - - -// vector access -static VALUE rb_dfmemory_vec_new(VALUE self) -{ - std::vector *ptr = new std::vector; - return rb_uint2inum((uintptr_t)ptr); -} -static VALUE rb_dfmemory_vec_delete(VALUE self, VALUE addr) -{ - std::vector *ptr = (std::vector*)rb_num2ulong(addr); - if (ptr) - delete ptr; - return Qtrue; -} -static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr) -{ - new((void*)rb_num2ulong(addr)) std::vector(); - return Qtrue; -} -// vector -static VALUE rb_dfmemory_vec8_length(VALUE self, VALUE addr) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->size()); -} -static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum((uintptr_t)&v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec8_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); - return Qtrue; -} -static VALUE rb_dfmemory_vec8_deleteat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->erase(v->begin()+FIX2INT(idx)); - return Qtrue; -} - -// vector -static VALUE rb_dfmemory_vec16_length(VALUE self, VALUE addr) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->size()); -} -static VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum((uintptr_t)&v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec16_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); - return Qtrue; -} -static VALUE rb_dfmemory_vec16_deleteat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->erase(v->begin()+FIX2INT(idx)); - return Qtrue; -} - -// vector -static VALUE rb_dfmemory_vec32_length(VALUE self, VALUE addr) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->size()); -} -static VALUE rb_dfmemory_vec32_ptrat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum((uintptr_t)&v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec32_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); - return Qtrue; -} -static VALUE rb_dfmemory_vec32_deleteat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->erase(v->begin()+FIX2INT(idx)); - return Qtrue; -} - -// vector -static VALUE rb_dfmemory_vec64_length(VALUE self, VALUE addr) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->size()); -} -static VALUE rb_dfmemory_vec64_ptrat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum((uintptr_t)&v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec64_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); - return Qtrue; -} -static VALUE rb_dfmemory_vec64_deleteat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->erase(v->begin()+FIX2INT(idx)); - return Qtrue; -} - -// vector -static VALUE rb_dfmemory_vecbool_new(VALUE self) -{ - std::vector *ptr = new std::vector; - return rb_uint2inum((uintptr_t)ptr); -} -static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr) -{ - std::vector *ptr = (std::vector*)rb_num2ulong(addr); - if (ptr) - delete ptr; - return Qtrue; -} -static VALUE rb_dfmemory_vecbool_init(VALUE self, VALUE addr) -{ - new((void*)rb_num2ulong(addr)) std::vector(); - return Qtrue; -} -static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->size()); -} -static VALUE rb_dfmemory_vecbool_at(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - return v->at(FIX2INT(idx)) ? Qtrue : Qfalse; -} -static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->at(FIX2INT(idx)) = (BOOL_ISFALSE(val) ? 0 : 1); - return Qtrue; -} -static VALUE rb_dfmemory_vecbool_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->insert(v->begin()+FIX2INT(idx), (BOOL_ISFALSE(val) ? 0 : 1)); - return Qtrue; -} -static VALUE rb_dfmemory_vecbool_deleteat(VALUE self, VALUE addr, VALUE idx) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->erase(v->begin()+FIX2INT(idx)); - return Qtrue; -} - -// BitArray -static VALUE rb_dfmemory_bitarray_length(VALUE self, VALUE addr) -{ - DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); - return rb_uint2inum(b->size*8); // b->size is in bytes -} -static VALUE rb_dfmemory_bitarray_resize(VALUE self, VALUE addr, VALUE sz) -{ - DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); - b->resize(rb_num2ulong(sz)); - return Qtrue; -} -static VALUE rb_dfmemory_bitarray_isset(VALUE self, VALUE addr, VALUE idx) -{ - DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); - return b->is_set(rb_num2ulong(idx)) ? Qtrue : Qfalse; -} -static VALUE rb_dfmemory_bitarray_set(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); - b->set(rb_num2ulong(idx), (BOOL_ISFALSE(val) ? 0 : 1)); - return Qtrue; -} - -// add basic support for std::set used for passing keyboard keys to viewscreens -#include -static VALUE rb_dfmemory_set_new(VALUE self) -{ - std::set *ptr = new std::set; - return rb_uint2inum((uintptr_t)ptr); -} - -static VALUE rb_dfmemory_set_delete(VALUE self, VALUE set) -{ - std::set *ptr = (std::set*)rb_num2ulong(set); - if (ptr) - delete ptr; - return Qtrue; -} - -static VALUE rb_dfmemory_set_set(VALUE self, VALUE set, VALUE key) -{ - std::set *ptr = (std::set*)rb_num2ulong(set); - ptr->insert(rb_num2ulong(key)); - return Qtrue; -} - -static VALUE rb_dfmemory_set_isset(VALUE self, VALUE set, VALUE key) -{ - std::set *ptr = (std::set*)rb_num2ulong(set); - return ptr->count(rb_num2ulong(key)) ? Qtrue : Qfalse; -} - -static VALUE rb_dfmemory_set_deletekey(VALUE self, VALUE set, VALUE key) -{ - std::set *ptr = (std::set*)rb_num2ulong(set); - ptr->erase(rb_num2ulong(key)); - return Qtrue; -} - -static VALUE rb_dfmemory_set_clear(VALUE self, VALUE set) -{ - std::set *ptr = (std::set*)rb_num2ulong(set); - ptr->clear(); - return Qtrue; -} - - -/* call an arbitrary object virtual method */ -#if defined(_WIN32) && !defined(_WIN64) -__declspec(naked) static intptr_t raw_vcall(void *that, void *fptr, uintptr_t a0, - uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) -{ - // __thiscall requires that the callee cleans up the stack - // here we dont know how many arguments it will take, so - // we simply fix esp across the funcall - __asm { - push ebp - mov ebp, esp - - push a5 - push a4 - push a3 - push a2 - push a1 - push a0 - - mov ecx, that - - call fptr - - mov esp, ebp - pop ebp - ret - } -} -#else -static intptr_t raw_vcall(void *that, void *fptr, uintptr_t a0, - uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) -{ - intptr_t (*t_fptr)(void *me, uintptr_t, uintptr_t, uintptr_t, - uintptr_t, uintptr_t, uintptr_t); - t_fptr = (decltype(t_fptr))fptr; - return t_fptr(that, a0, a1, a2, a3, a4, a5); -} -#endif - -// call an arbitrary vmethod, convert args/ret to native values for raw_vcall -static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE fptr, VALUE a0, VALUE a1, VALUE a2, VALUE a3, VALUE a4, VALUE a5) -{ - return rb_int2inum(raw_vcall((void*)rb_num2ulong(cppobj), (void*)rb_num2ulong(fptr), - rb_num2ulong(a0), rb_num2ulong(a1), - rb_num2ulong(a2), rb_num2ulong(a3), - rb_num2ulong(a4), rb_num2ulong(a5))); -} - - -// define module DFHack and its methods -static void ruby_bind_dfhack(void) { - rb_cDFHack = rb_define_module("DFHack"); - - rb_define_singleton_method(rb_cDFHack, "onupdate_active", RUBY_METHOD_FUNC(rb_dfonupdate_active), 0); - rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdate_active_set), 1); - rb_define_singleton_method(rb_cDFHack, "onupdate_minyear", RUBY_METHOD_FUNC(rb_dfonupdate_minyear), 0); - rb_define_singleton_method(rb_cDFHack, "onupdate_minyear=", RUBY_METHOD_FUNC(rb_dfonupdate_minyear_set), 1); - rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick), 0); - rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick_set), 1); - rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartickadv", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartickadv), 0); - rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartickadv=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartickadv_set), 1); - rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); - rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); - rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); - rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1); - rb_define_singleton_method(rb_cDFHack, "get_selected_building_id", RUBY_METHOD_FUNC(rb_dfget_selected_building_id), 0); - rb_define_singleton_method(rb_cDFHack, "get_selected_item_id", RUBY_METHOD_FUNC(rb_dfget_selected_item_id), 0); - rb_define_singleton_method(rb_cDFHack, "get_selected_unit_id", RUBY_METHOD_FUNC(rb_dfget_selected_unit_id), 0); - rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1); - rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); - rb_define_singleton_method(rb_cDFHack, "print_color", RUBY_METHOD_FUNC(rb_dfprint_color), 2); - rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); - rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1); - rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); - rb_define_singleton_method(rb_cDFHack, "pagealloc", RUBY_METHOD_FUNC(rb_dfmemory_pagealloc), 1); - rb_define_singleton_method(rb_cDFHack, "pagedealloc", RUBY_METHOD_FUNC(rb_dfmemory_pagedealloc), 2); - rb_define_singleton_method(rb_cDFHack, "pageprotect", RUBY_METHOD_FUNC(rb_dfmemory_pageprotect), 3); - rb_define_singleton_method(rb_cDFHack, "vmethod_do_call", RUBY_METHOD_FUNC(rb_dfvcall), 8); - rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfversion), 0); - - rb_define_singleton_method(rb_cDFHack, "memory_read", RUBY_METHOD_FUNC(rb_dfmemory_read), 2); - rb_define_singleton_method(rb_cDFHack, "memory_read_int8", RUBY_METHOD_FUNC(rb_dfmemory_read_int8), 1); - rb_define_singleton_method(rb_cDFHack, "memory_read_int16", RUBY_METHOD_FUNC(rb_dfmemory_read_int16), 1); - rb_define_singleton_method(rb_cDFHack, "memory_read_int32", RUBY_METHOD_FUNC(rb_dfmemory_read_int32), 1); - rb_define_singleton_method(rb_cDFHack, "memory_read_float", RUBY_METHOD_FUNC(rb_dfmemory_read_float), 1); - rb_define_singleton_method(rb_cDFHack, "memory_read_double", RUBY_METHOD_FUNC(rb_dfmemory_read_double), 1); - - rb_define_singleton_method(rb_cDFHack, "memory_write", RUBY_METHOD_FUNC(rb_dfmemory_write), 2); - rb_define_singleton_method(rb_cDFHack, "memory_write_int8", RUBY_METHOD_FUNC(rb_dfmemory_write_int8), 2); - rb_define_singleton_method(rb_cDFHack, "memory_write_int16", RUBY_METHOD_FUNC(rb_dfmemory_write_int16), 2); - rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2); - rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); - rb_define_singleton_method(rb_cDFHack, "memory_write_double", RUBY_METHOD_FUNC(rb_dfmemory_write_double), 2); - rb_define_singleton_method(rb_cDFHack, "memory_check", RUBY_METHOD_FUNC(rb_dfmemory_check), 1); - rb_define_singleton_method(rb_cDFHack, "memory_patch", RUBY_METHOD_FUNC(rb_dfmemory_patch), 2); - - rb_define_singleton_method(rb_cDFHack, "memory_stlstring_new", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_new), 0); - rb_define_singleton_method(rb_cDFHack, "memory_stlstring_delete", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_delete), 1); - rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1); - rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); - rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector_new", RUBY_METHOD_FUNC(rb_dfmemory_vec_new), 0); - rb_define_singleton_method(rb_cDFHack, "memory_vector_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec_delete), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insertat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_deleteat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insertat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_deleteat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insertat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_deleteat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector64_length", RUBY_METHOD_FUNC(rb_dfmemory_vec64_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector64_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec64_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector64_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec64_insertat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector64_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec64_deleteat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_new", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_new), 0); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_init", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_init), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_length", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_at", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_at), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insertat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_deleteat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_bitarray_length", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_bitarray_resize", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_resize), 2); - rb_define_singleton_method(rb_cDFHack, "memory_bitarray_isset", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_isset), 2); - rb_define_singleton_method(rb_cDFHack, "memory_bitarray_set", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_set), 3); - rb_define_singleton_method(rb_cDFHack, "memory_stlset_new", RUBY_METHOD_FUNC(rb_dfmemory_set_new), 0); - rb_define_singleton_method(rb_cDFHack, "memory_stlset_delete", RUBY_METHOD_FUNC(rb_dfmemory_set_delete), 1); - rb_define_singleton_method(rb_cDFHack, "memory_stlset_set", RUBY_METHOD_FUNC(rb_dfmemory_set_set), 2); - rb_define_singleton_method(rb_cDFHack, "memory_stlset_isset", RUBY_METHOD_FUNC(rb_dfmemory_set_isset), 2); - rb_define_singleton_method(rb_cDFHack, "memory_stlset_deletekey", RUBY_METHOD_FUNC(rb_dfmemory_set_deletekey), 2); - rb_define_singleton_method(rb_cDFHack, "memory_stlset_clear", RUBY_METHOD_FUNC(rb_dfmemory_set_clear), 1); -} diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb deleted file mode 100644 index 663f85111..000000000 --- a/plugins/ruby/ruby.rb +++ /dev/null @@ -1,258 +0,0 @@ -# redefine standard i/o methods to use the dfhack console -module Kernel - def puts(*a) - a.flatten.each { |l| - # XXX looks like print_str crashes with strings longer than 4096... maybe not nullterminated ? - # this workaround fixes it - s = l.to_s.chomp + "\n" - while s.length > 0 - DFHack.print_str(s[0, 4000]) - s[0, 4000] = '' - end - } - nil - end - - def puts_err(*a) - a.flatten.each { |l| - s = l.to_s.chomp + "\n" - while s.length > 0 - DFHack.print_err(s[0, 4000]) - s[0, 4000] = '' - end - } - nil - end - - def p(*a) - a.each { |e| - puts_err e.inspect - } - nil - end -end - -module DFHack - VERSION = version - - class OnupdateCallback - attr_accessor :callback, :timelimit, :minyear, :minyeartick, :description - def initialize(descr, cb, tl, initdelay=0) - @description = descr - @callback = cb - @ticklimit = tl - @minyear = (tl ? df.cur_year : 0) - @minyeartick = (tl ? df.cur_year_tick+initdelay : 0) - end - - # run callback if timedout - def check_run(year, yeartick, yearlen) - if @ticklimit - return unless year > @minyear or (year == @minyear and yeartick >= @minyeartick) - @minyear = year - @minyeartick = yeartick + @ticklimit - if @minyeartick > yearlen - @minyear += 1 - @minyeartick -= yearlen - end - end - # t0 = Time.now - @callback.call - # dt = Time.now - t0 ; puts "rb cb #@description took #{'%.02f' % dt}s" if dt > 0.1 - rescue Exception - df.onupdate_unregister self - puts_err "onupdate #@description unregistered: #$!", $!.backtrace - end - - def <=>(o) - [@minyear, @minyeartick] <=> [o.minyear, o.minyeartick] - end - end - - class << self - attr_accessor :onupdate_list, :onstatechange_list - - # register a callback to be called every gframe or more - # ex: DFHack.onupdate_register('fastdwarf') { DFHack.world.units[0].counters.job_counter = 0 } - # if ticklimit is given, do not call unless this much game ticks have passed. Handles advmode time stretching. - def onupdate_register(descr, ticklimit=nil, initialtickdelay=0, &b) - raise ArgumentError, 'need a description as 1st arg' unless descr.kind_of?(::String) - @onupdate_list ||= [] - @onupdate_list << OnupdateCallback.new(descr, b, ticklimit, initialtickdelay) - DFHack.onupdate_active = true - if onext = @onupdate_list.min - DFHack.onupdate_minyear = onext.minyear - DFHack.onupdate_minyeartick = onext.minyeartick - end - @onupdate_list.last - end - - # delete the callback for onupdate ; use the value returned by onupdate_register or the description - def onupdate_unregister(b) - b = @onupdate_list.find { |bb| bb.description == b } if b.kind_of?(String) - @onupdate_list.delete b - if @onupdate_list.empty? - DFHack.onupdate_active = false - DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = DFHack.onupdate_minyeartickadv = -1 - end - end - - # same as onupdate_register, but remove the callback once it returns true - def onupdate_register_once(*a) - handle = onupdate_register(*a) { - onupdate_unregister(handle) if yield - } - end - - TICKS_PER_YEAR = 1200*28*12 - # this method is called by ruby.cpp if df.onupdate_active is true - def onupdate - @onupdate_list ||= [] - - y = yt = 0 - y = cur_year rescue 0 - ytmax = TICKS_PER_YEAR - if df.gamemode == :ADVENTURE and df.respond_to?(:cur_year_tick_advmode) - yt = cur_year_tick_advmode - ytmax *= 144 - else - yt = cur_year_tick rescue 0 - end - - @onupdate_list.each { |o| - o.check_run(y, yt, ytmax) - } - - if onext = @onupdate_list.min - DFHack.onupdate_minyear = onext.minyear - if ytmax > TICKS_PER_YEAR - DFHack.onupdate_minyeartick = -1 - DFHack.onupdate_minyeartickadv = onext.minyeartick - else - DFHack.onupdate_minyeartick = onext.minyeartick - DFHack.onupdate_minyeartickadv = -1 - end - end - end - - # register a callback to be called every gframe or more - # ex: DFHack.onstatechange_register { |newstate| puts "state changed to #{newstate}" } - def onstatechange_register(&b) - @onstatechange_list ||= [] - @onstatechange_list << b - @onstatechange_list.last - end - - # delete the callback for onstatechange ; use the value returned by onstatechange_register - def onstatechange_unregister(b) - @onstatechange_list.delete b - end - - # same as onstatechange_register, but auto-unregisters if the block returns true - def onstatechange_register_once - handle = onstatechange_register { |st| - onstatechange_unregister(handle) if yield(st) - } - end - - - # this method is called by dfhack every 'onstatechange' - def onstatechange(newstate) - @onstatechange_list ||= [] - @onstatechange_list.each { |cb| cb.call(newstate) } - end - - # return true if the argument is under the cursor - def at_cursor?(obj) - same_pos?(obj, cursor) - end - - # returns true if both arguments are at the same x/y/z - def same_pos?(pos1, pos2) - pos1 = pos1.pos if pos1.respond_to?(:pos) - pos2 = pos2.pos if pos2.respond_to?(:pos) - pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z - end - - # try to match a user-specified name to one from the raws - # uses case-switching and substring matching - # eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS' - def match_rawname(name, rawlist) - rawlist.each { |r| return r if name == r } - rawlist.each { |r| return r if name.downcase == r.downcase } - may = rawlist.find_all { |r| r.downcase.index(name.downcase) } - may.first if may.length == 1 - end - - def translate_name(name, english=true, onlylastpart=false) - out = [] - - if not onlylastpart - out << name.first_name if name.first_name != '' - if name.nickname != '' - case respond_to?(:d_init) && d_init.nickname[gametype] - when :REPLACE_ALL; return "`#{name.nickname}'" - when :REPLACE_FIRST; out.pop - end - out << "`#{name.nickname}'" - end - end - return out.join(' ') unless name.words.find { |w| w >= 0 } - - if not english - tsl = world.raws.language.translations[name.language] - if name.words[0] >= 0 or name.words[1] >= 0 - out << '' - out.last << tsl.words[name.words[0]] if name.words[0] >= 0 - out.last << tsl.words[name.words[1]] if name.words[1] >= 0 - end - if name.words[5] >= 0 - out << '' - (2..5).each { |i| out.last << tsl.words[name.words[i]] if name.words[i] >= 0 } - end - if name.words[6] >= 0 - out << tsl.words[name.words[6]] - end - else - wl = world.raws.language - if name.words[0] >= 0 or name.words[1] >= 0 - out << '' - out.last << wl.words[name.words[0]].forms[name.parts_of_speech[0]] if name.words[0] >= 0 - out.last << wl.words[name.words[1]].forms[name.parts_of_speech[1]] if name.words[1] >= 0 - end - if name.words[5] >= 0 - out << 'the' - out.last.capitalize! if out.length == 1 - out << wl.words[name.words[2]].forms[name.parts_of_speech[2]] if name.words[2] >= 0 - out << wl.words[name.words[3]].forms[name.parts_of_speech[3]] if name.words[3] >= 0 - if name.words[4] >= 0 - out << wl.words[name.words[4]].forms[name.parts_of_speech[4]] - out.last << '-' - else - out << '' - end - out.last << wl.words[name.words[5]].forms[name.parts_of_speech[5]] - end - if name.words[6] >= 0 - out << 'of' - out.last.capitalize! if out.length == 1 - out << wl.words[name.words[6]].forms[name.parts_of_speech[6]] - end - end - - out.join(' ') - end - end -end - -# global alias so we can write 'df.world.units.all[0]' -def df - DFHack -end - -# load autogenned file -require './hack/ruby/ruby-autogen-defs' -require(RUBY_PLATFORM =~ /mswin|mingw|cygwin/i ? './hack/ruby/ruby-autogen-win' : './hack/ruby/ruby-autogen-gcc') - -# load all modules -Dir['./hack/ruby/*.rb'].each { |m| require m.chomp('.rb') if m !~ /ruby-autogen/ } diff --git a/plugins/ruby/ui.rb b/plugins/ruby/ui.rb deleted file mode 100644 index 9268dcb9e..000000000 --- a/plugins/ruby/ui.rb +++ /dev/null @@ -1,91 +0,0 @@ -# df user-interface related methods -module DFHack - class << self - # returns the current active viewscreen - def curview - ret = gview.view - ret = ret.child while ret.child - ret - end - - # center the DF screen on something - # updates the cursor position if visible - def center_viewscreen(x, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - - # compute screen 'map' size (tiles) - menuwidth = ui_menu_width[0] - # ui_menu_width shows only the 'tab' status - menuwidth = 1 if menuwidth == 2 and ui_menu_width[1] == 2 and cursor.x != -30000 - menuwidth = 2 if menuwidth == 3 and cursor.x != -30000 - w_w = gps.dimx - 2 - w_h = gps.dimy - 2 - case menuwidth - when 1; w_w -= 55 - when 2; w_w -= (ui_menu_width[1] == 2 ? 24 : 31) - end - - # center view - w_x = x - w_w/2 - w_y = y - w_h/2 - w_z = z - # round view coordinates (optional) - #w_x -= w_x % 10 - #w_y -= w_y % 10 - # crop to map limits - w_x = [[w_x, world.map.x_count - w_w].min, 0].max - w_y = [[w_y, world.map.y_count - w_h].min, 0].max - - self.window_x = w_x - self.window_y = w_y - self.window_z = w_z - - if cursor.x != -30000 - cursor.x, cursor.y, cursor.z = x, y, z - end - end - - # add an announcement - # color = integer, bright = bool - def add_announcement(str, color=nil, bright=nil) - cont = false - while str.length > 0 - rep = Report.cpp_new - rep.color = color if color - rep.bright = ((bright && bright != 0) ? 1 : 0) if bright != nil - rep.year = cur_year - rep.time = cur_year_tick - rep.flags.continuation = cont - cont = true - rep.flags.announcement = true - rep.text = str[0, 73] - str = str[73..-1].to_s - rep.id = world.status.next_report_id - world.status.next_report_id += 1 - world.status.reports << rep - world.status.announcements << rep - world.status.display_timer = 2000 - yield rep if block_given? - end - end - - # add an announcement to display in a game popup message - # (eg "the megabeast foobar arrived") - def popup_announcement(str, color=nil, bright=nil) - pop = PopupMessage.cpp_new(:text => str) - pop.color = color if color - pop.bright = bright if bright - world.status.popups << pop - end - end - - class Viewscreen - def feed_keys(*keys) - keyset = StlSet.cpp_new(keys, InterfaceKey) - ret = feed(keyset) - keyset._cpp_delete - ret - end - end -end diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb deleted file mode 100644 index b27f2f0c3..000000000 --- a/plugins/ruby/unit.rb +++ /dev/null @@ -1,280 +0,0 @@ -module DFHack - class << self - # return an Unit - # with no arg, return currently selected unit in df UI ('v' or 'k' menu) - # with numeric arg, search unit by unit.id - # with an argument that respond to x/y/z (eg cursor), find first unit at this position - def unit_find(what=:selected, y=nil, z=nil) - if what == :selected - return world.units.all.binsearch(df.get_selected_unit_id) - elsif what.kind_of?(Integer) - # search by id - return world.units.all.binsearch(what) if not z - # search by coords - x = what - world.units.all.find { |u| u.pos.x == x and u.pos.y == y and u.pos.z == z } - elsif what.respond_to?(:x) or what.respond_to?(:pos) - world.units.all.find { |u| same_pos?(what, u) } - else - raise "what what?" - end - end - - # returns an Array of all units that are current fort citizen (dwarves, on map, not hostile) - def unit_citizens - world.units.active.find_all { |u| - unit_iscitizen(u) - } - end - - def unit_testflagcurse(u, flag) - return false if u.curse.rem_tags1.send(flag) - return true if u.curse.add_tags1.send(flag) - return false if u.caste < 0 - u.race_tg.caste[u.caste].flags[flag] - end - - def unit_isfortmember(u) - # RE from viewscreen_unitlistst ctor - return false if df.gamemode != :DWARF or - u.mood == :Berserk or - unit_testflagcurse(u, :CRAZED) or - unit_testflagcurse(u, :OPPOSED_TO_LIFE) or - u.enemy.undead or - u.flags3.ghostly or - u.flags1.marauder or u.flags1.active_invader or u.flags1.invader_origin or - u.flags1.forest or - u.flags1.merchant or u.flags1.diplomat - return true if u.flags1.tame - return false if u.flags2.underworld or u.flags2.resident or - u.flags2.visitor_uninvited or u.flags2.visitor or - u.civ_id == -1 or - u.civ_id != df.ui.civ_id - true - end - - # return the page in viewscreen_unitlist where the unit would appear - def unit_category(u) - return if u.flags1.left or u.flags1.incoming - # return if hostile & unit_invisible(u) (hidden_in_ambush or caged+mapblock.hidden or caged+holder.ambush - return :Dead if u.flags2.killed - return :Dead if u.flags3.ghostly # hostile ? - return if u.flags1.inactive - return :Others if !unit_isfortmember(u) - casteflags = u.race_tg.caste[u.caste].flags if u.caste >= 0 - return :Livestock if casteflags and (casteflags[:PET] or casteflags[:PET_EXOTIC]) - return :Citizens if unit_testflagcurse(u, :CAN_SPEAK) - :Livestock - # some other stuff with ui.race_id ? (jobs only?) - end - - # merchant: df.ui.caravans.find { |cv| cv.entity == u.civ_id } - # diplomat: df.ui.dip_meeting_info.find { |m| m.diplomat_id == u.hist_figure_id or m.diplomat_id2 == u.hist_figure_id } - - - def unit_nemesis(u) - if ref = u.general_refs.find { |r| r.kind_of?(DFHack::GeneralRefIsNemesisst) } - ref.nemesis_tg - end - end - - # return the subcategory for :Others (from vs_unitlist) - def unit_other_category(u) - # comment is actual code returned by the df function - return :Berserk if u.mood == :Berserk # 5 - return :Berserk if unit_testflagcurse(u, :CRAZED) # 14 - return :Undead if unit_testflagcurse(u, :OPPOSED_TO_LIFE) # 1 - return :Undead if u.flags3.ghostly # 15 - - if df.gamemode == :ADVENTURE - return :Hostile if u.civ_id == -1 # 2 - if u.animal.population.region_x == -1 - return :Wild if u.flags2.roaming_wilderness_population_source_not_a_map_feature # 0 - else - return :Hostile if u.flags2.important_historical_figure and n = unit_nemesis(u) and n.flags[:ACTIVE_ADVENTURER] # 2 - end - return :Hostile if u.flags2.resident # 3 - return :Hostile # 4 - end - - return :Invader if u.flags1.active_invader or u.flags1.invader_origin # 6 - return :Friendly if u.flags1.forest or u.flags1.merchant or u.flags1.diplomat # 8 - return :Hostile if u.flags1.tame # 7 - - if u.civ_id != -1 - return :Unsure if u.civ_id != df.ui.civ_id or u.flags1.resident or u.flags1.visitor or u.flags1.visitor_uninvited # 10 - return :Hostile # 7 - - elsif u.animal.population.region_x == -1 - return :Friendly if u.flags2.visitor # 8 - return :Uninvited if u.flags2.visitor_uninvited # 12 - return :Underworld if r = u.race_tg and r.underground_layer_min == 5 # 9 - return :Resident if u.flags2.resident # 13 - return :Friendly # 8 - - else - return :Friendly if u.flags2.visitor # 8 - return :Underworld if r = u.race_tg and r.underground_layer_min == 5 # 9 - return :Wild if u.animal.population.feature_idx == -1 and u.animal.population.cave_id == -1 # 0 - return :Wild # 11 - end - end - - def unit_iscitizen(u) - unit_category(u) == :Citizens - end - - def unit_hostiles - world.units.active.find_all { |u| - unit_ishostile(u) - } - end - - # returns if an unit is openly hostile - # does not include ghosts / wildlife - def unit_ishostile(u) - # return true if u.flags3.ghostly and not u.flags1.inactive - return false unless unit_category(u) == :Others - - case unit_other_category(u) - when :Berserk, :Undead, :Hostile, :Invader, :Underworld - # XXX :Resident, :Uninvited? - true - - when :Unsure - # from df code, with removed duplicate checks already in other_category - return true if u.enemy.undead or u.flags3.ghostly or u.flags1.marauder - return false if u.flags1.forest or u.flags1.merchant or u.flags1.diplomat or u.flags2.visitor - return true if u.flags1.tame or u.flags2.underworld - - if histfig = u.hist_figure_tg - group = df.ui.group_tg - case unit_checkdiplomacy_hf_ent(histfig, group) - when 4, 5 - true - else - false - end - - elsif diplo = u.civ_tg.unknown1b.diplomacy.binsearch(df.ui.group_id, :group_id) - diplo.relation != 1 and diplo.relation != 5 - - else - u.animal.population.region_x != -1 or u.flags2.resident or u.flags2.visitor_uninvited - end - end - end - - def unit_checkdiplomacy_hf_ent(histfig, group) - var_3d = var_3e = var_45 = var_46 = var_47 = var_48 = var_49 = nil - - var_3d = 1 if group.type == :Outcast or group.type == :NomadicGroup or - (group.type == :Civilization and group.entity_raw.flags[:LOCAL_BANDITRY]) - - histfig.entity_links.each { |link| - if link.entity_id == group.id - case link.getType - when :MEMBER, :MERCENARY, :SLAVE, :PRISONER, :POSITION, :HERO - var_47 = 1 - when :FORMER_MEMBER, :FORMER_MERCENARY, :FORMER_SLAVE, :FORMER_PRISONER - var_48 = 1 - when :ENEMY - var_49 = 1 - when :CRIMINAL - var_45 = 1 - end - else - case link.getType - when :MEMBER, :MERCENARY, :SLAVE - if link_entity = link.entity_tg - diplo = group.unknown1b.diplomacy.binsearch(link.entity_id, :group_id) - case diplo.relation - when 0, 3, 4 - var_48 = 1 - when 1, 5 - var_46 = 1 - end - - var_3e = 1 if link_entity.type == :Outcast or link_entity.type == :NomadicGroup or - (link_entity.type == :Civilization and link_entity.entity_raw.flags[:LOCAL_BANDITRY]) - end - end - end - } - - if var_49 - 4 - elsif var_46 - 5 - elsif !var_47 and group.resources.ethic[:KILL_NEUTRAL] == 16 - 4 - elsif df.gamemode == :ADVENTURE and !var_47 and (var_3e or !var_3d) - 4 - elsif var_45 - 3 - elsif var_47 - 2 - elsif var_48 - 1 - else - 0 - end - end - - - # list workers (citizen, not crazy / child / inmood / noble) - def unit_workers - world.units.active.find_all { |u| - unit_isworker(u) - } - end - - def unit_isworker(u) - unit_iscitizen(u) and - u.race == df.ui.race_id and - u.mood == :None and - u.profession != :CHILD and - u.profession != :BABY and - # TODO MENIAL_WORK_EXEMPTION_SPOUSE - !unit_entitypositions(u).find { |pos| pos.flags[:MENIAL_WORK_EXEMPTION] } - end - - # list currently idle workers - def unit_idlers - world.units.active.find_all { |u| - unit_isidler(u) - } - end - - def unit_isidler(u) - unit_isworker(u) and - # current_job includes eat/drink/sleep/pickupequip - !u.job.current_job and - # filter 'attend meeting' - not u.specific_refs.find { |s| s.type == :ACTIVITY } and - # filter soldiers (TODO check schedule) - u.military.squad_id == -1 and - # filter incoming migrants - not u.status.misc_traits.find { |t| t.id == :Migrant } - end - - def unit_entitypositions(unit) - list = [] - return list if not histfig = unit.hist_figure_tg - histfig.entity_links.each { |el| - next if el._rtti_classname != :histfig_entity_link_positionst - next if not ent = el.entity_tg - next if not pa = ent.positions.assignments.binsearch(el.assignment_id) - next if not pos = ent.positions.own.binsearch(pa.position_id) - list << pos - } - list - end - end - - class LanguageName - def to_s(english=false) - df.translate_name(self, english) - end - end -end diff --git a/plugins/ruby/win32/.gitignore b/plugins/ruby/win32/.gitignore deleted file mode 100644 index ef44e3942..000000000 --- a/plugins/ruby/win32/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libruby* diff --git a/plugins/ruby/win64/.gitignore b/plugins/ruby/win64/.gitignore deleted file mode 100644 index ef44e3942..000000000 --- a/plugins/ruby/win64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libruby* From a56792a5332ef49cf2e9772613452655af305be8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 00:01:29 -0800 Subject: [PATCH 0100/2222] note that Ruby is deprecated in the changelog script --- docs/sphinx_extensions/dfhack/changelog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx_extensions/dfhack/changelog.py b/docs/sphinx_extensions/dfhack/changelog.py index 0cd732988..dd59fa9d5 100644 --- a/docs/sphinx_extensions/dfhack/changelog.py +++ b/docs/sphinx_extensions/dfhack/changelog.py @@ -27,7 +27,7 @@ CHANGELOG_SECTIONS = [ 'API', 'Internals', 'Lua', - 'Ruby', + 'Ruby', # deprecated, but still here so old changelogs build 'Structures', 'Documentation', ] From 60170f252fe8d2eb0d965fbf5653ccc6f7ffcadb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 00:42:37 -0800 Subject: [PATCH 0101/2222] add regrass plugin back in (no changes) --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 06d2b5c0b..58712bdf5 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -142,7 +142,7 @@ dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) dfhack_plugin(probe probe.cpp) #dfhack_plugin(prospector prospector.cpp LINK_LIBRARIES lua) #dfhack_plugin(power-meter power-meter.cpp LINK_LIBRARIES lua) -#dfhack_plugin(regrass regrass.cpp) +dfhack_plugin(regrass regrass.cpp) add_subdirectory(remotefortressreader) #dfhack_plugin(rename rename.cpp LINK_LIBRARIES lua PROTOBUFS rename) #add_subdirectory(rendermax) From ad4ce706f1530c631b14971d7cb9f526fe544af6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 00:46:52 -0800 Subject: [PATCH 0102/2222] enable fastdwarf (no changes) --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 58712bdf5..6e02c644c 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -112,7 +112,7 @@ dfhack_plugin(debug debug.cpp LINK_LIBRARIES jsoncpp_static) #add_subdirectory(embark-assistant) #dfhack_plugin(embark-tools embark-tools.cpp) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) -#dfhack_plugin(fastdwarf fastdwarf.cpp) +dfhack_plugin(fastdwarf fastdwarf.cpp) #dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fixveins fixveins.cpp) From 1cf4604e6ce8d51aa09374e68313e398db4d6555 Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 10 Jan 2023 15:13:49 -0500 Subject: [PATCH 0103/2222] Changelog for #2583 --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b5e6d3f97..d0c7b1abb 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -51,6 +51,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Gui::getDFViewscreen``: returns the topmost underlying DF viewscreen ## Lua +- Removed ``os.execute()`` and ``io.popen()`` built-in functions - ``gui.View``: ``visible`` and ``active`` can now be functions that return a boolean - ``widgets.Panel``: new attributes to control window dragging and resizing with mouse or keyboard - ``widgets.Window``: Panel subclass with attributes preset for top-level windows From 353117d484ea0017ba86b7d9bd000ddf93d8a014 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 19:05:04 -0800 Subject: [PATCH 0104/2222] fix a crash in Screen when read or paint tile indices are negative --- library/modules/Screen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 65817aa93..30e922708 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -208,7 +208,7 @@ static bool doSetTile(const Pen &pen, int x, int y, bool map) bool Screen::paintTile(const Pen &pen, int x, int y, bool map) { - if (!gps || !pen.valid()) return false; + if (!gps || !pen.valid() || x < 0 || y < 0) return false; doSetTile(pen, x, y, map); return true; @@ -301,7 +301,7 @@ static Pen doGetTile(int x, int y, bool map) Pen Screen::readTile(int x, int y, bool map) { - if (!gps) return Pen(0,0,0,-1); + if (!gps || x < 0 || y < 0) return Pen(0,0,0,-1); return doGetTile(x, y, map); } From 1d0f6b3a95b52fd289d900d5578c980a1c3ae3cb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 19:40:13 -0800 Subject: [PATCH 0105/2222] more careful bounds checking for screen tiles --- library/modules/Screen.cpp | 45 ++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 30e922708..ae0a15b5e 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -116,17 +116,22 @@ bool Screen::inGraphicsMode() } static bool doSetTile_map(const Pen &pen, int x, int y) { - size_t max_index = gps->main_viewport->dim_x * gps->main_viewport->dim_y - 1; - size_t index = (x * gps->main_viewport->dim_y) + y; + auto &vp = gps->main_viewport; - if (index < 0 || index > max_index) + if (x < 0 || x >= vp->dim_x || y < 0 || y >= vp->dim_y) + return false; + + size_t max_index = vp->dim_x * vp->dim_y - 1; + size_t index = (x * vp->dim_y) + y; + + if (index > max_index) return false; long texpos = pen.tile; if (texpos == 0) { texpos = init->font.large_font_texpos[(uint8_t)pen.ch]; } - gps->main_viewport->screentexpos_interface[index] = texpos; + vp->screentexpos_interface[index] = texpos; return true; } @@ -137,6 +142,9 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) if (map && use_graphics) return doSetTile_map(pen, x, y); + if (x < 0 || x >= gps->dimx || y < 0 || y >= gps->dimy) + return false; + size_t index = (x * gps->dimy) + y; uint8_t *screen = &gps->screen[index * 8]; @@ -208,28 +216,33 @@ static bool doSetTile(const Pen &pen, int x, int y, bool map) bool Screen::paintTile(const Pen &pen, int x, int y, bool map) { - if (!gps || !pen.valid() || x < 0 || y < 0) return false; + if (!gps || !pen.valid()) return false; doSetTile(pen, x, y, map); return true; } static Pen doGetTile_map(int x, int y) { - size_t max_index = gps->main_viewport->dim_x * gps->main_viewport->dim_y - 1; - size_t index = (x * gps->main_viewport->dim_y) + y; + auto &vp = gps->main_viewport; + + if (x < 0 || x >= vp->dim_x || y < 0 || y >= vp->dim_y) + return Pen(0, 0, 0, -1); + + size_t max_index = vp->dim_x * vp->dim_y - 1; + size_t index = (x * vp->dim_y) + y; if (index < 0 || index > max_index) return Pen(0, 0, 0, -1); - int tile = gps->main_viewport->screentexpos[index]; + int tile = vp->screentexpos[index]; if (tile == 0) - tile = gps->main_viewport->screentexpos_item[index]; + tile = vp->screentexpos_item[index]; if (tile == 0) - tile = gps->main_viewport->screentexpos_building_one[index]; + tile = vp->screentexpos_building_one[index]; if (tile == 0) - tile = gps->main_viewport->screentexpos_background_two[index]; + tile = vp->screentexpos_background_two[index]; if (tile == 0) - tile = gps->main_viewport->screentexpos_background[index]; + tile = vp->screentexpos_background[index]; char ch = 0; uint8_t fg = 0; @@ -249,14 +262,14 @@ static uint8_t to_16_bit_color(uint8_t *rgb) { } static Pen doGetTile_default(int x, int y, bool map) { - if (x < 0 || y < 0) - return Pen(0, 0, 0, -1); - bool use_graphics = Screen::inGraphicsMode(); if (map && use_graphics) return doGetTile_map(x, y); + if (x < 0 || x >= gps->dimx || y < 0 || y >= gps->dimy) + return Pen(0, 0, 0, -1); + size_t index = (x * gps->dimy) + y; uint8_t *screen = &gps->screen[index * 8]; @@ -301,7 +314,7 @@ static Pen doGetTile(int x, int y, bool map) Pen Screen::readTile(int x, int y, bool map) { - if (!gps || x < 0 || y < 0) return Pen(0,0,0,-1); + if (!gps) return Pen(0,0,0,-1); return doGetTile(x, y, map); } From e25f06f2feed9589c0532de76148b71007627cd9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 22:23:59 -0800 Subject: [PATCH 0106/2222] register new "untested" tag --- docs/Tags.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index 2ee18de48..60a6a6813 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -14,7 +14,7 @@ for the tag assignment spreadsheet. "when" tags ----------- - `adventure `: Tools that are useful while in adventure mode. Note that some tools only tagged with "fort" might also work in adventure mode, but not always in expected ways. Feel free to experiment, though! -- `dfhack `: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. settings, saving, etc.) +- `dfhack `: Tools that you use to run DFHack commands or interact with the DFHack library. This tag also includes tools that help you manage the DF game itself (e.g. quicksave, etc.) - `embark `: Tools that are useful while on the fort embark screen or while creating an adventurer. - `fort `: Tools that are useful while in fort mode. - `legends `: Tools that are useful while in legends mode. @@ -46,3 +46,7 @@ for the tag assignment spreadsheet. - `stockpiles `: Tools that interact with stockpiles. - `units `: Tools that interact with units. - `workorders `: Tools that interact with workorders. + +"misc" tags +----------- +- `untested `: Tools that are not yet tested with the current release. From 5a040a44cb2cbd0389b35e68f4c1670b05378b4a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 22:26:12 -0800 Subject: [PATCH 0107/2222] add untested tag to plugins --- docs/plugins/3dveins.rst | 2 +- docs/plugins/RemoteFortressReader.rst | 2 +- docs/plugins/add-spatter.rst | 2 +- docs/plugins/autoclothing.rst | 2 +- docs/plugins/autodump.rst | 2 +- docs/plugins/autogems.rst | 2 +- docs/plugins/autohauler.rst | 2 +- docs/plugins/autolabor.rst | 2 +- docs/plugins/automaterial.rst | 2 +- docs/plugins/automelt.rst | 2 +- docs/plugins/autonestbox.rst | 2 +- docs/plugins/autotrade.rst | 2 +- docs/plugins/blueprint.rst | 2 +- docs/plugins/building-hacks.rst | 2 +- docs/plugins/buildingplan.rst | 2 +- docs/plugins/burrows.rst | 2 +- docs/plugins/changeitem.rst | 2 +- docs/plugins/changelayer.rst | 2 +- docs/plugins/changevein.rst | 2 +- docs/plugins/channel-safely.rst | 2 +- docs/plugins/cleanconst.rst | 2 +- docs/plugins/cleaners.rst | 2 +- docs/plugins/confirm.rst | 2 +- docs/plugins/createitem.rst | 2 +- docs/plugins/cursecheck.rst | 2 +- docs/plugins/cxxrandom.rst | 2 +- docs/plugins/deramp.rst | 2 +- docs/plugins/dig-now.rst | 2 +- docs/plugins/dig.rst | 2 +- docs/plugins/digFlood.rst | 2 +- docs/plugins/diggingInvaders.rst | 2 +- docs/plugins/dwarfmonitor.rst | 2 +- docs/plugins/dwarfvet.rst | 2 +- docs/plugins/embark-assistant.rst | 2 +- docs/plugins/embark-tools.rst | 2 +- docs/plugins/fastdwarf.rst | 2 +- docs/plugins/filltraffic.rst | 2 +- docs/plugins/fix-unit-occupancy.rst | 2 +- docs/plugins/fixveins.rst | 2 +- docs/plugins/flows.rst | 2 +- docs/plugins/follow.rst | 2 +- docs/plugins/forceequip.rst | 2 +- docs/plugins/generated-creature-renamer.rst | 2 +- docs/plugins/getplants.rst | 2 +- docs/plugins/infiniteSky.rst | 2 +- docs/plugins/isoworldremote.rst | 2 +- docs/plugins/jobutils.rst | 2 +- docs/plugins/labormanager.rst | 2 +- docs/plugins/lair.rst | 2 +- docs/plugins/liquids.rst | 2 +- docs/plugins/luasocket.rst | 2 +- docs/plugins/manipulator.rst | 2 +- docs/plugins/map-render.rst | 2 +- docs/plugins/misery.rst | 2 +- docs/plugins/mode.rst | 2 +- docs/plugins/mousequery.rst | 2 +- docs/plugins/nestboxes.rst | 2 +- docs/plugins/petcapRemover.rst | 2 +- docs/plugins/plants.rst | 2 +- docs/plugins/power-meter.rst | 2 +- docs/plugins/prospector.rst | 2 +- docs/plugins/rename.rst | 2 +- docs/plugins/rendermax.rst | 2 +- docs/plugins/search.rst | 2 +- docs/plugins/siege-engine.rst | 2 +- docs/plugins/sort.rst | 2 +- docs/plugins/spectate.rst | 2 +- docs/plugins/steam-engine.rst | 2 +- docs/plugins/stockflow.rst | 2 +- docs/plugins/stockpiles.rst | 2 +- docs/plugins/stocks.rst | 2 +- docs/plugins/strangemood.rst | 2 +- docs/plugins/tailor.rst | 2 +- docs/plugins/tiletypes.rst | 2 +- docs/plugins/title-folder.rst | 2 +- docs/plugins/title-version.rst | 2 +- docs/plugins/trackstop.rst | 2 +- docs/plugins/tubefill.rst | 2 +- docs/plugins/tweak.rst | 2 +- docs/plugins/workNow.rst | 2 +- docs/plugins/workflow.rst | 2 +- docs/plugins/xlsxreader.rst | 2 +- docs/plugins/zone.rst | 2 +- 83 files changed, 83 insertions(+), 83 deletions(-) diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 96fbcf836..1d7acd94f 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -3,7 +3,7 @@ .. dfhack-tool:: :summary: Rewrite layer veins to expand in 3D space. - :tags: fort gameplay map + :tags: untested fort gameplay map Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in their place. The transformation preserves the mineral counts diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index c70adc608..9b45452bc 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -3,7 +3,7 @@ RemoteFortressReader .. dfhack-tool:: :summary: Backend for Armok Vision. - :tags: dev graphics + :tags: untested dev graphics :no-command: .. dfhack-command:: RemoteFortressReader_version diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 1ce32fcbe..fc3c507c2 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -3,7 +3,7 @@ add-spatter .. dfhack-tool:: :summary: Make tagged reactions produce contaminants. - :tags: adventure fort gameplay items + :tags: untested adventure fort gameplay items :no-command: Give some use to all those poisons that can be bought from caravans! The plugin diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 40fd4d996..2e90d92a5 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -3,7 +3,7 @@ autoclothing .. dfhack-tool:: :summary: Automatically manage clothing work orders. - :tags: fort auto workorders + :tags: untested fort auto workorders This command allows you to set how many of each clothing type every citizen should have. diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index 03e10db9c..ec83b6082 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -3,7 +3,7 @@ autodump .. dfhack-tool:: :summary: Automatically set items in a stockpile to be dumped. - :tags: fort armok fps productivity items stockpiles + :tags: untested fort armok fps productivity items stockpiles :no-command: .. dfhack-command:: autodump diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index d4112c6c0..f18b671a1 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -3,7 +3,7 @@ autogems .. dfhack-tool:: :summary: Automatically cut rough gems. - :tags: fort auto workorders + :tags: untested fort auto workorders :no-command: .. dfhack-command:: autogems-reload diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst index 09c3c2f8f..a40d50f34 100644 --- a/docs/plugins/autohauler.rst +++ b/docs/plugins/autohauler.rst @@ -3,7 +3,7 @@ autohauler .. dfhack-tool:: :summary: Automatically manage hauling labors. - :tags: fort auto labors + :tags: untested fort auto labors Similar to `autolabor`, but instead of managing all labors, autohauler only addresses hauling labors, leaving the assignment of skilled labors entirely up diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 4b92e5fd8..1fa859987 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -3,7 +3,7 @@ autolabor .. dfhack-tool:: :summary: Automatically manage dwarf labors. - :tags: fort auto labors + :tags: untested fort auto labors Autolabor attempts to keep as many dwarves as possible busy while allowing dwarves to specialize in specific skills. diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst index ac1b3a1da..54653a685 100644 --- a/docs/plugins/automaterial.rst +++ b/docs/plugins/automaterial.rst @@ -3,7 +3,7 @@ automaterial .. dfhack-tool:: :summary: Sorts building materials by recent usage. - :tags: fort design productivity buildings map + :tags: untested fort design productivity buildings map :no-command: This plugin makes building constructions (walls, floors, fortifications, etc) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 12e3412e2..9abe2cc9c 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -3,7 +3,7 @@ automelt .. dfhack-tool:: :summary: Quickly designate items to be melted. - :tags: fort productivity items stockpiles + :tags: untested fort productivity items stockpiles :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst index 27074fe1c..a107dff9b 100644 --- a/docs/plugins/autonestbox.rst +++ b/docs/plugins/autonestbox.rst @@ -3,7 +3,7 @@ autonestbox .. dfhack-tool:: :summary: Auto-assign egg-laying female pets to nestbox zones. - :tags: fort auto animals + :tags: untested fort auto animals To use this feature, you must create pen/pasture zones on the same tiles as built nestboxes. If the pen is bigger than 1x1, the nestbox must be in the top diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index f355d40ef..439eead40 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -3,7 +3,7 @@ autotrade .. dfhack-tool:: :summary: Quickly designate items to be traded. - :tags: fort productivity items stockpiles + :tags: untested fort productivity items stockpiles :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 6c38a6152..aa2d0fa03 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -3,7 +3,7 @@ blueprint .. dfhack-tool:: :summary: Record a live game map in a quickfort blueprint. - :tags: fort design buildings map stockpiles + :tags: untested fort design buildings map stockpiles With ``blueprint``, you can export the structure of a portion of your fortress in a blueprint file that you (or anyone else) can later play back with diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index 12f49fbbe..cce3479d8 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -3,7 +3,7 @@ building-hacks .. dfhack-tool:: :summary: Provides a Lua API for creating powered workshops. - :tags: fort gameplay buildings + :tags: untested fort gameplay buildings :no-command: See `building-hacks-api` for more details. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index ef159443d..1eb18b1d5 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -3,7 +3,7 @@ buildingplan .. dfhack-tool:: :summary: Plan building construction before you have materials. - :tags: fort design buildings + :tags: untested fort design buildings This plugin adds a planning mode for building placement. You can then place furniture, constructions, and other buildings before the required materials are diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index fb3876e97..54812ece5 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -3,7 +3,7 @@ burrows .. dfhack-tool:: :summary: Auto-expand burrows as you dig. - :tags: fort auto design productivity map units + :tags: untested fort auto design productivity map units :no-command: .. dfhack-command:: burrow diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index f7418238a..fe9229a09 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -3,7 +3,7 @@ changeitem .. dfhack-tool:: :summary: Change item material or base quality. - :tags: adventure fort armok items + :tags: untested adventure fort armok items By default, a change is only allowed if the existing and desired item materials are of the same subtype (for example wood -> wood, stone -> stone, etc). But diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index f986a1e3d..0f5421ee9 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -3,7 +3,7 @@ changelayer .. dfhack-tool:: :summary: Change the material of an entire geology layer. - :tags: fort armok map + :tags: untested fort armok map Note that one layer can stretch across many z-levels, and changes to the geology layer will affect all surrounding regions, not just your embark! Mineral veins diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index 4e4512bb1..c1485de5b 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -3,7 +3,7 @@ changevein .. dfhack-tool:: :summary: Change the material of a mineral inclusion. - :tags: fort armok map + :tags: untested fort armok map You can change a vein to any inorganic material RAW id. Note that this command only affects tiles within the current 16x16 block - for large veins and diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index 1b72c93df..e87a36455 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -3,7 +3,7 @@ channel-safely .. dfhack-tool:: :summary: Auto-manage channel designations to keep dwarves safe. - :tags: fort auto + :tags: untested fort auto Multi-level channel projects can be dangerous, and managing the safety of your dwarves throughout the completion of such projects can be difficult and time diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index 12b1db0e5..420635fe2 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -3,7 +3,7 @@ cleanconst .. dfhack-tool:: :summary: Cleans up construction materials. - :tags: fort fps buildings + :tags: untested fort fps buildings This tool alters all constructions on the map so that they spawn their building component when they are disassembled, allowing their actual build items to be diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index 14a25d62f..dd484f5df 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -6,7 +6,7 @@ cleaners .. dfhack-tool:: :summary: Provides commands for cleaning spatter from the map. - :tags: adventure fort armok fps items map units + :tags: untested adventure fort armok fps items map units :no-command: .. dfhack-command:: clean diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index 6679cf4e3..dcf5a4554 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -3,7 +3,7 @@ confirm .. dfhack-tool:: :summary: Adds confirmation dialogs for destructive actions. - :tags: fort interface + :tags: untested fort interface Now you can get the chance to avoid seizing goods from traders or deleting a hauling route in case you hit the key accidentally. diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index b29e41521..7abe06cff 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -3,7 +3,7 @@ createitem .. dfhack-tool:: :summary: Create arbitrary items. - :tags: adventure fort armok items + :tags: untested adventure fort armok items You can create new items of any type and made of any material. A unit must be selected in-game to use this command. By default, items created are spawned at diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index 94012919b..fd97ee252 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -3,7 +3,7 @@ cursecheck .. dfhack-tool:: :summary: Check for cursed creatures. - :tags: fort armok inspection units + :tags: untested fort armok inspection units This command checks a single map tile (or the whole map/world) for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies, etc.). diff --git a/docs/plugins/cxxrandom.rst b/docs/plugins/cxxrandom.rst index 19788e4a4..d2775977d 100644 --- a/docs/plugins/cxxrandom.rst +++ b/docs/plugins/cxxrandom.rst @@ -3,7 +3,7 @@ cxxrandom .. dfhack-tool:: :summary: Provides a Lua API for random distributions. - :tags: dev + :tags: untested dev :no-command: See `cxxrandom-api` for details. diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index fdc0f7619..641f370a6 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -3,7 +3,7 @@ deramp .. dfhack-tool:: :summary: Removes all ramps designated for removal from the map. - :tags: fort armok map + :tags: untested fort armok map It also removes any "floating" down ramps that can remain after a cave-in. diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst index 43ed6ce8f..f4a5d5d28 100644 --- a/docs/plugins/dig-now.rst +++ b/docs/plugins/dig-now.rst @@ -3,7 +3,7 @@ dig-now .. dfhack-tool:: :summary: Instantly complete dig designations. - :tags: fort armok map + :tags: untested fort armok map This tool will magically complete non-marker dig designations, modifying tile shapes and creating boulders, ores, and gems as if a miner were doing the mining diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index a10db97df..afe1fac17 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -6,7 +6,7 @@ dig .. dfhack-tool:: :summary: Provides commands for designating tiles for digging. - :tags: fort design productivity map + :tags: untested fort design productivity map :no-command: .. dfhack-command:: digv diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst index 57d4584a8..70dd9b388 100644 --- a/docs/plugins/digFlood.rst +++ b/docs/plugins/digFlood.rst @@ -3,7 +3,7 @@ digFlood .. dfhack-tool:: :summary: Digs out veins as they are discovered. - :tags: fort auto map + :tags: untested fort auto map Once you register specific vein types, this tool will automatically designate tiles of those types of veins for digging as your miners complete adjacent diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index ace04e9fc..4ea52013e 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -3,7 +3,7 @@ diggingInvaders .. dfhack-tool:: :summary: Invaders dig and destroy to get to your dwarves. - :tags: fort gameplay military units + :tags: untested fort gameplay military units Usage ----- diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index c1eb27aef..47d49d1e0 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -3,7 +3,7 @@ dwarfmonitor .. dfhack-tool:: :summary: Report on dwarf preferences and efficiency. - :tags: fort inspection jobs units + :tags: untested fort inspection jobs units It can also show heads-up display widgets with live fort statistics. diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index fa165250d..7480c10a6 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -3,7 +3,7 @@ dwarfvet .. dfhack-tool:: :summary: Allows animals to be treated at animal hospitals. - :tags: fort gameplay animals + :tags: untested fort gameplay animals Annoyed that your dragons become useless after a minor injury? Well, with dwarfvet, injured animals will be treated at an animal hospital, which is simply diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index c7a3f778e..bb74f221d 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -3,7 +3,7 @@ embark-assistant .. dfhack-tool:: :summary: Embark site selection support. - :tags: embark fort interface + :tags: untested embark fort interface Run this command while the pre-embark screen is displayed to show extended (and reasonably correct) resource information for the embark rectangle as well as diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index 781d66e90..6a600019b 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -3,7 +3,7 @@ embark-tools .. dfhack-tool:: :summary: Extend the embark screen functionality. - :tags: embark fort interface + :tags: untested embark fort interface Usage ----- diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst index fb0524c3b..217c098ef 100644 --- a/docs/plugins/fastdwarf.rst +++ b/docs/plugins/fastdwarf.rst @@ -3,7 +3,7 @@ fastdwarf .. dfhack-tool:: :summary: Dwarves teleport and/or finish jobs instantly. - :tags: fort armok units + :tags: untested fort armok units Usage ----- diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index beb57dccd..f92549dee 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -6,7 +6,7 @@ filltraffic .. dfhack-tool:: :summary: Set traffic designations using flood-fill starting at the cursor. - :tags: fort design productivity map + :tags: untested fort design productivity map .. dfhack-command:: alltraffic :summary: Set traffic designations for every single tile of the map. diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index 0559d4a2f..8589f765a 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -3,7 +3,7 @@ fix-unit-occupancy .. dfhack-tool:: :summary: Fix phantom unit occupancy issues. - :tags: fort bugfix map + :tags: untested fort bugfix map If you see "unit blocking tile" messages that you can't account for (:bug:`3499`), this tool can help. diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst index 616f84fae..c13bbe9a1 100644 --- a/docs/plugins/fixveins.rst +++ b/docs/plugins/fixveins.rst @@ -3,7 +3,7 @@ fixveins .. dfhack-tool:: :summary: Restore missing mineral inclusions. - :tags: fort bugfix map + :tags: untested fort bugfix map This tool can also remove invalid references to mineral inclusions if you broke your embark with tools like `tiletypes`. diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst index bbb2c6661..209bb577c 100644 --- a/docs/plugins/flows.rst +++ b/docs/plugins/flows.rst @@ -3,7 +3,7 @@ flows .. dfhack-tool:: :summary: Counts map blocks with flowing liquids. - :tags: fort inspection map + :tags: untested fort inspection map If you suspect that your magma sea leaks into HFS, you can use this tool to be sure without revealing the map. diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst index 0bf1e4d0f..47db40bd8 100644 --- a/docs/plugins/follow.rst +++ b/docs/plugins/follow.rst @@ -3,7 +3,7 @@ follow .. dfhack-tool:: :summary: Make the screen follow the selected unit. - :tags: fort interface units + :tags: untested fort interface units Once you exit from the current menu or cursor mode, the screen will stay centered on the unit. Handy for watching dwarves running around. Deactivated by diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index 26ab378ee..d4c9a6c4a 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -3,7 +3,7 @@ forceequip .. dfhack-tool:: :summary: Move items into a unit's inventory. - :tags: adventure fort animals items military units + :tags: untested adventure fort animals items military units This tool is typically used to equip specific clothing/armor items onto a dwarf, but can also be used to put armor onto a war animal or to add unusual items diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst index 68302dbf5..ba1761a1b 100644 --- a/docs/plugins/generated-creature-renamer.rst +++ b/docs/plugins/generated-creature-renamer.rst @@ -3,7 +3,7 @@ generated-creature-renamer .. dfhack-tool:: :summary: Automatically renames generated creatures. - :tags: adventure fort legends units + :tags: untested adventure fort legends units :no-command: .. dfhack-command:: list-generated diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 05bac71c9..756fa1aaa 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -3,7 +3,7 @@ getplants .. dfhack-tool:: :summary: Designate trees for chopping and shrubs for gathering. - :tags: fort productivity plants + :tags: untested fort productivity plants Specify the types of trees to cut down and/or shrubs to gather by their plant names. diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index c15f87e26..7e8ec2f31 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -3,7 +3,7 @@ infiniteSky .. dfhack-tool:: :summary: Automatically allocate new z-levels of sky - :tags: fort design map + :tags: untested fort auto design map If enabled, this plugin will automatically allocate new z-levels of sky at the top of the map as you build up. Or it can allocate one or many additional levels diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst index 284794f96..2442f70ef 100644 --- a/docs/plugins/isoworldremote.rst +++ b/docs/plugins/isoworldremote.rst @@ -3,7 +3,7 @@ isoworldremote .. dfhack-tool:: :summary: Provides a remote API used by Isoworld. - :tags: dev graphics + :tags: untested dev graphics :no-command: See `remote` for related remote APIs. diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index 9d083d92f..f68c200b2 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -5,7 +5,7 @@ jobutils .. dfhack-tool:: :summary: Provides commands for interacting with jobs. - :tags: fort inspection jobs + :tags: untested fort inspection jobs :no-command: .. dfhack-command:: job diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst index e3443280d..f808fcb59 100644 --- a/docs/plugins/labormanager.rst +++ b/docs/plugins/labormanager.rst @@ -3,7 +3,7 @@ labormanager .. dfhack-tool:: :summary: Automatically manage dwarf labors. - :tags: fort auto labors + :tags: untested fort auto labors Labormanager is derived from `autolabor` but uses a completely different approach to assigning jobs to dwarves. While autolabor tries to keep as many diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index 9bded57ab..77ad0a045 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -3,7 +3,7 @@ lair .. dfhack-tool:: :summary: Mark the map as a monster lair. - :tags: fort armok map + :tags: untested fort armok map This avoids item scatter when the fortress is abandoned. diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst index 5bc24d04f..febdcce8d 100644 --- a/docs/plugins/liquids.rst +++ b/docs/plugins/liquids.rst @@ -5,7 +5,7 @@ liquids .. dfhack-tool:: :summary: Place magma, water or obsidian. - :tags: adventure fort armok map + :tags: untested adventure fort armok map .. dfhack-command:: liquids-here :summary: Spawn liquids on the selected tile. diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst index 4b5b18540..69f566e24 100644 --- a/docs/plugins/luasocket.rst +++ b/docs/plugins/luasocket.rst @@ -3,7 +3,7 @@ luasocket .. dfhack-tool:: :summary: Provides a Lua API for accessing network sockets. - :tags: dev + :tags: untested dev :no-command: See `luasocket-api` for details. diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index f333f3be0..b4c0d6160 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -3,7 +3,7 @@ manipulator .. dfhack-tool:: :summary: An in-game labor management interface. - :tags: fort productivity labors + :tags: untested fort productivity labors :no-command: It is equivalent to the popular Dwarf Therapist utility. diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst index 935fc1010..bbd01aa97 100644 --- a/docs/plugins/map-render.rst +++ b/docs/plugins/map-render.rst @@ -3,7 +3,7 @@ map-render .. dfhack-tool:: :summary: Provides a Lua API for re-rendering portions of the map. - :tags: dev graphics + :tags: untested dev graphics :no-command: See `map-render-api` for details. diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index b3634d2ac..38c2b23b4 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -3,7 +3,7 @@ misery .. dfhack-tool:: :summary: Increase the intensity of negative dwarven thoughts. - :tags: fort armok units + :tags: untested fort armok auto units When enabled, negative thoughts that your dwarves have will multiply by the specified factor. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index 33173a5d3..42c7f6fcc 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -3,7 +3,7 @@ mode .. dfhack-tool:: :summary: See and change the game mode. - :tags: armok dev gameplay + :tags: untested armok dev gameplay .. warning:: diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst index 12f4092bd..0e49d3924 100644 --- a/docs/plugins/mousequery.rst +++ b/docs/plugins/mousequery.rst @@ -3,7 +3,7 @@ mousequery .. dfhack-tool:: :summary: Adds mouse controls to the DF interface. - :tags: fort productivity interface + :tags: untested fort productivity interface Adds mouse controls to the DF interface. For example, with ``mousequery`` you can click on buildings to configure them, hold the mouse button to draw dig diff --git a/docs/plugins/nestboxes.rst b/docs/plugins/nestboxes.rst index 3c07cc30c..91d83aa1a 100644 --- a/docs/plugins/nestboxes.rst +++ b/docs/plugins/nestboxes.rst @@ -3,7 +3,7 @@ nestboxes .. dfhack-tool:: :summary: Protect fertile eggs incubating in a nestbox. - :tags: fort auto animals + :tags: untested fort auto animals :no-command: This plugin will automatically scan for and forbid fertile eggs incubating in a diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index 62959e731..798e39f32 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -3,7 +3,7 @@ petcapRemover .. dfhack-tool:: :summary: Modify the pet population cap. - :tags: fort animals + :tags: untested fort auto animals In vanilla DF, pets will not reproduce unless the population is below 50 and the number of children of that species is below a certain percentage. This plugin diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst index f0a4f4af5..cdcc0daac 100644 --- a/docs/plugins/plants.rst +++ b/docs/plugins/plants.rst @@ -5,7 +5,7 @@ plants .. dfhack-tool:: :summary: Provides commands that interact with plants. - :tags: adventure fort armok map plants + :tags: untested adventure fort armok map plants :no-command: .. dfhack-command:: plant diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 701a656b5..8ffc79e3e 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -3,7 +3,7 @@ power-meter .. dfhack-tool:: :summary: Allow pressure plates to measure power. - :tags: fort gameplay buildings + :tags: untested fort gameplay buildings :no-command: If you run `gui/power-meter` while building a pressure plate, the pressure diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 4628d11c8..3fb47cc57 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -5,7 +5,7 @@ prospector .. dfhack-tool:: :summary: Provides commands that help you analyze natural resources. - :tags: embark fort armok inspection map + :tags: untested embark fort armok inspection map :no-command: .. dfhack-command:: prospect diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst index 137cf56a4..3c6ed4a77 100644 --- a/docs/plugins/rename.rst +++ b/docs/plugins/rename.rst @@ -3,7 +3,7 @@ rename .. dfhack-tool:: :summary: Easily rename things. - :tags: adventure fort productivity buildings stockpiles units + :tags: untested adventure fort productivity buildings stockpiles units Use `gui/rename` for an in-game interface. diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index ee909fd9a..370771d6b 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -3,7 +3,7 @@ rendermax .. dfhack-tool:: :summary: Modify the map lighting. - :tags: adventure fort gameplay graphics + :tags: untested adventure fort gameplay graphics This plugin provides a collection of OpenGL lighting filters that affect how the map is drawn to the screen. diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index e588e3196..8e1a93143 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -5,7 +5,7 @@ search .. dfhack-tool:: :summary: Adds search capabilities to the UI. - :tags: fort productivity interface + :tags: untested fort productivity interface :no-command: Search options are added to the Stocks, Animals, Trading, Stockpile, Noble diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst index d01fc524c..fa18dcd48 100644 --- a/docs/plugins/siege-engine.rst +++ b/docs/plugins/siege-engine.rst @@ -3,7 +3,7 @@ siege-engine .. dfhack-tool:: :summary: Extend the functionality and usability of siege engines. - :tags: fort gameplay buildings + :tags: untested fort gameplay buildings :no-command: Siege engines in DF haven't been updated since the game was 2D, and can only aim diff --git a/docs/plugins/sort.rst b/docs/plugins/sort.rst index f5bce9bae..3a8c7c0f5 100644 --- a/docs/plugins/sort.rst +++ b/docs/plugins/sort.rst @@ -3,7 +3,7 @@ sort .. dfhack-tool:: :summary: Sort lists shown in the DF interface. - :tags: fort productivity interface + :tags: untested fort productivity interface :no-command: .. dfhack-command:: sort-items diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index f54d68142..448369cd8 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -3,7 +3,7 @@ spectate .. dfhack-tool:: :summary: Automatically follow productive dwarves. - :tags: fort interface + :tags: untested fort interface Usage ----- diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst index 6560ae714..11d0145f3 100644 --- a/docs/plugins/steam-engine.rst +++ b/docs/plugins/steam-engine.rst @@ -3,7 +3,7 @@ steam-engine .. dfhack-tool:: :summary: Allow modded steam engine buildings to function. - :tags: fort gameplay buildings + :tags: untested fort gameplay buildings :no-command: The steam-engine plugin detects custom workshops with the string diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst index 9ce91eaca..f501fc904 100644 --- a/docs/plugins/stockflow.rst +++ b/docs/plugins/stockflow.rst @@ -3,7 +3,7 @@ stockflow .. dfhack-tool:: :summary: Queue manager jobs based on free space in stockpiles. - :tags: fort auto stockpiles workorders + :tags: untested fort auto stockpiles workorders With this plugin, the fortress bookkeeper can tally up free space in specific stockpiles and queue jobs through the manager to produce items to fill the free diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index e7f02f970..bfc378ed0 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -5,7 +5,7 @@ stockpiles .. dfhack-tool:: :summary: Import and export stockpile settings. - :tags: fort design productivity stockpiles + :tags: untested fort design productivity stockpiles :no-command: .. dfhack-command:: copystock diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index 03eb3a72c..c924b748c 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -3,7 +3,7 @@ stocks .. dfhack-tool:: :summary: Enhanced fortress stock management interface. - :tags: fort productivity items + :tags: untested fort productivity items When the plugin is enabled, two new hotkeys become available: diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index 7aabda93d..a5b2b6e1f 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -3,7 +3,7 @@ strangemood .. dfhack-tool:: :summary: Trigger a strange mood. - :tags: fort armok units + :tags: untested fort armok units Usage ----- diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index af7d253b2..d578fd308 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -3,7 +3,7 @@ tailor .. dfhack-tool:: :summary: Automatically keep your dwarves in fresh clothing. - :tags: fort auto workorders + :tags: untested fort auto workorders Whenever the bookkeeper updates stockpile records, this plugin will scan the fort. If there are fresh cloths available, dwarves who are wearing tattered diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index 7b93dd6de..09a4da583 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -6,7 +6,7 @@ tiletypes .. dfhack-tool:: :summary: Paints tiles of specified types onto the map. - :tags: adventure fort armok map + :tags: untested adventure fort armok map .. dfhack-command:: tiletypes-command :summary: Run tiletypes commands. diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst index a6c7ee63c..32818071b 100644 --- a/docs/plugins/title-folder.rst +++ b/docs/plugins/title-folder.rst @@ -3,7 +3,7 @@ title-folder .. dfhack-tool:: :summary: Displays the DF folder name in the window title bar. - :tags: interface + :tags: untested interface :no-command: Usage diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst index 307a4da18..18c7c3ba8 100644 --- a/docs/plugins/title-version.rst +++ b/docs/plugins/title-version.rst @@ -3,7 +3,7 @@ title-version .. dfhack-tool:: :summary: Displays the DFHack version on DF's title screen. - :tags: interface + :tags: untested interface :no-command: Usage diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst index 3ca47f465..6ae23dcd0 100644 --- a/docs/plugins/trackstop.rst +++ b/docs/plugins/trackstop.rst @@ -3,7 +3,7 @@ trackstop .. dfhack-tool:: :summary: Add dynamic configuration options for track stops. - :tags: fort gameplay buildings + :tags: untested fort gameplay buildings :no-command: When enabled, this plugin adds a :kbd:`q` menu for track stops, which is diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst index 80282f6d9..eb568ef03 100644 --- a/docs/plugins/tubefill.rst +++ b/docs/plugins/tubefill.rst @@ -3,7 +3,7 @@ tubefill .. dfhack-tool:: :summary: Replenishes mined-out adamantine. - :tags: fort armok map + :tags: untested fort armok map Veins that were originally hollow will be left alone. diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index aa1ad578b..dc5ed19a8 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -3,7 +3,7 @@ tweak .. dfhack-tool:: :summary: A collection of tweaks and bugfixes. - :tags: adventure fort armok bugfix fps interface + :tags: untested adventure fort armok bugfix fps interface Usage ----- diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst index 55004bca2..66bc822ae 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/workNow.rst @@ -3,7 +3,7 @@ workNow .. dfhack-tool:: :summary: Reduce the time that dwarves idle after completing a job. - :tags: fort auto labors + :tags: untested fort auto labors After finishing a job, dwarves will wander away for a while before picking up a new job. This plugin will automatically poke the game to assign dwarves to new diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index 822b525f9..72b7a8e2b 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -3,7 +3,7 @@ workflow .. dfhack-tool:: :summary: Manage automated item production rules. - :tags: fort auto jobs + :tags: untested fort auto jobs Manage repeat jobs according to stock levels. `gui/workflow` provides a simple front-end integrated in the game UI. diff --git a/docs/plugins/xlsxreader.rst b/docs/plugins/xlsxreader.rst index 3d6cc0ac8..fe86a072d 100644 --- a/docs/plugins/xlsxreader.rst +++ b/docs/plugins/xlsxreader.rst @@ -3,7 +3,7 @@ xlsxreader .. dfhack-tool:: :summary: Provides a Lua API for reading xlsx files. - :tags: dev + :tags: untested dev :no-command: See `xlsxreader-api` for details. diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index 0f77e1d3e..9d9991dde 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -3,7 +3,7 @@ zone .. dfhack-tool:: :summary: Manage activity zones, cages, and the animals therein. - :tags: fort productivity animals buildings + :tags: untested fort productivity animals buildings Usage ----- From 1725a514009dcbe4213b2dc1a6ca402754d9fc97 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 11 Jan 2023 07:14:45 +0000 Subject: [PATCH 0108/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 9e70e2d6e..301f12e37 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9e70e2d6eb35ac6b6646bacdde7176b1afa28e52 +Subproject commit 301f12e372cf2da00c760e698395708d22343c47 From 679e91d2e7efb09d29c9e8dbcbccf3c253d2c225 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 10 Jan 2023 23:21:30 -0800 Subject: [PATCH 0109/2222] warn against running untested scripts but allow after the first try --- library/lua/dfhack.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 4a46040af..3886dcbc5 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -695,7 +695,14 @@ local valid_script_flags = { scripts = {required = false}, } +local warned_scripts = {} function dfhack.run_script(name,...) + if not warned_scripts[name] and require('helpdb').get_entry_tags(name).untested then + warned_scripts[name] = true + dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) + dfhack.printerr('It may not work as expected, or it may corrupt your game.') + qerror('Please run the command again to ignore this warning and proceed.') + end return dfhack.run_script_with_env(nil, name, nil, ...) end From 88f158921f8c6f42e16e3d3dc83f65b0470043c8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 11 Jan 2023 19:48:15 -0800 Subject: [PATCH 0110/2222] move warning to run_script_with_env --- library/lua/dfhack.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 3886dcbc5..7c7994f26 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -695,14 +695,7 @@ local valid_script_flags = { scripts = {required = false}, } -local warned_scripts = {} function dfhack.run_script(name,...) - if not warned_scripts[name] and require('helpdb').get_entry_tags(name).untested then - warned_scripts[name] = true - dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) - dfhack.printerr('It may not work as expected, or it may corrupt your game.') - qerror('Please run the command again to ignore this warning and proceed.') - end return dfhack.run_script_with_env(nil, name, nil, ...) end @@ -736,7 +729,16 @@ function dfhack.script_environment(name, strict) end end +local warned_scripts = {} + function dfhack.run_script_with_env(envVars, name, flags, ...) + if not warned_scripts[name] and require('helpdb').get_entry_tags(name).untested then + warned_scripts[name] = true + dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) + dfhack.printerr('It may not work as expected, or it may corrupt your game.') + qerror('Please run the command again to ignore this warning and proceed.') + end + if type(flags) ~= 'table' then flags = {} end local file = dfhack.findScript(name) if not file then From 6e4a994364957ce5477b5f12292c839eecd2a9c6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 11 Jan 2023 20:02:13 -0800 Subject: [PATCH 0111/2222] "1 trees" to "1 tree(s)" --- plugins/autochop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 59dc32a60..0c20c0cb4 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -201,7 +201,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) { if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { int32_t designated = do_cycle(out); if (0 < designated) - out.print("autochop: designated %d trees for chopping\n", designated); + out.print("autochop: designated %d tree(s) for chopping\n", designated); } return CR_OK; } From 3ed65aedbf8b2647c3af99a9c6ea8761ecabe673 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 12 Jan 2023 05:26:56 +0000 Subject: [PATCH 0112/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 301f12e37..7b79aa13b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 301f12e372cf2da00c760e698395708d22343c47 +Subproject commit 7b79aa13bda3013bb4c76f2894c5971ec2ffbd1b From 079d5e4178031287c5ad248d761b0640de12e938 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 11 Jan 2023 18:23:16 -0800 Subject: [PATCH 0113/2222] support foreground and background writing from pens --- docs/changelog.txt | 4 ++++ docs/dev/Lua API.rst | 18 +++++++++++++++--- library/LuaApi.cpp | 14 ++++++++++++++ library/include/modules/Screen.h | 3 +++ library/lua/gui.lua | 3 ++- library/lua/gui/widgets.lua | 4 +++- library/modules/Screen.cpp | 13 +++++++++---- 7 files changed, 50 insertions(+), 9 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d0c7b1abb..08aa39951 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::getDwarfmodeDims``: now only returns map viewport dimensions; menu dimensions are obsolete - ``Gui::getDFViewscreen``: returns the topmost underlying DF viewscreen +- ``Screen::Pen``: now accepts ``keep_lower`` and ``write_to_lower`` properties to support foreground and background textures in graphics mode ## Lua - Removed ``os.execute()`` and ``io.popen()`` built-in functions @@ -58,10 +59,13 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.CycleHotkeyLabel``: now supports rendering option labels in the color of your choice - ``widgets.CycleHotkeyLabel``: new functions ``setOption()`` and ``getOptionPen()`` - ``widgets.ToggleHotkeyLabel``: now renders the ``On`` option in green text +- ``widgets.Label``: tiles can now have an associated width - `overlay`: ``OverlayWidget`` now inherits from ``Panel`` instead of ``Widget`` to get all the frame and mouse integration goodies - ``dfhack.gui.getDFViewscreen()``: returns the topmost underlying DF viewscreen - ``gui.ZScreen``: Screen subclass that implements window raising, multi-viewscreen input handling, and viewscreen event pass-through so the underlying map can be interacted with and dragged around while DFHack screens are visible - ``gui.View``: new function ``view:getMouseFramePos()`` for detecting whether the mouse is within (or over) the exterior frame of a view +- ``gui.CLEAR_PEN``: now clears the background and foreground and writes to the background (before it would always write to the foreground) +- ``gui.KEEP_LOWER_PEN``: a general use pen that writes associated tiles to the foreground while keeping the existing background ## Internals diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 90ccdb851..f73af1472 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2266,6 +2266,12 @@ a table with the following possible fields: Specifies that the tile should be shaded with *fg/bg*. ``tile_fg, tile_bg`` If specified, overrides *tile_color* and supplies shading colors directly. + ``keep_lower`` + If set to true, will not overwrite the background tile when filling in + the foreground tile. + ``write_to_lower`` + If set to true, the specified ``tile`` will be written to the background + instead of the foreground. Alternatively, it may be a pre-parsed native object with the following API: @@ -3654,7 +3660,13 @@ Misc * ``CLEAR_PEN`` - The black pen used to clear the screen. + The black pen used to clear the screen. In graphics mode, it will clear the + foreground and set the background to the standard black tile. + +* ``KEEP_LOWER_PEN`` + + A pen that will write tiles over existing background tiles instead of clearing + them. * ``simulateInput(screen, keys...)`` @@ -4582,8 +4594,8 @@ containing newlines, or a table with the following possible fields: * ``token.width = ...`` - If specified either as a value or a callback, the text field is padded - or truncated to the specified number. + If specified either as a value or a callback, the text (or tile) field is + padded or truncated to the specified number. * ``token.pad_char = '?'`` diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index c327efe1b..90815708d 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -177,6 +177,17 @@ static bool get_char_field(lua_State *L, char *pf, int idx, const char *name, ch } } +static bool get_bool_field(lua_State *L, bool *pf, int idx, const char *name, bool defval) { + lua_getfield(L, idx, name); + bool nil = lua_isnil(L, -1); + if (nil) + *pf = defval; + else + *pf = lua_toboolean(L, -1); + lua_pop(L, 1); + return !nil; +} + static void decode_pen(lua_State *L, Pen &pen, int idx) { idx = lua_absindex(L, idx); @@ -208,6 +219,9 @@ static void decode_pen(lua_State *L, Pen &pen, int idx) pen.tile_mode = (lua_toboolean(L, -1) ? Pen::CharColor : Pen::AsIs); lua_pop(L, 1); } + + get_bool_field(L, &pen.keep_lower, idx, "keep_lower", false); + get_bool_field(L, &pen.write_to_lower, idx, "write_to_lower", false); } /************************************************** diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index bc8a406a0..9417d60e9 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -84,6 +84,9 @@ namespace DFHack } tile_mode; int8_t tile_fg, tile_bg; + bool write_to_lower = false; + bool keep_lower = false; + bool valid() const { return tile >= 0; } bool empty() const { return ch == 0 && tile == 0; } diff --git a/library/lua/gui.lua b/library/lua/gui.lua index ade8d0ceb..50b5fed3a 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -9,7 +9,8 @@ local getval = utils.getval local to_pen = dfhack.pen.parse -CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0} +CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0, write_to_lower=true} +KEEP_LOWER_PEN = to_pen{ch=32, fg=0, bg=0, keep_lower=true} local FAKE_INPUT_KEYS = { _MOUSE_L = true, diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 4a39efa8e..52c89b63b 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -688,7 +688,6 @@ function EditField:onRenderBody(dc) end_pos == #txt and '' or string.char(26)) end dc:advance(self.text_offset):string(txt) - dc:string((' '):rep(dc.clip_x2 - dc.x)) end function EditField:insert(text) @@ -1102,6 +1101,9 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled) x = x + 1 if dc then dc:tile(nil, token.tile) + if token.width then + dc:advance(token.width-1) + end end end diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index ae0a15b5e..a05db1da1 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -157,7 +157,8 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) *screen = 0; *texpos = 0; - *texpos_lower = 0; + if (!pen.keep_lower) + *texpos_lower = 0; gps->screentexpos_anchored[index] = 0; // keep SCREENTEXPOS_FLAG_ANCHOR_SUBORDINATE so occluded anchored textures // don't appear corrupted @@ -171,7 +172,8 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) *screen = 0; *texpos = 0; - *texpos_lower = 0; + if (!pen.keep_lower) + *texpos_lower = 0; gps->screentexpos_top_anchored[index] = 0; *flag &= 4; // keep SCREENTEXPOS_FLAG_ANCHOR_SUBORDINATE } @@ -190,10 +192,13 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) } if (pen.tile && use_graphics) { - *texpos = pen.tile; + if (pen.write_to_lower) + *texpos_lower = pen.tile; + else + *texpos = pen.tile; } else { screen[0] = uint8_t(pen.ch); - *texpos_lower = 909; + *texpos_lower = 909; // basic black background } auto rgb_fg = &gps->uccolor[fg][0]; From 15998f2ebeb915e6399680c5870d2d38b63707dc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 12:44:50 -0800 Subject: [PATCH 0114/2222] add gui.TRANSPARENT_PEN for clearing the UI layer --- docs/dev/Lua API.rst | 4 ++++ library/lua/gui.lua | 1 + library/modules/Screen.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index f73af1472..56d5eac5f 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -3663,6 +3663,10 @@ Misc The black pen used to clear the screen. In graphics mode, it will clear the foreground and set the background to the standard black tile. +* ``TRANSPARENT_PEN`` + + A pen that will clear all textures from the UI layer, making the tile transparent. + * ``KEEP_LOWER_PEN`` A pen that will write tiles over existing background tiles instead of clearing diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 50b5fed3a..8a2a406aa 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -10,6 +10,7 @@ local getval = utils.getval local to_pen = dfhack.pen.parse CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0, write_to_lower=true} +TRANSPARENT_PEN = to_pen{tile=0, ch=0} KEEP_LOWER_PEN = to_pen{ch=32, fg=0, bg=0, keep_lower=true} local FAKE_INPUT_KEYS = { diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index a05db1da1..3ece27b78 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -128,10 +128,10 @@ static bool doSetTile_map(const Pen &pen, int x, int y) { return false; long texpos = pen.tile; - if (texpos == 0) { + if (!texpos && pen.ch) texpos = init->font.large_font_texpos[(uint8_t)pen.ch]; - } - vp->screentexpos_interface[index] = texpos; + if (texpos) + vp->screentexpos_interface[index] = texpos; return true; } @@ -196,7 +196,7 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) *texpos_lower = pen.tile; else *texpos = pen.tile; - } else { + } else if (pen.ch) { screen[0] = uint8_t(pen.ch); *texpos_lower = 909; // basic black background } From 52a39438f758c4dc70d446e9cb5945441c85f66d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 12:47:43 -0800 Subject: [PATCH 0115/2222] don't make EditField's transparent this is actually a very old bug, but now that I know how transparency works, it's obvious how to fix this --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 52c89b63b..5414fefaa 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -659,7 +659,7 @@ function EditField:postUpdateLayout() end function EditField:onRenderBody(dc) - dc:pen(self.text_pen or COLOR_LIGHTCYAN):fill(0,0,dc.width-1,0) + dc:pen(self.text_pen or COLOR_LIGHTCYAN) local cursor_char = '_' if not getval(self.active) or not self.focus or gui.blink_visible(300) then From 49cd82a9b821f7ed274cd4a0f1b54bbab63a1d20 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 13:48:58 -0800 Subject: [PATCH 0116/2222] enable a batch of compilable plugins for testing --- plugins/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6e02c644c..049c05f89 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -91,8 +91,8 @@ dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) #add_subdirectory(buildingplan) #dfhack_plugin(changeitem changeitem.cpp) -#dfhack_plugin(changelayer changelayer.cpp) -#dfhack_plugin(changevein changevein.cpp) +dfhack_plugin(changelayer changelayer.cpp) +dfhack_plugin(changevein changevein.cpp) #add_subdirectory(channel-safely) #dfhack_plugin(cleanconst cleanconst.cpp) #dfhack_plugin(cleaners cleaners.cpp) @@ -104,7 +104,7 @@ dfhack_plugin(cleanowned cleanowned.cpp) #dfhack_plugin(deramp deramp.cpp) dfhack_plugin(debug debug.cpp LINK_LIBRARIES jsoncpp_static) #dfhack_plugin(dig dig.cpp) -#dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) +dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) #dfhack_plugin(digFlood digFlood.cpp) #add_subdirectory(diggingInvaders) #dfhack_plugin(dwarfvet dwarfvet.cpp) @@ -126,7 +126,7 @@ dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) #dfhack_plugin(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) #dfhack_plugin(jobutils jobutils.cpp) #dfhack_plugin(lair lair.cpp) -#dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) +dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) #dfhack_plugin(manipulator manipulator.cpp) #dfhack_plugin(map-render map-render.cpp LINK_LIBRARIES lua) @@ -159,7 +159,7 @@ dfhack_plugin(seedwatch seedwatch.cpp) #dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(strangemood strangemood.cpp) #dfhack_plugin(tailor tailor.cpp) -#dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) +dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-version title-version.cpp) #dfhack_plugin(trackstop trackstop.cpp) From c98e389ac8b1ca3317b555dcd966712a06395dbc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 14:15:43 -0800 Subject: [PATCH 0117/2222] move untested warning back to run_script so we don't waste our warning on the initial env scans --- library/lua/dfhack.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 7c7994f26..26794eedf 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -695,7 +695,16 @@ local valid_script_flags = { scripts = {required = false}, } +local warned_scripts = {} + function dfhack.run_script(name,...) + if not warned_scripts[name] and require('helpdb').get_entry_tags(name).untested then + warned_scripts[name] = true + dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) + dfhack.printerr('It may not work as expected, or it may corrupt your game.') + qerror('Please run the command again to ignore this warning and proceed.') + end + return dfhack.run_script_with_env(nil, name, nil, ...) end @@ -729,16 +738,7 @@ function dfhack.script_environment(name, strict) end end -local warned_scripts = {} - function dfhack.run_script_with_env(envVars, name, flags, ...) - if not warned_scripts[name] and require('helpdb').get_entry_tags(name).untested then - warned_scripts[name] = true - dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) - dfhack.printerr('It may not work as expected, or it may corrupt your game.') - qerror('Please run the command again to ignore this warning and proceed.') - end - if type(flags) ~= 'table' then flags = {} end local file = dfhack.findScript(name) if not file then From 9910c2610e389a4063d79ac64383695aa23c419d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 14:43:46 -0800 Subject: [PATCH 0118/2222] clarify the identity of the icons --- plugins/pathable.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index 7ed7fffd0..b66099a1f 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -36,6 +36,9 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { int selected_tile_texpos = 0; Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos); + long pathable_tile_texpos = 779; + long unpathable_tile_texpos = 782; + auto dims = Gui::getDwarfmodeViewDims().map(); for (int y = dims.first.y; y <= dims.second.y; ++y) { for (int x = dims.first.x; x <= dims.second.x; ++x) { @@ -75,7 +78,8 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { if (map_pos == target) { cur_tile.tile = selected_tile_texpos; } else{ - cur_tile.tile = can_walk ? 779 : 782; + cur_tile.tile = can_walk ? + pathable_tile_texpos : unpathable_tile_texpos; } } else { int color = can_walk ? COLOR_GREEN : COLOR_RED; From ecbbfb635c6bffe0d2f02abddc2482df02e7ff3e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 17:14:03 -0800 Subject: [PATCH 0119/2222] mark tools as tested --- docs/plugins/changelayer.rst | 2 +- docs/plugins/changevein.rst | 2 +- docs/plugins/dig-now.rst | 2 +- docs/plugins/liquids.rst | 2 +- docs/plugins/tiletypes.rst | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/plugins/changelayer.rst b/docs/plugins/changelayer.rst index 0f5421ee9..f986a1e3d 100644 --- a/docs/plugins/changelayer.rst +++ b/docs/plugins/changelayer.rst @@ -3,7 +3,7 @@ changelayer .. dfhack-tool:: :summary: Change the material of an entire geology layer. - :tags: untested fort armok map + :tags: fort armok map Note that one layer can stretch across many z-levels, and changes to the geology layer will affect all surrounding regions, not just your embark! Mineral veins diff --git a/docs/plugins/changevein.rst b/docs/plugins/changevein.rst index c1485de5b..4e4512bb1 100644 --- a/docs/plugins/changevein.rst +++ b/docs/plugins/changevein.rst @@ -3,7 +3,7 @@ changevein .. dfhack-tool:: :summary: Change the material of a mineral inclusion. - :tags: untested fort armok map + :tags: fort armok map You can change a vein to any inorganic material RAW id. Note that this command only affects tiles within the current 16x16 block - for large veins and diff --git a/docs/plugins/dig-now.rst b/docs/plugins/dig-now.rst index f4a5d5d28..43ed6ce8f 100644 --- a/docs/plugins/dig-now.rst +++ b/docs/plugins/dig-now.rst @@ -3,7 +3,7 @@ dig-now .. dfhack-tool:: :summary: Instantly complete dig designations. - :tags: untested fort armok map + :tags: fort armok map This tool will magically complete non-marker dig designations, modifying tile shapes and creating boulders, ores, and gems as if a miner were doing the mining diff --git a/docs/plugins/liquids.rst b/docs/plugins/liquids.rst index febdcce8d..5bc24d04f 100644 --- a/docs/plugins/liquids.rst +++ b/docs/plugins/liquids.rst @@ -5,7 +5,7 @@ liquids .. dfhack-tool:: :summary: Place magma, water or obsidian. - :tags: untested adventure fort armok map + :tags: adventure fort armok map .. dfhack-command:: liquids-here :summary: Spawn liquids on the selected tile. diff --git a/docs/plugins/tiletypes.rst b/docs/plugins/tiletypes.rst index 09a4da583..7b93dd6de 100644 --- a/docs/plugins/tiletypes.rst +++ b/docs/plugins/tiletypes.rst @@ -6,7 +6,7 @@ tiletypes .. dfhack-tool:: :summary: Paints tiles of specified types onto the map. - :tags: untested adventure fort armok map + :tags: adventure fort armok map .. dfhack-command:: tiletypes-command :summary: Run tiletypes commands. From ac06508d92ec9bc7f52ceb819894d46c3107f42b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 14:42:40 -0800 Subject: [PATCH 0120/2222] add some more icon textures we can use contributed by TaxiService on the DFHack Discord server --- data/art/icons.png | Bin 0 -> 2026 bytes library/include/modules/Textures.h | 8 +++++++- library/modules/Textures.cpp | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 data/art/icons.png diff --git a/data/art/icons.png b/data/art/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..141d9f79f4004fc1417346adbcfae25aee34df50 GIT binary patch literal 2026 zcmbVNdsI?+9w&3R&2-$v)UqWcn`5m47X-YdF$!25(+rt3bw?2{LJF7QDwNesQ$BOY znlhSfa_W@nnmUe|qmDDaQkycf(iRPE8y|@nz3ipvUNYPLqceNX-gAHV_xgT5-}kxM z;b9vc>{i%eFc=5kCJrB+z0l$ICmZy;Xv9m0PRkK)6cPbT5RFg;VFJZ)0)*$ug^3U! z5{h^2ybAeaFbgFzK@<`dx|u10R73;=jKoe22S=ybpvPay*o5*dBzK7cQiMrTqO z_;(Kh)us|ln0!v~J6-55fFMN>C6h$bXf#9(l?ba6NdO3fRt^fq2bJ(q?@}N_t&c*z zdY*v;sYNQ85|P0Qyp>Uy0H+`U1k}>xjo!sP%!5D3r$slIfO=1U3mqjBdsK7^(Cv4cPgn~V+`0B|`V znZssNC^Rme1_BHQdmhVEs1cz;1kLM}p?c@ALBYR^WwKR}5P?+!7*3wAfN&{{z-lS1 z#Iqylc&|vALJVuvYpv;-^OggtWT}uiSOv@RZ{y39eLz2h_P_j|!;1f(n2}IpNY>)` zuVR@$LMza^d|&$L=6&-(3bbQXXln>vb14{%?Ft?zP@wHQc6^I8mbfse&DBVQZRPdn#uTZ7V#kUEU>aIIu*miBS{6Q^Rr6_?nY zCKk~MowtHJ?en{B*O3xbxn@HkX z;`zE9Gb2BIYT=YJ zpN{;Xi%i+mS%2Ympo!ddvcY@Fl}(K1R@<7tR6SqSRT#OaV#waSXR71*dhBZlS!!zU zEVumP#aHgCy?YH6%S=ZW2=ilD`>H0#V@^F&)QnHf>Ovk8P42}=d+yB8eA`zH^%*;!k{=%Q(FtNSb23PPM&-^NLzXn+!>QlvoEcl z5A44nTX#@vc@Qx=_zK&yCGKP?IU*@2+cWIK?9b1@G^du#ihZr?+A0p_M$Gi+sqGtP z-gJ~ygLh)ewpC3Sjm8eof@4F~M~91!*(@`8@*XZMfr_a9mfZ0#N^abj^3GGsfCJZX zVcv~wQk>?|4pchIhxgib8~8_tQ2By-EU%FUQl#~_XTCC=elfhu}vAf z4cFC_{rZz7-=v>y*EYV&8Whztp&oa)%BzddeA2cv_oL$Y+p(vggl7cyg@_ja!|o8x z!*>Z*k3BL3{s=pZeE8IvQ#Wz0m>V87x5v)adQ5Q3ekE^hc71tRd@lM~Lb<)|P|?e? zdtDZ;tUBk{-g>#TzWu7DyV_+mSnJg$aco?G>&DiVetJEsa?*Y>_rhPBKRQGhI&^hm z$p-HgOT+*CXiaj{cVEX&x!lRxc(~Li1KDxj=fp-}hh6@at&2A=%Nc`z{oDdGr*n!g zHEko>3oMQ~+sCtHrYU^izE(4J2yA}zvG;APMX#=jAHQclChjx1$DQ*0eCkZ1;I4Dk zBX^^rZTr*0AM1J`Ag8u#vtOq<&oy%7)wORw+ddm=@qP5|EymRT-4oBdv!`e6$QV1* WSykTcj~`qAzC3Oir#vYB%YOsl(pV1w literal 0 HcmV?d00001 diff --git a/library/include/modules/Textures.h b/library/include/modules/Textures.h index e088ce477..e3e5a8ec0 100644 --- a/library/include/modules/Textures.h +++ b/library/include/modules/Textures.h @@ -31,9 +31,15 @@ void cleanup(); DFHACK_EXPORT long getDfhackLogoTexposStart(); /** - * Get the texpos for the UI pin tiles. Each are 2x2 grids. + * Get the first texpos for the UI pin tiles. Each are 2x2 grids. */ DFHACK_EXPORT long getGreenPinTexposStart(); DFHACK_EXPORT long getRedPinTexposStart(); + +/** + * Get the first texpos for the DFHack icons. It's a 5x2 grid. + */ +DFHACK_EXPORT long getIconsTexposStart(); + } } diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index 3ae8658c8..78ed53d97 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -21,6 +21,7 @@ static long g_num_dfhack_textures = 0; static long g_dfhack_logo_texpos_start = -1; static long g_green_pin_texpos_start = -1; static long g_red_pin_texpos_start = -1; +static long g_icons_texpos_start = -1; // Converts an arbitrary Surface to something like the display format // (32-bit RGBA), and converts magenta to transparency if convert_magenta is set @@ -117,6 +118,8 @@ void Textures::init(color_ostream &out) { &g_green_pin_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/red-pin.png", &g_red_pin_texpos_start); + g_num_dfhack_textures += load_textures(out, "hack/data/art/icons.png", + &g_icons_texpos_start); DEBUG(textures,out).print("loaded %ld textures\n", g_num_dfhack_textures); @@ -160,3 +163,7 @@ long Textures::getGreenPinTexposStart() { long Textures::getRedPinTexposStart() { return g_red_pin_texpos_start; } + +long Textures::getIconsTexposStart() { + return g_icons_texpos_start; +} From 478b5ada3975703a7866c9c24cfe806bf644b5e8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 22:17:39 -0800 Subject: [PATCH 0121/2222] expose icons texpos to Lua --- library/LuaApi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 90815708d..9f3d590dd 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1692,6 +1692,7 @@ static const LuaWrapper::FunctionReg dfhack_textures_module[] = { WRAPM(Textures, getDfhackLogoTexposStart), WRAPM(Textures, getGreenPinTexposStart), WRAPM(Textures, getRedPinTexposStart), + WRAPM(Textures, getIconsTexposStart), { NULL, NULL } }; From 508777897be527c1e7c64afe47b62bfcfc14503f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 22:30:47 -0800 Subject: [PATCH 0122/2222] allow tile list icons to be rendered properly --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 5414fefaa..79372b153 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1694,7 +1694,7 @@ function List:onRenderBody(dc) local function paint_icon(icon, obj) if type(icon) ~= 'string' then - dc:char(nil,icon) + dc:tile(nil,icon) else if current then dc:string(icon, obj.icon_pen or self.icon_pen or cur_pen) From f6372b97d162376135dfe46259390e0d364d541f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 13 Jan 2023 07:14:32 +0000 Subject: [PATCH 0123/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index cf4940071..18446d51e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit cf4940071c0ac16e3af8d2f46abc6a25d3251492 +Subproject commit 18446d51e4133a06271fd2672f5b816b8c56e937 diff --git a/scripts b/scripts index 7b79aa13b..a8c5ac9e9 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7b79aa13bda3013bb4c76f2894c5971ec2ffbd1b +Subproject commit a8c5ac9e94e26f0901917efac213ebedc7e0c276 From 3af91e15729e59f5eb457fb19ac3dba360e42053 Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 13 Jan 2023 11:48:07 +0000 Subject: [PATCH 0124/2222] update fortress reader for structures change --- plugins/remotefortressreader/building_reader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/remotefortressreader/building_reader.cpp b/plugins/remotefortressreader/building_reader.cpp index e1f29e358..7cc715147 100644 --- a/plugins/remotefortressreader/building_reader.cpp +++ b/plugins/remotefortressreader/building_reader.cpp @@ -619,13 +619,13 @@ void CopyBuilding(int buildingIndex, RemoteFortressReader::BuildingInstance * re continue; if (zone->type != df::civzone_type::ArcheryRange) continue; - if(zone->dir_x < 0) + if(zone->zone_settings.archery.dir_x < 0) remote_build->set_direction(EAST); - else if(zone->dir_x > 0) + else if(zone->zone_settings.archery.dir_x > 0) remote_build->set_direction(WEST); - else if (zone->dir_y < 0) + else if (zone->zone_settings.archery.dir_y < 0) remote_build->set_direction(SOUTH); - else if (zone->dir_y > 0) + else if (zone->zone_settings.archery.dir_y > 0) remote_build->set_direction(NORTH); break; } From 99350bdb733b49ab34c6ce798897eb6f6d431cb7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 09:45:33 -0800 Subject: [PATCH 0125/2222] add xlsxreader back to the build --- docs/plugins/xlsxreader.rst | 2 +- plugins/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/xlsxreader.rst b/docs/plugins/xlsxreader.rst index fe86a072d..3d6cc0ac8 100644 --- a/docs/plugins/xlsxreader.rst +++ b/docs/plugins/xlsxreader.rst @@ -3,7 +3,7 @@ xlsxreader .. dfhack-tool:: :summary: Provides a Lua API for reading xlsx files. - :tags: untested dev + :tags: dev :no-command: See `xlsxreader-api` for details. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 049c05f89..727df63c5 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -167,7 +167,7 @@ dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #add_subdirectory(tweak) #dfhack_plugin(workflow workflow.cpp LINK_LIBRARIES lua) #dfhack_plugin(workNow workNow.cpp) -#dfhack_plugin(xlsxreader xlsxreader.cpp LINK_LIBRARIES lua xlsxio_read_STATIC zip expat) +dfhack_plugin(xlsxreader xlsxreader.cpp LINK_LIBRARIES lua xlsxio_read_STATIC zip expat) #dfhack_plugin(zone zone.cpp) # If you are adding a plugin that you do not intend to commit to the DFHack repo, From cc0ff6a93dd3635425e6c4604868eb739c5a8348 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 12:16:49 -0800 Subject: [PATCH 0126/2222] raise trigger lock screens (if possible) so they don't get stuck under new viewscreens and become lost (and therefore overlay will be forever locked) --- plugins/lua/overlay.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 4fc375d72..42116735a 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -43,7 +43,12 @@ end local function triggered_screen_has_lock() if not trigger_lock_holder_screen then return false end - if trigger_lock_holder_screen:isActive() then return true end + if trigger_lock_holder_screen:isActive() then + if trigger_lock_holder_screen.raise then + trigger_lock_holder_screen:raise() + end + return true + end return register_trigger_lock_screen(nil, nil) end @@ -429,9 +434,8 @@ local function _update_viewscreen_widgets(vs_name, vs, now_ms) return now_ms end --- not subject to trigger lock since these widgets are already filtered by --- viewscreen function update_viewscreen_widgets(vs_name, vs) + if triggered_screen_has_lock() then return end local now_ms = _update_viewscreen_widgets(vs_name, vs, nil) _update_viewscreen_widgets('all', vs, now_ms) end From 6c6c4e159f9b86329459214c4da2fc08d3b70cec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 12:17:51 -0800 Subject: [PATCH 0127/2222] add reminder why we can't clear mouse_lbut --- library/lua/gui.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 8a2a406aa..a0412a3a8 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -725,6 +725,7 @@ function ZScreen:onInput(keys) if ZScreen.super.onInput(self, keys) then -- ensure underlying DF screens don't also react to handled clicks if keys._MOUSE_L_DOWN then + -- note we can't clear mouse_lbut here. otherwise we break dragging, df.global.enabler.mouse_lbut_down = 0 end if keys._MOUSE_R_DOWN then From 807f3f6327d9930078b1336869933fbc40677baa Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 12:18:18 -0800 Subject: [PATCH 0128/2222] update behavior of hotspot menu - disappears on click outside its borders - disappears on r-click - mouse over the help panel counts as "over the menu" (so the menu doesn't close if the player moves the mouse to the help text) - menu panels appear next to the logo hotspot instead of over it, allowing players to avoid clicking on the wrong item if they intend to click on the logo --- plugins/lua/hotkeys.lua | 101 +++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 53885d8fa..2fb32a692 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -32,12 +32,7 @@ function HotspotMenuWidget:overlay_onupdate() end function HotspotMenuWidget:overlay_trigger() - local hotkeys, bindings = getHotkeys() - return MenuScreen{ - hotspot_frame=self.frame, - hotkeys=hotkeys, - bindings=bindings, - mouseover=self.mouseover}:show() + return MenuScreen{hotspot_frame=self.frame}:show() end local dscreen = dfhack.screen @@ -71,21 +66,17 @@ end -- register the menu hotspot with the overlay OVERLAY_WIDGETS = {menu=HotspotMenuWidget} --- ---------- -- --- MenuScreen -- --- ---------- -- +-- ---- -- +-- Menu -- +-- ---- -- local ARROW = string.char(26) local MAX_LIST_WIDTH = 45 local MAX_LIST_HEIGHT = 15 -MenuScreen = defclass(MenuScreen, gui.Screen) -MenuScreen.ATTRS{ - focus_path='hotkeys/menu', +Menu = defclass(MenuScreen, widgets.Panel) +Menu.ATTRS{ hotspot_frame=DEFAULT_NIL, - hotkeys=DEFAULT_NIL, - bindings=DEFAULT_NIL, - mouseover=false, } -- get a map from the binding string to a list of hotkey strings that all @@ -142,10 +133,11 @@ local function get_choices(hotkeys, bindings, is_inverted) return choices, max_width end -function MenuScreen:init() +function Menu:init() + local hotkeys, bindings = getHotkeys() + local is_inverted = not not self.hotspot_frame.b - local choices,list_width = get_choices(self.hotkeys, self.bindings, - is_inverted) + local choices,list_width = get_choices(hotkeys, bindings, is_inverted) local list_frame = copyall(self.hotspot_frame) list_frame.w = list_width + 2 @@ -156,9 +148,9 @@ function MenuScreen:init() list_frame.b = math.max(0, list_frame.b - 1) end if list_frame.l then - list_frame.l = math.max(0, list_frame.l - 1) + list_frame.l = math.max(0, list_frame.l + 5) else - list_frame.r = math.max(0, list_frame.r - 1) + list_frame.r = math.max(0, list_frame.r + 5) end local help_frame = {w=list_frame.w, l=list_frame.l, r=list_frame.r} @@ -207,11 +199,7 @@ function MenuScreen:init() end end -function MenuScreen:onDismiss() - cleanupHotkeys() -end - -function MenuScreen:onSelect(_, choice) +function Menu:onSelect(_, choice) if not choice or #self.subviews == 0 then return end local first_word = choice.command:trim():split(' +')[1] if first_word:startswith(':') then first_word = first_word:sub(2) end @@ -220,22 +208,21 @@ function MenuScreen:onSelect(_, choice) self.subviews.help_panel:updateLayout() end -function MenuScreen:onSubmit(_, choice) +function Menu:onSubmit(_, choice) if not choice then return end - dfhack.screen.hideGuard(self, dfhack.run_command, choice.command) - self:dismiss() + dfhack.screen.hideGuard(self.parent_view, dfhack.run_command, choice.command) + self.parent_view:dismiss() end -function MenuScreen:onSubmit2(_, choice) +function Menu:onSubmit2(_, choice) if not choice then return end - self:dismiss() + self.parent_view:dismiss() dfhack.run_script('gui/launcher', choice.command) end -function MenuScreen:onInput(keys) - if keys.LEAVESCREEN then - self:dismiss() - return true +function Menu:onInput(keys) + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + return false elseif keys.STANDARDSCROLL_RIGHT then self:onSubmit2(self.subviews.list:getSelected()) return true @@ -246,19 +233,28 @@ function MenuScreen:onInput(keys) self:onSubmit2(list:getSelected()) return true end + if not self:getMouseFramePos() then + self.parent_view:dismiss() + return true + end end - return self:inputToSubviews(keys) + self:inputToSubviews(keys) + return true -- we're modal end -function MenuScreen:onRenderFrame(dc, rect) +function Menu:onRenderFrame(dc, rect) if self.initialize then self.initialize() self.initialize = nil end - self:renderParent() end -function MenuScreen:onRenderBody(dc) +function Menu:getMouseFramePos() + return self.subviews.list_panel:getMouseFramePos() or + self.subviews.help_panel:getMouseFramePos() +end + +function Menu:onRenderBody(dc) local panel = self.subviews.list_panel local list = self.subviews.list local idx = list:getIdxUnderMouse() @@ -267,14 +263,35 @@ function MenuScreen:onRenderBody(dc) -- selection, don't override the selection until the mouse moves to -- another item list:setSelected(idx) - self.mouseover = true self.last_mouse_idx = idx - elseif not panel:getMousePos(gui.ViewRect{rect=panel.frame_rect}) - and self.mouseover then + end + if self:getMouseFramePos() then + self.mouseover = true + elseif self.mouseover then -- once the mouse has entered the list area, leaving the frame should -- close the menu screen - self:dismiss() + self.parent_view:dismiss() end end +-- ---------- -- +-- MenuScreen -- +-- ---------- -- + +MenuScreen = defclass(MenuScreen, gui.ZScreen) +MenuScreen.ATTRS { + focus_path='hotkeys/menu', + hotspot_frame=DEFAULT_NIL, +} + +function MenuScreen:init() + self:addviews{ + Menu{hotspot_frame=self.hotspot_frame}, + } +end + +function MenuScreen:onDismiss() + cleanupHotkeys() +end + return _ENV From 8f434bf9a697a81b5b0635023248903fdda0f9b3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 12:36:16 -0800 Subject: [PATCH 0129/2222] add blueprint back to the build (dig and build phases only) --- docs/plugins/blueprint.rst | 9 ++++++++- plugins/CMakeLists.txt | 2 +- plugins/blueprint.cpp | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index aa2d0fa03..3b63719cb 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -3,7 +3,7 @@ blueprint .. dfhack-tool:: :summary: Record a live game map in a quickfort blueprint. - :tags: untested fort design buildings map stockpiles + :tags: fort design buildings map stockpiles With ``blueprint``, you can export the structure of a portion of your fortress in a blueprint file that you (or anyone else) can later play back with @@ -15,6 +15,13 @@ selected interactively with the ``gui/blueprint`` command or, if the GUI is not used, starts at the active cursor location and extends right and down for the requested width and height. +.. admonition:: Note + + blueprint is still in the process of being updated for the new version of + DF. Stockpiles (the "place" phase), zones (the "zone" phase), building + (the "query" phase), and game configuration (the "config" phase) are not + yet supported. + Usage ----- diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 727df63c5..1166caae7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -86,7 +86,7 @@ dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(automelt automelt.cpp) #dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) -#dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) +dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) #add_subdirectory(buildingplan) diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 509b1e6bc..3d34f592e 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -850,6 +850,7 @@ static const char * get_tile_build(const df::coord &pos, return add_expansion_syntax(ctx, keys); } +/* TODO: understand how this changes for v50 static const char * get_place_keys(const tile_context &ctx) { df::building_stockpilest* sp = virtual_cast(ctx.b); @@ -1086,6 +1087,7 @@ static const char * get_tile_rooms(const df::coord &, const tile_context &ctx) { str << "r{+ " << (max_dim - 3) << "}&"; return cache(str); } +*/ static bool create_output_dir(color_ostream &out, const blueprint_options &opts) { @@ -1326,6 +1328,7 @@ static bool do_transform(color_ostream &out, get_tile_construct, ensure_building); add_processor(processors, opts, "build", "build", opts.build, get_tile_build, ensure_building); +/* TODO: understand how this changes for v50 add_processor(processors, opts, "place", "place", opts.place, get_tile_place, ensure_building); add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone); @@ -1333,6 +1336,14 @@ static bool do_transform(color_ostream &out, get_tile_query, ensure_building); add_processor(processors, opts, "query", "rooms", opts.rooms, get_tile_rooms, ensure_building); +*/ if (opts.place) + out.printerr("'place' blueprints are not yet supported for the current version of DF\n"); + if (opts.zone) + out.printerr("'zone' blueprints are not yet supported for the current version of DF\n"); + if (opts.query) + out.printerr("'query' blueprints are not yet supported for the current version of DF\n"); + if (opts.rooms) + out.printerr("'rooms' blueprints are not yet supported for the current version of DF\n"); if (processors.empty()) { out.printerr("no phases requested! nothing to do!\n"); From 40aa7539536a205f4fc583ad59da5af6fbcac461 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 12:43:49 -0800 Subject: [PATCH 0130/2222] fix docs typo --- docs/plugins/blueprint.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index 3b63719cb..b10921d30 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -19,8 +19,8 @@ requested width and height. blueprint is still in the process of being updated for the new version of DF. Stockpiles (the "place" phase), zones (the "zone" phase), building - (the "query" phase), and game configuration (the "config" phase) are not - yet supported. + configuration (the "query" phase), and game configuration (the "config" + phase) are not yet supported. Usage ----- From 21d160c47801ea2efdc19691132acf7873464a12 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 16:07:27 -0800 Subject: [PATCH 0131/2222] terminology change: locked -> pinned --- docs/dev/Lua API.rst | 22 ++++++++++---------- library/lua/gui.lua | 40 ++++++++++++++++++------------------- library/lua/gui/widgets.lua | 16 +++++++-------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 56d5eac5f..33b48c0a7 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4111,13 +4111,13 @@ through to the underlying viewscreen. If :kbd:`Esc` or the right mouse button is pressed, and the ZScreen widgets don't otherwise handle them, then the top ZScreen is dismissed. If the ZScreen -is "locked", then the screen is not dismissed and the input is passed on to the -underlying DF viewscreen. :kbd:`Alt`:kbd:`L` toggles the locked status if the +is "pinned", then the screen is not dismissed and the input is passed on to the +underlying DF viewscreen. :kbd:`Alt`:kbd:`L` toggles the pinned status if the ZScreen widgets don't otherwise handle that key sequence. If you have a -``Panel`` with the ``lockable`` attribute set and a frame that has pens defined -for the lock icon (like ``Window`` widgets have by default), then a lock icon +``Panel`` with the ``pinnable`` attribute set and a frame that has pens defined +for the pin icon (like ``Window`` widgets have by default), then a pin icon will appear in the upper right corner of the frame. Clicking on this icon will -toggle the ZScreen ``locked`` status just as if :kbd:`Alt`:kbd:`L` had been +toggle the ZScreen ``pinned`` status just as if :kbd:`Alt`:kbd:`L` had been pressed. Keyboard input goes to the top ZScreen, as usual. If the subviews of the top @@ -4146,10 +4146,10 @@ ZScreen provides the following functions: when the tool command is run and raise the existing dialog if it exists or show a new dialog if it doesn't. See the sample code below for an example. -* ``zscreen:toggleLocked()`` +* ``zscreen:togglePinned()`` - Toggles whether the window closes on :kbd:`ESC` or r-click (unlocked) or not - (locked). + Toggles whether the window closes on :kbd:`ESC` or r-click (unpinned) or not + (pinned). * ``zscreen:isMouseOver()`` @@ -4341,9 +4341,9 @@ Has attributes: hitting :kbd:`Esc` (while resizing with the mouse or keyboard), or by calling ``Panel:setKeyboardResizeEnabled(false)`` (while resizing with the keyboard). -* ``lockable = bool`` (default: ``false``) +* ``pinnable = bool`` (default: ``false``) - Determines whether the panel will draw a lock icon in its frame. See + Determines whether the panel will draw a pin icon in its frame. See `ZScreen class`_ for details. * ``autoarrange_subviews = bool`` (default: ``false``) @@ -4407,7 +4407,7 @@ Window class ------------ Subclass of Panel; sets Panel attributes to useful defaults for a top-level -framed, lockable, draggable window. +framed, pinnable, draggable window. ResizingPanel class ------------------- diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 8a2a406aa..e7f59a148 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -708,8 +708,8 @@ function ZScreen:isOnTop() return dfhack.gui.getCurViewscreen(true) == self._native end -function ZScreen:toggleLocked() - self.locked = not self.locked +function ZScreen:togglePinned() + self.pinned = not self.pinned end function ZScreen:onInput(keys) @@ -734,11 +734,11 @@ function ZScreen:onInput(keys) end if keys.CUSTOM_ALT_L then - self:toggleLocked() + self:togglePinned() return end - if (self:isMouseOver() or not self.locked) + if (self:isMouseOver() or not self.pinned) and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then self:dismiss() -- ensure underlying DF screens don't also react to the click @@ -814,11 +814,11 @@ GREY_LINE_FRAME = { title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, inactive_title_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, - locked_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, - unlocked_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, + pinned_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, + unpinned_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, } -function paint_frame(dc,rect,style,title,show_lock,locked,inactive) +function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 dscreen.paintTile(style.lt_frame_pen or pen, x1, y1) @@ -843,26 +843,26 @@ function paint_frame(dc,rect,style,title,show_lock,locked,inactive) x, y1, tstr) end - if show_lock then - if locked and style.locked_pen then + if show_pin then + if pinned and style.pinned_pen then local pin_texpos = dfhack.textures.getGreenPinTexposStart() if pin_texpos == -1 then - dscreen.paintTile(style.locked_pen, x2-1, y1) + dscreen.paintTile(style.pinned_pen, x2-1, y1) else - dscreen.paintTile(style.locked_pen, x2-2, y1-1, nil, pin_texpos+0) - dscreen.paintTile(style.locked_pen, x2-1, y1-1, nil, pin_texpos+1) - dscreen.paintTile(style.locked_pen, x2-2, y1, nil, pin_texpos+2) - dscreen.paintTile(style.locked_pen, x2-1, y1, nil, pin_texpos+3) + dscreen.paintTile(style.pinned_pen, x2-2, y1-1, nil, pin_texpos+0) + dscreen.paintTile(style.pinned_pen, x2-1, y1-1, nil, pin_texpos+1) + dscreen.paintTile(style.pinned_pen, x2-2, y1, nil, pin_texpos+2) + dscreen.paintTile(style.pinned_pen, x2-1, y1, nil, pin_texpos+3) end - elseif not locked and style.unlocked_pen then + elseif not pinned and style.unpinned_pen then local pin_texpos = dfhack.textures.getRedPinTexposStart() if pin_texpos == -1 then - dscreen.paintTile(style.unlocked_pen, x2-1, y1) + dscreen.paintTile(style.unpinned_pen, x2-1, y1) else - dscreen.paintTile(style.unlocked_pen, x2-2, y1-1, nil, pin_texpos+0) - dscreen.paintTile(style.unlocked_pen, x2-1, y1-1, nil, pin_texpos+1) - dscreen.paintTile(style.unlocked_pen, x2-2, y1, nil, pin_texpos+2) - dscreen.paintTile(style.unlocked_pen, x2-1, y1, nil, pin_texpos+3) + dscreen.paintTile(style.unpinned_pen, x2-2, y1-1, nil, pin_texpos+0) + dscreen.paintTile(style.unpinned_pen, x2-1, y1-1, nil, pin_texpos+1) + dscreen.paintTile(style.unpinned_pen, x2-2, y1, nil, pin_texpos+2) + dscreen.paintTile(style.unpinned_pen, x2-1, y1, nil, pin_texpos+3) end end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 5414fefaa..9293e737c 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -81,7 +81,7 @@ Panel.ATTRS { resize_min = DEFAULT_NIL, on_resize_begin = DEFAULT_NIL, on_resize_end = DEFAULT_NIL, - lockable = false, + pinnable = false, autoarrange_subviews = false, -- whether to automatically lay out subviews autoarrange_gap = 0, -- how many blank lines to insert between widgets } @@ -280,7 +280,7 @@ local function panel_is_on_pin(self) end local function panel_is_pinnable(self) - return self.lockable and self.parent_view and self.parent_view.toggleLocked + return self.pinnable and self.parent_view and self.parent_view.togglePinned end function Panel:getMouseFramePos() @@ -322,7 +322,7 @@ function Panel:onInput(keys) end if panel_is_pinnable(self) and keys._MOUSE_L_DOWN then if panel_is_on_pin(self) then - self.parent_view:toggleLocked() + self.parent_view:togglePinned() return true end end @@ -491,14 +491,14 @@ end function Panel:onRenderFrame(dc, rect) Panel.super.onRenderFrame(self, dc, rect) if not self.frame_style then return end - local locked = nil - if self.lockable then - locked = self.parent_view and self.parent_view.locked + local pinned = nil + if self.pinnable then + pinned = self.parent_view and self.parent_view.pinned end local inactive = self.parent_view and self.parent_view.isOnTop and not self.parent_view:isOnTop() gui.paint_frame(dc, rect, self.frame_style, self.frame_title, - self.lockable, locked, inactive) + self.pinnable, pinned, inactive) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} @@ -521,7 +521,7 @@ Window.ATTRS { frame_background = gui.CLEAR_PEN, frame_inset = 1, draggable = true, - lockable = true, + pinnable = true, } ------------------- From 4f88d27e08e54ed5be41c95bc083b43eea94d338 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 17:05:08 -0800 Subject: [PATCH 0132/2222] cancel EditField editing on r-click --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 9293e737c..ff1c0cedc 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -708,7 +708,7 @@ function EditField:onInput(keys) end end - if self.key and keys.LEAVESCREEN then + if self.key and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then local old = self.text self:setText(self.saved_text) if self.on_change and old ~= self.saved_text then From e450af74aab8bf3dbadd55c33221f583c3d2bdbd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 17:05:23 -0800 Subject: [PATCH 0133/2222] pull the useful bits out of MenuOverlay --- library/lua/gui/dwarfmode.lua | 140 +++++++++------------------------- 1 file changed, 35 insertions(+), 105 deletions(-) diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index bb381be24..675a7228e 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -286,6 +286,19 @@ function get_hotkey_target(key) end end +function getMapKey(keys) + for code in pairs(keys) do + if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] + or code == '_MOUSE_M_DOWN' or code == '_MOUSE_M' + or code == 'ZOOM_OUT' or code == 'ZOOM_IN' then + if not HOTKEY_KEYS[code] or get_hotkey_target(code) then + return true + end + return code + end + end +end + function Viewport:scrollByKey(key) local dx, dy, dz = get_movement_delta(key, 10, 20) if dx then @@ -339,13 +352,9 @@ function DwarfOverlay:selectBuilding(building,cursor,viewport,gap) end function DwarfOverlay:propagateMoveKeys(keys) - for code,_ in pairs(keys) do - if MOVEMENT_KEYS[code] or HOTKEY_KEYS[code] then - if not HOTKEY_KEYS[code] or get_hotkey_target(code) then - self:sendInputToParent(code) - end - return code - end + local map_key = getMapKey(keys) + if map_key then + self:sendInputToParent(map_key) end end @@ -414,124 +423,45 @@ function DwarfOverlay:onAboutToShow(parent) end end -MenuOverlay = defclass(MenuOverlay, DwarfOverlay) - -MenuOverlay.ATTRS { - frame_inset = 0, - frame_background = gui.CLEAR_PEN, - - -- if sidebar_mode is set, we will enter the specified sidebar mode on show - -- and restore the previous sidebar mode on dismiss. otherwise it is up to - -- the caller to ensure we are in a sidebar mode where the menu is visible. - sidebar_mode = DEFAULT_NIL, -} - -function MenuOverlay:init() - if not dfhack.isMapLoaded() then - -- sidebar menus are only valid when a fort map is loaded - error('A fortress map must be loaded.') - end - - if self.sidebar_mode then - self.saved_sidebar_mode = df.global.plotinfo.main.mode - -- what mode should we restore when this window is dismissed? ideally, we'd - -- restore the mode that the user has set, but we should fall back to - -- restoring the default mode if either of the following conditions are - -- true: - -- 1) enterSidebarMode doesn't support getting back into the current mode - -- 2) a dfhack viewscreen is currently visible. in this case, we can't trust - -- that the current sidebar mode was set by the user. it could just be a - -- MenuOverlay subclass that is currently being shown that has set the - -- sidebar mode for its own purposes. - if not SIDEBAR_MODE_KEYS[self.saved_sidebar_mode] - or dfhack.gui.getCurFocus(true):find('^dfhack/') then - self.saved_sidebar_mode = df.ui_sidebar_mode.Default - end - - enterSidebarMode(self.sidebar_mode) - end -end - -function MenuOverlay:computeFrame(parent_rect) - return self.df_layout.menu, gui.inset_frame(self.df_layout.menu, self.frame_inset) -end - -function MenuOverlay:onAboutToShow(parent) - self:updateLayout() - if not self.df_layout.menu then - error("The menu panel of dwarfmode is not visible") - end -end - -function MenuOverlay:onDismiss() - if self.saved_sidebar_mode then - enterSidebarMode(self.saved_sidebar_mode) - end -end - -function MenuOverlay:render(dc) - self:renderParent() - - local menu = self.df_layout.menu - if menu then - -- Paint signature on the frame. - dscreen.paintString( - {fg=COLOR_BLACK,bg=COLOR_DARKGREY}, - menu.x1+1, menu.y2+1, "DFHack" - ) - - if self.frame_background then - dc:fill(menu, self.frame_background) - end - - MenuOverlay.super.render(self, dc) - end -end - -- Framework for managing rendering over the map area. This function is intended --- to be called from a subclass's onRenderBody() function. +-- to be called from a window's onRenderFrame() function. -- --- get_overlay_char_fn takes a coordinate position and an is_cursor boolean and --- returns the char to render at that position and, optionally, the foreground --- and background colors to use to draw the char. If nothing should be rendered --- at that position, the function should return nil. If no foreground color is --- specified, it defaults to COLOR_GREEN. If no background color is specified, --- it defaults to COLOR_BLACK. +-- get_overlay_pen_fn takes a coordinate position and an is_cursor boolean and +-- returns the pen (and optional char and tile) to render at that position. If +-- nothing should be rendered at that position, the function should return nil. -- -- bounds_rect has elements {x1, x2, y1, y2} in global map coordinates (not -- screen coordinates). The rect is intersected with the visible map viewport to -- get the range over which get_overlay_char_fn is called. If bounds_rect is not -- specified, the entire viewport is scanned. -- --- example call from a subclass: --- function MyMenuOverlaySubclass:onRenderBody() --- local function get_overlay_char(pos) --- return safe_index(self.overlay_chars, pos.z, pos.y, pos.x), COLOR_RED +-- example call: +-- function MyMapOverlay:onRenderFrame(dc, rect) +-- local function get_overlay_pen(pos) +-- if safe_index(self.overlay_map, pos.z, pos.y, pos.x) then +-- return COLOR_GREEN, 'X', dfhack.screen.findGraphicsTile('CURSORS', 4, 3) +-- end -- end --- self:renderMapOverlay(get_overlay_char, self.overlay_bounds) +-- guidm.renderMapOverlay(get_overlay_pen, self.overlay_bounds) -- end -function MenuOverlay:renderMapOverlay(get_overlay_char_fn, bounds_rect) - local vp = self:getViewport() +function renderMapOverlay(get_overlay_pen_fn, bounds_rect) + local vp = Viewport.get() local rect = gui.ViewRect{rect=vp, clip_view=bounds_rect and gui.ViewRect{rect=bounds_rect} or nil} - -- nothing to do if the viewport is completely separate from the bounds_rect + -- nothing to do if the viewport is completely disjoint from the bounds_rect if rect:isDefunct() then return end - local dc = gui.Painter.new(self.df_layout.map) local z = df.global.window_z local cursor = getCursorPos() for y=rect.clip_y1,rect.clip_y2 do for x=rect.clip_x1,rect.clip_x2 do local pos = xyz2pos(x, y, z) - local overlay_char, fg_color, bg_color = get_overlay_char_fn( - pos, same_xy(cursor, pos)) - if not overlay_char then goto continue end - local stile = vp:tileToScreen(pos) - dc:map(true):seek(stile.x, stile.y): - pen(fg_color or COLOR_GREEN, bg_color or COLOR_BLACK): - char(overlay_char):map(false) - ::continue:: + local overlay_pen, char, tile = get_overlay_pen_fn(pos, same_xy(cursor, pos)) + if overlay_pen then + local stile = vp:tileToScreen(pos) + dscreen.paintTile(overlay_pen, stile.x, stile.y, char, tile, true) + end end end end From b77e896041fca4d557e333cd73c05dc36c9d0449 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 13 Jan 2023 17:08:25 -0800 Subject: [PATCH 0134/2222] update EditField docs --- docs/dev/Lua API.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 33b48c0a7..b3f1f52db 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4468,9 +4468,10 @@ calling ``setFocus(true)`` on the field object. If an activation ``key`` is specified, the ``EditField`` will manage its own focus. It will start in the unfocused state, and pressing the activation key will acquire keyboard focus. Pressing the Enter key will release keyboard focus -and then call the ``on_submit`` callback. Pressing the Escape key will also -release keyboard focus, but first it will restore the text that was displayed -before the ``EditField`` gained focus and then call the ``on_change`` callback. +and then call the ``on_submit`` callback. Pressing the Escape key (or r-clicking +with the mouse) will also release keyboard focus, but first it will restore the +text that was displayed before the ``EditField`` gained focus and then call the +``on_change`` callback. The ``EditField`` cursor can be moved to where you want to insert/remove text. You can click where you want the cursor to move or you can use any of the From 3459d61dc4c83c7b3f0ebfab21dccf1063e3063e Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Sat, 14 Jan 2023 13:16:07 +1100 Subject: [PATCH 0135/2222] Use vswhere to find MSBUILD --- build/win64/build-debug.bat | 7 +++---- build/win64/build-release.bat | 6 ++---- build/win64/generate-MSVC-all.bat | 4 +--- build/win64/generate-MSVC-gui.bat | 6 ++---- build/win64/generate-MSVC-minimal.bat | 4 +--- build/win64/generate-MSVC-release.bat | 4 +--- build/win64/generate-MSVC-testing.bat | 4 +--- build/win64/install-debug.bat | 6 ++---- build/win64/install-release.bat | 6 ++---- build/win64/msvc_include.bat | 9 +++++++++ build/win64/package-debug.bat | 7 ++----- build/win64/package-release.bat | 7 ++----- 12 files changed, 28 insertions(+), 42 deletions(-) create mode 100644 build/win64/msvc_include.bat diff --git a/build/win64/build-debug.bat b/build/win64/build-debug.bat index a04cb9984..fdcdce6b8 100644 --- a/build/win64/build-debug.bat +++ b/build/win64/build-debug.bat @@ -1,4 +1,3 @@ -call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 -cd VC2022 -msbuild /m /p:Platform=x64 /p:Configuration=RelWithDebInfo ALL_BUILD.vcxproj -cd .. +call msvc_include.bat +%msbuild% /m /p:Platform=x64 /p:Configuration=RelWithDebInfo VC2022/ALL_BUILD.vcxproj + diff --git a/build/win64/build-release.bat b/build/win64/build-release.bat index 8068e5074..39174794a 100644 --- a/build/win64/build-release.bat +++ b/build/win64/build-release.bat @@ -1,5 +1,3 @@ -call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 -cd VC2022 -msbuild /m /p:Platform=x64 /p:Configuration=Release ALL_BUILD.vcxproj -cd .. +call msvc_include.bat +%msbuild% /m /p:Platform=x64 /p:Configuration=Release VC2022/ALL_BUILD.vcxproj pause diff --git a/build/win64/generate-MSVC-all.bat b/build/win64/generate-MSVC-all.bat index ea8db34c0..f43a03596 100644 --- a/build/win64/generate-MSVC-all.bat +++ b/build/win64/generate-MSVC-all.bat @@ -1,6 +1,4 @@ IF EXIST DF_PATH.txt SET /P _DF_PATH= Date: Sat, 14 Jan 2023 14:07:14 +1100 Subject: [PATCH 0136/2222] let cmake deal with VS2022 instead :) --- build/win64/build-debug.bat | 4 +--- build/win64/build-release.bat | 4 +--- build/win64/install-debug.bat | 3 +-- build/win64/install-release.bat | 3 +-- build/win64/msvc_include.bat | 9 --------- build/win64/package-debug.bat | 3 +-- build/win64/package-release.bat | 3 +-- 7 files changed, 6 insertions(+), 23 deletions(-) delete mode 100644 build/win64/msvc_include.bat diff --git a/build/win64/build-debug.bat b/build/win64/build-debug.bat index fdcdce6b8..2db9df402 100644 --- a/build/win64/build-debug.bat +++ b/build/win64/build-debug.bat @@ -1,3 +1 @@ -call msvc_include.bat -%msbuild% /m /p:Platform=x64 /p:Configuration=RelWithDebInfo VC2022/ALL_BUILD.vcxproj - +cmake --build VC2022 -t ALL_BUILD -- /m /p:Platform=x64 /p:Configuration=RelWithDebInfo diff --git a/build/win64/build-release.bat b/build/win64/build-release.bat index 39174794a..f719d64bc 100644 --- a/build/win64/build-release.bat +++ b/build/win64/build-release.bat @@ -1,3 +1 @@ -call msvc_include.bat -%msbuild% /m /p:Platform=x64 /p:Configuration=Release VC2022/ALL_BUILD.vcxproj -pause +cmake --build VC2022 -t ALL_BUILD -- /m /p:Platform=x64 /p:Configuration=Release diff --git a/build/win64/install-debug.bat b/build/win64/install-debug.bat index 2fc03eca1..b6c22b0e3 100644 --- a/build/win64/install-debug.bat +++ b/build/win64/install-debug.bat @@ -1,2 +1 @@ -call msvc_include.bat -%msbuild% /m /p:Platform=x64 /p:Configuration=RelWithDebInfo VC2022/INSTALL.vcxproj +cmake --build VC2022 -t INSTALL -- /m /p:Platform=x64 /p:Configuration=RelWithDebInfo diff --git a/build/win64/install-release.bat b/build/win64/install-release.bat index 3194812b8..61aabf771 100644 --- a/build/win64/install-release.bat +++ b/build/win64/install-release.bat @@ -1,2 +1 @@ -call msvc_include.bat -%msbuild% /m /p:Platform=x64 /p:Configuration=Release VC2022/INSTALL.vcxproj +cmake --build VC2022 -t INSTALL -- /m /p:Platform=x64 /p:Configuration=Release diff --git a/build/win64/msvc_include.bat b/build/win64/msvc_include.bat deleted file mode 100644 index 0dbcc90cc..000000000 --- a/build/win64/msvc_include.bat +++ /dev/null @@ -1,9 +0,0 @@ -@ECHO OFF -setlocal enabledelayedexpansion - -FOR /F "usebackq tokens=*" %%F IN (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -requires Microsoft.Component.MSBuild -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -version [17.0^,] -products * -find MSBuild\**\Bin\MSBuild.exe`) DO ( - endlocal & set "MSBUILD="%%F"" - goto :EOF -) -echo "Cannot find a Visual Studio 2022/Build Tools installation" -exit 1 \ No newline at end of file diff --git a/build/win64/package-debug.bat b/build/win64/package-debug.bat index 72ca65a0a..afaec234a 100644 --- a/build/win64/package-debug.bat +++ b/build/win64/package-debug.bat @@ -1,3 +1,2 @@ -call msvc_include.bat -%msbuild% /m /p:Platform=x64 /p:Configuration=RelWithDebInfo VC2022/PACKAGE.vcxproj +cmake --build VC2022 -t PACKAGE -- /m /p:Platform=x64 /p:Configuration=RelWithDebInfo exit %ERRORLEVEL% diff --git a/build/win64/package-release.bat b/build/win64/package-release.bat index e796d6481..75b35bcd5 100644 --- a/build/win64/package-release.bat +++ b/build/win64/package-release.bat @@ -1,2 +1 @@ -call msvc_include.bat -%msbuild% /m /p:Platform=x64 /p:Configuration=Release VC2022/PACKAGE.vcxproj +cmake --build VC2022 -t PACKAGE -- /m /p:Platform=x64 /p:Configuration=Release From 0e021e392d856b593e4a08ec46e9b666373d0764 Mon Sep 17 00:00:00 2001 From: Rose Date: Fri, 13 Jan 2023 21:20:01 -0800 Subject: [PATCH 0137/2222] Added a report to autoclothing, as well as some changes to DFHack::Units to enable it. --- library/include/modules/Units.h | 2 + library/modules/Units.cpp | 14 +++++ plugins/CMakeLists.txt | 2 +- plugins/autoclothing.cpp | 104 ++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index effa7cd1a..3cdc9264a 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -179,6 +179,8 @@ DFHACK_EXPORT std::string getRaceName(df::unit* unit); DFHACK_EXPORT std::string getPhysicalDescription(df::unit* unit); DFHACK_EXPORT std::string getRaceNamePluralById(int32_t race_id); DFHACK_EXPORT std::string getRaceNamePlural(df::unit* unit); +DFHACK_EXPORT std::string getRaceReadableNameById(int32_t race_id); +DFHACK_EXPORT std::string getRaceReadableName(df::unit* unit); DFHACK_EXPORT std::string getRaceBabyNameById(int32_t race_id); DFHACK_EXPORT std::string getRaceBabyName(df::unit* unit); DFHACK_EXPORT std::string getRaceChildNameById(int32_t race_id); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 6d5ec7fde..1fd72cb0b 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1036,6 +1036,20 @@ string Units::getRaceName(df::unit* unit) return getRaceNameById(unit->race); } +// get human-readable race name by id or unit pointer +string Units::getRaceReadableNameById(int32_t id) +{ + df::creature_raw* raw = world->raws.creatures.all[id]; + if (raw) + return raw->name[0]; + return ""; +} +string Units::getRaceReadableName(df::unit* unit) +{ + CHECK_NULL_POINTER(unit); + return getRaceReadableNameById(unit->race); +} + void df_unit_get_physical_description(df::unit* unit, string* out_str) { static auto* const fn = diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 727df63c5..06159c51c 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -77,7 +77,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) #dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) -#dfhack_plugin(autoclothing autoclothing.cpp) +dfhack_plugin(autoclothing autoclothing.cpp) #dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index 1535b5eb8..8cbbf203f 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -13,6 +13,7 @@ #include "modules/Materials.h" #include "modules/Units.h" #include "modules/World.h" +#include "modules/Translation.h" #include "df/itemdef_armorst.h" #include "df/itemdef_glovesst.h" @@ -55,6 +56,7 @@ static void cleanup_state(color_ostream &out); static void do_autoclothing(); static bool validateMaterialCategory(ClothingRequirement * requirement); static bool setItem(std::string name, ClothingRequirement* requirement); +static void generate_report(color_ostream& out); std::vectorclothingOrders; @@ -366,6 +368,12 @@ command_result autoclothing(color_ostream &out, std::vector & para } return CR_OK; } + else if (parameters.size() == 1 && parameters[0] == "report") + { + CoreSuspender suspend; + generate_report(out); + return CR_OK; + } else if (parameters.size() < 2 || parameters.size() > 3) { out << "Wrong number of arguments." << endl; @@ -663,3 +671,99 @@ static void save_state(color_ostream &out) item.val() = clothingOrders[i].Serialize(); } } + +static void list_unit_counts(color_ostream& out, std::map& unitList) +{ + for (const auto& race : unitList) + { + if (race.second == 1) + out << " 1 " << Units::getRaceReadableNameById(race.first) << endl; + else + out << " " << race.second << " " << Units::getRaceNamePluralById(race.first) << endl; + } +} + +static void generate_report(color_ostream& out) +{ + std::map fullUnitList; + std::map missingArmor; + std::map missingShoes; + std::map missingHelms; + std::map missingGloves; + std::map missingPants; + for (df::unit* unit : world->units.active) + { + if (!Units::isCitizen(unit)) + continue; + fullUnitList[unit->race]++; + int numArmor = 0, numShoes = 0, numHelms = 0, numGloves = 0, numPants = 0; + for (auto itemId : unit->owned_items) + { + + auto item = Items::findItemByID(itemId); + if (item->getWear() >= 1) + continue; + switch (item->getType()) + { + case df::item_type::ARMOR: + numArmor++; + break; + case df::item_type::SHOES: + numShoes++; + break; + case df::item_type::HELM: + numHelms++; + break; + case df::item_type::GLOVES: + numGloves++; + break; + case df::item_type::PANTS: + numPants++; + break; + default: + break; + } + } + if (numArmor == 0) + missingArmor[unit->race]++; + if (numShoes < 2) + missingShoes[unit->race]++; + if (numHelms == 0) + missingHelms[unit->race]++; + if (numGloves < 2) + missingGloves[unit->race]++; + if (numPants == 0) + missingPants[unit->race]++; + //out << Translation::TranslateName(Units::getVisibleName(unit)) << " has " << numArmor << " armor, " << numShoes << " shoes, " << numHelms << " helms, " << numGloves << " gloves, " << numPants << " pants" << endl; + } + if (missingArmor.size() + missingShoes.size() + missingHelms.size() + missingGloves.size() + missingPants.size() == 0) + { + out << "Everybody has a full set of clothes to wear, congrats!" << endl; + return; + } + if (missingArmor.size()) + { + out << "Following units need new bodywear:" << endl; + list_unit_counts(out, missingArmor); + } + if (missingShoes.size()) + { + out << "Following units need new shoes:" << endl; + list_unit_counts(out, missingShoes); + } + if (missingHelms.size()) + { + out << "Following units need new headwear:" << endl; + list_unit_counts(out, missingHelms); + } + if (missingGloves.size()) + { + out << "Following units need new handwear:" << endl; + list_unit_counts(out, missingGloves); + } + if (missingPants.size()) + { + out << "Following units need new legwear:" << endl; + list_unit_counts(out, missingPants); + } +} From 5f60791b399319bf4ec82a53e34145c946f45629 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 14 Jan 2023 07:13:34 +0000 Subject: [PATCH 0138/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index a8c5ac9e9..e1ef58a34 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a8c5ac9e94e26f0901917efac213ebedc7e0c276 +Subproject commit e1ef58a343b01b91766deb17d5945b6274797eae From f19ae16c9d64f2678bef68af5719eededbc2e279 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 01:00:38 -0800 Subject: [PATCH 0139/2222] give more control over the resizing dimensions of ResizingPanels --- docs/dev/Lua API.rst | 7 +++++++ library/lua/gui/widgets.lua | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 33b48c0a7..728551ff6 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4416,6 +4416,13 @@ Subclass of Panel; automatically adjusts its own frame height and width to the minimum required to show its subviews. Pairs nicely with a parent Panel that has ``autoarrange_subviews`` enabled. +It has the following attributes: + +:auto_height: Sets self.frame.h from the positions and height of its subviews + (default is ``true``). +:auto_width: Sets self.frame.w from the positions and width of its subviews + (default is ``false``). + Pages class ----------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 9293e737c..1488eda90 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -530,6 +530,11 @@ Window.ATTRS { ResizingPanel = defclass(ResizingPanel, Panel) +ResizingPanel.ATTRS{ + auto_height = true, + auto_width = false, +} + -- adjust our frame dimensions according to positions and sizes of our subviews function ResizingPanel:postUpdateLayout(frame_body) local w, h = 0, 0 @@ -550,6 +555,8 @@ function ResizingPanel:postUpdateLayout(frame_body) end if not self.frame then self.frame = {} end local oldw, oldh = self.frame.w, self.frame.h + if not self.auto_height then h = oldh end + if not self.auto_width then w = oldw end self.frame.w, self.frame.h = w, h if not self._updateLayoutGuard and (oldw ~= w or oldh ~= h) then self._updateLayoutGuard = true -- protect against infinite loops From 69e5730f77dde00c693d92ae5c114112e6a0a26e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 01:07:18 -0800 Subject: [PATCH 0140/2222] document dfhack.job.removeJob() --- docs/dev/Lua API.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 33b48c0a7..49e275578 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1144,6 +1144,10 @@ Job module Prints info about the job item. +* ``dfhack.job.removeJob(job)`` + + Cancels a job, cleans up all references to it, and removes it from the world. + * ``dfhack.job.getGeneralRef(job, type)`` Searches for a general_ref with the given type. From 7c53d848cdcf50cb96f3f697c2d3fc0a30012d0f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 01:21:58 -0800 Subject: [PATCH 0141/2222] sync tags from spreadsheet --- docs/plugins/fastdwarf.rst | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/fastdwarf.rst b/docs/plugins/fastdwarf.rst index 217c098ef..fb0524c3b 100644 --- a/docs/plugins/fastdwarf.rst +++ b/docs/plugins/fastdwarf.rst @@ -3,7 +3,7 @@ fastdwarf .. dfhack-tool:: :summary: Dwarves teleport and/or finish jobs instantly. - :tags: untested fort armok units + :tags: fort armok units Usage ----- diff --git a/scripts b/scripts index e1ef58a34..8064f7160 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e1ef58a343b01b91766deb17d5945b6274797eae +Subproject commit 8064f71601664fb841f830850ce390092e685276 From f82acec9d4c6af09d869535505588a4072180792 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 14 Jan 2023 09:26:48 +0000 Subject: [PATCH 0142/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 18446d51e..ab0593b40 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 18446d51e4133a06271fd2672f5b816b8c56e937 +Subproject commit ab0593b40a1a41c19432ec6bd88fc9ecf60b5fb6 From 87c54c871645b9388e04eff591ab62485787acfb Mon Sep 17 00:00:00 2001 From: Rose Date: Sat, 14 Jan 2023 01:28:13 -0800 Subject: [PATCH 0143/2222] Added amount of available clothing to autolabor reports. --- plugins/autoclothing.cpp | 142 ++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 15 deletions(-) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index 8cbbf203f..4b795c02f 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -15,6 +15,15 @@ #include "modules/World.h" #include "modules/Translation.h" +#include "df/item.h" +#include "df/item_actual.h" +#include "df/item_crafted.h" +#include "df/item_constructed.h" +#include "df/item_armorst.h" +#include "df/item_glovesst.h" +#include "df/item_shoesst.h" +#include "df/item_helmst.h" +#include "df/item_pantsst.h" #include "df/itemdef_armorst.h" #include "df/itemdef_glovesst.h" #include "df/itemdef_shoesst.h" @@ -57,6 +66,8 @@ static void do_autoclothing(); static bool validateMaterialCategory(ClothingRequirement * requirement); static bool setItem(std::string name, ClothingRequirement* requirement); static void generate_report(color_ostream& out); +static bool isAvailableItem(df::item* item); + std::vectorclothingOrders; @@ -683,6 +694,43 @@ static void list_unit_counts(color_ostream& out, std::map& unitList) } } +static bool isAvailableItem(df::item* item) +{ + if (item->flags.bits.in_job) + return false; + if (item->flags.bits.hostile) + return false; + if (item->flags.bits.in_building) + return false; + if (item->flags.bits.in_building) + return false; + if (item->flags.bits.encased) + return false; + if (item->flags.bits.foreign) + return false; + if (item->flags.bits.trader) + return false; + if (item->flags.bits.owned) + return false; + if (item->flags.bits.artifact) + return false; + if (item->flags.bits.forbid) + return false; + if (item->flags.bits.dump) + return false; + if (item->flags.bits.on_fire) + return false; + if (item->flags.bits.melt) + return false; + if (item->flags.bits.hidden) + return false; + if (item->getWear() > 1) + return false; + if (!item->isClothing()) + return false; + return true; +} + static void generate_report(color_ostream& out) { std::map fullUnitList; @@ -741,29 +789,93 @@ static void generate_report(color_ostream& out) out << "Everybody has a full set of clothes to wear, congrats!" << endl; return; } - if (missingArmor.size()) + else + { + if (missingArmor.size()) + { + out << "Following units need new bodywear:" << endl; + list_unit_counts(out, missingArmor); + } + if (missingShoes.size()) + { + out << "Following units need new shoes:" << endl; + list_unit_counts(out, missingShoes); + } + if (missingHelms.size()) + { + out << "Following units need new headwear:" << endl; + list_unit_counts(out, missingHelms); + } + if (missingGloves.size()) + { + out << "Following units need new handwear:" << endl; + list_unit_counts(out, missingGloves); + } + if (missingPants.size()) + { + out << "Following units need new legwear:" << endl; + list_unit_counts(out, missingPants); + } + } + std::map availableArmor; + for (auto armor : world->items.other.ARMOR) { - out << "Following units need new bodywear:" << endl; - list_unit_counts(out, missingArmor); + if (!isAvailableItem(armor)) + continue; + availableArmor[armor->maker_race]++; } - if (missingShoes.size()) + if (availableArmor.size()) { - out << "Following units need new shoes:" << endl; - list_unit_counts(out, missingShoes); + out << "We have available bodywear for:" << endl; + list_unit_counts(out, availableArmor); } - if (missingHelms.size()) + std::map availableShoes; + for (auto shoe : world->items.other.SHOES) { - out << "Following units need new headwear:" << endl; - list_unit_counts(out, missingHelms); + if (!isAvailableItem(shoe)) + continue; + availableShoes[shoe->maker_race]++; } - if (missingGloves.size()) + if (availableShoes.size()) { - out << "Following units need new handwear:" << endl; - list_unit_counts(out, missingGloves); + out << "We have available footwear for:" << endl; + list_unit_counts(out, availableShoes); } - if (missingPants.size()) + std::map availableHelms; + for (auto helm : world->items.other.HELM) { - out << "Following units need new legwear:" << endl; - list_unit_counts(out, missingPants); + if (!isAvailableItem(helm)) + continue; + availableHelms[helm->maker_race]++; } + if (availableHelms.size()) + { + out << "We have available headwear for:" << endl; + list_unit_counts(out, availableHelms); + } + std::map availableGloves; + for (auto glove : world->items.other.HELM) + { + if (!isAvailableItem(glove)) + continue; + availableGloves[glove->maker_race]++; + } + if (availableGloves.size()) + { + out << "We have available handwear for:" << endl; + list_unit_counts(out, availableGloves); + } + std::map availablePants; + for (auto pants : world->items.other.HELM) + { + if (!isAvailableItem(pants)) + continue; + availablePants[pants->maker_race]++; + } + if (availablePants.size()) + { + out << "We have available legwear for:" << endl; + list_unit_counts(out, availablePants); + } + } From dbc26f0d37863e60dea4d3e3f96029a230909856 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 12 Jan 2023 17:14:43 -0800 Subject: [PATCH 0144/2222] enable next batch of plugins to test --- plugins/CMakeLists.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1166caae7..a09f453db 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -84,7 +84,7 @@ dfhack_plugin(autofarm autofarm.cpp) #add_subdirectory(autolabor) #dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) #dfhack_plugin(automelt automelt.cpp) -#dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) +dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) @@ -94,12 +94,12 @@ dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) dfhack_plugin(changelayer changelayer.cpp) dfhack_plugin(changevein changevein.cpp) #add_subdirectory(channel-safely) -#dfhack_plugin(cleanconst cleanconst.cpp) -#dfhack_plugin(cleaners cleaners.cpp) +dfhack_plugin(cleanconst cleanconst.cpp) +dfhack_plugin(cleaners cleaners.cpp) dfhack_plugin(cleanowned cleanowned.cpp) #dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) #dfhack_plugin(createitem createitem.cpp) -#dfhack_plugin(cursecheck cursecheck.cpp) +dfhack_plugin(cursecheck cursecheck.cpp) #dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) #dfhack_plugin(deramp deramp.cpp) dfhack_plugin(debug debug.cpp LINK_LIBRARIES jsoncpp_static) @@ -113,7 +113,7 @@ dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) #dfhack_plugin(embark-tools embark-tools.cpp) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) dfhack_plugin(fastdwarf fastdwarf.cpp) -#dfhack_plugin(filltraffic filltraffic.cpp) +dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fixveins fixveins.cpp) #dfhack_plugin(flows flows.cpp) @@ -122,7 +122,7 @@ dfhack_plugin(fastdwarf fastdwarf.cpp) #dfhack_plugin(generated-creature-renamer generated-creature-renamer.cpp) #dfhack_plugin(getplants getplants.cpp) dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) -#dfhack_plugin(infiniteSky infiniteSky.cpp) +dfhack_plugin(infiniteSky infiniteSky.cpp) #dfhack_plugin(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) #dfhack_plugin(jobutils jobutils.cpp) #dfhack_plugin(lair lair.cpp) @@ -130,10 +130,10 @@ dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) #dfhack_plugin(manipulator manipulator.cpp) #dfhack_plugin(map-render map-render.cpp LINK_LIBRARIES lua) -#dfhack_plugin(misery misery.cpp) +dfhack_plugin(misery misery.cpp) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) -#dfhack_plugin(nestboxes nestboxes.cpp) +dfhack_plugin(nestboxes nestboxes.cpp) dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) @@ -149,7 +149,7 @@ add_subdirectory(remotefortressreader) dfhack_plugin(reveal reveal.cpp LINK_LIBRARIES lua) #dfhack_plugin(search search.cpp) dfhack_plugin(seedwatch seedwatch.cpp) -#dfhack_plugin(showmood showmood.cpp) +dfhack_plugin(showmood showmood.cpp) #dfhack_plugin(siege-engine siege-engine.cpp LINK_LIBRARIES lua) #dfhack_plugin(sort sort.cpp LINK_LIBRARIES lua) #dfhack_plugin(steam-engine steam-engine.cpp) @@ -158,7 +158,7 @@ dfhack_plugin(seedwatch seedwatch.cpp) #add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(strangemood strangemood.cpp) -#dfhack_plugin(tailor tailor.cpp) +dfhack_plugin(tailor tailor.cpp) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-version title-version.cpp) @@ -166,7 +166,7 @@ dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(tubefill tubefill.cpp) #add_subdirectory(tweak) #dfhack_plugin(workflow workflow.cpp LINK_LIBRARIES lua) -#dfhack_plugin(workNow workNow.cpp) +dfhack_plugin(workNow workNow.cpp) dfhack_plugin(xlsxreader xlsxreader.cpp LINK_LIBRARIES lua xlsxio_read_STATIC zip expat) #dfhack_plugin(zone zone.cpp) From 9606f7bf4fdacdeeb56de37417c3f4e020379153 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 09:50:17 -0800 Subject: [PATCH 0145/2222] remove ones that don't work, mark as tested those that do --- docs/plugins/RemoteFortressReader.rst | 2 +- docs/plugins/cleaners.rst | 2 +- docs/plugins/cursecheck.rst | 2 +- docs/plugins/filltraffic.rst | 2 +- docs/plugins/misery.rst | 2 +- docs/plugins/nestboxes.rst | 2 +- plugins/CMakeLists.txt | 8 ++++---- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/plugins/RemoteFortressReader.rst b/docs/plugins/RemoteFortressReader.rst index 9b45452bc..c70adc608 100644 --- a/docs/plugins/RemoteFortressReader.rst +++ b/docs/plugins/RemoteFortressReader.rst @@ -3,7 +3,7 @@ RemoteFortressReader .. dfhack-tool:: :summary: Backend for Armok Vision. - :tags: untested dev graphics + :tags: dev graphics :no-command: .. dfhack-command:: RemoteFortressReader_version diff --git a/docs/plugins/cleaners.rst b/docs/plugins/cleaners.rst index dd484f5df..14a25d62f 100644 --- a/docs/plugins/cleaners.rst +++ b/docs/plugins/cleaners.rst @@ -6,7 +6,7 @@ cleaners .. dfhack-tool:: :summary: Provides commands for cleaning spatter from the map. - :tags: untested adventure fort armok fps items map units + :tags: adventure fort armok fps items map units :no-command: .. dfhack-command:: clean diff --git a/docs/plugins/cursecheck.rst b/docs/plugins/cursecheck.rst index fd97ee252..94012919b 100644 --- a/docs/plugins/cursecheck.rst +++ b/docs/plugins/cursecheck.rst @@ -3,7 +3,7 @@ cursecheck .. dfhack-tool:: :summary: Check for cursed creatures. - :tags: untested fort armok inspection units + :tags: fort armok inspection units This command checks a single map tile (or the whole map/world) for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies, etc.). diff --git a/docs/plugins/filltraffic.rst b/docs/plugins/filltraffic.rst index f92549dee..beb57dccd 100644 --- a/docs/plugins/filltraffic.rst +++ b/docs/plugins/filltraffic.rst @@ -6,7 +6,7 @@ filltraffic .. dfhack-tool:: :summary: Set traffic designations using flood-fill starting at the cursor. - :tags: untested fort design productivity map + :tags: fort design productivity map .. dfhack-command:: alltraffic :summary: Set traffic designations for every single tile of the map. diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index 38c2b23b4..f2c4d1952 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -3,7 +3,7 @@ misery .. dfhack-tool:: :summary: Increase the intensity of negative dwarven thoughts. - :tags: untested fort armok auto units + :tags: fort armok auto units When enabled, negative thoughts that your dwarves have will multiply by the specified factor. diff --git a/docs/plugins/nestboxes.rst b/docs/plugins/nestboxes.rst index 91d83aa1a..3c07cc30c 100644 --- a/docs/plugins/nestboxes.rst +++ b/docs/plugins/nestboxes.rst @@ -3,7 +3,7 @@ nestboxes .. dfhack-tool:: :summary: Protect fertile eggs incubating in a nestbox. - :tags: untested fort auto animals + :tags: fort auto animals :no-command: This plugin will automatically scan for and forbid fertile eggs incubating in a diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index a09f453db..661fa2624 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -84,7 +84,7 @@ dfhack_plugin(autofarm autofarm.cpp) #add_subdirectory(autolabor) #dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) #dfhack_plugin(automelt automelt.cpp) -dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) +#dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) @@ -122,7 +122,7 @@ dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(generated-creature-renamer generated-creature-renamer.cpp) #dfhack_plugin(getplants getplants.cpp) dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) -dfhack_plugin(infiniteSky infiniteSky.cpp) +#dfhack_plugin(infiniteSky infiniteSky.cpp) #dfhack_plugin(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) #dfhack_plugin(jobutils jobutils.cpp) #dfhack_plugin(lair lair.cpp) @@ -158,7 +158,7 @@ dfhack_plugin(showmood showmood.cpp) #add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(strangemood strangemood.cpp) -dfhack_plugin(tailor tailor.cpp) +#dfhack_plugin(tailor tailor.cpp) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-version title-version.cpp) @@ -166,7 +166,7 @@ dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(tubefill tubefill.cpp) #add_subdirectory(tweak) #dfhack_plugin(workflow workflow.cpp LINK_LIBRARIES lua) -dfhack_plugin(workNow workNow.cpp) +#dfhack_plugin(workNow workNow.cpp) dfhack_plugin(xlsxreader xlsxreader.cpp LINK_LIBRARIES lua xlsxio_read_STATIC zip expat) #dfhack_plugin(zone zone.cpp) From e7696b1f0362e2422127caaefe047bee20c7f059 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Fri, 13 Jan 2023 21:02:33 -0800 Subject: [PATCH 0146/2222] Allow symlinked dfhack-run This is useful for symlinking this script somewhere on your $PATH, and having it work without having to find the correct directory. --- package/linux/dfhack-run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/linux/dfhack-run b/package/linux/dfhack-run index 55001cfcb..b5e6ccfe2 100755 --- a/package/linux/dfhack-run +++ b/package/linux/dfhack-run @@ -1,6 +1,6 @@ #!/bin/sh -DF_DIR=$(dirname "$0") +DF_DIR=$(dirname "$(readlink -f "$0")") cd "${DF_DIR}" export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./hack/libs":"./hack" From 1eb5dd9a4f428cc851272b683146254de5872b27 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 18:30:44 -0800 Subject: [PATCH 0147/2222] fix incorrect function name for setting targets --- plugins/lua/autochop.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/autochop.lua b/plugins/lua/autochop.lua index e6f8e7008..6b8c9f943 100644 --- a/plugins/lua/autochop.lua +++ b/plugins/lua/autochop.lua @@ -63,7 +63,7 @@ function parse_commandline(...) elseif command == 'undesignate' then autochop_undesignate() elseif command == 'target' then - setTarget(args[2], args[3]) + setTargets(args[2], args[3]) elseif command == 'chop' then do_set_burrow_config('chop', true, args[2]) elseif command == 'nochop' then From cd114603f4fcb02db56d83fac5cf18a4f01b2140 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 14 Jan 2023 21:34:25 -0500 Subject: [PATCH 0148/2222] pre-commit: fix path to Authors.rst --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6b23e9cdd..669a08db9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,6 +39,6 @@ repos: name: Check Authors.rst language: python entry: python3 ci/authors-rst.py - files: docs/Authors\.rst + files: docs/about/Authors\.rst pass_filenames: false exclude: '^(depends/|data/.*\.json$|.*\.diff$)' From b0d6f6a3c39c8868c27a26f92d87be8f81ae4ab8 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 14 Jan 2023 21:48:50 -0500 Subject: [PATCH 0149/2222] Update Authors.rst Missing authors since 0.47.05-r5 --- docs/about/Authors.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index 280594674..b0031607a 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -13,6 +13,7 @@ Name Github Other Abel abstern acwatkins acwatkins Alexander Gavrilov angavrilov ag +Amber Brown hawkowl Amostubal Amostubal Andrea Cattaneo acattaneo88 AndreasPK AndreasPK @@ -46,10 +47,12 @@ David Corbett dscorbett David Seguin dseguin David Timm dtimm Deon +dikbut Tjudge1 DoctorVanGogh DoctorVanGogh Donald Ruegsegger hashaash doomchild doomchild DwarvenM DwarvenM +EarthPulseAcademy EarthPulseAcademy ElMendukol ElMendukol enjia2000 Eric Wald eswald @@ -66,8 +69,10 @@ Guilherme Abraham GuilhermeAbraham Harlan Playford playfordh Hayati Ayguen hayguen Herwig Hochleitner bendlas +Hevlikn Hevlikn Ian S kremlin- IndigoFenix +James 20k James Gilles kazimuth James Logsdon jlogsdon Jared Adams @@ -80,14 +85,18 @@ Joel Meador janxious John Beisley huin John Shade gsvslto Jonas Ask +Jonathan Clark AridTag Josh Cooper cppcooper coope +jowario jowario kane-t kane-t Kelly Kinkade ab9rf +Kib Arekatír arekatir KlonZK KlonZK Kris Parker kaypy Kristjan Moore kristjanmoore Kromtec Kromtec Kurik Amudnil +Kévin Boissonneault KABoissonneault Lethosor lethosor LordGolias LordGolias Mark Nielson pseudodragon @@ -111,6 +120,7 @@ Milo Christiansen milochristiansen MithrilTuxedo MithrilTuxedo mizipzor mizipzor moversti moversti +Murad Beybalaev Erquint Myk Taylor myk002 napagokc napagokc Neil Little nmlittle @@ -119,7 +129,10 @@ Nicolas Ayala nicolasayala Nik Nyby nikolas Nikolay Amiantov abbradar nocico nocico +NotRexButCaesar NotRexButCaesar +Nuno Fernandes UnknowableCoder Omniclasm +oorzkws oorzkws OwnageIsMagic OwnageIsMagic palenerd dlmarquis PassionateAngler PassionateAngler @@ -133,6 +146,7 @@ Pierre-David Bélanger pierredavidbelanger potato Priit Laes plaes Putnam Putnam3145 +quarque2 quarque2 Quietust quietust _Q RafaÅ‚ Karczmarczyk CarabusX Raidau Raidau @@ -145,6 +159,7 @@ reverb Rich Rauenzahn rrauenza Rinin Rinin rndmvar rndmvar +Rob Bailey actionninja Robert Heinrich rh73 Robert Janetzko robertjanetzko Rocco Moretti roccomoretti @@ -165,8 +180,10 @@ scamtank scamtank Sebastian Wolfertz Enkrod seishuuu seishuuu Seth Woodworth sethwoodworth +Shim Panze Shim-Panze simon Simon Jackson sizeak +Simon Lees simotek stolencatkarma Stoyan Gaydarov sgayda2 Su Moth-Tolias @@ -198,6 +215,7 @@ Vjek vjek Warmist warmist Wes Malone wesQ3 Will Rogers wjrogers +WoosterUK WoosterUK ZechyW ZechyW Zhentar Zhentar zilpin zilpin From 6a107ba68b4388ec7093ac204f1138d391843078 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 14 Jan 2023 21:48:50 -0500 Subject: [PATCH 0150/2222] Update links to compilation docs from remaining pages Ref #2517 --- docs/Installing.rst | 4 ++-- index.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Installing.rst b/docs/Installing.rst index 05db198b8..bc58a39b7 100644 --- a/docs/Installing.rst +++ b/docs/Installing.rst @@ -50,7 +50,7 @@ The GCC 4.8 build is built on Ubuntu 14.04 and targets an older glibc, so it should work on older distributions. In the event that none of the provided binaries work on your distribution, -you may need to `compile DFHack from source `. +you may need to `compile DFHack from source `. macOS ----- @@ -86,7 +86,7 @@ restrictions apply (e.g. a file named ``Windows64`` is for 64-bit Windows DF). or by clicking "Download ZIP" on the repo homepage. This will give you an incomplete copy of the DFHack source code, which will not work as-is. (If you want to compile DFHack instead of using a pre-built release, see - `compile` for instructions.) + `building-dfhack-index` for instructions.) Installing DFHack ================= diff --git a/index.rst b/index.rst index af0f76052..cf808a59e 100644 --- a/index.rst +++ b/index.rst @@ -17,7 +17,7 @@ Quick Links * `Installation guide ` * `Getting help ` * :source:`Source code <>` - (**important:** read `compile` before attempting to build from source.) + (**important:** read `building-dfhack-index` before attempting to build from source.) User Manual =========== From 698c467f6ad059f0a18a9cf4cafcd21bdf81d398 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 23:56:30 -0800 Subject: [PATCH 0151/2222] add quickstart help --- docs/Quickstart.rst | 167 ++++++++++++++++++++++++++++++++++++++++++++ index.rst | 2 + 2 files changed, 169 insertions(+) create mode 100644 docs/Quickstart.rst diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst new file mode 100644 index 000000000..c61658f35 --- /dev/null +++ b/docs/Quickstart.rst @@ -0,0 +1,167 @@ +.. _quickstart: + +Quickstart guide +================ + +Welcome to DFHack! This guide will help get you oriented with the DFHack system +and teach you how to find and use the tools productively. If you're reading this +in `dfhack-quickstart-guide`, hit the right arrow key or click on the hotkey +hint in the lower right corner of the window to go to the next page. + +What is DFHack? +--------------- + +DFHack is a framework for Dwarf Fortress that provides a unified, cross-platform +environment that enables mods and tools to significantly extend the game. The +default DFHack distribution contains a variety of tools, including bugfixes, +interface improvements, automation agents, design blueprints, modding tools, and +more. Third-party tools (e.g. mods downloaded from Steam Workshop or the forums) +can also seamlessly integrate with the DFHack framework and extend the game far +beyond what can be done by just modding the raws. + +DFHack's mission is to provide tools and interfaces for players and modders to: +- expand the bounds of what is possible in Dwarf Fortress +- reduce the impact of game bugs +- give the player more agency and control over the game +- provide alternatives to toilsome or frustrating aspects of gameplay +- overall, make the game more fun + +What can I do with DFHack tools? +-------------------------------- + +DFHack has been around for a long time -- almost as long as Dwarf Fortress +itself. Many of the game's rough edges have been smoothed with DFHack tools. +Here are some common tasks people use DFHack tools to accomplish: + +- Automatically chop trees when log stocks are low +- Record fortress layouts in blueprint files and later play them back in + different forts +- Import and export lists of manager orders +- Clean contaminants from map squares that dwarves can't reach +- Automatically butcher excess livestock so you don't become overrun with + animals +- Promote time-sensitive job types (e.g. food hauling) so they are done + expediently +- Quickly scan the map for visible ores of specific types so you can focus + your mining + +Some tools are one-shot commands. For example, you can run `unforbid all ` +to claim all items on the map after a messy siege. + +Other tools must be `enabled ` and then they will run in the background. +For example, `enable seedwatch ` will start monitoring your stocks of +seeds and preventing your chefs from cooking seeds that you need for planting. +Enableable tools that affect a fort save their state with the fort and will +remember that they are enabled the next time you load your save. + +Other tools add information to the screen or provide new integrated functionality +via the DFHack `overlay` framework. For example, the `unsuspend` tool, in addition +to its basic function of unsuspending all building construction jobs, can also +overlay a marker on suspended buildings to indicate that they are suspended (and +will use different markers to tell you whether this is a problem). These overlays +can be configured with the `gui/overlay` tool. + +How can I figure out which commands to run? +------------------------------------------- + +There are several ways to scan DFHack tools and find the one you need right now. + +The first place to check is the DFHack logo hover hotspot. It's in the upper +left corner of the screen by default, though you can move it anywhere you want +with the `gui/overlay` tool. + +When you hover the mouse over the logo (or hit the :kbd:`Ctrl`:kbd:`Shift`:kbd:`C` +keyboard shortcut) a list of DFHack tools relevant to the current context comes up. +For example, when you have a unit selected, the hotspot will show a list of tools +that inspect units, allow you to edit them, or maybe even teleport them. Next to +each tool, you'll see the global hotkey you can hit to invoke the command without +even opening the hover list. + +You can run any DFHack tool from `gui/launcher`, which is always listed first in +the hover list. You can also bring up the launcher by tapping the backtick key +(\`) or hitting :kbd:`Ctrl`:kbd:`Shift`:kbd:`D`. In the launcher, you can quickly +autocomplete any command name by selecting it in the list on the right side of +the window. Commands are ordered by how often you run them, so your favorite +commands will always be on top. You can also pull full commandlines out of your +history with :kbd:`Alt`:kbd:`S` (or by clicking on the "history search" hotkey hint). + +Once you have typed (or autocompleted, or searched for) a command name, other +commands related to the one you have selected will appear in the autocomplete list. +Scanning through the related tools is a great way to learn about new tools that +you might find useful. You can also see how commands are grouped by running the +`tags` command. + +The bottom panel will show the full help text for the command you are running, +allowing you to refer to the usage documentation and examples when you are typing +your command. + +How do DFHack in-game windows work? +----------------------------------- + +Many DFHack tools have graphical interfaces that appear in-game. You can tell +which windows belong to DFHack tools because they will have the word "DFHack" +printed across their bottom frame. DFHack provides a custom windowing system +that gives the player a lot of control over where the windows appear and whether +they capture keyboard and mouse input. + +The DFHack windowing system allows you to use DFHack tools without interrupting +the game. That is, if the game is unpaused, it will continue to run while a +DFHack window is open. You can also interact with the map, scrolling it with the +keyboard or mouse and selecting units, buildings, and items. Some tools will +force-pause the game if it make sense to. + +DFHack windows are draggable from the title bar or from anywhere on the window +that doesn't have a mouse-clickable widget on it. Many are resizable as well +(if the tool window has components that can be reasonably resized). DFHack windows +close with :kbd:`Esc` or a right mouse click, but if you want to keep a DFHack +tool open while you interact with the game, you can hit :kbd:`Alt`:kbd:`L` or +click the pin in the upper right corner of the DFHack window so that it turns +green. The DFHack window will then ignore :kbd:`Esc` button presses and right +clicks that would otherwise close the window. Note that you can still right +click *on* the DFHack tool window to close it, even when it is pinned. + +You can have multiple DFHack tool windows on the screen at the same time. The +one that is receiving keyboard input has a highlighted title bar and will appear +over other windows if dragged over them. Clicking on a DFHack window that is not +currently active will bring it to the foreground and make it the active window. + +Where do I go next? +------------------- + +To recap: + +You can get to popular, relevant tools for the current context by hovering +the mouse over DFHack logo or by hitting :kbd:`Ctrl`:kbd:`Shift`:kbd:`C`. + +You can get to the launcher and its integrated autocomplete, history search, +and help text by hitting backtick (\`) or :kbd:`Ctrl`:kbd:`Shift`:kbd:`D`, +or, of course, by running it from the logo hover list. + +You can list and start tools that run in the background with the `enable` +command. + +You can configure screen overlays with the `gui/overlay` tool. + +With those four tools, you have the complete DFHack tool suite at your +fingertips. So what to run first? Here are a few commands to get you started. +You can run them from the launcher. + +First, let's import some useful manager orders to keep your fort stocked with +basic necessities. Run ``orders import library/basic``. If you go to your +mangager orders screen, you can see all the orders that have been created for you. + +Next, try setting up `autochop` by running the GUI configuration `gui/autochop`. +You can enable it from the GUI, so you don't need to run `enable autochop ` +directly. You can set a target number of logs, and let autochop will manage +your logging industry for you. You can control where your woodsdwarves go to +cut down trees by setting up burrows and configuring autochop to only cut in +those burrows. + +Finally, let's set up a water supply for your fort with `gui/quickfort`. Launching +`gui/quickfort` will give you a list of blueprints you can load. Type in ``aquifer_tap`` +to filter for just those blueprints. Select the ``aquifer_tap -n /help`` blueprint +to see the instructions for how to build an aquifer tap. Then, go back and load the +``aquifer_tap -n /dig`` blueprint, find some space in a light aquifer layer, and +apply the blueprint there. It was that easy! + +There are many more tools to explore. Have fun! diff --git a/index.rst b/index.rst index af0f76052..2792e2d9a 100644 --- a/index.rst +++ b/index.rst @@ -15,6 +15,7 @@ Quick Links * `Downloads `_ * `Installation guide ` +* `quickstart` * `Getting help ` * :source:`Source code <>` (**important:** read `compile` before attempting to build from source.) @@ -26,6 +27,7 @@ User Manual :maxdepth: 2 /docs/Introduction + /docs/Quickstart /docs/Installing /docs/Core /docs/Tools From 121ab059c8628f9988681b841f3e0b77dc506b18 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 23:58:18 -0800 Subject: [PATCH 0152/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 08aa39951..64ae81399 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,6 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Documentation - `overlay-dev-guide`: added troubleshooting tips and common development workflows - added DFHack architecture diagrams to the dev intro +- added DFHack Quickstart guide ## API - ``Gui::getDwarfmodeDims``: now only returns map viewport dimensions; menu dimensions are obsolete From ab52d7a4ca19e5c08fbb0329082620b38a66b43e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 14 Jan 2023 23:59:01 -0800 Subject: [PATCH 0153/2222] remove extra whitespace --- docs/Quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index c61658f35..ac7309d9a 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -19,7 +19,7 @@ more. Third-party tools (e.g. mods downloaded from Steam Workshop or the forums) can also seamlessly integrate with the DFHack framework and extend the game far beyond what can be done by just modding the raws. -DFHack's mission is to provide tools and interfaces for players and modders to: +DFHack's mission is to provide tools and interfaces for players and modders to: - expand the bounds of what is possible in Dwarf Fortress - reduce the impact of game bugs - give the player more agency and control over the game From 22e0f4325c18702ea6ea0431ea81b26c7901f6eb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 00:49:11 -0800 Subject: [PATCH 0154/2222] wording --- docs/Quickstart.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index ac7309d9a..0e7f82c55 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -34,8 +34,7 @@ itself. Many of the game's rough edges have been smoothed with DFHack tools. Here are some common tasks people use DFHack tools to accomplish: - Automatically chop trees when log stocks are low -- Record fortress layouts in blueprint files and later play them back in - different forts +- Record blueprint files that allow copy and paste of fort designs - Import and export lists of manager orders - Clean contaminants from map squares that dwarves can't reach - Automatically butcher excess livestock so you don't become overrun with @@ -54,12 +53,12 @@ seeds and preventing your chefs from cooking seeds that you need for planting. Enableable tools that affect a fort save their state with the fort and will remember that they are enabled the next time you load your save. -Other tools add information to the screen or provide new integrated functionality -via the DFHack `overlay` framework. For example, the `unsuspend` tool, in addition -to its basic function of unsuspending all building construction jobs, can also -overlay a marker on suspended buildings to indicate that they are suspended (and -will use different markers to tell you whether this is a problem). These overlays -can be configured with the `gui/overlay` tool. +And still other tools add information to the screen or provide new integrated +functionality via the DFHack `overlay` framework. For example, the `unsuspend` +tool, in addition to its basic function of unsuspending all building construction +jobs, can also overlay a marker on suspended buildings to indicate that they are +suspended (and will use different markers to tell you whether this is a problem). +These overlays can be configured with the `gui/overlay` tool. How can I figure out which commands to run? ------------------------------------------- From d14a3b2c8312ed42f74a85c6c4b4b52c9c0b9d19 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 00:49:30 -0800 Subject: [PATCH 0155/2222] ensure the untested warning doesn't bork on bad scripts --- library/lua/dfhack.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 26794eedf..df6302b82 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -698,11 +698,14 @@ local valid_script_flags = { local warned_scripts = {} function dfhack.run_script(name,...) - if not warned_scripts[name] and require('helpdb').get_entry_tags(name).untested then - warned_scripts[name] = true - dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) - dfhack.printerr('It may not work as expected, or it may corrupt your game.') - qerror('Please run the command again to ignore this warning and proceed.') + if not warned_scripts[name] then + local helpdb = require('helpdb') + if helpdb.is_entry(name) and helpdb.get_entry_tags(name).untested then + warned_scripts[name] = true + dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) + dfhack.printerr('It may not work as expected, or it may corrupt your game.') + qerror('Please run the command again to ignore this warning and proceed.') + end end return dfhack.run_script_with_env(nil, name, nil, ...) From de8815c85c2fc72dcacd03aa9efc944cf0a7783d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 10:39:06 -0800 Subject: [PATCH 0156/2222] apply suggestions from the good people on discord --- docs/Quickstart.rst | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 0e7f82c55..ed2707c45 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -107,17 +107,22 @@ The DFHack windowing system allows you to use DFHack tools without interrupting the game. That is, if the game is unpaused, it will continue to run while a DFHack window is open. You can also interact with the map, scrolling it with the keyboard or mouse and selecting units, buildings, and items. Some tools will -force-pause the game if it make sense to. +force-pause the game if it makes sense to, like `gui/quickfort`, since you cannot +interact with the map normally while placing a blueprint. DFHack windows are draggable from the title bar or from anywhere on the window that doesn't have a mouse-clickable widget on it. Many are resizable as well -(if the tool window has components that can be reasonably resized). DFHack windows -close with :kbd:`Esc` or a right mouse click, but if you want to keep a DFHack -tool open while you interact with the game, you can hit :kbd:`Alt`:kbd:`L` or -click the pin in the upper right corner of the DFHack window so that it turns -green. The DFHack window will then ignore :kbd:`Esc` button presses and right -clicks that would otherwise close the window. Note that you can still right -click *on* the DFHack tool window to close it, even when it is pinned. +(if the tool window has components that can be reasonably resized). + +DFHack windows close with a right mouse click or keyboard :kbd:`Esc`, but if you +want to keep a DFHack tool open while you interact with the game, you can click the +pin in the upper right corner of the DFHack window or hit :kbd:`Alt`:kbd:`L` so +that the pin turns green. The DFHack window will then ignore right clicks and +:kbd:`Esc` key presses that would otherwise close the window. This is especially +useful for the configuration tool windows for the automation tools. For example, +you can pin the `gui/autochop` window and let it sit there monitoring your +logging industry as you play, using it as a live status window. Note that you can +still right click *on* the DFHack tool window to close it, even when it is pinned. You can have multiple DFHack tool windows on the screen at the same time. The one that is receiving keyboard input has a highlighted title bar and will appear From caf533c3c3e3e7aec6b0de8580441a0be150e7d5 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 15 Jan 2023 21:38:11 +0000 Subject: [PATCH 0157/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 8064f7160..a370b2ff6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 8064f71601664fb841f830850ce390092e685276 +Subproject commit a370b2ff60988e832af5955ae4288d4118a9a26e From b855170c64a3f4595297ed9f9bd14438afb7e6d2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 13:42:26 -0800 Subject: [PATCH 0158/2222] fix typos in quickstart guide --- docs/Quickstart.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index ed2707c45..f9e929b14 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -20,6 +20,7 @@ can also seamlessly integrate with the DFHack framework and extend the game far beyond what can be done by just modding the raws. DFHack's mission is to provide tools and interfaces for players and modders to: + - expand the bounds of what is possible in Dwarf Fortress - reduce the impact of game bugs - give the player more agency and control over the game @@ -49,7 +50,7 @@ to claim all items on the map after a messy siege. Other tools must be `enabled ` and then they will run in the background. For example, `enable seedwatch ` will start monitoring your stocks of -seeds and preventing your chefs from cooking seeds that you need for planting. +seeds and prevent your chefs from cooking seeds that you need for planting. Enableable tools that affect a fort save their state with the fort and will remember that they are enabled the next time you load your save. @@ -135,7 +136,7 @@ Where do I go next? To recap: You can get to popular, relevant tools for the current context by hovering -the mouse over DFHack logo or by hitting :kbd:`Ctrl`:kbd:`Shift`:kbd:`C`. +the mouse over the DFHack logo or by hitting :kbd:`Ctrl`:kbd:`Shift`:kbd:`C`. You can get to the launcher and its integrated autocomplete, history search, and help text by hitting backtick (\`) or :kbd:`Ctrl`:kbd:`Shift`:kbd:`D`, @@ -156,7 +157,7 @@ mangager orders screen, you can see all the orders that have been created for yo Next, try setting up `autochop` by running the GUI configuration `gui/autochop`. You can enable it from the GUI, so you don't need to run `enable autochop ` -directly. You can set a target number of logs, and let autochop will manage +directly. You can set a target number of logs, and autochop will manage your logging industry for you. You can control where your woodsdwarves go to cut down trees by setting up burrows and configuring autochop to only cut in those burrows. From 20f865eba0a5f9748aa191b083495291897e0c07 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 15:59:09 -0800 Subject: [PATCH 0159/2222] don't reset scroll pos when window is resized or moved --- docs/changelog.txt | 1 + library/lua/gui/widgets.lua | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 64ae81399..0deac83e8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- ``widgets.WrappedLabel``: no longer resets scroll position when window is moved or resized ## Misc Improvements - Scrollable widgets now react to mouse wheel events when the mouse is over the widget diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index e3cdfc808..73de5ad4d 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1366,6 +1366,10 @@ function WrappedLabel:getWrappedText(width) return text_to_wrap:wrap(width - self.indent) end +function WrappedLabel:preUpdateLayout() + self.saved_start_line_num = self.start_line_num +end + -- we can't set the text in init() since we may not yet have a frame that we -- can get wrapping bounds from. function WrappedLabel:postComputeFrame() @@ -1378,6 +1382,7 @@ function WrappedLabel:postComputeFrame() table.insert(text, NEWLINE) end self:setText(text) + self:scroll(self.saved_start_line_num - 1) end ------------------ From 65ceb565f89327fb2bac3dffd751eb4e3d76d735 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 16:30:55 -0800 Subject: [PATCH 0160/2222] rename dfhack-quickstart-guide -> quickstart-guide --- docs/Quickstart.rst | 4 ++-- scripts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index f9e929b14..31aadfd3f 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -5,8 +5,8 @@ Quickstart guide Welcome to DFHack! This guide will help get you oriented with the DFHack system and teach you how to find and use the tools productively. If you're reading this -in `dfhack-quickstart-guide`, hit the right arrow key or click on the hotkey -hint in the lower right corner of the window to go to the next page. +in `quickstart-guide`, hit the right arrow key or click on the hotkey hint in +the lower right corner of the window to go to the next page. What is DFHack? --------------- diff --git a/scripts b/scripts index a370b2ff6..11d5ac853 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a370b2ff60988e832af5955ae4288d4118a9a26e +Subproject commit 11d5ac853dbf708e994323e4e1b78a2298c977fe From 86ddf43230d2d9d249422c161fa12f998b1b1f21 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 20:17:23 -0800 Subject: [PATCH 0161/2222] add hotkey for launching the quickstart guide from the hover menu --- plugins/lua/hotkeys.lua | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 2fb32a692..2ec38fff6 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -140,12 +140,18 @@ function Menu:init() local choices,list_width = get_choices(hotkeys, bindings, is_inverted) local list_frame = copyall(self.hotspot_frame) + local list_widget_frame = {h=math.min(#choices, MAX_LIST_HEIGHT)} + local quickstart_frame = {} list_frame.w = list_width + 2 - list_frame.h = math.min(#choices, MAX_LIST_HEIGHT) + 2 + list_frame.h = list_widget_frame.h + 4 if list_frame.t then list_frame.t = math.max(0, list_frame.t - 1) + list_widget_frame.t = 0 + quickstart_frame.b = 0 else list_frame.b = math.max(0, list_frame.b - 1) + list_widget_frame.b = 0 + quickstart_frame.t = 0 end if list_frame.l then list_frame.l = math.max(0, list_frame.l + 5) @@ -161,21 +167,30 @@ function Menu:init() end self:addviews{ - widgets.ResizingPanel{ + widgets.Panel{ view_id='list_panel', - autoarrange_subviews=true, frame=list_frame, frame_style=gui.GREY_LINE_FRAME, frame_background=gui.CLEAR_PEN, subviews={ widgets.List{ view_id='list', + frame=list_widget_frame, choices=choices, icon_width=2, on_select=self:callback('onSelect'), on_submit=self:callback('onSubmit'), on_submit2=self:callback('onSubmit2'), }, + widgets.Panel{frame={h=1}}, + widgets.HotkeyLabel{ + frame=quickstart_frame, + label='Quickstart guide', + key='STRING_A063', + on_activate=function() + self:onSubmit(nil, {command='quickstart-guide'}) + end, + }, }, }, widgets.ResizingPanel{ @@ -247,6 +262,7 @@ function Menu:onRenderFrame(dc, rect) self.initialize() self.initialize = nil end + Menu.super.onRenderFrame(dc, rect) end function Menu:getMouseFramePos() From fb5675ef1bbe34a13f5e77f5d2d092366ac9336c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 20:41:48 -0800 Subject: [PATCH 0162/2222] keep windows on the screen when the DF window is resized --- library/lua/gui/widgets.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 73de5ad4d..f56eff096 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -456,6 +456,20 @@ end function Panel:computeFrame(parent_rect) local sw, sh = parent_rect.width, parent_rect.height + if self.frame then + if self.frame.t and self.frame.h and self.frame.t + self.frame.h > sh then + self.frame.t = math.max(0, sh - self.frame.h) + end + if self.frame.b and self.frame.h and self.frame.b + self.frame.h > sh then + self.frame.b = math.max(0, sh - self.frame.h) + end + if self.frame.l and self.frame.w and self.frame.l + self.frame.w > sw then + self.frame.l = math.max(0, sw - self.frame.w) + end + if self.frame.r and self.frame.w and self.frame.r + self.frame.w > sw then + self.frame.r = math.max(0, sw - self.frame.w) + end + end return gui.compute_frame_body(sw, sh, self.frame, self.frame_inset, self.frame_style and 1 or 0) end From e7a30a9af8a8c8e47ef76789644ad906dd82735f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 22:23:29 -0800 Subject: [PATCH 0163/2222] sync tested spreadsheet to docs --- docs/plugins/cleanconst.rst | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/cleanconst.rst b/docs/plugins/cleanconst.rst index 420635fe2..12b1db0e5 100644 --- a/docs/plugins/cleanconst.rst +++ b/docs/plugins/cleanconst.rst @@ -3,7 +3,7 @@ cleanconst .. dfhack-tool:: :summary: Cleans up construction materials. - :tags: untested fort fps buildings + :tags: fort fps buildings This tool alters all constructions on the map so that they spawn their building component when they are disassembled, allowing their actual build items to be diff --git a/scripts b/scripts index 11d5ac853..59a10f9e0 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 11d5ac853dbf708e994323e4e1b78a2298c977fe +Subproject commit 59a10f9e072e54da2b8fee7dac6e0ada639eb67b From 50cc6d965dd4ede112432ff3a202204ce6716a66 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 23:13:58 -0800 Subject: [PATCH 0164/2222] update to new save directory structure --- ci/download-df.sh | 6 +++--- docs/Core.rst | 14 ++++++-------- docs/guides/modding-guide.rst | 3 +-- library/Core.cpp | 8 +++----- library/lua/dfhack.lua | 2 +- plugins/autogems.cpp | 2 +- plugins/rendermax/renderer_light.cpp | 4 ++-- plugins/tweak/tweaks/title-start-rename.h | 2 +- 8 files changed, 18 insertions(+), 23 deletions(-) diff --git a/ci/download-df.sh b/ci/download-df.sh index bb1e91759..8d94d54dd 100755 --- a/ci/download-df.sh +++ b/ci/download-df.sh @@ -23,7 +23,7 @@ if ! test -f "$df_tardest"; then fi done <}/raw/scripts` (only if a save is loaded) -#. :file:`raw/scripts` +#. :file:`save/{world}/scripts` (only if a save is loaded) #. :file:`hack/scripts` For example, if ``teleport`` is run, these folders are searched in order for -``teleport.lua`` or ``teleport.rb``, and the first matching file is run. +``teleport.lua``, and the first matching file is run. Script paths can be added by modifying :file:`dfhack-config/script-paths.txt`. Each line should start with one of these characters: diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 9fe62bf6a..ab52e0288 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -61,8 +61,7 @@ be: 1. ``own-scripts/`` 2. ``dfhack-config/scripts/`` -3. ``data/save/*/raw/scripts/`` -4. ``raw/scripts/`` +3. ``save/*/scripts/`` 5. ``hack/scripts/`` The structure of the game diff --git a/library/Core.cpp b/library/Core.cpp index 78257a262..e2c983116 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -472,9 +472,8 @@ void Core::getScriptPaths(std::vector *dest) if (df::global::world && isWorldLoaded()) { string save = World::ReadWorldFolder(); if (save.size()) - dest->push_back(df_path + "/data/save/" + save + "/raw/scripts"); + dest->push_back(df_path + "/save/" + save + "/scripts"); } - dest->push_back(df_path + "/raw/scripts"); dest->push_back(df_path + "/hack/scripts"); for (auto it = script_paths[1].begin(); it != script_paths[1].end(); ++it) dest->push_back(*it); @@ -2053,7 +2052,7 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve if (!df::global::world) return; - std::string rawFolder = "data/save/" + (df::global::world->cur_savegame.save_dir) + "/raw/"; + std::string rawFolder = "save/" + (df::global::world->cur_savegame.save_dir) + "/init"; auto i = table.find(event); if ( i != table.end() ) { @@ -2064,7 +2063,6 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve loadScriptFiles(this, out, set, CONFIG_PATH + "init"); loadScriptFiles(this, out, set, rawFolder); - loadScriptFiles(this, out, set, rawFolder + "objects/"); } for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) @@ -2125,7 +2123,7 @@ void Core::onStateChange(color_ostream &out, state_change_event event) case SC_MAP_UNLOADED: if (world && world->cur_savegame.save_dir.size()) { - std::string save_dir = "data/save/" + world->cur_savegame.save_dir; + std::string save_dir = "save/" + world->cur_savegame.save_dir; std::string evtlogpath = save_dir + "/events-dfhack.log"; std::ofstream evtlog; evtlog.open(evtlogpath, std::ios_base::app); // append diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index df6302b82..a4d233a9e 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -871,7 +871,7 @@ end function dfhack.getSavePath() if dfhack.isWorldLoaded() then - return dfhack.getDFPath() .. '/data/save/' .. df.global.world.cur_savegame.save_dir + return dfhack.getDFPath() .. '/save/' .. df.global.world.cur_savegame.save_dir end end diff --git a/plugins/autogems.cpp b/plugins/autogems.cpp index 8cf419aba..87eaafd16 100644 --- a/plugins/autogems.cpp +++ b/plugins/autogems.cpp @@ -299,7 +299,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(autogem_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(autogem_hook, render); bool read_config(color_ostream &out) { - std::string path = "data/save/" + World::ReadWorldFolder() + "/autogems.json"; + std::string path = "save/" + World::ReadWorldFolder() + "/autogems.json"; if (!Filesystem::isfile(path)) { // no need to require the config file to exist return true; diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 428de3a25..d806d0c3e 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -1171,11 +1171,11 @@ void lightingEngineViewscreen::loadSettings() std::string rawFolder; if(df::global::world->cur_savegame.save_dir!="") { - rawFolder= "data/save/" + (df::global::world->cur_savegame.save_dir) + "/raw/"; + rawFolder= "save/" + (df::global::world->cur_savegame.save_dir) + "/"; } else { - rawFolder= "raw/"; + rawFolder= "dfhack-config/"; } const std::string settingsfile=rawFolder+"rendermax.lua"; diff --git a/plugins/tweak/tweaks/title-start-rename.h b/plugins/tweak/tweaks/title-start-rename.h index baa1552ab..289979be4 100644 --- a/plugins/tweak/tweaks/title-start-rename.h +++ b/plugins/tweak/tweaks/title-start-rename.h @@ -17,7 +17,7 @@ struct title_start_rename_hook : df::viewscreen_titlest { inline std::string full_save_dir(const std::string ®ion_name) { - return std::string("data/save/") + region_name; + return std::string("save/") + region_name; } bool do_rename() From 762cd46d14be7738464c430ec4919092882654e4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 23:28:01 -0800 Subject: [PATCH 0165/2222] look for init.d directories in the root instead of raw/ --- docs/Core.rst | 6 +++--- docs/dev/Lua API.rst | 4 ++-- docs/guides/modding-guide.rst | 14 +++++++------- library/lua/dfhack.lua | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index 28e20a827..86e357b27 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -216,10 +216,10 @@ after a modded save is unloaded. .. _other_init_files: -raw/init.d/\*.lua -................. +init.d/\*.lua +............. -Any lua script named ``raw/init.d/*.lua``, in the save or main DF directory, +Any lua script named ``init.d/*.lua``, in the save or main DF directory, will be run when any world or that save is loaded. diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 39bd57046..813406013 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5883,9 +5883,9 @@ all script modules. Save init script ================ -If a save directory contains a file called ``raw/init.lua``, it is +If a save directory contains a file called ``init.lua``, it is automatically loaded and executed every time the save is loaded. -The same applies to any files called ``raw/init.d/*.lua``. Every +The same applies to any files called ``init.d/*.lua``. Every such script can define the following functions to be called by dfhack: * ``function onStateChange(op) ... end`` diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index ab52e0288..224448cf1 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -358,7 +358,7 @@ names for the mod folders within it. In the example below, we'll use a mod ID of ``example-mod`` mod will be developed in the ``/path/to/mymods/example-mod/`` directory and has a basic structure that looks like this:: - raw/init.d/example-mod.lua + init.d/example-mod.lua raw/objects/... raw/scripts/example-mod.lua raw/scripts/example-mod/... @@ -366,13 +366,13 @@ directory and has a basic structure that looks like this:: Let's go through that line by line. -* A short (one-line) script in ``raw/init.d/`` to initialise your +* A short (one-line) script in ``init.d/`` to initialise your mod when a save is loaded. * Modifications to the game raws (potentially with custom raw tokens) go in ``raw/objects/``. -* A control script in ``raw/scripts/`` that handles enabling and disabling your +* A control script in ``scripts/`` that handles enabling and disabling your mod. -* A subfolder for your mod under ``raw/scripts/`` will contain all the internal +* A subfolder for your mod under ``scripts/`` will contain all the internal scripts and/or modules used by your mod. It is a good idea to use a version control system to organize changes to your @@ -384,10 +384,10 @@ Unless you want to install your ``raw/`` folder into your DF game folder every time you make a change to your scripts, you should add your development scripts directory to your script paths in ``dfhack-config/script-paths.txt``:: - +/path/to/mymods/example-mod/raw/scripts/ + +/path/to/mymods/example-mod/scripts/ Ok, you're all set up! Now, let's take a look at an example -``raw/scripts/example-mod.lua`` file:: +``scripts/example-mod.lua`` file:: -- main setup and teardown for example-mod -- this next line indicates that the script supports the "enable" @@ -474,7 +474,7 @@ Ok, you're all set up! Now, let's take a look at an example You can call ``enable example-mod`` and ``disable example-mod`` yourself while developing, but for end users you can start your mod automatically from -``raw/init.d/example-mod.lua``:: +``init.d/example-mod.lua``:: dfhack.run_command('enable example-mod') diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index a4d233a9e..e2cb7069b 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -905,14 +905,14 @@ if dfhack.is_core_context then local path = dfhack.getSavePath() if path and op == SC_WORLD_LOADED then - loadInitFile(path, path..'/raw/init.lua') + loadInitFile(path, path..'/init.lua') - local dirlist = dfhack.internal.getDir(path..'/raw/init.d/') + local dirlist = dfhack.internal.getDir(path..'/init.d/') if dirlist then table.sort(dirlist) for i,name in ipairs(dirlist) do if string.match(name,'%.lua$') then - loadInitFile(path, path..'/raw/init.d/'..name) + loadInitFile(path, path..'/init.d/'..name) end end end From a56d62e1dc86e281fe2e99e9910bb25c43d7182b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 15 Jan 2023 23:28:14 -0800 Subject: [PATCH 0166/2222] update changelog --- docs/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0deac83e8..06c3975ca 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Scrollable widgets now react to mouse wheel events when the mouse is over the widget - the ``dfhack-config/scripts/`` folder is now searched for scripts by default - `hotkeys`: overlay hotspot widget now shows the DFHack logo in graphics mode and "DFHack" in text mode +- `script-paths`: removed "raw" directories from default script paths. now the default locations to search for scripts are ``dfhack-config/scripts``, ``save/*/scripts``, and ``hack/scripts`` +- ``init.d``: directories have moved from the ``raw`` subfolder (which no longer exists) to the root of the main DF folder or a savegame folder ## Documentation - `overlay-dev-guide`: added troubleshooting tips and common development workflows @@ -69,8 +71,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``gui.CLEAR_PEN``: now clears the background and foreground and writes to the background (before it would always write to the foreground) - ``gui.KEEP_LOWER_PEN``: a general use pen that writes associated tiles to the foreground while keeping the existing background -## Internals - ## Removed - ``fix-job-postings`` from the `workflow` plugin is now obsolete since affected savegames can no longer be loaded - Ruby is no longer a supported DFHack scripting language From 6b039aac9d22954fed007c5d008d92f8ebab186b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 00:15:17 -0800 Subject: [PATCH 0167/2222] update Quickstart guide --- docs/Quickstart.rst | 124 +++++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 31aadfd3f..f6f8920c2 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -13,11 +13,11 @@ What is DFHack? DFHack is a framework for Dwarf Fortress that provides a unified, cross-platform environment that enables mods and tools to significantly extend the game. The -default DFHack distribution contains a variety of tools, including bugfixes, -interface improvements, automation agents, design blueprints, modding tools, and -more. Third-party tools (e.g. mods downloaded from Steam Workshop or the forums) -can also seamlessly integrate with the DFHack framework and extend the game far -beyond what can be done by just modding the raws. +default DFHack distribution contains a wide variety of tools, including bugfixes, +interface improvements, automation agents, design blueprints, modding building +blocks, and more. Third-party tools (e.g. mods downloaded from Steam Workshop or +the forums) can also seamlessly integrate with the DFHack framework and extend +the game far beyond what can be done by just modding the raws. DFHack's mission is to provide tools and interfaces for players and modders to: @@ -25,7 +25,7 @@ DFHack's mission is to provide tools and interfaces for players and modders to: - reduce the impact of game bugs - give the player more agency and control over the game - provide alternatives to toilsome or frustrating aspects of gameplay -- overall, make the game more fun +- **make the game more fun** What can I do with DFHack tools? -------------------------------- @@ -43,7 +43,7 @@ Here are some common tasks people use DFHack tools to accomplish: - Promote time-sensitive job types (e.g. food hauling) so they are done expediently - Quickly scan the map for visible ores of specific types so you can focus - your mining + your mining efforts Some tools are one-shot commands. For example, you can run `unforbid all ` to claim all items on the map after a messy siege. @@ -51,15 +51,15 @@ to claim all items on the map after a messy siege. Other tools must be `enabled ` and then they will run in the background. For example, `enable seedwatch ` will start monitoring your stocks of seeds and prevent your chefs from cooking seeds that you need for planting. -Enableable tools that affect a fort save their state with the fort and will -remember that they are enabled the next time you load your save. +Tools that are enabled in the context of a fort will save their state with that +fort, and the will remember that they are enabled the next time you load your save. -And still other tools add information to the screen or provide new integrated +A third class of tools add information to the screen or provide new integrated functionality via the DFHack `overlay` framework. For example, the `unsuspend` tool, in addition to its basic function of unsuspending all building construction jobs, can also overlay a marker on suspended buildings to indicate that they are suspended (and will use different markers to tell you whether this is a problem). -These overlays can be configured with the `gui/overlay` tool. +These overlays can be enabled and configured with the `gui/overlay` interface. How can I figure out which commands to run? ------------------------------------------- @@ -70,26 +70,25 @@ The first place to check is the DFHack logo hover hotspot. It's in the upper left corner of the screen by default, though you can move it anywhere you want with the `gui/overlay` tool. -When you hover the mouse over the logo (or hit the :kbd:`Ctrl`:kbd:`Shift`:kbd:`C` -keyboard shortcut) a list of DFHack tools relevant to the current context comes up. -For example, when you have a unit selected, the hotspot will show a list of tools -that inspect units, allow you to edit them, or maybe even teleport them. Next to -each tool, you'll see the global hotkey you can hit to invoke the command without -even opening the hover list. +When you hover the mouse over the logo (or hit the Ctrl-Shift-C keyboard shortcut) +a list of DFHack tools relevant to the current context comes up. For example, when +you have a unit selected, the hotspot will show a list of tools that inspect +units, allow you to edit them, or maybe even teleport them. Next to each tool, +you'll see the global hotkey you can hit to invoke the command without even +opening the hover list. You can run any DFHack tool from `gui/launcher`, which is always listed first in the hover list. You can also bring up the launcher by tapping the backtick key -(\`) or hitting :kbd:`Ctrl`:kbd:`Shift`:kbd:`D`. In the launcher, you can quickly -autocomplete any command name by selecting it in the list on the right side of -the window. Commands are ordered by how often you run them, so your favorite -commands will always be on top. You can also pull full commandlines out of your -history with :kbd:`Alt`:kbd:`S` (or by clicking on the "history search" hotkey hint). - -Once you have typed (or autocompleted, or searched for) a command name, other -commands related to the one you have selected will appear in the autocomplete list. -Scanning through the related tools is a great way to learn about new tools that -you might find useful. You can also see how commands are grouped by running the -`tags` command. +(\`) or hitting Ctrl-Shift-D. In the launcher, you can quickly autocomplete any +command name by selecting it in the list on the right side of the window. +Commands are ordered by how often you run them, so your favorite commands will +always be on top. You can also pull full commandlines out of your history with +Alt-S (or by clicking on the "history search" hotkey hint). + +Once you have typed (or autocompleted, or searched for) a command, other commands +related to the one you have selected will appear in the autocomplete list. +Scanning through that list is a great way to learn about new tools that you might +find useful. You can also see how commands are grouped by running the `tags` command. The bottom panel will show the full help text for the command you are running, allowing you to refer to the usage documentation and examples when you are typing @@ -100,7 +99,7 @@ How do DFHack in-game windows work? Many DFHack tools have graphical interfaces that appear in-game. You can tell which windows belong to DFHack tools because they will have the word "DFHack" -printed across their bottom frame. DFHack provides a custom windowing system +printed across their bottom frame edge. DFHack provides a custom windowing system that gives the player a lot of control over where the windows appear and whether they capture keyboard and mouse input. @@ -108,26 +107,28 @@ The DFHack windowing system allows you to use DFHack tools without interrupting the game. That is, if the game is unpaused, it will continue to run while a DFHack window is open. You can also interact with the map, scrolling it with the keyboard or mouse and selecting units, buildings, and items. Some tools will +capture all keyboard input, such as tools with editable text fields, and some will force-pause the game if it makes sense to, like `gui/quickfort`, since you cannot -interact with the map normally while placing a blueprint. +interact with the map normally while trying to apply a blueprint. DFHack windows are draggable from the title bar or from anywhere on the window that doesn't have a mouse-clickable widget on it. Many are resizable as well -(if the tool window has components that can be reasonably resized). +(if the tool window has components that can reasonably be resized). -DFHack windows close with a right mouse click or keyboard :kbd:`Esc`, but if you +DFHack windows close with a right mouse click or keyboard Esc, but if you want to keep a DFHack tool open while you interact with the game, you can click the -pin in the upper right corner of the DFHack window or hit :kbd:`Alt`:kbd:`L` so +pin in the upper right corner of the DFHack window or hit Alt-L so that the pin turns green. The DFHack window will then ignore right clicks and -:kbd:`Esc` key presses that would otherwise close the window. This is especially +Esc key presses that would otherwise close the window. This is especially useful for the configuration tool windows for the automation tools. For example, -you can pin the `gui/autochop` window and let it sit there monitoring your -logging industry as you play, using it as a live status window. Note that you can -still right click *on* the DFHack tool window to close it, even when it is pinned. +you can pin the `gui/autochop` window, set it to minimal mode, and let it sit +there monitoring your logging industry as you play, using it as a live status +window. Note that you can still right click *on* the DFHack tool window to close +it, even when it is pinned. You can have multiple DFHack tool windows on the screen at the same time. The one that is receiving keyboard input has a highlighted title bar and will appear -over other windows if dragged over them. Clicking on a DFHack window that is not +over other windows if dragged across them. Clicking on a DFHack window that is not currently active will bring it to the foreground and make it the active window. Where do I go next? @@ -136,11 +137,11 @@ Where do I go next? To recap: You can get to popular, relevant tools for the current context by hovering -the mouse over the DFHack logo or by hitting :kbd:`Ctrl`:kbd:`Shift`:kbd:`C`. +the mouse over the DFHack logo or by hitting Ctrl-Shift-C. You can get to the launcher and its integrated autocomplete, history search, -and help text by hitting backtick (\`) or :kbd:`Ctrl`:kbd:`Shift`:kbd:`D`, -or, of course, by running it from the logo hover list. +and help text by hitting backtick (\`) or Ctrl-Shift-D, or, of course, by +running it from the logo hover list. You can list and start tools that run in the background with the `enable` command. @@ -149,7 +150,7 @@ You can configure screen overlays with the `gui/overlay` tool. With those four tools, you have the complete DFHack tool suite at your fingertips. So what to run first? Here are a few commands to get you started. -You can run them from the launcher. +You can run them all from the launcher. First, let's import some useful manager orders to keep your fort stocked with basic necessities. Run ``orders import library/basic``. If you go to your @@ -160,13 +161,32 @@ You can enable it from the GUI, so you don't need to run `enable autochop Date: Mon, 16 Jan 2023 00:15:36 -0800 Subject: [PATCH 0168/2222] update scripts head --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 59a10f9e0..28f8aef51 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 59a10f9e072e54da2b8fee7dac6e0ada639eb67b +Subproject commit 28f8aef51277ebaa8923f381482ec161870609af From 00cee1c6002107637c5fc31c68a3da6408c6e4c5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 11:03:51 -0800 Subject: [PATCH 0169/2222] fix numbered list --- docs/guides/modding-guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 224448cf1..73d9407d6 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -62,7 +62,7 @@ be: 1. ``own-scripts/`` 2. ``dfhack-config/scripts/`` 3. ``save/*/scripts/`` -5. ``hack/scripts/`` +4. ``hack/scripts/`` The structure of the game ------------------------- From 9c36c3978fe870c8fe1a8097bd126a91bb5d7f7c Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 16 Jan 2023 16:06:00 -0500 Subject: [PATCH 0170/2222] Update xml, scripts --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index ab0593b40..06906687f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ab0593b40a1a41c19432ec6bd88fc9ecf60b5fb6 +Subproject commit 06906687f89733032e5cc813bdc9acfdd2405f31 diff --git a/scripts b/scripts index 28f8aef51..64013d4b6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 28f8aef51277ebaa8923f381482ec161870609af +Subproject commit 64013d4b65ee60f36191a7d442fc2c8ef1fc388d From e98271ddc389b54cd3514466116dfcd8acef2237 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 13:53:16 -0800 Subject: [PATCH 0171/2222] document that enableable scripts must also be modules --- docs/dev/Lua API.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 813406013..ab7d6434e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5812,9 +5812,13 @@ Enabling and disabling scripts ============================== Scripts can choose to recognize the built-in ``enable`` and ``disable`` commands -by including the following line anywhere in their file:: +by including the following line near the top of their file:: - --@ enable = true + --@enable = true + --@module = true + +Note that enableable scripts must also be `modules ` so their +``isEnabled()`` functions can be called from outside the script. When the ``enable`` and ``disable`` commands are invoked, the ``dfhack_flags`` table passed to the script will have the following fields set: @@ -5829,7 +5833,8 @@ command. Example usage:: - --@ enable = true + --@enable = true + --@module = true enabled = enabled or false function isEnabled() From 3958d376843f858c9056bed1a295a2508f49c271 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 16:56:31 -0800 Subject: [PATCH 0172/2222] update changelog and submodules for 50.05-alpha1 --- CMakeLists.txt | 2 +- docs/changelog.txt | 14 ++++++++++++++ library/xml | 2 +- scripts | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dcb5b42ff..49bb29f04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ endif() # set up versioning. set(DF_VERSION "50.05") -set(DFHACK_RELEASE "alpha0") +set(DFHACK_RELEASE "alpha1") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 06c3975ca..a5d4ab434 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,20 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.05-alpha1 + ## Fixes - ``widgets.WrappedLabel``: no longer resets scroll position when window is moved or resized diff --git a/library/xml b/library/xml index 06906687f..9cd1b4ea9 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 06906687f89733032e5cc813bdc9acfdd2405f31 +Subproject commit 9cd1b4ea9ab99919a64f2dfaa0c776ab007ec2d4 diff --git a/scripts b/scripts index 64013d4b6..d3bc3b4d3 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 64013d4b65ee60f36191a7d442fc2c8ef1fc388d +Subproject commit d3bc3b4d379d3511a1aae51da7ccd6f553198195 From bb3f640afa36661dfd08b1b2996196df82e88f00 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 17:43:00 -0800 Subject: [PATCH 0173/2222] silence check for usefulness. it's useful. i get it --- plugins/autochop.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 0c20c0cb4..34a8979ff 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -522,10 +522,8 @@ static void scan_logs(int32_t *usable_logs, const vector &citizens, if (item->getType() != item_type::WOOD) continue; - if (!is_valid_item(item)) { - INFO(status).print("autochop is_valid_item actually caught something useful!! Please tell the DFHack team.\n"); + if (!is_valid_item(item)) continue; - } if (!is_accessible_item(item->pos, citizens)) { if (inaccessible_logs) From d6b69ca8fc7357c25c09f87a29a9ac427869ecbf Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 01:08:02 -0800 Subject: [PATCH 0174/2222] move useful functions from uicommon to MiscUtils --- library/include/MiscUtils.h | 31 ++++++++++++++++++++++++++----- plugins/uicommon.h | 18 ------------------ 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index d089640ef..124987535 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -23,17 +23,20 @@ distribution. */ #pragma once + #include "Export.h" + #include -#include -#include #include #include -#include -#include -#include #include +#include +#include +#include #include +#include +#include +#include #if defined(_MSC_VER) #define DFHACK_FUNCTION_SIG __FUNCSIG__ @@ -338,6 +341,24 @@ inline typename T::mapped_type map_find( return (it == map.end()) ? defval : it->second; } +template +static void for_each_(std::vector &v, Fn func) +{ + std::for_each(v.begin(), v.end(), func); +} + +template +static void for_each_(std::map &v, Fn func) +{ + std::for_each(v.begin(), v.end(), func); +} + +template +static void transform_(const std::vector &src, std::vector &dst, Fn func) +{ + std::transform(src.begin(), src.end(), std::back_inserter(dst), func); +} + DFHACK_EXPORT bool prefix_matches(const std::string &prefix, const std::string &key, std::string *tail = NULL); template diff --git a/plugins/uicommon.h b/plugins/uicommon.h index 03914b461..6179324d4 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -65,24 +65,6 @@ struct coord32_t } }; -template -static void for_each_(vector &v, Fn func) -{ - for_each(v.begin(), v.end(), func); -} - -template -static void for_each_(map &v, Fn func) -{ - for_each(v.begin(), v.end(), func); -} - -template -static void transform_(const vector &src, vector &dst, Fn func) -{ - transform(src.begin(), src.end(), back_inserter(dst), func); -} - typedef int8_t UIColor; static inline void OutputString(UIColor color, int &x, int &y, const std::string &text, From a96ddcec80d6bfae7a1256eca5bab81e2c1b3881 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 16 Jan 2023 23:49:11 -0800 Subject: [PATCH 0175/2222] add buildingplan skeleton --- plugins/CMakeLists.txt | 2 +- plugins/buildingplan.cpp | 166 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 plugins/buildingplan.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 661fa2624..e38c27125 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -89,7 +89,7 @@ dfhack_plugin(autofarm autofarm.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) -#add_subdirectory(buildingplan) +dfhack_plugin(buildingplan buildingplan.cpp LINK_LIBRARIES lua) #dfhack_plugin(changeitem changeitem.cpp) dfhack_plugin(changelayer changelayer.cpp) dfhack_plugin(changevein changevein.cpp) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp new file mode 100644 index 000000000..1688d7d55 --- /dev/null +++ b/plugins/buildingplan.cpp @@ -0,0 +1,166 @@ +#include "Core.h" +#include "Debug.h" +#include "PluginManager.h" + +#include "modules/Persistence.h" +#include "modules/World.h" + +#include "df/world.h" + +#include +#include + +using std::string; +using std::vector; + +using namespace DFHack; + +DFHACK_PLUGIN("buildingplan"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +REQUIRE_GLOBAL(world); + +// logging levels can be dynamically controlled with the `debugfilter` command. +namespace DFHack { + // for configuration-related logging + DBG_DECLARE(buildingplan, status, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(buildingplan, cycle, DebugCategory::LINFO); +} + +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static PersistentDataItem config; +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_BLOCKS = 1, + CONFIG_BOULDERS = 2, + CONFIG_LOGS = 3, + CONFIG_BARS = 4, +}; +static int get_config_val(int index) { + if (!config.isValid()) + return -1; + return config.ival(index); +} +static bool get_config_bool(int index) { + return get_config_val(index) == 1; +} +static void set_config_val(int index, int value) { + if (config.isValid()) + config.ival(index) = value; +} +static void set_config_bool(int index, bool value) { + set_config_val(index, value ? 1 : 0); +} + +static const int32_t CYCLE_TICKS = 600; // twice per game day +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, vector ¶meters); +static void do_cycle(color_ostream &out); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(status,out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Plan building placement before you have materials.", + do_command)); + + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(status,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(CONFIG_IS_ENABLED, is_enabled); + } else { + DEBUG(status,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(status,out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} + +DFhackCExport command_result plugin_load_data (color_ostream &out) { + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) { + DEBUG(status,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(CONFIG_IS_ENABLED, is_enabled); + set_config_bool(CONFIG_BLOCKS, true); + set_config_bool(CONFIG_BOULDERS, true); + set_config_bool(CONFIG_LOGS, true); + set_config_bool(CONFIG_BARS, false); + } + + // we have to copy our enabled flag into the global plugin variable, but + // all the other state we can directly read/modify from the persistent + // data structure. + is_enabled = get_config_bool(CONFIG_IS_ENABLED); + DEBUG(status,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(status,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; + } + } + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) + do_cycle(out); + return CR_OK; +} + +static command_result do_command(color_ostream &out, vector ¶meters) { + // be sure to suspend the core if any DF state is read or modified + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + // TODO: configuration logic + // simple commandline parsing can be done in C++, but there are lua libraries + // that can easily handle more complex commandlines. see the blueprint plugin + // for an example. + + return CR_OK; +} + +///////////////////////////////////////////////////// +// cycle logic +// + +static void do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + + // TODO: logic that runs every get_config_val(CONFIG_CYCLE_TICKS) ticks +} From bc42f7c73c7ef04ad376724563ed1e4fbb5ddd89 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 17 Jan 2023 04:05:17 -0800 Subject: [PATCH 0176/2222] get buildingplan minimally functional for quickfort --- plugins/buildingplan.cpp | 588 +++++++++++++++++++++++++++++++++-- plugins/lua/buildingplan.lua | 29 +- 2 files changed, 587 insertions(+), 30 deletions(-) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 1688d7d55..7080be4d5 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -1,16 +1,30 @@ #include "Core.h" #include "Debug.h" +#include "LuaTools.h" #include "PluginManager.h" +#include "modules/Items.h" +#include "modules/Job.h" +#include "modules/Materials.h" #include "modules/Persistence.h" #include "modules/World.h" +#include "df/building.h" +#include "df/building_design.h" +#include "df/item.h" +#include "df/job_item.h" #include "df/world.h" +#include #include #include +#include +using std::map; +using std::pair; +using std::queue; using std::string; +using std::unordered_map; using std::vector; using namespace DFHack; @@ -29,7 +43,8 @@ namespace DFHack { } static const string CONFIG_KEY = string(plugin_name) + "/config"; -static PersistentDataItem config; +static const string BLD_CONFIG_KEY = string(plugin_name) + "/building"; + enum ConfigValues { CONFIG_IS_ENABLED = 0, CONFIG_BLOCKS = 1, @@ -37,20 +52,76 @@ enum ConfigValues { CONFIG_LOGS = 3, CONFIG_BARS = 4, }; -static int get_config_val(int index) { - if (!config.isValid()) + +enum BuildingConfigValues { + BLD_CONFIG_ID = 0, +}; + +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) return -1; - return config.ival(index); + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; } -static bool get_config_bool(int index) { - return get_config_val(index) == 1; +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; } -static void set_config_val(int index, int value) { - if (config.isValid()) - config.ival(index) = value; +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); } -static void set_config_bool(int index, bool value) { - set_config_val(index, value ? 1 : 0); + +class PlannedBuilding { +public: + const df::building::key_field_type id; + + PlannedBuilding(color_ostream &out, df::building *building) : id(building->id) { + DEBUG(status,out).print("creating persistent data for building %d\n", id); + config = DFHack::World::AddPersistentData(BLD_CONFIG_KEY); + set_config_val(config, BLD_CONFIG_ID, id); + } + + PlannedBuilding(DFHack::PersistentDataItem &config) + : config(config), id(get_config_val(config, BLD_CONFIG_ID)) { } + + void remove(color_ostream &out); + + // Ensure the building still exists and is in a valid state. It can disappear + // for lots of reasons, such as running the game with the buildingplan plugin + // disabled, manually removing the building, modifying it via the API, etc. + df::building * getBuildingIfValidOrRemoveIfNot(color_ostream &out) { + auto bld = df::building::find(id); + bool valid = bld && bld->getBuildStage() == 0; + if (!valid) { + remove(out); + return NULL; + } + return bld; + } + +private: + DFHack::PersistentDataItem config; +}; + +static PersistentDataItem config; +// building id -> PlannedBuilding +unordered_map planned_buildings; +// vector id -> filter bucket -> queue of (building id, job_item index) +map>>> tasks; + +// note that this just removes the PlannedBuilding. the tasks will get dropped +// as we discover them in the tasks queues and they fail to be found in planned_buildings. +// this "lazy" task cleaning algorithm works because there is no way to +// re-register a building once it has been removed -- if it has been booted out of +// planned_buildings, then it has either been built or desroyed. therefore there is +// no chance of duplicate tasks getting added to the tasks queues. +void PlannedBuilding::remove(color_ostream &out) { + DEBUG(status,out).print("removing persistent data for building %d\n", id); + DFHack::World::DeletePersistentData(config); + if (planned_buildings.count(id) > 0) + planned_buildings.erase(id); } static const int32_t CYCLE_TICKS = 600; // twice per game day @@ -58,6 +129,7 @@ static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); static void do_cycle(color_ostream &out); +static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { DEBUG(status,out).print("initializing %s\n", plugin_name); @@ -81,7 +153,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { is_enabled = enable; DEBUG(status,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); - set_config_bool(CONFIG_IS_ENABLED, is_enabled); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); } else { DEBUG(status,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", @@ -102,19 +174,28 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { if (!config.isValid()) { DEBUG(status,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); - set_config_bool(CONFIG_IS_ENABLED, is_enabled); - set_config_bool(CONFIG_BLOCKS, true); - set_config_bool(CONFIG_BOULDERS, true); - set_config_bool(CONFIG_LOGS, true); - set_config_bool(CONFIG_BARS, false); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + set_config_bool(config, CONFIG_BLOCKS, true); + set_config_bool(config, CONFIG_BOULDERS, true); + set_config_bool(config, CONFIG_LOGS, true); + set_config_bool(config, CONFIG_BARS, false); } // we have to copy our enabled flag into the global plugin variable, but // all the other state we can directly read/modify from the persistent // data structure. - is_enabled = get_config_bool(CONFIG_IS_ENABLED); + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); DEBUG(status,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); + + vector building_configs; + World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); + planned_buildings.clear(); + tasks.clear(); + const size_t num_building_configs = building_configs.size(); + for (size_t idx = 0; idx < num_building_configs; ++idx) + registerPlannedBuilding(out, PlannedBuilding(building_configs[idx])); + return CR_OK; } @@ -124,19 +205,43 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DEBUG(status,out).print("world unloaded; disabling %s\n", plugin_name); is_enabled = false; + planned_buildings.clear(); + tasks.clear(); } } return CR_OK; } +static bool cycle_requested = false; + DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) + if (is_enabled && + (cycle_requested || world->frame_counter - cycle_timestamp >= CYCLE_TICKS)) do_cycle(out); return CR_OK; } +static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(status).print("calling buildingplan lua function: '%s'\n", fn_name); + + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.buildingplan", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} + static command_result do_command(color_ostream &out, vector ¶meters) { - // be sure to suspend the core if any DF state is read or modified CoreSuspender suspend; if (!Core::getInstance().isWorldLoaded()) { @@ -144,23 +249,452 @@ static command_result do_command(color_ostream &out, vector ¶meters) return CR_FAILURE; } - // TODO: configuration logic - // simple commandline parsing can be done in C++, but there are lua libraries - // that can easily handle more complex commandlines. see the blueprint plugin - // for an example. + bool show_help = false; + if (!call_buildingplan_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; + } - return CR_OK; + return show_help ? CR_WRONG_USAGE : CR_OK; } ///////////////////////////////////////////////////// // cycle logic // +struct BadFlags { + uint32_t whole; + + BadFlags() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(in_job); + F(owned); F(in_chest); F(removed); F(encased); + F(spider_web); + #undef F + whole = flags.whole; + } +}; + +static bool itemPassesScreen(df::item * item) { + static const BadFlags bad_flags; + return !(item->flags.whole & bad_flags.whole) + && !item->isAssignedToStockpile(); +} + +static bool matchesFilters(df::item * item, df::job_item * job_item) { + // check the properties that are not checked by Job::isSuitableItem() + if (job_item->item_type > -1 && job_item->item_type != item->getType()) + return false; + + if (job_item->item_subtype > -1 && + job_item->item_subtype != item->getSubtype()) + return false; + + if (job_item->flags2.bits.building_material && !item->isBuildMat()) + return false; + + if (job_item->metal_ore > -1 && !item->isMetalOre(job_item->metal_ore)) + return false; + + if (job_item->has_tool_use > df::tool_uses::NONE + && !item->hasToolUse(job_item->has_tool_use)) + return false; + + return DFHack::Job::isSuitableItem( + job_item, item->getType(), item->getSubtype()) + && DFHack::Job::isSuitableMaterial( + job_item, item->getMaterial(), item->getMaterialIndex(), + item->getType()); +} + +static bool isJobReady(color_ostream &out, df::job * job) { + int needed_items = 0; + for (auto job_item : job->job_items) { needed_items += job_item->quantity; } + if (needed_items) { + DEBUG(cycle,out).print("building needs %d more item(s)\n", needed_items); + return false; + } + return true; +} + +static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b) { + // we want the items in the opposite order of the filters + return a->job_item_idx > b->job_item_idx; +} + +// this function does not remove the job_items since their quantity fields are +// now all at 0, so there is no risk of having extra items attached. we don't +// remove them to keep the "finalize with buildingplan active" path as similar +// as possible to the "finalize with buildingplan disabled" path. +static void finalizeBuilding(color_ostream &out, df::building * bld) { + DEBUG(cycle,out).print("finalizing building %d\n", bld->id); + auto job = bld->jobs[0]; + + // sort the items so they get added to the structure in the correct order + std::sort(job->items.begin(), job->items.end(), job_item_idx_lt); + + // derive the material properties of the building and job from the first + // applicable item. if any boulders are involved, it makes the whole + // structure "rough". + bool rough = false; + for (auto attached_item : job->items) { + df::item *item = attached_item->item; + rough = rough || item->getType() == df::item_type::BOULDER; + if (bld->mat_type == -1) { + bld->mat_type = item->getMaterial(); + job->mat_type = bld->mat_type; + } + if (bld->mat_index == -1) { + bld->mat_index = item->getMaterialIndex(); + job->mat_index = bld->mat_index; + } + } + + if (bld->needsDesign()) { + auto act = (df::building_actual *)bld; + if (!act->design) + act->design = new df::building_design(); + act->design->flags.bits.rough = rough; + } + + // we're good to go! + job->flags.bits.suspend = false; + Job::checkBuildingsNow(); +} + +static df::building * popInvalidTasks(color_ostream &out, queue> & task_queue) { + while (!task_queue.empty()) { + auto & task = task_queue.front(); + auto id = task.first; + if (planned_buildings.count(id) > 0) { + auto bld = planned_buildings.at(id).getBuildingIfValidOrRemoveIfNot(out); + if (bld && bld->jobs[0]->job_items[task.second]->quantity) + return bld; + } + DEBUG(cycle,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n", id, task.second); + task_queue.pop(); + } + return NULL; +} + +static void doVector(color_ostream &out, df::job_item_vector_id vector_id, + map>> & buckets) { + auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); + auto item_vector = df::global::world->items.other[other_id]; + DEBUG(cycle,out).print("matching %zu item(s) in vector %s against %zu filter bucket(s)\n", + item_vector.size(), + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + buckets.size()); + for (auto item_it = item_vector.rbegin(); + item_it != item_vector.rend(); + ++item_it) { + auto item = *item_it; + if (!itemPassesScreen(item)) + continue; + for (auto bucket_it = buckets.begin(); bucket_it != buckets.end(); ) { + auto & task_queue = bucket_it->second; + auto bld = popInvalidTasks(out, task_queue); + if (!bld) { + DEBUG(cycle,out).print("removing empty bucket: %s/%s; %zu bucket(s) left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str(), + buckets.size() - 1); + bucket_it = buckets.erase(bucket_it); + continue; + } + auto & task = task_queue.front(); + auto id = task.first; + auto job = bld->jobs[0]; + auto filter_idx = task.second; + if (matchesFilters(item, job->job_items[filter_idx]) + && DFHack::Job::attachJobItem(job, item, + df::job_item_ref::Hauled, filter_idx)) + { + MaterialInfo material; + material.decode(item); + ItemTypeInfo item_type; + item_type.decode(item); + DEBUG(cycle,out).print("attached %s %s to filter %d for %s(%d): %s/%s\n", + material.toString().c_str(), + item_type.toString().c_str(), + filter_idx, + ENUM_KEY_STR(building_type, bld->getType()).c_str(), + id, + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str()); + // keep quantity aligned with the actual number of remaining + // items so if buildingplan is turned off, the building will + // be completed with the correct number of items. + --job->job_items[filter_idx]->quantity; + task_queue.pop(); + if (isJobReady(out, job)) { + finalizeBuilding(out, bld); + planned_buildings.at(id).remove(out); + } + if (task_queue.empty()) { + DEBUG(cycle,out).print( + "removing empty item bucket: %s/%s; %zu left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str(), + buckets.size() - 1); + buckets.erase(bucket_it); + } + // we found a home for this item; no need to look further + break; + } + ++bucket_it; + } + if (buckets.empty()) + break; + } +} + +struct VectorsToScanLast { + std::vector vectors; + VectorsToScanLast() { + // order is important here. we want to match boulders before wood and + // everything before bars. blocks are not listed here since we'll have + // already scanned them when we did the first pass through the buckets. + vectors.push_back(df::job_item_vector_id::BOULDER); + vectors.push_back(df::job_item_vector_id::WOOD); + vectors.push_back(df::job_item_vector_id::BAR); + } +}; + static void do_cycle(color_ostream &out) { + static const VectorsToScanLast vectors_to_scan_last; + // mark that we have recently run cycle_timestamp = world->frame_counter; + cycle_requested = false; + + DEBUG(cycle,out).print("running %s cycle for %zu registered buildings\n", + plugin_name, planned_buildings.size()); + + for (auto it = tasks.begin(); it != tasks.end(); ) { + auto vector_id = it->first; + // we could make this a set, but it's only three elements + if (std::find(vectors_to_scan_last.vectors.begin(), + vectors_to_scan_last.vectors.end(), + vector_id) != vectors_to_scan_last.vectors.end()) { + ++it; + continue; + } + + auto & buckets = it->second; + doVector(out, vector_id, buckets); + if (buckets.empty()) { + DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + tasks.size() - 1); + it = tasks.erase(it); + } + else + ++it; + } + for (auto vector_id : vectors_to_scan_last.vectors) { + if (tasks.count(vector_id) == 0) + continue; + auto & buckets = tasks[vector_id]; + doVector(out, vector_id, buckets); + if (buckets.empty()) { + DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + tasks.size() - 1); + tasks.erase(vector_id); + } + } + DEBUG(cycle,out).print("cycle done; %zu registered building(s) left\n", + planned_buildings.size()); +} + +///////////////////////////////////////////////////// +// Lua API +// core will already be suspended when coming in through here +// - DEBUG(cycle,out).print("running %s cycle\n", plugin_name); +static string getBucket(const df::job_item & ji) { + std::ostringstream ser; - // TODO: logic that runs every get_config_val(CONFIG_CYCLE_TICKS) ticks + // pull out and serialize only known relevant fields. if we miss a few, then + // the filter bucket will be slighly less specific than it could be, but + // that's probably ok. we'll just end up bucketing slightly different items + // together. this is only a problem if the different filter at the front of + // the queue doesn't match any available items and blocks filters behind it + // that could be matched. + ser << ji.item_type << ':' << ji.item_subtype << ':' << ji.mat_type << ':' + << ji.mat_index << ':' << ji.flags1.whole << ':' << ji.flags2.whole + << ':' << ji.flags3.whole << ':' << ji.flags4 << ':' << ji.flags5 << ':' + << ji.metal_ore << ':' << ji.has_tool_use; + + return ser.str(); } + +// get a list of item vectors that we should search for matches +static vector getVectorIds(color_ostream &out, df::job_item *job_item) +{ + std::vector ret; + + // if the filter already has the vector_id set to something specific, use it + if (job_item->vector_id > df::job_item_vector_id::IN_PLAY) + { + DEBUG(status,out).print("using vector_id from job_item: %s\n", + ENUM_KEY_STR(job_item_vector_id, job_item->vector_id).c_str()); + ret.push_back(job_item->vector_id); + return ret; + } + + // if the filer is for building material, refer to our global settings for + // which vectors to search + if (job_item->flags2.bits.building_material) + { + if (get_config_bool(config, CONFIG_BLOCKS)) + ret.push_back(df::job_item_vector_id::BLOCKS); + if (get_config_bool(config, CONFIG_BOULDERS)) + ret.push_back(df::job_item_vector_id::BOULDER); + if (get_config_bool(config, CONFIG_LOGS)) + ret.push_back(df::job_item_vector_id::WOOD); + if (get_config_bool(config, CONFIG_BARS)) + ret.push_back(df::job_item_vector_id::BAR); + } + + // fall back to IN_PLAY if no other vector was appropriate + if (ret.empty()) + ret.push_back(df::job_item_vector_id::IN_PLAY); + return ret; +} +static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { + df::building * bld = pb.getBuildingIfValidOrRemoveIfNot(out); + if (!bld) + return false; + + if (bld->jobs.size() != 1) { + DEBUG(status,out).print("unexpected number of jobs: want 1, got %zu\n", bld->jobs.size()); + return false; + } + auto job_items = bld->jobs[0]->job_items; + int num_job_items = job_items.size(); + if (num_job_items < 1) { + DEBUG(status,out).print("unexpected number of job items: want >0, got %d\n", num_job_items); + return false; + } + int32_t id = bld->id; + for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) { + auto job_item = job_items[job_item_idx]; + auto bucket = getBucket(*job_item); + auto vector_ids = getVectorIds(out, job_item); + + // if there are multiple vector_ids, schedule duplicate tasks. after + // the correct number of items are matched, the extras will get popped + // as invalid + for (auto vector_id : vector_ids) { + for (int item_num = 0; item_num < job_item->quantity; ++item_num) { + tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx)); + DEBUG(status,out).print("added task: %s/%s/%d,%d; " + "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket.c_str(), id, job_item_idx, tasks.size(), + tasks[vector_id].size(), tasks[vector_id][bucket].size()); + } + } + } + + // suspend jobs + for (auto job : bld->jobs) + job->flags.bits.suspend = true; + + // add the planned buildings to our register + planned_buildings.emplace(bld->id, pb); + + return true; +} + +static void printStatus(color_ostream &out) { + DEBUG(status,out).print("entering buildingplan_printStatus\n"); + out.print("buildingplan is %s\n\n", is_enabled ? "enabled" : "disabled"); + out.print(" finding materials for %zd buildings\n", planned_buildings.size()); + out.print("Current settings:\n"); + out.print(" use blocks: %s\n", get_config_bool(config, CONFIG_BLOCKS) ? "yes" : "no"); + out.print(" use boulders: %s\n", get_config_bool(config, CONFIG_BOULDERS) ? "yes" : "no"); + out.print(" use logs: %s\n", get_config_bool(config, CONFIG_LOGS) ? "yes" : "no"); + out.print(" use bars: %s\n", get_config_bool(config, CONFIG_BARS) ? "yes" : "no"); + out.print("\n"); +} + +static bool setSetting(color_ostream &out, string name, bool value) { + DEBUG(status,out).print("entering setSetting (%s -> %s)\n", name, value ? "true" : "false"); + if (name == "blocks") + set_config_bool(config, CONFIG_BLOCKS, value); + else if (name == "boulders") + set_config_bool(config, CONFIG_BOULDERS, value); + else if (name == "logs") + set_config_bool(config, CONFIG_LOGS, value); + else if (name == "bars") + set_config_bool(config, CONFIG_BARS, value); + else { + out.printerr("unrecognized setting: '%s'\n", name.c_str()); + return false; + } + return true; +} + +static bool isPlannableBuilding(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom) { + DEBUG(status,out).print("entering isPlannableBuilding\n"); + int num_filters = 0; + if (!call_buildingplan_lua(&out, "get_num_filters", 3, 1, + [&](lua_State *L) { + Lua::Push(L, type); + Lua::Push(L, subtype); + Lua::Push(L, custom); + }, + [&](lua_State *L) { + num_filters = lua_tonumber(L, -1); + })) { + return false; + } + return num_filters >= 1; +} + +static bool isPlannedBuilding(color_ostream &out, df::building *bld) { + TRACE(status,out).print("entering isPlannedBuilding\n"); + return bld && planned_buildings.count(bld->id) > 0; +} + +static bool addPlannedBuilding(color_ostream &out, df::building *bld) { + DEBUG(status,out).print("entering addPlannedBuilding\n"); + if (!bld || planned_buildings.count(bld->id) + || !isPlannableBuilding(out, bld->getType(), bld->getSubtype(), + bld->getCustomType())) + return false; + return registerPlannedBuilding(out, PlannedBuilding(out, bld)); +} + +static void doCycle(color_ostream &out) { + DEBUG(status,out).print("entering doCycle\n"); + do_cycle(out); +} + +static void scheduleCycle(color_ostream &out) { + DEBUG(status,out).print("entering scheduleCycle\n"); + cycle_requested = true; +} + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(printStatus), + DFHACK_LUA_FUNCTION(setSetting), + DFHACK_LUA_FUNCTION(isPlannableBuilding), + DFHACK_LUA_FUNCTION(isPlannedBuilding), + DFHACK_LUA_FUNCTION(addPlannedBuilding), + DFHACK_LUA_FUNCTION(doCycle), + DFHACK_LUA_FUNCTION(scheduleCycle), + DFHACK_LUA_END +}; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index a01a1d0b1..9b953dd7c 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -14,11 +14,31 @@ local _ENV = mkmodule('plugins.buildingplan') --]] -local dialogs = require('gui.dialogs') -local guidm = require('gui.dwarfmode') +local argparse = require('argparse') require('dfhack.buildings') --- does not need the core suspended +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + return true +end + function get_num_filters(btype, subtype, custom) local filters = dfhack.buildings.getFiltersByType( {}, btype, subtype, custom) @@ -26,6 +46,9 @@ function get_num_filters(btype, subtype, custom) return 0 end +local dialogs = require('gui.dialogs') +local guidm = require('gui.dwarfmode') + local function to_title_case(str) str = str:gsub('(%a)([%w_]*)', function (first, rest) return first:upper()..rest:lower() end) From 27ba25cdbbf6c084c016465e85414e72f3a9c415 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 17 Jan 2023 09:34:56 -0600 Subject: [PATCH 0177/2222] remove compatibility with msvc express because it's not 2017 anymore --- CMakeLists.txt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 49bb29f04..37f44a727 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,14 +132,8 @@ endif() find_package(Perl REQUIRED) # set up folder structures for IDE solutions -# MSVC Express won't load solutions that use this. It also doesn't include MFC supported -# Check for MFC! -find_package(MFC QUIET) -if(MFC_FOUND OR (NOT MSVC)) - option(CMAKE_USE_FOLDERS "Enable folder grouping of projects in IDEs." ON) -else() - option(CMAKE_USE_FOLDERS "Enable folder grouping of projects in IDEs." OFF) -endif() +# checking for msvc express is meaningless now, all available editions of msvc support folder groupings +option(CMAKE_USE_FOLDERS "Enable folder grouping of projects in IDEs." ON) if(CMAKE_USE_FOLDERS) set_property(GLOBAL PROPERTY USE_FOLDERS ON) From acd2256900baba0e214982ba5db96364b21102c2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 17 Jan 2023 21:26:33 -0800 Subject: [PATCH 0178/2222] restore orders library functionality --- docs/changelog.txt | 1 + docs/plugins/orders.rst | 2 +- plugins/orders.cpp | 21 ++++++++------------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a5d4ab434..542c2b54f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) ## Misc Improvements diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index bc15fd175..8beb246e4 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -23,7 +23,7 @@ Usage one-time orders first, then yearly, seasonally, monthly, and finally, daily. You can keep your orders automatically sorted by adding the following command to -your ``onMapLoad.init`` file:: +your ``dfhack-config/init/onMapLoad.init`` file:: repeat -name orders-sort -time 1 -timeUnits days -command [ orders sort ] diff --git a/plugins/orders.cpp b/plugins/orders.cpp index 6118b3997..e3c57d0f1 100644 --- a/plugins/orders.cpp +++ b/plugins/orders.cpp @@ -41,7 +41,7 @@ DFHACK_PLUGIN("orders"); REQUIRE_GLOBAL(world); static const std::string ORDERS_DIR = "dfhack-config/orders"; -static const std::string ORDERS_LIBRARY_DIR = "dfhack-config/orders/library"; +static const std::string ORDERS_LIBRARY_DIR = "hack/data/orders"; static command_result orders_command(color_ostream & out, std::vector & parameters); @@ -86,6 +86,11 @@ static command_result orders_command(color_ostream & out, std::vector files; - if (0 < Filesystem::listdir_recursive(ORDERS_DIR, files, 0, false)) - { - out << COLOR_LIGHTRED << "Unable to list files in directory: " << ORDERS_DIR << std::endl; - return CR_FAILURE; - } + Filesystem::listdir_recursive(ORDERS_DIR, files, 0, false); - if (files.empty()) - { - out << COLOR_YELLOW << "No exported orders yet. Create manager orders and export them with 'orders export ', or copy pre-made orders .json files into " << ORDERS_DIR << "." << std::endl << std::endl; - } - - for (auto it : files) - { + for (auto it : files) { if (it.second) continue; // skip directories std::string name = it.first; From 7d8066f3c7c54c62d75dccf7c21da6b91fef350b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 17 Jan 2023 21:55:27 -0800 Subject: [PATCH 0179/2222] allow buildingplan to be "always on" --- data/init/dfhack.tools.init | 3 +++ plugins/buildingplan.cpp | 45 +++++++++++++------------------------ 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 741298109..fdad95be6 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -81,6 +81,9 @@ # Allow DFHack tools to overlay functionality and information on the DF screen enable overlay +# Allow buildings to be placed now and built later when materials are available +enable buildingplan + # Dwarf Manipulator (simple in-game Dwarf Therapist replacement) #enable manipulator diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 7080be4d5..09313c21a 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -46,7 +46,6 @@ static const string CONFIG_KEY = string(plugin_name) + "/config"; static const string BLD_CONFIG_KEY = string(plugin_name) + "/building"; enum ConfigValues { - CONFIG_IS_ENABLED = 0, CONFIG_BLOCKS = 1, CONFIG_BOULDERS = 2, CONFIG_LOGS = 3, @@ -79,12 +78,12 @@ public: PlannedBuilding(color_ostream &out, df::building *building) : id(building->id) { DEBUG(status,out).print("creating persistent data for building %d\n", id); - config = DFHack::World::AddPersistentData(BLD_CONFIG_KEY); - set_config_val(config, BLD_CONFIG_ID, id); + bld_config = DFHack::World::AddPersistentData(BLD_CONFIG_KEY); + set_config_val(bld_config, BLD_CONFIG_ID, id); } - PlannedBuilding(DFHack::PersistentDataItem &config) - : config(config), id(get_config_val(config, BLD_CONFIG_ID)) { } + PlannedBuilding(DFHack::PersistentDataItem &bld_config) + : bld_config(bld_config), id(get_config_val(bld_config, BLD_CONFIG_ID)) { } void remove(color_ostream &out); @@ -102,7 +101,7 @@ public: } private: - DFHack::PersistentDataItem config; + DFHack::PersistentDataItem bld_config; }; static PersistentDataItem config; @@ -144,16 +143,10 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector building_configs; - World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); + DEBUG(status,out).print("loading persisted state\n"); planned_buildings.clear(); tasks.clear(); + vector building_configs; + World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); for (size_t idx = 0; idx < num_building_configs; ++idx) registerPlannedBuilding(out, PlannedBuilding(building_configs[idx])); @@ -201,13 +187,9 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { if (event == DFHack::SC_WORLD_UNLOADED) { - if (is_enabled) { - DEBUG(status,out).print("world unloaded; disabling %s\n", - plugin_name); - is_enabled = false; - planned_buildings.clear(); - tasks.clear(); - } + DEBUG(status,out).print("world unloaded; clearing state for %s\n", plugin_name); + planned_buildings.clear(); + tasks.clear(); } return CR_OK; } @@ -215,6 +197,9 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan static bool cycle_requested = false; DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (!Core::getInstance().isWorldLoaded()) + return CR_OK; + if (is_enabled && (cycle_requested || world->frame_counter - cycle_timestamp >= CYCLE_TICKS)) do_cycle(out); @@ -245,7 +230,7 @@ static command_result do_command(color_ostream &out, vector ¶meters) CoreSuspender suspend; if (!Core::getInstance().isWorldLoaded()) { - out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + out.printerr("Cannot configure %s without a loaded world.\n", plugin_name); return CR_FAILURE; } From 3dd418a46ccbd633a42482690760cfa489ab0eb8 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 18 Jan 2023 07:14:53 +0000 Subject: [PATCH 0180/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index d3bc3b4d3..f50bd8cb4 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d3bc3b4d379d3511a1aae51da7ccd6f553198195 +Subproject commit f50bd8cb4cb9aa8fc4b57c4bb1cd95bd5f8c3582 From ba8e3c187b311f126b602bc033168a685bca330a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 17 Jan 2023 23:56:54 -0800 Subject: [PATCH 0181/2222] add reference to removed script ref: DFHack/scripts#531 --- docs/about/Removed.rst | 6 ++++++ scripts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 0fef8cbe9..d8bc204b8 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -124,6 +124,12 @@ Tool that warned the user when the ``dfhack.init`` file did not exist. Now that ``dfhack.init`` is autogenerated in ``dfhack-config/init``, this warning is no longer necessary. +.. _masspit: + +masspit +======= +Replaced with a GUI version: `gui/masspit`. + .. _resume: resume diff --git a/scripts b/scripts index f50bd8cb4..36c564406 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f50bd8cb4cb9aa8fc4b57c4bb1cd95bd5f8c3582 +Subproject commit 36c564406babb7734caeb691928083dd850eed8a From 653e09c322c895f0e3dc7c6d324ac2d5e046ad88 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 00:08:21 -0800 Subject: [PATCH 0182/2222] make gcc happy --- plugins/buildingplan.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 09313c21a..1407e5c46 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -83,7 +83,7 @@ public: } PlannedBuilding(DFHack::PersistentDataItem &bld_config) - : bld_config(bld_config), id(get_config_val(bld_config, BLD_CONFIG_ID)) { } + : id(get_config_val(bld_config, BLD_CONFIG_ID)), bld_config(bld_config) { } void remove(color_ostream &out); @@ -179,8 +179,10 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { vector building_configs; World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); - for (size_t idx = 0; idx < num_building_configs; ++idx) - registerPlannedBuilding(out, PlannedBuilding(building_configs[idx])); + for (size_t idx = 0; idx < num_building_configs; ++idx) { + PlannedBuilding pb(building_configs[idx]); + registerPlannedBuilding(out, pb); + } return CR_OK; } @@ -616,7 +618,7 @@ static void printStatus(color_ostream &out) { } static bool setSetting(color_ostream &out, string name, bool value) { - DEBUG(status,out).print("entering setSetting (%s -> %s)\n", name, value ? "true" : "false"); + DEBUG(status,out).print("entering setSetting (%s -> %s)\n", name.c_str(), value ? "true" : "false"); if (name == "blocks") set_config_bool(config, CONFIG_BLOCKS, value); else if (name == "boulders") @@ -660,7 +662,8 @@ static bool addPlannedBuilding(color_ostream &out, df::building *bld) { || !isPlannableBuilding(out, bld->getType(), bld->getSubtype(), bld->getCustomType())) return false; - return registerPlannedBuilding(out, PlannedBuilding(out, bld)); + PlannedBuilding pb(out, bld); + return registerPlannedBuilding(out, pb); } static void doCycle(color_ostream &out) { From b598b38891455cc7ef1b9a6b4578a81da08e8c22 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 18 Jan 2023 14:40:31 -0600 Subject: [PATCH 0183/2222] tailor: reenable and improve logging reenable plugin remove `using std;` switch to standard logger and add some debug and trace level log messages --- plugins/CMakeLists.txt | 2 +- plugins/tailor.cpp | 91 ++++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 661fa2624..71a4fef82 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -158,7 +158,7 @@ dfhack_plugin(showmood showmood.cpp) #add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(strangemood strangemood.cpp) -#dfhack_plugin(tailor tailor.cpp) +dfhack_plugin(tailor tailor.cpp) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-version title-version.cpp) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 0a8a82f5b..2b1d0c63c 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -6,6 +6,7 @@ #include "Core.h" #include "DataDefs.h" +#include "Debug.h" #include "PluginManager.h" #include "df/creature_raw.h" @@ -29,18 +30,23 @@ #include "modules/World.h" using namespace DFHack; -using namespace std; using df::global::world; using df::global::plotinfo; DFHACK_PLUGIN("tailor"); + #define AUTOENABLE false DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(plotinfo); +namespace DFHack { + DBG_DECLARE(tailor, cycle, DebugCategory::LINFO); + DBG_DECLARE(tailor, config, DebugCategory::LINFO); +} + class Tailor { // ARMOR, SHOES, HELM, GLOVES, PANTS @@ -48,7 +54,7 @@ class Tailor { private: - const map jobTypeMap = { + const std::map jobTypeMap = { { df::job_type::MakeArmor, df::item_type::ARMOR }, { df::job_type::MakePants, df::item_type::PANTS }, { df::job_type::MakeHelm, df::item_type::HELM }, @@ -56,7 +62,7 @@ private: { df::job_type::MakeShoes, df::item_type::SHOES } }; - const map itemTypeMap = { + const std::map itemTypeMap = { { df::item_type::ARMOR, df::job_type::MakeArmor }, { df::item_type::PANTS, df::job_type::MakePants }, { df::item_type::HELM, df::job_type::MakeHelm }, @@ -107,13 +113,13 @@ private: std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; - map, int> available; // key is item type & size - map, int> needed; // same - map, int> queued; // same + std::map, int> available; // key is item type & size + std::map, int> needed; // same + std::map, int> queued; // same - map sizes; // this maps body size to races + std::map sizes; // this maps body size to races - map, int> orders; // key is item type, item subtype, size + std::map, int> orders; // key is item type, item subtype, size std::map supply; @@ -147,7 +153,7 @@ private: df::item_type t = i->getType(); int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - available[make_pair(t, size)] += 1; + available[std::make_pair(t, size)] += 1; } } @@ -180,7 +186,7 @@ private: supply[M_LEATHER] += i->getStackSize(); } - out->print("tailor: available silk %d yarn %d cloth %d leather %d\n", supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER]); + DEBUG(cycle).print("tailor: available silk %d yarn %d cloth %d leather %d\n", supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER]); } void scan_replacements() @@ -193,10 +199,10 @@ private: Units::isBaby(u)) continue; // skip units we don't control - set wearing; + std::set wearing; wearing.clear(); - deque worn; + std::deque worn; worn.clear(); for (auto inv : u->inventory) @@ -212,10 +218,16 @@ private: int size = world->raws.creatures.all[u->race]->adultsize; sizes[size] = u->race; - for (auto ty : set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) + for (auto ty : std::set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) { if (wearing.count(ty) == 0) - needed[make_pair(ty, size)] += 1; + { + TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), + size, + Translation::TranslateName(&u->name, false).c_str()); + needed[std::make_pair(ty, size)] += 1; + } } for (auto w : worn) @@ -227,13 +239,13 @@ private: std::string description; w->getItemDescription(&description, 0); - if (available[make_pair(ty, size)] > 0) + if (available[std::make_pair(ty, size)] > 0) { if (w->flags.bits.owned) { bool confiscated = Items::setOwner(w, NULL); - out->print( + INFO(cycle).print( "tailor: %s %s from %s.\n", (confiscated ? "confiscated" : "could not confiscate"), description.c_str(), @@ -242,18 +254,22 @@ private: } if (wearing.count(ty) == 0) - available[make_pair(ty, size)] -= 1; + { + DEBUG(cycle).print("tailor: allocating a %s to %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), + Translation::TranslateName(&u->name, false).c_str()); + available[std::make_pair(ty, size)] -= 1; + } if (w->getWear() > 1) w->flags.bits.dump = true; } else { - // out->print("%s worn by %s needs replacement\n", - // description.c_str(), - // Translation::TranslateName(&u->name, false).c_str() - // ); - orders[make_tuple(o, w->getSubtype(), size)] += 1; + DEBUG(cycle).print ("%s worn by %s needs replacement, but none available\n", + description.c_str(), + Translation::TranslateName(&u->name, false).c_str()); + orders[std::make_tuple(o, w->getSubtype(), size)] += 1; } } } @@ -270,7 +286,7 @@ private: int count = a.second; int sub = 0; - vector v; + std::vector v; switch (ty) { case df::item_type::ARMOR: v = entity->resources.armor_type; break; @@ -299,7 +315,8 @@ private: } const df::job_type j = itemTypeMap.at(ty); - orders[make_tuple(j, sub, size)] += count; + orders[std::make_tuple(j, sub, size)] += count; + DEBUG(cycle).print("tailor: %s times %d of size %d ordered\n", ENUM_KEY_STR(job_type, j).c_str(), count, size); } } @@ -318,7 +335,11 @@ private: int size = world->raws.creatures.all[race]->adultsize; - orders[make_tuple(o->job_type, sub, size)] -= o->amount_left; + orders[std::make_tuple(o->job_type, sub, size)] -= o->amount_left; + TRACE(cycle).print("tailor: existing order for %d %s of size %d detected\n", + o->amount_left, + ENUM_KEY_STR(job_type, o->job_type).c_str(), + size); } } @@ -333,14 +354,14 @@ private: int sub; int size; - tie(ty, sub, size) = o.first; + std::tie(ty, sub, size) = o.first; int count = o.second; if (count > 0) { - vector v; + std::vector v; BitArray* fl; - string name_s, name_p; + std::string name_s, name_p; switch (ty) { @@ -382,7 +403,7 @@ private: if (!can_make) { - out->print("tailor: civilization cannot make %s, skipped\n", name_p.c_str()); + INFO(cycle).print("tailor: civilization cannot make %s, skipped\n", name_p.c_str()); continue; } @@ -416,7 +437,7 @@ private: world->manager_orders.push_back(order); - out->print("tailor: added order #%d for %d %s %s, sized for %s\n", + INFO(cycle).print("tailor: added order #%d for %d %s %s, sized for %s\n", order->id, c, bitfield_to_string(order->material_category).c_str(), @@ -464,9 +485,9 @@ public: } public: - command_result set_materials(color_ostream& out, vector& parameters) + command_result set_materials(color_ostream& out, std::vector& parameters) { - list newmat; + std::list newmat; newmat.clear(); for (auto m = parameters.begin() + 1; m != parameters.end(); m++) @@ -475,7 +496,7 @@ public: auto mm = std::find_if(all_materials.begin(), all_materials.end(), nameMatch); if (mm == all_materials.end()) { - out.print("tailor: material %s not recognized\n", m->c_str()); + WARN(config,out).print("tailor: material %s not recognized\n", m->c_str()); return CR_WRONG_USAGE; } else { @@ -484,7 +505,7 @@ public: } material_order = newmat; - out.print("tailor: material list set to %s\n", get_material_list().c_str()); + INFO(config,out).print("tailor: material list set to %s\n", get_material_list().c_str()); return CR_OK; } @@ -549,7 +570,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) return CR_OK; } -static command_result tailor_cmd(color_ostream& out, vector & parameters) { +static command_result tailor_cmd(color_ostream& out, std::vector & parameters) { bool desired = enabled; if (parameters.size() == 1 && (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1")) { From 1a283eaf47167f98c3398ba0c461c4e47721053d Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 18 Jan 2023 16:24:01 -0600 Subject: [PATCH 0184/2222] autofarm: fix repetition in status output because C++ std::map is not the same as a ruby table --- docs/changelog.txt | 1 + plugins/autofarm.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 542c2b54f..c119e0c89 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `autofarm`: don't duplicate status line entries for crops with no current supply - `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) ## Misc Improvements diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index 238627c3a..f7acde17f 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -332,6 +332,7 @@ public: void status(color_ostream& out) { out << "Autofarm is " << (enabled ? "Active." : "Stopped.") << '\n'; + for (auto& lc : lastCounts) { auto plant = world->raws.plants.all[lc.first]; @@ -340,7 +341,7 @@ public: for (auto& th : thresholds) { - if (lastCounts[th.first] > 0) + if (lastCounts.find(th.first) != lastCounts.end()) continue; auto plant = world->raws.plants.all[th.first]; out << plant->id << " limit " << getThreshold(th.first) << " current 0" << '\n'; From a813bcb76929cba6fbc17fddbcbc8a88d335cde4 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 18 Jan 2023 16:54:30 -0600 Subject: [PATCH 0185/2222] use slightly more succinct idiom --- plugins/autofarm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index f7acde17f..566c49355 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -341,7 +341,7 @@ public: for (auto& th : thresholds) { - if (lastCounts.find(th.first) != lastCounts.end()) + if (lastCounts.count(th.first) > 0) continue; auto plant = world->raws.plants.all[th.first]; out << plant->id << " limit " << getThreshold(th.first) << " current 0" << '\n'; From 2cf0d0769c0b2bfa90f6b60dfffab9f3f5845b34 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 15:44:47 -0800 Subject: [PATCH 0186/2222] fix reference to renamed var --- plugins/lua/overlay.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 42116735a..cf599fd08 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -348,7 +348,7 @@ end local function do_trigger(args, quiet) if triggered_screen_has_lock() then dfhack.printerr(('cannot trigger widget; widget "%s" is already active') - :format(active_triggered_widget)) + :format(trigger_lock_holder_description)) return end do_by_names_or_numbers(args[1], function(name, db_entry) From 452d44d9858003fae4dc2d4401346c326c9fb65b Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 18 Jan 2023 23:55:29 +0000 Subject: [PATCH 0187/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 9cd1b4ea9..dc8875f93 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9cd1b4ea9ab99919a64f2dfaa0c776ab007ec2d4 +Subproject commit dc8875f93437b18ec46c7721afa31e6857c1ae74 diff --git a/scripts b/scripts index 36c564406..7482da71a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 36c564406babb7734caeb691928083dd850eed8a +Subproject commit 7482da71ac3d869dda44854f4649b1be8318f69b From 5b1b0ca867b564a775c94eb50d6bfa781af2cb8f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 16:05:07 -0800 Subject: [PATCH 0188/2222] sync spreadsheet to docs --- docs/plugins/tailor.rst | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index d578fd308..af7d253b2 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -3,7 +3,7 @@ tailor .. dfhack-tool:: :summary: Automatically keep your dwarves in fresh clothing. - :tags: untested fort auto workorders + :tags: fort auto workorders Whenever the bookkeeper updates stockpile records, this plugin will scan the fort. If there are fresh cloths available, dwarves who are wearing tattered diff --git a/scripts b/scripts index 7482da71a..5df7b41a0 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7482da71ac3d869dda44854f4649b1be8318f69b +Subproject commit 5df7b41a05137ce9245eaaaaf85571a10214c005 From 086ca5bcff3e6a301aa1dad454a98cb56162fe6c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 16:15:49 -0800 Subject: [PATCH 0189/2222] tombstone create-items --- docs/about/Removed.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index d8bc204b8..6b812bd64 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -16,6 +16,12 @@ command-prompt ============== Replaced by `gui/launcher --minimal `. +.. _create-items: + +create-items +============ +Replaced by `gui/create-item --multi`. + .. _deteriorateclothes: deteriorateclothes From f9ac9873673aab117e5b707946cfd2f7d83f6417 Mon Sep 17 00:00:00 2001 From: Rose Date: Wed, 18 Jan 2023 20:52:58 -0800 Subject: [PATCH 0190/2222] Added bounds checking to various creature name functions in the Units module. --- library/modules/Units.cpp | 45 ++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 1fd72cb0b..05c0df0cb 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1025,9 +1025,12 @@ df::unit_misc_trait *Units::getMiscTrait(df::unit *unit, df::misc_trait_type typ // get race name by id or unit pointer string Units::getRaceNameById(int32_t id) { - df::creature_raw *raw = world->raws.creatures.all[id]; - if (raw) - return raw->creature_id; + if (id >= 0 && id < world->raws.creatures.all.size()) + { + df::creature_raw* raw = world->raws.creatures.all[id]; + if (raw) + return raw->creature_id; + } return ""; } string Units::getRaceName(df::unit* unit) @@ -1039,9 +1042,12 @@ string Units::getRaceName(df::unit* unit) // get human-readable race name by id or unit pointer string Units::getRaceReadableNameById(int32_t id) { - df::creature_raw* raw = world->raws.creatures.all[id]; - if (raw) - return raw->name[0]; + if (id >= 0 && id < world->raws.creatures.all.size()) + { + df::creature_raw* raw = world->raws.creatures.all[id]; + if (raw) + return raw->name[0]; + } return ""; } string Units::getRaceReadableName(df::unit* unit) @@ -1072,9 +1078,12 @@ string Units::getPhysicalDescription(df::unit* unit) // get plural of race name (used for display in autobutcher UI and for sorting the watchlist) string Units::getRaceNamePluralById(int32_t id) { - df::creature_raw *raw = world->raws.creatures.all[id]; - if (raw) - return raw->name[1]; // second field is plural of race name + if (id >= 0 && id < world->raws.creatures.all.size()) + { + df::creature_raw* raw = world->raws.creatures.all[id]; + if (raw) + return raw->name[1]; // second field is plural of race name + } return ""; } @@ -1086,9 +1095,12 @@ string Units::getRaceNamePlural(df::unit* unit) string Units::getRaceBabyNameById(int32_t id) { - df::creature_raw *raw = world->raws.creatures.all[id]; - if (raw) - return raw->general_baby_name[0]; + if (id >= 0 && id < world->raws.creatures.all.size()) + { + df::creature_raw* raw = world->raws.creatures.all[id]; + if (raw) + return raw->general_baby_name[0]; + } return ""; } @@ -1100,9 +1112,12 @@ string Units::getRaceBabyName(df::unit* unit) string Units::getRaceChildNameById(int32_t id) { - df::creature_raw *raw = world->raws.creatures.all[id]; - if (raw) - return raw->general_child_name[0]; + if (id >= 0 && id < world->raws.creatures.all.size()) + { + df::creature_raw* raw = world->raws.creatures.all[id]; + if (raw) + return raw->general_child_name[0]; + } return ""; } From d52aff8ff39a0e7db9cdede585891bc0fb988063 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 19 Jan 2023 07:14:40 +0000 Subject: [PATCH 0191/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index dc8875f93..bb4e4e053 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit dc8875f93437b18ec46c7721afa31e6857c1ae74 +Subproject commit bb4e4e05380404356321ba83f0160c31bf2ec647 diff --git a/scripts b/scripts index 5df7b41a0..ecab2af83 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5df7b41a05137ce9245eaaaaf85571a10214c005 +Subproject commit ecab2af833a4a3f5bb85226a739e64bc8f48f2ef From d5610ab85968f79b4d4261cf0c7e77e12fe81451 Mon Sep 17 00:00:00 2001 From: 20k Date: Thu, 19 Jan 2023 03:26:00 +0000 Subject: [PATCH 0192/2222] zone <-> building interop --- library/modules/Buildings.cpp | 268 +++++++++++++++++++++++++--------- 1 file changed, 203 insertions(+), 65 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 2e7143533..600be91d7 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -102,7 +102,6 @@ struct CoordHash { }; static unordered_map locationToBuilding; -static unordered_map, CoordHash> locationToCivzones; static df::building_extents_type *getExtentTile(df::building_extents &extent, df::coord2d tile) { @@ -115,18 +114,36 @@ static df::building_extents_type *getExtentTile(df::building_extents &extent, df return &extent.extents[dx + dy*extent.width]; } +void add_building_to_all_zones(df::building* bld); + +static void buildings_fixzones() +{ + auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; + + bool changed = false; + + for (size_t i = 0; i < vec.size(); i++) + { + df::building* bld = vec[i]; + + add_building_to_all_zones(bld); + } +} + /* * A monitor to work around this bug, in its application to buildings: * * http://www.bay12games.com/dwarves/mantisbt/view.php?id=1416 */ bool buildings_do_onupdate = false; +static bool buildings_do_fixzones = false; void buildings_onStateChange(color_ostream &out, state_change_event event) { switch (event) { case SC_MAP_LOADED: buildings_do_onupdate = true; + buildings_do_fixzones = true; break; case SC_MAP_UNLOADED: buildings_do_onupdate = false; @@ -138,6 +155,12 @@ void buildings_onStateChange(color_ostream &out, state_change_event event) void buildings_onUpdate(color_ostream &out) { + if (buildings_do_fixzones) + { + buildings_fixzones(); + buildings_do_fixzones = false; + } + buildings_do_onupdate = false; df::job_list_link *link = world->jobs.list.next; @@ -167,6 +190,163 @@ void buildings_onUpdate(color_ostream &out) } } +static void building_into_zone_unidir(df::building* bld, df::building_civzonest* zone) +{ + for (size_t bid = 0; bid < zone->contained_buildings.size(); bid++) + { + if (zone->contained_buildings[bid] == bld) + return; + } + + zone->contained_buildings.push_back(bld); + + std::sort(zone->contained_buildings.begin(), zone->contained_buildings.end(), [](df::building* b1, df::building* b2) + { + return b1->id < b2->id; + }); +} + +static void zone_into_building_unidir(df::building* bld, df::building_civzonest* zone) +{ + for (size_t bid = 0; bid < bld->relations.size(); bid++) + { + if (bld->relations[bid] == zone) + return; + } + + bld->relations.push_back(zone); + + std::sort(bld->relations.begin(), bld->relations.end(), [](df::building* b1, df::building* b2) + { + return b1->id < b2->id; + }); +} + +static void add_building_to_zone(df::building* bld, df::building_civzonest* zone) +{ + if (!bld->canBeRoom()) + return; + + building_into_zone_unidir(bld, zone); + zone_into_building_unidir(bld, zone); +} + +static bool is_suitable_building_for_zoning(df::building* bld) +{ + return bld->canBeRoom(); +} + +static void add_building_to_all_zones(df::building* bld) +{ + if (!is_suitable_building_for_zoning(bld)) + return; + + df::coord coord(bld->centerx, bld->centery, bld->z); + + std::vector cv; + Buildings::findCivzonesAt(&cv, coord); + + for (size_t i=0; i < cv.size(); i++) + { + add_building_to_zone(bld, cv[i]); + } +} + +static void add_zone_to_all_buildings(df::building* zone_as_building) +{ + if (zone_as_building->getType() != building_type::Civzone) + return; + + auto zone = strict_virtual_cast(zone_as_building); + + if (zone == nullptr) + return; + + auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; + + for (size_t i = 0; i < vec.size(); i++) + { + auto against = vec[i]; + + if (zone->z != against->z) + continue; + + if (!is_suitable_building_for_zoning(against)) + continue; + + int32_t cx = against->centerx; + int32_t cy = against->centery; + + df::coord2d coord(cx, cy); + + //can a zone without extents exist? + if (zone->room.extents && zone->isExtentShaped()) + { + auto etile = getExtentTile(zone->room, coord); + if (!etile || !*etile) + continue; + + add_building_to_zone(against, zone); + } + } +} + +static void remove_building_from_zone(df::building* bld, df::building_civzonest* zone) +{ + for (int bid = 0; bid < (int)zone->contained_buildings.size(); bid++) + { + if (zone->contained_buildings[bid] == bld) + { + zone->contained_buildings.erase(zone->contained_buildings.begin() + bid); + bid--; + } + } + + for (int bid = 0; bid < (int)bld->relations.size(); bid++) + { + if (bld->relations[bid] == zone) + { + bld->relations.erase(bld->relations.begin() + bid); + bid--; + } + } +} + +static void remove_building_from_all_zones(df::building* bld) +{ + df::coord coord(bld->centerx, bld->centery, bld->z); + + std::vector cv; + Buildings::findCivzonesAt(&cv, coord); + + for (size_t i=0; i < cv.size(); i++) + { + remove_building_from_zone(bld, cv[i]); + } +} + +static void remove_zone_from_all_buildings(df::building* zone_as_building) +{ + if (zone_as_building->getType() != building_type::Civzone) + return; + + auto zone = strict_virtual_cast(zone_as_building); + + if (zone == nullptr) + return; + + auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; + + //this is a paranoid check and slower than it could be. Zones contain a list of children + //good for fixing potentially bad game states when deleting a zone + for (size_t i = 0; i < vec.size(); i++) + { + df::building* bld = vec[i]; + + remove_building_from_zone(bld, zone); + } +} + uint32_t Buildings::getNumBuildings() { return world->buildings.all.size(); @@ -325,78 +505,30 @@ static void cacheBuilding(df::building *building, bool is_civzone) { for (int32_t y = p1.y; y <= p2.y; y++) { df::coord pt(x, y, building->z); if (Buildings::containsTile(building, pt, is_civzone)) { - if (is_civzone) - locationToCivzones[pt].push_back(id); - else + if (!is_civzone) locationToBuilding[pt] = id; } } } } -static int32_t nextCivzone = 0; -static void cacheNewCivzones() { - if (!world || !building_next_id) - return; - - int32_t nextBuildingId = *building_next_id; - for (int32_t id = nextCivzone; id < nextBuildingId; ++id) { - auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE]; - int32_t idx = df::building::binsearch_index(vec, id); - if (idx > -1) - cacheBuilding(vec[idx], true); - } - nextCivzone = nextBuildingId; -} - bool Buildings::findCivzonesAt(std::vector *pvec, df::coord pos) { pvec->clear(); - // Tiles have an occupancy->bits.building flag to quickly determine if it is - // covered by a buildling, but there is no such flag for civzones. - // Therefore, we need to make sure that our cache is authoratative. - // Otherwise, we would have to fall back to linearly scanning the list of - // all civzones on a cache miss. - // - // Since we guarantee our cache contains *at least* all tiles that are - // currently covered by civzones, we can conclude that if a tile is not in - // the cache, there is no civzone there. Civzones *can* be dynamically - // shrunk, so we still need to verify that civzones that once covered this - // tile continue to cover this tile. - cacheNewCivzones(); - - auto civzones_it = locationToCivzones.find(pos); - if (civzones_it == locationToCivzones.end()) - return false; - - set ids_to_remove; - auto &civzones = civzones_it->second; - for (int32_t id : civzones) { - int32_t idx = df::building::binsearch_index( - world->buildings.other[buildings_other_id::ANY_ZONE], id); - df::building_civzonest *civzone = NULL; - if (idx > -1) - civzone = world->buildings.other.ANY_ZONE[idx]; - if (!civzone || civzone->z != pos.z || - !containsTile(civzone, pos, true)) { - ids_to_remove.insert(id); + for (df::building_civzonest* zone : world->buildings.other.ACTIVITY_ZONE) + { + if (pos.z != zone->z) continue; - } - pvec->push_back(civzone); - } - // civzone no longer occupies this tile; update the cache - if (!ids_to_remove.empty()) { - for (auto it = civzones.begin(); it != civzones.end(); ) { - if (ids_to_remove.count(*it)) { - it = civzones.erase(it); + if (zone->room.extents && zone->isExtentShaped()) + { + auto etile = getExtentTile(zone->room, pos); + if (!etile || !*etile) continue; - } - ++it; + + pvec->push_back(zone); } - if (civzones.empty()) - locationToCivzones.erase(pos); } return !pvec->empty(); @@ -1053,7 +1185,6 @@ static int getMaxStockpileId() return max_id; } -/* TODO: understand how this changes for v50 static int getMaxCivzoneId() { auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE]; @@ -1068,7 +1199,6 @@ static int getMaxCivzoneId() return max_id; } -*/ bool Buildings::constructAbstract(df::building *bld) { @@ -1087,12 +1217,14 @@ bool Buildings::constructAbstract(df::building *bld) stock->stockpile_number = getMaxStockpileId() + 1; break; -/* TODO: understand how this changes for v50 case building_type::Civzone: if (auto zone = strict_virtual_cast(bld)) + { zone->zone_num = getMaxCivzoneId() + 1; + + add_zone_to_all_buildings(zone); + } break; -*/ default: break; @@ -1186,6 +1318,8 @@ bool Buildings::constructWithItems(df::building *bld, std::vector ite bld->mat_index = items[i]->getMaterialIndex(); } + add_building_to_all_zones(bld); + createDesign(bld, rough); return true; } @@ -1232,6 +1366,8 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectoruncategorize(); + + remove_building_from_all_zones(bld); + remove_zone_from_all_buildings(bld); + delete bld; if (world->selected_building == bld) @@ -1314,8 +1454,6 @@ void Buildings::clearBuildings(color_ostream& out) { corner1.clear(); corner2.clear(); locationToBuilding.clear(); - locationToCivzones.clear(); - nextCivzone = 0; } void Buildings::updateBuildings(color_ostream&, void* ptr) From c05b7526b1fe9af7dd913a9c5b241f437de4e802 Mon Sep 17 00:00:00 2001 From: cjhammel Date: Thu, 19 Jan 2023 15:12:24 -0500 Subject: [PATCH 0193/2222] Update Units.cpp fixes renamed value --- library/modules/Units.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 6d5ec7fde..787160f76 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1426,7 +1426,7 @@ int Units::computeMovementSpeed(df::unit *unit) if (isBaby(unit)) speed += 3000; - if (unit->flags3.bits.unk15) + if (unit->flags3.bits.diving) speed /= 20; if (unit->counters2.exhaustion >= 2000) From 5982644383dd427444bfd87f73ae2e7e7b081818 Mon Sep 17 00:00:00 2001 From: cjhammel Date: Thu, 19 Jan 2023 15:32:52 -0500 Subject: [PATCH 0194/2222] Update Removed.rst fixed formatting for create-items --- docs/about/Removed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 6b812bd64..5da3c2442 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -20,7 +20,7 @@ Replaced by `gui/launcher --minimal `. create-items ============ -Replaced by `gui/create-item --multi`. +Replaced by ``gui/create-item --multi``. .. _deteriorateclothes: From 8b7c8d83b599ef4ba437ef098a3fcf96b646b2fc Mon Sep 17 00:00:00 2001 From: 20k Date: Thu, 19 Jan 2023 17:49:24 +0000 Subject: [PATCH 0195/2222] remove save fixup Add notifyCivzoneModified --- docs/dev/Lua API.rst | 5 +++++ library/LuaApi.cpp | 1 + library/include/modules/Buildings.h | 5 +++++ library/modules/Buildings.cpp | 34 +++++++++-------------------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ab7d6434e..ebd82ce19 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1998,6 +1998,11 @@ Low-level building creation functions: Destroys the building, or queues a deconstruction job. Returns *true* if the building was destroyed and deallocated immediately. +* ``dfhack.buildings.notifyCivzoneModified(building)`` + + Rebuilds the civzone <-> overlapping building association mapping. + Call after changing extents or modifying size in some fashion + * ``dfhack.buildings.markedForRemoval(building)`` Returns *true* if the building is marked for removal (with :kbd:`x`), *false* diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 9f3d590dd..84612f790 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2239,6 +2239,7 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { WRAPM(Buildings, constructWithItems), WRAPM(Buildings, constructWithFilters), WRAPM(Buildings, deconstruct), + WRAPM(Buildings, notifyCivzoneModified), WRAPM(Buildings, markedForRemoval), WRAPM(Buildings, getRoomDescription), WRAPM(Buildings, isActivityZone), diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index aa539c02a..f4e8f37be 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -202,6 +202,11 @@ DFHACK_EXPORT bool deconstruct(df::building *bld); */ DFHACK_EXPORT bool markedForRemoval(df::building *bld); +/** + * Rebuilds a civzones building associations after it has been modified +*/ +DFHACK_EXPORT void notifyCivzoneModified(df::building* bld); + void updateBuildings(color_ostream& out, void* ptr); void clearBuildings(color_ostream& out); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 600be91d7..6e7190f9c 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -114,36 +114,18 @@ static df::building_extents_type *getExtentTile(df::building_extents &extent, df return &extent.extents[dx + dy*extent.width]; } -void add_building_to_all_zones(df::building* bld); - -static void buildings_fixzones() -{ - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - - bool changed = false; - - for (size_t i = 0; i < vec.size(); i++) - { - df::building* bld = vec[i]; - - add_building_to_all_zones(bld); - } -} - /* * A monitor to work around this bug, in its application to buildings: * * http://www.bay12games.com/dwarves/mantisbt/view.php?id=1416 */ bool buildings_do_onupdate = false; -static bool buildings_do_fixzones = false; void buildings_onStateChange(color_ostream &out, state_change_event event) { switch (event) { case SC_MAP_LOADED: buildings_do_onupdate = true; - buildings_do_fixzones = true; break; case SC_MAP_UNLOADED: buildings_do_onupdate = false; @@ -155,12 +137,6 @@ void buildings_onStateChange(color_ostream &out, state_change_event event) void buildings_onUpdate(color_ostream &out) { - if (buildings_do_fixzones) - { - buildings_fixzones(); - buildings_do_fixzones = false; - } - buildings_do_onupdate = false; df::job_list_link *link = world->jobs.list.next; @@ -1450,6 +1426,16 @@ bool Buildings::markedForRemoval(df::building *bld) return false; } +void Buildings::notifyCivzoneModified(df::building* bld) +{ + if (bld->getType() != building_type::Civzone) + return; + + //remove zone here needs to be the slow method + remove_zone_from_all_buildings(bld); + add_zone_to_all_buildings(bld); +} + void Buildings::clearBuildings(color_ostream& out) { corner1.clear(); corner2.clear(); From 4d9c411358e7619f51dafe2edb7a05d64cd49c11 Mon Sep 17 00:00:00 2001 From: 20k Date: Thu, 19 Jan 2023 22:17:54 +0000 Subject: [PATCH 0196/2222] fix flag --- library/modules/Buildings.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 6e7190f9c..653f8bccf 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -198,20 +198,20 @@ static void zone_into_building_unidir(df::building* bld, df::building_civzonest* }); } +static bool is_suitable_building_for_zoning(df::building* bld) +{ + return bld->canMakeRoom(); +} + static void add_building_to_zone(df::building* bld, df::building_civzonest* zone) { - if (!bld->canBeRoom()) + if (!is_suitable_building_for_zoning(bld)) return; building_into_zone_unidir(bld, zone); zone_into_building_unidir(bld, zone); } -static bool is_suitable_building_for_zoning(df::building* bld) -{ - return bld->canBeRoom(); -} - static void add_building_to_all_zones(df::building* bld) { if (!is_suitable_building_for_zoning(bld)) From 7b906cbf6fa6489a9f35e292de7cfc4946db9713 Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 19 Jan 2023 16:53:24 -0800 Subject: [PATCH 0197/2222] Update docs/about/Removed.rst Co-authored-by: Alan --- docs/about/Removed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 5da3c2442..ad36ed8a8 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -20,7 +20,7 @@ Replaced by `gui/launcher --minimal `. create-items ============ -Replaced by ``gui/create-item --multi``. +Replaced by `gui/create-item --multi `. .. _deteriorateclothes: From 298736d911e2074b94ab71ccfe500cc48238769b Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 20 Jan 2023 07:14:41 +0000 Subject: [PATCH 0198/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index ecab2af83..d6e975748 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ecab2af833a4a3f5bb85226a739e64bc8f48f2ef +Subproject commit d6e975748c4e4a468fe54ebcc82f7eeec3e1f662 From 4183bace4dfebfec0e9b46097586cc1a16a4267a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 13:38:23 -0800 Subject: [PATCH 0199/2222] update autodump, only commands, add hotkeys --- data/init/dfhack.keybindings.init | 17 +++++------ docs/plugins/autodump.rst | 40 ++++++++------------------ library/modules/Gui.cpp | 7 ++--- plugins/CMakeLists.txt | 2 +- plugins/autodump.cpp | 47 +++++++++++++++++-------------- 5 files changed, 51 insertions(+), 62 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index c4323ecbc..7ef615984 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -27,8 +27,8 @@ keybinding add Ctrl-Shift-K gui/cp437-table # dwarfmode bindings # ###################### -# quicksave, only in main dwarfmode screen and menu page -#keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave +# quicksave +keybinding add Ctrl-Alt-S@dwarfmode quicksave # toggle the display of water level as 1-7 tiles #keybinding add Ctrl-W@dwarfmode|dungeonmode twaterlvl @@ -40,14 +40,15 @@ keybinding add Ctrl-Shift-K gui/cp437-table # clean the selected tile of blood etc #keybinding add Ctrl-C spotclean -# destroy the selected item -#keybinding add Ctrl-K@dwarfmode autodump-destroy-item +# teleport all unforbidden items marked for dumping to the tile under the keyboard cursor +keybinding add Ctrl-Alt-D autodump -# destroy items designated for dump in the selected tile -#keybinding add Ctrl-Shift-K@dwarfmode autodump-destroy-here +# destroy the selected unit or items that are marked for dump under +keybinding add Ctrl-Alt-K autodump-destroy-here +#keybinding add Ctrl-Alt-K@dwarfmode "exterminate this" -# apply blueprints to the map (Alt-F for compatibility with LNP Quickfort) -#keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort +# apply blueprints to the map +keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort #keybinding add Alt-F@dwarfmode gui/quickfort # show information collected by dwarfmonitor diff --git a/docs/plugins/autodump.rst b/docs/plugins/autodump.rst index ec83b6082..facf70ca9 100644 --- a/docs/plugins/autodump.rst +++ b/docs/plugins/autodump.rst @@ -2,48 +2,32 @@ autodump ======== .. dfhack-tool:: - :summary: Automatically set items in a stockpile to be dumped. - :tags: untested fort armok fps productivity items stockpiles - :no-command: - -.. dfhack-command:: autodump - :summary: Teleports items marked for dumping to the cursor position. + :summary: Instantly gather or destroy items marked for dumping. + :tags: fort armok fps items .. dfhack-command:: autodump-destroy-here - :summary: Destroy items marked for dumping under the cursor. - -.. dfhack-command:: autodump-destroy-item - :summary: Destroys the selected item. + :summary: Destroy items marked for dumping under the keyboard cursor. -When `enabled `, this plugin adds an option to the :kbd:`q` menu for -stockpiles. When the ``autodump`` option is selected for the stockpile, any -items placed in the stockpile will automatically be designated to be dumped. +This tool can instantly move all unforbidden items marked for dumping to the +tile under the keyboard cursor. After moving the items, the dump flag is unset +and the forbid flag is set, just as if it had been dumped normally. Be aware +that dwarves that are en route to pick up the item for dumping may still come +and move the item to your dump zone. -When invoked as a command, it can instantly move all unforbidden items marked -for dumping to the tile under the cursor. After moving the items, the dump flag -is unset and the forbid flag is set, just as if it had been dumped normally. Be -aware that dwarves that are en route to pick up the item for dumping may still -come and move the item to your dump zone. - -The cursor must be placed on a floor tile so the items can be dumped there. +The keyboard cursor must be placed on a floor tile so the items can be dumped +there. Usage ----- :: - enable autodump autodump [] autodump-destroy-here - autodump-destroy-item ``autodump-destroy-here`` is an alias for ``autodump destroy-here`` and is intended for use as a keybinding. -``autodump-destroy-item`` destroys only the selected item. The item may be -selected in the :kbd:`k` list or in the container item list. If called again -before the game is resumed, cancels destruction of the item. - Options ------- @@ -67,5 +51,5 @@ Examples Teleports items marked for dumping to the cursor position. ``autodump destroy`` Destroys all unforbidden items marked for dumping -``autodump-destroy-item`` - Destroys the selected item. +``autodump-destroy-here`` + Destroys items on the selected tile that are marked for dumping. diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 0fb455fc9..35631dfc6 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -46,6 +46,7 @@ using namespace DFHack; #include "modules/Screen.h" #include "modules/Maps.h" #include "modules/Units.h" +#include "modules/World.h" #include "DataDefs.h" @@ -625,10 +626,8 @@ bool Gui::anywhere_hotkey(df::viewscreen *) { return true; } -bool Gui::dwarfmode_hotkey(df::viewscreen *top) -{ - // Require the main dwarf mode screen - return !!strict_virtual_cast(top); +bool Gui::dwarfmode_hotkey(df::viewscreen *top) { + return World::isFortressMode(); } bool Gui::unitjobs_hotkey(df::viewscreen *top) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 200cb3e2f..04bfceab1 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -78,7 +78,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) #dfhack_plugin(autoclothing autoclothing.cpp) -#dfhack_plugin(autodump autodump.cpp) +dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) #add_subdirectory(autolabor) diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index fb215c26b..c21f5c31b 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -1,33 +1,34 @@ // Quick Dumper : Moves items marked as "dump" to cursor // FIXME: local item cache in map blocks needs to be fixed after teleporting items -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; #include "Core.h" #include "Console.h" +#include "DataDefs.h" #include "Export.h" #include "PluginManager.h" + #include "modules/Maps.h" #include "modules/Gui.h" #include "modules/Items.h" #include "modules/Materials.h" #include "modules/MapCache.h" -#include "DataDefs.h" #include "df/item.h" #include "df/world.h" #include "df/general_ref.h" #include "df/viewscreen_dwarfmodest.h" #include "df/building_stockpilest.h" -#include "uicommon.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; using namespace DFHack; using namespace df::enums; @@ -40,6 +41,7 @@ DFHACK_PLUGIN("autodump"); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); +/* TODO: merge with stockpiles plugin // Stockpile interface START static const string PERSISTENCE_KEY = "autodump/stockpiles"; @@ -267,6 +269,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) } // Stockpile interface END +*/ command_result df_autodump(color_ostream &out, vector & parameters); command_result df_autodump_destroy_here(color_ostream &out, vector & parameters); @@ -276,18 +279,21 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector & parame MapCache MC; int dumped_total = 0; - int cx, cy, cz; DFCoord pos_cursor; if(!destroy || here) { - if (!Gui::getCursorCoords(cx,cy,cz)) - { - out.printerr("Cursor position not found. Please enable the cursor.\n"); + pos_cursor = Gui::getMousePos(); + if (!pos_cursor.isValid()) { + out.printerr("Mouse cursor must be over a suitable map tile.\n"); return CR_FAILURE; } - pos_cursor = DFCoord(cx,cy,cz); } if (!destroy) { @@ -441,13 +445,14 @@ command_result df_autodump(color_ostream &out, vector & parameters) command_result df_autodump_destroy_here(color_ostream &out, vector & parameters) { - // HOTKEY COMMAND; CORE ALREADY SUSPENDED if (!parameters.empty()) return CR_WRONG_USAGE; vector args; args.push_back("destroy-here"); + CoreSuspender suspend; + return autodump_main(out, args); } From eae2cec22f2911b6a08b166cee8a2ff9b482039a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 13:57:42 -0800 Subject: [PATCH 0200/2222] use keyboard cursor and adapt to a "bad" cursor not being equal to -30000 anymore --- library/include/df/custom/coord.methods.inc | 2 +- library/include/df/custom/coord2d.methods.inc | 2 +- library/lua/dfhack.lua | 4 ++-- library/modules/Gui.cpp | 6 +++--- plugins/autodump.cpp | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/library/include/df/custom/coord.methods.inc b/library/include/df/custom/coord.methods.inc index 6814be3c2..aa6eda484 100644 --- a/library/include/df/custom/coord.methods.inc +++ b/library/include/df/custom/coord.methods.inc @@ -3,7 +3,7 @@ coord(uint16_t _x, uint16_t _y, uint16_t _z) : x(_x), y(_y), z(_z) {} operator coord2d() const { return coord2d(x,y); } -bool isValid() const { return x != -30000; } +bool isValid() const { return x >= 0; } void clear() { x = y = z = -30000; } bool operator==(const coord &other) const diff --git a/library/include/df/custom/coord2d.methods.inc b/library/include/df/custom/coord2d.methods.inc index 202192bb8..512149ce3 100644 --- a/library/include/df/custom/coord2d.methods.inc +++ b/library/include/df/custom/coord2d.methods.inc @@ -1,6 +1,6 @@ coord2d(uint16_t _x, uint16_t _y) : x(_x), y(_y) {} -bool isValid() const { return x != -30000; } +bool isValid() const { return x >= 0; } void clear() { x = y = -30000; } bool operator==(const coord2d &other) const diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index e2cb7069b..b931b2ff4 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -321,7 +321,7 @@ end function pos2xyz(pos) if pos then local x = pos.x - if x and x ~= -30000 then + if x and x >= 0 then return x, pos.y, pos.z end end @@ -346,7 +346,7 @@ end function pos2xy(pos) if pos then local x = pos.x - if x and x ~= -30000 then + if x and x >= 0 then return x, pos.y end end diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 35631dfc6..c661cf254 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -649,7 +649,7 @@ bool Gui::item_details_hotkey(df::viewscreen *top) static bool has_cursor() { - return df::global::cursor && df::global::cursor->x != -30000; + return Gui::getCursorPos().isValid(); } bool Gui::cursor_hotkey(df::viewscreen *top) @@ -1720,7 +1720,7 @@ bool Gui::autoDFAnnouncement(df::report_init r, string message) // Check if the announcement will actually be announced if (*gamemode == game_mode::ADVENTURE) { - if (r.pos.x != -30000 && + if (r.pos.x >= 0 && r.type != announcement_type::CREATURE_SOUND && r.type != announcement_type::REGULAR_CONVERSATION && r.type != announcement_type::CONFLICT_CONVERSATION && @@ -2151,7 +2151,7 @@ bool Gui::getDesignationCoords (int32_t &x, int32_t &y, int32_t &z) x = selection_rect->start_x; y = selection_rect->start_y; z = selection_rect->start_z; - return (x == -30000) ? false : true; + return (x >= 0) ? false : true; } bool Gui::setDesignationCoords (const int32_t x, const int32_t y, const int32_t z) diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index c21f5c31b..aed6fa0ea 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -281,12 +281,12 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector & parame MapCache MC; int dumped_total = 0; - DFCoord pos_cursor; + df::coord pos_cursor; if(!destroy || here) { - pos_cursor = Gui::getMousePos(); + pos_cursor = Gui::getCursorPos(); if (!pos_cursor.isValid()) { - out.printerr("Mouse cursor must be over a suitable map tile.\n"); + out.printerr("Keyboard cursor must be over a suitable map tile.\n"); return CR_FAILURE; } } From a1a4125fd970e2a13fc49155e67f3a94857872e4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 18 Jan 2023 15:44:25 -0800 Subject: [PATCH 0201/2222] properly react to lack of a mouse cursor --- library/lua/gui.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 9f1db9d7c..530848f61 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -481,7 +481,7 @@ end function View:getMousePos(view_rect) local rect = view_rect or self.frame_body local x,y = dscreen.getMousePos() - if rect and rect:inClipGlobalXY(x,y) then + if rect and x and rect:inClipGlobalXY(x,y) then return rect:localXY(x,y) end end From 226a0ff1646d7a9d5af789ccd0daac2795577786 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 20 Jan 2023 09:39:37 -0800 Subject: [PATCH 0202/2222] revert autodump hotkeys. we need to discuss how keybindings work more --- data/init/dfhack.keybindings.init | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index 7ef615984..ea38f35ba 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -40,14 +40,13 @@ keybinding add Ctrl-Alt-S@dwarfmode quicksave # clean the selected tile of blood etc #keybinding add Ctrl-C spotclean -# teleport all unforbidden items marked for dumping to the tile under the keyboard cursor -keybinding add Ctrl-Alt-D autodump +# destroy the selected item +#keybinding add Ctrl-K@dwarfmode autodump-destroy-item -# destroy the selected unit or items that are marked for dump under -keybinding add Ctrl-Alt-K autodump-destroy-here -#keybinding add Ctrl-Alt-K@dwarfmode "exterminate this" +# destroy items designated for dump in the selected tile +#keybinding add Ctrl-Shift-K@dwarfmode autodump-destroy-here -# apply blueprints to the map +# apply blueprints to the map (Alt-F for compatibility with LNP Quickfort) keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort #keybinding add Alt-F@dwarfmode gui/quickfort From b7f90d543e6dfa1698d1646b3d9897990471c495 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:47:11 +0000 Subject: [PATCH 0203/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index bb4e4e053..5d43fd9fd 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit bb4e4e05380404356321ba83f0160c31bf2ec647 +Subproject commit 5d43fd9fd91007cf674bfde44b2c5a0ac70170db diff --git a/scripts b/scripts index d6e975748..76beb51ec 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d6e975748c4e4a468fe54ebcc82f7eeec3e1f662 +Subproject commit 76beb51ecb8897e5c2679a87b34e88f443d44f9a From 25c3bf4a2445162fd6f495b6ffcd3c2e893664d9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 20 Jan 2023 11:57:13 -0800 Subject: [PATCH 0204/2222] adjust to name change --- library/modules/Units.cpp | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 787160f76..a6e6daa46 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1936,7 +1936,7 @@ int Units::getStressCategory(df::unit *unit) if (!unit->status.current_soul) return int(stress_cutoffs.size()) / 2; - return getStressCategoryRaw(unit->status.current_soul->personality.stress_level); + return getStressCategoryRaw(unit->status.current_soul->personality.stress); } int Units::getStressCategoryRaw(int32_t stress_level) diff --git a/scripts b/scripts index 76beb51ec..5bdfd379b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 76beb51ecb8897e5c2679a87b34e88f443d44f9a +Subproject commit 5bdfd379b2d13f56bab455f94af56ec0ce3e0cb1 From e138ac1da5f7d6f7289d58d18f39996e8baf4749 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 20 Jan 2023 12:23:02 -0800 Subject: [PATCH 0205/2222] update stonesense --- plugins/stonesense | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stonesense b/plugins/stonesense index 6376bbc63..a045369db 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 6376bbc630feaf8b4f68623cdd2c28c87ac11af4 +Subproject commit a045369db6728979e709625908df3f4f36e868ca From e8f67c7b620026369d7c1c03b312c5cc16eeebe1 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 20 Jan 2023 15:21:45 -0600 Subject: [PATCH 0206/2222] add support for using undyed cloth also improved debugging messages --- docs/changelog.txt | 1 + plugins/tailor.cpp | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c119e0c89..240ea0bec 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `autofarm`: don't duplicate status line entries for crops with no current supply - `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) +- `tailor`: now respects the setting of the "used dyed clothing" standing order toggle ## Misc Improvements diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 2b1d0c63c..4e750cb3f 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -41,6 +41,7 @@ DFHACK_PLUGIN_IS_ENABLED(enabled); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(plotinfo); +REQUIRE_GLOBAL(standing_orders_use_dyed_cloth); namespace DFHack { DBG_DECLARE(tailor, cycle, DebugCategory::LINFO); @@ -159,12 +160,21 @@ private: void scan_materials() { + bool require_dyed = df::global::standing_orders_use_dyed_cloth ? (*df::global::standing_orders_use_dyed_cloth) : false; + for (auto i : world->items.other[df::items_other_id::CLOTH]) { if (i->flags.whole & bad_flags.whole) continue; - if (!i->hasImprovements()) // only count dyed + + if (require_dyed && !i->hasImprovements()) + { + // only count dyed + std::string d; + i->getItemDescription(&d, 0); + TRACE(cycle).print("tailor: skipping undyed %s\n", d.c_str()); continue; + } MaterialInfo mat(i); int ss = i->getStackSize(); @@ -176,6 +186,12 @@ private: supply[M_CLOTH] += ss; else if (mat.material->flags.is_set(df::material_flags::YARN)) supply[M_YARN] += ss; + else + { + std::string d; + i->getItemDescription(&d, 0); + WARN(cycle).print("tailor: weird cloth item found: %s (%d)\n", d.c_str(), i->id); + } } } @@ -266,7 +282,7 @@ private: } else { - DEBUG(cycle).print ("%s worn by %s needs replacement, but none available\n", + DEBUG(cycle).print ("tailor: %s worn by %s needs replacement, but none available\n", description.c_str(), Translation::TranslateName(&u->name, false).c_str()); orders[std::make_tuple(o, w->getSubtype(), size)] += 1; @@ -407,6 +423,8 @@ private: continue; } + DEBUG(cycle).print("tailor: ordering %d %s\n", count, name_p.c_str()); + for (auto& m : material_order) { if (count <= 0) @@ -418,7 +436,11 @@ private: if (supply[m] > res && fl->is_set(m.armor_flag)) { int c = count; if (supply[m] < count + res) + { c = supply[m] - res; + TRACE(cycle).print("tailor: order reduced from %d to %d to protect reserves of %s\n", + count, c, m.name.c_str()); + } supply[m] -= c; auto order = new df::manager_order; @@ -447,6 +469,11 @@ private: count -= c; } + else + { + TRACE(cycle).print("tailor: material %s skipped due to lack of reserves, %d available\n", m.name.c_str(), supply[m]); + } + } } } From 8e6252fc2b3cb2b5867efa9e8e4fa1374bebbe0a Mon Sep 17 00:00:00 2001 From: Rose Date: Fri, 20 Jan 2023 13:45:56 -0800 Subject: [PATCH 0207/2222] Small fixes to make things more clean. --- library/modules/Units.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 05c0df0cb..06cb4e62d 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1025,7 +1025,7 @@ df::unit_misc_trait *Units::getMiscTrait(df::unit *unit, df::misc_trait_type typ // get race name by id or unit pointer string Units::getRaceNameById(int32_t id) { - if (id >= 0 && id < world->raws.creatures.all.size()) + if (id >= 0 && (size_t)id < world->raws.creatures.all.size()) { df::creature_raw* raw = world->raws.creatures.all[id]; if (raw) @@ -1042,7 +1042,7 @@ string Units::getRaceName(df::unit* unit) // get human-readable race name by id or unit pointer string Units::getRaceReadableNameById(int32_t id) { - if (id >= 0 && id < world->raws.creatures.all.size()) + if (id >= 0 && (size_t)id < world->raws.creatures.all.size()) { df::creature_raw* raw = world->raws.creatures.all[id]; if (raw) @@ -1078,7 +1078,7 @@ string Units::getPhysicalDescription(df::unit* unit) // get plural of race name (used for display in autobutcher UI and for sorting the watchlist) string Units::getRaceNamePluralById(int32_t id) { - if (id >= 0 && id < world->raws.creatures.all.size()) + if (id >= 0 && (size_t)id < world->raws.creatures.all.size()) { df::creature_raw* raw = world->raws.creatures.all[id]; if (raw) @@ -1095,7 +1095,7 @@ string Units::getRaceNamePlural(df::unit* unit) string Units::getRaceBabyNameById(int32_t id) { - if (id >= 0 && id < world->raws.creatures.all.size()) + if (id >= 0 && (size_t)id < world->raws.creatures.all.size()) { df::creature_raw* raw = world->raws.creatures.all[id]; if (raw) @@ -1112,7 +1112,7 @@ string Units::getRaceBabyName(df::unit* unit) string Units::getRaceChildNameById(int32_t id) { - if (id >= 0 && id < world->raws.creatures.all.size()) + if (id >= 0 && (size_t)id < world->raws.creatures.all.size()) { df::creature_raw* raw = world->raws.creatures.all[id]; if (raw) From 8698016b06d491419e63e25dda1d44a5638b1ff5 Mon Sep 17 00:00:00 2001 From: Rose Date: Fri, 20 Jan 2023 13:54:18 -0800 Subject: [PATCH 0208/2222] Updated docs for Autoclothing --- docs/plugins/autoclothing.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 2e90d92a5..8fc72352d 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -15,6 +15,7 @@ Usage autoclothing autoclothing [quantity] + autoclothing report ``material`` can be "cloth", "silk", "yarn", or "leather". The ``item`` can be anything your civilization can produce, such as "dress" or "mitten". @@ -23,6 +24,10 @@ When invoked without parameters, it shows a summary of all managed clothing orders. When invoked with a material and item, but without a quantity, it shows the current configuration for that material and item. +``report`` gives a list of how many units of each race in your fortress that are +missing clothing in each available slot, as well as how much spare clothing you +have per slot, per race. + Examples -------- @@ -30,3 +35,5 @@ Examples Sets the desired number of cloth short skirts available per citizen to 10. ``autoclothing cloth dress`` Displays the currently set number of cloth dresses chosen per citizen. +``autoclothing report`` + Displays a report of your clothing situation. From acf2b8b319d71ad8ab569c4291022b01dee541e7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 20 Jan 2023 14:30:48 -0800 Subject: [PATCH 0209/2222] use newly narrowd relations vector --- library/xml | 2 +- plugins/autobutcher.cpp | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/library/xml b/library/xml index 5d43fd9fd..7c396a2e9 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 5d43fd9fd91007cf674bfde44b2c5a0ac70170db +Subproject commit 7c396a2e9df433bb4904e7d7eb0f46c80b48c721 diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index d75108f3f..7fb785cc1 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -10,6 +10,7 @@ #include #include "df/building_cagest.h" +#include "df/building_civzonest.h" #include "df/creature_raw.h" #include "df/world.h" @@ -718,14 +719,7 @@ static bool isInBuiltCageRoom(df::unit *unit) { if (building->getType() != df::building_type::Cage) continue; - bool in_zone = false; - for (auto relation : building->relations) { - if (relation->getType() == df::building_type::Civzone) { - in_zone = true; - break; - } - } - if (!in_zone) + if (!building->relations.size()) continue; df::building_cagest* cage = (df::building_cagest*)building; From ac483ed10550976ac6fca2bf48c85367401225c7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 20 Jan 2023 22:48:21 +0000 Subject: [PATCH 0210/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 5bdfd379b..b6e894ced 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5bdfd379b2d13f56bab455f94af56ec0ce3e0cb1 +Subproject commit b6e894ced9f185e6400ec19415458931480cb799 From 3fc4d056c30905ea2e793003e1de5f5c4fa5eb7b Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Fri, 20 Jan 2023 17:58:38 -0500 Subject: [PATCH 0211/2222] initial automelt --- plugins/CMakeLists.txt | 2 +- plugins/automelt.cpp | 804 +++++++++++++++++++++++++++++---------- plugins/lua/automelt.lua | 75 ++++ 3 files changed, 683 insertions(+), 198 deletions(-) create mode 100644 plugins/lua/automelt.lua diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3d36b95fc..e92bfaac7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -83,7 +83,7 @@ dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) #add_subdirectory(autolabor) #dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) -#dfhack_plugin(automelt automelt.cpp) +dfhack_plugin(automelt automelt.cpp LINK_LIBRARIES lua) #dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 9ca20e9ea..1808a6a59 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -1,294 +1,704 @@ -#include "uicommon.h" -#include "modules/Gui.h" +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" +#include "TileTypes.h" + +#include "modules/Buildings.h" +#include "modules/Maps.h" +#include "modules/Items.h" +#include "modules/World.h" +#include "modules/Designations.h" +#include "modules/Persistence.h" +#include "modules/Units.h" + +// #include "uicommon.h" #include "df/world.h" +#include "df/building.h" #include "df/world_raws.h" #include "df/building_def.h" #include "df/viewscreen_dwarfmodest.h" #include "df/building_stockpilest.h" -#include "modules/Buildings.h" -#include "modules/Items.h" #include "df/plotinfost.h" -#include "modules/Maps.h" -#include "modules/World.h" #include "df/item_quality.h" +#include +#include + using df::building_stockpilest; +using std::map; +using std::multimap; +using std::pair; +using std::string; +using std::unordered_map; +using std::vector; + +using namespace DFHack; +using namespace df::enums; DFHACK_PLUGIN("automelt"); -#define PLUGIN_VERSION 0.3 REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(cursor); REQUIRE_GLOBAL(plotinfo); -static const string PERSISTENCE_KEY = "automelt/stockpiles"; +namespace DFHack +{ + DBG_DECLARE(automelt, status, DebugCategory::LINFO); + DBG_DECLARE(automelt, cycle, DebugCategory::LINFO); + DBG_DECLARE(automelt, perf, DebugCategory::LTRACE); +} + +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static const string STOCKPILE_CONFIG_KEY_PREFIX = string(plugin_name) + "/stockpile/"; +static PersistentDataItem config; + +static vector watched_stockpiles; +static unordered_map watched_stockpiles_indices; + -static int mark_item(df::item *item, df::item_flags bad_flags, int32_t stockpile_id) +enum ConfigValues { - if (item->flags.whole & bad_flags.whole) - return 0; + CONFIG_IS_ENABLED = 0, - if (item->isAssignedToThisStockpile(stockpile_id)) { - size_t marked_count = 0; - std::vector contents; - Items::getContainedItems(item, &contents); - for (auto child = contents.begin(); child != contents.end(); child++) - { - marked_count += mark_item(*child, bad_flags, stockpile_id); - } +}; - return marked_count; - } +enum StockpileConfigValues +{ + STOCKPILE_CONFIG_ID = 0, + STOCKPILE_CONFIG_MONITORED = 1, + STOCKPILE_CONFIG_MELT = 2, - if (!can_melt(item)) - return 0; +}; - if (is_set_to_melt(item)) - return 0; +static int get_config_val(PersistentDataItem &c, int index) +{ + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) +{ + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) +{ + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) +{ + set_config_val(c, index, value ? 1 : 0); +} - insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item); - item->flags.bits.melt = true; - return 1; +static PersistentDataItem &ensure_stockpile_config(color_ostream &out, int id) +{ + if (watched_stockpiles_indices.count(id)) + return watched_stockpiles[watched_stockpiles_indices[id]]; + string keyname = STOCKPILE_CONFIG_KEY_PREFIX + int_to_string(id); + DEBUG(status, out).print("creating new persistent key for stockpile %d\n", id); + watched_stockpiles.emplace_back(World::GetPersistentData(keyname, NULL)); + size_t idx = watched_stockpiles.size() - 1; + watched_stockpiles_indices.emplace(id, idx); + return watched_stockpiles[idx]; } -static void mark_all_in_stockpiles(vector &stockpiles) +static void remove_stockpile_config(color_ostream &out, int id) { - // Precompute a bitmask with the bad flags - df::item_flags bad_flags; - bad_flags.whole = 0; + if (!watched_stockpiles_indices.count(id)) + return; + DEBUG(status, out).print("removing persistent key for stockpile %d\n", id); + size_t idx = watched_stockpiles_indices[id]; + World::DeletePersistentData(watched_stockpiles[idx]); + watched_stockpiles.erase(watched_stockpiles.begin() + idx); + watched_stockpiles_indices.erase(id); +} -#define F(x) bad_flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(artifact); - F(spider_web); F(owned); F(in_job); -#undef F +static void validate_stockpile_configs(color_ostream &out) +{ + for (auto &c : watched_stockpiles) { + int id = get_config_val(c, STOCKPILE_CONFIG_ID); + if (!df::building::find(id)){ + remove_stockpile_config(out, id); + } + } +} - size_t marked_count = 0; - for (auto it = stockpiles.begin(); it != stockpiles.end(); it++) - { - if (!it->isValid()) - continue; +static const int32_t CYCLE_TICKS = 1200; +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - auto spid = it->getId(); - Buildings::StockpileIterator stored; - for (stored.begin(it->getStockpile()); !stored.done(); ++stored) - { - marked_count += mark_item(*stored, bad_flags, spid); - } +static command_result do_command(color_ostream &out, vector ¶meters); +static int32_t do_cycle(color_ostream &out); + +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +{ + DEBUG(status, out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Auto melt items in designated stockpiles.", + do_command)); + + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (!Core::getInstance().isWorldLoaded()) + { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; } - if (marked_count) - Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items to melt", COLOR_GREEN, false); + if (enable != is_enabled) + { + is_enabled = enable; + DEBUG(status, out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } + else + { + DEBUG(status, out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); + } + return CR_OK; } -/* - * Stockpile Monitoring - */ +DFhackCExport command_result plugin_shutdown(color_ostream &out) +{ + DEBUG(status, out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} -class StockpileMonitor +DFhackCExport command_result plugin_load_data(color_ostream &out) { -public: - bool isMonitored(df::building_stockpilest *sp) + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) - { - if (it->matches(sp)) - return true; - } + DEBUG(status, out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } - return false; + // we have to copy our enabled flag into the global plugin variable, but + // all the other state we can directly read/modify from the persistent + // data structure. + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(status, out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); + World::GetPersistentData(&watched_stockpiles, STOCKPILE_CONFIG_KEY_PREFIX, true); + watched_stockpiles_indices.clear(); + const size_t num_watched_stockpiles = watched_stockpiles.size(); + for (size_t idx = 0; idx < num_watched_stockpiles; ++idx) + { + auto &c = watched_stockpiles[idx]; + watched_stockpiles_indices.emplace(get_config_val(c, STOCKPILE_CONFIG_ID), idx); } + validate_stockpile_configs(out); + + return CR_OK; +} - void add(df::building_stockpilest *sp) +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + if (event == DFHack::SC_WORLD_UNLOADED) { - auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY); - if (pile.isValid()) + if (is_enabled) { - monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); - monitored_stockpiles.back().save(); + DEBUG(status, out).print("world unloaded; disabling %s\n", plugin_name); + is_enabled = false; } } + + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) +{ + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) + { + int32_t marked = do_cycle(out); + if (0 < marked) + out.print("automelt: marked %d item(s) for melting\n", marked); + } + return CR_OK; +} + + +static bool call_automelt_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(status).print("calling automelt lua function: '%s'\n", fn_name); + + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.automelt", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} + +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + bool show_help = false; + if (!call_automelt_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; + } + + return show_help ? CR_WRONG_USAGE : CR_OK; +} + +static inline bool is_metal_item(df::item *item) +{ + MaterialInfo mat(item); + return (mat.getCraftClass() == craft_material_class::Metal); +} + +static bool isStockpile(df::building * building) { + return building->getType() == df::building_type::Stockpile; +} + +// Copied from Kelly Martin's code +static inline bool can_melt(df::item *item) +{ + + df::item_flags bad_flags; + bad_flags.whole = 0; + +#define F(x) bad_flags.bits.x = true; + F(dump); + F(forbid); + F(garbage_collect); + F(in_job); + F(hostile); + F(on_fire); + F(rotten); + F(trader); + F(in_building); + F(construction); + F(artifact); + F(melt); +#undef F + + if (item->flags.whole & bad_flags.whole) + return false; + + df::item_type t = item->getType(); + + if (t == df::enums::item_type::BOX || t == df::enums::item_type::BAR) + return false; + + if (!is_metal_item(item)) + return false; - void remove(df::building_stockpilest *sp) + for (auto g = item->general_refs.begin(); g != item->general_refs.end(); g++) { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++) + switch ((*g)->getType()) + { + case general_ref_type::CONTAINS_ITEM: + case general_ref_type::UNIT_HOLDER: + case general_ref_type::CONTAINS_UNIT: + return false; + case general_ref_type::CONTAINED_IN_ITEM: { - if (it->matches(sp)) + df::item *c = (*g)->getItem(); + for (auto gg = c->general_refs.begin(); gg != c->general_refs.end(); gg++) { - it->remove(); - monitored_stockpiles.erase(it); - break; + if ((*gg)->getType() == general_ref_type::UNIT_HOLDER) + return false; } } + break; + default: + break; + } } - void doCycle() - { - for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end();) - { - if (!it->isValid()) - it = monitored_stockpiles.erase(it); - else - ++it; - } + if (item->getQuality() >= item_quality::Masterful) + return false; + + return true; +} + +static inline bool is_set_to_melt(df::item *item) +{ + return item->flags.bits.melt; +} - mark_all_in_stockpiles(monitored_stockpiles); +static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flags, int32_t stockpile_id, int32_t &premarked_item_count, int32_t &item_count, bool should_melt) +{ + TRACE(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); + string name = ""; + item->getItemDescription(&name, 0); + TRACE(perf,out).print("item %s %d\n", name, item->id); + if (item->flags.whole & bad_flags.whole){ + TRACE(perf,out).print("rejected flag check\n"); + item_count++; + return 0; } - void reset() + if (item->isAssignedToThisStockpile(stockpile_id)) { - monitored_stockpiles.clear(); - std::vector items; - DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY); - - for (auto i = items.begin(); i != items.end(); i++) + TRACE(perf,out).print("assignedToStockpile\n"); + size_t marked_count = 0; + std::vector contents; + Items::getContainedItems(item, &contents); + for (auto child = contents.begin(); child != contents.end(); child++) { - auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY); - if (pile.load()) - monitored_stockpiles.push_back(PersistentStockpileInfo(pile)); - else - pile.remove(); + TRACE(perf,out).print("inside child loop\n"); + marked_count += mark_item(out, *child, bad_flags, stockpile_id, premarked_item_count, item_count, should_melt); } + return marked_count; } + TRACE(perf,out).print("past recurse loop\n"); -private: - vector monitored_stockpiles; -}; + if (is_set_to_melt(item)) { + TRACE(perf,out).print("already set to melt\n"); + premarked_item_count++; + item_count++; + return 0; + } + + if (!can_melt(item)) { + TRACE(perf,out).print("cannot melt\n"); + item_count++; + return 0; + } -static StockpileMonitor monitor; + TRACE(perf,out).print("increment item count\n"); + item_count++; -#define DELTA_TICKS 610 + //Only melt if told to, else count + if (should_melt) { + TRACE(perf,out).print("should_melt hit\n"); + insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item); + item->flags.bits.melt = 1; + return 1; + } else { + return 0; + } -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +} + + +static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, bool should_melt) { - if(!Maps::IsValid()) - return CR_OK; + TRACE(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d", plugin_name, should_melt); + // Precompute a bitmask with the bad flags + df::item_flags bad_flags; + bad_flags.whole = 0; + +#define F(x) bad_flags.bits.x = true; + F(dump); + F(forbid); + F(garbage_collect); + F(hostile); + F(on_fire); + F(rotten); + F(trader); + F(in_building); + F(construction); + F(artifact); + F(spider_web); + F(owned); + F(in_job); +#undef F - if (DFHack::World::ReadPauseState()) - return CR_OK; + size_t marked_count = 0; + + if(!stockpile.isValid()) { + return 0; + } - if (world->frame_counter % DELTA_TICKS != 0) - return CR_OK; + int spid = get_config_val(stockpile, STOCKPILE_CONFIG_ID); + auto found = df::building::find(spid); + if (!isStockpile(found)){ + + return 0; + } - monitor.doCycle(); + df::building_stockpilest * pile_cast = virtual_cast(found); - return CR_OK; + if (!pile_cast) + return 0; + + Buildings::StockpileIterator stored; + for (stored.begin(pile_cast); !stored.done(); ++stored) { + + marked_count += mark_item(out, *stored, bad_flags, spid, premarked_item_count, item_count, should_melt); + } + TRACE(perf,out).print("marked_count %d\npremarked_count %d\n", marked_count, premarked_item_count); + return marked_count; } -/* - * Interface - */ +static int32_t scan_stockpiles(color_ostream &out, bool disable_melt, map &item_counts, map &marked_item_counts, map &premarked_item_counts) { + TRACE(perf, out).print("%s running scan_stockpiles\n", plugin_name); + int32_t newly_marked = 0; + + // if (item_counts) + item_counts.clear(); + // if (marked_item_counts) + marked_item_counts.clear(); + // if (premarked_item_counts) + premarked_item_counts.clear(); -struct melt_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - bool handleInput(set *input) - { - if (Gui::inRenameBuilding()) - return false; + //Parse all the watched piles + for (auto &c : watched_stockpiles) { + int id = get_config_val(c, STOCKPILE_CONFIG_ID); + //Check for monitor status + bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); + //Check melt status + bool melt_enabled = get_config_bool(c, STOCKPILE_CONFIG_MELT); - building_stockpilest *sp = get_selected_stockpile(); - if (!sp) - return false; + melt_enabled = melt_enabled && (!disable_melt); - if (input->count(interface_key::CUSTOM_SHIFT_M)) - { - if (monitor.isMonitored(sp)) - monitor.remove(sp); - else - monitor.add(sp); - } + if (!monitored) continue; + + TRACE(perf,out).print("%s past monitor check\nmelt_enabled=%d", plugin_name, melt_enabled); + + int32_t item_count = 0; + + int32_t premarked_count = 0; + + int32_t marked = mark_all_in_stockpile(out, c, premarked_count, item_count, melt_enabled); + + item_counts.emplace(id, item_count); + + marked_item_counts.emplace(id, marked); + + premarked_item_counts.emplace(id, premarked_count); + + newly_marked += marked; - return false; } - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!handleInput(input)) - INTERPOSE_NEXT(feed)(input); + return newly_marked; +} + + +static int32_t do_cycle(color_ostream &out) { + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + cycle_timestamp = world->frame_counter; + + validate_stockpile_configs(out); + + int32_t marked_items_total; + map item_counts, marked_items_counts, premarked_item_counts; + + marked_items_total = scan_stockpiles(out, false, item_counts, marked_items_counts, premarked_item_counts); + + return marked_items_total; +} + +static void push_stockpile_config(lua_State *L, int id, bool monitored, + bool melt) { + map stockpile_config; + stockpile_config.emplace("id", id); + stockpile_config.emplace("monitored", monitored); + stockpile_config.emplace("melt", melt); + + Lua::Push(L, stockpile_config); +} + +static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { + push_stockpile_config(L, get_config_val(c, STOCKPILE_CONFIG_ID), + get_config_bool(c, STOCKPILE_CONFIG_MONITORED), + get_config_bool(c, STOCKPILE_CONFIG_MELT)); +} + +static void automelt_designate(color_ostream &out) { + DEBUG(status, out).print("entering automelt designate\n"); + out.print("designated %d item(s) for melting\n", do_cycle(out)); +} + +static void automelt_printStatus(color_ostream &out) { + DEBUG(status,out).print("entering automelt_printStatus\n"); + validate_stockpile_configs(out); + out.print("automelt is %s\n\n", is_enabled ? "enabled" : "disabled"); + + map item_counts, marked_item_counts, premarked_item_counts; + int32_t marked_items = scan_stockpiles(out, true, item_counts, marked_item_counts, premarked_item_counts); + + int32_t total_items_all_piles = 0; + int32_t premarked_items_all_piles = 0; + + + for (auto &i : watched_stockpiles) { + int id = get_config_val(i, STOCKPILE_CONFIG_ID); + total_items_all_piles+= item_counts[id]; + premarked_items_all_piles += premarked_item_counts[id]; } - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - building_stockpilest *sp = get_selected_stockpile(); - if (!sp) - return; - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = dims.y2 - 6; - - int links = 0; - links += sp->links.give_to_pile.size(); - links += sp->links.take_from_pile.size(); - links += sp->links.give_to_workshop.size(); - links += sp->links.take_from_workshop.size(); - bool state = monitor.isMonitored(sp); - - if (links + 12 >= y) { - y = dims.y2; - OutputString(COLOR_WHITE, x, y, "Auto: "); - x += 5; - OutputString(COLOR_LIGHTRED, x, y, "M"); - OutputString(state? COLOR_LIGHTGREEN: COLOR_GREY, x, y, "elt "); - } else { - OutputToggleString(x, y, "Auto melt", "M", state, true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); + out.print("summary:\n"); + out.print(" total items in monitored piles: %d\n", total_items_all_piles); + out.print(" marked items in monitored piles: %d\n", premarked_items_all_piles); + + int name_width = 11; + for (auto &stockpile : world->buildings.other.STOCKPILE) { + if (!isStockpile(stockpile)) continue; + name_width = std::max(name_width, (int)stockpile->name.size()); + } + name_width = -name_width; + + const char *fmt = "%*s %4s %4s %4s\n"; + out.print(fmt, name_width, "name", " id ", "monitored", "melt"); + out.print(fmt, name_width, "----", "----", "---------", "----"); + + for (auto &stockpile : world->buildings.other.STOCKPILE) { + if (!isStockpile(stockpile)) continue; + bool melt = false; + bool monitored = false; + if (watched_stockpiles_indices.count(stockpile->id)) { + auto &c = watched_stockpiles[watched_stockpiles_indices[stockpile->id]]; + melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); + monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); } + + out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), + monitored ? "[x]" : "[ ]", melt ? "[x]": "[ ]"); } -}; -IMPLEMENT_VMETHOD_INTERPOSE(melt_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(melt_hook, render); +} +static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitored, bool melt) { + DEBUG(status,out).print("entering automelt_setStockpileConfig\n"); + validate_stockpile_configs(out); + bool isInvalidStockpile = !df::building::find(id); + bool hasNoData = !melt && !monitored; + if (isInvalidStockpile || hasNoData) { + remove_stockpile_config(out, id); + return; + } + + PersistentDataItem &c = ensure_stockpile_config(out, id); + set_config_val(c, STOCKPILE_CONFIG_ID, id); + set_config_bool(c, STOCKPILE_CONFIG_MONITORED, monitored); + set_config_bool(c, STOCKPILE_CONFIG_MELT, melt); +} -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) - { - case DFHack::SC_MAP_LOADED: - monitor.reset(); - break; - case DFHack::SC_MAP_UNLOADED: - break; - default: - break; +static int automelt_getStockpileConfig(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status, *out).print("entering automelt_getStockpileConfig\n"); + validate_stockpile_configs(*out); + + int id; + if (lua_isnumber(L, -1)) { + id = lua_tointeger(L, -1); + if (!df::building::find(id)) + return 0; + + } else { + const char * name = lua_tostring(L, -1); + if (!name) + return 0; + string nameStr = name; + bool found = false; + for (auto &stockpile : world->buildings.other.STOCKPILE) { + if (!isStockpile(stockpile)) continue; + if (nameStr == stockpile->name) { + id = stockpile->id; + found = true; + break; + } + + } + if (!found) + return 0; } - return CR_OK; + + if (watched_stockpiles_indices.count(id)) { + push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); + } else { + push_stockpile_config(L, id, false, false); + } + return 1; } -DFHACK_PLUGIN_IS_ENABLED(is_enabled); +//TODO +static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering automelt_getItemCountsAndStockpileConfigs\n"); + validate_stockpile_configs(*out); -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (enable != is_enabled) - { - if (!INTERPOSE_HOOK(melt_hook, feed).apply(enable) || - !INTERPOSE_HOOK(melt_hook, render).apply(enable)) - return CR_FAILURE; + map item_counts, marked_item_counts, premarked_item_counts; + int32_t marked_items = scan_stockpiles(*out, true, item_counts, marked_item_counts, premarked_item_counts); - is_enabled = enable; + int32_t total_items_all_piles = 0; + int32_t premarked_items_all_piles = 0; + + + for (auto &i : watched_stockpiles) { + int id = get_config_val(i, STOCKPILE_CONFIG_ID); + total_items_all_piles+= item_counts[id]; + premarked_items_all_piles += premarked_item_counts[id]; } - return CR_OK; -} + map summary; + summary.emplace("total_items", total_items_all_piles); + summary.emplace("marked_items", marked_items); + summary.emplace("premarked_items", premarked_items_all_piles); + + Lua::Push(L, summary); + Lua::Push(L, item_counts); + Lua::Push(L, marked_item_counts); + Lua::Push(L, premarked_item_counts); + int32_t bldg_count = 0; + + for (auto pile : world->buildings.other.STOCKPILE) { + if (!isStockpile(pile)) + continue; + bldg_count++; + + int id = pile->id; + if (watched_stockpiles_indices.count(id)) { + push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); + } else { + push_stockpile_config(L, id, false, false); + } + } + return 4+bldg_count; -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - return CR_OK; -} -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - // ensure we disengage our hooks - plugin_enable(out, false); - return CR_OK; } + +DFHACK_PLUGIN_LUA_FUNCTIONS{ + DFHACK_LUA_FUNCTION(automelt_printStatus), + DFHACK_LUA_FUNCTION(automelt_designate), + DFHACK_LUA_FUNCTION(automelt_setStockpileConfig), + DFHACK_LUA_END}; + +DFHACK_PLUGIN_LUA_COMMANDS{ + DFHACK_LUA_COMMAND(automelt_getStockpileConfig), + DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), + DFHACK_LUA_END}; \ No newline at end of file diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua new file mode 100644 index 000000000..bba93b3c4 --- /dev/null +++ b/plugins/lua/automelt.lua @@ -0,0 +1,75 @@ +local _ENV = mkmodule('plugins.automelt') + +local argparse = require('argparse') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + + +local function do_set_stockpile_config(var_name, val, stockpiles) + for _,bspec in ipairs(argparse.stringList(stockpiles)) do + local config = automelt_getStockpileConfig(bspec) + config[var_name] = val + automelt_setStockpileConfig(config.id, config.monitor, config.melt) + end +end + + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = positionals[1] + if not command or command == 'status' then + automelt_printStatus() + elseif command == 'designate' then + automelt_designate() + elseif command == 'melt' then + do_set_stockpile_config('melt', true, args[2]) + elseif command == 'nomelt' then + do_set_stockpile_config('melt', false, args[2]) + elseif command == 'monitor' then + do_set_stockpile_config('monitor', true, args[2]) + elseif command == 'nomonitor'then + do_set_stockpile_config('monitor', false, args[2]) + else + return false + end + + return true +end + +-- used by gui/autochop +function setStockpileConfig(config) + automelt_setStockpileConfig(config.id, config.monitored, config.melt) +end + +function getItemCountsAndStockpileConfigs() + local data = {automelt_getItemCountsAndStockpileConfigs()} + local ret = {} + ret.summary = table.remove(data, 1) + ret.item_counts = table.remove(data, 1) + ret.marked_item_counts = table.remove(data, 1) + ret.premarked_item_counts = table.remove(data, 1) + ret.stockpile_configs = data + for _,c in ipairs(ret.stockpile_configs) do + c.name = df.building.find(c.id).name + c.monitored = c.monitored ~= 0 + c.melt = c.melt ~= 0 + end + return ret +end + +return _ENV From a98c4c0ff0706d136580440be6ca787e2424e3af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 20 Jan 2023 16:35:02 -0800 Subject: [PATCH 0212/2222] mark autoclothing as tested --- docs/plugins/autoclothing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 8fc72352d..9df013d19 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -3,7 +3,7 @@ autoclothing .. dfhack-tool:: :summary: Automatically manage clothing work orders. - :tags: untested fort auto workorders + :tags: fort auto workorders This command allows you to set how many of each clothing type every citizen should have. From bb16009f495472bc4814ab46841ad708e033f6db Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 20 Jan 2023 17:13:28 -0800 Subject: [PATCH 0213/2222] bump version to 50.05-alpha2 --- CMakeLists.txt | 2 +- docs/changelog.txt | 10 +++++++--- library/xml | 2 +- scripts | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 37f44a727..378ef57a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,7 +190,7 @@ endif() # set up versioning. set(DF_VERSION "50.05") -set(DFHACK_RELEASE "alpha1") +set(DFHACK_RELEASE "alpha2") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 240ea0bec..94b7d61c9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,9 +36,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- `autofarm`: don't duplicate status line entries for crops with no current supply -- `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) -- `tailor`: now respects the setting of the "used dyed clothing" standing order toggle ## Misc Improvements @@ -50,6 +47,13 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Removed +# 50.05-alpha2 + +## Fixes +- `autofarm`: don't duplicate status line entries for crops with no current supply +- `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) +- `tailor`: now respects the setting of the "used dyed clothing" standing order toggle + # 50.05-alpha1 ## Fixes diff --git a/library/xml b/library/xml index 7c396a2e9..65c61135e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7c396a2e9df433bb4904e7d7eb0f46c80b48c721 +Subproject commit 65c61135e05a8fe380e8216534615cb96b26c1ed diff --git a/scripts b/scripts index b6e894ced..4e97b8095 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit b6e894ced9f185e6400ec19415458931480cb799 +Subproject commit 4e97b809511660f7b5fc98e82f7d1cfff49395ee From e4e4cae5e67e2b841132474a3363a78514ef177b Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sat, 21 Jan 2023 02:08:58 -0500 Subject: [PATCH 0214/2222] fix check failures --- plugins/automelt.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 1808a6a59..13b8ef9c5 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -213,7 +213,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan is_enabled = false; } } - + return CR_OK; } @@ -354,7 +354,7 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag TRACE(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); string name = ""; item->getItemDescription(&name, 0); - TRACE(perf,out).print("item %s %d\n", name, item->id); + TRACE(perf,out).print("item %s %d\n", name.c_str(), item->id); if (item->flags.whole & bad_flags.whole){ TRACE(perf,out).print("rejected flag check\n"); item_count++; @@ -429,7 +429,7 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st F(in_job); #undef F - size_t marked_count = 0; + int32_t marked_count = 0; if(!stockpile.isValid()) { return 0; @@ -438,7 +438,7 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st int spid = get_config_val(stockpile, STOCKPILE_CONFIG_ID); auto found = df::building::find(spid); if (!isStockpile(found)){ - + return 0; } @@ -460,7 +460,7 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st static int32_t scan_stockpiles(color_ostream &out, bool disable_melt, map &item_counts, map &marked_item_counts, map &premarked_item_counts) { TRACE(perf, out).print("%s running scan_stockpiles\n", plugin_name); int32_t newly_marked = 0; - + // if (item_counts) item_counts.clear(); // if (marked_item_counts) @@ -485,7 +485,7 @@ static int32_t scan_stockpiles(color_ostream &out, bool disable_melt, map item_counts, marked_item_counts, premarked_item_counts; - int32_t marked_items = scan_stockpiles(out, true, item_counts, marked_item_counts, premarked_item_counts); + scan_stockpiles(out, true, item_counts, marked_item_counts, premarked_item_counts); int32_t total_items_all_piles = 0; int32_t premarked_items_all_piles = 0; @@ -581,7 +581,7 @@ static void automelt_printStatus(color_ostream &out) { monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); } - out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), + out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]" : "[ ]", melt ? "[x]": "[ ]"); } @@ -595,7 +595,7 @@ static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitor if (isInvalidStockpile || hasNoData) { remove_stockpile_config(out, id); return; - } + } PersistentDataItem &c = ensure_stockpile_config(out, id); set_config_val(c, STOCKPILE_CONFIG_ID, id); @@ -615,7 +615,7 @@ static int automelt_getStockpileConfig(lua_State *L) { id = lua_tointeger(L, -1); if (!df::building::find(id)) return 0; - + } else { const char * name = lua_tostring(L, -1); if (!name) @@ -668,18 +668,18 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { summary.emplace("total_items", total_items_all_piles); summary.emplace("marked_items", marked_items); summary.emplace("premarked_items", premarked_items_all_piles); - + Lua::Push(L, summary); Lua::Push(L, item_counts); Lua::Push(L, marked_item_counts); Lua::Push(L, premarked_item_counts); int32_t bldg_count = 0; - + for (auto pile : world->buildings.other.STOCKPILE) { if (!isStockpile(pile)) continue; bldg_count++; - + int id = pile->id; if (watched_stockpiles_indices.count(id)) { push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); From 0a66f411522e96802c56a637e761ef2a9cef6a1d Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sat, 21 Jan 2023 02:15:35 -0500 Subject: [PATCH 0215/2222] fix newline pre-commit.ci complaint --- plugins/automelt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 13b8ef9c5..aa73e8895 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -701,4 +701,5 @@ DFHACK_PLUGIN_LUA_FUNCTIONS{ DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(automelt_getStockpileConfig), DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), - DFHACK_LUA_END}; \ No newline at end of file + DFHACK_LUA_END}; + \ No newline at end of file From 50c3bea84bb72f81923f807486eaff66d5113442 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 21 Jan 2023 07:20:18 +0000 Subject: [PATCH 0216/2222] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- plugins/automelt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index aa73e8895..69f72d408 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -702,4 +702,4 @@ DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(automelt_getStockpileConfig), DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), DFHACK_LUA_END}; - \ No newline at end of file + From 8c68f54f50af134b82ce187d4ac88da84e9e7a2f Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 20 Jan 2023 18:02:55 +0000 Subject: [PATCH 0217/2222] update with squad removal support --- library/modules/Buildings.cpp | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 653f8bccf..d010210f5 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -82,6 +82,7 @@ using namespace DFHack; #include "df/map_block.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" +#include "df/squad.h" #include "df/ui_look_list.h" #include "df/unit.h" #include "df/unit_relationship_type.h" @@ -1348,6 +1349,43 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorgetType() != building_type::Civzone) + return; + + auto zone = strict_virtual_cast(bld); + + if (zone == nullptr) + return; + + for (df::building_civzonest::T_squad_room_info* room_info : zone->squad_room_info) + { + int32_t squad_id = room_info->squad_id; + + df::squad* squad = df::squad::find(squad_id); + + //if this is null, something has gone just *terribly* wrong + if (squad) + { + for (int i=0; i < squad->rooms.size(); i++) + { + if (squad->rooms[i]->building_id == bld->id) + { + auto room = squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + delete room; + i--; + } + } + } + + delete room_info; + } + + zone->squad_room_info.clear(); +} + bool Buildings::deconstruct(df::building *bld) { using df::global::plotinfo; @@ -1388,6 +1426,8 @@ bool Buildings::deconstruct(df::building *bld) remove_building_from_all_zones(bld); remove_zone_from_all_buildings(bld); + delete_civzone_squad_links(bld); + delete bld; if (world->selected_building == bld) From 1dbf01e5d19de1d5979574ca4b8a9ddee431dc9d Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 20 Jan 2023 18:30:38 +0000 Subject: [PATCH 0218/2222] Civzone interop style changes --- library/modules/Buildings.cpp | 58 ++++++++++------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index d010210f5..4ec9f9e12 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -169,34 +169,24 @@ void buildings_onUpdate(color_ostream &out) static void building_into_zone_unidir(df::building* bld, df::building_civzonest* zone) { - for (size_t bid = 0; bid < zone->contained_buildings.size(); bid++) + for (auto contained_building : zone->contained_buildings) { - if (zone->contained_buildings[bid] == bld) + if (contained_building == bld) return; } - zone->contained_buildings.push_back(bld); - - std::sort(zone->contained_buildings.begin(), zone->contained_buildings.end(), [](df::building* b1, df::building* b2) - { - return b1->id < b2->id; - }); + insert_into_vector(zone->contained_buildings, &df::building::id, bld); } static void zone_into_building_unidir(df::building* bld, df::building_civzonest* zone) { - for (size_t bid = 0; bid < bld->relations.size(); bid++) + for (auto relation : bld->relations) { - if (bld->relations[bid] == zone) + if (relation == zone) return; } - bld->relations.push_back(zone); - - std::sort(bld->relations.begin(), bld->relations.end(), [](df::building* b1, df::building* b2) - { - return b1->id < b2->id; - }); + insert_into_vector(bld->relations, &df::building::id, (df::building*)zone); } static bool is_suitable_building_for_zoning(df::building* bld) @@ -223,10 +213,8 @@ static void add_building_to_all_zones(df::building* bld) std::vector cv; Buildings::findCivzonesAt(&cv, coord); - for (size_t i=0; i < cv.size(); i++) - { - add_building_to_zone(bld, cv[i]); - } + for (auto zone : cv) + add_building_to_zone(bld, zone); } static void add_zone_to_all_buildings(df::building* zone_as_building) @@ -239,20 +227,16 @@ static void add_zone_to_all_buildings(df::building* zone_as_building) if (zone == nullptr) return; - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - - for (size_t i = 0; i < vec.size(); i++) + for (auto bld : world->buildings.other.IN_PLAY) { - auto against = vec[i]; - - if (zone->z != against->z) + if (zone->z != bld->z) continue; - if (!is_suitable_building_for_zoning(against)) + if (!is_suitable_building_for_zoning(bld)) continue; - int32_t cx = against->centerx; - int32_t cy = against->centery; + int32_t cx = bld->centerx; + int32_t cy = bld->centery; df::coord2d coord(cx, cy); @@ -263,7 +247,7 @@ static void add_zone_to_all_buildings(df::building* zone_as_building) if (!etile || !*etile) continue; - add_building_to_zone(against, zone); + add_building_to_zone(bld, zone); } } } @@ -296,10 +280,8 @@ static void remove_building_from_all_zones(df::building* bld) std::vector cv; Buildings::findCivzonesAt(&cv, coord); - for (size_t i=0; i < cv.size(); i++) - { - remove_building_from_zone(bld, cv[i]); - } + for (auto zone : cv) + remove_building_from_zone(bld, zone); } static void remove_zone_from_all_buildings(df::building* zone_as_building) @@ -312,16 +294,10 @@ static void remove_zone_from_all_buildings(df::building* zone_as_building) if (zone == nullptr) return; - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - //this is a paranoid check and slower than it could be. Zones contain a list of children //good for fixing potentially bad game states when deleting a zone - for (size_t i = 0; i < vec.size(); i++) - { - df::building* bld = vec[i]; - + for (auto bld : world->buildings.other.IN_PLAY) remove_building_from_zone(bld, zone); - } } uint32_t Buildings::getNumBuildings() From 3024c4a0dfdf3a281cf96d6576b33f44b1db23a7 Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 18:43:45 +0000 Subject: [PATCH 0219/2222] update to remove ambiguity after structures change --- library/modules/Buildings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 4ec9f9e12..8f0c68eb2 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -186,7 +186,7 @@ static void zone_into_building_unidir(df::building* bld, df::building_civzonest* return; } - insert_into_vector(bld->relations, &df::building::id, (df::building*)zone); + insert_into_vector(bld->relations, &df::building_civzonest::id, zone); } static bool is_suitable_building_for_zoning(df::building* bld) From 9054efd7c8b9ecf487816c1d59de7b92293ee9e9 Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 19:15:28 +0000 Subject: [PATCH 0220/2222] Update miscutils to support member pointer to a variable defined in the base when passing in a derived type --- library/include/MiscUtils.h | 8 ++++---- library/modules/Buildings.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 124987535..c9a5f66d6 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -167,8 +167,8 @@ int linear_index(const std::vector &vec, FT CT::*field, FT key) return -1; } -template -int binsearch_index(const std::vector &vec, FT CT::*field, FT key, bool exact = true) +template +int binsearch_index(const std::vector &vec, FT MT::*field, FT key, bool exact = true) { // Returns the index of the value >= the key int min = -1, max = (int)vec.size(); @@ -245,8 +245,8 @@ unsigned insert_into_vector(std::vector &vec, FT key, bool *inserted = NULL) return pos; } -template -unsigned insert_into_vector(std::vector &vec, FT CT::*field, CT *obj, bool *inserted = NULL) +template +unsigned insert_into_vector(std::vector &vec, FT MT::*field, CT *obj, bool *inserted = NULL) { unsigned pos = (unsigned)binsearch_index(vec, field, obj->*field, false); bool to_ins = (pos >= vec.size() || vec[pos] != obj); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 8f0c68eb2..9d525d96b 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -186,7 +186,7 @@ static void zone_into_building_unidir(df::building* bld, df::building_civzonest* return; } - insert_into_vector(bld->relations, &df::building_civzonest::id, zone); + insert_into_vector(bld->relations, &df::building_civzonest::id, zone); } static bool is_suitable_building_for_zoning(df::building* bld) @@ -1344,7 +1344,7 @@ static void delete_civzone_squad_links(df::building* bld) //if this is null, something has gone just *terribly* wrong if (squad) { - for (int i=0; i < squad->rooms.size(); i++) + for (int i=0; i < (int)squad->rooms.size(); i++) { if (squad->rooms[i]->building_id == bld->id) { From 598354d9d4d2271e606f57f720f911f6cfac43cc Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 20:31:39 +0000 Subject: [PATCH 0221/2222] getbiometype naming, docs --- docs/dev/Lua API.rst | 6 +++++- library/LuaApi.cpp | 8 ++++++++ library/include/modules/Maps.h | 4 ++-- library/modules/Maps.cpp | 6 +++--- plugins/autofarm.cpp | 2 +- plugins/embark-assistant/survey.cpp | 2 +- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ebd82ce19..8b2d8b513 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1779,6 +1779,10 @@ Maps module Returns the biome info struct for the given global map region. + ``dfhack.maps.getBiomeType(region_coord2d)`` or ``getBiomeType(x,y)`` + + Returns the biome_type for the given global map region. + * ``dfhack.maps.enableBlockUpdates(block[,flow,temperature])`` Enables updates for liquid flow or temperature, unless already active. @@ -1798,7 +1802,7 @@ Maps module * ``dfhack.maps.getTileBiomeRgn(coords)``, or ``getTileBiomeRgn(x,y,z)`` - Returns *x, y* for use with ``getRegionBiome``. + Returns *x, y* for use with ``getRegionBiome`` and ``getBiomeType``. * ``dfhack.maps.getPlantAtTile(pos)``, or ``getPlantAtTile(x,y,z)`` diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 84612f790..ceb79556f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2131,6 +2131,13 @@ static int maps_getPlantAtTile(lua_State *L) return 1; } +static int maps_getBiomeType(lua_State *L) +{ + auto pos = CheckCoordXY(L, 1, true); + lua_pushinteger(L, Maps::getBiomeType(pos.x, pos.y)); + return 1; +} + static const luaL_Reg dfhack_maps_funcs[] = { { "isValidTilePos", maps_isValidTilePos }, { "isTileVisible", maps_isTileVisible }, @@ -2141,6 +2148,7 @@ static const luaL_Reg dfhack_maps_funcs[] = { { "getRegionBiome", maps_getRegionBiome }, { "getTileBiomeRgn", maps_getTileBiomeRgn }, { "getPlantAtTile", maps_getPlantAtTile }, + { "getBiomeType", maps_getBiomeType }, { NULL, NULL } }; diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 225efae17..e468dcf9c 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -349,8 +349,8 @@ extern DFHACK_EXPORT df::plant *getPlantAtTile(int32_t x, int32_t y, int32_t z); inline df::plant *getPlantAtTile(df::coord pos) { return getPlantAtTile(pos.x, pos.y, pos.z); } -DFHACK_EXPORT df::enums::biome_type::biome_type GetBiomeType(int world_coord_x, int world_coord_y); -DFHACK_EXPORT df::enums::biome_type::biome_type GetBiomeTypeWithRef(int world_coord_x, int world_coord_y, int world_ref_y_coord); +DFHACK_EXPORT df::enums::biome_type::biome_type getBiomeType(int world_coord_x, int world_coord_y); +DFHACK_EXPORT df::enums::biome_type::biome_type getBiomeTypeWithRef(int world_coord_x, int world_coord_y, int world_ref_y_coord); } diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index e046faa0e..ddcd6078f 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -987,7 +987,7 @@ Return the biome type, given a position coordinate expressed in world_tiles The world ref coordinates are used for tropicality determination and may refer to a tile neighboring the "official" one. *****************************************************************************/ -df::enums::biome_type::biome_type Maps::GetBiomeTypeWithRef(int world_coord_x, +df::enums::biome_type::biome_type Maps::getBiomeTypeWithRef(int world_coord_x, int world_coord_y, int world_ref_coord_y ) @@ -1186,7 +1186,7 @@ df::enums::biome_type::biome_type Maps::GetBiomeTypeWithRef(int world_coord_x, Module main function. Return the biome type, given a position coordinate expressed in world_tiles *****************************************************************************/ -df::enums::biome_type::biome_type Maps::GetBiomeType(int world_coord_x, int world_coord_y) +df::enums::biome_type::biome_type Maps::getBiomeType(int world_coord_x, int world_coord_y) { - return Maps::GetBiomeTypeWithRef(world_coord_x, world_coord_y, world_coord_y); + return Maps::getBiomeTypeWithRef(world_coord_x, world_coord_y, world_coord_y); } diff --git a/plugins/autofarm.cpp b/plugins/autofarm.cpp index 566c49355..44253b2f9 100644 --- a/plugins/autofarm.cpp +++ b/plugins/autofarm.cpp @@ -315,7 +315,7 @@ public: biome = biome_type::SUBTERRANEAN_WATER; else { df::coord2d region(Maps::getTileBiomeRgn(df::coord(bb->centerx, bb->centery, bb->z))); - biome = Maps::GetBiomeType(region.x, region.y); + biome = Maps::getBiomeType(region.x, region.y); } farms[biome].push_back(farm); } diff --git a/plugins/embark-assistant/survey.cpp b/plugins/embark-assistant/survey.cpp index 80bf58fbd..9655fa886 100644 --- a/plugins/embark-assistant/survey.cpp +++ b/plugins/embark-assistant/survey.cpp @@ -790,7 +790,7 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat offset_count++; results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id; - results.biome[l] = DFHack::Maps::GetBiomeTypeWithRef(adjusted.x, adjusted.y, k); + results.biome[l] = DFHack::Maps::getBiomeTypeWithRef(adjusted.x, adjusted.y, k); temperature = world_data->region_map[adjusted.x][adjusted.y].temperature; negative = temperature < 0; From 0fbf17f1c8a3ef63e9b6681191d7bbb2d251ae9b Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sat, 21 Jan 2023 18:22:15 -0500 Subject: [PATCH 0222/2222] refactor/fix as per GH suggest --- data/init/dfhack.tools.init | 3 + plugins/automelt.cpp | 171 +++++++++++++++++++++--------------- plugins/lua/automelt.lua | 7 +- 3 files changed, 104 insertions(+), 77 deletions(-) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index fdad95be6..e2abf3dbe 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -84,6 +84,9 @@ enable overlay # Allow buildings to be placed now and built later when materials are available enable buildingplan +#Allow designated stockpiles to automatically mark items for melting +enable automelt + # Dwarf Manipulator (simple in-game Dwarf Therapist replacement) #enable manipulator diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index aa73e8895..cb6525628 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -11,6 +11,8 @@ #include "modules/Designations.h" #include "modules/Persistence.h" #include "modules/Units.h" +#include "modules/Screen.h" +#include "modules/Gui.h" // #include "uicommon.h" @@ -38,6 +40,7 @@ using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("automelt"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(cursor); @@ -47,7 +50,7 @@ namespace DFHack { DBG_DECLARE(automelt, status, DebugCategory::LINFO); DBG_DECLARE(automelt, cycle, DebugCategory::LINFO); - DBG_DECLARE(automelt, perf, DebugCategory::LTRACE); + DBG_DECLARE(automelt, perf, DebugCategory::LINFO); } static const string CONFIG_KEY = string(plugin_name) + "/config"; @@ -57,18 +60,10 @@ static PersistentDataItem config; static vector watched_stockpiles; static unordered_map watched_stockpiles_indices; - -enum ConfigValues -{ - CONFIG_IS_ENABLED = 0, - -}; - enum StockpileConfigValues { STOCKPILE_CONFIG_ID = 0, STOCKPILE_CONFIG_MONITORED = 1, - STOCKPILE_CONFIG_MELT = 2, }; @@ -131,8 +126,6 @@ static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); static int32_t do_cycle(color_ostream &out); -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { DEBUG(status, out).print("initializing %s\n", plugin_name); @@ -148,17 +141,10 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vectorframe_counter - cycle_timestamp >= CYCLE_TICKS) { int32_t marked = do_cycle(out); @@ -349,53 +332,57 @@ static inline bool is_set_to_melt(df::item *item) return item->flags.bits.melt; } +static inline bool is_in_job(df::item *item) { + return item->flags.bits.in_job; +} + static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flags, int32_t stockpile_id, int32_t &premarked_item_count, int32_t &item_count, bool should_melt) { - TRACE(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); + DEBUG(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); string name = ""; item->getItemDescription(&name, 0); - TRACE(perf,out).print("item %s %d\n", name.c_str(), item->id); + DEBUG(perf,out).print("item %s %d\n", name.c_str(), item->id); if (item->flags.whole & bad_flags.whole){ - TRACE(perf,out).print("rejected flag check\n"); + DEBUG(perf,out).print("rejected flag check\n"); item_count++; return 0; } if (item->isAssignedToThisStockpile(stockpile_id)) { - TRACE(perf,out).print("assignedToStockpile\n"); + DEBUG(perf,out).print("assignedToStockpile\n"); size_t marked_count = 0; std::vector contents; Items::getContainedItems(item, &contents); for (auto child = contents.begin(); child != contents.end(); child++) { - TRACE(perf,out).print("inside child loop\n"); + DEBUG(perf,out).print("inside child loop\n"); marked_count += mark_item(out, *child, bad_flags, stockpile_id, premarked_item_count, item_count, should_melt); } return marked_count; } - TRACE(perf,out).print("past recurse loop\n"); + DEBUG(perf,out).print("past recurse loop\n"); if (is_set_to_melt(item)) { - TRACE(perf,out).print("already set to melt\n"); + DEBUG(perf,out).print("already set to melt\n"); premarked_item_count++; item_count++; return 0; } if (!can_melt(item)) { - TRACE(perf,out).print("cannot melt\n"); + DEBUG(perf,out).print("cannot melt\n"); item_count++; return 0; } - TRACE(perf,out).print("increment item count\n"); + DEBUG(perf,out).print("increment item count\n"); item_count++; //Only melt if told to, else count if (should_melt) { - TRACE(perf,out).print("should_melt hit\n"); + DEBUG(perf,out).print("should_melt hit\n"); insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item); item->flags.bits.melt = 1; return 1; @@ -408,7 +395,7 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, bool should_melt) { - TRACE(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d", plugin_name, should_melt); + DEBUG(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d\n", plugin_name, should_melt); // Precompute a bitmask with the bad flags df::item_flags bad_flags; bad_flags.whole = 0; @@ -426,7 +413,6 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st F(artifact); F(spider_web); F(owned); - F(in_job); #undef F int32_t marked_count = 0; @@ -448,17 +434,20 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st return 0; Buildings::StockpileIterator stored; + DEBUG(perf,out).print("starting item iter. loop\n"); for (stored.begin(pile_cast); !stored.done(); ++stored) { - + DEBUG(perf,out).print("calling mark_item\n"); marked_count += mark_item(out, *stored, bad_flags, spid, premarked_item_count, item_count, should_melt); + DEBUG(perf,out).print("end mark_item call\n"); } - TRACE(perf,out).print("marked_count %d\npremarked_count %d\n", marked_count, premarked_item_count); + DEBUG(perf,out).print("end item iter. loop\n"); + DEBUG(perf,out).print("exit mark_all_in_stockpile\nmarked_count %d\npremarked_count %d\n", marked_count, premarked_item_count); return marked_count; } -static int32_t scan_stockpiles(color_ostream &out, bool disable_melt, map &item_counts, map &marked_item_counts, map &premarked_item_counts) { - TRACE(perf, out).print("%s running scan_stockpiles\n", plugin_name); +static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_counts, map &marked_item_counts, map &premarked_item_counts) { + DEBUG(perf,out).print("running scan_stockpiles\n"); int32_t newly_marked = 0; // if (item_counts) @@ -472,22 +461,18 @@ static int32_t scan_stockpiles(color_ostream &out, bool disable_melt, map item_counts, marked_items_counts, premarked_item_counts; - marked_items_total = scan_stockpiles(out, false, item_counts, marked_items_counts, premarked_item_counts); - + marked_items_total = scan_stockpiles(out, true, item_counts, marked_items_counts, premarked_item_counts); + DEBUG(perf,out).print("exit %s do_cycle\n", plugin_name); return marked_items_total; } -static void push_stockpile_config(lua_State *L, int id, bool monitored, - bool melt) { +static int getSelectedStockpile(color_ostream &out) { + int32_t stock_id = 0; + df::building *selected_bldg = NULL; + selected_bldg = Gui::getSelectedBuilding(out, true); + if (selected_bldg->getType() != df::building_type::Stockpile) { + DEBUG(status,out).print("Selected building is not stockpile\n"); + return -1; + } + + return selected_bldg->id; +} + +static PersistentDataItem *getSelectedStockpileConfig(color_ostream &out) { + int32_t bldg_id = getSelectedStockpile(out); + if (bldg_id == -1) { + DEBUG(status,out).print("Selected bldg invalid\n"); + return NULL; + } + + validate_stockpile_configs(out); + PersistentDataItem *c = NULL; + if (watched_stockpiles_indices.count(bldg_id)) { + c = &(watched_stockpiles[watched_stockpiles_indices[bldg_id]]); + return c; + } else { + DEBUG(status,out).print("No existing config\n"); + return NULL; + } + +} + + + +static void push_stockpile_config(lua_State *L, int id, bool monitored) { map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); - stockpile_config.emplace("melt", melt); Lua::Push(L, stockpile_config); } static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { push_stockpile_config(L, get_config_val(c, STOCKPILE_CONFIG_ID), - get_config_bool(c, STOCKPILE_CONFIG_MONITORED), - get_config_bool(c, STOCKPILE_CONFIG_MELT)); + get_config_bool(c, STOCKPILE_CONFIG_MONITORED)); } static void automelt_designate(color_ostream &out) { @@ -544,7 +559,7 @@ static void automelt_printStatus(color_ostream &out) { out.print("automelt is %s\n\n", is_enabled ? "enabled" : "disabled"); map item_counts, marked_item_counts, premarked_item_counts; - scan_stockpiles(out, true, item_counts, marked_item_counts, premarked_item_counts); + scan_stockpiles(out, false, item_counts, marked_item_counts, premarked_item_counts); int32_t total_items_all_piles = 0; int32_t premarked_items_all_piles = 0; @@ -567,31 +582,29 @@ static void automelt_printStatus(color_ostream &out) { } name_width = -name_width; - const char *fmt = "%*s %4s %4s %4s\n"; - out.print(fmt, name_width, "name", " id ", "monitored", "melt"); - out.print(fmt, name_width, "----", "----", "---------", "----"); + const char *fmt = "%*s %4s %4s\n"; + out.print(fmt, name_width, "name", " id ", "monitored"); + out.print(fmt, name_width, "----", "----", "---------"); for (auto &stockpile : world->buildings.other.STOCKPILE) { if (!isStockpile(stockpile)) continue; - bool melt = false; bool monitored = false; if (watched_stockpiles_indices.count(stockpile->id)) { auto &c = watched_stockpiles[watched_stockpiles_indices[stockpile->id]]; - melt = get_config_bool(c, STOCKPILE_CONFIG_MELT); monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); } - out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), - monitored ? "[x]" : "[ ]", melt ? "[x]": "[ ]"); + out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]": "[ ]"); } + DEBUG(status,out).print("exiting automelt_printStatus\n"); } -static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitored, bool melt) { +static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitored) { DEBUG(status,out).print("entering automelt_setStockpileConfig\n"); validate_stockpile_configs(out); bool isInvalidStockpile = !df::building::find(id); - bool hasNoData = !melt && !monitored; + bool hasNoData = !monitored; if (isInvalidStockpile || hasNoData) { remove_stockpile_config(out, id); return; @@ -600,7 +613,6 @@ static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitor PersistentDataItem &c = ensure_stockpile_config(out, id); set_config_val(c, STOCKPILE_CONFIG_ID, id); set_config_bool(c, STOCKPILE_CONFIG_MONITORED, monitored); - set_config_bool(c, STOCKPILE_CONFIG_MELT, melt); } static int automelt_getStockpileConfig(lua_State *L) { @@ -638,11 +650,29 @@ static int automelt_getStockpileConfig(lua_State *L) { if (watched_stockpiles_indices.count(id)) { push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); } else { - push_stockpile_config(L, id, false, false); + push_stockpile_config(L, id, false); } return 1; } +static int automelt_getSelectedStockpileConfig(lua_State *L){ + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status, *out).print("entering automelt_getSelectedStockpileConfig\n"); + validate_stockpile_configs(*out); + + int32_t stock_id = getSelectedStockpile(*out); + PersistentDataItem *c = getSelectedStockpileConfig(*out); + + //If we have a valid config entry, use that. Else assume all false. + if (c) { + push_stockpile_config(L, *c); + } else { + push_stockpile_config(L, stock_id, false); + } +} + //TODO static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); @@ -652,7 +682,7 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { validate_stockpile_configs(*out); map item_counts, marked_item_counts, premarked_item_counts; - int32_t marked_items = scan_stockpiles(*out, true, item_counts, marked_item_counts, premarked_item_counts); + int32_t marked_items = scan_stockpiles(*out, false, item_counts, marked_item_counts, premarked_item_counts); int32_t total_items_all_piles = 0; int32_t premarked_items_all_piles = 0; @@ -684,12 +714,12 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { if (watched_stockpiles_indices.count(id)) { push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); } else { - push_stockpile_config(L, id, false, false); + push_stockpile_config(L, id, false); } } - return 4+bldg_count; - + DEBUG(perf, *out).print("exit automelt_getItemCountsAndStockpileConfigs\n"); + return 4+bldg_count; } DFHACK_PLUGIN_LUA_FUNCTIONS{ @@ -702,4 +732,3 @@ DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(automelt_getStockpileConfig), DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), DFHACK_LUA_END}; - \ No newline at end of file diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index bba93b3c4..2cccc7342 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -36,10 +36,6 @@ function parse_commandline(...) automelt_printStatus() elseif command == 'designate' then automelt_designate() - elseif command == 'melt' then - do_set_stockpile_config('melt', true, args[2]) - elseif command == 'nomelt' then - do_set_stockpile_config('melt', false, args[2]) elseif command == 'monitor' then do_set_stockpile_config('monitor', true, args[2]) elseif command == 'nomonitor'then @@ -53,7 +49,7 @@ end -- used by gui/autochop function setStockpileConfig(config) - automelt_setStockpileConfig(config.id, config.monitored, config.melt) + automelt_setStockpileConfig(config.id, config.monitored) end function getItemCountsAndStockpileConfigs() @@ -67,7 +63,6 @@ function getItemCountsAndStockpileConfigs() for _,c in ipairs(ret.stockpile_configs) do c.name = df.building.find(c.id).name c.monitored = c.monitored ~= 0 - c.melt = c.melt ~= 0 end return ret end From 9a42e399899ebf333dc4781b77affc829e97508e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 21 Jan 2023 16:19:29 -0800 Subject: [PATCH 0223/2222] fix sample commandline in dreamfort docs --- docs/guides/quickfort-library-guide.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/quickfort-library-guide.rst b/docs/guides/quickfort-library-guide.rst index 157bb10a1..74b384cc0 100644 --- a/docs/guides/quickfort-library-guide.rst +++ b/docs/guides/quickfort-library-guide.rst @@ -33,8 +33,8 @@ and bedrooms for hundreds of dwarves. It also comes with manager work orders to automate basic fort needs, such as food, booze, and item production. It can function by itself or as the core of a larger, more ambitious fortress. Read the high-level walkthrough by running ``quickfort run library/dreamfort.csv`` and -list the walkthroughs for the individual levels by running ``quickfort list -l -dreamfort -m notes`` or ``gui/quickfort dreamfort notes``. +list the walkthroughs for the individual levels by running ``quickfort list +dreamfort notes`` or ``gui/quickfort dreamfort notes``. Dreamfort blueprints are available for easy viewing and copying `online `__. From aa4cc8ff4153d20ca33a991428d266d6b496b8f7 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Sun, 22 Jan 2023 16:42:19 +1100 Subject: [PATCH 0224/2222] Update orders related to bags for v50.x Bags now have their own item type, they're no longer mixed in with boxes --- data/orders/basic.json | 38 ++++++++++++++++---------------------- data/orders/furnace.json | 5 ++--- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/data/orders/basic.json b/data/orders/basic.json index b3fae8dcd..1ab67c743 100644 --- a/data/orders/basic.json +++ b/data/orders/basic.json @@ -256,10 +256,9 @@ "condition" : "AtLeast", "flags" : [ - "empty", - "bag" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 5 } ], @@ -310,10 +309,9 @@ "condition" : "AtLeast", "flags" : [ - "empty", - "bag" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 5 } ], @@ -844,14 +842,13 @@ "condition" : "AtMost", "flags" : [ - "empty", - "sewn_imageless" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 30 } ], - "job" : "ConstructChest", + "job" : "ConstructBag", "material_category" : [ "leather" @@ -880,14 +877,13 @@ "condition" : "AtMost", "flags" : [ - "empty", - "sewn_imageless" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 30 } ], - "job" : "ConstructChest", + "job" : "ConstructBag", "material_category" : [ "silk" @@ -916,14 +912,13 @@ "condition" : "AtMost", "flags" : [ - "empty", - "sewn_imageless" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 30 } ], - "job" : "ConstructChest", + "job" : "ConstructBag", "material_category" : [ "cloth" @@ -952,14 +947,13 @@ "condition" : "AtMost", "flags" : [ - "empty", - "sewn_imageless" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 30 } ], - "job" : "ConstructChest", + "job" : "ConstructBag", "material_category" : [ "yarn" diff --git a/data/orders/furnace.json b/data/orders/furnace.json index 54d7d758e..62e389ded 100644 --- a/data/orders/furnace.json +++ b/data/orders/furnace.json @@ -152,10 +152,9 @@ "condition" : "AtLeast", "flags" : [ - "empty", - "bag" + "empty" ], - "item_type" : "BOX", + "item_type" : "BAG", "value" : 10 }, { From 3f9f4fe2439843c584b2979e76f942cc5eb37ef8 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Sun, 22 Jan 2023 17:02:56 +1100 Subject: [PATCH 0225/2222] Attempt to fix lye order limits --- data/orders/basic.json | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/data/orders/basic.json b/data/orders/basic.json index 1ab67c743..badcbf85f 100644 --- a/data/orders/basic.json +++ b/data/orders/basic.json @@ -1131,8 +1131,11 @@ }, { "condition" : "AtMost", - "item_type" : "LIQUID_MISC", - "material" : "LYE", + "contains" : + [ + "lye" + ], + "reaction_id" : "MAKE_SOAP_FROM_TALLOW", "value" : 5 } ], @@ -1160,8 +1163,11 @@ }, { "condition" : "AtLeast", - "item_type" : "LIQUID_MISC", - "material" : "LYE", + "contains" : + [ + "lye" + ], + "reaction_id" : "MAKE_SOAP_FROM_TALLOW", "value" : 3 }, { @@ -1194,8 +1200,11 @@ }, { "condition" : "AtLeast", - "item_type" : "LIQUID_MISC", - "material" : "LYE", + "contains" : + [ + "lye" + ], + "reaction_id" : "MAKE_SOAP_FROM_OIL", "value" : 3 }, { From 11befd9726f986bad258c1a75461b9ea153c24fb Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 22 Jan 2023 07:13:34 +0000 Subject: [PATCH 0226/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 4e97b8095..9666cb8eb 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 4e97b809511660f7b5fc98e82f7d1cfff49395ee +Subproject commit 9666cb8ebcc51f8e4bb2fb99116636793a7e4270 From bb5a3ac8e937a243a8b9e0b9807d191a24c54a77 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 00:06:03 -0800 Subject: [PATCH 0227/2222] fix library order for minecarts --- data/orders/basic.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/orders/basic.json b/data/orders/basic.json index badcbf85f..16b05e0ba 100644 --- a/data/orders/basic.json +++ b/data/orders/basic.json @@ -723,6 +723,10 @@ }, { "condition" : "AtMost", + "flags" : + [ + "empty" + ], "item_subtype" : "ITEM_TOOL_MINECART", "item_type" : "TOOL", "value" : 2 From 5a4d61e7fc22adf5c82145912b2860cbdaaa1b64 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 00:43:10 -0800 Subject: [PATCH 0228/2222] don't close the hotspot menu if the logo is clicked some people click the logo by mistake. this shouldn't close the menu --- plugins/lua/hotkeys.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 2ec38fff6..a6eaf7d81 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -32,7 +32,7 @@ function HotspotMenuWidget:overlay_onupdate() end function HotspotMenuWidget:overlay_trigger() - return MenuScreen{hotspot_frame=self.frame}:show() + return MenuScreen{hotspot=self}:show() end local dscreen = dfhack.screen @@ -74,9 +74,9 @@ local ARROW = string.char(26) local MAX_LIST_WIDTH = 45 local MAX_LIST_HEIGHT = 15 -Menu = defclass(MenuScreen, widgets.Panel) +Menu = defclass(Menu, widgets.Panel) Menu.ATTRS{ - hotspot_frame=DEFAULT_NIL, + hotspot=DEFAULT_NIL, } -- get a map from the binding string to a list of hotkey strings that all @@ -136,10 +136,10 @@ end function Menu:init() local hotkeys, bindings = getHotkeys() - local is_inverted = not not self.hotspot_frame.b + local is_inverted = not not self.hotspot.frame.b local choices,list_width = get_choices(hotkeys, bindings, is_inverted) - local list_frame = copyall(self.hotspot_frame) + local list_frame = copyall(self.hotspot.frame) local list_widget_frame = {h=math.min(#choices, MAX_LIST_HEIGHT)} local quickstart_frame = {} list_frame.w = list_width + 2 @@ -248,7 +248,7 @@ function Menu:onInput(keys) self:onSubmit2(list:getSelected()) return true end - if not self:getMouseFramePos() then + if not self:getMouseFramePos() and not self.hotspot:getMousePos() then self.parent_view:dismiss() return true end @@ -297,12 +297,12 @@ end MenuScreen = defclass(MenuScreen, gui.ZScreen) MenuScreen.ATTRS { focus_path='hotkeys/menu', - hotspot_frame=DEFAULT_NIL, + hotspot=DEFAULT_NIL, } function MenuScreen:init() self:addviews{ - Menu{hotspot_frame=self.hotspot_frame}, + Menu{hotspot=self.hotspot}, } end From 0f433c61a170fe8bd1566e15486bb3e533e395c1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 00:44:14 -0800 Subject: [PATCH 0229/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 94b7d61c9..5e1904e43 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `hotkeys`: clicking on the DFHack logo no longer closes the popup menu ## Documentation From 76822ac7b783f0e4be0fb60b3fe29d404d6472ea Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 00:59:27 -0800 Subject: [PATCH 0230/2222] remove orphan fortplan.lua file --- plugins/lua/fortplan.lua | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 plugins/lua/fortplan.lua diff --git a/plugins/lua/fortplan.lua b/plugins/lua/fortplan.lua deleted file mode 100644 index 71e69c69d..000000000 --- a/plugins/lua/fortplan.lua +++ /dev/null @@ -1,13 +0,0 @@ -local _ENV = mkmodule('plugins.fortplan') - -require('dfhack.buildings') - -function construct_building_from_params(building_type, x, y, z) - local pos = xyz2pos(x, y, z) - local bld, err = - dfhack.buildings.constructBuilding{type=building_type, pos=pos} - if err then error(err) end - return bld -end - -return _ENV From b84bce719d5dfa01e93b2eb12b8526a4d5a751d5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 01:07:30 -0800 Subject: [PATCH 0231/2222] remove unused always_enabled attribute for overlays it was made for gui/pathable, but that became a ZScreen this option made me uncomfortable for overlays. it didn't seem like the right user experience --- docs/dev/overlay-dev-guide.rst | 4 ---- plugins/lua/overlay.lua | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index bce623180..50998530a 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -125,10 +125,6 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes: not annoy the player. Set to 0 to be called at the maximum rate. Be aware that running more often than you really need to will impact game FPS, especially if your widget can run while the game is unpaused. -- ``always_enabled`` (default: ``false``) - Set this to ``true`` if you don't want to let the user disable the widget. - This is useful for widgets that are controlled purely through their - triggers. See `gui/pathable` for an example. Registering a widget with the overlay framework *********************************************** diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index cf599fd08..b90c1c651 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -184,7 +184,6 @@ end local function do_disable(args, quiet) local disable_fn = function(name, db_entry) - if db_entry.widget.always_enabled then return end overlay_config[name].enabled = false if db_entry.widget.hotspot then active_hotspot_widgets[name] = nil @@ -252,7 +251,7 @@ local function load_widget(name, widget_class) local config = overlay_config[name] config.pos = sanitize_pos(config.pos or widget.default_pos) widget.frame = make_frame(config.pos, widget.frame) - if config.enabled or widget.always_enabled then + if config.enabled then do_enable(name, true, true) else config.enabled = false @@ -492,7 +491,6 @@ OverlayWidget.ATTRS{ hotspot=false, -- whether to call overlay_onupdate on all screens viewscreens={}, -- override with associated viewscreen or list of viewscrens overlay_onupdate_max_freq_seconds=5, -- throttle calls to overlay_onupdate - always_enabled=false, -- for overlays that should never be disabled } function OverlayWidget:init() From 6585055ed244be7adcec919d7e401567bc2dcbc8 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 04:13:23 -0500 Subject: [PATCH 0232/2222] fixes, global item counts --- plugins/automelt.cpp | 163 +++++++++++++++++++++++++++++-------------- 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 71421bacd..031c574f4 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -73,15 +73,18 @@ static int get_config_val(PersistentDataItem &c, int index) return -1; return c.ival(index); } + static bool get_config_bool(PersistentDataItem &c, int index) { return get_config_val(c, index) == 1; } + static void set_config_val(PersistentDataItem &c, int index, int value) { if (c.isValid()) c.ival(index) = value; } + static void set_config_bool(PersistentDataItem &c, int index, bool value) { set_config_val(c, index, value ? 1 : 0); @@ -336,7 +339,8 @@ static inline bool is_in_job(df::item *item) { return item->flags.bits.in_job; } -static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flags, int32_t stockpile_id, int32_t &premarked_item_count, int32_t &item_count, bool should_melt) +static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flags, int32_t stockpile_id, + int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); string name = ""; @@ -357,7 +361,7 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag for (auto child = contents.begin(); child != contents.end(); child++) { DEBUG(perf,out).print("inside child loop\n"); - marked_count += mark_item(out, *child, bad_flags, stockpile_id, premarked_item_count, item_count, should_melt); + marked_count += mark_item(out, *child, bad_flags, stockpile_id, premarked_item_count, item_count, tracked_item_map, should_melt); } return marked_count; } @@ -366,7 +370,9 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag if (is_set_to_melt(item)) { DEBUG(perf,out).print("already set to melt\n"); + tracked_item_map.emplace(item->id, true); premarked_item_count++; + DEBUG(perf,out).print("premarked_item_count=%d\n", premarked_item_count); item_count++; return 0; } @@ -385,6 +391,7 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag DEBUG(perf,out).print("should_melt hit\n"); insert_into_vector(world->items.other[items_other_id::ANY_MELT_DESIGNATED], &df::item::id, item); item->flags.bits.melt = 1; + tracked_item_map.emplace(item->id, true); return 1; } else { return 0; @@ -393,7 +400,7 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag } -static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, bool should_melt) +static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d\n", plugin_name, should_melt); // Precompute a bitmask with the bad flags @@ -437,8 +444,8 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st DEBUG(perf,out).print("starting item iter. loop\n"); for (stored.begin(pile_cast); !stored.done(); ++stored) { DEBUG(perf,out).print("calling mark_item\n"); - marked_count += mark_item(out, *stored, bad_flags, spid, premarked_item_count, item_count, should_melt); - DEBUG(perf,out).print("end mark_item call\n"); + marked_count += mark_item(out, *stored, bad_flags, spid, premarked_item_count, item_count, tracked_item_map, should_melt); + DEBUG(perf,out).print("end mark_item call\npremarked_item_count=%d\n", premarked_item_count); } DEBUG(perf,out).print("end item iter. loop\n"); DEBUG(perf,out).print("exit mark_all_in_stockpile\nmarked_count %d\npremarked_count %d\n", marked_count, premarked_item_count); @@ -446,17 +453,14 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st } -static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_counts, map &marked_item_counts, map &premarked_item_counts) { +static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_count_piles, map &premarked_item_count_piles, + map &marked_item_count_piles, map &tracked_item_map) { DEBUG(perf,out).print("running scan_stockpiles\n"); int32_t newly_marked = 0; - // if (item_counts) - item_counts.clear(); - // if (marked_item_counts) - marked_item_counts.clear(); - // if (premarked_item_counts) - premarked_item_counts.clear(); - + item_count_piles.clear(); + premarked_item_count_piles.clear(); + marked_item_count_piles.clear(); //Parse all the watched piles for (auto &c : watched_stockpiles) { @@ -472,13 +476,15 @@ static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &tracked_item_map) { + + DEBUG(perf,out).print("running scan_all_melt_designated\n"); + int32_t marked_item_count = 0; + //loop over all items marked as melt-designated + for (auto item : world->items.other[items_other_id::ANY_MELT_DESIGNATED]) { + //item has already been marked/counted as inside a stockpile. Don't double-count. + if (tracked_item_map.count(item->id)) { + continue; + } + marked_item_count++; + } + DEBUG(perf,out).print("exiting scan_all_melt_designated\n"); + return marked_item_count; +} + +static int32_t scan_count_all(color_ostream &out, bool should_melt, int32_t &marked_item_count_total, int32_t &marked_total_count_all_piles, int32_t &marked_item_count_global, + int32_t &total_items_all_piles, map &item_count_piles, map &premarked_item_count_piles, map &marked_item_count_piles) { + + //Wraps both scan_stockpiles and scan_all_melt_designated + //Returns count of items in piles freshly marked + + int32_t newly_marked_items_piles = 0; + + map tracked_item_map_piles; + + tracked_item_map_piles.clear(); + + newly_marked_items_piles = scan_stockpiles(out, should_melt, item_count_piles, premarked_item_count_piles, marked_item_count_piles, tracked_item_map_piles); + marked_item_count_global = scan_all_melt_designated(out, tracked_item_map_piles); + + for (auto &i : watched_stockpiles) { + int id = get_config_val(i, STOCKPILE_CONFIG_ID); + total_items_all_piles+= item_count_piles[id]; + marked_total_count_all_piles += premarked_item_count_piles[id]; + } + + marked_item_count_total = marked_item_count_global + marked_total_count_all_piles; + + + return newly_marked_items_piles; + +} static int32_t do_cycle(color_ostream &out) { DEBUG(cycle,out).print("running %s cycle\n", plugin_name); @@ -494,12 +543,18 @@ static int32_t do_cycle(color_ostream &out) { validate_stockpile_configs(out); - int32_t marked_items_total; - map item_counts, marked_items_counts, premarked_item_counts; + int32_t marked_item_count_total = 0; + int32_t marked_item_count_global = 0; + int32_t newly_marked_items_piles = 0; + int32_t total_items_all_piles = 0; + int32_t marked_total_count_all_piles = 0; + map item_count_piles, premarked_item_count_piles, marked_item_count_piles; + + newly_marked_items_piles = scan_count_all(out, true, marked_item_count_total, marked_total_count_all_piles, marked_item_count_global, + total_items_all_piles, item_count_piles, premarked_item_count_piles, marked_item_count_piles); - marked_items_total = scan_stockpiles(out, true, item_counts, marked_items_counts, premarked_item_counts); DEBUG(perf,out).print("exit %s do_cycle\n", plugin_name); - return marked_items_total; + return newly_marked_items_piles; } static int getSelectedStockpile(color_ostream &out) { @@ -533,8 +588,6 @@ static PersistentDataItem *getSelectedStockpileConfig(color_ostream &out) { } - - static void push_stockpile_config(lua_State *L, int id, bool monitored) { map stockpile_config; stockpile_config.emplace("id", id); @@ -558,22 +611,20 @@ static void automelt_printStatus(color_ostream &out) { validate_stockpile_configs(out); out.print("automelt is %s\n\n", is_enabled ? "enabled" : "disabled"); - map item_counts, marked_item_counts, premarked_item_counts; - scan_stockpiles(out, false, item_counts, marked_item_counts, premarked_item_counts); - + int32_t marked_item_count_total = 0; + int32_t marked_item_count_global = 0; int32_t total_items_all_piles = 0; - int32_t premarked_items_all_piles = 0; + int32_t marked_total_count_all_piles = 0; + map item_count_piles, premarked_item_count_piles, marked_item_count_piles; - - for (auto &i : watched_stockpiles) { - int id = get_config_val(i, STOCKPILE_CONFIG_ID); - total_items_all_piles+= item_counts[id]; - premarked_items_all_piles += premarked_item_counts[id]; - } + scan_count_all(out, false, marked_item_count_total, marked_total_count_all_piles, marked_item_count_global, + total_items_all_piles, item_count_piles, premarked_item_count_piles, marked_item_count_piles); out.print("summary:\n"); - out.print(" total items in monitored piles: %d\n", total_items_all_piles); - out.print(" marked items in monitored piles: %d\n", premarked_items_all_piles); + out.print(" total items in monitored piles: %d\n", total_items_all_piles); + out.print(" marked items in monitored piles: %d\n", marked_total_count_all_piles); + out.print("marked items global (excludes those in monitored piles): %d\n", marked_item_count_global); + out.print(" marked items global (monitored piles + others): %d\n", marked_item_count_total); int name_width = 11; for (auto &stockpile : world->buildings.other.STOCKPILE) { @@ -582,19 +633,26 @@ static void automelt_printStatus(color_ostream &out) { } name_width = -name_width; - const char *fmt = "%*s %4s %4s\n"; - out.print(fmt, name_width, "name", " id ", "monitored"); - out.print(fmt, name_width, "----", "----", "---------"); + const char *fmt = "%*s %4s %4s %5s %5s\n"; + out.print(fmt, name_width, "name", " id ", "monitored", "items", "marked"); + out.print(fmt, name_width, "----", "----", "---------", "-----", "------"); for (auto &stockpile : world->buildings.other.STOCKPILE) { if (!isStockpile(stockpile)) continue; bool monitored = false; + int32_t item_count = 0; + int32_t marked_item_count = 0; if (watched_stockpiles_indices.count(stockpile->id)) { auto &c = watched_stockpiles[watched_stockpiles_indices[stockpile->id]]; monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); + int id = get_config_val(c, STOCKPILE_CONFIG_ID); + item_count = item_count_piles[id]; + marked_item_count = premarked_item_count_piles[id]; + } - out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]": "[ ]"); + out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]": "[ ]", + int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); } DEBUG(status,out).print("exiting automelt_printStatus\n"); @@ -681,28 +739,25 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { DEBUG(status,*out).print("entering automelt_getItemCountsAndStockpileConfigs\n"); validate_stockpile_configs(*out); - map item_counts, marked_item_counts, premarked_item_counts; - int32_t marked_items = scan_stockpiles(*out, false, item_counts, marked_item_counts, premarked_item_counts); - + int32_t marked_item_count_total = 0; + int32_t marked_item_count_global = 0; int32_t total_items_all_piles = 0; - int32_t premarked_items_all_piles = 0; + int32_t marked_total_count_all_piles = 0; + map item_count_piles, premarked_item_count_piles, marked_item_count_piles; - - for (auto &i : watched_stockpiles) { - int id = get_config_val(i, STOCKPILE_CONFIG_ID); - total_items_all_piles+= item_counts[id]; - premarked_items_all_piles += premarked_item_counts[id]; - } + scan_count_all(*out, false, marked_item_count_total, marked_total_count_all_piles, marked_item_count_global, + total_items_all_piles, item_count_piles, premarked_item_count_piles, marked_item_count_piles); map summary; summary.emplace("total_items", total_items_all_piles); - summary.emplace("marked_items", marked_items); - summary.emplace("premarked_items", premarked_items_all_piles); + summary.emplace("premarked_items", marked_total_count_all_piles); + summary.emplace("marked_item_count_global", marked_item_count_global); + summary.emplace("marked_item_count_total", marked_item_count_total); Lua::Push(L, summary); - Lua::Push(L, item_counts); - Lua::Push(L, marked_item_counts); - Lua::Push(L, premarked_item_counts); + Lua::Push(L, item_count_piles); + Lua::Push(L, marked_item_count_piles); + Lua::Push(L, premarked_item_count_piles); int32_t bldg_count = 0; for (auto pile : world->buildings.other.STOCKPILE) { From 969f8162a83e1fb598ff04a8fd50feff2b359588 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 04:39:41 -0500 Subject: [PATCH 0233/2222] Further updates from comments/review. --- plugins/automelt.cpp | 93 +++++++++++++++++++--------------------- plugins/lua/automelt.lua | 2 +- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 031c574f4..60b76730a 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -268,27 +268,40 @@ static bool isStockpile(df::building * building) { return building->getType() == df::building_type::Stockpile; } +struct BadFlagsCanMelt { + uint32_t whole; + + BadFlagsCanMelt() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(in_job); + #undef F + whole = flags.whole; + } +}; + +struct BadFlagsMarkItem { + uint32_t whole; + + BadFlagsMarkItem() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(artifact); + F(spider_web); F(owned); + #undef F + whole = flags.whole; + } +}; + // Copied from Kelly Martin's code static inline bool can_melt(df::item *item) { - df::item_flags bad_flags; - bad_flags.whole = 0; - -#define F(x) bad_flags.bits.x = true; - F(dump); - F(forbid); - F(garbage_collect); - F(in_job); - F(hostile); - F(on_fire); - F(rotten); - F(trader); - F(in_building); - F(construction); - F(artifact); - F(melt); -#undef F + static const BadFlagsCanMelt bad_flags; if (item->flags.whole & bad_flags.whole) return false; @@ -301,9 +314,9 @@ static inline bool can_melt(df::item *item) if (!is_metal_item(item)) return false; - for (auto g = item->general_refs.begin(); g != item->general_refs.end(); g++) + for (auto &g : item->general_refs) { - switch ((*g)->getType()) + switch (g->getType()) { case general_ref_type::CONTAINS_ITEM: case general_ref_type::UNIT_HOLDER: @@ -311,10 +324,10 @@ static inline bool can_melt(df::item *item) return false; case general_ref_type::CONTAINED_IN_ITEM: { - df::item *c = (*g)->getItem(); - for (auto gg = c->general_refs.begin(); gg != c->general_refs.end(); gg++) + df::item *c = g->getItem(); + for (auto &gg : c->general_refs) { - if ((*gg)->getType() == general_ref_type::UNIT_HOLDER) + if (gg->getType() == general_ref_type::UNIT_HOLDER) return false; } } @@ -335,17 +348,17 @@ static inline bool is_set_to_melt(df::item *item) return item->flags.bits.melt; } -static inline bool is_in_job(df::item *item) { - return item->flags.bits.in_job; -} - -static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flags, int32_t stockpile_id, +static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_flags, int32_t stockpile_id, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); - string name = ""; - item->getItemDescription(&name, 0); - DEBUG(perf,out).print("item %s %d\n", name.c_str(), item->id); + + if (DBG_NAME(perf).isEnabled(DebugCategory::LDEBUG)) { + string name = ""; + item->getItemDescription(&name, 0); + DEBUG(perf,out).print("item %s %d\n", name.c_str(), item->id); + } + if (item->flags.whole & bad_flags.whole){ DEBUG(perf,out).print("rejected flag check\n"); item_count++; @@ -403,24 +416,8 @@ static int mark_item(color_ostream &out, df::item *item, df::item_flags bad_flag static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d\n", plugin_name, should_melt); - // Precompute a bitmask with the bad flags - df::item_flags bad_flags; - bad_flags.whole = 0; - -#define F(x) bad_flags.bits.x = true; - F(dump); - F(forbid); - F(garbage_collect); - F(hostile); - F(on_fire); - F(rotten); - F(trader); - F(in_building); - F(construction); - F(artifact); - F(spider_web); - F(owned); -#undef F + + static const BadFlagsMarkItem bad_flags; int32_t marked_count = 0; diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 2cccc7342..9be662ee9 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -47,7 +47,7 @@ function parse_commandline(...) return true end --- used by gui/autochop +-- used by gui/auomelt function setStockpileConfig(config) automelt_setStockpileConfig(config.id, config.monitored) end From 84f7fc85da0a03299794f56a7d7c33c4ceac7413 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 04:44:33 -0500 Subject: [PATCH 0234/2222] trim trailing whitespace --- plugins/automelt.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 60b76730a..e112491a7 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -348,11 +348,11 @@ static inline bool is_set_to_melt(df::item *item) return item->flags.bits.melt; } -static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_flags, int32_t stockpile_id, +static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_flags, int32_t stockpile_id, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); - + if (DBG_NAME(perf).isEnabled(DebugCategory::LDEBUG)) { string name = ""; item->getItemDescription(&name, 0); @@ -416,7 +416,7 @@ static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_fl static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d\n", plugin_name, should_melt); - + static const BadFlagsMarkItem bad_flags; int32_t marked_count = 0; @@ -450,7 +450,7 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st } -static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_count_piles, map &premarked_item_count_piles, +static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_count_piles, map &premarked_item_count_piles, map &marked_item_count_piles, map &tracked_item_map) { DEBUG(perf,out).print("running scan_stockpiles\n"); int32_t newly_marked = 0; @@ -508,7 +508,7 @@ static int32_t scan_all_melt_designated(color_ostream &out, map & static int32_t scan_count_all(color_ostream &out, bool should_melt, int32_t &marked_item_count_total, int32_t &marked_total_count_all_piles, int32_t &marked_item_count_global, int32_t &total_items_all_piles, map &item_count_piles, map &premarked_item_count_piles, map &marked_item_count_piles) { - + //Wraps both scan_stockpiles and scan_all_melt_designated //Returns count of items in piles freshly marked @@ -528,7 +528,7 @@ static int32_t scan_count_all(color_ostream &out, bool should_melt, int32_t &mar } marked_item_count_total = marked_item_count_global + marked_total_count_all_piles; - + return newly_marked_items_piles; @@ -648,7 +648,7 @@ static void automelt_printStatus(color_ostream &out) { } - out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]": "[ ]", + out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]": "[ ]", int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); } DEBUG(status,out).print("exiting automelt_printStatus\n"); From 2e6ba64f5648aa5aceb4e752a22528480c7bff4c Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 04:46:35 -0500 Subject: [PATCH 0235/2222] Hopefully fix end-of-file-fixer complaint --- plugins/automelt.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index e112491a7..0b3c1bdf7 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -784,4 +784,3 @@ DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(automelt_getStockpileConfig), DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), DFHACK_LUA_END}; - From 9bcd9c27bf5e465a1e7042ed0970683cae95bdd8 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 04:55:14 -0500 Subject: [PATCH 0236/2222] Fix build failure --- plugins/automelt.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 0b3c1bdf7..b12bc3b7c 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -555,7 +555,6 @@ static int32_t do_cycle(color_ostream &out) { } static int getSelectedStockpile(color_ostream &out) { - int32_t stock_id = 0; df::building *selected_bldg = NULL; selected_bldg = Gui::getSelectedBuilding(out, true); if (selected_bldg->getType() != df::building_type::Stockpile) { @@ -726,6 +725,8 @@ static int automelt_getSelectedStockpileConfig(lua_State *L){ } else { push_stockpile_config(L, stock_id, false); } + + return 1; } //TODO @@ -783,4 +784,5 @@ DFHACK_PLUGIN_LUA_FUNCTIONS{ DFHACK_PLUGIN_LUA_COMMANDS{ DFHACK_LUA_COMMAND(automelt_getStockpileConfig), DFHACK_LUA_COMMAND(automelt_getItemCountsAndStockpileConfigs), + DFHACK_LUA_COMMAND(automelt_getSelectedStockpileConfig), DFHACK_LUA_END}; From 4d876f85b73b3f28bbc2f949f7d0f95e12302028 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 05:14:08 -0500 Subject: [PATCH 0237/2222] Prelim. doc updates. --- docs/plugins/automelt.rst | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 9abe2cc9c..4a3bc6e60 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -6,9 +6,9 @@ automelt :tags: untested fort productivity items stockpiles :no-command: -When `enabled `, this plugin adds an option to the :kbd:`q` menu for -stockpiles. When the ``automelt`` option is selected for the stockpile, any -items placed in the stockpile will automatically be designated to be melted. +Automelt checks monitor-enabled stockpiles once every in-game day, and will mark valid items for melting. + +Please see `gui/automelt` for the interactive configuration dialog. Usage ----- @@ -16,3 +16,31 @@ Usage :: enable automelt + automelt [status] + automelt (designate) + automelt (monitor|nomonitor) + +Examples +-------- + +Automatically designate all meltable items in the stockpile `melt` for melting. :: + + enable automelt + automelt monitor melt + +Enable monitoring for the stockpile `melt`, and mmediately designate all meltable items in monitored stockpiles for melting. :: + + automelt monitor melt + automelt designate + +Commands +-------- + +``status`` + Shows current configuration and relevant statistics + +``designate`` + Designates items in monitored stockpiles for melting right now. This works even if ``automelt`` is not currently enabled. + +``(no)monitor `` + Enable/disable monitoring of a given stockpile. Requires the stockpile to have a name set. \ No newline at end of file From 96fc53f29d3bf02d075fa5eac8b3411accf1c7e6 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Sun, 22 Jan 2023 05:22:43 -0500 Subject: [PATCH 0238/2222] Always with the trailing whitespace --- docs/plugins/automelt.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 4a3bc6e60..83f433599 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -23,13 +23,13 @@ Usage Examples -------- -Automatically designate all meltable items in the stockpile `melt` for melting. :: +Automatically designate all meltable items in the stockpile ("melt") for melting. :: enable automelt automelt monitor melt -Enable monitoring for the stockpile `melt`, and mmediately designate all meltable items in monitored stockpiles for melting. :: - +Enable monitoring for the stockpile ("melt"), and mmediately designate all meltable items in monitored stockpiles for melting. :: + automelt monitor melt automelt designate @@ -43,4 +43,4 @@ Commands Designates items in monitored stockpiles for melting right now. This works even if ``automelt`` is not currently enabled. ``(no)monitor `` - Enable/disable monitoring of a given stockpile. Requires the stockpile to have a name set. \ No newline at end of file + Enable/disable monitoring of a given stockpile. Requires the stockpile to have a name set. From f0d44342d891bc42e9cd45d66d7732ea1a50d535 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 03:08:36 -0800 Subject: [PATCH 0239/2222] first attempt at a manager orders overlay --- plugins/CMakeLists.txt | 2 +- plugins/lua/orders.lua | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 plugins/lua/orders.lua diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3d36b95fc..a05ae6a83 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -134,7 +134,7 @@ dfhack_plugin(misery misery.cpp) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) -dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static) +dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static lua) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #dfhack_plugin(petcapRemover petcapRemover.cpp) diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua new file mode 100644 index 000000000..aecd940d6 --- /dev/null +++ b/plugins/lua/orders.lua @@ -0,0 +1,100 @@ +local _ENV = mkmodule('plugins.orders') + +local dialogs = require('gui.dialogs') +local gui = require('gui') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +-- +-- OrdersOverlay +-- + +local function is_orders_panel_visible() + local info = df.global.game.main_interface.info + return info.open and info.current_mode == df.info_interface_mode_type.WORK_ORDERS +end + +local function do_sort() + dfhack.run_command('orders', 'sort') +end + +local function do_clear() + dialogs.showYesNoPrompt('Clear manager orders?', + 'Are you sure you want to clear the manager orders?', nil, + function() dfhack.run_command('orders', 'clear') end) +end + +local function do_import() + local output = dfhack.run_command_silent('orders', 'list') + dialogs.ListBox{ + frame_title='Import Manager Orders', + with_filter=true, + choices=output:split('\n'), + on_select=function(idx, choice) + dfhack.run_command('orders', 'import', choice.text) + end, + }:show() +end + +local function do_export() + dialogs.InputBox{ + frame_title='Export Manager Orders', + on_input=function(text) + dfhack.run_command('orders', 'export', text) + end + }:show() +end + +OrdersOverlay = defclass(OrdersOverlay, overlay.OverlayWidget) +OrdersOverlay.ATTRS{ + default_pos={x=61,y=-6}, + viewscreens='dwarfmode', + frame={w=30, h=4}, + frame_style=gui.GREY_LINE_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function OrdersOverlay:init() + self:addviews{ + widgets.HotkeyLabel{ + frame={t=0, l=0}, + label='import', + key='CUSTOM_CTRL_I', + on_activate=do_import, + }, + widgets.HotkeyLabel{ + frame={t=1, l=0}, + label='export', + key='CUSTOM_CTRL_E', + on_activate=do_export, + }, + widgets.HotkeyLabel{ + frame={t=0, l=15}, + label='sort', + key='CUSTOM_CTRL_O', + on_activate=do_sort, + }, + widgets.HotkeyLabel{ + frame={t=1, l=15}, + label='clear', + key='CUSTOM_CTRL_C', + on_activate=do_clear, + }, + } +end + +function OrdersOverlay:render(dc) + if not is_orders_panel_visible() then return false end + OrdersOverlay.super.render(self, dc) +end + +function OrdersOverlay:onInput(keys) + if not is_orders_panel_visible() then return false end + OrdersOverlay.super.onInput(self, keys) +end + +OVERLAY_WIDGETS = { + overlay=OrdersOverlay, +} + +return _ENV From f862e4a6b713a78fb7fb03b77f6316ab702f4c5a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 03:09:07 -0800 Subject: [PATCH 0240/2222] comment out SEC_SELECT in dialogs --- library/lua/gui/dialogs.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index e7228db1e..0b923e7c0 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -218,9 +218,9 @@ end function ListBox:onRenderFrame(dc,rect) ListBox.super.onRenderFrame(self,dc,rect) - if self.select2_hint then - dc:seek(rect.x1+2,rect.y2):key('SEC_SELECT'):string(': '..self.select2_hint,COLOR_GREY) - end + --if self.select2_hint then + -- dc:seek(rect.x1+2,rect.y2):key('SEC_SELECT'):string(': '..self.select2_hint,COLOR_GREY) + --end end function ListBox:getWantedFrameSize() From c7ca9d0d7b7fd0c207467022cdaad106acf85ae7 Mon Sep 17 00:00:00 2001 From: 20k Date: Sun, 22 Jan 2023 13:56:33 +0000 Subject: [PATCH 0241/2222] reverse squad iteration delete --- library/modules/Buildings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 9d525d96b..a17c79de2 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1344,14 +1344,13 @@ static void delete_civzone_squad_links(df::building* bld) //if this is null, something has gone just *terribly* wrong if (squad) { - for (int i=0; i < (int)squad->rooms.size(); i++) + for (int i=(int)squad->rooms.size() - 1; i >= 0; i--) { if (squad->rooms[i]->building_id == bld->id) { auto room = squad->rooms[i]; squad->rooms.erase(squad->rooms.begin() + i); delete room; - i--; } } } From b15fcc93d2247e144de6c51979d7bb9a92d7c2b3 Mon Sep 17 00:00:00 2001 From: 20k Date: Sun, 22 Jan 2023 14:53:56 +0000 Subject: [PATCH 0242/2222] remove assignments on zone destroy --- library/modules/Buildings.cpp | 46 ++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index a17c79de2..c4615c61a 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1325,16 +1325,8 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorgetType() != building_type::Civzone) - return; - - auto zone = strict_virtual_cast(bld); - - if (zone == nullptr) - return; - for (df::building_civzonest::T_squad_room_info* room_info : zone->squad_room_info) { int32_t squad_id = room_info->squad_id; @@ -1346,7 +1338,7 @@ static void delete_civzone_squad_links(df::building* bld) { for (int i=(int)squad->rooms.size() - 1; i >= 0; i--) { - if (squad->rooms[i]->building_id == bld->id) + if (squad->rooms[i]->building_id == zone->id) { auto room = squad->rooms[i]; squad->rooms.erase(squad->rooms.begin() + i); @@ -1361,6 +1353,31 @@ static void delete_civzone_squad_links(df::building* bld) zone->squad_room_info.clear(); } +//unit owned_building pointers are known-bad as of 50.05 and dangle on zone delete +//do not use anything that touches anything other than the pointer value +//this means also that if dwarf fortress reuses a memory allocation, we will end up with duplicates +//this vector is also not sorted by id +static void delete_assigned_unit_links(df::building_civzonest* zone) +{ + if (zone->assigned_unit_id == -1) + return; + + df::unit* unit = zone->assigned_unit; + + for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + { + if (unit->owned_buildings[i] == zone) + unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + } +} + +static void on_civzone_delete(df::building_civzonest* civzone) +{ + remove_zone_from_all_buildings(civzone); + delete_civzone_squad_links(civzone); + delete_assigned_unit_links(civzone); +} + bool Buildings::deconstruct(df::building *bld) { using df::global::plotinfo; @@ -1399,9 +1416,14 @@ bool Buildings::deconstruct(df::building *bld) bld->uncategorize(); remove_building_from_all_zones(bld); - remove_zone_from_all_buildings(bld); - delete_civzone_squad_links(bld); + if (bld->getType() == df::building_type::Civzone) + { + auto zone = strict_virtual_cast(bld); + + if (zone) + on_civzone_delete(zone); + } delete bld; From 740abba621f26f097b345ed7693f1fc4ccc4a524 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 08:40:16 -0800 Subject: [PATCH 0243/2222] update comments in quickfort config file --- data/dfhack-config/quickfort/quickfort.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/dfhack-config/quickfort/quickfort.txt b/data/dfhack-config/quickfort/quickfort.txt index e3f5aeaec..92af2909e 100644 --- a/data/dfhack-config/quickfort/quickfort.txt +++ b/data/dfhack-config/quickfort/quickfort.txt @@ -4,8 +4,8 @@ # temporarily overridden in the active session with the `quickfort set` command. # # If you have edited this file but want to revert to "factory defaults", delete -# this file and a fresh one will be copied from -# dfhack-config/default/quickfort/quickfort.txt the next time you start DFHack. +# this file and a default one will be regenerated for you the next time you +# start DFHack. # Directory tree to search for blueprints. Can be set to an absolute or relative # path. If set to a relative path, resolves to a directory under the DF folder. @@ -30,8 +30,8 @@ query_unsafe=false # Set to the maximum number of resources you want assigned to stockpiles of the # relevant types. Set to -1 for DF defaults (number of stockpile tiles for # stockpiles that take barrels and bins, 1 wheelbarrow for stone stockpiles). -# The default here for wheelbarrows is 0 since using wheelbarrows normally -# *decreases* the efficiency of your fort. +# The default here for wheelbarrows is 0 since restricting stockpiles to a +# single wheelbarrow can drastically *decrease* the efficiency of your fort. stockpiles_max_barrels=-1 stockpiles_max_bins=-1 stockpiles_max_wheelbarrows=0 From aaaf2d9f22d577510f145508886a50ac2992a336 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sat, 14 Jan 2023 22:14:06 -0800 Subject: [PATCH 0244/2222] Add cross-compile script That will simply generate the win64 artifacts for those of us that run DF under Proton on Steam for Linux. --- build/.gitignore | 1 + build/build-win64-from-linux.sh | 39 +++++++++++++++++++++ docs/dev/compile/Compile.rst | 60 +++++++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100755 build/build-win64-from-linux.sh diff --git a/build/.gitignore b/build/.gitignore index 3c85ab4df..f25d10ca7 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -6,3 +6,4 @@ DF_PATH.txt _CPack_Packages *.tar.* .cmake +win64-cross diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh new file mode 100755 index 000000000..74c307c89 --- /dev/null +++ b/build/build-win64-from-linux.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env sh + +# Number of jobs == core count +jobs=$(grep -c ^processor /proc/cpuinfo) + +# Calculate absolute paths for docker to do mounts +srcdir=$(realpath "$(dirname "$(readlink -f "$0")")"/..) + +cd "$srcdir"/build + +mkdir -p win64-cross +mkdir -p win64-cross/output + +# Assumes you built a container image called dfhack-build-msvc from +# https://github.com/BenLubar/build-env/tree/master/msvc, see +# docs/dev/compile/Compile.rst. +# +# NOTE: win64-cross is mounted in /src/build due to the hardcoded `cmake ..` in +# the Dockerfile +# +# TODO: make this work for rootless docker, i.e. remove the sudo for those that +# don't normally need to use sudo to run docker. +if ! sudo docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ + --user buildmaster \ + --name dfhack-win \ + dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output && dfhack-make -j$jobs install" \ + ; then + echo + echo "Build failed" + exit 1 +else + echo + echo "Windows artifacts are at win64-cross/output. Copy or symlink them to" + echo "your steam DF directory to install dfhack (and optionally delete the" + echo "hack/ directory already present)" + echo + echo "Typically this can be done like this:" + echo " cp -r win64-cross/output/* \"$HOME/.local/share/Steam/steamapps/common/Dwarf Fortress\"" +fi diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index 032eb7524..8e38139fa 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -284,8 +284,8 @@ addition to the normal ``CC`` and ``CXX`` flags above:: export PATH=/usr/local/bin:$PATH -Windows cross compiling from Linux -================================== +Windows cross compiling from Linux (running DF inside docker) +============================================================= .. highlight:: bash @@ -368,6 +368,62 @@ host when you want to reattach:: If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. That will handle all the wineserver management for you. +Cross-compiling windows files for running DF in Steam for Linux +=============================================================== + +.. highlight:: bash + +If you wish, you can use Docker to build just the Windows files to copy to your +existing Steam installation on Linux. + +.. contents:: + :local: + :depth: 1 + +Step 1: Build the MSVC builder image +------------------------------------ + +It'll be called ``dfhack-build-msvc:latest`` after it's done building:: + + git clone https://github.com/BenLubar/build-env.git + cd build-env/msvc + docker build --build-arg BUILDER_UID=$(id -u) -t dfhack-build-msvc . + +The ``BUILDER_UID`` argument is used to make sure the build user can access your +source code directory inside the container. + +The docker build takes a while, but only needs to be done once, unless the build +environment changes. + +Step 2: Get dfhack, and run the build script +-------------------------------------------- + +Check out ``dfhack`` into another directory, and run the build script:: + + git clone https://github.com/DFHack/dfhack.git + cd dfhack + git submodule update --init --recursive + cd build + ./build-win64-from-linux.sh + +The last step may ask you for your ``sudo`` password -- if you are able to run +``docker`` without ``sudo`` normally, you can remove the ``sudo`` from the build +script. + +The script will mount your host's ``dfhack`` directory to docker, use it to +build the artifacts in ``build/win64-cross``, and put all the files needed to +install in ``build/win64-cross/output``. + +Step 3: install dfhack to your Steam DF install +----------------------------------------------- +As the script will tell you, you can then copy the files into your DF folder:: + + # Optional -- remove the old hack directory in case we leave files behind + rm ~/.local/share/Steam/steamapps/common/"Dwarf Fortress"/hack + cp -r win64-cross/output/* ~/.local/share/Steam/steamapps/common/"Dwarf Fortress"/ + +Afterward, just run DF as normal. + .. _note-offline-builds: Building DFHack Offline From 5a20f775bf13baac435a739defd99d4601638d01 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sun, 15 Jan 2023 20:34:52 -0800 Subject: [PATCH 0245/2222] Set UIDs properly on run; fix sudo use This depends on a new entrypoint put inside the docker image, that will read BUILDER_UID and fix up the users and files. This also removes "sudo" and allows the entire script to be run with sudo without messing with the build file permissions. --- build/build-win64-from-linux.sh | 29 +++++++++++++++++++++++------ docs/dev/compile/Compile.rst | 14 ++++++-------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 74c307c89..7f23d78c3 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -1,5 +1,6 @@ -#!/usr/bin/env sh +#!/bin/bash +set -e # Number of jobs == core count jobs=$(grep -c ^processor /proc/cpuinfo) @@ -8,20 +9,36 @@ srcdir=$(realpath "$(dirname "$(readlink -f "$0")")"/..) cd "$srcdir"/build +builder_uid=$(id -u) + mkdir -p win64-cross mkdir -p win64-cross/output +# Check for sudo; we want to use the real user +if [[ $(id -u) -eq 0 ]]; then + if [[ -z "$SUDO_UID" || "$SUDO_UID" -eq 0 ]]; then + echo "Please don't run this script directly as root, use sudo instead:" + echo + echo " sudo $0" + # This is because we can't change the buildmaster UID in the container to 0 -- + # that's already taken by root. + exit 1 + fi + + # If this was run using sudo, let's make sure the directories are owned by the + # real user (and set the BUILDER_UID to it) + builder_uid=$SUDO_UID + chown $builder_uid win64-cross win64-cross/output +fi + # Assumes you built a container image called dfhack-build-msvc from # https://github.com/BenLubar/build-env/tree/master/msvc, see # docs/dev/compile/Compile.rst. # # NOTE: win64-cross is mounted in /src/build due to the hardcoded `cmake ..` in # the Dockerfile -# -# TODO: make this work for rootless docker, i.e. remove the sudo for those that -# don't normally need to use sudo to run docker. -if ! sudo docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ - --user buildmaster \ +if ! docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ + -e BUILDER_UID=$builder_uid \ --name dfhack-win \ dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output && dfhack-make -j$jobs install" \ ; then diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index 8e38139fa..f91c43c07 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -387,10 +387,7 @@ It'll be called ``dfhack-build-msvc:latest`` after it's done building:: git clone https://github.com/BenLubar/build-env.git cd build-env/msvc - docker build --build-arg BUILDER_UID=$(id -u) -t dfhack-build-msvc . - -The ``BUILDER_UID`` argument is used to make sure the build user can access your -source code directory inside the container. + docker build -t dfhack-build-msvc . The docker build takes a while, but only needs to be done once, unless the build environment changes. @@ -406,14 +403,15 @@ Check out ``dfhack`` into another directory, and run the build script:: cd build ./build-win64-from-linux.sh -The last step may ask you for your ``sudo`` password -- if you are able to run -``docker`` without ``sudo`` normally, you can remove the ``sudo`` from the build -script. - The script will mount your host's ``dfhack`` directory to docker, use it to build the artifacts in ``build/win64-cross``, and put all the files needed to install in ``build/win64-cross/output``. +If you need to run ``docker`` using ``sudo``, run the script using ``sudo`` +rather than directly:: + + sudo ./build-win64-from-linux.sh + Step 3: install dfhack to your Steam DF install ----------------------------------------------- As the script will tell you, you can then copy the files into your DF folder:: From 839636a56339d449d6a8ed0a80ccafbef1207397 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Fri, 20 Jan 2023 17:21:33 -0800 Subject: [PATCH 0246/2222] Change $HOME to ~ As $HOME shows /root under sudo --- build/build-win64-from-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 7f23d78c3..713ae90e4 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -52,5 +52,5 @@ else echo "hack/ directory already present)" echo echo "Typically this can be done like this:" - echo " cp -r win64-cross/output/* \"$HOME/.local/share/Steam/steamapps/common/Dwarf Fortress\"" + echo " cp -r win64-cross/output/* ~/.local/share/Steam/steamapps/common/\"Dwarf Fortress\"" fi From ce12e2d90633af18166a31543447884df9ebfd36 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sat, 21 Jan 2023 09:38:46 -0800 Subject: [PATCH 0247/2222] Also build docs. The in-game docs aren't built otherwise, so you won't get help for the various commands if this isn't set. --- build/build-win64-from-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 713ae90e4..08ea88793 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -40,7 +40,7 @@ fi if ! docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ -e BUILDER_UID=$builder_uid \ --name dfhack-win \ - dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output && dfhack-make -j$jobs install" \ + dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ ; then echo echo "Build failed" From fb1ce3fed7062ce6d9d0637f5dc2d461e6b72345 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sun, 22 Jan 2023 11:44:35 -0800 Subject: [PATCH 0248/2222] Add changelog. --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 94b7d61c9..8d5665c85 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) ## Documentation From 4559168005ccf7c496dbf8a5e982b802be551c64 Mon Sep 17 00:00:00 2001 From: 20k Date: Sun, 22 Jan 2023 22:56:51 +0000 Subject: [PATCH 0249/2222] update for multiple ownership --- library/modules/Buildings.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index c4615c61a..5077b5b52 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1357,17 +1357,20 @@ static void delete_civzone_squad_links(df::building_civzonest* zone) //do not use anything that touches anything other than the pointer value //this means also that if dwarf fortress reuses a memory allocation, we will end up with duplicates //this vector is also not sorted by id +//it also turns out that multiple units eg (solely?) spouses can point to one room static void delete_assigned_unit_links(df::building_civzonest* zone) { - if (zone->assigned_unit_id == -1) - return; - - df::unit* unit = zone->assigned_unit; + //not clear if this is always true + /*if (zone->assigned_unit_id == -1) + return;*/ - for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + for (df::unit* unit : world->units.active) { - if (unit->owned_buildings[i] == zone) - unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + { + if (unit->owned_buildings[i] == zone) + unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + } } } From b4c49f36e11fa98f75ef6cf3afc55bf0d6975eef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 15:39:05 -0800 Subject: [PATCH 0250/2222] document orders overlay --- docs/plugins/orders.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 8beb246e4..5dfe23758 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -40,6 +40,17 @@ Examples Import manager orders from the library that keep your fort stocked with basic essentials. +Overlay +------- + +Orders plugin functionality is directly available when the manager orders screen +is open via an `overlay` widget. There are hotkeys assigned to export, import, +sort, and clear. You can also click on the hotkey hints as if they were buttons. +Clearing will ask for confirmation before acting. + +If you want to change where the hotkey hints appear, you can move them via +`gui/overlay`. + The orders library ------------------ From 0c5514ff2d0aa2079468965b19a2b6e968eaffbd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 15:39:12 -0800 Subject: [PATCH 0251/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 94b7d61c9..4a484f692 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open ## Documentation From 78c6b3683e5bd515994ef36fc6d8e3d0176b8066 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Mon, 23 Jan 2023 01:56:33 -0500 Subject: [PATCH 0252/2222] Changes as per GH suggestions. Doc updates. --- docs/plugins/automelt.rst | 23 ++++++++++----- plugins/automelt.cpp | 59 +++++++++++++++++++++++++++++++-------- plugins/lua/automelt.lua | 9 ++++-- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 83f433599..45f51561a 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -17,21 +17,28 @@ Usage enable automelt automelt [status] - automelt (designate) - automelt (monitor|nomonitor) + automelt designate + automelt (monitor|nomonitor) [,...] Examples -------- -Automatically designate all meltable items in the stockpile ("melt") for melting. :: +Automatically monitor stockpile ("melt"), marking new valid items for melting. This also immediately marks all present items for melting:: enable automelt automelt monitor melt -Enable monitoring for the stockpile ("melt"), and mmediately designate all meltable items in monitored stockpiles for melting. :: +Enable monitoring for ("Stockpile #52"), which has not been given a custom name:: - automelt monitor melt - automelt designate + automelt monitor "Stockpile #52" + +Enable monitoring for ("Stockpile #52"), which has not been given a custom name:: + + automelt monitor 52 + +Enable monitoring for multiple stockpiles ("Stockpile #52", "Stockpile #35", and "melt"):: + + automelt monitor 52,"Stockpile #35",melt Commands -------- @@ -43,4 +50,6 @@ Commands Designates items in monitored stockpiles for melting right now. This works even if ``automelt`` is not currently enabled. ``(no)monitor `` - Enable/disable monitoring of a given stockpile. Requires the stockpile to have a name set. + Enable/disable monitoring of a given stockpile. Works with either the stockpile's name (if set) or ID. + If the stockpile has no custom name set, you may designate it by either the full name as reported by + the status command, or by just the number. \ No newline at end of file diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index b12bc3b7c..c0d80532a 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -276,7 +276,8 @@ struct BadFlagsCanMelt { #define F(x) flags.bits.x = true; F(dump); F(forbid); F(garbage_collect); F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(in_job); + F(in_building); F(construction); F(artifact); + F(spider_web); F(owned); F(in_job); #undef F whole = flags.whole; } @@ -361,7 +362,6 @@ static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_fl if (item->flags.whole & bad_flags.whole){ DEBUG(perf,out).print("rejected flag check\n"); - item_count++; return 0; } @@ -392,7 +392,6 @@ static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_fl if (!can_melt(item)) { DEBUG(perf,out).print("cannot melt\n"); - item_count++; return 0; } @@ -588,7 +587,6 @@ static void push_stockpile_config(lua_State *L, int id, bool monitored) { map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); - Lua::Push(L, stockpile_config); } @@ -625,13 +623,18 @@ static void automelt_printStatus(color_ostream &out) { int name_width = 11; for (auto &stockpile : world->buildings.other.STOCKPILE) { if (!isStockpile(stockpile)) continue; - name_width = std::max(name_width, (int)stockpile->name.size()); + if (stockpile->name.empty()) { + string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); + name_width = std::max(name_width, (int)(stock_name.size())); + } else { + name_width = std::max(name_width, (int)stockpile->name.size()); + } } name_width = -name_width; - const char *fmt = "%*s %4s %4s %5s %5s\n"; - out.print(fmt, name_width, "name", " id ", "monitored", "items", "marked"); - out.print(fmt, name_width, "----", "----", "---------", "-----", "------"); + const char *fmt = "%*s %9s %5s %6s\n"; + out.print(fmt, name_width, "name", "monitored", "items", "marked"); + out.print(fmt, name_width, "----", "---------", "-----", "------"); for (auto &stockpile : world->buildings.other.STOCKPILE) { if (!isStockpile(stockpile)) continue; @@ -644,11 +647,19 @@ static void automelt_printStatus(color_ostream &out) { int id = get_config_val(c, STOCKPILE_CONFIG_ID); item_count = item_count_piles[id]; marked_item_count = premarked_item_count_piles[id]; + } + int32_t stockpile_number = stockpile->stockpile_number; + if (stockpile->name.empty()) { + string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); + out.print(fmt, name_width, stock_name, monitored ? "[x]": "[ ]", + int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); + } else { + out.print(fmt, name_width, stockpile->name.c_str(), monitored ? "[x]": "[ ]", + int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); } - out.print(fmt, name_width, stockpile->name.c_str(), int_to_string(stockpile->id).c_str(), monitored ? "[x]": "[ ]", - int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); + } DEBUG(status,out).print("exiting automelt_printStatus\n"); @@ -667,6 +678,11 @@ static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitor PersistentDataItem &c = ensure_stockpile_config(out, id); set_config_val(c, STOCKPILE_CONFIG_ID, id); set_config_bool(c, STOCKPILE_CONFIG_MONITORED, monitored); + + //If we're marking something as monitored, go ahead and designate contents. + if (monitored) { + automelt_designate(out); + } } static int automelt_getStockpileConfig(lua_State *L) { @@ -678,10 +694,23 @@ static int automelt_getStockpileConfig(lua_State *L) { int id; if (lua_isnumber(L, -1)) { + bool found = false; id = lua_tointeger(L, -1); - if (!df::building::find(id)) + + for (auto &stockpile : world->buildings.other.STOCKPILE) { + if (!isStockpile(stockpile)) continue; + if (id == stockpile->stockpile_number){ + id = stockpile->id; + found = true; + break; + + } + } + + if (!found) return 0; + } else { const char * name = lua_tostring(L, -1); if (!name) @@ -694,8 +723,16 @@ static int automelt_getStockpileConfig(lua_State *L) { id = stockpile->id; found = true; break; + } else { + string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); + if (stock_name == nameStr) { + id = stockpile->id; + found = true; + break; + } } + } if (!found) return 0; diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 9be662ee9..dc6c94f9b 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -38,7 +38,7 @@ function parse_commandline(...) automelt_designate() elseif command == 'monitor' then do_set_stockpile_config('monitor', true, args[2]) - elseif command == 'nomonitor'then + elseif command == 'nomonitor' or command == 'unmonitor' then do_set_stockpile_config('monitor', false, args[2]) else return false @@ -47,12 +47,13 @@ function parse_commandline(...) return true end --- used by gui/auomelt +-- used by gui/automelt function setStockpileConfig(config) automelt_setStockpileConfig(config.id, config.monitored) end function getItemCountsAndStockpileConfigs() + local fmt = 'Stockpile #%-5s' local data = {automelt_getItemCountsAndStockpileConfigs()} local ret = {} ret.summary = table.remove(data, 1) @@ -62,6 +63,10 @@ function getItemCountsAndStockpileConfigs() ret.stockpile_configs = data for _,c in ipairs(ret.stockpile_configs) do c.name = df.building.find(c.id).name + if not c.name or c.name == '' then + c.name = (fmt):format(tostring(df.building.find(c.id).stockpile_number)) + c.name = c.name.gsub(c.name, '^%s*(.-)%s*$', '%1') + end c.monitored = c.monitored ~= 0 end return ret From 44d8ce2bdcb4f06f2e520c57f9918837fcdfd4bd Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Mon, 23 Jan 2023 01:58:57 -0500 Subject: [PATCH 0253/2222] whitespace fix --- docs/plugins/automelt.rst | 2 +- plugins/automelt.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 45f51561a..6b14d9dc4 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -52,4 +52,4 @@ Commands ``(no)monitor `` Enable/disable monitoring of a given stockpile. Works with either the stockpile's name (if set) or ID. If the stockpile has no custom name set, you may designate it by either the full name as reported by - the status command, or by just the number. \ No newline at end of file + the status command, or by just the number. diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index c0d80532a..c78f36f51 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -696,7 +696,7 @@ static int automelt_getStockpileConfig(lua_State *L) { if (lua_isnumber(L, -1)) { bool found = false; id = lua_tointeger(L, -1); - + for (auto &stockpile : world->buildings.other.STOCKPILE) { if (!isStockpile(stockpile)) continue; if (id == stockpile->stockpile_number){ @@ -706,7 +706,7 @@ static int automelt_getStockpileConfig(lua_State *L) { } } - + if (!found) return 0; From 8fbaf169a43b4b61abac0bb3b55bacd8e63ff41d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 23 Jan 2023 07:14:54 +0000 Subject: [PATCH 0254/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 9666cb8eb..7f08c8c2b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9666cb8ebcc51f8e4bb2fb99116636793a7e4270 +Subproject commit 7f08c8c2bad5b98db4846a857ca7d41c86929660 From a6b3de1afbff256e01aa8efe4c6a40bd51122dfe Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 02:15:45 -0800 Subject: [PATCH 0255/2222] support scrolling to the beginning or end in Label --- docs/dev/Lua API.rst | 4 ++-- library/lua/gui/widgets.lua | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8b2d8b513..c696248ab 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4684,8 +4684,8 @@ The Label widget implements the following methods: This method takes the number of lines to scroll as positive or negative integers or one of the following keywords: ``+page``, ``-page``, - ``+halfpage``, or ``-halfpage``. It returns the number of lines that were - actually scrolled (negative for scrolling up). + ``+halfpage``, ``-halfpage``, ``home``, or ``end``. It returns the number of + lines that were actually scrolled (negative for scrolling up). WrappedLabel class ------------------ diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index f56eff096..f0129ffe3 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1315,6 +1315,7 @@ end function Label:scroll(nlines) if not nlines then return end + local text_height = math.max(1, self:getTextHeight()) if type(nlines) == 'string' then if nlines == '+page' then nlines = self.frame_body.height @@ -1324,12 +1325,15 @@ function Label:scroll(nlines) nlines = math.ceil(self.frame_body.height/2) elseif nlines == '-halfpage' then nlines = -math.ceil(self.frame_body.height/2) + elseif nlines == 'home' then + nlines = 1 - self.start_line_num + elseif nlines == 'end' then + nlines = text_height else error(('unhandled scroll keyword: "%s"'):format(nlines)) end end local n = self.start_line_num + nlines - local text_height = math.max(1, self:getTextHeight()) n = math.min(n, text_height - self.frame_body.height + 1) n = math.max(n, 1) nlines = n - self.start_line_num From f50cef93b1de6283e2c8e1cd16c3de51030514de Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 02:16:56 -0800 Subject: [PATCH 0256/2222] update changelog --- docs/changelog.txt | 1 + scripts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index f83892e69..5e114c5e2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom ## Removed diff --git a/scripts b/scripts index 7f08c8c2b..9666cb8eb 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7f08c8c2bad5b98db4846a857ca7d41c86929660 +Subproject commit 9666cb8ebcc51f8e4bb2fb99116636793a7e4270 From d34238918e1412d7e3b5dd5c2c9d65ba7c2b3039 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 22:10:17 -0800 Subject: [PATCH 0257/2222] move helpdb from autorefresh to explicit refresh this greatly speeds up the launch time of `gui/launcher` --- docs/changelog.txt | 3 +++ docs/dev/Lua API.rst | 15 +++++++++++---- library/Core.cpp | 1 + library/lua/helpdb.lua | 30 ++++++++++-------------------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5e114c5e2..073837eff 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu +- `gui/launcher`: sped up initialization time for faster load of the UI - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open ## Documentation @@ -47,6 +48,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play +- `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index c696248ab..cc590b179 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -3319,17 +3319,20 @@ function: argument specifies the indentation step size in spaces. For the other arguments see the original documentation link above. +.. _helpdb: + helpdb ====== Unified interface for DFHack tool help text. Help text is read from the rendered -text in ``hack/docs/docs/``. If no rendered text exists, help is read from the -script sources (for scripts) or the string passed to the ``PluginCommand`` +text in ``hack/docs/docs/tools``. If no rendered text exists, help is read from +the script sources (for scripts) or the string passed to the ``PluginCommand`` initializer (for plugins). See `documentation` for details on how DFHack's help system works. -The database is lazy-loaded when an API method is called. It rechecks its help -sources for updates if an API method has not been called in the last 60 seconds. +The database is loaded when DFHack initializes, but can be explicitly refreshed +with a call to ``helpdb.refresh()`` if docs are added/changed during a play +session. Each entry has several properties associated with it: @@ -3345,6 +3348,10 @@ Each entry has several properties associated with it: - Long help, the entire contents of the associated help file. - A list of tags that define the groups that the entry belongs to. +* ``helpdb.refresh()`` + + Scan for changes in available commands and their documentation. + * ``helpdb.is_entry(str)``, ``helpdb.is_entry(list)`` Returns whether the given string (or list of strings) is an entry (are all diff --git a/library/Core.cpp b/library/Core.cpp index e2c983116..bdd38799f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2114,6 +2114,7 @@ void Core::onStateChange(color_ostream &out, state_change_event event) { auto L = Lua::Core::State; Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(con, L, "helpdb", "refresh"); Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); } break; diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index 711555a78..df5482aec 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -1,23 +1,9 @@ -- The help text database and query interface. --- --- Help text is read from the rendered text in hack/docs/docs/. If no rendered --- text exists, it is read from the script sources (for scripts) or the string --- passed to the PluginCommand initializer (for plugins). --- --- There should be one help file for each plugin that contains a summary for the --- plugin itself and help for all the commands that plugin provides (if any). --- Each script should also have one documentation file. --- --- The database is lazy-loaded when an API method is called. It rechecks its --- help sources for updates if an API method has not been called in the last --- 60 seconds. local _ENV = mkmodule('helpdb') local argparse = require('argparse') -local MAX_STALE_MS = 60000 - -- paths local RENDERED_PATH = 'hack/docs/docs/tools/' local TAG_DEFINITIONS = 'hack/docs/docs/Tags.txt' @@ -415,13 +401,12 @@ local function index_tags() end end --- ensures the db is up to date by scanning all help sources. does not do --- anything if it has already been run within the last MAX_STALE_MS milliseconds -local last_refresh_ms = 0 +local needs_refresh = true + +-- ensures the db is loaded local function ensure_db() - local now_ms = dfhack.getTickCount() - if now_ms - last_refresh_ms <= MAX_STALE_MS then return end - last_refresh_ms = now_ms + if not needs_refresh then return end + needs_refresh = false local old_db = textdb textdb, entrydb, tag_index = {}, {}, {} @@ -433,6 +418,11 @@ local function ensure_db() index_tags() end +function refresh() + needs_refresh = true + ensure_db() +end + local function parse_blocks(text) local blocks = {} for line in text:gmatch('[^\n]*') do From 9aaa55cd7f22b0a0078dd7c2f283a9a84c373001 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 02:25:16 -0800 Subject: [PATCH 0258/2222] add textures and frames for various use cases --- data/art/border-medium.png | Bin 0 -> 641 bytes data/art/border-panel.png | Bin 0 -> 446 bytes data/art/border-thin.png | Bin 0 -> 471 bytes data/art/border-window.png | Bin 0 -> 563 bytes docs/dev/Lua API.rst | 15 ++++++----- library/LuaApi.cpp | 4 +++ library/include/modules/Textures.h | 8 ++++++ library/lua/gui.lua | 39 +++++++++++++++++++++-------- library/modules/Textures.cpp | 28 +++++++++++++++++++++ plugins/lua/orders.lua | 2 +- 10 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 data/art/border-medium.png create mode 100644 data/art/border-panel.png create mode 100644 data/art/border-thin.png create mode 100644 data/art/border-window.png diff --git a/data/art/border-medium.png b/data/art/border-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..529a60ee433fd2358d7252a8108ecef2cd4463d6 GIT binary patch literal 641 zcmV-{0)G98P)D}R6#sT*unh7LiB=un|9u))k zbP~IB^j1c{tVfSeK4D^lZ*2&i;t5LmGgnj!L$K^o&Kwa_w=kDfvnrrX_#;vSwO5|D z6s}Xj44)N`sB#f!anB(ars`ME>*aO`I|Hg}6%5L=U60Cr0{K8xuIh!JBgmAa;-q>r zSr#SfR|;r(#@{p0!j^#R$)15?W!SvuxN~rRKLu$7d7)aHN!6zkdsPT%nJnB7&|uc; z50XxtDKty&G^HZ_gf+w&p>FePp#O&JrFt2kOG{VQSbsC=+d9|lsuh|$@>zR|w0y;=*$cL;fQUN=@4Fbxi?zRx+)fy6=8ZN=s9au>TC)?Z@duziic2Ma6GnV{ zl#H4kYb_5~&q8Xukofi}sXcmJwLC;U3qAWiBcpf%h9Vutizx>H0R{j?e$peluCCPp o000hUSV?A0O#mtY000O800000007cclK=n!07*qoM6N<$f;ro-?N8mzF` z}nQwdY=6}2<~lv&oM5~dy;rB5$hOq^o{+Nm?O6)9pDh&Gy0 zkI|eiIto~E9Ti*AQXLfoTB@VM#YD$PaRqLUlC5Re72s`F7%0wW_g^KumRUC2945gJ z8A)NYo1+*Y7Wq`B&V=YIZbRjPdRttruS~^c@P!|`u>1I@rDe!!^Hx9FSTDW0sfc^0 zM!ozfv9pKz>ZzEGck!e2&YsfO(AP5g(QIcA<6iz?pHgvJ6iOotPrh3 zbjYMpNX7@oIFaQ5E(O7~4pB(y2uzo7LHy+!ONk4#nM6STVR9T2eS4hYaW7a%fL~ zB3AL0PAdrJNJy(-2!tpx>3iTqbjYMpJmoiXIZg8r?I9vX;wrz9%W0a2C}y<`v76MM z57it>>vjUw97<|?6jH$sfJ5=OM=@*mf-Nf`;*P=lE(Y^r?e8PE6POr=2pDzffM+IL z{c)WAbmD4uf;B!lnF(vvOniG3mrDF5P!#pRLYs^cD|(lsKqo>vu8^W0tsEtzX2)8~ z!_~8p+AhTEO04B5sXcmJwLC;U3wh~mZQsV@kKfU=drfB41B2g@ftRku6EGC%FkVbK z00=Mu`kfg#VkT>U0000EWmrjOO-%qQ00008000000002eQ Date: Mon, 23 Jan 2023 04:03:31 -0800 Subject: [PATCH 0259/2222] add compatibility frames --- library/lua/gui.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index e415000ae..4b6bf6517 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -833,11 +833,15 @@ local function make_frame(name, double_line) end WINDOW_FRAME = make_frame('Window', true) -GREY_LINE_FRAME = WINDOW_FRAME -- for compatibility with pre-steam code PANEL_FRAME = make_frame('Panel', false) MEDIUM_FRAME = make_frame('Medium', false) THIN_FRAME = make_frame('Thin', false) +-- for compatibility with pre-steam code +GREY_LINE_FRAME = WINDOW_FRAME +BOUNDARY_FRAME = PANEL_FRAME +GREY_FRAME = MEDIUM_FRAME + function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 From d7e0dcfcf860b476e77ffb4c82dfe685bd1ba03f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 04:03:41 -0800 Subject: [PATCH 0260/2222] use new frames for hotkey hotspot --- plugins/lua/hotkeys.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index a6eaf7d81..99ba08a05 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -170,7 +170,7 @@ function Menu:init() widgets.Panel{ view_id='list_panel', frame=list_frame, - frame_style=gui.GREY_LINE_FRAME, + frame_style=gui.PANEL_FRAME, frame_background=gui.CLEAR_PEN, subviews={ widgets.List{ @@ -197,7 +197,7 @@ function Menu:init() view_id='help_panel', autoarrange_subviews=true, frame=help_frame, - frame_style=gui.GREY_LINE_FRAME, + frame_style=gui.PANEL_FRAME, frame_background=gui.CLEAR_PEN, subviews={ widgets.WrappedLabel{ From 7329f6dee20be6a598b29c17e7f38d5e40893c97 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 04:16:38 -0800 Subject: [PATCH 0261/2222] use new frame names --- library/lua/gui/dialogs.lua | 2 +- library/lua/gui/widgets.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 0b923e7c0..3b2470114 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -13,7 +13,7 @@ MessageBox = defclass(MessageBox, gui.FramedScreen) MessageBox.focus_path = 'MessageBox' MessageBox.ATTRS{ - frame_style = gui.GREY_LINE_FRAME, + frame_style = gui.WINDOW_FRAME, frame_inset = 1, -- new attrs on_accept = DEFAULT_NIL, diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index f0129ffe3..4538c39df 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -531,7 +531,7 @@ end Window = defclass(Window, Panel) Window.ATTRS { - frame_style = gui.GREY_LINE_FRAME, + frame_style = gui.WINDOW_FRAME, frame_background = gui.CLEAR_PEN, frame_inset = 1, draggable = true, From 38e6e0a747d8013e45e5b7c94284d61d73be1c70 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 08:01:45 -0800 Subject: [PATCH 0262/2222] ensure orders overlay is on the correct panel even when the total screen size is very small --- plugins/lua/orders.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index 40c79fca4..282b5997d 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -47,7 +47,7 @@ end OrdersOverlay = defclass(OrdersOverlay, overlay.OverlayWidget) OrdersOverlay.ATTRS{ - default_pos={x=61,y=-6}, + default_pos={x=53,y=-6}, viewscreens='dwarfmode', frame={w=30, h=4}, frame_style=gui.MEDIUM_FRAME, From 6592cadc0947ce1b223b1ac4f944268c4b8f1cdf Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Mon, 23 Jan 2023 13:35:26 -0500 Subject: [PATCH 0263/2222] Bugfixes, resolved CTD issue. --- plugins/automelt.cpp | 78 +++++++++++++++++++++------------------- plugins/lua/automelt.lua | 15 +++++--- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index c78f36f51..e9bd30f76 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -57,8 +57,10 @@ static const string CONFIG_KEY = string(plugin_name) + "/config"; static const string STOCKPILE_CONFIG_KEY_PREFIX = string(plugin_name) + "/stockpile/"; static PersistentDataItem config; -static vector watched_stockpiles; -static unordered_map watched_stockpiles_indices; +// static vector watched_stockpiles; +// static unordered_map watched_stockpiles_indices; + +static unordered_map watched_stockpiles; enum StockpileConfigValues { @@ -92,31 +94,31 @@ static void set_config_bool(PersistentDataItem &c, int index, bool value) static PersistentDataItem &ensure_stockpile_config(color_ostream &out, int id) { - if (watched_stockpiles_indices.count(id)) - return watched_stockpiles[watched_stockpiles_indices[id]]; + DEBUG(cycle,out).print("ensuring stockpile config id=%d\n", id); + if (watched_stockpiles.count(id)){ + DEBUG(cycle,out).print("stockpile exists in watched_indices\n"); + return watched_stockpiles[id]; + } + string keyname = STOCKPILE_CONFIG_KEY_PREFIX + int_to_string(id); - DEBUG(status, out).print("creating new persistent key for stockpile %d\n", id); - watched_stockpiles.emplace_back(World::GetPersistentData(keyname, NULL)); - size_t idx = watched_stockpiles.size() - 1; - watched_stockpiles_indices.emplace(id, idx); - return watched_stockpiles[idx]; + DEBUG(status,out).print("creating new persistent key for stockpile %d\n", id); + watched_stockpiles.emplace(id, World::GetPersistentData(keyname, NULL)); + return watched_stockpiles[id]; } static void remove_stockpile_config(color_ostream &out, int id) { - if (!watched_stockpiles_indices.count(id)) + if (!watched_stockpiles.count(id)) return; DEBUG(status, out).print("removing persistent key for stockpile %d\n", id); - size_t idx = watched_stockpiles_indices[id]; - World::DeletePersistentData(watched_stockpiles[idx]); - watched_stockpiles.erase(watched_stockpiles.begin() + idx); - watched_stockpiles_indices.erase(id); + World::DeletePersistentData(watched_stockpiles[id]); + watched_stockpiles.erase(id); } static void validate_stockpile_configs(color_ostream &out) { for (auto &c : watched_stockpiles) { - int id = get_config_val(c, STOCKPILE_CONFIG_ID); + int id = get_config_val(c.second, STOCKPILE_CONFIG_ID); if (!df::building::find(id)){ remove_stockpile_config(out, id); } @@ -174,13 +176,14 @@ DFhackCExport command_result plugin_load_data(color_ostream &out) } DEBUG(status, out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); - World::GetPersistentData(&watched_stockpiles, STOCKPILE_CONFIG_KEY_PREFIX, true); - watched_stockpiles_indices.clear(); - const size_t num_watched_stockpiles = watched_stockpiles.size(); + vector loaded_persist_data; + World::GetPersistentData(&loaded_persist_data, STOCKPILE_CONFIG_KEY_PREFIX, true); + watched_stockpiles.clear(); + const size_t num_watched_stockpiles = loaded_persist_data.size(); for (size_t idx = 0; idx < num_watched_stockpiles; ++idx) { - auto &c = watched_stockpiles[idx]; - watched_stockpiles_indices.emplace(get_config_val(c, STOCKPILE_CONFIG_ID), idx); + auto &c = loaded_persist_data[idx]; + watched_stockpiles.emplace(get_config_val(c, STOCKPILE_CONFIG_ID), c); } validate_stockpile_configs(out); @@ -460,9 +463,9 @@ static int32_t scan_stockpiles(color_ostream &out, bool should_melt, mapid)) { - auto &c = watched_stockpiles[watched_stockpiles_indices[stockpile->id]]; + if (watched_stockpiles.count(stockpile->id)) { + auto &c = watched_stockpiles[stockpile->id]; monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); int id = get_config_val(c, STOCKPILE_CONFIG_ID); item_count = item_count_piles[id]; @@ -666,11 +669,12 @@ static void automelt_printStatus(color_ostream &out) { } static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitored) { - DEBUG(status,out).print("entering automelt_setStockpileConfig\n"); + DEBUG(status,out).print("entering automelt_setStockpileConfig for id=%d and monitored=%d\n", id, monitored); validate_stockpile_configs(out); - bool isInvalidStockpile = !df::building::find(id); + bool isInvalidStockpile = !df::building::find(id) || !isStockpile(df::building::find(id)); bool hasNoData = !monitored; if (isInvalidStockpile || hasNoData) { + DEBUG(cycle,out).print("calling remove_stockpile_config with id=%d monitored=%d\n", id, monitored); remove_stockpile_config(out, id); return; } @@ -703,14 +707,12 @@ static int automelt_getStockpileConfig(lua_State *L) { id = stockpile->id; found = true; break; - } } if (!found) return 0; - } else { const char * name = lua_tostring(L, -1); if (!name) @@ -732,14 +734,13 @@ static int automelt_getStockpileConfig(lua_State *L) { } } - } if (!found) return 0; } - if (watched_stockpiles_indices.count(id)) { - push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); + if (watched_stockpiles.count(id)) { + push_stockpile_config(L, watched_stockpiles[id]); } else { push_stockpile_config(L, id, false); } @@ -801,8 +802,11 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { bldg_count++; int id = pile->id; - if (watched_stockpiles_indices.count(id)) { - push_stockpile_config(L, watched_stockpiles[watched_stockpiles_indices[id]]); + DEBUG(cycle,*out).print("id=%d\ncount_res=%d\n", id, watched_stockpiles.count(id)); + + if (watched_stockpiles.count(id)) { + DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); + push_stockpile_config(L, watched_stockpiles[id]); } else { push_stockpile_config(L, id, false); } diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index dc6c94f9b..d70cf2e8e 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -62,12 +62,17 @@ function getItemCountsAndStockpileConfigs() ret.premarked_item_counts = table.remove(data, 1) ret.stockpile_configs = data for _,c in ipairs(ret.stockpile_configs) do - c.name = df.building.find(c.id).name - if not c.name or c.name == '' then - c.name = (fmt):format(tostring(df.building.find(c.id).stockpile_number)) - c.name = c.name.gsub(c.name, '^%s*(.-)%s*$', '%1') + if not c.id or c.id == -1 then + c.name = "ERROR" + c.monitored = false + else + c.name = df.building.find(c.id).name + if not c.name or c.name == '' then + c.name = (fmt):format(tostring(df.building.find(c.id).stockpile_number)) + end + c.monitored = c.monitored ~= 0 end - c.monitored = c.monitored ~= 0 + end return ret end From 75364269298635ba2394223aff9bf1f0bb30e805 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Mon, 23 Jan 2023 13:36:12 -0500 Subject: [PATCH 0264/2222] trailing whitespace trim --- plugins/automelt.cpp | 2 +- plugins/lua/automelt.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index e9bd30f76..231a060fa 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -803,7 +803,7 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { int id = pile->id; DEBUG(cycle,*out).print("id=%d\ncount_res=%d\n", id, watched_stockpiles.count(id)); - + if (watched_stockpiles.count(id)) { DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); push_stockpile_config(L, watched_stockpiles[id]); diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index d70cf2e8e..4f1d82ad9 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -72,7 +72,7 @@ function getItemCountsAndStockpileConfigs() end c.monitored = c.monitored ~= 0 end - + end return ret end From a45f9921798c33099e260a70affb968b4f71bc49 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 11:22:44 -0800 Subject: [PATCH 0265/2222] update scrips head --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 9666cb8eb..92ede36dc 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9666cb8ebcc51f8e4bb2fb99116636793a7e4270 +Subproject commit 92ede36dcad040ab79df9e0ecfb267e4ee3a71e3 From bf2dcc0070eb5e867f7168a2bffd863c93a5045c Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:44:25 +0000 Subject: [PATCH 0266/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 92ede36dc..5f0ed4260 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 92ede36dcad040ab79df9e0ecfb267e4ee3a71e3 +Subproject commit 5f0ed4260541353ab52a649645094f33480c8933 From 386546126ede76f3b6b1ad3fdfabec37a0fd9a98 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Mon, 23 Jan 2023 16:14:49 -0500 Subject: [PATCH 0267/2222] fix GH suggest, cache found building --- plugins/automelt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 231a060fa..ba35683c1 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -671,7 +671,8 @@ static void automelt_printStatus(color_ostream &out) { static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitored) { DEBUG(status,out).print("entering automelt_setStockpileConfig for id=%d and monitored=%d\n", id, monitored); validate_stockpile_configs(out); - bool isInvalidStockpile = !df::building::find(id) || !isStockpile(df::building::find(id)); + auto bldg = df::building::find(id); + bool isInvalidStockpile = bldg || !isStockpile(bldg); bool hasNoData = !monitored; if (isInvalidStockpile || hasNoData) { DEBUG(cycle,out).print("calling remove_stockpile_config with id=%d monitored=%d\n", id, monitored); From c5ce1b17660254ce1ad9b045ac717b113a001936 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Mon, 23 Jan 2023 16:20:37 -0500 Subject: [PATCH 0268/2222] Fix build complaints --- plugins/automelt.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index ba35683c1..e56c79d9f 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -652,10 +652,9 @@ static void automelt_printStatus(color_ostream &out) { marked_item_count = premarked_item_count_piles[id]; } - int32_t stockpile_number = stockpile->stockpile_number; if (stockpile->name.empty()) { string stock_name = "Stockpile #" + int_to_string(stockpile->stockpile_number); - out.print(fmt, name_width, stock_name, monitored ? "[x]": "[ ]", + out.print(fmt, name_width, stock_name.c_str(), monitored ? "[x]": "[ ]", int_to_string(item_count).c_str(), int_to_string(marked_item_count).c_str()); } else { out.print(fmt, name_width, stockpile->name.c_str(), monitored ? "[x]": "[ ]", @@ -803,8 +802,6 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { bldg_count++; int id = pile->id; - DEBUG(cycle,*out).print("id=%d\ncount_res=%d\n", id, watched_stockpiles.count(id)); - if (watched_stockpiles.count(id)) { DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); push_stockpile_config(L, watched_stockpiles[id]); From 87507079485c44161d29ce083ef9157057c462c7 Mon Sep 17 00:00:00 2001 From: Eamon Bode Date: Mon, 23 Jan 2023 16:32:47 -0500 Subject: [PATCH 0269/2222] Update plugins/automelt.cpp Co-authored-by: Myk --- plugins/automelt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index e56c79d9f..5b68bb10b 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -671,7 +671,7 @@ static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitor DEBUG(status,out).print("entering automelt_setStockpileConfig for id=%d and monitored=%d\n", id, monitored); validate_stockpile_configs(out); auto bldg = df::building::find(id); - bool isInvalidStockpile = bldg || !isStockpile(bldg); + bool isInvalidStockpile = !bldg || !isStockpile(bldg); bool hasNoData = !monitored; if (isInvalidStockpile || hasNoData) { DEBUG(cycle,out).print("calling remove_stockpile_config with id=%d monitored=%d\n", id, monitored); From 73dd0b4489f5f8922f72ce679954ca6b556ca335 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 13:46:39 -0800 Subject: [PATCH 0270/2222] stronger wording for ZScreen guidance --- docs/dev/Lua API.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ad1e6ae3a..63b533d61 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4145,7 +4145,7 @@ ZScreen don't handle the input (i.e. they all return something falsey), the input is passed directly to the first underlying non-ZScreen. All this behavior is implemented in ``ZScreen:onInput()``, which subclasses -should *not* override. Instead, ZScreen subclasses should delegate all input +**must not override**. Instead, ZScreen subclasses should delegate all input processing to subviews. Consider using a `Window class`_ widget as your top level input processor. From 5fad172adc2f92f2e775a83ec1f5e9a3964bf041 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 13:46:50 -0800 Subject: [PATCH 0271/2222] update scripts head --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 5f0ed4260..cec17bfaa 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5f0ed4260541353ab52a649645094f33480c8933 +Subproject commit cec17bfaaa777de62833c818316e33bad75fa8e6 From 540faff88f97f9c9f2f7b8af2c992e9b66d38d8a Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 8 Dec 2022 11:21:17 -0800 Subject: [PATCH 0272/2222] Implements plugin: channel-safely v1.1.2b - Updates rst documentation - Adds troubleshooting section - Renames the monitor feature to monitoring - Adds cave-in helper functions - ChannelJobs::has_cavein_conditions() - ChannelJobs::possible_cavein() - find_dwarf() ~ finds the nearest dwarf or the first one that has a path to a position - Moves dignow/resurrect to inlines.h - Improves management of regular dig designations - Adds df::job* tracking back into ChannelJobs to simplify cancellations - Updates/improves debug logging - Switches unordered structures with ordered in some locations to have ordered debugging information - Simplifies ChannelManager::manage_group() - Fixes up ChannelManager::manage_one() - the return value is now useful even if unused --- docs/plugins/channel-safely.rst | 19 ++- plugins/channel-safely/channel-groups.cpp | 77 ++++++++++-- plugins/channel-safely/channel-manager.cpp | 67 +++++++---- .../channel-safely/channel-safely-plugin.cpp | 113 ++++++++---------- .../channel-safely/include/channel-groups.h | 8 +- plugins/channel-safely/include/channel-jobs.h | 12 ++ .../channel-safely/include/channel-manager.h | 6 +- plugins/channel-safely/include/inlines.h | 101 +++++++++++++++- plugins/channel-safely/include/plugin.h | 3 +- 9 files changed, 298 insertions(+), 108 deletions(-) diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index e87a36455..b6957b95e 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -52,18 +52,17 @@ Commands -------- :runonce: Run the safety procedures once to set the marker mode of designations. -:rebuild: Rebuild the designation group data. Intended for to be used in the event - the marker mode isn't being set correctly (mostly for debugging). +:rebuild: Rebuild the designation group data. You should also read Troubleshooting. Features -------- :require-vision: Toggle whether the dwarves need vision of a tile before channeling to it can be deemed unsafe. (default: enabled) -:monitor: Toggle whether to monitor the conditions of active digs. (default: disabled) +:monitoring: Toggle whether to monitor the conditions of active digs. (default: disabled) :resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled units who die while digging. (default: disabled) :insta-dig: Toggle whether to use insta-digging on unreachable designations. - Runs on the refresh cycles. (default: disabled) + Runs on the refresh cycles. Use with caution. (default: disabled) Settings -------- @@ -74,3 +73,15 @@ Settings :ignore-threshold: Sets the priority threshold below which designations are processed. You can set to 1 or 0 to effectively disable the scanning. (default: 5) :fall-threshold: Sets the fall threshold beyond which is considered unsafe. (default: 1) + +Troubleshooting +--------------- + +If designations aren't switching correctly, try putting the designations into marker mode. +Then press . (next) or resume. If you're debugging code you'll want these: + +[todo: make this a block of code] + +:filter1: ``debugfilter set Info channel manager`` +:filter2: ``debugfilter set Debug channel plugin`` +:filter3: ``debugfilter set Trace channel group`` diff --git a/plugins/channel-safely/channel-groups.cpp b/plugins/channel-safely/channel-groups.cpp index 8f7f4b117..6c310b5c4 100644 --- a/plugins/channel-safely/channel-groups.cpp +++ b/plugins/channel-safely/channel-groups.cpp @@ -13,12 +13,63 @@ void ChannelJobs::load_channel_jobs() { while (node) { df::job* job = node->item; node = node->next; - if (is_dig_job(job)) { + if (is_channel_job(job)) { locations.emplace(job->pos); + jobs.emplace(job->pos, job); } } } +bool ChannelJobs::has_cavein_conditions(const df::coord &map_pos) { + auto p = map_pos; + auto ttype = *Maps::getTileType(p); + if (!DFHack::isOpenTerrain(ttype)) { + // check shared neighbour for cave-in conditions + df::coord neighbours[4]; + get_connected_neighbours(map_pos, neighbours); + int connectedness = 4; + for (auto n: neighbours) { + if (active.count(n) || DFHack::isOpenTerrain(*Maps::getTileType(n))) { + connectedness--; + } + } + if (!connectedness) { + // do what? + p.z--; + ttype = *Maps::getTileType(p); + if (DFHack::isOpenTerrain(ttype) || DFHack::isFloorTerrain(ttype)) { + return true; + } + } + } + return false; +} + +bool ChannelJobs::possible_cavein(const df::coord &job_pos) { + for (auto iter = active.begin(); iter != active.end(); ++iter) { + if (*iter == job_pos) continue; + if (calc_distance(job_pos, *iter) <= 2) { + // find neighbours + df::coord n1[8]; + df::coord n2[8]; + get_neighbours(job_pos, n1); + get_neighbours(*iter, n2); + // find shared neighbours + for (int i = 0; i < 7; ++i) { + for (int j = i + 1; j < 8; ++j) { + if (n1[i] == n2[j]) { + if (has_cavein_conditions(n1[i])) { + WARN(jobs).print("Channel-Safely::jobs: Cave-in conditions detected at (" COORD ")\n", COORDARGS(n1[i])); + return true; + } + } + } + } + } + } + return false; +} + // adds map_pos to a group if an adjacent one exists, or creates one if none exist... if multiple exist they're merged into the first found void ChannelGroups::add(const df::coord &map_pos) { // if we've already added this, we don't need to do it again @@ -137,6 +188,7 @@ void ChannelGroups::scan(bool full_scan) { std::set gone_jobs; set_difference(last_jobs, jobs, gone_jobs); set_difference(jobs, last_jobs, new_jobs); + INFO(groups).print("gone jobs: %" PRIu64 "\nnew jobs: %" PRIu64 "\n",gone_jobs.size(), new_jobs.size()); for (auto &pos : new_jobs) { add(pos); } @@ -168,7 +220,15 @@ void ChannelGroups::scan(bool full_scan) { jobs.erase(map_pos); } block->designation[lx][ly].bits.dig = df::tile_dig_designation::No; - } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked) { + } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked ) { + if (!is_channel_designation(block->designation[lx][ly])) { + if (df::map_block* block_above = Maps::getBlock(bx, by, z+1)) { + if (!is_channel_designation(block_above->designation[lx][ly])) { + // dig designation without a channel above it, we can skip this. + continue; + } + } + } for (df::block_square_event* event: block->block_events) { if (auto evT = virtual_cast(event)) { // we want to let the user keep some designations free of being managed @@ -203,6 +263,7 @@ void ChannelGroups::scan(bool full_scan) { void ChannelGroups::clear() { debug_map(); WARN(groups).print(" <- clearing groups\n"); + jobs.clear(); group_blocks.clear(); free_spots.clear(); groups_map.clear(); @@ -269,13 +330,13 @@ size_t ChannelGroups::count(const df::coord &map_pos) const { // prints debug info about the groups stored, and their members void ChannelGroups::debug_groups() { - if (DFHack::debug_groups.isEnabled(DebugCategory::LTRACE)) { + if (DFHack::debug_groups.isEnabled(DebugCategory::LDEBUG)) { int idx = 0; - TRACE(groups).print(" debugging group data\n"); + DEBUG(groups).print(" debugging group data\n"); for (auto &group: groups) { - TRACE(groups).print(" group %d (size: %zu)\n", idx, group.size()); + DEBUG(groups).print(" group %d (size: %zu)\n", idx, group.size()); for (auto &pos: group) { - TRACE(groups).print(" (%d,%d,%d)\n", pos.x, pos.y, pos.z); + DEBUG(groups).print(" (%d,%d,%d)\n", pos.x, pos.y, pos.z); } idx++; } @@ -284,10 +345,10 @@ void ChannelGroups::debug_groups() { // prints debug info group mappings void ChannelGroups::debug_map() { - if (DFHack::debug_groups.isEnabled(DebugCategory::LDEBUG)) { + if (DFHack::debug_groups.isEnabled(DebugCategory::LTRACE)) { INFO(groups).print("Group Mappings: %zu\n", groups_map.size()); for (auto &pair: groups_map) { - DEBUG(groups).print(" map[" COORD "] = %d\n", COORDARGS(pair.first), pair.second); + TRACE(groups).print(" map[" COORD "] = %d\n", COORDARGS(pair.first), pair.second); } } } diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index e905f2cfb..e6c7ea671 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -5,6 +5,28 @@ #include //hash function for df::coord #include +#include +#include + +df::unit* find_dwarf(const df::coord &map_pos) { + df::unit* nearest = nullptr; + uint32_t distance; + for (auto unit : df::global::world->units.active) { + if (!nearest) { + nearest = unit; + distance = calc_distance(unit->pos, map_pos); + } else if (unit->status.labors[df::unit_labor::MINE]) { + uint32_t d = calc_distance(unit->pos, map_pos); + if (d < distance) { + nearest = unit; + distance = d; + } else if (Maps::canWalkBetween(unit->pos, map_pos)) { + return unit; + } + } + } + return nearest; +} // sets mark flags as necessary, for all designations void ChannelManager::manage_groups() { @@ -33,21 +55,17 @@ void ChannelManager::manage_group(const df::coord &map_pos, bool set_marker_mode void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool marker_mode) { INFO(manager).print("manage_group()\n"); if (!set_marker_mode) { - if (has_any_groups_above(groups, group)) { - marker_mode = true; - } else { - marker_mode = false; - } + marker_mode = has_any_groups_above(groups, group); } for (auto &designation: group) { - manage_one(group, designation, true, marker_mode); + manage_one(designation, true, marker_mode); } INFO(manager).print("manage_group() is done\n"); } -bool ChannelManager::manage_one(const Group &group, const df::coord &map_pos, bool set_marker_mode, bool marker_mode) { +bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, bool marker_mode) { if (Maps::isValidTilePos(map_pos)) { - INFO(manager).print("manage_one(" COORD ")\n", COORDARGS(map_pos)); + TRACE(manager).print("manage_one((" COORD "), %d, %d)\n", COORDARGS(map_pos), set_marker_mode, marker_mode); df::map_block* block = Maps::getTileBlock(map_pos); // we calculate the position inside the block* df::coord local(map_pos); @@ -58,41 +76,46 @@ bool ChannelManager::manage_one(const Group &group, const df::coord &map_pos, bo if (map_pos.z < mapz - 3) { // do we already know whether to set marker mode? if (set_marker_mode) { - DEBUG(manager).print(" -> marker_mode\n"); + TRACE(manager).print(" -> setting marker mode\n"); // if enabling marker mode, just do it if (marker_mode) { - tile_occupancy.bits.dig_marked = marker_mode; - return true; + goto markerMode; } - // if activating designation, check if it is safe to dig or not a channel designation + // otherwise we check for some safety if (!is_channel_designation(block->designation[Coord(local)]) || is_safe_to_dig_down(map_pos)) { + // not a channel designation, or it is safe to dig down if it is if (!block->flags.bits.designated) { block->flags.bits.designated = true; } - tile_occupancy.bits.dig_marked = false; + // we need to cache the tile now that we're activating the designation TileCache::Get().cache(map_pos, block->tiletype[Coord(local)]); + TRACE(manager).print("marker mode DISABLED\n"); + tile_occupancy.bits.dig_marked = false; + } else { + // we want to activate the designation, that's not what we get + goto markerMode; } - return false; - } else { - // next search for the designation priority - DEBUG(manager).print(" if(has_groups_above())\n"); + // not set_marker_mode + TRACE(manager).print(" if(has_groups_above())\n"); // check that the group has no incomplete groups directly above it if (has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos)) { - DEBUG(manager).print(" has_groups_above: setting marker mode\n"); + + markerMode: + + TRACE(manager).print("marker mode ENABLED\n"); tile_occupancy.bits.dig_marked = true; if (jobs.count(map_pos)) { - jobs.erase(map_pos); + cancel_job(map_pos); } - WARN(manager).print(" <- manage_one() exits normally\n"); - return true; } } } else { // if we are though, it should be totally safe to dig tile_occupancy.bits.dig_marked = false; } - WARN(manager).print(" <- manage_one() exits normally\n"); + TRACE(manager).print(" <- manage_one() exits normally\n"); + return true; } return false; } diff --git a/plugins/channel-safely/channel-safely-plugin.cpp b/plugins/channel-safely/channel-safely-plugin.cpp index 8af90c335..882685385 100644 --- a/plugins/channel-safely/channel-safely-plugin.cpp +++ b/plugins/channel-safely/channel-safely-plugin.cpp @@ -1,7 +1,7 @@ /* Prevent channeling down into known open space. Author: Josh Cooper Created: Aug. 4 2020 -Updated: Nov. 28 2022 +Updated: Dec. 5 2022 Enable plugin: -> build groups @@ -56,8 +56,6 @@ Updated: Nov. 28 2022 #include #include -#include -#include #include #include #include @@ -98,7 +96,8 @@ enum FeatureConfigData { VISION, MONITOR, RESURRECT, - INSTADIG + INSTADIG, + RISKAVERSE }; enum SettingConfigData { @@ -136,38 +135,13 @@ df::coord simulate_area_fall(const df::coord &pos) { return lowest; } -// executes dig designations for the specified tile coordinates -inline bool dig_now(color_ostream &out, const df::coord &map_pos) { - bool ret = false; - - lua_State* state = Lua::Core::State; - static const char* module_name = "plugins.dig-now"; - static const char* fn_name = "dig_now_tile"; - // the stack layout isn't likely to change, ever - static auto args_lambda = [&map_pos](lua_State* L) { - Lua::Push(L, map_pos); - }; - static auto res_lambda = [&ret](lua_State* L) { - ret = lua_toboolean(L, -1); - }; - - Lua::StackUnwinder top(state); - Lua::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda); - return ret; -} - -// fully heals the unit specified, resurrecting if need be -inline void resurrect(color_ostream &out, const int32_t &unit) { - std::vector params{"-r", "--unit", std::to_string(unit)}; - Core::getInstance().runCommand(out,"full-heal", params); -} - namespace CSP { std::unordered_map endangered_units; std::unordered_map job_id_map; std::unordered_map active_jobs; std::unordered_map active_workers; + std::unordered_map last_safe; std::unordered_set dignow_queue; @@ -184,10 +158,11 @@ namespace CSP { void SaveSettings() { if (pfeature.isValid() && psetting.isValid()) { try { - pfeature.ival(MONITOR) = config.monitor_active; + pfeature.ival(MONITOR) = config.monitoring; pfeature.ival(VISION) = config.require_vision; pfeature.ival(INSTADIG) = config.insta_dig; pfeature.ival(RESURRECT) = config.resurrect; + pfeature.ival(RISKAVERSE) = config.riskaverse; psetting.ival(REFRESH_RATE) = config.refresh_freq; psetting.ival(MONITOR_RATE) = config.monitor_freq; @@ -209,10 +184,11 @@ namespace CSP { SaveSettings(); } else { try { - config.monitor_active = pfeature.ival(MONITOR); + config.monitoring = pfeature.ival(MONITOR); config.require_vision = pfeature.ival(VISION); config.insta_dig = pfeature.ival(INSTADIG); config.resurrect = pfeature.ival(RESURRECT); + config.riskaverse = pfeature.ival(RISKAVERSE); config.ignore_threshold = psetting.ival(IGNORE_THRESH); config.fall_threshold = psetting.ival(FALL_THRESH); @@ -227,28 +203,36 @@ namespace CSP { void UnpauseEvent(bool full_scan = false){ CoreSuspender suspend; // we need exclusive access to df memory and this call stack doesn't already have a lock - INFO(monitor).print("UnpauseEvent()\n"); + DEBUG(plugin).print("UnpauseEvent()\n"); ChannelManager::Get().build_groups(full_scan); ChannelManager::Get().manage_groups(); - ChannelManager::Get().debug(); - INFO(monitor).print("UnpauseEvent() exits\n"); + DEBUG(plugin).print("UnpauseEvent() exits\n"); } void JobStartedEvent(color_ostream &out, void* j) { if (enabled && World::isFortressMode() && Maps::IsValid()) { - INFO(jobs).print("JobStartedEvent()\n"); + TRACE(jobs).print("JobStartedEvent()\n"); auto job = (df::job*) j; // validate job type if (ChannelManager::Get().exists(job->pos)) { - WARN(jobs).print(" valid channel job:\n"); + DEBUG(jobs).print(" valid channel job:\n"); df::unit* worker = Job::getWorker(job); // there is a valid worker (living citizen) on the job? right.. if (worker && Units::isAlive(worker) && Units::isCitizen(worker)) { + ChannelManager::Get().jobs.mark_active(job->pos); + if (config.riskaverse) { + if (ChannelManager::Get().jobs.possible_cavein(job->pos)) { + cancel_job(job); + ChannelManager::Get().manage_one(job->pos, true, true); + } else { + ChannelManager::Get().manage_group(job->pos, true, false); + } + } DEBUG(jobs).print(" valid worker:\n"); // track workers on jobs df::coord &pos = job->pos; - WARN(jobs).print(" -> Starting job at (" COORD ")\n", COORDARGS(pos)); - if (config.monitor_active || config.resurrect) { + TRACE(jobs).print(" -> Starting job at (" COORD ")\n", COORDARGS(pos)); + if (config.monitoring || config.resurrect) { job_id_map.emplace(job, job->id); active_jobs.emplace(job->id, job); active_workers[job->id] = worker; @@ -263,16 +247,17 @@ namespace CSP { Maps::getTileDesignation(job->pos)->bits.traffic = df::tile_traffic::Restricted; } } - INFO(jobs).print(" <- JobStartedEvent() exits normally\n"); + TRACE(jobs).print(" <- JobStartedEvent() exits normally\n"); } } void JobCompletedEvent(color_ostream &out, void* j) { if (enabled && World::isFortressMode() && Maps::IsValid()) { - INFO(jobs).print("JobCompletedEvent()\n"); + TRACE(jobs).print("JobCompletedEvent()\n"); auto job = (df::job*) j; // we only care if the job is a channeling one if (ChannelManager::Get().exists(job->pos)) { + ChannelManager::Get().manage_group(job->pos, true, false); // check job outcome auto block = Maps::getTileBlock(job->pos); df::coord local(job->pos); @@ -283,15 +268,14 @@ namespace CSP { // the job can be considered done df::coord below(job->pos); below.z--; - WARN(jobs).print(" -> (" COORD ") is marked done, managing group below.\n", COORDARGS(job->pos)); - // mark done and manage below + DEBUG(jobs).print(" -> (" COORD ") is marked done, managing group below.\n", COORDARGS(job->pos)); + // mark done and manage below (and the rest of the group, if there were cavein candidates) block->designation[Coord(local)].bits.traffic = df::tile_traffic::Normal; ChannelManager::Get().mark_done(job->pos); ChannelManager::Get().manage_group(below); - ChannelManager::Get().debug(); if (config.resurrect) { // this is the only place we can be 100% sure of "safety" - // (excluding deadly enemies that will have arrived) + // (excluding deadly enemies that may have arrived, and future digging) if (active_workers.count(job->id)) { df::unit* worker = active_workers[job->id]; last_safe[worker->id] = worker->pos; @@ -304,7 +288,7 @@ namespace CSP { active_workers.erase(job->id); active_jobs.erase(job->id); } - INFO(jobs).print("JobCompletedEvent() exits\n"); + TRACE(jobs).print("JobCompletedEvent() exits\n"); } } @@ -365,9 +349,7 @@ namespace CSP { } void OnUpdate(color_ostream &out) { - static auto print_res_msg = [](df::unit* unit) { - WARN(plugin).print("Channel-Safely: Resurrecting..\n [id: %d]\n", unit->id); - }; + CoreSuspender suspend; if (enabled && World::isFortressMode() && Maps::IsValid() && !World::ReadPauseState()) { static int32_t last_tick = df::global::world->frame_counter; static int32_t last_monitor_tick = df::global::world->frame_counter; @@ -382,17 +364,18 @@ namespace CSP { if (config.insta_dig) { TRACE(monitor).print(" -> evaluate dignow queue\n"); for (auto iter = dignow_queue.begin(); iter != dignow_queue.end();) { - dig_now(out, *iter); // teleports units to the bottom of a simulated fall + auto map_pos = *iter; + dig_now(out, map_pos); // teleports units to the bottom of a simulated fall + ChannelManager::Get().mark_done(map_pos); iter = dignow_queue.erase(iter); - DEBUG(plugin).print(">INSTA-DIGGING<\n"); } } - UnpauseEvent(); + UnpauseEvent(false); TRACE(monitor).print("OnUpdate() refresh done\n"); } // Clean up stale df::job* - if ((config.monitor_active || config.resurrect) && tick - last_tick >= 1) { + if ((config.monitoring || config.resurrect) && tick - last_tick >= 1) { last_tick = tick; // make note of valid jobs std::unordered_map valid_jobs; @@ -415,7 +398,7 @@ namespace CSP { } // Monitoring Active and Resurrecting Dead - if (config.monitor_active && tick - last_monitor_tick >= config.monitor_freq) { + if (config.monitoring && tick - last_monitor_tick >= config.monitor_freq) { last_monitor_tick = tick; TRACE(monitor).print("OnUpdate() monitoring now\n"); @@ -427,7 +410,7 @@ namespace CSP { if (!Maps::isValidTilePos(job->pos)) continue; TRACE(monitor).print(" -> check for job in tracking\n"); if (Units::isAlive(unit)) { - if (!config.monitor_active) continue; + if (!config.monitoring) continue; TRACE(monitor).print(" -> compare positions of worker and job\n"); // save position @@ -475,7 +458,6 @@ namespace CSP { df::coord lowest = simulate_fall(last_safe[unit->id]); Units::teleport(unit, lowest); } - print_res_msg(unit); } } TRACE(monitor).print("OnUpdate() monitoring done\n"); @@ -504,7 +486,6 @@ namespace CSP { df::coord lowest = simulate_fall(last_safe[unit->id]); Units::teleport(unit, lowest); } - print_res_msg(unit); } } } @@ -547,7 +528,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { EM::registerListener(EventType::JOB_COMPLETED, jobCompletionHandler, plugin_self); // manage designations to start off (first time building groups [very important]) out.print("channel-safely: enabled!\n"); - CSP::UnpauseEvent(); + CSP::UnpauseEvent(true); } else if (!enable) { // don't need the groups if the plugin isn't going to be enabled EM::unregisterAll(plugin_self); @@ -562,11 +543,10 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan case SC_UNPAUSED: if (enabled && World::isFortressMode() && Maps::IsValid()) { // manage all designations on unpause - CSP::UnpauseEvent(); + CSP::UnpauseEvent(true); } break; case SC_MAP_LOADED: - case SC_WORLD_LOADED: // cache the map size Maps::getSize(mapx, mapy, mapz); CSP::ClearData(); @@ -609,15 +589,17 @@ command_result channel_safely(color_ostream &out, std::vector ¶ return DFHack::CR_WRONG_USAGE; } try { - if(parameters[1] == "monitor"){ - if (state != config.monitor_active) { - config.monitor_active = state; + if(parameters[1] == "monitoring"){ + if (state != config.monitoring) { + config.monitoring = state; // if this is a fresh start if (state && !config.resurrect) { // we need a fresh start CSP::active_workers.clear(); } } + } else if (parameters[1] == "risk-averse") { + config.riskaverse = state; } else if (parameters[1] == "require-vision") { config.require_vision = state; } else if (parameters[1] == "insta-dig") { @@ -626,7 +608,7 @@ command_result channel_safely(color_ostream &out, std::vector ¶ if (state != config.resurrect) { config.resurrect = state; // if this is a fresh start - if (state && !config.monitor_active) { + if (state && !config.monitoring) { // we need a fresh start CSP::active_workers.clear(); } @@ -656,7 +638,8 @@ command_result channel_safely(color_ostream &out, std::vector ¶ } else { out.print("Channel-Safely is %s\n", enabled ? "ENABLED." : "DISABLED."); out.print(" FEATURES:\n"); - out.print(" %-20s\t%s\n", "monitor-active: ", config.monitor_active ? "on." : "off."); + out.print(" %-20s\t%s\n", "risk-averse: ", config.riskaverse ? "on." : "off."); + out.print(" %-20s\t%s\n", "monitoring: ", config.monitoring ? "on." : "off."); out.print(" %-20s\t%s\n", "require-vision: ", config.require_vision ? "on." : "off."); out.print(" %-20s\t%s\n", "insta-dig: ", config.insta_dig ? "on." : "off."); out.print(" %-20s\t%s\n", "resurrect: ", config.resurrect ? "on." : "off."); diff --git a/plugins/channel-safely/include/channel-groups.h b/plugins/channel-safely/include/channel-groups.h index 8e3224c98..5ec6b15c9 100644 --- a/plugins/channel-safely/include/channel-groups.h +++ b/plugins/channel-safely/include/channel-groups.h @@ -12,7 +12,7 @@ using namespace DFHack; -using Group = std::unordered_set; +using Group = std::set; using Groups = std::vector; /* Used to build groups of adjacent channel designations/jobs @@ -37,6 +37,12 @@ private: protected: void add(const df::coord &map_pos); public: + int debugGIndex(const df::coord &map_pos) { + if (groups_map.count(map_pos)) { + return groups_map[map_pos]; + } + return -1; + } explicit ChannelGroups(ChannelJobs &jobs) : jobs(jobs) { groups.reserve(200); } void scan_one(const df::coord &map_pos); void scan(bool full_scan = false); diff --git a/plugins/channel-safely/include/channel-jobs.h b/plugins/channel-safely/include/channel-jobs.h index 3be704aeb..13f0d0e77 100644 --- a/plugins/channel-safely/include/channel-jobs.h +++ b/plugins/channel-safely/include/channel-jobs.h @@ -6,6 +6,7 @@ #include #include +#include using namespace DFHack; @@ -23,20 +24,31 @@ private: friend class ChannelGroup; using Jobs = std::unordered_set; // job* will exist until it is complete, and likely beyond + std::unordered_map jobs; Jobs locations; + Jobs active; +protected: + bool has_cavein_conditions(const df::coord &map_pos); public: void load_channel_jobs(); + bool possible_cavein(const df::coord &job_pos); + void mark_active(const df::coord &map_pos) { active.emplace(map_pos); } void clear() { + active.clear(); locations.clear(); + jobs.clear(); } int count(const df::coord &map_pos) const { return locations.count(map_pos); } Jobs::iterator erase(const df::coord &map_pos) { + active.erase(map_pos); + jobs.erase(map_pos); auto iter = locations.find(map_pos); if (iter != locations.end()) { return locations.erase(iter); } return iter; } + df::job* find_job(const df::coord &map_pos) const { return jobs.count(map_pos) ? jobs.find(map_pos)->second : nullptr; } Jobs::const_iterator find(const df::coord &map_pos) const { return locations.find(map_pos); } Jobs::const_iterator begin() const { return locations.begin(); } Jobs::const_iterator end() const { return locations.end(); } diff --git a/plugins/channel-safely/include/channel-manager.h b/plugins/channel-safely/include/channel-manager.h index 323ce8468..4df02ed38 100644 --- a/plugins/channel-safely/include/channel-manager.h +++ b/plugins/channel-safely/include/channel-manager.h @@ -12,11 +12,11 @@ using namespace DFHack; // Uses GroupData to detect an unsafe work environment class ChannelManager { private: - ChannelJobs jobs; ChannelManager()= default; protected: public: ChannelGroups groups = ChannelGroups(jobs); + ChannelJobs jobs; static ChannelManager& Get(){ static ChannelManager instance; @@ -28,12 +28,12 @@ public: void manage_groups(); void manage_group(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); void manage_group(const Group &group, bool set_marker_mode = false, bool marker_mode = false); - bool manage_one(const Group &group, const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); + bool manage_one(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); void mark_done(const df::coord &map_pos); bool exists(const df::coord &map_pos) const { return groups.count(map_pos); } void debug() { DEBUG(groups).print(" DEBUGGING GROUPS:\n"); - groups.debug_groups(); groups.debug_map(); + groups.debug_groups(); } }; diff --git a/plugins/channel-safely/include/inlines.h b/plugins/channel-safely/include/inlines.h index a8686d235..38e73814c 100644 --- a/plugins/channel-safely/include/inlines.h +++ b/plugins/channel-safely/include/inlines.h @@ -2,21 +2,41 @@ #include "plugin.h" #include "channel-manager.h" +#include +#include +#include #include #include -#include #include #include #define Coord(id) id.x][id.y -#define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 +#define COORD "%" PRIi16 ",%" PRIi16 ",%" PRIi16 #define COORDARGS(id) id.x, id.y, id.z namespace CSP { extern std::unordered_set dignow_queue; } +inline uint32_t calc_distance(df::coord p1, df::coord p2) { + // calculate chebyshev (chessboard) distance + uint32_t distance = abs(p2.z - p1.z); + distance += max(abs(p2.x - p1.x), abs(p2.y - p1.y)); + return distance; +} + +inline void get_connected_neighbours(const df::coord &map_pos, df::coord(&neighbours)[4]) { + neighbours[0] = map_pos; + neighbours[1] = map_pos; + neighbours[2] = map_pos; + neighbours[3] = map_pos; + neighbours[0].y--; + neighbours[1].x--; + neighbours[2].x++; + neighbours[3].y++; +} + inline void get_neighbours(const df::coord &map_pos, df::coord(&neighbours)[8]) { neighbours[0] = map_pos; neighbours[1] = map_pos; @@ -36,6 +56,39 @@ inline void get_neighbours(const df::coord &map_pos, df::coord(&neighbours)[8]) neighbours[7].x++; neighbours[7].y++; } +inline uint8_t count_accessibility(const df::coord &unit_pos, const df::coord &map_pos) { + df::coord neighbours[8]; + df::coord connections[4]; + get_neighbours(map_pos, neighbours); + get_connected_neighbours(map_pos, connections); + uint8_t accessibility = Maps::canWalkBetween(unit_pos, map_pos) ? 1 : 0; + for (auto n: neighbours) { + if (Maps::canWalkBetween(unit_pos, n)) { + accessibility++; + } + } + for (auto n : connections) { + if (Maps::canWalkBetween(unit_pos, n)) { + accessibility++; + } + } + return accessibility; +} + +inline bool isEntombed(const df::coord &unit_pos, const df::coord &map_pos) { + if (Maps::canWalkBetween(unit_pos, map_pos)) { + return false; + } + df::coord neighbours[8]; + get_neighbours(map_pos, neighbours); + for (auto n: neighbours) { + if (Maps::canWalkBetween(unit_pos, n)) { + return false; + } + } + return true; +} + inline bool is_dig_job(const df::job* job) { return job->job_type == df::job_type::Dig || job->job_type == df::job_type::DigChannel; } @@ -135,15 +188,20 @@ inline bool has_any_groups_above(const ChannelGroups &groups, const Group &group } inline void cancel_job(df::job* job) { - if (job != nullptr) { - df::coord &pos = job->pos; + if (job) { + const df::coord &pos = job->pos; df::map_block* job_block = Maps::getTileBlock(pos); uint16_t x, y; x = pos.x % 16; y = pos.y % 16; df::tile_designation &designation = job_block->designation[x][y]; auto type = job->job_type; + ChannelManager::Get().jobs.erase(pos); + Job::removeWorker(job); + Job::removePostings(job, true); Job::removeJob(job); + job_block->flags.bits.designated = true; + job_block->occupancy[x][y].bits.dig_marked = true; switch (type) { case job_type::Dig: designation.bits.dig = df::tile_dig_designation::Default; @@ -170,6 +228,41 @@ inline void cancel_job(df::job* job) { } } +inline void cancel_job(const df::coord &map_pos) { + cancel_job(ChannelManager::Get().jobs.find_job(map_pos)); + ChannelManager::Get().jobs.erase(map_pos); +} + +// executes dig designations for the specified tile coordinates +inline bool dig_now(color_ostream &out, const df::coord &map_pos) { + out.color(color_value::COLOR_YELLOW); + out.print("channel-safely: insta-dig: digging (" COORD ")<\n", COORDARGS(map_pos)); + bool ret = false; + + lua_State* state = Lua::Core::State; + static const char* module_name = "plugins.dig-now"; + static const char* fn_name = "dig_now_tile"; + // the stack layout isn't likely to change, ever + static auto args_lambda = [&map_pos](lua_State* L) { + Lua::Push(L, map_pos); + }; + static auto res_lambda = [&ret](lua_State* L) { + ret = lua_toboolean(L, -1); + }; + + Lua::StackUnwinder top(state); + Lua::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda); + return ret; +} + +// fully heals the unit specified, resurrecting if need be +inline void resurrect(color_ostream &out, const int32_t &unit) { + out.color(DFHack::COLOR_RED); + out.print("channel-safely: resurrecting [id: %d]\n", unit); + std::vector params{"-r", "--unit", std::to_string(unit)}; + Core::getInstance().runCommand(out,"full-heal", params); +} + template void set_difference(const Ctr1 &c1, const Ctr2 &c2, Ctr3 &c3) { for (const auto &a : c1) { diff --git a/plugins/channel-safely/include/plugin.h b/plugins/channel-safely/include/plugin.h index 23b2f8441..336d944ab 100644 --- a/plugins/channel-safely/include/plugin.h +++ b/plugins/channel-safely/include/plugin.h @@ -9,7 +9,8 @@ namespace DFHack { } struct Configuration { - bool monitor_active = false; + bool riskaverse = true; + bool monitoring = false; bool require_vision = true; bool insta_dig = false; bool resurrect = false; From a4bf2667700c6c2e441b2526bfc177d68bd64538 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 8 Dec 2022 11:37:28 -0800 Subject: [PATCH 0273/2222] Implements plugin: channel-safely v1.2 - Updates rst documentation - Adds feature: risk-averse - Revises ChannelManager::manage_group - Now performs analysis of group designations - If any designation has fall space, designations are analyzed for accessibility (a weighted score of how many ways it can be accessed) - If a designation has no fall space, but cannot be accessed it will be "dig_now"'ed - accessibility scores are stored for the management phase - Management loop has been extended - iff no cave-in candidates exist, then perform simple management (as requested) - if candidates do exist, then we must check if our current position is one - if the current position is a cave-in candidate it must also be within range (+2) of the least access - if the candidate is in range or on the dignow queue, then we activate the designation and modify the dig priority according to distance from least_access - if not a candidate, or the other checks failed, then we set the designation to marker mode --- docs/plugins/channel-safely.rst | 2 + plugins/channel-safely/channel-manager.cpp | 93 +++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index b6957b95e..1a2866920 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -58,6 +58,8 @@ Features -------- :require-vision: Toggle whether the dwarves need vision of a tile before channeling to it can be deemed unsafe. (default: enabled) +:risk-averse: Toggles whether to use cave-in prevention. Designations are activated in stages + and their priorities along edges are modified. (default: enabled) :monitoring: Toggle whether to monitor the conditions of active digs. (default: disabled) :resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled units who die while digging. (default: disabled) diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index e6c7ea671..df3bcad71 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -57,8 +57,97 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool if (!set_marker_mode) { marker_mode = has_any_groups_above(groups, group); } - for (auto &designation: group) { - manage_one(designation, true, marker_mode); + // cavein prevention + bool cavein_possible = false; + uint8_t least_access = 100; + std::unordered_map cavein_candidates; + if (!marker_mode) { + /* To prevent cave-ins we're looking at accessibility of tiles with open space below them + * If it has space below, it has somewhere to fall + * Accessibility tells us how close to a cave-in a tile is, low values are at risk of cave-ins + * To count access, we find a random miner dwarf and count how many tile neighbours they can path to + * */ + // find a dwarf to path from + df::coord miner_pos = find_dwarf(*group.begin())->pos; + + // Analyze designations + for (const auto &pos: group) { + df::coord below(pos); + below.z--; + const auto below_ttype = *Maps::getTileType(below); + // we can skip designations already queued for insta-digging + if (CSP::dignow_queue.count(pos)) continue; + if (DFHack::isOpenTerrain(below_ttype) || DFHack::isFloorTerrain(below_ttype)) { + // the tile below is not solid earth + // we're counting accessibility then dealing with 0 access + DEBUG(manager).print("analysis: cave-in condition found\n"); + INFO(manager).print("(%d) checking accessibility of (" COORD ") from (" COORD ")\n", cavein_possible,COORDARGS(pos),COORDARGS(miner_pos)); + auto access = count_accessibility(miner_pos, pos); + if (access == 0) { + TRACE(groups).print(" has 0 access\n"); + if (config.insta_dig) { + manage_one(pos, true, false); + dig_now(DFHack::Core::getInstance().getConsole(), pos); + mark_done(pos); + } else { + // todo: engage dig management, swap channel designations for dig + } + } else { + WARN(manager).print(" has %d access\n", access); + cavein_possible = config.riskaverse; + cavein_candidates.emplace(pos, access); + least_access = min(access, least_access); + } + } else if (config.insta_dig && isEntombed(miner_pos, pos)) { + manage_one(pos, true, false); + dig_now(DFHack::Core::getInstance().getConsole(), pos); + mark_done(pos); + } + } + DEBUG(manager).print("cavein possible(%d)\n" + "%zu candidates\n" + "least access %d\n", cavein_possible, cavein_candidates.size(), least_access); + } + // managing designations + if (!group.empty()) { + DEBUG(manager).print("managing group #%d\n", groups.debugGIndex(*group.begin())); + } + for (auto &pos: group) { + // if no cave-in is possible [or we don't check for], we'll just execute normally and move on + if (!cavein_possible) { + TRACE(manager).print("cave-in evaluated false\n"); + assert(manage_one(pos, true, marker_mode)); + continue; + } + // cavein is only possible if marker_mode is false + const static uint8_t MAX = 84; //arbitrary number that indicates the value has changed + if (CSP::dignow_queue.count(pos) || (cavein_candidates.count(pos) && + least_access < MAX && cavein_candidates[pos] <= least_access+2)) { + + TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+2); + // we want to dig the cavein candidates first + df::coord local(pos); + local.x %= 16; + local.y %= 16; + auto block = Maps::ensureTileBlock(pos); + for (df::block_square_event* event: block->block_events) { + if (auto evT = virtual_cast(event)) { + // we want to let the user keep some designations free of being managed + auto b = max(0, cavein_candidates[pos] - least_access); + auto v = 1000 + (b * 1700); + DEBUG(manager).print("(" COORD ") 1000+1000(%d) -> %d {least-access: %d}\n",COORDARGS(pos), b, v, least_access); + evT->priority[Coord(local)] = v; + } + } + assert(manage_one(pos, true, false)); + continue; + } + if (cavein_candidates.count(pos)) { + DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access + 2); + } else { + DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n"); + } + assert(manage_one(pos, true, true)); } INFO(manager).print("manage_group() is done\n"); } From ae8291b9525898ab1acaccfdd6e2c9295053a1d8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 8 Dec 2022 14:59:09 -0800 Subject: [PATCH 0274/2222] Implements plugin: channel-safely v1.2.1 - Removes insta-dig feature (too many problems, rarely worked) - Fixes a segmentation fault introduced in v1.2 - Improves manage_group readability/nesting - Improves manage_one readability/nesting Update docs/plugins/channel-safely.rst Co-authored-by: Myk Fixes some formatting consistency --- docs/plugins/channel-safely.rst | 12 +-- plugins/channel-safely/channel-groups.cpp | 12 +-- plugins/channel-safely/channel-manager.cpp | 68 ++++++--------- .../channel-safely/channel-safely-plugin.cpp | 22 +++-- .../channel-safely/include/channel-groups.h | 4 +- plugins/channel-safely/include/channel-jobs.h | 2 - .../channel-safely/include/channel-manager.h | 4 +- plugins/channel-safely/include/inlines.h | 85 +++++++++++++------ 8 files changed, 113 insertions(+), 96 deletions(-) diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index 1a2866920..3acbe66cd 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -63,8 +63,6 @@ Features :monitoring: Toggle whether to monitor the conditions of active digs. (default: disabled) :resurrect: Toggle whether to resurrect units involved in cave-ins, and if monitor is enabled units who die while digging. (default: disabled) -:insta-dig: Toggle whether to use insta-digging on unreachable designations. - Runs on the refresh cycles. Use with caution. (default: disabled) Settings -------- @@ -80,10 +78,8 @@ Troubleshooting --------------- If designations aren't switching correctly, try putting the designations into marker mode. -Then press . (next) or resume. If you're debugging code you'll want these: +Then press . (next) or resume. If you're debugging code you'll want these:: -[todo: make this a block of code] - -:filter1: ``debugfilter set Info channel manager`` -:filter2: ``debugfilter set Debug channel plugin`` -:filter3: ``debugfilter set Trace channel group`` + debugfilter set Info channel manager + debugfilter set Debug channel plugin + debugfilter set Trace channel group diff --git a/plugins/channel-safely/channel-groups.cpp b/plugins/channel-safely/channel-groups.cpp index 6c310b5c4..7ee6c5b44 100644 --- a/plugins/channel-safely/channel-groups.cpp +++ b/plugins/channel-safely/channel-groups.cpp @@ -46,14 +46,14 @@ bool ChannelJobs::has_cavein_conditions(const df::coord &map_pos) { } bool ChannelJobs::possible_cavein(const df::coord &job_pos) { - for (auto iter = active.begin(); iter != active.end(); ++iter) { - if (*iter == job_pos) continue; - if (calc_distance(job_pos, *iter) <= 2) { + for (auto iter : active) { + if (iter == job_pos) continue; + if (calc_distance(job_pos, iter) <= 2) { // find neighbours df::coord n1[8]; df::coord n2[8]; get_neighbours(job_pos, n1); - get_neighbours(*iter, n2); + get_neighbours(iter, n2); // find shared neighbours for (int i = 0; i < 7; ++i) { for (int j = i + 1; j < 8; ++j) { @@ -133,7 +133,7 @@ void ChannelGroups::add(const df::coord &map_pos) { TRACE(groups).print(" -> brand new group\n"); // we create a brand-new group to use group_index = groups.size(); - groups.push_back(Group()); + groups.emplace_back(); group = &groups[group_index]; } } @@ -220,7 +220,7 @@ void ChannelGroups::scan(bool full_scan) { jobs.erase(map_pos); } block->designation[lx][ly].bits.dig = df::tile_dig_designation::No; - } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked ) { + } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked) { if (!is_channel_designation(block->designation[lx][ly])) { if (df::map_block* block_above = Maps::getBlock(bx, by, z+1)) { if (!is_channel_designation(block_above->designation[lx][ly])) { diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index df3bcad71..b6b19a0ef 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -52,7 +52,7 @@ void ChannelManager::manage_group(const df::coord &map_pos, bool set_marker_mode INFO(manager).print("manage_group() is done\n"); } -void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool marker_mode) { +void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool marker_mode) const { INFO(manager).print("manage_group()\n"); if (!set_marker_mode) { marker_mode = has_any_groups_above(groups, group); @@ -88,7 +88,6 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool if (config.insta_dig) { manage_one(pos, true, false); dig_now(DFHack::Core::getInstance().getConsole(), pos); - mark_done(pos); } else { // todo: engage dig management, swap channel designations for dig } @@ -101,12 +100,11 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool } else if (config.insta_dig && isEntombed(miner_pos, pos)) { manage_one(pos, true, false); dig_now(DFHack::Core::getInstance().getConsole(), pos); - mark_done(pos); } } DEBUG(manager).print("cavein possible(%d)\n" - "%zu candidates\n" - "least access %d\n", cavein_possible, cavein_candidates.size(), least_access); + "%zu candidates\n" + "least access %d\n", cavein_possible, cavein_candidates.size(), least_access); } // managing designations if (!group.empty()) { @@ -120,16 +118,18 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool continue; } // cavein is only possible if marker_mode is false + // we want to dig the cavein candidates first, the least accessible ones specifically const static uint8_t MAX = 84; //arbitrary number that indicates the value has changed + const static uint8_t OFFSET = 2; //value has been tweaked to avoid cave-ins whilst activating as many designations as possible if (CSP::dignow_queue.count(pos) || (cavein_candidates.count(pos) && - least_access < MAX && cavein_candidates[pos] <= least_access+2)) { + least_access < MAX && cavein_candidates[pos] <= least_access+OFFSET)) { - TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+2); - // we want to dig the cavein candidates first + TRACE(manager).print("cave-in evaluated true and either of dignow or (%d <= %d)\n", cavein_candidates[pos], least_access+OFFSET); df::coord local(pos); local.x %= 16; local.y %= 16; auto block = Maps::ensureTileBlock(pos); + // if we don't find the priority in block_events, it probably means bad things for (df::block_square_event* event: block->block_events) { if (auto evT = virtual_cast(event)) { // we want to let the user keep some designations free of being managed @@ -142,8 +142,9 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool assert(manage_one(pos, true, false)); continue; } + // cavein possible, but we failed to meet the criteria for activation if (cavein_candidates.count(pos)) { - DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access + 2); + DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access+OFFSET); } else { DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n"); } @@ -152,7 +153,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool INFO(manager).print("manage_group() is done\n"); } -bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, bool marker_mode) { +bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, bool marker_mode) const { if (Maps::isValidTilePos(map_pos)) { TRACE(manager).print("manage_one((" COORD "), %d, %d)\n", COORDARGS(map_pos), set_marker_mode, marker_mode); df::map_block* block = Maps::getTileBlock(map_pos); @@ -164,41 +165,22 @@ bool ChannelManager::manage_one(const df::coord &map_pos, bool set_marker_mode, // ensure that we aren't on the top-most layers if (map_pos.z < mapz - 3) { // do we already know whether to set marker mode? - if (set_marker_mode) { - TRACE(manager).print(" -> setting marker mode\n"); - // if enabling marker mode, just do it - if (marker_mode) { - goto markerMode; - } - // otherwise we check for some safety - if (!is_channel_designation(block->designation[Coord(local)]) || is_safe_to_dig_down(map_pos)) { - // not a channel designation, or it is safe to dig down if it is - if (!block->flags.bits.designated) { - block->flags.bits.designated = true; - } - // we need to cache the tile now that we're activating the designation - TileCache::Get().cache(map_pos, block->tiletype[Coord(local)]); - TRACE(manager).print("marker mode DISABLED\n"); - tile_occupancy.bits.dig_marked = false; - } else { - // we want to activate the designation, that's not what we get - goto markerMode; - } - } else { - // not set_marker_mode - TRACE(manager).print(" if(has_groups_above())\n"); - // check that the group has no incomplete groups directly above it - if (has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos)) { - - markerMode: - - TRACE(manager).print("marker mode ENABLED\n"); - tile_occupancy.bits.dig_marked = true; - if (jobs.count(map_pos)) { - cancel_job(map_pos); - } + if (!marker_mode) { + // marker_mode is set to true if it is unsafe to dig + marker_mode = (!set_marker_mode && + (has_group_above(groups, map_pos) || !is_safe_to_dig_down(map_pos))) || + (set_marker_mode && + is_channel_designation(block->designation[Coord(local)]) && !is_safe_to_dig_down(map_pos)); + } + if (marker_mode) { + if (jobs.count(map_pos)) { + cancel_job(map_pos); } + } else if (!block->flags.bits.designated) { + block->flags.bits.designated = true; } + tile_occupancy.bits.dig_marked = marker_mode; + TRACE(manager).print("marker mode %s\n", marker_mode ? "ENABLED" : "DISABLED"); } else { // if we are though, it should be totally safe to dig tile_occupancy.bits.dig_marked = false; diff --git a/plugins/channel-safely/channel-safely-plugin.cpp b/plugins/channel-safely/channel-safely-plugin.cpp index 882685385..73a1fea19 100644 --- a/plugins/channel-safely/channel-safely-plugin.cpp +++ b/plugins/channel-safely/channel-safely-plugin.cpp @@ -1,7 +1,10 @@ /* Prevent channeling down into known open space. Author: Josh Cooper Created: Aug. 4 2020 -Updated: Dec. 5 2022 +Updated: Dec. 8 2022 +*/ +/* +This skeletal logic has not been kept up-to-date since ~v0.5 Enable plugin: -> build groups @@ -160,7 +163,7 @@ namespace CSP { try { pfeature.ival(MONITOR) = config.monitoring; pfeature.ival(VISION) = config.require_vision; - pfeature.ival(INSTADIG) = config.insta_dig; + pfeature.ival(INSTADIG) = false; //config.insta_dig; pfeature.ival(RESURRECT) = config.resurrect; pfeature.ival(RISKAVERSE) = config.riskaverse; @@ -186,7 +189,7 @@ namespace CSP { try { config.monitoring = pfeature.ival(MONITOR); config.require_vision = pfeature.ival(VISION); - config.insta_dig = pfeature.ival(INSTADIG); + config.insta_dig = false; //pfeature.ival(INSTADIG); config.resurrect = pfeature.ival(RESURRECT); config.riskaverse = pfeature.ival(RISKAVERSE); @@ -303,9 +306,9 @@ namespace CSP { switch (report->type) { case announcement_type::CANCEL_JOB: if (config.insta_dig) { - if (report->text.find("cancels Dig") != std::string::npos) { - dignow_queue.emplace(report->pos); - } else if (report->text.find("path") != std::string::npos) { + if (report->text.find("cancels Dig") != std::string::npos || + report->text.find("path") != std::string::npos) { + dignow_queue.emplace(report->pos); } DEBUG(plugin).print("%d, pos: " COORD ", pos2: " COORD "\n%s\n", report_id, COORDARGS(report->pos), @@ -439,7 +442,7 @@ namespace CSP { Maps::getTileOccupancy(job->pos)->bits.dig_marked = true; // prevent algorithm from re-enabling designation - for (auto &be: Maps::getBlock(job->pos)->block_events) { ; + for (auto &be: Maps::getBlock(job->pos)->block_events) { if (auto bsedp = virtual_cast( be)) { df::coord local(job->pos); @@ -603,7 +606,8 @@ command_result channel_safely(color_ostream &out, std::vector ¶ } else if (parameters[1] == "require-vision") { config.require_vision = state; } else if (parameters[1] == "insta-dig") { - config.insta_dig = state; + //config.insta_dig = state; + config.insta_dig = false; } else if (parameters[1] == "resurrect") { if (state != config.resurrect) { config.resurrect = state; @@ -641,7 +645,7 @@ command_result channel_safely(color_ostream &out, std::vector ¶ out.print(" %-20s\t%s\n", "risk-averse: ", config.riskaverse ? "on." : "off."); out.print(" %-20s\t%s\n", "monitoring: ", config.monitoring ? "on." : "off."); out.print(" %-20s\t%s\n", "require-vision: ", config.require_vision ? "on." : "off."); - out.print(" %-20s\t%s\n", "insta-dig: ", config.insta_dig ? "on." : "off."); + //out.print(" %-20s\t%s\n", "insta-dig: ", config.insta_dig ? "on." : "off."); out.print(" %-20s\t%s\n", "resurrect: ", config.resurrect ? "on." : "off."); out.print(" SETTINGS:\n"); out.print(" %-20s\t%" PRIi32 "\n", "refresh-freq: ", config.refresh_freq); diff --git a/plugins/channel-safely/include/channel-groups.h b/plugins/channel-safely/include/channel-groups.h index 5ec6b15c9..ef3f5d082 100644 --- a/plugins/channel-safely/include/channel-groups.h +++ b/plugins/channel-safely/include/channel-groups.h @@ -37,9 +37,9 @@ private: protected: void add(const df::coord &map_pos); public: - int debugGIndex(const df::coord &map_pos) { + int debugGIndex(const df::coord &map_pos) const { if (groups_map.count(map_pos)) { - return groups_map[map_pos]; + return groups_map.find(map_pos)->second; } return -1; } diff --git a/plugins/channel-safely/include/channel-jobs.h b/plugins/channel-safely/include/channel-jobs.h index 13f0d0e77..2e6c6a567 100644 --- a/plugins/channel-safely/include/channel-jobs.h +++ b/plugins/channel-safely/include/channel-jobs.h @@ -21,8 +21,6 @@ using namespace DFHack; */ class ChannelJobs { private: - friend class ChannelGroup; - using Jobs = std::unordered_set; // job* will exist until it is complete, and likely beyond std::unordered_map jobs; Jobs locations; diff --git a/plugins/channel-safely/include/channel-manager.h b/plugins/channel-safely/include/channel-manager.h index 4df02ed38..582916e06 100644 --- a/plugins/channel-safely/include/channel-manager.h +++ b/plugins/channel-safely/include/channel-manager.h @@ -27,8 +27,8 @@ public: void destroy_groups() { groups.clear(); debug(); } void manage_groups(); void manage_group(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); - void manage_group(const Group &group, bool set_marker_mode = false, bool marker_mode = false); - bool manage_one(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false); + void manage_group(const Group &group, bool set_marker_mode = false, bool marker_mode = false) const; + bool manage_one(const df::coord &map_pos, bool set_marker_mode = false, bool marker_mode = false) const; void mark_done(const df::coord &map_pos); bool exists(const df::coord &map_pos) const { return groups.count(map_pos); } void debug() { diff --git a/plugins/channel-safely/include/inlines.h b/plugins/channel-safely/include/inlines.h index 38e73814c..172275778 100644 --- a/plugins/channel-safely/include/inlines.h +++ b/plugins/channel-safely/include/inlines.h @@ -10,10 +10,11 @@ #include #include +#include -#define Coord(id) id.x][id.y +#define Coord(id) (id).x][(id).y #define COORD "%" PRIi16 ",%" PRIi16 ",%" PRIi16 -#define COORDARGS(id) id.x, id.y, id.z +#define COORDARGS(id) (id).x, (id).y, (id).z namespace CSP { extern std::unordered_set dignow_queue; @@ -81,12 +82,9 @@ inline bool isEntombed(const df::coord &unit_pos, const df::coord &map_pos) { } df::coord neighbours[8]; get_neighbours(map_pos, neighbours); - for (auto n: neighbours) { - if (Maps::canWalkBetween(unit_pos, n)) { - return false; - } - } - return true; + return std::all_of(neighbours+0, neighbours+8, [&unit_pos](df::coord n) { + return !Maps::canWalkBetween(unit_pos, n); + }); } inline bool is_dig_job(const df::job* job) { @@ -145,22 +143,6 @@ inline bool is_safe_to_dig_down(const df::coord &map_pos) { return false; } -inline bool can_reach_designation(const df::coord &start, const df::coord &end) { - if (start != end) { - if (!Maps::canWalkBetween(start, end)) { - df::coord neighbours[8]; - get_neighbours(end, neighbours); - for (auto &pos: neighbours) { - if (Maps::isValidTilePos(pos) && Maps::canWalkBetween(start, pos)) { - return true; - } - } - return false; - } - } - return true; -} - inline bool has_unit(const df::tile_occupancy* occupancy) { return occupancy->bits.unit || occupancy->bits.unit_grounded; } @@ -235,8 +217,62 @@ inline void cancel_job(const df::coord &map_pos) { // executes dig designations for the specified tile coordinates inline bool dig_now(color_ostream &out, const df::coord &map_pos) { + static std::default_random_engine rng; + std::uniform_int_distribution<> dist(0,5); out.color(color_value::COLOR_YELLOW); out.print("channel-safely: insta-dig: digging (" COORD ")<\n", COORDARGS(map_pos)); + + df::coord below(map_pos); + below.z--; + auto ttype_below = *Maps::getTileType(below); + if (isOpenTerrain(ttype_below) || isFloorTerrain(ttype_below)) { + *Maps::getTileType(map_pos) = tiletype::OpenSpace; + } else { + auto ttype_p = Maps::getTileType(map_pos); + if (isSoilMaterial(*ttype_p)) { + switch(dist(rng)) { + case 0: + *ttype_p = tiletype::SoilFloor1; + break; + case 1: + *ttype_p = tiletype::SoilFloor2; + break; + case 2: + *ttype_p = tiletype::SoilFloor3; + break; + case 3: + *ttype_p = tiletype::SoilFloor4; + break; + default: + *ttype_p = tiletype::SoilFloor1; + break; + } + } else if (isStoneMaterial(*ttype_p)) { + switch(dist(rng)) { + case 0: + *ttype_p = tiletype::FeatureFloor1; + break; + case 1: + *ttype_p = tiletype::FeatureFloor2; + break; + case 2: + *ttype_p = tiletype::FeatureFloor3; + break; + case 3: + *ttype_p = tiletype::FeatureFloor4; + break; + default: + *ttype_p = tiletype::MineralFloor1; + break; + } + } else { + out.print("Unknown type\n"); + return false; + } + } + + return true; + /* bool ret = false; lua_State* state = Lua::Core::State; @@ -253,6 +289,7 @@ inline bool dig_now(color_ostream &out, const df::coord &map_pos) { Lua::StackUnwinder top(state); Lua::CallLuaModuleFunction(out, state, module_name, fn_name, 1, 1, args_lambda, res_lambda); return ret; + */ } // fully heals the unit specified, resurrecting if need be From 89eefd006f8e255b4d6127fc0de7870dca05cb3f Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 12 Dec 2022 10:45:57 -0800 Subject: [PATCH 0275/2222] Implements plugin: channel-safely v1.2.2 minor changes only The largest change is moving a df::map_block* declaration to an outer loop --- plugins/channel-safely/channel-groups.cpp | 16 ++++++++-------- plugins/channel-safely/channel-manager.cpp | 3 --- plugins/channel-safely/channel-safely-plugin.cpp | 6 ------ 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/plugins/channel-safely/channel-groups.cpp b/plugins/channel-safely/channel-groups.cpp index 7ee6c5b44..5338eb6ed 100644 --- a/plugins/channel-safely/channel-groups.cpp +++ b/plugins/channel-safely/channel-groups.cpp @@ -207,6 +207,7 @@ void ChannelGroups::scan(bool full_scan) { if (!full_scan && !block->flags.bits.designated) { continue; } + df::map_block* block_above = Maps::getBlock(bx, by, z+1); // foreach tile bool empty_group = true; for (int16_t lx = 0; lx < 16; ++lx) { @@ -220,14 +221,13 @@ void ChannelGroups::scan(bool full_scan) { jobs.erase(map_pos); } block->designation[lx][ly].bits.dig = df::tile_dig_designation::No; - } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked) { - if (!is_channel_designation(block->designation[lx][ly])) { - if (df::map_block* block_above = Maps::getBlock(bx, by, z+1)) { - if (!is_channel_designation(block_above->designation[lx][ly])) { - // dig designation without a channel above it, we can skip this. - continue; - } - } + } else if (is_dig_designation(block->designation[lx][ly]) || block->occupancy[lx][ly].bits.dig_marked ) { + // We have a dig designated, or marked. Some of these will not need intervention. + if (block_above && + !is_channel_designation(block->designation[lx][ly]) && + !is_channel_designation(block_above->designation[lx][ly])) { + // if this tile isn't a channel designation, and doesn't have a channel designation above it.. we can skip it + continue; } for (df::block_square_event* event: block->block_events) { if (auto evT = virtual_cast(event)) { diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index b6b19a0ef..c22bd8a8e 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -5,9 +5,6 @@ #include //hash function for df::coord #include -#include -#include - df::unit* find_dwarf(const df::coord &map_pos) { df::unit* nearest = nullptr; uint32_t distance; diff --git a/plugins/channel-safely/channel-safely-plugin.cpp b/plugins/channel-safely/channel-safely-plugin.cpp index 73a1fea19..adb668468 100644 --- a/plugins/channel-safely/channel-safely-plugin.cpp +++ b/plugins/channel-safely/channel-safely-plugin.cpp @@ -416,12 +416,6 @@ namespace CSP { if (!config.monitoring) continue; TRACE(monitor).print(" -> compare positions of worker and job\n"); - // save position - if (unit->pos != job->pos && isFloorTerrain(*Maps::getTileType(unit->pos))) { - // worker is probably safe right now - continue; - } - // check for fall safety if (unit->pos == job->pos && !is_safe_fall(job->pos)) { // unsafe From eab08f64303078aa18c6d64c790428da4d733800 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 14:51:48 -0800 Subject: [PATCH 0276/2222] update tag for automelt --- docs/plugins/automelt.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/automelt.rst b/docs/plugins/automelt.rst index 6b14d9dc4..f652e52b8 100644 --- a/docs/plugins/automelt.rst +++ b/docs/plugins/automelt.rst @@ -3,7 +3,7 @@ automelt .. dfhack-tool:: :summary: Quickly designate items to be melted. - :tags: untested fort productivity items stockpiles + :tags: fort productivity items stockpiles :no-command: Automelt checks monitor-enabled stockpiles once every in-game day, and will mark valid items for melting. From 21ebbad66935972d8475d15cf983fbe4f27005c2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 15:40:56 -0800 Subject: [PATCH 0277/2222] add keybinding for gui/quickcmd --- data/init/dfhack.keybindings.init | 3 +++ docs/changelog.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index ea38f35ba..c1a4191ef 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -19,6 +19,9 @@ keybinding add Ctrl-Shift-C hotkeys # on-screen keyboard keybinding add Ctrl-Shift-K gui/cp437-table +# customizable quick command list +keybinding add Ctrl-Shift-A gui/quickcmd + # an in-game init file editor #keybinding add Alt-S@title|dwarfmode/Default|dungeonmode gui/settings-manager diff --git a/docs/changelog.txt b/docs/changelog.txt index 073837eff..405a2c3bb 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `gui/launcher`: sped up initialization time for faster load of the UI - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open +- `gui/quickcmd`: now has it's own global keybinding for your convenience: Ctrl-Shift-A ## Documentation From 5ad6ce16e8f3e26fd2eef5cb45ee3d7b46278ed2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 16:00:10 -0800 Subject: [PATCH 0278/2222] don't change definitions of deprecated frame styles --- library/lua/gui.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 4b6bf6517..524978baf 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -839,8 +839,6 @@ THIN_FRAME = make_frame('Thin', false) -- for compatibility with pre-steam code GREY_LINE_FRAME = WINDOW_FRAME -BOUNDARY_FRAME = PANEL_FRAME -GREY_FRAME = MEDIUM_FRAME function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) local pen = style.frame_pen From 8b98ba5042f7ee33d92ad8f2b284c5e771a6820f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 17:40:16 -0800 Subject: [PATCH 0279/2222] allow windows to be defocused instead of pinned --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 108 +++++++++++++++++++++------------ library/lua/gui.lua | 117 ++++++++++++++++++++++-------------- library/lua/gui/widgets.lua | 13 ++-- plugins/lua/hotkeys.lua | 1 + 5 files changed, 146 insertions(+), 94 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 073837eff..daba5b791 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `gui/launcher`: sped up initialization time for faster load of the UI - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open +- Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 63b533d61..a832639e6 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4117,32 +4117,28 @@ It adds the following methods: ZScreen class ------------- -A screen subclass that allows the underlying viewscreens to be interacted with. -For example, a DFHack GUI tool implemented as a ZScreen can allow the player to -interact with the underlying map. That is, even when the DFHack tool window is -visible, players will be able to use vanilla designation tools, select units, or -scan/drag the map around. - -If multiple ZScreens are on the stack and the player clicks on a visible element -of a non-top ZScreen, that ZScreen will be raised to the top of the viewscreen -stack. This allows multiple DFHack gui tools to be usable at the same time. -Clicks that are not over any visible ZScreen element, of course, are passed -through to the underlying viewscreen. - -If :kbd:`Esc` or the right mouse button is pressed, and the ZScreen widgets -don't otherwise handle them, then the top ZScreen is dismissed. If the ZScreen -is "pinned", then the screen is not dismissed and the input is passed on to the -underlying DF viewscreen. :kbd:`Alt`:kbd:`L` toggles the pinned status if the -ZScreen widgets don't otherwise handle that key sequence. If you have a -``Panel`` with the ``pinnable`` attribute set and a frame that has pens defined -for the pin icon (like ``Window`` widgets have by default), then a pin icon -will appear in the upper right corner of the frame. Clicking on this icon will -toggle the ZScreen ``pinned`` status just as if :kbd:`Alt`:kbd:`L` had been -pressed. - -Keyboard input goes to the top ZScreen, as usual. If the subviews of the top -ZScreen don't handle the input (i.e. they all return something falsey), the -input is passed directly to the first underlying non-ZScreen. +A screen subclass that allows multi-layer interactivity. For example, a DFHack +GUI tool implemented as a ZScreen can allow the player to interact with the +underlying map, or even other DFHack ZScreen windows! That is, even when the +DFHack tool window is visible, players will be able to use vanilla designation +tools, select units, and scan/drag the map around. + +At most one ZScreen can have keyboard focus at a time. That ZScreen's widgets +will have a chance to handle the input before anything else. If unhandled, the +input skips all unfocused ZScreens under that ZScreen and is passed directly to +the first non-ZScreen viewscreen. There are class attributes that can be set to +control what kind of unhandled input is passed to the lower layers. + +If multiple ZScreens are visible and the player left or right clicks on a +visible element of a non-focused ZScreen, that ZScreen will be given focus. This +allows multiple DFHack GUI tools to be usable at the same time. If the mouse is +clicked away from the ZScreen widgets, that ZScreen loses focus. If no ZScreen +has focus, all input is passed directly through to the first underlying +non-ZScreen viewscreen. + +For a ZScreen with keyboard focus, if :kbd:`Esc` or the right mouse button is +pressed, and the ZScreen widgets don't otherwise handle them, then the ZScreen +is dismissed. All this behavior is implemented in ``ZScreen:onInput()``, which subclasses **must not override**. Instead, ZScreen subclasses should delegate all input @@ -4152,24 +4148,23 @@ level input processor. When rendering, the parent viewscreen is automatically rendered first, so subclasses do not have to call ``self:renderParent()``. Calls to ``logic()`` (a world "tick" when playing the game) are also passed through, so the game -progresses normally and can be paused/unpaused as normal by the player. -ZScreens that handle the :kbd:`Space` key may want to provide an alternate way -to pause. Note that passing ``logic()`` calls through to the underlying map is -required for allowing the player to drag the map with the mouse. +progresses normally and can be paused/unpaused as normal by the player. Note +that passing ``logic()`` calls through to the underlying map is required for +allowing the player to drag the map with the mouse. ZScreen subclasses can set +attributes that control whether the game is paused when the ZScreen is shown and +whether the game is forced to continue being paused while the ZScreen is shown. +If pausing is forced, child ``Window`` widgets will show a force-pause icon to +indicate which tool is forcing the pausing. ZScreen provides the following functions: * ``zscreen:raise()`` - Raises the ZScreen to the top of the viewscreen stack and returns a reference - to ``self``. A common pattern is to check if a tool dialog is already active - when the tool command is run and raise the existing dialog if it exists or - show a new dialog if it doesn't. See the sample code below for an example. - -* ``zscreen:togglePinned()`` - - Toggles whether the window closes on :kbd:`ESC` or r-click (unpinned) or not - (pinned). + Raises the ZScreen to the top of the viewscreen stack, gives it keyboard + focus, and returns a reference to ``self``. A common pattern is to check if a + tool dialog is already active when the tool command is run and raise the + existing dialog if it exists or show a new dialog if it doesn't. See the + sample code below for an example. * ``zscreen:isMouseOver()`` @@ -4177,6 +4172,37 @@ ZScreen provides the following functions: subclass and sees if ``getMouseFramePos()`` returns a position for any of them. Subclasses can override this function if that logic is not appropriate. +* ``zscreen:hasFocus()`` + + Whether the ZScreen has keyboard focus. Subclasses will generally not need to + check this because they can assume if they are getting input, then they have + focus. + +ZScreen subclasses can set the following attributes: + +* ``initial_pause`` (default: ``true``) + + Whether to pause the game when the ZScreen is shown. + +* ``force_pause`` (default: ``false``) + + Whether to ensure the game *stays* paused while the ZScreen is shown. + +* ``pass_pause`` (default: ``true``) + + Whether to pass the pause key to the lower viewscreens if it is not handled + by this ZScreen. + +* ``pass_movement_keys`` (default: ``false``) + + Whether to pass the map movement keys to the lower viewscreens if they ar not + handled by this ZScreen. + +* ``pass_mouse_clicks`` (default: ``true``) + + Whether to pass mouse clicks to the lower viewscreens if they are not handled + by this ZScreen. + Here is an example skeleton for a ZScreen tool dialog:: local gui = require('gui') @@ -4187,11 +4213,12 @@ Here is an example skeleton for a ZScreen tool dialog:: frame_title='My Window', frame={w=50, h=45}, resizable=true, -- if resizing makes sense for your dialog + resize_min={w=50, h=20}, -- try to allow users to shrink your windows } function MyWindow:init() self:addviews{ - -- add subviews here + -- add subview widgets here } end @@ -4202,6 +4229,7 @@ Here is an example skeleton for a ZScreen tool dialog:: MyScreen = defclass(MyScreen, gui.ZScreen) MyScreen.ATTRS { focus_path='myscreen', + -- set pause and passthrough attributes as appropriate } function MyScreen:init() diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 524978baf..af9d68870 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -13,16 +13,18 @@ CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0, write_to_lower=true} TRANSPARENT_PEN = to_pen{tile=0, ch=0} KEEP_LOWER_PEN = to_pen{ch=32, fg=0, bg=0, keep_lower=true} -local FAKE_INPUT_KEYS = { +local MOUSE_KEYS = { _MOUSE_L = true, _MOUSE_R = true, _MOUSE_M = true, _MOUSE_L_DOWN = true, _MOUSE_R_DOWN = true, _MOUSE_M_DOWN = true, - _STRING = true, } +local FAKE_INPUT_KEYS = copyall(MOUSE_KEYS) +FAKE_INPUT_KEYS._STRING = true + function simulateInput(screen,...) local keys = {} local function push_key(arg) @@ -692,8 +694,34 @@ end ----------------------------- ZScreen = defclass(ZScreen, Screen) +ZScreen.ATTRS{ + initial_pause=true, + force_pause=false, + pass_pause=true, + pass_movement_keys=false, + pass_mouse_clicks=true, +} + +function ZScreen:init() + self.saved_pause_state = df.global.pause_state + if self.initial_pause then + df.global.pause_state = true + end + self.defocused = false +end + +function ZScreen:onDestroy() + if self.force_pause or self.initial_pause then + -- never go from unpaused to paused, just from paused to unpaused + df.global.pause_state = df.global.pause_state or self.saved_pause_state + end +end +-- this is necessary for middle-click map scrolling to function function ZScreen:onIdle() + if self.force_pause then + df.global.pause_state = true + end if self._native and self._native.parent then self._native.parent:logic() end @@ -704,17 +732,15 @@ function ZScreen:render(dc) ZScreen.super.render(self, dc) end -function ZScreen:isOnTop() - return dfhack.gui.getCurViewscreen(true) == self._native -end - -function ZScreen:togglePinned() - self.pinned = not self.pinned +function ZScreen:hasFocus() + return not self.defocused + and dfhack.gui.getCurViewscreen(true) == self._native end function ZScreen:onInput(keys) - if not self:isOnTop() then - if keys._MOUSE_L_DOWN and self:isMouseOver() then + local has_mouse = self:isMouseOver() + if not self:hasFocus() then + if (keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN) and has_mouse then self:raise() else self:sendInputToParent(keys) @@ -734,30 +760,42 @@ function ZScreen:onInput(keys) return end - if keys.CUSTOM_ALT_L then - self:togglePinned() + if keys._MOUSE_L_DOWN and not has_mouse then + self.defocused = true + self:sendInputToParent(keys) return - end - - if (self:isMouseOver() or not self.pinned) - and (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) then + elseif keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() - -- ensure underlying DF screens don't also react to the click + -- ensure underlying DF screens don't also react to the rclick df.global.enabler.mouse_rbut_down = 0 df.global.enabler.mouse_rbut = 0 return - end - - if not keys._MOUSE_L or not self:isMouseOver() then - self:sendInputToParent(keys) + else + local passit = self.pass_pause and keys.D_PAUSE + if not passit and self.pass_mouse_clicks then + for key in pairs(MOUSE_KEYS) do + if keys[key] then + passit = true + break + end + end + end + if not passit and self.pass_movement_keys then + passit = require('gui.dwarfmode').getMapKey(keys) + end + if passit then + self:sendInputToParent(keys) + end + return end end function ZScreen:raise() - if self:isDismissed() or self:isOnTop() then + if self:isDismissed() or self:hasFocus() then return self end dscreen.raise(self) + self.defocused = false return self end @@ -809,8 +847,7 @@ local BASE_FRAME = { title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, inactive_title_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, - pinned_pen = to_pen{tile=779, ch=216, fg=COLOR_GREY, bg=COLOR_GREEN}, - unpinned_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, + paused_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, } local function make_frame(name, double_line) @@ -840,7 +877,7 @@ THIN_FRAME = make_frame('Thin', false) -- for compatibility with pre-steam code GREY_LINE_FRAME = WINDOW_FRAME -function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) +function paint_frame(dc,rect,style,title,inactive, pause_forced) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 dscreen.paintTile(style.lt_frame_pen or pen, x1, y1) @@ -865,27 +902,15 @@ function paint_frame(dc,rect,style,title,show_pin,pinned,inactive) x, y1, tstr) end - if show_pin then - if pinned and style.pinned_pen then - local pin_texpos = dfhack.textures.getGreenPinTexposStart() - if pin_texpos == -1 then - dscreen.paintTile(style.pinned_pen, x2-1, y1) - else - dscreen.paintTile(style.pinned_pen, x2-2, y1-1, nil, pin_texpos+0) - dscreen.paintTile(style.pinned_pen, x2-1, y1-1, nil, pin_texpos+1) - dscreen.paintTile(style.pinned_pen, x2-2, y1, nil, pin_texpos+2) - dscreen.paintTile(style.pinned_pen, x2-1, y1, nil, pin_texpos+3) - end - elseif not pinned and style.unpinned_pen then - local pin_texpos = dfhack.textures.getRedPinTexposStart() - if pin_texpos == -1 then - dscreen.paintTile(style.unpinned_pen, x2-1, y1) - else - dscreen.paintTile(style.unpinned_pen, x2-2, y1-1, nil, pin_texpos+0) - dscreen.paintTile(style.unpinned_pen, x2-1, y1-1, nil, pin_texpos+1) - dscreen.paintTile(style.unpinned_pen, x2-2, y1, nil, pin_texpos+2) - dscreen.paintTile(style.unpinned_pen, x2-1, y1, nil, pin_texpos+3) - end + if pause_forced then + local pause_texpos = dfhack.textures.getRedPinTexposStart() + if pause_texpos == -1 then + dscreen.paintTile(style.paused_pen, x2-1, y1) + else + dscreen.paintTile(style.paused_pen, x2-2, y1-1, nil, pause_texpos+0) + dscreen.paintTile(style.paused_pen, x2-1, y1-1, nil, pause_texpos+1) + dscreen.paintTile(style.paused_pen, x2-2, y1, nil, pause_texpos+2) + dscreen.paintTile(style.paused_pen, x2-1, y1, nil, pause_texpos+3) end end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 4538c39df..21f79c0ef 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -505,14 +505,11 @@ end function Panel:onRenderFrame(dc, rect) Panel.super.onRenderFrame(self, dc, rect) if not self.frame_style then return end - local pinned = nil - if self.pinnable then - pinned = self.parent_view and self.parent_view.pinned - end - local inactive = self.parent_view and self.parent_view.isOnTop - and not self.parent_view:isOnTop() - gui.paint_frame(dc, rect, self.frame_style, self.frame_title, - self.pinnable, pinned, inactive) + local inactive = self.parent_view and self.parent_view.hasFocus + and not self.parent_view:hasFocus() + local pause_forced = self.parent_view and self.parent_view.force_pause + gui.paint_frame(dc, rect, self.frame_style, self.frame_title, inactive, + pause_forced) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 99ba08a05..c377666c2 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -297,6 +297,7 @@ end MenuScreen = defclass(MenuScreen, gui.ZScreen) MenuScreen.ATTRS { focus_path='hotkeys/menu', + initial_pause=false, hotspot=DEFAULT_NIL, } From 4377065081a91d344ab49c91ca3ce243c7eb9e19 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 17:46:03 -0800 Subject: [PATCH 0280/2222] move pause restore to dismiss and fix the condition --- library/lua/gui.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index af9d68870..a1671a230 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -710,10 +710,11 @@ function ZScreen:init() self.defocused = false end -function ZScreen:onDestroy() +function ZScreen:dismiss() + ZScreen.super.dismiss(self) if self.force_pause or self.initial_pause then -- never go from unpaused to paused, just from paused to unpaused - df.global.pause_state = df.global.pause_state or self.saved_pause_state + df.global.pause_state = df.global.pause_state and self.saved_pause_state end end From 0905943ecf0a4a10c49381b56d530873b9de6018 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 18:05:03 -0800 Subject: [PATCH 0281/2222] don't unfocus if mouse is not being passed through --- library/lua/gui.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index a1671a230..da26692ba 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -761,7 +761,7 @@ function ZScreen:onInput(keys) return end - if keys._MOUSE_L_DOWN and not has_mouse then + if self.pass_mouse_clicks and keys._MOUSE_L_DOWN and not has_mouse then self.defocused = true self:sendInputToParent(keys) return From 1c2f8eccbf22a67220bc1d3e9ff904f7d26d8119 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 19:02:26 -0800 Subject: [PATCH 0282/2222] paint pause symbol for force-pause windows --- library/lua/gui.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index da26692ba..2274c8794 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -904,14 +904,18 @@ function paint_frame(dc,rect,style,title,inactive, pause_forced) end if pause_forced then - local pause_texpos = dfhack.textures.getRedPinTexposStart() - if pause_texpos == -1 then + -- get the tiles for the activated pause symbol + local pause_texpos_ul = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 18, 28) + local pause_texpos_ur = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 19, 28) + local pause_texpos_ll = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 18, 29) + local pause_texpos_lr = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 19, 29) + if not pause_texpos_ul then dscreen.paintTile(style.paused_pen, x2-1, y1) else - dscreen.paintTile(style.paused_pen, x2-2, y1-1, nil, pause_texpos+0) - dscreen.paintTile(style.paused_pen, x2-1, y1-1, nil, pause_texpos+1) - dscreen.paintTile(style.paused_pen, x2-2, y1, nil, pause_texpos+2) - dscreen.paintTile(style.paused_pen, x2-1, y1, nil, pause_texpos+3) + dscreen.paintTile(style.paused_pen, x2-2, y1-1, nil, pause_texpos_ul) + dscreen.paintTile(style.paused_pen, x2-1, y1-1, nil, pause_texpos_ur) + dscreen.paintTile(style.paused_pen, x2-2, y1, nil, pause_texpos_ll) + dscreen.paintTile(style.paused_pen, x2-1, y1, nil, pause_texpos_lr) end end end From 1fa71c0d9237ba177e1adc8a12d0050a29974a1e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 19:24:37 -0800 Subject: [PATCH 0283/2222] update quickstart guide with new ZScreen semantics --- docs/Quickstart.rst | 57 +++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index f6f8920c2..81415e3a9 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -99,37 +99,33 @@ How do DFHack in-game windows work? Many DFHack tools have graphical interfaces that appear in-game. You can tell which windows belong to DFHack tools because they will have the word "DFHack" -printed across their bottom frame edge. DFHack provides a custom windowing system -that gives the player a lot of control over where the windows appear and whether -they capture keyboard and mouse input. - -The DFHack windowing system allows you to use DFHack tools without interrupting -the game. That is, if the game is unpaused, it will continue to run while a -DFHack window is open. You can also interact with the map, scrolling it with the -keyboard or mouse and selecting units, buildings, and items. Some tools will -capture all keyboard input, such as tools with editable text fields, and some will -force-pause the game if it makes sense to, like `gui/quickfort`, since you cannot -interact with the map normally while trying to apply a blueprint. +printed across their bottom frame edge. DFHack provides an advanced windowing +system that gives the player a lot of control over where the windows appear and +whether they capture keyboard and mouse input. + +The DFHack windowing system allows multiple overlapping windows to be active at +once. The one with the highlighted title bar has focus and will receive anything +you type at the keyboard. Hit Esc or right click to close the window or cancel +the current operation. You can click anywhere on the screen that is not a +DFHack window to unfocus the window and let it just sit in the background. It won't +respond to key presses or mouse clicks until you click on it again to give it +focus. You can right click directly on an unfocused window to close it without +left clicking to activate it first. DFHack windows are draggable from the title bar or from anywhere on the window that doesn't have a mouse-clickable widget on it. Many are resizable as well (if the tool window has components that can reasonably be resized). -DFHack windows close with a right mouse click or keyboard Esc, but if you -want to keep a DFHack tool open while you interact with the game, you can click the -pin in the upper right corner of the DFHack window or hit Alt-L so -that the pin turns green. The DFHack window will then ignore right clicks and -Esc key presses that would otherwise close the window. This is especially -useful for the configuration tool windows for the automation tools. For example, -you can pin the `gui/autochop` window, set it to minimal mode, and let it sit -there monitoring your logging industry as you play, using it as a live status -window. Note that you can still right click *on* the DFHack tool window to close -it, even when it is pinned. - -You can have multiple DFHack tool windows on the screen at the same time. The -one that is receiving keyboard input has a highlighted title bar and will appear -over other windows if dragged across them. Clicking on a DFHack window that is not -currently active will bring it to the foreground and make it the active window. +You can generally use DFHack tools without interrupting the game. That is, if the +game is unpaused, it can continue to run while a DFHack window is open. Many tools +will initially pause the game to let you focus on the task at hand, but you can +unpause like normal if you want. You can also interact with the map, scrolling it +with the keyboard or mouse and selecting units, buildings, and items. Some tools +will capture all keyboard input, such as tools with editable text fields, and some +will force-pause the game if it makes sense to, like `gui/quickfort`, since you +cannot interact with the map normally while trying to apply a blueprint. Windows +for tools that force-pause the game will have a pause icon in their upper right +corner to indicate which tool is responsible for the pausing. Where do I go next? ------------------- @@ -161,10 +157,11 @@ You can enable it from the GUI, so you don't need to run `enable autochop Date: Mon, 23 Jan 2023 19:34:48 -0800 Subject: [PATCH 0284/2222] remove references to pinnable --- docs/dev/Lua API.rst | 7 +------ library/lua/gui/widgets.lua | 16 ++++------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index a832639e6..eb05db0a1 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4392,11 +4392,6 @@ Has attributes: hitting :kbd:`Esc` (while resizing with the mouse or keyboard), or by calling ``Panel:setKeyboardResizeEnabled(false)`` (while resizing with the keyboard). -* ``pinnable = bool`` (default: ``false``) - - Determines whether the panel will draw a pin icon in its frame. See - `ZScreen class`_ for details. - * ``autoarrange_subviews = bool`` (default: ``false``) * ``autoarrange_gap = int`` (default: ``0``) @@ -4458,7 +4453,7 @@ Window class ------------ Subclass of Panel; sets Panel attributes to useful defaults for a top-level -framed, pinnable, draggable window. +framed, draggable window. ResizingPanel class ------------------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 21f79c0ef..ab4dad88e 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -81,7 +81,6 @@ Panel.ATTRS { resize_min = DEFAULT_NIL, on_resize_begin = DEFAULT_NIL, on_resize_end = DEFAULT_NIL, - pinnable = false, autoarrange_subviews = false, -- whether to automatically lay out subviews autoarrange_gap = 0, -- how many blank lines to insert between widgets } @@ -272,21 +271,21 @@ local function Panel_on_double_click(self) Panel_update_frame(self, frame, true) end -local function panel_is_on_pin(self) +local function panel_mouse_is_on_pause_icon(self) local frame_rect = self.frame_rect local x,y = dscreen.getMousePos() return (x == frame_rect.x2-2 or x == frame_rect.x2-1) and (y == frame_rect.y1-1 or y == frame_rect.y1) end -local function panel_is_pinnable(self) - return self.pinnable and self.parent_view and self.parent_view.togglePinned +local function panel_has_pause_icon(self) + return self.parent_view and self.parent_view.force_pause end function Panel:getMouseFramePos() local x,y = Panel.super.getMouseFramePos(self) if x then return x, y end - if panel_is_pinnable(self) and panel_is_on_pin(self) then + if panel_has_pause_icon(self) and panel_mouse_is_on_pause_icon(self) then local frame_rect = self.frame_rect return frame_rect.width - 3, 0 end @@ -320,12 +319,6 @@ function Panel:onInput(keys) end return true end - if panel_is_pinnable(self) and keys._MOUSE_L_DOWN then - if panel_is_on_pin(self) then - self.parent_view:togglePinned() - return true - end - end if Panel.super.onInput(self, keys) then return true end @@ -532,7 +525,6 @@ Window.ATTRS { frame_background = gui.CLEAR_PEN, frame_inset = 1, draggable = true, - pinnable = true, } ------------------- From f640d153558c36e405e1cd3c4a8cf024e2aae22a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 23 Jan 2023 22:24:45 -0600 Subject: [PATCH 0285/2222] tailor: avoid bad key exception in std::map --- plugins/tailor.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 4e750cb3f..64c976424 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -249,7 +249,12 @@ private: for (auto w : worn) { auto ty = w->getType(); - auto o = itemTypeMap.at(ty); + auto oo = itemTypeMap.find(ty); + if (oo == itemTypeMap.end()) + { + continue; + } + const df::job_type o = oo->second; int size = world->raws.creatures.all[w->getMakerRace()]->adultsize; std::string description; @@ -330,9 +335,13 @@ private: } } - const df::job_type j = itemTypeMap.at(ty); - orders[std::make_tuple(j, sub, size)] += count; - DEBUG(cycle).print("tailor: %s times %d of size %d ordered\n", ENUM_KEY_STR(job_type, j).c_str(), count, size); + auto jj = itemTypeMap.find(ty); + if (jj != itemTypeMap.end()) + { + const df::job_type j = jj->second; + orders[std::make_tuple(j, sub, size)] += count; + DEBUG(cycle).print("tailor: %s times %d of size %d ordered\n", ENUM_KEY_STR(job_type, j).c_str(), count, size); + } } } From d221f2b105272e02745dd32ff9e07a2dba4b4ef0 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 24 Jan 2023 07:14:02 +0000 Subject: [PATCH 0286/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index cec17bfaa..39ee60363 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit cec17bfaaa777de62833c818316e33bad75fa8e6 +Subproject commit 39ee60363892b7b916d670b02d46a606e3969b7c From 0e03ac8e5b72a28d4d068b1f4f8b95b6a3fe08d7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 23 Jan 2023 23:50:08 -0800 Subject: [PATCH 0287/2222] don't use tabs in enable output so they display nicely in gui/launcher --- library/Core.cpp | 2 +- library/lua/script-manager.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index bdd38799f..98582d8cd 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -804,7 +804,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v if (!plug->can_be_enabled()) continue; con.print( - "%20s\t%-3s%s\n", + "%21s %-3s%s\n", (plug->getName()+":").c_str(), plug->is_enabled() ? "on" : "off", plug->can_set_enabled() ? "" : " (controlled internally)" diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index 008e9443e..c1b8c80d5 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -49,7 +49,7 @@ function list() -- just been added reload() for name,fn in pairs(enabled_map) do - print(('%20s\t%-3s'):format(name..':', fn() and 'on' or 'off')) + print(('%21s %-3s'):format(name..':', fn() and 'on' or 'off')) end end From cbff07dc378616d2867684627374e8ae6e19841c Mon Sep 17 00:00:00 2001 From: Dmitrii Kurkin Date: Tue, 24 Jan 2023 21:55:22 +0800 Subject: [PATCH 0288/2222] Update orders.rst --- docs/plugins/orders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 5dfe23758..2c5b0255f 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -8,7 +8,7 @@ orders Usage ----- -``orders orders list`` +``orders list`` Shows the list of previously exported orders, including the orders library. ``orders export `` Saves all the current manager orders in a file. From 018715f869666ac6addf7c48d4dd65ba85196a66 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 24 Jan 2023 19:18:16 +0000 Subject: [PATCH 0289/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 65c61135e..ab3d50522 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 65c61135e05a8fe380e8216534615cb96b26c1ed +Subproject commit ab3d50522263d3bd28cbb0757729463a74c4c9c3 diff --git a/scripts b/scripts index 39ee60363..3a158ea39 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 39ee60363892b7b916d670b02d46a606e3969b7c +Subproject commit 3a158ea399e6906f245915e4e8146b56320e82cc From 739263eb59279a293b2496bcfd551fd429fef94f Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 24 Jan 2023 16:25:41 -0800 Subject: [PATCH 0290/2222] Re-introduces channel-safely plugin to the build --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 95b1d9ba8..284d1788e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -93,7 +93,7 @@ dfhack_plugin(buildingplan buildingplan.cpp LINK_LIBRARIES lua) #dfhack_plugin(changeitem changeitem.cpp) dfhack_plugin(changelayer changelayer.cpp) dfhack_plugin(changevein changevein.cpp) -#add_subdirectory(channel-safely) +add_subdirectory(channel-safely) dfhack_plugin(cleanconst cleanconst.cpp) dfhack_plugin(cleaners cleaners.cpp) dfhack_plugin(cleanowned cleanowned.cpp) From cbcd68317da71ac5a3e6bf0d157046e9f1b9f166 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 24 Jan 2023 19:28:17 -0600 Subject: [PATCH 0291/2222] tailor: increased check frequency do bookkeepers work faster now? could be --- plugins/tailor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 64c976424..2cca8a2c8 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -582,7 +582,7 @@ public: static std::unique_ptr tailor_instance; -#define DELTA_TICKS 600 +#define DELTA_TICKS 50 DFhackCExport command_result plugin_onupdate(color_ostream& out) { From 4e35895cf45db09ff4e6ae02b70e3806c989d42f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 24 Jan 2023 18:48:23 -0800 Subject: [PATCH 0292/2222] fix build errors with channel-safely --- library/xml | 2 +- plugins/channel-safely/channel-groups.cpp | 2 +- scripts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index ab3d50522..65c61135e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ab3d50522263d3bd28cbb0757729463a74c4c9c3 +Subproject commit 65c61135e05a8fe380e8216534615cb96b26c1ed diff --git a/plugins/channel-safely/channel-groups.cpp b/plugins/channel-safely/channel-groups.cpp index 5338eb6ed..2650d92d0 100644 --- a/plugins/channel-safely/channel-groups.cpp +++ b/plugins/channel-safely/channel-groups.cpp @@ -188,7 +188,7 @@ void ChannelGroups::scan(bool full_scan) { std::set gone_jobs; set_difference(last_jobs, jobs, gone_jobs); set_difference(jobs, last_jobs, new_jobs); - INFO(groups).print("gone jobs: %" PRIu64 "\nnew jobs: %" PRIu64 "\n",gone_jobs.size(), new_jobs.size()); + INFO(groups).print("gone jobs: %zd\nnew jobs: %zd\n",gone_jobs.size(), new_jobs.size()); for (auto &pos : new_jobs) { add(pos); } diff --git a/scripts b/scripts index 3a158ea39..39ee60363 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3a158ea399e6906f245915e4e8146b56320e82cc +Subproject commit 39ee60363892b7b916d670b02d46a606e3969b7c From a72b26c4f2a6a68a93828ced319ccca31babcc5c Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Tue, 24 Jan 2023 16:17:49 -0800 Subject: [PATCH 0293/2222] GHA: Add win64 cross compile build This also removes -t from docker run, as we don't actually require a terminal, this was just muscle memory. This also archives the artifacts for testing. --- .github/workflows/build.yml | 23 +++++++++++++++++++++++ build/build-win64-from-linux.sh | 5 +++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7819cf2ec..4e959249a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -125,6 +125,29 @@ jobs: run: | rm -rf "$DF_FOLDER" + build-cross-win64: + name: Build MSVC win64 + runs-on: ubuntu-22.04 + steps: + - name: Clone DFHack + uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + - name: Cross-compile win64 artifacts + run: | + cd build + bash -x build-win64-from-linux.sh + - name: Format artifact name + id: artifactname + run: | + echo name=$(date +%Y%m%d)-$(git rev-parse --short $GITHUB_REF) >> $GITHUB_OUTPUT + - name: Upload win64 artifacts + uses: actions/upload-artifact@v3 + with: + name: dfhack-win64-build-${{ steps.artifactname.outputs.name }} + path: build/win64-cross/output/* + docs: runs-on: ubuntu-18.04 steps: diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 08ea88793..65c9f2f58 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -37,10 +37,11 @@ fi # # NOTE: win64-cross is mounted in /src/build due to the hardcoded `cmake ..` in # the Dockerfile -if ! docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ +if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ -e BUILDER_UID=$builder_uid \ --name dfhack-win \ - dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ + ghcr.io/dfhack/build-env:msvc \ + bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ ; then echo echo "Build failed" From cf11280b8b917294ff7f950b531d8f220ac0c047 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Tue, 24 Jan 2023 16:22:22 -0800 Subject: [PATCH 0294/2222] Simplify cross-compile instructions Now that users don't have to build the image. --- docs/dev/compile/Compile.rst | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index f91c43c07..22b9a7b1a 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -296,18 +296,13 @@ on a Linux host system. :local: :depth: 1 -Step 1: prepare a docker image ------------------------------- +Step 1: prepare a build container +--------------------------------- On your Linux host, install and run the docker daemon and then run these commands:: xhost +local:root - git clone https://github.com/BenLubar/build-env.git - cd build-env/msvc - docker build . - docker image ls - IMAGE_ID= - docker run -it --env="DISPLAY" --env="QT_X11_NO_MITSHM=1" --volume=/tmp/.X11-unix:/tmp/.X11-unix --user buildmaster --name dfhack-win $IMAGE_ID + docker run -it --env="DISPLAY" --env="QT_X11_NO_MITSHM=1" --volume=/tmp/.X11-unix:/tmp/.X11-unix --user buildmaster --name dfhack-win ghcr.io/dfhack/build-env:msvc The ``xhost`` command and ``--env`` parameters are there so you can eventually run Dwarf Fortress from the container and have it display on your host. @@ -380,19 +375,7 @@ existing Steam installation on Linux. :local: :depth: 1 -Step 1: Build the MSVC builder image ------------------------------------- - -It'll be called ``dfhack-build-msvc:latest`` after it's done building:: - - git clone https://github.com/BenLubar/build-env.git - cd build-env/msvc - docker build -t dfhack-build-msvc . - -The docker build takes a while, but only needs to be done once, unless the build -environment changes. - -Step 2: Get dfhack, and run the build script +Step 1: Get dfhack, and run the build script -------------------------------------------- Check out ``dfhack`` into another directory, and run the build script:: @@ -412,7 +395,7 @@ rather than directly:: sudo ./build-win64-from-linux.sh -Step 3: install dfhack to your Steam DF install +Step 2: install dfhack to your Steam DF install ----------------------------------------------- As the script will tell you, you can then copy the files into your DF folder:: From a496f88a296356b15f8fa7208ee58051bf17369d Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Tue, 24 Jan 2023 18:48:06 -0800 Subject: [PATCH 0295/2222] Change $GITHUB_REF to HEAD GITHUB_REF apparently doesn't work in PR builds. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4e959249a..b7aeddc70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -141,7 +141,7 @@ jobs: - name: Format artifact name id: artifactname run: | - echo name=$(date +%Y%m%d)-$(git rev-parse --short $GITHUB_REF) >> $GITHUB_OUTPUT + echo name=$(date +%Y%m%d)-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT - name: Upload win64 artifacts uses: actions/upload-artifact@v3 with: From 5227d29f1fe7693703ad9b9bcebaaf384d200243 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Tue, 24 Jan 2023 19:27:12 -0800 Subject: [PATCH 0296/2222] Use GHA caching with ccache --- .github/workflows/build.yml | 8 ++++++++ build/build-win64-from-linux.sh | 2 ++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7aeddc70..9b8892882 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -134,6 +134,14 @@ jobs: with: submodules: true fetch-depth: 0 + - name: Fetch ccache + uses: actions/cache@v3 + with: + path: build/win64-cross/ccache + key: ccache-win64-cross-msvc-${{ github.ref_name }}-${{ github.sha }} + restore-keys: | + ccache-win64-cross-msvc-${{ github.ref_name }} + ccache-win64-cross-msvc - name: Cross-compile win64 artifacts run: | cd build diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 65c9f2f58..eb366e83e 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -13,6 +13,7 @@ builder_uid=$(id -u) mkdir -p win64-cross mkdir -p win64-cross/output +mkdir -p win64-cross/ccache # Check for sudo; we want to use the real user if [[ $(id -u) -eq 0 ]]; then @@ -39,6 +40,7 @@ fi # the Dockerfile if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ -e BUILDER_UID=$builder_uid \ + -e CCACHE_DIR=/src/build/ccache \ --name dfhack-win \ ghcr.io/dfhack/build-env:msvc \ bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ From c5f25885222eecce50af28d2e9c41255dd94f53a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 24 Jan 2023 21:28:25 -0800 Subject: [PATCH 0297/2222] fix issues with clicks "falling through" scrollbars --- docs/changelog.txt | 1 + library/lua/gui.lua | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 40cb2040c..2c6821790 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- Fix issues with clicks "passing through" some DFHack window elements, like scrollbars ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 2274c8794..dc08506d7 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -754,6 +754,7 @@ function ZScreen:onInput(keys) if keys._MOUSE_L_DOWN then -- note we can't clear mouse_lbut here. otherwise we break dragging, df.global.enabler.mouse_lbut_down = 0 + self.inhibit_mouse_l = true end if keys._MOUSE_R_DOWN then df.global.enabler.mouse_rbut_down = 0 @@ -772,6 +773,13 @@ function ZScreen:onInput(keys) df.global.enabler.mouse_rbut = 0 return else + if self.inhibit_mouse_l then + if keys._MOUSE_L then + return + else + self.inhibit_mouse_l = nil + end + end local passit = self.pass_pause and keys.D_PAUSE if not passit and self.pass_mouse_clicks then for key in pairs(MOUSE_KEYS) do From 15ab59b12df7cfe77cff8ddec312562b68b538e9 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 25 Jan 2023 07:17:04 +0000 Subject: [PATCH 0298/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 65c61135e..29a087c46 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 65c61135e05a8fe380e8216534615cb96b26c1ed +Subproject commit 29a087c460117ab8e4b01f63e90314715add47db diff --git a/scripts b/scripts index 39ee60363..ea3aac0be 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 39ee60363892b7b916d670b02d46a606e3969b7c +Subproject commit ea3aac0bef216b0e5f97a8ae6c27dc05fca689c9 From 33132e5556c81e01c6c540b577c3c62bfe5bbfbc Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:41:45 +0000 Subject: [PATCH 0299/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 29a087c46..4bd9fe468 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 29a087c460117ab8e4b01f63e90314715add47db +Subproject commit 4bd9fe468ce2ab9200372e5585dd907dccbea05f From be590832028cadce305c7b0ed97344b0154bc4d5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 25 Jan 2023 22:59:29 -0800 Subject: [PATCH 0300/2222] reduce spacing for CycleHotkeyLabel from 2 to 1 --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index ab4dad88e..473f09db9 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1456,7 +1456,7 @@ function CycleHotkeyLabel:init() self:setText{ {key=self.key, key_sep=': ', text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, - ' ', + ' ', {text=self:callback('getOptionLabel'), pen=self:callback('getOptionPen')}, } From 2a8520fb9eeabe79fab43014e2ed8820bdf683a6 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 26 Jan 2023 07:15:05 +0000 Subject: [PATCH 0301/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 4bd9fe468..856273fa3 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 4bd9fe468ce2ab9200372e5585dd907dccbea05f +Subproject commit 856273fa362095482139b7bd3e61cddb55929a6d diff --git a/scripts b/scripts index ea3aac0be..3bb021f4d 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ea3aac0bef216b0e5f97a8ae6c27dc05fca689c9 +Subproject commit 3bb021f4d2af6aadaf712fd8116034c8a39eae9a From 998a63a9794ab6dcdd70118481f6da4392535c33 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 26 Jan 2023 00:53:57 -0800 Subject: [PATCH 0302/2222] allow overlay widgets to specify a default enabled state and make relevant library widgets enabled by default and remove the default overlay.json config file --- data/dfhack-config/overlay.json | 8 -------- docs/dev/overlay-dev-guide.rst | 3 +++ plugins/lua/hotkeys.lua | 1 + plugins/lua/orders.lua | 1 + plugins/lua/overlay.lua | 4 ++++ 5 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 data/dfhack-config/overlay.json diff --git a/data/dfhack-config/overlay.json b/data/dfhack-config/overlay.json deleted file mode 100644 index 7ebae0948..000000000 --- a/data/dfhack-config/overlay.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "hotkeys.menu": { - "enabled": true - }, - "unsuspend.overlay": { - "enabled": true - } -} diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index 50998530a..c7e094299 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -96,6 +96,9 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes: mean for the position. Players can change the widget position at any time via the `overlay position ` command, so don't assume that your widget will always be at the default position. +- ``default_enabled`` (default: ``false``) + Override this attribute if the overlay should be enabled by default if it + does not already have a state stored in ``dfhack-config/overlay.json``. - ``viewscreens`` (default: ``{}``) The list of viewscreens that this widget should be associated with. When one of these viewscreens is on top of the viewscreen stack, your widget's diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index c377666c2..163091318 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -12,6 +12,7 @@ local widgets = require('gui.widgets') HotspotMenuWidget = defclass(HotspotMenuWidget, overlay.OverlayWidget) HotspotMenuWidget.ATTRS{ default_pos={x=2,y=2}, + default_enabled=true, hotspot=true, viewscreens='all', overlay_onupdate_max_freq_seconds=0, diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index 282b5997d..972edcfea 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -48,6 +48,7 @@ end OrdersOverlay = defclass(OrdersOverlay, overlay.OverlayWidget) OrdersOverlay.ATTRS{ default_pos={x=53,y=-6}, + default_enabled=true, viewscreens='dwarfmode', frame={w=30, h=4}, frame_style=gui.MEDIUM_FRAME, diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index b90c1c651..bf944d7e9 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -249,6 +249,9 @@ local function load_widget(name, widget_class) } if not overlay_config[name] then overlay_config[name] = {} end local config = overlay_config[name] + if config.enabled == nil then + config.enabled = widget.default_enabled + end config.pos = sanitize_pos(config.pos or widget.default_pos) widget.frame = make_frame(config.pos, widget.frame) if config.enabled then @@ -487,6 +490,7 @@ OverlayWidget = defclass(OverlayWidget, widgets.Panel) OverlayWidget.ATTRS{ name=DEFAULT_NIL, -- this is set by the framework to the widget name default_pos={x=DEFAULT_X_POS, y=DEFAULT_Y_POS}, -- 1-based widget screen pos + default_enabled=false, -- initial enabled state if not in config overlay_only=false, -- true if there is no widget to reposition hotspot=false, -- whether to call overlay_onupdate on all screens viewscreens={}, -- override with associated viewscreen or list of viewscrens From 1a659f89b9cb3abc2981866d6daf858a97d99c65 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 26 Jan 2023 00:55:12 -0800 Subject: [PATCH 0303/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2c6821790..8d84e3cff 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,6 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open - `gui/quickcmd`: now has it's own global keybinding for your convenience: Ctrl-Shift-A - Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. +- `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file ## Documentation From 2ae3d7dd856310399d53605530b1fa15268335be Mon Sep 17 00:00:00 2001 From: Scott Ellis Date: Thu, 26 Jan 2023 08:09:25 -0600 Subject: [PATCH 0304/2222] Stop creating workorders to create platinum weapons Platinum weapons are normally only created by artifact moods. Removed creation of platinum weapons from military.json Altered other weapon work orders to not care about the existance of platinum. Renamed original file for those who want to create platinum weapons anyway. --- data/orders/military.json | 207 - .../military_include_artifact_materials.json | 4971 +++++++++++++++++ 2 files changed, 4971 insertions(+), 207 deletions(-) create mode 100644 data/orders/military_include_artifact_materials.json diff --git a/data/orders/military.json b/data/orders/military.json index 536b2cd7a..5b7604dac 100644 --- a/data/orders/military.json +++ b/data/orders/military.json @@ -1066,105 +1066,6 @@ "job" : "MakeAmmo", "material" : "INORGANIC:IRON" }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 31, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:PLATINUM" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 32, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:PLATINUM" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 64, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:PLATINUM" - }, { "amount_left" : 1, "amount_total" : 1, @@ -1174,12 +1075,6 @@ "is_validated" : false, "item_conditions" : [ - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtLeast", "item_type" : "BAR", @@ -1216,12 +1111,6 @@ "is_validated" : false, "item_conditions" : [ - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtLeast", "item_type" : "BAR", @@ -1270,12 +1159,6 @@ "material" : "COAL", "value" : 100 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -1563,12 +1446,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "item_subtype" : "ITEM_WEAPON_MACE", @@ -1608,12 +1485,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", @@ -1786,12 +1657,6 @@ "material" : "INORGANIC:STEEL", "value" : 10 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "LessThan", "item_type" : "BAR", @@ -2198,12 +2063,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -2258,12 +2117,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -2510,12 +2363,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -2937,12 +2784,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - } ], "item_subtype" : "ITEM_WEAPON_MACE", "job" : "MakeWeapon", @@ -2987,12 +2828,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3239,12 +3074,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3697,12 +3526,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3763,12 +3586,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -4045,12 +3862,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -4557,12 +4368,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -4629,12 +4434,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "LessThan", "item_type" : "BAR", @@ -4941,12 +4740,6 @@ "material" : "INORGANIC:SILVER", "value" : 5 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, { "condition" : "AtMost", "flags" : diff --git a/data/orders/military_include_artifact_materials.json b/data/orders/military_include_artifact_materials.json new file mode 100644 index 000000000..536b2cd7a --- /dev/null +++ b/data/orders/military_include_artifact_materials.json @@ -0,0 +1,4971 @@ +[ + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 0, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 10 + }, + { + "condition" : "AtMost", + "item_type" : "BACKPACK", + "value" : 10 + } + ], + "job" : "MakeBackpack", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 1, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 10 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_type" : "FLASK", + "value" : 10 + } + ], + "job" : "MakeFlask", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 2, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 10 + }, + { + "condition" : "AtMost", + "item_type" : "QUIVER", + "value" : 10 + } + ], + "job" : "MakeQuiver", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 3, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 10 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_subtype" : "ITEM_ARMOR_CLOAK", + "item_type" : "ARMOR", + "value" : 10 + } + ], + "item_subtype" : "ITEM_ARMOR_CLOAK", + "job" : "MakeArmor", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 4, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "WOOD", + "value" : 50 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material_category" : + [ + "wood" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 5, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 25 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "item_type" : "SHIELD", + "value" : 1 + } + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "job" : "MakeShield", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 6, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 25 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_ARMOR_LEATHER", + "item_type" : "ARMOR", + "value" : 1 + } + ], + "item_subtype" : "ITEM_ARMOR_LEATHER", + "job" : "MakeArmor", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 7, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 25 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_subtype" : "ITEM_HELM_HELM", + "item_type" : "HELM", + "value" : 1 + } + ], + "item_subtype" : "ITEM_HELM_HELM", + "job" : "MakeHelm", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 8, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 25 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "item_type" : "SHOES", + "value" : 2 + } + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "job" : "MakeShoes", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 9, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 25 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_subtype" : "ITEM_PANTS_LEGGINGS", + "item_type" : "PANTS", + "value" : 1 + } + ], + "item_subtype" : "ITEM_PANTS_LEGGINGS", + "job" : "MakePants", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 10, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "SKIN_TANNED", + "value" : 25 + }, + { + "condition" : "AtMost", + "flags" : + [ + "leather" + ], + "item_subtype" : "ITEM_GLOVES_GLOVES", + "item_type" : "GLOVES", + "value" : 2 + } + ], + "item_subtype" : "ITEM_GLOVES_GLOVES", + "job" : "MakeGloves", + "material_category" : + [ + "leather" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 11, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "flags" : + [ + "unrotten", + "bone", + "body_part" + ], + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "bone" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 1000 + } + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "job" : "MakeAmmo", + "material_category" : + [ + "bone" + ] + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 12, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "WOOD", + "value" : 150 + }, + { + "condition" : "AtMost", + "flags" : + [ + "bone" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 200 + }, + { + "condition" : "AtMost", + "flags" : + [ + "plant" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 1000 + } + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "job" : "MakeAmmo", + "material_category" : + [ + "wood" + ] + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 13, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:CASSITERITE", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:TIN", + "value" : 20 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:CASSITERITE" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 14, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:HEMATITE", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 40 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:HEMATITE" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 15, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:HORN_SILVER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 10 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:HORN_SILVER" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 16, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:LIMONITE", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 40 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:LIMONITE" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 17, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:NATIVE_COPPER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 40 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:NATIVE_COPPER" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 18, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:NATIVE_PLATINUM", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 10 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:NATIVE_PLATINUM" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 19, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:NATIVE_SILVER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 10 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:NATIVE_SILVER" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 20, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:MAGNETITE", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 40 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:MAGNETITE" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 21, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:MALACHITE", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 40 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:MALACHITE" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 22, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "material" : "INORGANIC:TETRAHEDRITE", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 40 + } + ], + "job" : "SmeltOre", + "material" : "INORGANIC:TETRAHEDRITE" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 23, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "bearing" : "TIN", + "condition" : "AtLeast", + "item_type" : "BOULDER", + "value" : 5 + }, + { + "bearing" : "COPPER", + "condition" : "AtLeast", + "item_type" : "BOULDER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 40 + } + ], + "job" : "CustomReaction", + "reaction" : "BRONZE_MAKING" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 24, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:TIN", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 40 + }, + { + "bearing" : "TIN", + "condition" : "AtMost", + "item_type" : "BOULDER", + "value" : 5 + }, + { + "bearing" : "COPPER", + "condition" : "AtMost", + "item_type" : "BOULDER", + "value" : 5 + } + ], + "job" : "CustomReaction", + "reaction" : "BRONZE_MAKING2" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 25, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:PIG_IRON", + "value" : 10 + } + ], + "job" : "CustomReaction", + "reaction" : "PIG_IRON_MAKING" + }, + { + "amount_left" : 4, + "amount_total" : 4, + "frequency" : "Daily", + "id" : 26, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:PIG_IRON", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 40 + } + ], + "job" : "CustomReaction", + "reaction" : "STEEL_MAKING" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 27, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 1000 + } + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "job" : "MakeAmmo", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 28, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "min_dimension" : 150, + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 1000 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "job" : "MakeAmmo", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 29, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 1000 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "job" : "MakeAmmo", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 30, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "min_dimension" : 150, + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "item_type" : "AMMO", + "value" : 1000 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + } + ], + "item_subtype" : "ITEM_AMMO_BOLTS", + "job" : "MakeAmmo", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 31, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "material" : "INORGANIC:PLATINUM", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:PLATINUM" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 32, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "material" : "INORGANIC:PLATINUM", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:PLATINUM" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 64, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "material" : "INORGANIC:PLATINUM", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:PLATINUM" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 35, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:SILVER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 35, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:SILVER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 64, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:SILVER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 37, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_SHIELD_SHIELD", + "item_type" : "SHIELD", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "job" : "MakeShield", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 38, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "job" : "MakeArmor", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 39, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_HELM_HELM", + "item_type" : "HELM", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_HELM_HELM", + "job" : "MakeHelm", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 40, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_SHOES_BOOTS", + "item_type" : "SHOES", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "job" : "MakeShoes", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 41, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "item_type" : "GLOVES", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "job" : "MakeGloves", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 42, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_PANTS_GREAVES", + "item_type" : "PANTS", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "material" : "INORGANIC:STEEL", + "value" : 5 + } + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "job" : "MakePants", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 43, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "item_type" : "ARMOR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "material" : "INORGANIC:STEEL", + "value" : 5 + } + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "job" : "MakeArmor", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 44, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 45, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 46, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_SPEAR", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 47, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 48, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 49, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_PICK", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 50, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:STEEL" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 51, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "item_type" : "SHIELD", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "job" : "MakeShield", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 52, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "job" : "MakeArmor", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 53, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_HELM_HELM", + "item_type" : "HELM", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_HELM_HELM", + "job" : "MakeHelm", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 54, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "item_type" : "SHOES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "job" : "MakeShoes", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 55, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "item_type" : "GLOVES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "job" : "MakeGloves", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 56, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "item_type" : "PANTS", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + } + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "job" : "MakePants", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 57, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + } + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "job" : "MakeArmor", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 58, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 59, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 60, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 61, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 62, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 63, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + } + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 64, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:IRON" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 79, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "item_type" : "SHIELD", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "job" : "MakeShield", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 80, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "job" : "MakeArmor", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 81, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_HELM_HELM", + "item_type" : "HELM", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_HELM_HELM", + "job" : "MakeHelm", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 82, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "item_type" : "SHOES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "job" : "MakeShoes", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 83, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "item_type" : "GLOVES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + } + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "job" : "MakeGloves", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 84, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "item_type" : "PANTS", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + } + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "job" : "MakePants", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 85, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + } + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "job" : "MakeArmor", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 72, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "min_dimension" : 150, + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 87, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 88, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 89, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 90, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 91, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + } + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 92, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:BISMUTH_BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 79, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "item_type" : "SHIELD", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "job" : "MakeShield", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 80, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "job" : "MakeArmor", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 81, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_HELM_HELM", + "item_type" : "HELM", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_HELM_HELM", + "job" : "MakeHelm", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 82, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "item_type" : "SHOES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "job" : "MakeShoes", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 83, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "item_type" : "GLOVES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "job" : "MakeGloves", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 84, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "item_type" : "PANTS", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "job" : "MakePants", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 85, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "job" : "MakeArmor", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 86, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 87, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 88, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 89, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 90, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 91, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 30 + } + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 92, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 30 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:BRONZE" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 93, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "item_type" : "SHIELD", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHIELD_SHIELD", + "job" : "MakeShield", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 94, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "job" : "MakeArmor", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 95, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_HELM_HELM", + "item_type" : "HELM", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_HELM_HELM", + "job" : "MakeHelm", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 96, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "item_type" : "SHOES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_SHOES_BOOTS", + "job" : "MakeShoes", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 97, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "item_type" : "GLOVES", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + } + ], + "item_subtype" : "ITEM_GLOVES_GAUNTLETS", + "job" : "MakeGloves", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 98, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "item_type" : "PANTS", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + } + ], + "item_subtype" : "ITEM_PANTS_GREAVES", + "job" : "MakePants", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 99, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 20 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "item_type" : "ARMOR", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 20 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 20 + }, + { + "condition" : "AtLeast", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", + "item_type" : "ARMOR", + "value" : 5 + } + ], + "item_subtype" : "ITEM_ARMOR_BREASTPLATE", + "job" : "MakeArmor", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 100, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 101, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 102, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SPEAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 103, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 104, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 105, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 10 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 30 + } + ], + "item_subtype" : "ITEM_WEAPON_PICK", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 106, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:COPPER", + "value" : 30 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BRONZE", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:IRON", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 30 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:PLATINUM", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:BISMUTH_BRONZE", + "value" : 30 + } + ], + "item_subtype" : "ITEM_WEAPON_CROSSBOW", + "job" : "MakeWeapon", + "material" : "INORGANIC:COPPER" + } +] From 54a44e589bcb3ba50e13df1f8a8ed478ab7f9cdc Mon Sep 17 00:00:00 2001 From: Scott Ellis Date: Thu, 26 Jan 2023 08:15:31 -0600 Subject: [PATCH 0305/2222] Update orders.rst to reflect changes in military.json --- docs/plugins/orders.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 2c5b0255f..74ca3a682 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -106,7 +106,7 @@ produces weapons and armor: - smelting for platinum, silver, steel, bronze, bismuth bronze, and copper (and their dependencies) - bronze/bismuth bronze/copper bolts -- platinum/silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, +- silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, with checks to ensure only the best available materials are being used If you set a stockpile to take weapons and armor of less than masterwork quality @@ -117,6 +117,16 @@ Make sure you have a lot of fuel (or magma forges and furnaces) before you turn This file should only be imported, of course, if you need to equip a military. +:source:`library/military ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As above, but this collection will also allow creation of platinum blunt weapons. +Normally these are only created by artifact moods, work orders can't be created +manually for them. + +- platinum/silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, + with checks to ensure only the best available materials are being used + :source:`library/smelting ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From a7b6ddc4da7b73ee94722b8cc83da754734854db Mon Sep 17 00:00:00 2001 From: vallode <18506096+vallode@users.noreply.github.com> Date: Thu, 26 Jan 2023 18:07:04 +0100 Subject: [PATCH 0306/2222] Remove liquids.png --- docs/images/liquids.png | Bin 3676 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/images/liquids.png diff --git a/docs/images/liquids.png b/docs/images/liquids.png deleted file mode 100644 index da70a058b0b4c5b31c71d71f4f9c505681a858ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3676 zcmY*X3pkWp_kU;1Fz#dI8hU9IW?YM0Dz95cNV(O>ovA1lDJGX@7!0DxZBp)aOc=M3 zaydDOFjFptQ0fpR#Gw#6eAD;;pYQp;{p`Jdd#`7&-+I<>t>oj!Y{f+sMF0R0w$rof4Ie2KN^5H?0BlnzzaHD$+W-aN8{pUGpI>f1AVf`9 z_-tS8tSww4z2})P%bw|>X>t(^35Q^E(th8Fq7YR^o#ZU`PpuD;-g|Jn`$dh`O#;)B z2CAY5x{l^3=NOyI=N+6+dlnxW?V(g4W&T!8wpAWBWzct0ZObH9{u+EH;X1sTJ>~Li0upps#Fu6*~;61rVlU$Er7JR@9QXY92Fz(3(iJPEWr)HjK9!75^Cp!-V zUBtgz6QHW6zUN*^K$yxHr}86D!UlrRHi}7vi(~}DP9){|StCDQOTteWqMeRuQX9;^ z9s~13msG=T+#uJYfa=QxvM9PLs=&Zjifu%h$uc&-mWWE|Wf-EfG`Myv<0>Gd#XF#( zyy#5IQj{rn`0!)A_|CH^D@gGMQ5>xN1Mjx_lXOBwKy6L`^zf}b3d(JTS>KYBb&@fB z2(d$BE{eXe2SYL(cwH5RqMpOW6@ybRrmm-YcF!%Sj@=I%EjvB@*CHTpw2)Zrboe+#nPH`=hTjmM{^yoJ&q#TM+P)8ixADDH$^r6(2C@*a{ zHgfrv%aQ0gXW1Ti+${Q99st{GZvsMn_IVcslo{9V1QDZz zM`tH!tXT@(xW^fG$owzSi;DFhYbYe;V@c%{z2?&tc>?8KMTZ1xrgptRSUIOWI;F%) zww8UxFsC#80N58t*`utMfX=Xmsl8~2S9fsyQ(XnHh+a5V3_ZFR`nft{$^!JHI%r(Sg#HjZBItnq{vo62r{=U-l2LSX!hNyULkx9oW@S(R!zd_BI$+wH*H zmqzY}2H?jTs^?P5Z#mN&RbMzcG8Ebi%DF*_uat$zB}jzDE}grlV@5)P&O=;^Z!O8t z>-A@C9$8n9=PVj3h^|%Z$(TKZb4w8GrguUmGArY{In!nxczfLsU|8Y~5BCq@MkH;l zQs_dKOfLh;!kZ297FGRi_)d__Oz`HW`n8lZ#|KZClcpxn&s9wGKa zW)@>aQ%dZ`9*5DRz7?GvBUu<_>s?9o?`s*UYH=~PL(CZ-_8_Fx`EKK>F0Y9_z_gAP zP>U30NOiZqoPiGVu)Co^NtCr?CM?;cTvwu_&cOhVXof7;@jR!xk~)0rO<|9He+<_#LE-UG7Z>LRY$4>j;gc0Mj=QlZHxeU%QV4?$Sq>SoUrMm$Z zKJI4Hp2Lb@fME9z4bbN+FZhf{A;)r$qHd^+KhOqD_d-o33>O%?ZuZKj#7k&)p`>>EM8}J`#+c;Y1+_@&&4h zy0I91l{hSM60$ZmPY{zmdGSqy$-L25j<m7f+hdt+2=st$?oM@Ws zEXNs4w<(&`l>@Kphu;k+K2)u5!~=HD?!GXPi?<=X*5Sd*9wtsvLDEYieee!Pu}QM0 znf=T+Ny6%7>UB{4h#D2LDc6_{Cl(xaoTcg5PnCJedda%v7(D5u8z8?6Z*n-$vZ8%% z95llGZAy`jU)I|DXoQ&%a7hF^bc*1*C7qAlS-{~o;Yl|UIt_M<)^--)JdZ0Nl?=6W zUr@z>_7?V}j_6&4<@t6Tpn2SCgqe+u8~T|=lC)|B{Csl6bwnZmbmt%1*|{{jkh2an z!@+NIwB}3|JOE3k4buh;C$5M$;SUFNf3}+8{J5!FsQ-XBxi68e8cvLU&Nz_B?~f(Z zZNQi7@qt|g$?VeXkF{hbJl)a8QV!2pct`3dkv3SQVn3X^@1nd~I1z|bVP8&e!K+`f z6b7RD(8WABI$&znLbGvb`24n{`3D7*R}O|KY~A!q%95FV3!SZ0sdC!t`rp?4IkwJ4 zbU$+4`3v_8%>~1e+pmaGY5s zfbd)gIRp{79YZ`?MeTO1!<~i=L@n|-MzzU5Ug3+{6v!>O*pmxFv>0O2gq}mSa)N6~ z2Ur|oaRk3s;H$_n1r_4%FGm9V79(|av@_F7x{8vUK&FE+Q$yX*Kz)|~?g^FwOqw_^((MG+!K zIhS0qRrus{C;DsibP1kB%m_8^);N%avxjR_;?kAnph+%9UIMid;`{x*i3S-cg7bBJxh9O)*Yfmn>2PfWxaecOrV4K3G@yvxDfMJIAKB;QVp69 zCVX0RRn?M{-AW`Qo5_p9%j^2)D3E6q`HlN2!*(s$UCCz+#i%6Lw~e%!9o8m%Awt(W z8q$l&$c}A!6sYqC?~vx*6s+>NOSC-f)b-j?DGGi(GyLSxy`%EE7T{7`Y*po_)$72k zH|=gVk3~8*&A_ihotah=eX1KQ9R|HWe&%!5@t?IIEkj_D*_=asG*9kF3sW zv`@qF(f4N6k}2CPyZrp7!eEvJePv-AK7kiS54C>{aIHT5-fSEcwl;e7Gm#=#|{_)qoFz3 zS0aM)9)Hq0|ImCtUXH5=H8m(baaqfpA2cx?XaZ<;cnE+Y(7z00UEtsNk0FNXmn{r* zo6zbv9)}xVK(8Psc7g}ny_SVCE|xZR6*r}A9C^lZ#0@wV^{(rqz&+G=Ek>2JE^yXu zz^T7B-2zzW=#>RoSn4(2zLgkqFZpGN@2vKlP3gQVb?JuyyR~l{a@MyO#>r1(#ucUg z@6*LX{eqIe$W&j8JC}Ke9Gp;N>5pTaNw8*2y`Ex-VC#lKXA?o15@yj~a6lv|aoW3} zya3d95DaVjedrSL5TP;K!gHv`=KQoi;Q~O8*@YQqTnU2>)O6HXCxzfX8ny*<5k2mn zU6dLZ$zS!aKLY-*@_#D+M~K(Dd_4luOi3ZtBmj;ADgSG7;r|H;-1;AiT>*R0(7Y>1 zF@{P-&H{c3zXHD@K^Ct$9)Q#LJvk<>8|Nm`@PiR*8D)}`yKr@Gh8BmW7oRJQZ~Xhf zraZZ>M#g$%YBzn0jLxpeh?alwn@dT>Ts<0XKZx|Vn7@ZX?;nbwA|7mknA1lxC=X{e zXOhlvB{QTAb_2qv;c%fq(HDEM)okSG@_W_wq#eBDar+{01Po(J$W1IY;RjMa7T_;C Mn`72hmcDWS0)ACjEdT%j From a767a917faec215db888d3446351668770c38797 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 26 Jan 2023 12:49:22 -0800 Subject: [PATCH 0307/2222] de-emphasize IRC in our docs --- docs/dev/Contributing.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/dev/Contributing.rst b/docs/dev/Contributing.rst index 66be801f4..5235b1c0f 100644 --- a/docs/dev/Contributing.rst +++ b/docs/dev/Contributing.rst @@ -27,8 +27,7 @@ modify. GitHub has several documentation pages on these topics, including: In general, if you are not sure where or how to make a change, or would like advice before attempting to make a change, please see `support` for ways to -contact maintainers - DFHack-specific channels such as IRC or Bay12 are -preferred. If you are interested in addressing an issue reported on the +contact maintainers. If you are interested in addressing an issue reported on the :issue:`issue tracker <>`, you can start a discussion there if you prefer. The sections below cover some guidelines that contributions should follow: @@ -45,7 +44,7 @@ General contribution guidelines * Update ``docs/changelog.txt`` and ``docs/about/Authors.rst`` when applicable. See `build-changelog` for more information on the changelog format. * Submit ideas and bug reports as :issue:`issues on GitHub <>`. - Posts in the forum thread can easily get missed or forgotten. + Posts in the forum thread or on Disord can easily get missed or forgotten. * Work on :issue:`reported problems ` will take priority over ideas or suggestions. From de262e610a075a46d48432a1bd82a08ebffa2bdf Mon Sep 17 00:00:00 2001 From: Scott Ellis Date: Thu, 26 Jan 2023 15:05:42 -0600 Subject: [PATCH 0308/2222] Fix lint error --- docs/plugins/orders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 74ca3a682..48e92ff9b 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -121,7 +121,7 @@ This file should only be imported, of course, if you need to equip a military. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As above, but this collection will also allow creation of platinum blunt weapons. -Normally these are only created by artifact moods, work orders can't be created +Normally these are only created by artifact moods, work orders can't be created manually for them. - platinum/silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, From 38706ff02a41a3b36592f9ac8880ace50289cd84 Mon Sep 17 00:00:00 2001 From: "Scott K. Ellis" Date: Thu, 26 Jan 2023 15:06:59 -0600 Subject: [PATCH 0309/2222] Update docs/plugins/orders.rst Co-authored-by: Myk --- docs/plugins/orders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 48e92ff9b..35ab71864 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -118,7 +118,7 @@ Make sure you have a lot of fuel (or magma forges and furnaces) before you turn This file should only be imported, of course, if you need to equip a military. :source:`library/military ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As above, but this collection will also allow creation of platinum blunt weapons. Normally these are only created by artifact moods, work orders can't be created From 9c884c6365692dac5b45b3f25e4cf8400d50ed63 Mon Sep 17 00:00:00 2001 From: Scott Ellis Date: Thu, 26 Jan 2023 16:30:53 -0600 Subject: [PATCH 0310/2222] Include proper name for library/military_include_artifact_materials --- docs/plugins/orders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 35ab71864..94c16b804 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -117,7 +117,7 @@ Make sure you have a lot of fuel (or magma forges and furnaces) before you turn This file should only be imported, of course, if you need to equip a military. -:source:`library/military ` +:source:`library/military_include_artifact_materials ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As above, but this collection will also allow creation of platinum blunt weapons. From 27663e50a30a9d2b69b8713fc35ec1e046af9b0c Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 26 Jan 2023 15:55:38 -0800 Subject: [PATCH 0311/2222] Update docs/plugins/orders.rst --- docs/plugins/orders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 94c16b804..6fe159899 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -118,7 +118,7 @@ Make sure you have a lot of fuel (or magma forges and furnaces) before you turn This file should only be imported, of course, if you need to equip a military. :source:`library/military_include_artifact_materials ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As above, but this collection will also allow creation of platinum blunt weapons. Normally these are only created by artifact moods, work orders can't be created From 5b83f30706622fbbb9f001828069b8c133ecfdd8 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 27 Jan 2023 03:14:34 +0000 Subject: [PATCH 0312/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 856273fa3..7050d2338 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 856273fa362095482139b7bd3e61cddb55929a6d +Subproject commit 7050d23387310660488b42483e6f03ba17ba6711 diff --git a/scripts b/scripts index 3bb021f4d..f4058184a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3bb021f4d2af6aadaf712fd8116034c8a39eae9a +Subproject commit f4058184a6716ef921e6019d1386354f59fdb0a8 From 6e1ab8d67b2063bee595bbdc422c37c73c4ff368 Mon Sep 17 00:00:00 2001 From: gearsix Date: Thu, 26 Jan 2023 18:58:25 +0000 Subject: [PATCH 0313/2222] issue #1805 - made getplants input case-insensitive Now `toUpper` is called on user input when it's added to `plantNames` to ensure it matches the ID fields which are all in upper-case. --- plugins/getplants.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 4fc82cb97..485dff967 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -7,6 +7,7 @@ #include "PluginManager.h" #include "DataDefs.h" #include "TileTypes.h" +#include "MiscUtils.h" #include "df/map_block.h" #include "df/map_block_column.h" @@ -395,7 +396,7 @@ command_result df_getplants (color_ostream &out, vector & parameters) } } else - plantNames.insert(parameters[i]); + plantNames.insert(toUpper(parameters[i])); } if (treesonly && shrubsonly) { From 6726b567a1224e22a9515fec5ec96b53c9b626ca Mon Sep 17 00:00:00 2001 From: gearsix Date: Fri, 27 Jan 2023 14:19:29 +0000 Subject: [PATCH 0314/2222] issue #2043 - `designate` now marks trees (regardless of if ripe). The `ripe` call was returning false on tree tiles, resulting in an inability to designate trees for chopping with `getplants`. This change adds a check to see if the tile is a tree or not and if it is, then the ripe check is ignored. --- plugins/getplants.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 485dff967..565316fa6 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -322,9 +322,9 @@ bool designate(const df::plant *plant, bool farming) { } } - if ((!farming || seedSource) && - ripe(plant->pos.x, plant->pos.y, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2) && - !picked(plant, i)) + bool istree = (tileMaterial(Maps::getTileBlock(plant->pos)->tiletype[plant->pos.x % 16][plant->pos.y % 16]) == tiletype_material::TREE); + bool isripe = ripe(plant->pos.x, plant->pos.y, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2); + if ((!farming || seedSource) && (istree || isripe) && !picked(plant, i)) { return Designations::markPlant(plant); } @@ -429,7 +429,7 @@ command_result df_getplants (color_ostream &out, vector & parameters) // plantSelections[i] = selectablePlant(out, plant, farming); plantSelections[i] = selectablePlant(plant, farming); } - else if (plantNames.find(plant->id) != plantNames.end()) + else if (plantNames.find(plant->id) != plantNames.end()) { plantNames.erase(plant->id); // plantSelections[i] = selectablePlant(out, plant, farming); From 8ac1ea1ca51ffc843db5147397ab1fa2d80df9bb Mon Sep 17 00:00:00 2001 From: vallode <18506096+vallode@users.noreply.github.com> Date: Fri, 27 Jan 2023 19:03:36 +0100 Subject: [PATCH 0315/2222] Add show-unit-syndromes to removed scripts --- docs/about/Removed.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index ad36ed8a8..174bd1185 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -151,6 +151,12 @@ ruby Support for the Ruby language in DFHack scripts was removed due to the issues the Ruby library causes when used as an embedded language. +.. _show_unit_syndromes: + +show_unit_syndromes +==== +Replaced with a GUI version: `gui/unit-syndromes`. + .. _warn-stuck-trees: warn-stuck-trees From c6f4a7ee4257c1d9e6a0a1cb3efb3f4f2981a75b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 13:36:01 -0800 Subject: [PATCH 0316/2222] inhibit _MOUSE_L globally once _MOUSE_L_DOWN is handled so the _MOUSE_L doesn't bleed through to the underlying viewscreen --- library/lua/gui.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index dc08506d7..d870399a3 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -693,6 +693,8 @@ end -- Z-order swapping screen -- ----------------------------- +local zscreen_inhibit_mouse_l = false + ZScreen = defclass(ZScreen, Screen) ZScreen.ATTRS{ initial_pause=true, @@ -754,7 +756,7 @@ function ZScreen:onInput(keys) if keys._MOUSE_L_DOWN then -- note we can't clear mouse_lbut here. otherwise we break dragging, df.global.enabler.mouse_lbut_down = 0 - self.inhibit_mouse_l = true + zscreen_inhibit_mouse_l = true end if keys._MOUSE_R_DOWN then df.global.enabler.mouse_rbut_down = 0 @@ -773,11 +775,11 @@ function ZScreen:onInput(keys) df.global.enabler.mouse_rbut = 0 return else - if self.inhibit_mouse_l then + if zscreen_inhibit_mouse_l then if keys._MOUSE_L then return else - self.inhibit_mouse_l = nil + zscreen_inhibit_mouse_l = false end end local passit = self.pass_pause and keys.D_PAUSE From 22dd49ce38b2afb4eb6395e78e04ed7b37414c1c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 13:38:21 -0800 Subject: [PATCH 0317/2222] remove "room" checking for buildings -- there is no room --- library/LuaApi.cpp | 4 +-- library/include/modules/Buildings.h | 6 ++-- library/modules/Buildings.cpp | 44 ++++++++--------------------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 439ed5d15..421821c05 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2234,8 +2234,8 @@ static const luaL_Reg dfhack_burrows_funcs[] = { /***** Buildings module *****/ -static bool buildings_containsTile(df::building *bld, int x, int y, bool room) { - return Buildings::containsTile(bld, df::coord2d(x,y), room); +static bool buildings_containsTile(df::building *bld, int x, int y) { + return Buildings::containsTile(bld, df::coord2d(x,y)); } static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index f4e8f37be..cb9091861 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -151,9 +151,11 @@ DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size, DFHACK_EXPORT int countExtentTiles(df::building_extents *ext, int defval = -1); /** - * Checks if the building contains the specified tile. + * Checks if the building contains the specified tile. If the building has + * extents, returns whether tile is included within the extents. The x and y in + * tile are the map coordinates without the z component. */ -DFHACK_EXPORT bool containsTile(df::building *bld, df::coord2d tile, bool room = false); +DFHACK_EXPORT bool containsTile(df::building *bld, df::coord2d tile); /** * Checks if the area has support from the terrain. diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 5077b5b52..828768a54 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -407,7 +407,7 @@ df::building *Buildings::findAtTile(df::coord pos) if (building && building->z == pos.z && building->isSettingOccupancy() && - containsTile(building, pos, false)) + containsTile(building, pos)) { return building; } @@ -442,24 +442,19 @@ df::building *Buildings::findAtTile(df::coord pos) static unordered_map corner1; static unordered_map corner2; -static void cacheBuilding(df::building *building, bool is_civzone) { +static void cacheBuilding(df::building *building) { int32_t id = building->id; df::coord p1(min(building->x1, building->x2), min(building->y1,building->y2), building->z); df::coord p2(max(building->x1, building->x2), max(building->y1,building->y2), building->z); - if (!is_civzone) { - // civzones can be dynamically shrunk so we don't bother to cache - // their boundaries. findCivzonesAt() will trim the cache as needed. - corner1[id] = p1; - corner2[id] = p2; - } + corner1[id] = p1; + corner2[id] = p2; for (int32_t x = p1.x; x <= p2.x; x++) { for (int32_t y = p1.y; y <= p2.y; y++) { df::coord pt(x, y, building->z); - if (Buildings::containsTile(building, pt, is_civzone)) { - if (!is_civzone) - locationToBuilding[pt] = id; + if (Buildings::containsTile(building, pt)) { + locationToBuilding[pt] = id; } } } @@ -868,31 +863,16 @@ int Buildings::countExtentTiles(df::building_extents *ext, int defval) return cnt; } -bool Buildings::containsTile(df::building *bld, df::coord2d tile, bool room) -{ +bool Buildings::containsTile(df::building *bld, df::coord2d tile) { CHECK_NULL_POINTER(bld); - if (room) - { -/* TODO: understand how this changes for v50 - if (!bld->is_room || !bld->room.extents) - return false; -*/ - } - else - { + if (!bld->isExtentShaped() || !bld->room.extents) { if (tile.x < bld->x1 || tile.x > bld->x2 || tile.y < bld->y1 || tile.y > bld->y2) return false; } - if (bld->room.extents && (room || bld->isExtentShaped())) - { - df::building_extents_type *etile = getExtentTile(bld->room, tile); - if (!etile || !*etile) - return false; - } - - return true; + df::building_extents_type *etile = getExtentTile(bld->room, tile); + return etile && *etile; } bool Buildings::hasSupport(df::coord pos, df::coord2d size) @@ -1491,7 +1471,7 @@ void Buildings::updateBuildings(color_ostream&, void* ptr) { bool is_civzone = !building->isSettingOccupancy(); if (!corner1.count(id) && !is_civzone) - cacheBuilding(building, false); + cacheBuilding(building); } else if (corner1.count(id)) { @@ -1697,7 +1677,7 @@ StockpileIterator& StockpileIterator::operator++() { continue; } - if (!Buildings::containsTile(stockpile, item->pos, false)) { + if (!Buildings::containsTile(stockpile, item->pos)) { continue; } From f700ef90b8df6dd7605a7cac51cccfa1ff4b368f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 13:38:49 -0800 Subject: [PATCH 0318/2222] expose tiletype setting to Lua --- plugins/tiletypes.cpp | 287 ++++++++++++++++++++++++------------------ 1 file changed, 164 insertions(+), 123 deletions(-) diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index f3773b0fe..e0738e7fa 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -259,7 +259,7 @@ struct TileType inline bool matches(const df::tiletype source, const df::tile_designation des, - const t_matpair mat) + const t_matpair mat) const { bool rv = true; rv &= (shape == -1 || shape == tileShape(source)); @@ -735,6 +735,111 @@ bool processTileType(color_ostream & out, TileType &paint, std::vector= 0) + source = blk->baseTiletypeAt(pos); + + t_matpair basemat = blk->baseMaterialAt(pos); + + if (!match.matches(source, des, basemat)) + return true; + + df::tiletype_shape shape = target.shape; + if (shape == tiletype_shape::NONE) + shape = tileShape(source); + + df::tiletype_material material = target.material; + if (material == tiletype_material::NONE) + material = tileMaterial(source); + + df::tiletype_special special = target.special; + if (special == tiletype_special::NONE) + special = tileSpecial(source); + + df::tiletype_variant variant = target.variant; + /* + * FIXME: variant should be: + * 1. If user variant: + * 2. If user variant \belongs target variants + * 3. use user variant + * 4. Else + * 5. use variant 0 + * 6. If the source variant \belongs target variants + * 7 use source variant + * 8 ElseIf num target shape/material variants > 1 + * 9. pick one randomly + * 10.Else + * 11. use variant 0 + * + * The following variant check has been disabled because it's severely limiting + * the usefullness of the tool. + */ + /* + if (variant == tiletype_variant::NONE) + { + variant = tileVariant(source); + } + */ + // Remove direction from directionless tiles + DFHack::TileDirection direction = tileDirection(source); + if (!(material == tiletype_material::RIVER || shape == tiletype_shape::BROOK_BED || special == tiletype_special::TRACK || (shape == tiletype_shape::WALL && (material == tiletype_material::CONSTRUCTION || special == tiletype_special::SMOOTH)))) + direction.whole = 0; + + df::tiletype type = DFHack::findTileType(shape, material, variant, special, direction); + // hack for empty space + if (shape == tiletype_shape::EMPTY && material == tiletype_material::AIR && variant == tiletype_variant::VAR_1 && special == tiletype_special::NORMAL && direction.whole == 0) + type = tiletype::OpenSpace; + + // make sure it's not invalid + if (type != tiletype::Void) { + if (target.stone_material >= 0) { + if (!blk->setStoneAt(pos, type, target.stone_material, target.vein_type, true, true)) + return false; + } + else + map.setTiletypeAt(pos, type); + } + + if (target.hidden > -1) + des.bits.hidden = target.hidden; + + if (target.light > -1) + des.bits.light = target.light; + + if (target.subterranean > -1) + des.bits.subterranean = target.subterranean; + + if (target.skyview > -1) + des.bits.outside = target.skyview; + + if (target.aquifer > -1) + des.bits.water_table = target.aquifer; + + // Remove liquid from walls, etc + if (type != (df::tiletype)-1 && !DFHack::FlowPassable(type)) + { + des.bits.flow_size = 0; + //des.bits.liquid_type = DFHack::liquid_water; + //des.bits.water_table = 0; + des.bits.flow_forbid = 0; + //des.bits.liquid_static = 0; + //des.bits.water_stagnant = 0; + //des.bits.water_salt = 0; + } + + map.setDesignationAt(pos, des); + return true; +} + command_result executePaintJob(color_ostream &out, const tiletypes_options &opts) { @@ -786,128 +891,8 @@ command_result executePaintJob(color_ostream &out, for (coord_vec::iterator iter = all_tiles.begin(); iter != all_tiles.end(); ++iter) { - MapExtras::Block *blk = map.BlockAtTile(*iter); - if (!blk) - continue; - - df::tiletype source = map.tiletypeAt(*iter); - df::tile_designation des = map.designationAt(*iter); - - // Stone painting operates on the base layer - if (paint.stone_material >= 0) - source = blk->baseTiletypeAt(*iter); - - t_matpair basemat = blk->baseMaterialAt(*iter); - - if (!filter.matches(source, des, basemat)) - { - continue; - } - - df::tiletype_shape shape = paint.shape; - if (shape == tiletype_shape::NONE) - { - shape = tileShape(source); - } - - df::tiletype_material material = paint.material; - if (material == tiletype_material::NONE) - { - material = tileMaterial(source); - } - - df::tiletype_special special = paint.special; - if (special == tiletype_special::NONE) - { - special = tileSpecial(source); - } - df::tiletype_variant variant = paint.variant; - /* - * FIXME: variant should be: - * 1. If user variant: - * 2. If user variant \belongs target variants - * 3. use user variant - * 4. Else - * 5. use variant 0 - * 6. If the source variant \belongs target variants - * 7 use source variant - * 8 ElseIf num target shape/material variants > 1 - * 9. pick one randomly - * 10.Else - * 11. use variant 0 - * - * The following variant check has been disabled because it's severely limiting - * the usefullness of the tool. - */ - /* - if (variant == tiletype_variant::NONE) - { - variant = tileVariant(source); - } - */ - // Remove direction from directionless tiles - DFHack::TileDirection direction = tileDirection(source); - if (!(material == tiletype_material::RIVER || shape == tiletype_shape::BROOK_BED || special == tiletype_special::TRACK || (shape == tiletype_shape::WALL && (material == tiletype_material::CONSTRUCTION || special == tiletype_special::SMOOTH)))) - { - direction.whole = 0; - } - - df::tiletype type = DFHack::findTileType(shape, material, variant, special, direction); - // hack for empty space - if (shape == tiletype_shape::EMPTY && material == tiletype_material::AIR && variant == tiletype_variant::VAR_1 && special == tiletype_special::NORMAL && direction.whole == 0) - { - type = tiletype::OpenSpace; - } - // make sure it's not invalid - if(type != tiletype::Void) - { - if (paint.stone_material >= 0) - { - if (!blk->setStoneAt(*iter, type, paint.stone_material, paint.vein_type, true, true)) - failures++; - } - else - map.setTiletypeAt(*iter, type); - } - - if (paint.hidden > -1) - { - des.bits.hidden = paint.hidden; - } - - if (paint.light > -1) - { - des.bits.light = paint.light; - } - - if (paint.subterranean > -1) - { - des.bits.subterranean = paint.subterranean; - } - - if (paint.skyview > -1) - { - des.bits.outside = paint.skyview; - } - - if (paint.aquifer > -1) - { - des.bits.water_table = paint.aquifer; - } - - // Remove liquid from walls, etc - if (type != (df::tiletype)-1 && !DFHack::FlowPassable(type)) - { - des.bits.flow_size = 0; - //des.bits.liquid_type = DFHack::liquid_water; - //des.bits.water_table = 0; - des.bits.flow_forbid = 0; - //des.bits.liquid_static = 0; - //des.bits.water_stagnant = 0; - //des.bits.water_salt = 0; - } - - map.setDesignationAt(*iter, des); + if (!paintTile(map, *iter, paint, filter)) + ++failures; } if (failures > 0) @@ -1135,3 +1120,59 @@ command_result df_tiletypes_here_point (color_ostream &out, vector & pa brush = old; return rv; } + +static bool setTile(color_ostream &out, df::coord pos, df::tiletype_shape shape, + df::tiletype_material material, df::tiletype_special special, + df::tiletype_variant variant) { + if (!Maps::isValidTilePos(pos)) { + out.printerr("Invalid map position: %d, %d, %d\n", pos.x, pos.y, pos.z); + return false; + } + + if (!is_valid_enum_item(shape)) { + out.printerr("Invalid shape type: %d\n", shape); + return false; + } + if (!is_valid_enum_item(material)) { + out.printerr("Invalid material type: %d\n", material); + return false; + } + if (!is_valid_enum_item(special)) { + out.printerr("Invalid special type: %d\n", special); + return false; + } + if (!is_valid_enum_item(variant)) { + out.printerr("Invalid variant type: %d\n", variant); + return false; + } + + TileType target; + target.shape = shape; + target.material = material; + target.special = special; + target.variant = variant; + + MapExtras::MapCache map; + return paintTile(map, pos, target) && map.WriteAll(); +} + +static int tiletypes_setTile(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + + df::coord pos; + Lua::CheckDFAssign(L, &pos, 1); + df::tiletype_shape shape = (df::tiletype_shape)lua_tointeger(L, 2); + df::tiletype_material material = (df::tiletype_material)lua_tointeger(L, 3); + df::tiletype_special special = (df::tiletype_special)lua_tointeger(L, 4); + df::tiletype_variant variant = (df::tiletype_variant)lua_tointeger(L, 5); + + Lua::Push(L, setTile(*out, pos, shape, material, special, variant)); + return 1; +} + +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(tiletypes_setTile), + DFHACK_LUA_END +}; From b492d92748706ef0a47f6af91ddf5c9efe668986 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 13:39:03 -0800 Subject: [PATCH 0319/2222] update docs --- docs/changelog.txt | 3 ++- docs/dev/Lua API.rst | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..871526c85 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,7 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- Fix issues with clicks "passing through" some DFHack window elements, like scrollbars +-@ Fix issues with clicks "passing through" some DFHack window elements, like scrollbars ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) @@ -50,6 +50,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Documentation ## API +- ``Buildings::containsTile()``: no longer takes a ``room`` parameter since that's not how rooms work anymore. If the building has extents, the extents will be checked. otherwise, the result just depends on whether the tile is within the building's bounding box. ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index eb05db0a1..a21ed0aba 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1933,9 +1933,11 @@ General Returns the number of tiles included by extents, or defval. -* ``dfhack.buildings.containsTile(building, x, y[, room])`` +* ``dfhack.buildings.containsTile(building, x, y)`` - Checks if the building contains the specified tile, either directly, or as room. + Checks if the building contains the specified tile. If the building contains extents, + then the extents are checked. Otherwise, returns whether the x and y map coordinates + are within the building's bounding box. * ``dfhack.buildings.hasSupport(pos,size)`` @@ -5603,6 +5605,13 @@ sort The `sort ` plugin does not export any native functions as of now. Instead, it calls Lua code to perform the actual ordering of list items. +tiletypes +========= + +* ``bool tiletypes_setTile(pos, shape, material, special, variant)`` where + the parameters are enum values from ``df.tiletype_shape``, + ``df.tiletype_material``, etc. Returns whether the conversion succeeded. + .. _xlsxreader-api: xlsxreader From 6408fdf28d1efaefdc2a267afa40c33efb0e8406 Mon Sep 17 00:00:00 2001 From: Myk Date: Fri, 27 Jan 2023 13:58:56 -0800 Subject: [PATCH 0320/2222] Update docs/about/Removed.rst --- docs/about/Removed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 174bd1185..efac2f29c 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -154,7 +154,7 @@ the Ruby library causes when used as an embedded language. .. _show_unit_syndromes: show_unit_syndromes -==== +=================== Replaced with a GUI version: `gui/unit-syndromes`. .. _warn-stuck-trees: From 18a2827385802887a429f1e267fe571ae512d80e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 14:20:21 -0800 Subject: [PATCH 0321/2222] only mark the border corner as resizable if the window is resizable --- docs/changelog.txt | 1 + library/lua/gui.lua | 8 ++++++-- library/lua/gui/widgets.lua | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..5d1508819 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/quickcmd`: now has it's own global keybinding for your convenience: Ctrl-Shift-A - Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file +-@ New borders for DFHack tool windows -- tell us what you think! ## Documentation diff --git a/library/lua/gui.lua b/library/lua/gui.lua index d870399a3..2cb84d682 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -888,13 +888,17 @@ THIN_FRAME = make_frame('Thin', false) -- for compatibility with pre-steam code GREY_LINE_FRAME = WINDOW_FRAME -function paint_frame(dc,rect,style,title,inactive, pause_forced) +function paint_frame(dc,rect,style,title,inactive,pause_forced,resizable) local pen = style.frame_pen local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2 dscreen.paintTile(style.lt_frame_pen or pen, x1, y1) dscreen.paintTile(style.rt_frame_pen or pen, x2, y1) dscreen.paintTile(style.lb_frame_pen or pen, x1, y2) - dscreen.paintTile(style.rb_frame_pen or pen, x2, y2) + local rb_frame_pen = style.rb_frame_pen + if rb_frame_pen == WINDOW_FRAME.rb_frame_pen and not resizable then + rb_frame_pen = PANEL_FRAME.rb_frame_pen + end + dscreen.paintTile(rb_frame_pen or pen, x2, y2) dscreen.fillRect(style.t_frame_pen or style.h_frame_pen or pen,x1+1,y1,x2-1,y1) dscreen.fillRect(style.b_frame_pen or style.h_frame_pen or pen,x1+1,y2,x2-1,y2) dscreen.fillRect(style.l_frame_pen or style.v_frame_pen or pen,x1,y1+1,x1,y2-1) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 473f09db9..653107179 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -502,7 +502,7 @@ function Panel:onRenderFrame(dc, rect) and not self.parent_view:hasFocus() local pause_forced = self.parent_view and self.parent_view.force_pause gui.paint_frame(dc, rect, self.frame_style, self.frame_title, inactive, - pause_forced) + pause_forced, self.resizable) if self.kbd_get_pos then local pos = self.kbd_get_pos() local pen = to_pen{fg=COLOR_GREEN, bg=COLOR_BLACK} From 9080af6948fdc30ca2616219bb90bf903e28b2dd Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:27:41 +0000 Subject: [PATCH 0322/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 7050d2338..cf659eb0a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7050d23387310660488b42483e6f03ba17ba6711 +Subproject commit cf659eb0a0bc064208fcfeb4695ecee22e253d9f diff --git a/scripts b/scripts index f4058184a..edd19a446 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f4058184a6716ef921e6019d1386354f59fdb0a8 +Subproject commit edd19a446fb1c38fb77a2fe1bac27ef3318e9823 From f47adba3d12ecd5d9011eda7265edf6b0bb3a231 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 14:45:56 -0800 Subject: [PATCH 0323/2222] fix underscores-to-dashes --- docs/about/Removed.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index efac2f29c..d30a00f00 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -151,9 +151,9 @@ ruby Support for the Ruby language in DFHack scripts was removed due to the issues the Ruby library causes when used as an embedded language. -.. _show_unit_syndromes: +.. _show-unit-syndromes: -show_unit_syndromes +show-unit-syndromes =================== Replaced with a GUI version: `gui/unit-syndromes`. From fb129c1a16ed5feff38d84d0f1db5c55ce661867 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Fri, 27 Jan 2023 15:12:02 -0800 Subject: [PATCH 0324/2222] Fix permissions in ccache dir I think this affects basically nobody but me since everyone else seems to run docker rootless. --- build/build-win64-from-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index eb366e83e..2ba053434 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -29,7 +29,7 @@ if [[ $(id -u) -eq 0 ]]; then # If this was run using sudo, let's make sure the directories are owned by the # real user (and set the BUILDER_UID to it) builder_uid=$SUDO_UID - chown $builder_uid win64-cross win64-cross/output + chown -R $builder_uid win64-cross fi # Assumes you built a container image called dfhack-build-msvc from From 37b5be1f357cb27ada067a3847b592fd4cf70904 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Fri, 27 Jan 2023 19:46:56 -0500 Subject: [PATCH 0325/2222] Implement autoslab engraving feature (#1) * Initial autoslab implementation --- docs/changelog.txt | 1 + docs/plugins/autoslab.rst | 20 +++ plugins/CMakeLists.txt | 1 + plugins/autoslab.cpp | 267 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+) create mode 100644 docs/plugins/autoslab.rst create mode 100644 plugins/autoslab.cpp diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..d4014c788 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `autoslab`: Automatically create work orders to engrave slabs for ghostly dwarves. ## Fixes - Fix issues with clicks "passing through" some DFHack window elements, like scrollbars diff --git a/docs/plugins/autoslab.rst b/docs/plugins/autoslab.rst new file mode 100644 index 000000000..e5db146cb --- /dev/null +++ b/docs/plugins/autoslab.rst @@ -0,0 +1,20 @@ +autoslab +======== + +.. dfhack-tool:: + :summary: Automatically engrave slabs for ghostly citizens! + :tags: untested fort auto workorders + :no-command: + +Automatically queue orders to engrave slabs of existing ghosts. Will only queue +an order if there is no existing slab with that unit's memorial engraved and +there is not already an existing work order to engrave a slab for that unit + +Usage +----- + +``enable autoslab`` + Enables the plugin and starts checking for ghosts that need memorializing. + +``disable autoslab`` + Disables the plugin. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 284d1788e..41b3ad542 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -134,6 +134,7 @@ dfhack_plugin(misery misery.cpp) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) +dfhack_plugin(autoslab autoslab.cpp) dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static lua) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp new file mode 100644 index 000000000..4dcfcecff --- /dev/null +++ b/plugins/autoslab.cpp @@ -0,0 +1,267 @@ +/* Simple plugin to check for ghosts and automatically queue jobs to engrave slabs for them. + * + * Enhancement idea: Queue up a ConstructSlab job, then link the engrave slab job to it. Avoids need to have slabs in stockpiles + * Would require argument parsing, specifying materials + * Enhancement idea: Automatically place the slab. This seems like a tricky problem but maybe solveable with named zones? + * Might be made obsolete by people just using buildingplan to pre-place plans for slab? + * Enhancement idea: Optionally enable autoengraving for pets. + * Enhancement idea: Try to get ahead of ghosts by autoengraving for dead dwarves with no remains. + */ + +#include "Core.h" +#include "Debug.h" +#include "PluginManager.h" + +#include "modules/Persistence.h" +#include "modules/World.h" +#include "modules/Translation.h" + +#include "df/manager_order.h" +#include "df/world.h" +#include "df/plotinfost.h" +#include "df/unit.h" +#include "df/item.h" +#include "df/historical_figure.h" + +using namespace DFHack; + +static command_result autoslab(color_ostream &out, std::vector ¶meters); + +DFHACK_PLUGIN("autoslab"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +REQUIRE_GLOBAL(world); + +// logging levels can be dynamically controlled with the `debugfilter` command. +namespace DFHack +{ + // for configuration-related logging + DBG_DECLARE(autoslab, status, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(autoslab, cycle, DebugCategory::LINFO); +} + +static const auto CONFIG_KEY = std::string(plugin_name) + "/config"; +static PersistentDataItem config; +enum ConfigValues +{ + CONFIG_IS_ENABLED = 0, + CONFIG_CYCLE_TICKS = 1, +}; +static int get_config_val(int index) +{ + if (!config.isValid()) + return -1; + return config.ival(index); +} +static bool get_config_bool(int index) +{ + return get_config_val(index) == 1; +} +static void set_config_val(int index, int value) +{ + if (config.isValid()) + config.ival(index) = value; +} +static void set_config_bool(int index, bool value) +{ + set_config_val(index, value ? 1 : 0); +} + +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, std::vector ¶meters); +static void do_cycle(color_ostream &out); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +{ + DEBUG(status, out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Automatically engrave slabs of ghostly citizens!", + do_command)); + + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (!Core::getInstance().isWorldLoaded()) + { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + if (enable != is_enabled) + { + is_enabled = enable; + DEBUG(status, out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); + set_config_bool(CONFIG_IS_ENABLED, is_enabled); + } + else + { + DEBUG(status, out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream &out) +{ + DEBUG(status, out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} + +DFhackCExport command_result plugin_load_data(color_ostream &out) +{ + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) + { + DEBUG(status, out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(CONFIG_IS_ENABLED, is_enabled); + set_config_val(CONFIG_CYCLE_TICKS, 1200); + } + + // we have to copy our enabled flag into the global plugin variable, but + // all the other state we can directly read/modify from the persistent + // data structure. + is_enabled = get_config_bool(CONFIG_IS_ENABLED); + DEBUG(status, out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + if (event == DFHack::SC_WORLD_UNLOADED) + { + if (is_enabled) + { + DEBUG(status, out).print("world unloaded; disabling %s\n", plugin_name); + is_enabled = false; + } + } + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) +{ + CoreSuspender suspend; + if (is_enabled && world->frame_counter - cycle_timestamp >= get_config_val(CONFIG_CYCLE_TICKS)) + do_cycle(out); + return CR_OK; +} + +static command_result do_command(color_ostream &out, std::vector ¶meters) +{ + // be sure to suspend the core if any DF state is read or modified + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) + { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + // TODO: decide what, if any configuration should be here + + return CR_OK; +} + +// Name functions taken from manipulator.cpp +static std::string get_first_name(df::unit *unit) +{ + return Translation::capitalize(unit->name.first_name); +} + +static std::string get_last_name(df::unit *unit) +{ + df::language_name name = unit->name; + std::string ret = ""; + for (int i = 0; i < 2; i++) + { + if (name.words[i] >= 0) + ret += *world->raws.language.translations[name.language]->words[name.words[i]]; + } + return Translation::capitalize(ret); +} + +// Couldn't figure out any other way to do this besides look for the dwarf name in +// the slab item description. +// Ideally, we could get the historical figure id from the slab but I didn't +// see anything like that in the item struct. This seems to work based on testing. +// Confirmed nicknames don't show up in engraved slab names, so this should probably work okay +bool engravedSlabItemExists(df::unit *unit, std::vector slabs) +{ + for (auto slab : slabs) + { + std::string desc = ""; + slab->getItemDescription(&desc, 0); + auto fullName = get_first_name(unit) + " " + get_last_name(unit); + if (desc.find(fullName) != std::string::npos) + return true; + } + + return false; +} + +// Queue up a single order to engrave the slab for the given unit +static void createSlabJob(df::unit *unit) +{ + auto next_id = world->manager_order_next_id++; + auto order = new df::manager_order(); + + order->id = next_id; + order->job_type = df::job_type::EngraveSlab; + order->hist_figure_id = unit->hist_figure_id; + order->amount_left = 1; + order->amount_total = 1; + world->manager_orders.push_back(order); +} + +static void checkslabs(color_ostream &out) +{ + // Get existing orders for slab engraving as map hist_figure_id -> order ID + std::map histToJob; + for (auto order : world->manager_orders) + { + if (order->job_type == df::job_type::EngraveSlab) + histToJob[order->hist_figure_id] = order->id; + } + + // Get list of engraved slab items on map + std::vector engravedSlabs; + std::copy_if(world->items.all.begin(), world->items.all.end(), + std::back_inserter(engravedSlabs), + [](df::item *item) + { return item->getType() == df::item_type::SLAB && item->getSlabEngravingType() == df::slab_engraving_type::Memorial; }); + + // Build list of ghosts + std::vector ghosts; + std::copy_if(world->units.all.begin(), world->units.all.end(), + std::back_inserter(ghosts), + [](const auto &unit) + { return unit->flags3.bits.ghostly; }); + + for (auto ghost : ghosts) + { + // Only create a job is the map has no existing jobs for that historical figure or no existing engraved slabs + if (histToJob.count(ghost->hist_figure_id) == 0 && !engravedSlabItemExists(ghost, engravedSlabs)) + { + createSlabJob(ghost); + auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); + out.print("Added slab order for ghost %s\n", fullName.c_str()); + } + } +} + +static void do_cycle(color_ostream &out) +{ + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + checkslabs(out); +} From b9b8b3665277ed17cc2a4802c67d3e60205888d1 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Fri, 27 Jan 2023 20:08:33 -0500 Subject: [PATCH 0326/2222] Sort header includes per guidelines --- plugins/autoslab.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 4dcfcecff..5f7f25f6f 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -13,15 +13,15 @@ #include "PluginManager.h" #include "modules/Persistence.h" -#include "modules/World.h" #include "modules/Translation.h" +#include "modules/World.h" +#include "df/historical_figure.h" +#include "df/item.h" #include "df/manager_order.h" -#include "df/world.h" #include "df/plotinfost.h" #include "df/unit.h" -#include "df/item.h" -#include "df/historical_figure.h" +#include "df/world.h" using namespace DFHack; From 277a37a12f8426d7e7044fee1768b4a79eb35c26 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 28 Jan 2023 07:13:34 +0000 Subject: [PATCH 0327/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index edd19a446..3193ad5c3 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit edd19a446fb1c38fb77a2fe1bac27ef3318e9823 +Subproject commit 3193ad5c354a18cb55d41792379894b7b054c7bc From 70b15ffcd1a48e1656f8cdbb1df14fe495ac4d17 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Fri, 27 Jan 2023 19:46:56 -0500 Subject: [PATCH 0328/2222] Implement autoslab engraving feature (#1) * Initial autoslab implementation --- docs/changelog.txt | 1 + docs/plugins/autoslab.rst | 20 +++ plugins/CMakeLists.txt | 1 + plugins/autoslab.cpp | 267 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+) create mode 100644 docs/plugins/autoslab.rst create mode 100644 plugins/autoslab.cpp diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..d4014c788 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `autoslab`: Automatically create work orders to engrave slabs for ghostly dwarves. ## Fixes - Fix issues with clicks "passing through" some DFHack window elements, like scrollbars diff --git a/docs/plugins/autoslab.rst b/docs/plugins/autoslab.rst new file mode 100644 index 000000000..e5db146cb --- /dev/null +++ b/docs/plugins/autoslab.rst @@ -0,0 +1,20 @@ +autoslab +======== + +.. dfhack-tool:: + :summary: Automatically engrave slabs for ghostly citizens! + :tags: untested fort auto workorders + :no-command: + +Automatically queue orders to engrave slabs of existing ghosts. Will only queue +an order if there is no existing slab with that unit's memorial engraved and +there is not already an existing work order to engrave a slab for that unit + +Usage +----- + +``enable autoslab`` + Enables the plugin and starts checking for ghosts that need memorializing. + +``disable autoslab`` + Disables the plugin. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 284d1788e..41b3ad542 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -134,6 +134,7 @@ dfhack_plugin(misery misery.cpp) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) +dfhack_plugin(autoslab autoslab.cpp) dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static lua) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp new file mode 100644 index 000000000..4dcfcecff --- /dev/null +++ b/plugins/autoslab.cpp @@ -0,0 +1,267 @@ +/* Simple plugin to check for ghosts and automatically queue jobs to engrave slabs for them. + * + * Enhancement idea: Queue up a ConstructSlab job, then link the engrave slab job to it. Avoids need to have slabs in stockpiles + * Would require argument parsing, specifying materials + * Enhancement idea: Automatically place the slab. This seems like a tricky problem but maybe solveable with named zones? + * Might be made obsolete by people just using buildingplan to pre-place plans for slab? + * Enhancement idea: Optionally enable autoengraving for pets. + * Enhancement idea: Try to get ahead of ghosts by autoengraving for dead dwarves with no remains. + */ + +#include "Core.h" +#include "Debug.h" +#include "PluginManager.h" + +#include "modules/Persistence.h" +#include "modules/World.h" +#include "modules/Translation.h" + +#include "df/manager_order.h" +#include "df/world.h" +#include "df/plotinfost.h" +#include "df/unit.h" +#include "df/item.h" +#include "df/historical_figure.h" + +using namespace DFHack; + +static command_result autoslab(color_ostream &out, std::vector ¶meters); + +DFHACK_PLUGIN("autoslab"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +REQUIRE_GLOBAL(world); + +// logging levels can be dynamically controlled with the `debugfilter` command. +namespace DFHack +{ + // for configuration-related logging + DBG_DECLARE(autoslab, status, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(autoslab, cycle, DebugCategory::LINFO); +} + +static const auto CONFIG_KEY = std::string(plugin_name) + "/config"; +static PersistentDataItem config; +enum ConfigValues +{ + CONFIG_IS_ENABLED = 0, + CONFIG_CYCLE_TICKS = 1, +}; +static int get_config_val(int index) +{ + if (!config.isValid()) + return -1; + return config.ival(index); +} +static bool get_config_bool(int index) +{ + return get_config_val(index) == 1; +} +static void set_config_val(int index, int value) +{ + if (config.isValid()) + config.ival(index) = value; +} +static void set_config_bool(int index, bool value) +{ + set_config_val(index, value ? 1 : 0); +} + +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, std::vector ¶meters); +static void do_cycle(color_ostream &out); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +{ + DEBUG(status, out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Automatically engrave slabs of ghostly citizens!", + do_command)); + + return CR_OK; +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (!Core::getInstance().isWorldLoaded()) + { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + if (enable != is_enabled) + { + is_enabled = enable; + DEBUG(status, out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); + set_config_bool(CONFIG_IS_ENABLED, is_enabled); + } + else + { + DEBUG(status, out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream &out) +{ + DEBUG(status, out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} + +DFhackCExport command_result plugin_load_data(color_ostream &out) +{ + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) + { + DEBUG(status, out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(CONFIG_IS_ENABLED, is_enabled); + set_config_val(CONFIG_CYCLE_TICKS, 1200); + } + + // we have to copy our enabled flag into the global plugin variable, but + // all the other state we can directly read/modify from the persistent + // data structure. + is_enabled = get_config_bool(CONFIG_IS_ENABLED); + DEBUG(status, out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + if (event == DFHack::SC_WORLD_UNLOADED) + { + if (is_enabled) + { + DEBUG(status, out).print("world unloaded; disabling %s\n", plugin_name); + is_enabled = false; + } + } + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) +{ + CoreSuspender suspend; + if (is_enabled && world->frame_counter - cycle_timestamp >= get_config_val(CONFIG_CYCLE_TICKS)) + do_cycle(out); + return CR_OK; +} + +static command_result do_command(color_ostream &out, std::vector ¶meters) +{ + // be sure to suspend the core if any DF state is read or modified + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) + { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + // TODO: decide what, if any configuration should be here + + return CR_OK; +} + +// Name functions taken from manipulator.cpp +static std::string get_first_name(df::unit *unit) +{ + return Translation::capitalize(unit->name.first_name); +} + +static std::string get_last_name(df::unit *unit) +{ + df::language_name name = unit->name; + std::string ret = ""; + for (int i = 0; i < 2; i++) + { + if (name.words[i] >= 0) + ret += *world->raws.language.translations[name.language]->words[name.words[i]]; + } + return Translation::capitalize(ret); +} + +// Couldn't figure out any other way to do this besides look for the dwarf name in +// the slab item description. +// Ideally, we could get the historical figure id from the slab but I didn't +// see anything like that in the item struct. This seems to work based on testing. +// Confirmed nicknames don't show up in engraved slab names, so this should probably work okay +bool engravedSlabItemExists(df::unit *unit, std::vector slabs) +{ + for (auto slab : slabs) + { + std::string desc = ""; + slab->getItemDescription(&desc, 0); + auto fullName = get_first_name(unit) + " " + get_last_name(unit); + if (desc.find(fullName) != std::string::npos) + return true; + } + + return false; +} + +// Queue up a single order to engrave the slab for the given unit +static void createSlabJob(df::unit *unit) +{ + auto next_id = world->manager_order_next_id++; + auto order = new df::manager_order(); + + order->id = next_id; + order->job_type = df::job_type::EngraveSlab; + order->hist_figure_id = unit->hist_figure_id; + order->amount_left = 1; + order->amount_total = 1; + world->manager_orders.push_back(order); +} + +static void checkslabs(color_ostream &out) +{ + // Get existing orders for slab engraving as map hist_figure_id -> order ID + std::map histToJob; + for (auto order : world->manager_orders) + { + if (order->job_type == df::job_type::EngraveSlab) + histToJob[order->hist_figure_id] = order->id; + } + + // Get list of engraved slab items on map + std::vector engravedSlabs; + std::copy_if(world->items.all.begin(), world->items.all.end(), + std::back_inserter(engravedSlabs), + [](df::item *item) + { return item->getType() == df::item_type::SLAB && item->getSlabEngravingType() == df::slab_engraving_type::Memorial; }); + + // Build list of ghosts + std::vector ghosts; + std::copy_if(world->units.all.begin(), world->units.all.end(), + std::back_inserter(ghosts), + [](const auto &unit) + { return unit->flags3.bits.ghostly; }); + + for (auto ghost : ghosts) + { + // Only create a job is the map has no existing jobs for that historical figure or no existing engraved slabs + if (histToJob.count(ghost->hist_figure_id) == 0 && !engravedSlabItemExists(ghost, engravedSlabs)) + { + createSlabJob(ghost); + auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); + out.print("Added slab order for ghost %s\n", fullName.c_str()); + } + } +} + +static void do_cycle(color_ostream &out) +{ + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + checkslabs(out); +} From 1a3d2c0a2d5fcfb42bc5946762844ce4345e1336 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Fri, 27 Jan 2023 20:08:33 -0500 Subject: [PATCH 0329/2222] Sort header includes per guidelines --- plugins/autoslab.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 4dcfcecff..5f7f25f6f 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -13,15 +13,15 @@ #include "PluginManager.h" #include "modules/Persistence.h" -#include "modules/World.h" #include "modules/Translation.h" +#include "modules/World.h" +#include "df/historical_figure.h" +#include "df/item.h" #include "df/manager_order.h" -#include "df/world.h" #include "df/plotinfost.h" #include "df/unit.h" -#include "df/item.h" -#include "df/historical_figure.h" +#include "df/world.h" using namespace DFHack; From 09c71bceebb6ea9ae698f068a7d39eeadb03b879 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sat, 28 Jan 2023 10:06:48 -0500 Subject: [PATCH 0330/2222] Fix dig plugin and enable building it --- plugins/dig.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 371dd588f..879d0dd52 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -9,7 +9,6 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" -#include "uicommon.h" #include "modules/Gui.h" #include "modules/MapCache.h" @@ -213,7 +212,7 @@ bool lineY (MapExtras::MapCache & MCache, int32_t parse_priority(color_ostream &out, vector ¶meters) { - int32_t default_priority = game->designation.priority; + int32_t default_priority = game->main_interface.designation.priority; for (auto it = parameters.begin(); it != parameters.end(); ++it) { From 863ca2ca65ffe1d9f7367ade656ef97f2174a2a1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 08:02:46 -0800 Subject: [PATCH 0331/2222] solve the inception problem where hideGuard smashes the viewscreen stack with multiple insertions for the same screen --- library/include/modules/Screen.h | 5 +-- library/modules/Screen.cpp | 54 ++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 9417d60e9..48f34888f 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -318,9 +318,10 @@ namespace DFHack static const int RESTORE_AT_TOP = 0x1; private: - void extract(df::viewscreen*); - void merge(df::viewscreen*); + void extract(); + void merge(); df::viewscreen* screen; + df::viewscreen* prev_parent; int flags; }; } diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 3ece27b78..f056a8578 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -527,43 +527,49 @@ void Screen::raise(df::viewscreen *screen) { namespace DFHack { namespace Screen { Hide::Hide(df::viewscreen* screen, int flags) : - screen{screen}, flags{flags} -{ - extract(screen); + screen{screen}, prev_parent{nullptr}, flags{flags} { + if (!screen) + return; + + // don't extract a screen that's not even in the stack + if (screen->parent) + extract(); } -Hide::~Hide() -{ +Hide::~Hide() { if (screen) - merge(screen); + merge(); } -void Hide::extract(df::viewscreen* a) -{ - df::viewscreen* ap = a->parent; - df::viewscreen* ac = a->child; +void Hide::extract() { + prev_parent = screen->parent; + df::viewscreen *prev_child = screen->child; - ap->child = ac; - if (ac) ac->parent = ap; - else Core::getInstance().top_viewscreen = ap; + screen->parent = nullptr; + screen->child = nullptr; + + prev_parent->child = prev_child; + if (prev_child) prev_child->parent = prev_parent; + else Core::getInstance().top_viewscreen = prev_parent; } -void Hide::merge(df::viewscreen* a) -{ +void Hide::merge() { + if (screen->parent) { + // we're somehow back on the stack; do nothing + return; + } + if (flags & RESTORE_AT_TOP) { - a->parent = NULL; - a->child = NULL; - Screen::show(std::unique_ptr(a)); + Screen::show(std::unique_ptr(screen)); return; } - df::viewscreen* ap = a->parent; - df::viewscreen* ac = a->parent->child; + df::viewscreen* new_child = prev_parent->child; - ap->child = a; - a->child = ac; - if (ac) ac->parent = a; - else Core::getInstance().top_viewscreen = a; + prev_parent->child = screen; + screen->child = new_child; + if (new_child) new_child->parent = screen; + else Core::getInstance().top_viewscreen = screen; } } } From 25ff01549747b2fbd4456f1823e624b491990b3d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 08:04:36 -0800 Subject: [PATCH 0332/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..49e3afc70 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - Fix issues with clicks "passing through" some DFHack window elements, like scrollbars +-@ ``Screen``: allow `gui/launcher` and `gui/quickcmd` to launch themselves without hanging the game ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) From f12ca33c0b801f095f9ca9e7f6d7755e60dbe3c5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 08:05:37 -0800 Subject: [PATCH 0333/2222] ensure DF screens don't get "stuck" when DFHack tool windows are on top --- library/lua/gui.lua | 19 +++++++++++++++---- library/modules/Screen.cpp | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index d870399a3..cf25b50cf 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -706,7 +706,7 @@ ZScreen.ATTRS{ function ZScreen:init() self.saved_pause_state = df.global.pause_state - if self.initial_pause then + if self.initial_pause and dfhack.isMapLoaded() then df.global.pause_state = true end self.defocused = false @@ -714,19 +714,30 @@ end function ZScreen:dismiss() ZScreen.super.dismiss(self) - if self.force_pause or self.initial_pause then + if (self.force_pause or self.initial_pause) and dfhack.isMapLoaded() then -- never go from unpaused to paused, just from paused to unpaused df.global.pause_state = df.global.pause_state and self.saved_pause_state end end +local NO_LOGIC_SCREENS = utils.invert{ + 'viewscreen_loadgamest', + 'viewscreen_export_regionst', + 'viewscreen_choose_game_typest', +} + -- this is necessary for middle-click map scrolling to function function ZScreen:onIdle() - if self.force_pause then + if self.force_pause and dfhack.isMapLoaded() then df.global.pause_state = true end if self._native and self._native.parent then - self._native.parent:logic() + local vs_name = getmetatable(dfhack.gui.getDFViewscreen(true)) + if NO_LOGIC_SCREENS[vs_name] then + self.force_pause = true + else + self._native.parent:logic() + end end end diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 3ece27b78..00a98e489 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -710,6 +710,21 @@ void dfhack_viewscreen::logic() // Various stuff works poorly unless always repainting Screen::invalidate(); + + // if the DF screen immediately beneath the DFHack viewscreens is waiting to + // be dismissed, raise it to the top so DF never gets stuck + auto *p = parent; + while (p) { + bool is_df_screen = !is_instance(p); + auto *next_p = p->parent; + if (is_df_screen && Screen::isDismissed(p)) { + DEBUG(screen).print("raising dismissed DF viewscreen %p\n", p); + Screen::raise(p); + } + if (is_df_screen) + break; + p = next_p; + } } void dfhack_viewscreen::render() @@ -800,6 +815,13 @@ int dfhack_lua_viewscreen::do_destroy(lua_State *L) auto self = get_self(L); if (!self) return 0; + if (!Screen::isDismissed(self)) { + WARNING(screen).print("DFHack screen was destroyed before it was dismissed\n"); + WARNING(screen).print("Please tell the DFHack team which DF screen you were just viewing\n"); + // run skipped onDismiss cleanup logic + Screen::dismiss(self); + } + lua_pushnil(L); lua_rawsetp(L, LUA_REGISTRYINDEX, self); From 65aa772a5b82eb6086d5b6e389f64e6f1dbcb830 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 08:07:29 -0800 Subject: [PATCH 0334/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..d10787f13 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +-@ Ensure DF windows don't get "stuck" on transitions when DFHack tool windows are visible. Instead, those DF screens are force-paused while DFHack windows are visible so the player can close them first and not corrupt the screen sequence. The "force pause" indicator will appear on these DFHack windows to indicate what is happening. - Fix issues with clicks "passing through" some DFHack window elements, like scrollbars ## Misc Improvements From a931ca692da906f9f25a365af0602239aa88ddfb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 08:20:55 -0800 Subject: [PATCH 0335/2222] Fix debug level typo --- library/modules/Screen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 00a98e489..7a5cbd161 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -816,8 +816,8 @@ int dfhack_lua_viewscreen::do_destroy(lua_State *L) if (!self) return 0; if (!Screen::isDismissed(self)) { - WARNING(screen).print("DFHack screen was destroyed before it was dismissed\n"); - WARNING(screen).print("Please tell the DFHack team which DF screen you were just viewing\n"); + WARN(screen).print("DFHack screen was destroyed before it was dismissed\n"); + WARN(screen).print("Please tell the DFHack team which DF screen you were just viewing\n"); // run skipped onDismiss cleanup logic Screen::dismiss(self); } From 114bc2a576bc5d32be0ab4381689a0de667c29c9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 08:57:24 -0800 Subject: [PATCH 0336/2222] also lock down the world map --- library/lua/gui.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index cf25b50cf..339894f2f 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -724,6 +724,7 @@ local NO_LOGIC_SCREENS = utils.invert{ 'viewscreen_loadgamest', 'viewscreen_export_regionst', 'viewscreen_choose_game_typest', + 'viewscreen_worldst', } -- this is necessary for middle-click map scrolling to function @@ -735,6 +736,8 @@ function ZScreen:onIdle() local vs_name = getmetatable(dfhack.gui.getDFViewscreen(true)) if NO_LOGIC_SCREENS[vs_name] then self.force_pause = true + self.pass_movement_keys = false + self.pass_mouse_clicks = false else self._native.parent:logic() end From 21ebd778b1774716b68022058f59375ce3a6e3ec Mon Sep 17 00:00:00 2001 From: gearsix Date: Sat, 28 Jan 2023 22:05:36 +0000 Subject: [PATCH 0337/2222] updated changelog --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8d84e3cff..3db740589 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - Fix issues with clicks "passing through" some DFHack window elements, like scrollbars +- `plugin/getplants`: tree are now designated correctly ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) @@ -46,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/quickcmd`: now has it's own global keybinding for your convenience: Ctrl-Shift-A - Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file +- `plugin/getplants`: ID values will now be accepted regardless of case ## Documentation From 6dc74f201df1b0ef3f63d6728eb614bd1bf9a1e9 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 29 Jan 2023 07:13:24 +0000 Subject: [PATCH 0338/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index cf659eb0a..a3b6cd550 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit cf659eb0a0bc064208fcfeb4695ecee22e253d9f +Subproject commit a3b6cd5507753207ce28fe0872c98d6434d58a90 diff --git a/scripts b/scripts index 3193ad5c3..bf914ddd8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3193ad5c354a18cb55d41792379894b7b054c7bc +Subproject commit bf914ddd8c58011368f72172f9d158d0043c61fc From 211d18717ce07401adfe97ab298a68089b530e2a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 23:34:23 -0800 Subject: [PATCH 0339/2222] test against type instead of string --- library/lua/gui.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index c44acee9e..6630f9cba 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -720,12 +720,18 @@ function ZScreen:dismiss() end end -local NO_LOGIC_SCREENS = utils.invert{ +local NO_LOGIC_SCREENS = { 'viewscreen_loadgamest', 'viewscreen_export_regionst', 'viewscreen_choose_game_typest', 'viewscreen_worldst', } +for _,v in ipairs(NO_LOGIC_SCREENS) do + if not df[v] then + error('invalid class name: ' .. v) + end + NO_LOGIC_SCREENS[df[v]] = true +end -- this is necessary for middle-click map scrolling to function function ZScreen:onIdle() @@ -733,8 +739,8 @@ function ZScreen:onIdle() df.global.pause_state = true end if self._native and self._native.parent then - local vs_name = getmetatable(dfhack.gui.getDFViewscreen(true)) - if NO_LOGIC_SCREENS[vs_name] then + local vs_type = dfhack.gui.getDFViewscreen(true)._type + if NO_LOGIC_SCREENS[vs_type] then self.force_pause = true self.pass_movement_keys = false self.pass_mouse_clicks = false From c2d6debcd8bac2a93ba9e17a918cff601a9b68bd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 28 Jan 2023 23:59:36 -0800 Subject: [PATCH 0340/2222] add on/off light textures and expose to lua --- data/art/on-off.png | Bin 0 -> 273 bytes library/LuaApi.cpp | 1 + library/include/modules/Textures.h | 5 +++++ library/modules/Textures.cpp | 7 +++++++ 4 files changed, 13 insertions(+) create mode 100644 data/art/on-off.png diff --git a/data/art/on-off.png b/data/art/on-off.png new file mode 100644 index 0000000000000000000000000000000000000000..2b731e1c6b89ab99485a34c31cf65d6b9cd949b1 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~e!2~3qw63}aq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c70vW?aSW-rl{97I#6$&F1ICZ)-u%=4nO3>WISWkSo_JV~ zZ;NB%SI+h9|Lg0Gn3Z;a&TsT!<576T Date: Sun, 29 Jan 2023 00:35:12 -0800 Subject: [PATCH 0341/2222] use new icons in pathable --- plugins/pathable.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index b66099a1f..dd26a712f 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -1,6 +1,7 @@ #include "modules/Gui.h" #include "modules/Maps.h" #include "modules/Screen.h" +#include "modules/Textures.h" #include "Debug.h" #include "LuaTools.h" @@ -38,6 +39,11 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { long pathable_tile_texpos = 779; long unpathable_tile_texpos = 782; + long on_off_texpos = Textures::getOnOffTexposStart(); + if (on_off_texpos > 0) { + pathable_tile_texpos = on_off_texpos + 0; + unpathable_tile_texpos = on_off_texpos + 1; + } auto dims = Gui::getDwarfmodeViewDims().map(); for (int y = dims.first.y; y <= dims.second.y; ++y) { From 4c455224f9d45f0e6dca27e35820d575e03dea67 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 27 Jan 2023 14:57:45 -0800 Subject: [PATCH 0342/2222] make initial pause configurable --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 7 +++++-- library/lua/gui.lua | 10 +++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5eb73c15b..215d5de5e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) +- You can now configure whether DFHack tool windows should pause the game by default - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `gui/launcher`: sped up initialization time for faster load of the UI - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index a21ed0aba..ec162f6ec 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4182,9 +4182,12 @@ ZScreen provides the following functions: ZScreen subclasses can set the following attributes: -* ``initial_pause`` (default: ``true``) +* ``initial_pause`` (default: ``DEFAULT_INITIAL_PAUSE``) - Whether to pause the game when the ZScreen is shown. + Whether to pause the game when the ZScreen is shown. ``DEFAULT_INITIAL_PAUSE`` + defaults to ``true`` but can be set via running a command like:: + + :lua require('gui.widgets').DEFAULT_INITIAL_PAUSE = false * ``force_pause`` (default: ``false``) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 2cb84d682..233496b82 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -693,17 +693,25 @@ end -- Z-order swapping screen -- ----------------------------- +DEFAULT_INITIAL_PAUSE = true + local zscreen_inhibit_mouse_l = false ZScreen = defclass(ZScreen, Screen) ZScreen.ATTRS{ - initial_pause=true, + initial_pause=DEFAULT_NIL, force_pause=false, pass_pause=true, pass_movement_keys=false, pass_mouse_clicks=true, } +function ZScreen:preinit(args) + if args.initial_pause == nil then + args.initial_pause = DEFAULT_INITIAL_PAUSE + end +end + function ZScreen:init() self.saved_pause_state = df.global.pause_state if self.initial_pause then From 8d4990b8fd4808b9b2d37ebfe10b814fc51267ee Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 29 Jan 2023 00:55:49 -0800 Subject: [PATCH 0343/2222] don't autorefresh the enableable tools list it just takes too long (>1s) to refresh every time. manual refresh with script_manager.reload() is still available for devs who need it --- library/lua/script-manager.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index c1b8c80d5..cc5dd9fe3 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -24,7 +24,7 @@ function foreach_module_script(cb) end end -local enabled_map = {} +local enabled_map = nil local function process_script(env_name, env) local global_name = 'isEnabled' @@ -44,10 +44,14 @@ function reload() foreach_module_script(process_script) end +local function ensure_loaded() + if not enabled_map then + reload() + end +end + function list() - -- call reload every time we list to make sure we get scripts that have - -- just been added - reload() + ensure_loaded() for name,fn in pairs(enabled_map) do print(('%21s %-3s'):format(name..':', fn() and 'on' or 'off')) end From 549ccfa3c0b9d25b05ecf4b9a81561af4591a344 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 29 Jan 2023 10:53:05 -0800 Subject: [PATCH 0344/2222] Apply suggestions from code review --- docs/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 6db8e0616..f7fe08455 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,7 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ Fix issues with clicks "passing through" some DFHack window elements, like scrollbars -- `plugin/getplants`: tree are now designated correctly +- `getplants`: tree are now designated correctly ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) @@ -47,7 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/quickcmd`: now has it's own global keybinding for your convenience: Ctrl-Shift-A - Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file -- `plugin/getplants`: ID values will now be accepted regardless of case +- `getplants`: ID values will now be accepted regardless of case -@ New borders for DFHack tool windows -- tell us what you think! ## Documentation From 9141c7e008b1c784bed3465d0226cd1f2f5bc6bd Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 29 Jan 2023 11:25:06 -0800 Subject: [PATCH 0345/2222] remove duplicate args --- build/win64/generate-MSVC-gui.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/win64/generate-MSVC-gui.bat b/build/win64/generate-MSVC-gui.bat index cd22c4e75..71bc1dd14 100644 --- a/build/win64/generate-MSVC-gui.bat +++ b/build/win64/generate-MSVC-gui.bat @@ -1,5 +1,5 @@ IF EXIST DF_PATH.txt SET /P _DF_PATH= Date: Sun, 29 Jan 2023 18:16:26 -0500 Subject: [PATCH 0346/2222] Address review comments, hopefully fix linux build --- docs/plugins/autoslab.rst | 4 ++-- plugins/autoslab.cpp | 35 ++++++----------------------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/docs/plugins/autoslab.rst b/docs/plugins/autoslab.rst index e5db146cb..5e61e0938 100644 --- a/docs/plugins/autoslab.rst +++ b/docs/plugins/autoslab.rst @@ -2,8 +2,8 @@ autoslab ======== .. dfhack-tool:: - :summary: Automatically engrave slabs for ghostly citizens! - :tags: untested fort auto workorders + :summary: Automatically engrave slabs for ghostly citizens. + :tags: fort auto workorders :no-command: Automatically queue orders to engrave slabs of existing ghosts. Will only queue diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 5f7f25f6f..4e9220cf2 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -5,7 +5,8 @@ * Enhancement idea: Automatically place the slab. This seems like a tricky problem but maybe solveable with named zones? * Might be made obsolete by people just using buildingplan to pre-place plans for slab? * Enhancement idea: Optionally enable autoengraving for pets. - * Enhancement idea: Try to get ahead of ghosts by autoengraving for dead dwarves with no remains. + * Enhancement idea: Try to get ahead of ghosts by autoengraving for dead dwarves with no remains, or dwarves + * whose remains are unreachable. */ #include "Core.h" @@ -25,8 +26,6 @@ using namespace DFHack; -static command_result autoslab(color_ostream &out, std::vector ¶meters); - DFHACK_PLUGIN("autoslab"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); @@ -70,19 +69,12 @@ static void set_config_bool(int index, bool value) static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle -static command_result do_command(color_ostream &out, std::vector ¶meters); static void do_cycle(color_ostream &out); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { DEBUG(status, out).print("initializing %s\n", plugin_name); - // provide a configuration interface for the plugin - commands.push_back(PluginCommand( - plugin_name, - "Automatically engrave slabs of ghostly citizens!", - do_command)); - return CR_OK; } @@ -123,7 +115,6 @@ DFhackCExport command_result plugin_load_data(color_ostream &out) DEBUG(status, out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); set_config_bool(CONFIG_IS_ENABLED, is_enabled); - set_config_val(CONFIG_CYCLE_TICKS, 1200); } // we have to copy our enabled flag into the global plugin variable, but @@ -147,30 +138,16 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; } +static const int32_t CYCLE_TICKS = 1200; + DFhackCExport command_result plugin_onupdate(color_ostream &out) { CoreSuspender suspend; - if (is_enabled && world->frame_counter - cycle_timestamp >= get_config_val(CONFIG_CYCLE_TICKS)) + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) do_cycle(out); return CR_OK; } -static command_result do_command(color_ostream &out, std::vector ¶meters) -{ - // be sure to suspend the core if any DF state is read or modified - CoreSuspender suspend; - - if (!Core::getInstance().isWorldLoaded()) - { - out.printerr("Cannot run %s without a loaded world.\n", plugin_name); - return CR_FAILURE; - } - - // TODO: decide what, if any configuration should be here - - return CR_OK; -} - // Name functions taken from manipulator.cpp static std::string get_first_name(df::unit *unit) { @@ -243,7 +220,7 @@ static void checkslabs(color_ostream &out) std::vector ghosts; std::copy_if(world->units.all.begin(), world->units.all.end(), std::back_inserter(ghosts), - [](const auto &unit) + [](const df::unit *unit) { return unit->flags3.bits.ghostly; }); for (auto ghost : ghosts) From 1c6235633f4917895c6faf050c29c805867d1508 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 29 Jan 2023 16:26:14 -0800 Subject: [PATCH 0347/2222] add dfhack.units.getCitizens() --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 5 +++++ library/LuaApi.cpp | 12 ++++++++++++ library/include/modules/Units.h | 1 + library/modules/Units.cpp | 8 ++++++++ 5 files changed, 28 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index eb5ba2710..14c78883b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,11 +55,13 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Buildings::containsTile()``: no longer takes a ``room`` parameter since that's not how rooms work anymore. If the building has extents, the extents will be checked. otherwise, the result just depends on whether the tile is within the building's bounding box. +- ``Units::getCitizens()``: gets a list of citizens, which otherwise you'd have to iterate over all units the world to discover ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play - `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom +- ``dfhack.units.getCitizens()``: gets a list of citizens ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index a21ed0aba..555b7b1a9 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1412,6 +1412,11 @@ Units module Note that ``pos2xyz()`` cannot currently be used to convert coordinate objects to the arguments required by this function. +* ``dfhack.units.getCitizens([ignore_sanity])`` + + Returns a table (list) of all citizens, which you would otherwise have to loop over all + units in world and test against ``isCitizen()`` to discover. + * ``dfhack.units.teleport(unit, pos)`` Moves the specified unit and any riders to the target coordinates, setting diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 421821c05..8641e8aaf 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1883,6 +1883,17 @@ static int units_getUnitsInBox(lua_State *state) return 2; } +static int units_getCitizens(lua_State *L) { + bool ignore_sanity = lua_toboolean(L, -1); // defaults to false + + std::vector citizens; + if (Units::getCitizens(citizens, ignore_sanity)) { + Lua::PushVector(L, citizens); + return 1; + } + return 0; +} + static int units_getStressCutoffs(lua_State *L) { lua_newtable(L); @@ -1896,6 +1907,7 @@ static const luaL_Reg dfhack_units_funcs[] = { { "getOuterContainerRef", units_getOuterContainerRef }, { "getNoblePositions", units_getNoblePositions }, { "getUnitsInBox", units_getUnitsInBox }, + { "getCitizens", units_getCitizens }, { "getStressCutoffs", units_getStressCutoffs }, { NULL, NULL } }; diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 3cdc9264a..442f3d009 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -145,6 +145,7 @@ DFHACK_EXPORT df::unit *getUnit(const int32_t index); DFHACK_EXPORT bool getUnitsInBox(std::vector &units, int16_t x1, int16_t y1, int16_t z1, int16_t x2, int16_t y2, int16_t z2); +DFHACK_EXPORT bool getCitizens(std::vector &citizens, bool ignore_sanity = false); DFHACK_EXPORT int32_t findIndexById(int32_t id); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index a60803800..abb35b9e0 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -756,6 +756,14 @@ bool Units::getUnitsInBox (std::vector &units, return true; } +bool Units::getCitizens(std::vector &citizens, bool ignore_sanity) { + for (auto &unit : world->units.active) { + if (isCitizen(unit, ignore_sanity)) + citizens.emplace_back(unit); + } + return true; +} + int32_t Units::findIndexById(int32_t creature_id) { return df::unit::binsearch_index(world->units.all, creature_id); From f1e8ee1b0a2fcdf1720da504f80792ec66a15f2d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 29 Jan 2023 16:28:56 -0800 Subject: [PATCH 0348/2222] use new API in autochop --- plugins/autochop.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 34a8979ff..e44b4f804 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -369,13 +369,6 @@ static void bucket_tree(df::plant *plant, bool designate_clearcut, bool *designa *can_chop = true; } -static void get_citizens(vector &vec) { - for (auto &unit : world->units.active) { - if (Units::isCitizen(unit)) - vec.emplace_back(unit); - } -} - static void bucket_watched_burrows(color_ostream & out, map &clearcut_burrows, map &chop_burrows) { @@ -546,7 +539,7 @@ static int32_t do_cycle(color_ostream &out, bool force_designate) { int32_t expected_yield; TreesBySize designatable_trees_by_size; vector citizens; - get_citizens(citizens); + Units::getCitizens(citizens); int32_t newly_marked = scan_trees(out, &expected_yield, &designatable_trees_by_size, true, citizens); @@ -634,7 +627,7 @@ static void autochop_printStatus(color_ostream &out) { int32_t designated_trees, expected_yield, accessible_yield; map tree_counts, designated_tree_counts; vector citizens; - get_citizens(citizens); + Units::getCitizens(citizens); scan_logs(&usable_logs, citizens, &inaccessible_logs); scan_trees(out, &expected_yield, NULL, false, citizens, &accessible_trees, &inaccessible_trees, &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); @@ -739,7 +732,7 @@ static int autochop_getLogCounts(lua_State *L) { DEBUG(status,*out).print("entering autochop_getNumLogs\n"); int32_t usable_logs, inaccessible_logs; vector citizens; - get_citizens(citizens); + Units::getCitizens(citizens); scan_logs(&usable_logs, citizens, &inaccessible_logs); Lua::Push(L, usable_logs); Lua::Push(L, inaccessible_logs); @@ -778,7 +771,7 @@ static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { int32_t designated_trees, expected_yield, accessible_yield; map tree_counts, designated_tree_counts; vector citizens; - get_citizens(citizens); + Units::getCitizens(citizens); scan_trees(*out, &expected_yield, NULL, false, citizens, &accessible_trees, &inaccessible_trees, &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); From 75c560d9e146e6eb5354a54909c4f1175c197521 Mon Sep 17 00:00:00 2001 From: Rose Date: Sun, 29 Jan 2023 17:19:21 -0800 Subject: [PATCH 0349/2222] Add in a strictness parameter to Autoclothing. It's not used yet. --- plugins/autoclothing.cpp | 117 ++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 44 deletions(-) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index 4b795c02f..f2e3578ee 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -1,7 +1,8 @@ #include "Core.h" -#include -#include -#include +#include "Console.h" +#include "Debug.h" +#include "Export.h" +#include "PluginManager.h" #include @@ -11,6 +12,7 @@ #include "modules/Items.h" #include "modules/Maps.h" #include "modules/Materials.h" +#include "modules/Persistence.h" #include "modules/Units.h" #include "modules/World.h" #include "modules/Translation.h" @@ -34,6 +36,9 @@ #include "df/world.h" using std::endl; +using std::string; +using std::vector; +using std::map; using namespace DFHack; using namespace DFHack::Items; @@ -41,35 +46,46 @@ using namespace DFHack::Units; using namespace df::enums; -// A plugin must be able to return its name and version. -// The name string provided must correspond to the filename - -// skeleton.plug.so, skeleton.plug.dylib, or skeleton.plug.dll in this case DFHACK_PLUGIN("autoclothing"); - -// Any globals a plugin requires (e.g. world) should be listed here. -// For example, this line expands to "using df::global::world" and prevents the -// plugin from being loaded if df::global::world is null (i.e. missing from symbols.xml): -// REQUIRE_GLOBAL(world); // Only run if this is enabled DFHACK_PLUGIN_IS_ENABLED(autoclothing_enabled); +// logging levels can be dynamically controlled with the `debugfilter` command. +namespace DFHack { + // for configuration-related logging + DBG_DECLARE(autoclothing, status, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(autoclothing, cycle, DebugCategory::LINFO); +} + +static const string CONFIG_KEY = string(plugin_name) + "/config"; + + // Here go all the command declarations... // mostly to allow having the mandatory stuff on top of the file and commands on the bottom struct ClothingRequirement; -command_result autoclothing(color_ostream &out, std::vector & parameters); +command_result autoclothing(color_ostream &out, vector & parameters); static void init_state(color_ostream &out); static void save_state(color_ostream &out); static void cleanup_state(color_ostream &out); static void do_autoclothing(); static bool validateMaterialCategory(ClothingRequirement * requirement); -static bool setItem(std::string name, ClothingRequirement* requirement); +static bool setItem(string name, ClothingRequirement* requirement); static void generate_report(color_ostream& out); static bool isAvailableItem(df::item* item); +enum match_strictness +{ + STRICT_PERMISSIVE, + STRICT_TYPE, + STRICT_MATERIAL +}; + +match_strictness strictnessSetting = STRICT_PERMISSIVE; -std::vectorclothingOrders; +vectorclothingOrders; struct ClothingRequirement { @@ -78,7 +94,7 @@ struct ClothingRequirement int16_t item_subtype; df::job_material_category material_category; int16_t needed_per_citizen; - std::map total_needed_per_race; + map total_needed_per_race; bool matches(ClothingRequirement * b) { @@ -93,7 +109,7 @@ struct ClothingRequirement return true; } - std::string Serialize() + string Serialize() { std::stringstream stream; stream << ENUM_KEY_STR(job_type, jobType) << " "; @@ -104,10 +120,10 @@ struct ClothingRequirement return stream.str(); } - void Deserialize(std::string s) + void Deserialize(string s) { std::stringstream stream(s); - std::string loadedJob; + string loadedJob; stream >> loadedJob; FOR_ENUM_ITEMS(job_type, job) { @@ -117,7 +133,7 @@ struct ClothingRequirement break; } } - std::string loadedItem; + string loadedItem; stream >> loadedItem; FOR_ENUM_ITEMS(item_type, item) { @@ -132,7 +148,7 @@ struct ClothingRequirement stream >> needed_per_citizen; } - bool SetFromParameters(color_ostream &out, std::vector & parameters) + bool SetFromParameters(color_ostream &out, vector & parameters) { if (!set_bitfield_field(&material_category, parameters[0], 1)) { @@ -151,12 +167,12 @@ struct ClothingRequirement return true; } - std::string ToReadableLabel() + string ToReadableLabel() { std::stringstream stream; stream << bitfield_to_string(material_category) << " "; - std::string adjective = ""; - std::string name = ""; + string adjective = ""; + string name = ""; switch (itemType) { case df::enums::item_type::ARMOR: @@ -193,7 +209,7 @@ struct ClothingRequirement // Mandatory init function. If you have some global state, create it here. -DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { // Fill the command list with your commands. commands.push_back(PluginCommand( @@ -256,12 +272,12 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) return CR_OK; } -static bool setItemFromName(std::string name, ClothingRequirement* requirement) +static bool setItemFromName(string name, ClothingRequirement* requirement) { #define SEARCH_ITEM_RAWS(rawType, job, item) \ for (auto& itemdef : world->raws.itemdefs.rawType) \ { \ - std::string fullName = itemdef->adjective.empty() ? itemdef->name : itemdef->adjective + " " + itemdef->name; \ + string fullName = itemdef->adjective.empty() ? itemdef->name : itemdef->adjective + " " + itemdef->name; \ if (fullName == name) \ { \ requirement->jobType = job_type::job; \ @@ -278,7 +294,7 @@ for (auto& itemdef : world->raws.itemdefs.rawType) \ return false; } -static bool setItemFromToken(std::string token, ClothingRequirement* requirement) +static bool setItemFromToken(string token, ClothingRequirement* requirement) { ItemTypeInfo itemInfo; if (!itemInfo.find(token)) @@ -308,7 +324,7 @@ static bool setItemFromToken(std::string token, ClothingRequirement* requirement return true; } -static bool setItem(std::string name, ClothingRequirement* requirement) +static bool setItem(string name, ClothingRequirement* requirement) { if (setItemFromName(name, requirement)) return true; @@ -362,7 +378,7 @@ static bool validateMaterialCategory(ClothingRequirement * requirement) // A command! It sits around and looks pretty. And it's nice and friendly. -command_result autoclothing(color_ostream &out, std::vector & parameters) +command_result autoclothing(color_ostream &out, vector & parameters) { // It's nice to print a help message you get invalid options // from the user instead of just acting strange. @@ -379,6 +395,20 @@ command_result autoclothing(color_ostream &out, std::vector & para } return CR_OK; } + else if (parameters[0] == "strictness") + { + if (parameters.size() != 2) + { + out << "Wrong number of arguments." << endl; + return CR_WRONG_USAGE; + } + if (parameters[1] == "permissive") + strictnessSetting = STRICT_PERMISSIVE; + else if (parameters[1] == "type") + strictnessSetting = STRICT_TYPE; + else if (parameters[1] == "material") + strictnessSetting = STRICT_MATERIAL; + } else if (parameters.size() == 1 && parameters[0] == "report") { CoreSuspender suspend; @@ -631,9 +661,8 @@ static void init_state(color_ostream &out) autoclothing_enabled = false; } - // Parse constraints - std::vector items; + vector items; World::GetPersistentData(&items, "autoclothing/clothingItems"); for (auto& item : items) @@ -662,7 +691,7 @@ static void save_state(color_ostream &out) // Parse constraints - std::vector items; + vector items; World::GetPersistentData(&items, "autoclothing/clothingItems"); for (size_t i = 0; i < items.size(); i++) @@ -683,7 +712,7 @@ static void save_state(color_ostream &out) } } -static void list_unit_counts(color_ostream& out, std::map& unitList) +static void list_unit_counts(color_ostream& out, map& unitList) { for (const auto& race : unitList) { @@ -733,12 +762,12 @@ static bool isAvailableItem(df::item* item) static void generate_report(color_ostream& out) { - std::map fullUnitList; - std::map missingArmor; - std::map missingShoes; - std::map missingHelms; - std::map missingGloves; - std::map missingPants; + map fullUnitList; + map missingArmor; + map missingShoes; + map missingHelms; + map missingGloves; + map missingPants; for (df::unit* unit : world->units.active) { if (!Units::isCitizen(unit)) @@ -817,7 +846,7 @@ static void generate_report(color_ostream& out) list_unit_counts(out, missingPants); } } - std::map availableArmor; + map availableArmor; for (auto armor : world->items.other.ARMOR) { if (!isAvailableItem(armor)) @@ -829,7 +858,7 @@ static void generate_report(color_ostream& out) out << "We have available bodywear for:" << endl; list_unit_counts(out, availableArmor); } - std::map availableShoes; + map availableShoes; for (auto shoe : world->items.other.SHOES) { if (!isAvailableItem(shoe)) @@ -841,7 +870,7 @@ static void generate_report(color_ostream& out) out << "We have available footwear for:" << endl; list_unit_counts(out, availableShoes); } - std::map availableHelms; + map availableHelms; for (auto helm : world->items.other.HELM) { if (!isAvailableItem(helm)) @@ -853,7 +882,7 @@ static void generate_report(color_ostream& out) out << "We have available headwear for:" << endl; list_unit_counts(out, availableHelms); } - std::map availableGloves; + map availableGloves; for (auto glove : world->items.other.HELM) { if (!isAvailableItem(glove)) @@ -865,7 +894,7 @@ static void generate_report(color_ostream& out) out << "We have available handwear for:" << endl; list_unit_counts(out, availableGloves); } - std::map availablePants; + map availablePants; for (auto pants : world->items.other.HELM) { if (!isAvailableItem(pants)) From 61f33258602ce027d8f3b70f4c8a530cd3c26f9c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 29 Jan 2023 17:35:02 -0800 Subject: [PATCH 0350/2222] update sample plugin code status -> config don't make cycle ticks configurable. nobody does that --- .../examples/persistent_per_save_example.cpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/plugins/examples/persistent_per_save_example.cpp b/plugins/examples/persistent_per_save_example.cpp index 5a7bf5224..55e4cb8b4 100644 --- a/plugins/examples/persistent_per_save_example.cpp +++ b/plugins/examples/persistent_per_save_example.cpp @@ -30,7 +30,7 @@ REQUIRE_GLOBAL(world); // logging levels can be dynamically controlled with the `debugfilter` command. namespace DFHack { // for configuration-related logging - DBG_DECLARE(persistent_per_save_example, status, DebugCategory::LINFO); + DBG_DECLARE(persistent_per_save_example, cycle, DebugCategory::LINFO); // for logging during the periodic scan DBG_DECLARE(persistent_per_save_example, cycle, DebugCategory::LINFO); } @@ -39,7 +39,7 @@ static const string CONFIG_KEY = string(plugin_name) + "/config"; static PersistentDataItem config; enum ConfigValues { CONFIG_IS_ENABLED = 0, - CONFIG_CYCLE_TICKS = 1, + CONFIG_SOMETHING_ELSE = 1, }; static int get_config_val(int index) { if (!config.isValid()) @@ -57,13 +57,14 @@ static void set_config_bool(int index, bool value) { set_config_val(index, value ? 1 : 0); } +static const int32_t CYCLE_TICKS = 1200; // one day static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); static void do_cycle(color_ostream &out); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { - DEBUG(status,out).print("initializing %s\n", plugin_name); + DEBUG(cycle,out).print("initializing %s\n", plugin_name); // provide a configuration interface for the plugin commands.push_back(PluginCommand( @@ -82,11 +83,11 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { if (enable != is_enabled) { is_enabled = enable; - DEBUG(status,out).print("%s from the API; persisting\n", + DEBUG(cycle,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(CONFIG_IS_ENABLED, is_enabled); } else { - DEBUG(status,out).print("%s from the API, but already %s; no action\n", + DEBUG(cycle,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); } @@ -94,7 +95,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { } DFhackCExport command_result plugin_shutdown (color_ostream &out) { - DEBUG(status,out).print("shutting down %s\n", plugin_name); + DEBUG(cycle,out).print("shutting down %s\n", plugin_name); return CR_OK; } @@ -103,17 +104,17 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { - DEBUG(status,out).print("no config found in this save; initializing\n"); + DEBUG(cycle,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); set_config_bool(CONFIG_IS_ENABLED, is_enabled); - set_config_val(CONFIG_CYCLE_TICKS, 6000); + set_config_val(CONFIG_SOMETHING_ELSE, 6000); } // we have to copy our enabled flag into the global plugin variable, but // all the other state we can directly read/modify from the persistent // data structure. is_enabled = get_config_bool(CONFIG_IS_ENABLED); - DEBUG(status,out).print("loading persisted enabled state: %s\n", + DEBUG(cycle,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); return CR_OK; } @@ -121,7 +122,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { if (event == DFHack::SC_WORLD_UNLOADED) { if (is_enabled) { - DEBUG(status,out).print("world unloaded; disabling %s\n", + DEBUG(cycle,out).print("world unloaded; disabling %s\n", plugin_name); is_enabled = false; } @@ -130,7 +131,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan } DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if (is_enabled && world->frame_counter - cycle_timestamp >= get_config_val(CONFIG_CYCLE_TICKS)) + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) do_cycle(out); return CR_OK; } @@ -162,5 +163,5 @@ static void do_cycle(color_ostream &out) { DEBUG(cycle,out).print("running %s cycle\n", plugin_name); - // TODO: logic that runs every get_config_val(CONFIG_CYCLE_TICKS) ticks + // TODO: logic that runs every CYCLE_TICKS ticks } From 2b3160b1b945bf5bd6113d47bf589ad0c3f838b7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 29 Jan 2023 17:39:48 -0800 Subject: [PATCH 0351/2222] fix typos --- .../examples/persistent_per_save_example.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/examples/persistent_per_save_example.cpp b/plugins/examples/persistent_per_save_example.cpp index 55e4cb8b4..603df1a3c 100644 --- a/plugins/examples/persistent_per_save_example.cpp +++ b/plugins/examples/persistent_per_save_example.cpp @@ -28,9 +28,11 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); // logging levels can be dynamically controlled with the `debugfilter` command. +// the names "config" and "cycle" are arbitrary and are just used to categorize +// your log messages. namespace DFHack { // for configuration-related logging - DBG_DECLARE(persistent_per_save_example, cycle, DebugCategory::LINFO); + DBG_DECLARE(persistent_per_save_example, config, DebugCategory::LINFO); // for logging during the periodic scan DBG_DECLARE(persistent_per_save_example, cycle, DebugCategory::LINFO); } @@ -64,7 +66,7 @@ static command_result do_command(color_ostream &out, vector ¶meters) static void do_cycle(color_ostream &out); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { - DEBUG(cycle,out).print("initializing %s\n", plugin_name); + DEBUG(config,out).print("initializing %s\n", plugin_name); // provide a configuration interface for the plugin commands.push_back(PluginCommand( @@ -83,11 +85,11 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { if (enable != is_enabled) { is_enabled = enable; - DEBUG(cycle,out).print("%s from the API; persisting\n", + DEBUG(config,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(CONFIG_IS_ENABLED, is_enabled); } else { - DEBUG(cycle,out).print("%s from the API, but already %s; no action\n", + DEBUG(config,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", is_enabled ? "enabled" : "disabled"); } @@ -95,7 +97,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { } DFhackCExport command_result plugin_shutdown (color_ostream &out) { - DEBUG(cycle,out).print("shutting down %s\n", plugin_name); + DEBUG(config,out).print("shutting down %s\n", plugin_name); return CR_OK; } @@ -104,7 +106,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { - DEBUG(cycle,out).print("no config found in this save; initializing\n"); + DEBUG(config,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); set_config_bool(CONFIG_IS_ENABLED, is_enabled); set_config_val(CONFIG_SOMETHING_ELSE, 6000); @@ -114,7 +116,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { // all the other state we can directly read/modify from the persistent // data structure. is_enabled = get_config_bool(CONFIG_IS_ENABLED); - DEBUG(cycle,out).print("loading persisted enabled state: %s\n", + DEBUG(config,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); return CR_OK; } @@ -122,7 +124,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { if (event == DFHack::SC_WORLD_UNLOADED) { if (is_enabled) { - DEBUG(cycle,out).print("world unloaded; disabling %s\n", + DEBUG(config,out).print("world unloaded; disabling %s\n", plugin_name); is_enabled = false; } From c79b95ec3362673ede6198dd85994ec1b43da6aa Mon Sep 17 00:00:00 2001 From: Rose Date: Sun, 29 Jan 2023 17:48:23 -0800 Subject: [PATCH 0352/2222] Don't crash autoclothing if a dwarf has a broken inventory item. --- plugins/autoclothing.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index f2e3578ee..bdf525a4c 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -55,7 +55,7 @@ DFHACK_PLUGIN_IS_ENABLED(autoclothing_enabled); // logging levels can be dynamically controlled with the `debugfilter` command. namespace DFHack { // for configuration-related logging - DBG_DECLARE(autoclothing, status, DebugCategory::LINFO); + DBG_DECLARE(autoclothing, report, DebugCategory::LINFO); // for logging during the periodic scan DBG_DECLARE(autoclothing, cycle, DebugCategory::LINFO); } @@ -524,6 +524,12 @@ static void find_needed_clothing_items() { auto item = findItemByID(ownedItem); + if (!item) + { + WARN(cycle).print("Invalid inventory item ID: %d\n", ownedItem); + continue; + } + if (item->getType() != clothingOrder.itemType) continue; if (item->getSubtype() != clothingOrder.item_subtype) @@ -811,7 +817,7 @@ static void generate_report(color_ostream& out) missingGloves[unit->race]++; if (numPants == 0) missingPants[unit->race]++; - //out << Translation::TranslateName(Units::getVisibleName(unit)) << " has " << numArmor << " armor, " << numShoes << " shoes, " << numHelms << " helms, " << numGloves << " gloves, " << numPants << " pants" << endl; + DEBUG(report) << Translation::TranslateName(Units::getVisibleName(unit)) << " has " << numArmor << " armor, " << numShoes << " shoes, " << numHelms << " helms, " << numGloves << " gloves, " << numPants << " pants" << endl; } if (missingArmor.size() + missingShoes.size() + missingHelms.size() + missingGloves.size() + missingPants.size() == 0) { From c9ddb4d9436de86f6f43fd27d625b742368d36ae Mon Sep 17 00:00:00 2001 From: Rose Date: Sun, 29 Jan 2023 17:51:10 -0800 Subject: [PATCH 0353/2222] There was one more place for autoclothing to crash. --- plugins/autoclothing.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index bdf525a4c..d7dee23ad 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -520,7 +520,7 @@ static void find_needed_clothing_items() int alreadyOwnedAmount = 0; //looping through the items first, then clothing order might be a little faster, but this way is cleaner. - for (auto& ownedItem : unit->owned_items) + for (auto ownedItem : unit->owned_items) { auto item = findItemByID(ownedItem); @@ -782,8 +782,12 @@ static void generate_report(color_ostream& out) int numArmor = 0, numShoes = 0, numHelms = 0, numGloves = 0, numPants = 0; for (auto itemId : unit->owned_items) { - auto item = Items::findItemByID(itemId); + if (!item) + { + WARN(cycle).print("Invalid inventory item ID: %d\n", itemId); + continue; + } if (item->getWear() >= 1) continue; switch (item->getType()) From 8774d3191f76b9f50a63851482720cedb8ced46d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 29 Jan 2023 17:59:16 -0800 Subject: [PATCH 0354/2222] sync tags from spreadsheet --- docs/plugins/dig.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/dig.rst b/docs/plugins/dig.rst index afe1fac17..a10db97df 100644 --- a/docs/plugins/dig.rst +++ b/docs/plugins/dig.rst @@ -6,7 +6,7 @@ dig .. dfhack-tool:: :summary: Provides commands for designating tiles for digging. - :tags: untested fort design productivity map + :tags: fort design productivity map :no-command: .. dfhack-command:: digv From 57c53409302fa12bcc8aed7561c836274b6971da Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 30 Jan 2023 02:14:05 +0000 Subject: [PATCH 0355/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index bf914ddd8..fd293eab7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit bf914ddd8c58011368f72172f9d158d0043c61fc +Subproject commit fd293eab744d4c01db66960fc9e7a858f0b6db1e From 4c17d79198aeaf84b496f79d120c50da13f54d03 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 30 Jan 2023 02:28:17 +0000 Subject: [PATCH 0356/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index a3b6cd550..518c1a431 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a3b6cd5507753207ce28fe0872c98d6434d58a90 +Subproject commit 518c1a431dcfa0ca23110d94223973853d640a11 From 3f9f7855890add716034a5fc576126d3d8262db7 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sun, 29 Jan 2023 21:34:47 -0500 Subject: [PATCH 0357/2222] Update doc and remove unused enum --- docs/plugins/autoslab.rst | 5 ++++- plugins/autoslab.cpp | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/plugins/autoslab.rst b/docs/plugins/autoslab.rst index 5e61e0938..2f14f590d 100644 --- a/docs/plugins/autoslab.rst +++ b/docs/plugins/autoslab.rst @@ -8,7 +8,10 @@ autoslab Automatically queue orders to engrave slabs of existing ghosts. Will only queue an order if there is no existing slab with that unit's memorial engraved and -there is not already an existing work order to engrave a slab for that unit +there is not already an existing work order to engrave a slab for that unit. +Make sure you have spare slabs on hand for engraving! If you run +`orders import library/rockstock `, you'll be sure to always have +some slabs in stock. Usage ----- diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 4e9220cf2..51c92b0f7 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -45,7 +45,6 @@ static PersistentDataItem config; enum ConfigValues { CONFIG_IS_ENABLED = 0, - CONFIG_CYCLE_TICKS = 1, }; static int get_config_val(int index) { From f1c173863c2bf2b4989b2dbc3d6555cd345d3fec Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sun, 29 Jan 2023 21:36:49 -0500 Subject: [PATCH 0358/2222] Remove trailing whitespace --- docs/plugins/autoslab.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/autoslab.rst b/docs/plugins/autoslab.rst index 2f14f590d..a947f7bbe 100644 --- a/docs/plugins/autoslab.rst +++ b/docs/plugins/autoslab.rst @@ -11,7 +11,7 @@ an order if there is no existing slab with that unit's memorial engraved and there is not already an existing work order to engrave a slab for that unit. Make sure you have spare slabs on hand for engraving! If you run `orders import library/rockstock `, you'll be sure to always have -some slabs in stock. +some slabs in stock. Usage ----- From 40e69bfa3f19b53ffa950fbd8044fe0f5001b0ac Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sun, 29 Jan 2023 20:37:01 -0600 Subject: [PATCH 0359/2222] remove autohauler autohauler just doesn't make sense in v50 --- docs/changelog.txt | 1 + plugins/autolabor/CMakeLists.txt | 1 - plugins/autolabor/autohauler.cpp | 727 ------------------------------- 3 files changed, 1 insertion(+), 728 deletions(-) delete mode 100644 plugins/autolabor/autohauler.cpp diff --git a/docs/changelog.txt b/docs/changelog.txt index 1174508e2..0f2159c38 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -66,6 +66,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``dfhack.units.getCitizens()``: gets a list of citizens ## Removed +- `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system # 50.05-alpha2 diff --git a/plugins/autolabor/CMakeLists.txt b/plugins/autolabor/CMakeLists.txt index c73e2ad6d..554a02ecf 100644 --- a/plugins/autolabor/CMakeLists.txt +++ b/plugins/autolabor/CMakeLists.txt @@ -12,5 +12,4 @@ list(APPEND COMMON_SRCS ${COMMON_HDRS}) dfhack_plugin(labormanager labormanager.cpp joblabormapper.cpp ${COMMON_SRCS}) -dfhack_plugin(autohauler autohauler.cpp ${COMMON_SRCS}) dfhack_plugin(autolabor autolabor.cpp ${COMMON_SRCS}) diff --git a/plugins/autolabor/autohauler.cpp b/plugins/autolabor/autohauler.cpp deleted file mode 100644 index 3bb2534f8..000000000 --- a/plugins/autolabor/autohauler.cpp +++ /dev/null @@ -1,727 +0,0 @@ -#include "Core.h" -#include -#include -#include -#include - -#include -#include - -#include "modules/Units.h" -#include "modules/World.h" - -// DF data structure definition headers -#include "DataDefs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "modules/MapCache.h" -#include "modules/Items.h" -#include "modules/Units.h" - -#include "laborstatemap.h" - -using namespace DFHack; -using namespace df::enums; - -DFHACK_PLUGIN("autohauler"); -REQUIRE_GLOBAL(plotinfo); -REQUIRE_GLOBAL(world); - -#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) - -/* - * Autohauler module for dfhack - * Fork of autolabor, DFHack version 0.40.24-r2 - * - * Rather than the all-of-the-above means of autolabor, autohauler will instead - * only manage hauling labors and leave skilled labors entirely to the user, who - * will probably use Dwarf Therapist to do so. - * Idle dwarves will be assigned the hauling labors; everyone else (including - * those currently hauling) will have the hauling labors removed. This is to - * encourage every dwarf to do their assigned skilled labors whenever possible, - * but resort to hauling when those jobs are not available. This also implies - * that the user will have a very tight skill assignment, with most skilled - * labors only being assigned to just one dwarf, no dwarf having more than two - * active skilled labors, and almost every non-military dwarf having at least - * one skilled labor assigned. - * Autohauler allows skills to be flagged as to prevent hauling labors from - * being assigned when the skill is present. By default this is the unused - * ALCHEMIST labor but can be changed by the user. - * It is noteworthy that, as stated in autolabor.cpp, "for almost all labors, - * once a dwarf begins a job it will finish that job even if the associated - * labor is removed." This is why we can remove hauling labors by default to try - * to force dwarves to do "real" jobs whenever they can. - * This is a standalone plugin. However, it would be wise to delete - * autolabor.plug.dll as this plugin is mutually exclusive with it. - */ - -DFHACK_PLUGIN_IS_ENABLED(enable_autohauler); - -namespace DFHack { - DBG_DECLARE(autohauler, cycle, DebugCategory::LINFO); -} - -static std::vector state_count(NUM_STATE); - -const static int DEFAULT_FRAME_SKIP = 30; - -static PersistentDataItem config; - -command_result autohauler (color_ostream &out, std::vector & parameters); - -static int frame_skip; - -static bool isOptionEnabled(unsigned flag) -{ - return config.isValid() && (config.ival(0) & flag) != 0; -} - -enum ConfigFlags { - CF_ENABLED = 1, -}; - -static void setOptionEnabled(ConfigFlags flag, bool on) -{ - if (!config.isValid()) - return; - - if (on) - config.ival(0) |= flag; - else - config.ival(0) &= ~flag; -} - -enum labor_mode { - ALLOW, - HAULERS, - FORBID -}; - -struct labor_info -{ - PersistentDataItem config; - - int active_dwarfs; - - labor_mode mode() { return (labor_mode) config.ival(0); } - - void set_mode(labor_mode mode) { config.ival(0) = mode; } - - void set_config(PersistentDataItem a) { config = a; } - - }; - -struct labor_default -{ - labor_mode mode; - int active_dwarfs; -}; - -static std::vector labor_infos; - -static const struct labor_default default_labor_infos[] = { - /* MINE */ {ALLOW, 0}, - /* HAUL_STONE */ {HAULERS, 0}, - /* HAUL_WOOD */ {HAULERS, 0}, - /* HAUL_BODY */ {HAULERS, 0}, - /* HAUL_FOOD */ {HAULERS, 0}, - /* HAUL_REFUSE */ {HAULERS, 0}, - /* HAUL_ITEM */ {HAULERS, 0}, - /* HAUL_FURNITURE */ {HAULERS, 0}, - /* HAUL_ANIMAL */ {HAULERS, 0}, - /* CLEAN */ {HAULERS, 0}, - /* CUTWOOD */ {ALLOW, 0}, - /* CARPENTER */ {ALLOW, 0}, - /* DETAIL */ {ALLOW, 0}, - /* MASON */ {ALLOW, 0}, - /* ARCHITECT */ {ALLOW, 0}, - /* ANIMALTRAIN */ {ALLOW, 0}, - /* ANIMALCARE */ {ALLOW, 0}, - /* DIAGNOSE */ {ALLOW, 0}, - /* SURGERY */ {ALLOW, 0}, - /* BONE_SETTING */ {ALLOW, 0}, - /* SUTURING */ {ALLOW, 0}, - /* DRESSING_WOUNDS */ {ALLOW, 0}, - /* FEED_WATER_CIVILIANS */ {HAULERS, 0}, // This could also be ALLOW - /* RECOVER_WOUNDED */ {HAULERS, 0}, - /* BUTCHER */ {ALLOW, 0}, - /* TRAPPER */ {ALLOW, 0}, - /* DISSECT_VERMIN */ {ALLOW, 0}, - /* LEATHER */ {ALLOW, 0}, - /* TANNER */ {ALLOW, 0}, - /* BREWER */ {ALLOW, 0}, - /* ALCHEMIST */ {FORBID, 0}, - /* SOAP_MAKER */ {ALLOW, 0}, - /* WEAVER */ {ALLOW, 0}, - /* CLOTHESMAKER */ {ALLOW, 0}, - /* MILLER */ {ALLOW, 0}, - /* PROCESS_PLANT */ {ALLOW, 0}, - /* MAKE_CHEESE */ {ALLOW, 0}, - /* MILK */ {ALLOW, 0}, - /* COOK */ {ALLOW, 0}, - /* PLANT */ {ALLOW, 0}, - /* HERBALIST */ {ALLOW, 0}, - /* FISH */ {ALLOW, 0}, - /* CLEAN_FISH */ {ALLOW, 0}, - /* DISSECT_FISH */ {ALLOW, 0}, - /* HUNT */ {ALLOW, 0}, - /* SMELT */ {ALLOW, 0}, - /* FORGE_WEAPON */ {ALLOW, 0}, - /* FORGE_ARMOR */ {ALLOW, 0}, - /* FORGE_FURNITURE */ {ALLOW, 0}, - /* METAL_CRAFT */ {ALLOW, 0}, - /* CUT_GEM */ {ALLOW, 0}, - /* ENCRUST_GEM */ {ALLOW, 0}, - /* WOOD_CRAFT */ {ALLOW, 0}, - /* STONE_CRAFT */ {ALLOW, 0}, - /* BONE_CARVE */ {ALLOW, 0}, - /* GLASSMAKER */ {ALLOW, 0}, - /* EXTRACT_STRAND */ {ALLOW, 0}, - /* SIEGECRAFT */ {ALLOW, 0}, - /* SIEGEOPERATE */ {ALLOW, 0}, - /* BOWYER */ {ALLOW, 0}, - /* MECHANIC */ {ALLOW, 0}, - /* POTASH_MAKING */ {ALLOW, 0}, - /* LYE_MAKING */ {ALLOW, 0}, - /* DYER */ {ALLOW, 0}, - /* BURN_WOOD */ {ALLOW, 0}, - /* OPERATE_PUMP */ {ALLOW, 0}, - /* SHEARER */ {ALLOW, 0}, - /* SPINNER */ {ALLOW, 0}, - /* POTTERY */ {ALLOW, 0}, - /* GLAZING */ {ALLOW, 0}, - /* PRESSING */ {ALLOW, 0}, - /* BEEKEEPING */ {ALLOW, 0}, - /* WAX_WORKING */ {ALLOW, 0}, - /* HANDLE_VEHICLES */ {HAULERS, 0}, - /* HAUL_TRADE */ {HAULERS, 0}, - /* PULL_LEVER */ {HAULERS, 0}, - /* REMOVE_CONSTRUCTION */ {HAULERS, 0}, - /* HAUL_WATER */ {HAULERS, 0}, - /* GELD */ {ALLOW, 0}, - /* BUILD_ROAD */ {HAULERS, 0}, - /* BUILD_CONSTRUCTION */ {HAULERS, 0}, - /* PAPERMAKING */ {ALLOW, 0}, - /* BOOKBINDING */ {ALLOW, 0} -}; - -struct dwarf_info_t -{ - dwarf_state state; - - bool haul_exempt; -}; - -static void cleanup_state() -{ - enable_autohauler = false; - labor_infos.clear(); -} - -static void reset_labor(df::unit_labor labor) -{ - labor_infos[labor].set_mode(default_labor_infos[labor].mode); -} - -static void enable_alchemist(color_ostream &out) -{ - if (!Units::setLaborValidity(unit_labor::ALCHEMIST, true)) - { - // informational only; this is a non-fatal error - out.printerr("%s: Could not flag Alchemist as a valid skill; Alchemist will not" - " be settable from DF or DFHack labor management screens.\n", plugin_name); - } -} - -static void init_state(color_ostream &out) -{ - config = World::GetPersistentData("autohauler/config"); - - if (config.isValid() && config.ival(0) == -1) - config.ival(0) = 0; - - enable_autohauler = isOptionEnabled(CF_ENABLED); - - if (!enable_autohauler) - return; - - auto cfg_frameskip = World::GetPersistentData("autohauler/frameskip"); - if (cfg_frameskip.isValid()) - { - frame_skip = cfg_frameskip.ival(0); - } - else - { - cfg_frameskip = World::AddPersistentData("autohauler/frameskip"); - cfg_frameskip.ival(0) = DEFAULT_FRAME_SKIP; - frame_skip = cfg_frameskip.ival(0); - } - labor_infos.resize(ARRAY_COUNT(default_labor_infos)); - - std::vector items; - World::GetPersistentData(&items, "autohauler/labors/", true); - - - for (auto& p : items) - { - std::string key = p.key(); - df::unit_labor labor = (df::unit_labor) atoi(key.substr(strlen("autohauler/labors/")).c_str()); - if (labor >= 0 && size_t(labor) < labor_infos.size()) - { - labor_infos[labor].set_config(p); - labor_infos[labor].active_dwarfs = 0; - } - } - - // Add default labors for those not in save - for (size_t i = 0; i < ARRAY_COUNT(default_labor_infos); i++) { - if (labor_infos[i].config.isValid()) - continue; - - std::stringstream name; - name << "autohauler/labors/" << i; - - labor_infos[i].set_config(World::AddPersistentData(name.str())); - - labor_infos[i].active_dwarfs = 0; - reset_labor((df::unit_labor) i); - } - - enable_alchemist(out); -} - -static void enable_plugin(color_ostream &out) -{ - if (!config.isValid()) - { - config = World::AddPersistentData("autohauler/config"); - config.ival(0) = 0; - } - - setOptionEnabled(CF_ENABLED, true); - enable_autohauler = true; - out << "Enabling the plugin." << std::endl; - - cleanup_state(); - init_state(out); -} - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - if(ARRAY_COUNT(default_labor_infos) != ENUM_LAST_ITEM(unit_labor) + 1) - { - out.printerr("autohauler: labor size mismatch\n"); - return CR_FAILURE; - } - - commands.push_back(PluginCommand( - "autohauler", - "Automatically manage hauling labors.", - autohauler)); - - init_state(out); - - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - cleanup_state(); - - return CR_OK; -} - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_MAP_LOADED: - cleanup_state(); - init_state(out); - break; - case SC_MAP_UNLOADED: - cleanup_state(); - break; - default: - break; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - static int step_count = 0; - if(!world || !world->map.block_index || !enable_autohauler) - { - return CR_OK; - } - - if (++step_count < frame_skip) - return CR_OK; - step_count = 0; - - std::vector dwarfs; - - for (auto& cre : world->units.active) - { - if (Units::isCitizen(cre)) - { - dwarfs.push_back(cre); - } - } - - int n_dwarfs = dwarfs.size(); - - if (n_dwarfs == 0) - return CR_OK; - - std::vector dwarf_info(n_dwarfs); - - state_count.clear(); - state_count.resize(NUM_STATE); - - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - /* Before determining how to handle employment status, handle - * hauling exemptions first */ - - // Default deny condition of on break for later else-if series - bool is_migrant = false; - - // Scan every labor. If a labor that disallows hauling is present - // for the dwarf, the dwarf is hauling exempt - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (!(labor == unit_labor::NONE)) - { - bool test1 = labor_infos[labor].mode() == FORBID; - bool test2 = dwarfs[dwarf]->status.labors[labor]; - - if(test1 && test2) dwarf_info[dwarf].haul_exempt = true; - } - } - - // Scan a dwarf's miscellaneous traits for on break or migrant status. - // If either of these are present, disable hauling because we want them - // to try to find real jobs first - auto v = dwarfs[dwarf]->status.misc_traits; - auto test_migrant = [](df::unit_misc_trait* t) { return t->id == misc_trait_type::Migrant; }; - is_migrant = std::find_if(v.begin(), v.end(), test_migrant ) != v.end(); - - /* Now determine a dwarf's employment status and decide whether - * to assign hauling */ - - // I don't think you can set the labors for babies and children, but let's - // ignore them anyway - if (Units::isBaby(dwarfs[dwarf]) || Units::isChild(dwarfs[dwarf])) - { - dwarf_info[dwarf].state = CHILD; - } - // Account for any hauling exemptions here - else if (dwarf_info[dwarf].haul_exempt) - { - dwarf_info[dwarf].state = BUSY; - } - // Account for the military - else if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession)) - dwarf_info[dwarf].state = MILITARY; - // Account for incoming migrants - else if (is_migrant) - { - dwarf_info[dwarf].state = OTHER; - } - else if (dwarfs[dwarf]->job.current_job == NULL) - { - dwarf_info[dwarf].state = IDLE; - } - else - { - int job = dwarfs[dwarf]->job.current_job->job_type; - if (job >= 0 && size_t(job) < ARRAY_COUNT(dwarf_states)) - dwarf_info[dwarf].state = dwarf_states[job]; - else - { - WARN(cycle, out).print("Dwarf %i \"%s\" has unknown job %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), job); - dwarf_info[dwarf].state = OTHER; - } - } - - state_count[dwarf_info[dwarf].state]++; - - TRACE(cycle, out).print("Dwarf %i \"%s\": state %s\n", - dwarf, dwarfs[dwarf]->name.first_name.c_str(), state_names[dwarf_info[dwarf].state]); - } - - // This is a vector of all the labors - std::vector labors; - - // For every labor... - FOR_ENUM_ITEMS(unit_labor, labor) - { - // Ignore all nonexistent labors - if (labor == unit_labor::NONE) - continue; - - // Set number of active dwarves for this job to zero - labor_infos[labor].active_dwarfs = 0; - - // And add the labor to the aforementioned vector of labors - labors.push_back(labor); - } - - // This is a different algorithm than Autolabor. Instead, the intent is to - // have "real" jobs filled first, then if nothing is available the dwarf - // instead resorts to hauling. - - // IDLE - Enable hauling - // BUSY - Disable hauling - // OTHER - Enable hauling - // MILITARY - Enable hauling - - // There was no reason to put potential haulers in an array. All of them are - // covered in the following for loop. - - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (labor == unit_labor::NONE) - continue; - if (labor_infos[labor].mode() != HAULERS) - continue; - - for(size_t dwarf = 0; dwarf < dwarfs.size(); dwarf++) - { - if (!Units::isValidLabor(dwarfs[dwarf], labor)) - continue; - - // Set hauling labors based on employment states - if(dwarf_info[dwarf].state == IDLE) { - dwarfs[dwarf]->status.labors[labor] = true; - } - else if(dwarf_info[dwarf].state == MILITARY) { - dwarfs[dwarf]->status.labors[labor] = true; - } - else if(dwarf_info[dwarf].state == OTHER) { - dwarfs[dwarf]->status.labors[labor] = true; - } - else if(dwarf_info[dwarf].state == BUSY) { - dwarfs[dwarf]->status.labors[labor] = false; - } - // If at the end of this the dwarf has the hauling labor, increment the - // counter - if(dwarfs[dwarf]->status.labors[labor]) - { - labor_infos[labor].active_dwarfs++; - } - // CHILD ignored - } - } - return CR_OK; -} - -void print_labor (df::unit_labor labor, color_ostream &out) -{ - std::string labor_name = ENUM_KEY_STR(unit_labor, labor); - out << labor_name << ": "; - for (int i = 0; i < 20 - (int)labor_name.length(); i++) - out << ' '; - if (labor_infos[labor].mode() == ALLOW) out << "allow" << std::endl; - else if(labor_infos[labor].mode() == FORBID) out << "forbid" << std::endl; - else if(labor_infos[labor].mode() == HAULERS) - { - out << "haulers, currently " << labor_infos[labor].active_dwarfs << " dwarfs" << std::endl; - } - else - { - out << "Warning: Invalid labor mode!" << std::endl; - } -} - -DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) -{ - if (!Core::getInstance().isWorldLoaded()) { - out.printerr("World is not loaded: please load a game first.\n"); - return CR_FAILURE; - } - - if (enable && !enable_autohauler) - { - enable_plugin(out); - } - else if(!enable && enable_autohauler) - { - enable_autohauler = false; - setOptionEnabled(CF_ENABLED, false); - - out << "Autohauler is disabled." << std::endl; - } - - return CR_OK; -} - -command_result autohauler (color_ostream &out, std::vector & parameters) -{ - CoreSuspender suspend; - - if (!Core::getInstance().isWorldLoaded()) { - out.printerr("World is not loaded: please load a game first.\n"); - return CR_FAILURE; - } - - if (parameters.size() == 1 && - (parameters[0] == "0" || parameters[0] == "enable" || - parameters[0] == "1" || parameters[0] == "disable")) - { - bool enable = (parameters[0] == "1" || parameters[0] == "enable"); - - return plugin_enable(out, enable); - } - else if (parameters.size() == 2 && parameters[0] == "frameskip") - { - auto cfg_frameskip = World::GetPersistentData("autohauler/frameskip"); - if(cfg_frameskip.isValid()) - { - int newValue = atoi(parameters[1].c_str()); - cfg_frameskip.ival(0) = newValue; - out << "Setting frame skip to " << newValue << std::endl; - frame_skip = cfg_frameskip.ival(0); - return CR_OK; - } - else - { - out << "Warning! No persistent data for frame skip!" << std::endl; - return CR_OK; - } - } - else if (parameters.size() >= 2 && parameters.size() <= 4) - { - if (!enable_autohauler) - { - out << "Error: The plugin is not enabled." << std::endl; - return CR_FAILURE; - } - - df::unit_labor labor = unit_labor::NONE; - - FOR_ENUM_ITEMS(unit_labor, test_labor) - { - if (parameters[0] == ENUM_KEY_STR(unit_labor, test_labor)) - labor = test_labor; - } - - if (labor == unit_labor::NONE) - { - out.printerr("Could not find labor %s.\n", parameters[0].c_str()); - return CR_WRONG_USAGE; - } - - if (parameters[1] == "haulers") - { - labor_infos[labor].set_mode(HAULERS); - print_labor(labor, out); - return CR_OK; - } - if (parameters[1] == "allow") - { - labor_infos[labor].set_mode(ALLOW); - print_labor(labor, out); - return CR_OK; - } - if (parameters[1] == "forbid") - { - labor_infos[labor].set_mode(FORBID); - print_labor(labor, out); - return CR_OK; - } - if (parameters[1] == "reset") - { - reset_labor(labor); - print_labor(labor, out); - return CR_OK; - } - - print_labor(labor, out); - - return CR_OK; - } - else if (parameters.size() == 1 && parameters[0] == "reset-all") - { - if (!enable_autohauler) - { - out << "Error: The plugin is not enabled." << std::endl; - return CR_FAILURE; - } - - for (size_t i = 0; i < labor_infos.size(); i++) - { - reset_labor((df::unit_labor) i); - } - out << "All labors reset." << std::endl; - return CR_OK; - } - else if (parameters.size() == 1 && (parameters[0] == "list" || parameters[0] == "status")) - { - if (!enable_autohauler) - { - out << "Error: The plugin is not enabled." << std::endl; - return CR_FAILURE; - } - - bool need_comma = false; - for (int i = 0; i < NUM_STATE; i++) - { - if (state_count[i] == 0) - continue; - if (need_comma) - out << ", "; - out << state_count[i] << ' ' << state_names[i]; - need_comma = true; - } - out << std::endl; - - out << "Autohauler is running every " << frame_skip << " frames." << std::endl; - - if (parameters[0] == "list") - { - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (labor == unit_labor::NONE) - continue; - - print_labor(labor, out); - } - } - - return CR_OK; - } - else - { - out.print("Automatically assigns hauling labors to dwarves.\n" - "Activate with 'enable autohauler', deactivate with 'disable autohauler'.\n" - "Current state: %d.\n", enable_autohauler); - - return CR_OK; - } -} From 93bfbde8d7e1d32468a82185cbf88b1703f0f2bc Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sun, 29 Jan 2023 21:54:02 -0600 Subject: [PATCH 0360/2222] doc updates for removal of autohauler --- docs/about/Removed.rst | 8 +++++ docs/plugins/autohauler.rst | 58 ------------------------------------- 2 files changed, 8 insertions(+), 58 deletions(-) delete mode 100644 docs/plugins/autohauler.rst diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index d30a00f00..5309372d8 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -10,6 +10,14 @@ work (e.g. links from the `changelog`). :local: :depth: 1 +.. _autohauler: + +autohauler +========== +An automated labor management tool that only addressed hauling labors, leaving the assignment +of skilled labors entirely up to the player. Fundamentally incompatible with the work detail +system of labor management in v50 of Dwarf Fortress. + .. _command-prompt: command-prompt diff --git a/docs/plugins/autohauler.rst b/docs/plugins/autohauler.rst deleted file mode 100644 index a40d50f34..000000000 --- a/docs/plugins/autohauler.rst +++ /dev/null @@ -1,58 +0,0 @@ -autohauler -========== - -.. dfhack-tool:: - :summary: Automatically manage hauling labors. - :tags: untested fort auto labors - -Similar to `autolabor`, but instead of managing all labors, autohauler only -addresses hauling labors, leaving the assignment of skilled labors entirely up -to you. You can use the in-game `manipulator` UI or an external tool like Dwarf -Therapist to do so. - -Idle dwarves who are not on active military duty will be assigned the hauling -labors; everyone else (including those currently hauling) will have the hauling -labors removed. This is to encourage every dwarf to do their assigned skilled -labors whenever possible, but resort to hauling when those jobs are not -available. This also implies that the user will have a very tight skill -assignment, with most skilled labors only being assigned to just a few dwarves -and almost every non-military dwarf having at least one skilled labor assigned. - -Autohauler allows a skill to be used as a flag to exempt a dwarf from -autohauler's effects. By default, this is the unused ALCHEMIST labor, but it -can be changed by the user. - -Autohauler uses DFHack's `debug` functionality to display information about the changes it makes. The amount of -information displayed can be controlled through appropriate use of the ``debugfilter`` command. - -Usage ------ - -``enable autohauler`` - Start managing hauling labors. This is normally all you need to do. - Autohauler works well on default settings. -``autohauler status`` - Show autohauler status and status of fort dwarves. -``autohauler haulers`` - Set whether a particular labor should be assigned to haulers. -``autohauler allow|forbid`` - Set whether a particular labor should mark a dwarf as exempt from hauling. - By default, only the ``ALCHEMIST`` labor is set to ``forbid``. -``autohauler reset-all| reset`` - Reset a particular labor (or all labors) to their default - haulers/allow/forbid state. -``autohauler list`` - Show the active configuration for all labors. -``autohauler frameskip `` - Set the number of frames between runs of autohauler. - -Examples --------- - -``autohauler HAUL_STONE haulers`` - Set stone hauling as a hauling labor (this is already the default). -``autohauler RECOVER_WOUNDED allow`` - Allow the "Recover wounded" labor to be manually assigned by the player. By - default it is automatically given to haulers. -``autohauler MINE forbid`` - Don't assign hauling labors to dwarves with the Mining labor enabled. From 282da701bbd5c54f7104255842ef25a7c3ed301d Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 29 Jan 2023 23:58:01 -0500 Subject: [PATCH 0361/2222] changelog: add #2689, fix a few typos, add a link --- docs/changelog.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8bbd3a412..74fc26086 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,15 +40,16 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ DF screens can no longer get "stuck" on transitions when DFHack tool windows are visible. Instead, those DF screens are force-paused while DFHack windows are visible so the player can close them first and not corrupt the screen sequence. The "force pause" indicator will appear on these DFHack windows to indicate what is happening. -@ ``Screen``: allow `gui/launcher` and `gui/quickcmd` to launch themselves without hanging the game -@ Fix issues with clicks "passing through" some DFHack window elements, like scrollbars -- `getplants`: tree are now designated correctly +- `getplants`: trees are now designated correctly +-@ Sample orders: fix orders that create bags ## Misc Improvements -- A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) +- A new cross-compile build script was added for building DFHack for Windows from a Linux Docker builder (see the `compile` instructions in the docs) - You can now configure whether DFHack tool windows should pause the game by default - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `gui/launcher`: sped up initialization time for faster load of the UI - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open -- `gui/quickcmd`: now has it's own global keybinding for your convenience: Ctrl-Shift-A +- `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A - Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - `getplants`: ID values will now be accepted regardless of case From 868d35bc781fbc8245090c18d16c6c8bb4994024 Mon Sep 17 00:00:00 2001 From: Corey Date: Sun, 29 Jan 2023 23:10:50 -0600 Subject: [PATCH 0362/2222] Always build stonesense Stonesense was not being built by github actions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b8892882..5bd5ae9f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,7 +87,7 @@ jobs: -DBUILD_DEV_PLUGINS:BOOL=${{ matrix.plugins == 'all' }} \ -DBUILD_SIZECHECK:BOOL=${{ matrix.plugins == 'all' }} \ -DBUILD_SKELETON:BOOL=${{ matrix.plugins == 'all' }} \ - -DBUILD_STONESENSE:BOOL=${{ matrix.plugins == 'all' }} \ + -DBUILD_STONESENSE:BOOL=1 \ -DBUILD_SUPPORTED:BOOL=1 \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ From ab4c7668171005234206c6908e58b86db7faca05 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 30 Jan 2023 08:48:45 -0800 Subject: [PATCH 0363/2222] Revert "allow tile list icons to be rendered properly" This reverts commit 508777897be527c1e7c64afe47b62bfcfc14503f. the fix was incorrect. the icon should be set to the pen when it is not a string, the icon_pen is only for when it is a string --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 653107179..0696459cd 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1713,7 +1713,7 @@ function List:onRenderBody(dc) local function paint_icon(icon, obj) if type(icon) ~= 'string' then - dc:tile(nil,icon) + dc:char(nil,icon) else if current then dc:string(icon, obj.icon_pen or self.icon_pen or cur_pen) From 2cbf9123f2725ed8806fc16d5cc4fe88a8d65b4a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 30 Jan 2023 22:23:13 +0000 Subject: [PATCH 0364/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index fd293eab7..9d42d260e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit fd293eab744d4c01db66960fc9e7a858f0b6db1e +Subproject commit 9d42d260e7baa5b4962582bc93f87ad37da39827 From 0c92317cce75d8d339832174d8fc411dd79502bb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 30 Jan 2023 13:30:15 -0800 Subject: [PATCH 0365/2222] give gui/control-panel a global hotkey --- data/init/dfhack.keybindings.init | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index c1a4191ef..a793e3248 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -16,6 +16,9 @@ keybinding add Ctrl-Shift-P "gui/launcher --minimal" # show hotkey popup menu keybinding add Ctrl-Shift-C hotkeys +# control panel +keybinding add Shift-` gui/control-panel + # on-screen keyboard keybinding add Ctrl-Shift-K gui/cp437-table From 3805925c02e5a5f877365609d5d93cc446eb715c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 30 Jan 2023 13:30:34 -0800 Subject: [PATCH 0366/2222] update quickstart guide with control panel info --- docs/Quickstart.rst | 111 +++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 81415e3a9..95b1fb0a1 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -46,53 +46,66 @@ Here are some common tasks people use DFHack tools to accomplish: your mining efforts Some tools are one-shot commands. For example, you can run `unforbid all ` -to claim all items on the map after a messy siege. +to claim all (reachable) items on the map after a messy siege. Other tools must be `enabled ` and then they will run in the background. For example, `enable seedwatch ` will start monitoring your stocks of seeds and prevent your chefs from cooking seeds that you need for planting. Tools that are enabled in the context of a fort will save their state with that -fort, and the will remember that they are enabled the next time you load your save. +fort, and they will remember that they are enabled the next time you load your save. A third class of tools add information to the screen or provide new integrated functionality via the DFHack `overlay` framework. For example, the `unsuspend` tool, in addition to its basic function of unsuspending all building construction jobs, can also overlay a marker on suspended buildings to indicate that they are suspended (and will use different markers to tell you whether this is a problem). -These overlays can be enabled and configured with the `gui/overlay` interface. How can I figure out which commands to run? ------------------------------------------- -There are several ways to scan DFHack tools and find the one you need right now. +There are several ways to scan DFHack tools and find the ones you need right now. The first place to check is the DFHack logo hover hotspot. It's in the upper left corner of the screen by default, though you can move it anywhere you want -with the `gui/overlay` tool. +with the `gui/overlay` configuration UI. When you hover the mouse over the logo (or hit the Ctrl-Shift-C keyboard shortcut) a list of DFHack tools relevant to the current context comes up. For example, when you have a unit selected, the hotspot will show a list of tools that inspect units, allow you to edit them, or maybe even teleport them. Next to each tool, -you'll see the global hotkey you can hit to invoke the command without even -opening the hover list. - -You can run any DFHack tool from `gui/launcher`, which is always listed first in -the hover list. You can also bring up the launcher by tapping the backtick key -(\`) or hitting Ctrl-Shift-D. In the launcher, you can quickly autocomplete any -command name by selecting it in the list on the right side of the window. -Commands are ordered by how often you run them, so your favorite commands will -always be on top. You can also pull full commandlines out of your history with -Alt-S (or by clicking on the "history search" hotkey hint). +you'll see the hotkey you can hit to invoke the command without even opening the +hover list. + +The second place to check is the DFHack control panel: `gui/control-panel`. It +will give you an overview of which tools are currently enabled, and will allow +you to toggle them on or off, see help text for them, or launch their dedicated +configuration UIs. You can launch the control panel from anywhere with the +tilde key (Shift-\`) or from the logo hover list. + +In the control panel, you can also select which tools you'd like to be +automatically enabled when you start a new fort. There are also system settings +you can change, like whether DFHack windows will pause the game when they come +up. + +Finally, you can explore the full extent of the DFHack catalog in `gui/launcher`, +which is always listed first in the DFHack logo hover list. You can also bring up +the launcher by tapping the backtick key (\`) or hitting Ctrl-Shift-D. In the +launcher, you can quickly autocomplete any command name by selecting it in the +list on the right side of the window. Commands are ordered by how often you run +them, so your favorite commands will always be on top. You can also pull full +commandlines out of your history with Alt-S or by clicking on the "history search" +hotkey hint. Once you have typed (or autocompleted, or searched for) a command, other commands -related to the one you have selected will appear in the autocomplete list. -Scanning through that list is a great way to learn about new tools that you might -find useful. You can also see how commands are grouped by running the `tags` command. +related to the one you have selected will appear in the right-hand panel. Scanning +through that list is a great way to learn about new tools that you might find +useful. You can also see how commands are grouped by running the `tags` command. The bottom panel will show the full help text for the command you are running, allowing you to refer to the usage documentation and examples when you are typing -your command. +your command. After you run a command, the bottom panel switches to command output +mode, but you can get back to the help text by hitting Ctrl-T or clicking on the +``Showing`` selector. How do DFHack in-game windows work? ----------------------------------- @@ -106,26 +119,27 @@ whether they capture keyboard and mouse input. The DFHack windowing system allows multiple overlapping windows to be active at once. The one with the highlighted title bar has focus and will receive anything you type at the keyboard. Hit Esc or right click to close the window or cancel -the current operation. You can click anywhere on the screen that is not a -DFHack window to unfocus the window and let it just sit in the background. It won't +the current action. You can click anywhere on the screen that is not a DFHack +window to unfocus the window and let it just sit in the background. It won't respond to key presses or mouse clicks until you click on it again to give it -focus. You can right click directly on an unfocused window to close it without -left clicking to activate it first. +focus. If no DFHack windows are focused, you can right click directly on a window +to close it without left clicking to focus it first. DFHack windows are draggable from the title bar or from anywhere on the window that doesn't have a mouse-clickable widget on it. Many are resizable as well (if the tool window has components that can reasonably be resized). You can generally use DFHack tools without interrupting the game. That is, if the -game is unpaused, it can continue to run while a DFHack window is open. Many tools -will initially pause the game to let you focus on the task at hand, but you can -unpause like normal if you want. You can also interact with the map, scrolling it -with the keyboard or mouse and selecting units, buildings, and items. Some tools -will capture all keyboard input, such as tools with editable text fields, and some -will force-pause the game if it makes sense to, like `gui/quickfort`, since you -cannot interact with the map normally while trying to apply a blueprint. Windows -for tools that force-pause the game will have a pause icon in their upper right -corner to indicate which tool is responsible for the pausing. +game is unpaused, it can continue to run while a DFHack window is open. If configured +to do so in `gui/control-panel`, tools will initially pause the game to let you +focus on the task at hand, but you can unpause like normal if you want. You can +also interact with the map, scrolling it with the keyboard or mouse and selecting +units, buildings, and items. Some tools will capture all keyboard input, such as +tools with editable text fields, and some will force-pause the game if it makes +sense to, like `gui/quickfort`, since you cannot interact with the map normally +while trying to apply a blueprint. Windows for tools that force-pause the game +will have a pause icon in their upper right corner to indicate which tool is +preventing you from unpausing. Where do I go next? ------------------- @@ -135,33 +149,32 @@ To recap: You can get to popular, relevant tools for the current context by hovering the mouse over the DFHack logo or by hitting Ctrl-Shift-C. +You can enable DFHack tools and configure settings with `gui/control-panel`, +which you can access directly with the tilde key (Shift-\`). + You can get to the launcher and its integrated autocomplete, history search, and help text by hitting backtick (\`) or Ctrl-Shift-D, or, of course, by running it from the logo hover list. -You can list and start tools that run in the background with the `enable` -command. - -You can configure screen overlays with the `gui/overlay` tool. - -With those four tools, you have the complete DFHack tool suite at your +With those three tools, you have the complete DFHack tool suite at your fingertips. So what to run first? Here are a few commands to get you started. You can run them all from the launcher. First, let's import some useful manager orders to keep your fort stocked with basic necessities. Run ``orders import library/basic``. If you go to your mangager orders screen, you can see all the orders that have been created for you. - -Next, try setting up `autochop` by running the GUI configuration `gui/autochop`. -You can enable it from the GUI, so you don't need to run `enable autochop ` -directly. You can set a target number of logs, and autochop will manage -your logging industry for you. You can control where your woodsdwarves go to -cut down trees by setting up burrows and configuring autochop to only cut in -those burrows. If you have the extra screen space, go ahead and set the -`gui/autochop` window to minimal mode (click on the hint near the upper right -corner of the window or hit Alt-M) and click on the map so the window loses -keyboard focus. As you play the game, you can glance at the status panel to -check on your stocks of wood. +Note that you could have imported the orders directly from this screen as well, +using the DFHack `overlay` widget at the bottom of the manager orders panel. + +Next, try setting up `autochop` to automatically designate trees for chopping when +you get low on usable logs. Run `gui/control-panel` and select ``autochop`` in the +list. Click on the button to the left of the name or hit Enter to enable it. You +can then click on the ``[configure]`` button to launch `gui/autochop` if you'd +like to customize its settings. If you have the extra screen space, you can go +ahead and set the `gui/autochop` window to minimal mode (click on the hint near +the upper right corner of the window or hit Alt-M) and click on the map so the +window loses keyboard focus. As you play the game, you can glance at the live +status panel to check on your stocks of wood. Finally, let's do some fort design copy-pasting. Go to some bedrooms that you have set up in your fort. Run `gui/blueprint`, set a name for your blueprint by From 69860ce419ce2141c5e9165f12108e4b977ff283 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 30 Jan 2023 13:32:51 -0800 Subject: [PATCH 0367/2222] update changelog --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 74fc26086..a78b14fc7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,8 +54,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - `getplants`: ID values will now be accepted regardless of case -@ New borders for DFHack tool windows -- tell us what you think! +- `gui/control-panel`: new global hotkey: tilde (Shift-backtick on most keyboards) ## Documentation +-@ Quickstart guide has been updated with info on new window behavior and how to use the control panel ## API - ``Buildings::containsTile()``: no longer takes a ``room`` parameter since that's not how rooms work anymore. If the building has extents, the extents will be checked. otherwise, the result just depends on whether the tile is within the building's bounding box. From d7bd3e5d55180c488d938f6fc19cd6eb003327be Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 31 Jan 2023 07:14:26 +0000 Subject: [PATCH 0368/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 9d42d260e..0410847a6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9d42d260e7baa5b4962582bc93f87ad37da39827 +Subproject commit 0410847a6dd65872d58287147dc4d1c81b60f86f From d6a4b1d37a751846a4719ec4ad5a9b0e88bb411b Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 1 Feb 2023 07:15:02 +0000 Subject: [PATCH 0369/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 0410847a6..936051185 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 0410847a6dd65872d58287147dc4d1c81b60f86f +Subproject commit 9360511856f3f85664c39793a8c037b8d0ce5c53 From aa22917aeba47ffcf581f56afeb73c2f289d0194 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 31 Jan 2023 23:52:30 -0800 Subject: [PATCH 0370/2222] add new control panel textures --- data/art/control-panel.png | Bin 0 -> 639 bytes library/include/modules/Textures.h | 5 +++++ library/modules/Textures.cpp | 7 +++++++ 3 files changed, 12 insertions(+) create mode 100644 data/art/control-panel.png diff --git a/data/art/control-panel.png b/data/art/control-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5e9468af89d99e93137a1b95babf4479dae8bd GIT binary patch literal 639 zcmV-_0)YLAP)u4(3A3g<=htBMUaLt3`NV13#62n`I8G)`W z_&$#$%@e+90!i7xmv9k?If5q3NHXXBPDM0xAif2H7l&7)Ddyo6gfJj}Hqtf%k{fqy zge&m(Km_-j51)>n*bih_TR4gc@F8Lt;#+5}7$AG^wIxJ4)b)VIZigGlA`ssyX{nRs zVdFxEv;Ds?#W8&g1Yf?thx-`7fgNr7m7b_o~%u_MWvUY0|bj-U%3tmRHzOPX`%2R}hG z2U6Lxa|!~>WK6$dO|)o98o7K%7J=5okOl)8LAz+rH_V0y<>ml^oryEnG8qCMJ-q{A z$Vsb^)9@H=S`Lb)C9c{SnBa$P(-K!(8s4-d)x(3jX$b_sZ(Kq5FlxIJqJa3eG}B;i zTH{&xf`zp$jp=_(A7T|B?41W(z9qZ!01#jRdw6w(;>%@%0000EWmrjOO-%qQ00008 Z000000002eQ Date: Tue, 31 Jan 2023 23:54:27 -0800 Subject: [PATCH 0371/2222] allow tile in Label tokens to be either pen or id --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 3 ++- library/lua/gui/widgets.lua | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a78b14fc7..7fac2030c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -68,6 +68,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom - ``dfhack.units.getCitizens()``: gets a list of citizens +- ``Label``: token ``tile`` properties can now be either pens or numeric texture ids ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 658893ec1..78b324f63 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4658,7 +4658,8 @@ containing newlines, or a table with the following possible fields: * ``token.tile = pen`` - Specifies a pen to paint as one tile before the main part of the token. + Specifies a pen or texture index to paint as one tile before the main part of + the token. * ``token.width = ...`` diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 0696459cd..4228bc9ad 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1110,7 +1110,9 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled) if token.tile then x = x + 1 if dc then - dc:tile(nil, token.tile) + local tile_pen = tonumber(token.tile) and + to_pen{tile=token.tile} or token.tile + dc:char(nil, token.tile) if token.width then dc:advance(token.width-1) end From 5127f0657193766a8e96d983f76f19acd393cd84 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 31 Jan 2023 23:55:30 -0800 Subject: [PATCH 0372/2222] expose new tiles to Lua --- library/LuaApi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 699c8aef7..bb2392546 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1694,6 +1694,7 @@ static const LuaWrapper::FunctionReg dfhack_textures_module[] = { WRAPM(Textures, getRedPinTexposStart), WRAPM(Textures, getIconsTexposStart), WRAPM(Textures, getOnOffTexposStart), + WRAPM(Textures, getControlPanelTexposStart), WRAPM(Textures, getThinBordersTexposStart), WRAPM(Textures, getMediumBordersTexposStart), WRAPM(Textures, getPanelBordersTexposStart), From 0ff7ad0cc4a131502994388404f9f1004a8f83b6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 31 Jan 2023 23:55:54 -0800 Subject: [PATCH 0373/2222] update tailor docs --- docs/plugins/tailor.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index af7d253b2..4dc4f53a4 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -35,6 +35,9 @@ are used, and in what order. Example ------- +``enable tailor`` + Start replacing tattered clothes with default settings. + ``tailor materials silk cloth yarn`` Restrict the materials used for automatically manufacturing clothing to silk, cloth, and yarn, preferred in that order. This saves leather for From be40d55e64e2a5fd1f211e845a2a81bdbc0c6184 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 9 Jan 2023 17:31:49 -0800 Subject: [PATCH 0374/2222] update seedwatch --- docs/plugins/seedwatch.rst | 41 ++- library/include/LuaTools.h | 8 + library/include/modules/Kitchen.h | 30 +- library/modules/Kitchen.cpp | 78 ---- plugins/CMakeLists.txt | 2 +- plugins/lua/seedwatch.lua | 67 ++++ plugins/seedwatch.cpp | 588 ++++++++++++++++++------------ 7 files changed, 452 insertions(+), 362 deletions(-) create mode 100644 plugins/lua/seedwatch.lua diff --git a/docs/plugins/seedwatch.rst b/docs/plugins/seedwatch.rst index b41f3a977..12a728c51 100644 --- a/docs/plugins/seedwatch.rst +++ b/docs/plugins/seedwatch.rst @@ -5,42 +5,47 @@ seedwatch :summary: Manages seed and plant cooking based on seed stock levels. :tags: fort auto plants -Each seed type can be assigned a target. If the number of seeds of that type -falls below that target, then the plants and seeds of that type will be excluded -from cookery. If the number rises above the target + 20, then cooking will be -allowed. +Unlike brewing and other kinds of processing, cooking plants does not produce +a usable seed. By default, all plants are allowed to be cooked. This often leads +to the situation where dwarves have no seeds left to plant crops with because +they cooked all the relevant plants. Seedwatch protects you from this problem. -The plugin needs a fortress to be loaded and will deactivate automatically -otherwise. You have to reactivate with ``enable seedwatch`` after you load a -fort. +Each seed type can be assigned a target stock amount. If the number of seeds of +that type falls below that target, then the plants and seeds of that type will +be protected from cookery. If the number rises above the target + 20, then +cooking will be allowed again. Usage ----- ``enable seedwatch`` - Start managing seed and plant cooking. By default, no types are watched. - You have to add them with further ``seedwatch`` commands. + Start managing seed and plant cooking. By default, all types are watched + with a target of ``30``, but you can adjust the list or even + ``seedwatch clear`` it and start your own if you like. +``seedwatch [status]`` + Display whether seedwatch is enabled and prints out the watch list, along + with the current seed counts. ``seedwatch `` Adds the specified type to the watchlist (if it's not already there) and sets the target number of seeds to the specified number. You can pass the keyword ``all`` instead of a specific type to set the target for all types. -``seedwatch `` - Removes the specified type from the watch list. + If you set the target to ``0``, it removes the specified type from the + watch list. ``seedwatch clear`` - Clears all types from the watch list. -``seedwatch info`` - Display whether seedwatch is enabled and prints out the watch list. + Clears all types from the watch list. Same as ``seedwatch all 0``. -To print out a list of all plant types, you can run this command:: +To see a list of all plant types that you might want to set targets for, you can +run this command:: devel/query --table df.global.world.raws.plants.all --search ^id --maxdepth 1 Examples -------- -``seedwatch all 30`` - Adds all seeds to the watch list and sets the targets to 30. +``enable seedwatch`` + Adds all seeds to the watch list, sets the targets to 30, and starts + monitoring your seed stock levels. ``seedwatch MUSHROOM_HELMET_PLUMP 50`` Add Plump Helmets to the watch list and sets the target to 50. -``seedwatch MUSHROOM_HELMET_PLUMP`` +``seedwatch MUSHROOM_HELMET_PLUMP 0`` removes Plump Helmets from the watch list. diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 9e1901f03..ca9aac788 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -30,6 +30,7 @@ distribution. #include #include #include +#include #include "df/interfacest.h" @@ -378,6 +379,13 @@ namespace DFHack {namespace Lua { TableInsert(L, entry.first, entry.second); } + template + void Push(lua_State *L, const std::unordered_map &pmap) { + lua_createtable(L, 0, pmap.size()); + for (auto &entry : pmap) + TableInsert(L, entry.first, entry.second); + } + DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true); DFHACK_EXPORT bool IsCoreContext(lua_State *state); diff --git a/library/include/modules/Kitchen.h b/library/include/modules/Kitchen.h index 3fde8edf8..f2708c844 100644 --- a/library/include/modules/Kitchen.h +++ b/library/include/modules/Kitchen.h @@ -27,11 +27,6 @@ distribution. * kitchen settings */ #include "Export.h" -#include "Module.h" -#include "Types.h" -#include "VersionInfo.h" -#include "Core.h" -#include "modules/Items.h" #include "df/kitchen_exc_type.h" /** @@ -43,38 +38,21 @@ namespace DFHack { namespace Kitchen { +// print the exclusion list, with the material index also translated into its token (for organics) - for debug really +DFHACK_EXPORT void debug_print(color_ostream &out); + /** * Kitchen exclusions manipulator. Currently geared towards plants and seeds. * \ingroup grp_modules * \ingroup grp_kitchen */ -// print the exclusion list, with the material index also translated into its token (for organics) - for debug really -DFHACK_EXPORT void debug_print(color_ostream &out); - // remove this plant from the exclusion list if it is in it DFHACK_EXPORT void allowPlantSeedCookery(int32_t plant_id); // add this plant to the exclusion list, if it is not already in it DFHACK_EXPORT void denyPlantSeedCookery(int32_t plant_id); -// fills a map with info from the limit info storage entries in the exclusion list -DFHACK_EXPORT void fillWatchMap(std::map& watchMap); - -// Finds the index of a limit info storage entry. Returns -1 if not found. -DFHACK_EXPORT int findLimit(int32_t plant_id); - -// removes a limit info storage entry from the exclusion list if it's present -DFHACK_EXPORT bool removeLimit(int32_t plant_id); - -// add a limit info storage item to the exclusion list, or alters an existing one -DFHACK_EXPORT bool setLimit(int32_t plant_id, int16_t limit); - -// clears all limit info storage items from the exclusion list -DFHACK_EXPORT void clearLimits(); - -DFHACK_EXPORT std::size_t size(); - // Finds the index of a kitchen exclusion in plotinfo.kitchen.exc_types. Returns -1 if not found. DFHACK_EXPORT int findExclusion(df::kitchen_exc_type type, df::item_type item_type, int16_t item_subtype, @@ -90,5 +68,7 @@ DFHACK_EXPORT bool removeExclusion(df::kitchen_exc_type type, df::item_type item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_index); +DFHACK_EXPORT std::size_t size(); + } } diff --git a/library/modules/Kitchen.cpp b/library/modules/Kitchen.cpp index 65e47f528..5bbc2ee7c 100644 --- a/library/modules/Kitchen.cpp +++ b/library/modules/Kitchen.cpp @@ -78,84 +78,6 @@ void Kitchen::denyPlantSeedCookery(int32_t plant_id) type->material_defs.idx[plant_material_def::basic_mat]); } -void Kitchen::fillWatchMap(std::map& watchMap) -{ - watchMap.clear(); - for (std::size_t i = 0; i < size(); ++i) - { - if (plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMTYPE && - plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && - plotinfo->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) - { - watchMap[plotinfo->kitchen.mat_indices[i]] = plotinfo->kitchen.mat_types[i]; - } - } -} - -int Kitchen::findLimit(int32_t plant_id) -{ - for (size_t i = 0; i < size(); ++i) - { - if (plotinfo->kitchen.item_types[i] == SEEDLIMIT_ITEMTYPE && - plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && - plotinfo->kitchen.mat_indices[i] == plant_id && - plotinfo->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) - { - return int(i); - } - } - return -1; -} - -bool Kitchen::removeLimit(int32_t plant_id) -{ - int i = findLimit(plant_id); - if (i < 0) - return false; - - plotinfo->kitchen.item_types.erase(plotinfo->kitchen.item_types.begin() + i); - plotinfo->kitchen.item_subtypes.erase(plotinfo->kitchen.item_subtypes.begin() + i); - plotinfo->kitchen.mat_types.erase(plotinfo->kitchen.mat_types.begin() + i); - plotinfo->kitchen.mat_indices.erase(plotinfo->kitchen.mat_indices.begin() + i); - plotinfo->kitchen.exc_types.erase(plotinfo->kitchen.exc_types.begin() + i); - return true; -} - -bool Kitchen::setLimit(int32_t plant_id, int16_t limit) -{ - if (limit > SEEDLIMIT_MAX) - limit = SEEDLIMIT_MAX; - - int i = findLimit(plant_id); - if (i < 0) - { - plotinfo->kitchen.item_types.push_back(SEEDLIMIT_ITEMTYPE); - plotinfo->kitchen.item_subtypes.push_back(SEEDLIMIT_ITEMSUBTYPE); - plotinfo->kitchen.mat_types.push_back(limit); - plotinfo->kitchen.mat_indices.push_back(plant_id); - plotinfo->kitchen.exc_types.push_back(SEEDLIMIT_EXCTYPE); - } - else - { - plotinfo->kitchen.mat_types[i] = limit; - } - return true; -} - -void Kitchen::clearLimits() -{ - for (size_t i = 0; i < size(); ++i) - { - if (plotinfo->kitchen.item_types[i] == SEEDLIMIT_ITEMTYPE && - plotinfo->kitchen.item_subtypes[i] == SEEDLIMIT_ITEMSUBTYPE && - plotinfo->kitchen.exc_types[i] == SEEDLIMIT_EXCTYPE) - { - removeLimit(plotinfo->kitchen.mat_indices[i]); - --i; - } - } -} - size_t Kitchen::size() { return plotinfo->kitchen.item_types.size(); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 41b3ad542..d5f35fb21 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -149,7 +149,7 @@ add_subdirectory(remotefortressreader) #add_subdirectory(rendermax) dfhack_plugin(reveal reveal.cpp LINK_LIBRARIES lua) #dfhack_plugin(search search.cpp) -dfhack_plugin(seedwatch seedwatch.cpp) +dfhack_plugin(seedwatch seedwatch.cpp LINK_LIBRARIES lua) dfhack_plugin(showmood showmood.cpp) #dfhack_plugin(siege-engine siege-engine.cpp LINK_LIBRARIES lua) #dfhack_plugin(sort sort.cpp LINK_LIBRARIES lua) diff --git a/plugins/lua/seedwatch.lua b/plugins/lua/seedwatch.lua new file mode 100644 index 000000000..a72c65d47 --- /dev/null +++ b/plugins/lua/seedwatch.lua @@ -0,0 +1,67 @@ +local _ENV = mkmodule('plugins.seedwatch') + +local argparse = require('argparse') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +local function print_status() + print(('seedwatch is %s'):format(isEnabled() and "enabled" or "disabled")) + print() + print('usable seed counts and current targets:') + local watch_map, seed_counts = seedwatch_getData() + local sum = 0 + local plants = df.global.world.raws.plants.all + for k,v in pairs(seed_counts) do + print((' %4d/%d %s'):format(v, watch_map[k] or 0, plants[k].id)) + sum = sum + v + end + print() + print(('total usable seeds: %d'):format(sum)) +end + +local function set_target(name, num) + if not name or #name == 0 then + qerror('must specify "all" or plant name') + end + + num = tonumber(num) + num = num and math.floor(num) or nil + if not num or num < 0 then + qerror('target must be a non-negative integer') + end + + seedwatch_setTarget(name, num) +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = positionals[1] + if not command or command == 'status' then + print_status() + elseif command == 'clear' then + set_target('all', 0) + elseif positionals[2] and positionals[3] then + set_target(positionals[2], positionals[3]) + else + return false + end + + return true +end + +return _ENV diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index 13ed8e66e..c6482b1ac 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -4,218 +4,113 @@ // With thanks to peterix for DFHack and Quietust for information // http://www.bay12forums.com/smf/index.php?topic=91166.msg2605147#msg2605147 -#include -#include -#include -#include "Console.h" -#include "Core.h" -#include "Export.h" +#include "Debug.h" +#include "LuaTools.h" #include "PluginManager.h" -#include "modules/World.h" -#include "modules/Materials.h" +#include "TileTypes.h" + #include "modules/Kitchen.h" -#include "VersionInfo.h" -#include "df/world.h" -#include "df/plant_raw.h" +#include "modules/Maps.h" +#include "modules/Persistence.h" +#include "modules/Units.h" +#include "modules/World.h" + #include "df/item_flags.h" #include "df/items_other_id.h" +#include "df/plant_raw.h" +#include "df/world.h" + +#include -using namespace std; using namespace DFHack; using namespace df::enums; +using std::map; +using std::string; +using std::unordered_map; +using std::vector; + DFHACK_PLUGIN("seedwatch"); -DFHACK_PLUGIN_IS_ENABLED(running); // whether seedwatch is counting the seeds or not +DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -const int buffer = 20; // seed number buffer - 20 is reasonable +namespace DFHack { + // for configuration-related logging + DBG_DECLARE(seedwatch, config, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(seedwatch, cycle, DebugCategory::LINFO); +} // abbreviations for the standard plants -map abbreviations; - -bool ignoreSeeds(df::item_flags& f) // seeds with the following flags should not be counted -{ - return - f.bits.dump || - f.bits.forbid || - f.bits.garbage_collect || - f.bits.hidden || - f.bits.hostile || - f.bits.on_fire || - f.bits.rotten || - f.bits.trader || - f.bits.in_building || - f.bits.in_job; -}; +static unordered_map abbreviations; +static map world_plant_ids; +static const int DEFAULT_TARGET = 30; +static const int TARGET_BUFFER = 20; // seed number buffer; 20 is reasonable -// searches abbreviations, returns expansion if so, returns original if not -string searchAbbreviations(string in) -{ - if(abbreviations.count(in) > 0) - { - return abbreviations[in]; - } - else - { - return in; - } +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static const string SEED_CONFIG_KEY_PREFIX = string(plugin_name) + "/seed/"; +static PersistentDataItem config; +static unordered_map watched_seeds; + +enum ConfigValues { + CONFIG_IS_ENABLED = 0, }; -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if(enable == true) - { - if(Core::getInstance().isWorldLoaded()) - { - running = true; - out.print("seedwatch supervision started.\n"); - } else { - out.printerr( - "This plugin needs a fortress to be loaded and will deactivate automatically otherwise.\n" - "Activate with 'seedwatch start' after you load the game.\n" - ); - } - } else { - running = false; - out.print("seedwatch supervision stopped.\n"); - } +enum SeedConfigValues { + SEED_CONFIG_ID = 0, + SEED_CONFIG_TARGET = 1, +}; - return CR_OK; +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); } -command_result df_seedwatch(color_ostream &out, vector& parameters) -{ - CoreSuspender suspend; - - map plantIDs; - for(size_t i = 0; i < world->raws.plants.all.size(); ++i) - { - auto & plant = world->raws.plants.all[i]; - if (plant->material_defs.type[plant_material_def::seed] != -1) - plantIDs[plant->id] = i; - } - - t_gamemodes gm; - World::ReadGameMode(gm);// FIXME: check return value - - // if game mode isn't fortress mode - if(gm.g_mode != game_mode::DWARF || !World::isFortressMode(gm.g_type)) - { - // just print the help - return CR_WRONG_USAGE; - } +static PersistentDataItem & ensure_seed_config(color_ostream &out, int id) { + if (watched_seeds.count(id)) + return watched_seeds[id]; + string keyname = SEED_CONFIG_KEY_PREFIX + int_to_string(id); + DEBUG(config,out).print("creating new persistent key for seed type %d\n", id); + watched_seeds.emplace(id, World::GetPersistentData(keyname, NULL)); + return watched_seeds[id]; +} +static void remove_seed_config(color_ostream &out, int id) { + if (!watched_seeds.count(id)) + return; + DEBUG(config,out).print("removing persistent key for seed type %d\n", id); + World::DeletePersistentData(watched_seeds[id]); + watched_seeds.erase(id); +} - string par; - int limit; - switch(parameters.size()) - { - case 0: - return CR_WRONG_USAGE; - - case 1: - par = parameters[0]; - if ((par == "help") || (par == "?")) - { - return CR_WRONG_USAGE; - } - else if(par == "start") - { - plugin_enable(out, true); +static const int32_t CYCLE_TICKS = 1200; +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - } - else if(par == "stop") - { - plugin_enable(out, false); - } - else if(par == "clear") - { - Kitchen::clearLimits(); - out.print("seedwatch watchlist cleared\n"); - } - else if(par == "info") - { - out.print("seedwatch Info:\n"); - if(running) - { - out.print("seedwatch is supervising. Use 'disable seedwatch' to stop supervision.\n"); - } - else - { - out.print("seedwatch is not supervising. Use 'enable seedwatch' to start supervision.\n"); - } - map watchMap; - Kitchen::fillWatchMap(watchMap); - if(watchMap.empty()) - { - out.print("The watch list is empty.\n"); - } - else - { - out.print("The watch list is:\n"); - for(auto i = watchMap.begin(); i != watchMap.end(); ++i) - { - out.print("%s : %u\n", world->raws.plants.all[i->first]->id.c_str(), i->second); - } - } - } - else if(par == "debug") - { - map watchMap; - Kitchen::fillWatchMap(watchMap); - Kitchen::debug_print(out); - } - else - { - string token = searchAbbreviations(par); - if(plantIDs.count(token) > 0) - { - Kitchen::removeLimit(plantIDs[token]); - out.print("%s is not being watched\n", token.c_str()); - } - else - { - out.print("%s has not been found as a material.\n", token.c_str()); - } - } - break; - case 2: - limit = atoi(parameters[1].c_str()); - if(limit < 0) limit = 0; - if(parameters[0] == "all") - { - for(auto & entry : plantIDs) - Kitchen::setLimit(entry.second, limit); - } - else - { - string token = searchAbbreviations(parameters[0]); - if(plantIDs.count(token) > 0) - { - Kitchen::setLimit(plantIDs[token], limit); - out.print("%s is being watched.\n", token.c_str()); - } - else - { - out.print("%s has not been found as a material.\n", token.c_str()); - } - } - break; - default: - return CR_WRONG_USAGE; - break; - } +static command_result do_command(color_ostream &out, vector ¶meters); +static void do_cycle(color_ostream &out, int32_t *num_enabled_seeds, int32_t *num_disabled_seeds); +static void seedwatch_setTarget(color_ostream &out, string name, int32_t num); - return CR_OK; -} +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(config,out).print("initializing %s\n", plugin_name); -DFhackCExport command_result plugin_init(color_ostream &out, vector& commands) -{ + // provide a configuration interface for the plugin commands.push_back(PluginCommand( - "seedwatch", - "Toggles seed cooking based on quantity available.", - df_seedwatch)); - // fill in the abbreviations map, with abbreviations for the standard plants + plugin_name, + "Automatically toggle seed cooking based on quantity available.", + do_command)); + + // fill in the abbreviations map abbreviations["bs"] = "SLIVER_BARB"; abbreviations["bt"] = "TUBER_BLOATED"; abbreviations["bw"] = "WEED_BLADE"; @@ -237,72 +132,285 @@ DFhackCExport command_result plugin_init(color_ostream &out, vector seedCount; // the number of seeds - - // count all seeds and plants by RAW material - for(size_t i = 0; i < world->items.other[items_other_id::SEEDS].size(); ++i) - { - df::item *item = world->items.other[items_other_id::SEEDS][i]; - MaterialInfo mat(item); - if (!mat.isPlant()) - continue; - if (!ignoreSeeds(item->flags)) - ++seedCount[mat.plant->index]; - } +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} + +DFhackCExport command_result plugin_load_data (color_ostream &out) { + world_plant_ids.clear(); + for (size_t i = 0; i < world->raws.plants.all.size(); ++i) { + auto & plant = world->raws.plants.all[i]; + if (plant->material_defs.type[plant_material_def::seed] != -1) + world_plant_ids[plant->id] = i; + } + + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } + + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + watched_seeds.clear(); + vector seed_configs; + World::GetPersistentData(&seed_configs, SEED_CONFIG_KEY_PREFIX, true); + const size_t num_seed_configs = seed_configs.size(); + for (size_t idx = 0; idx < num_seed_configs; ++idx) { + auto &c = seed_configs[idx]; + watched_seeds.emplace(get_config_val(c, SEED_CONFIG_ID), c); + } - map watchMap; - Kitchen::fillWatchMap(watchMap); - for(auto i = watchMap.begin(); i != watchMap.end(); ++i) - { - if(seedCount[i->first] <= i->second) - { - Kitchen::denyPlantSeedCookery(i->first); - } - else if(i->second + buffer < seedCount[i->first]) - { - Kitchen::allowPlantSeedCookery(i->first); - } + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; } } return CR_OK; } -DFhackCExport command_result plugin_shutdown(Core* pCore) -{ +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int32_t num_enabled_seeds, num_disabled_seeds; + do_cycle(out, &num_enabled_seeds, &num_disabled_seeds); + if (0 < num_enabled_seeds) + out.print("%s: enabled %d seed types for cooking\n", + plugin_name, num_enabled_seeds); + if (0 < num_disabled_seeds) + out.print("%s: protected %d seed types from cooking\n", + plugin_name, num_disabled_seeds); + } return CR_OK; } + +static bool call_seedwatch_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + DEBUG(config,*out).print("calling %s lua function: '%s'\n", plugin_name, fn_name); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.seedwatch", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} + +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + bool show_help = false; + if (!call_seedwatch_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; + } + + return show_help ? CR_WRONG_USAGE : CR_OK; +} + +///////////////////////////////////////////////////// +// cycle logic +// + +struct BadFlags { + uint32_t whole; + + // TODO: maybe don't filter out seeds that are in_building. that would + // allow us to count seeds that are in workshops. are there any negative + // consequences? + BadFlags() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(artifact); + F(in_job); F(owned); F(in_chest); F(removed); + F(encased); F(spider_web); + #undef F + whole = flags.whole; + } +}; + +static bool is_accessible_item(const df::coord &pos, const vector &citizens) { + for (auto &unit : citizens) { + if (Maps::canWalkBetween(unit->pos, pos)) + return true; + } + return false; +} + +static void scan_seeds(color_ostream &out, unordered_map *accessible_counts, + unordered_map *inaccessible_counts = NULL) { + static const BadFlags bad_flags; + + vector citizens; + Units::getCitizens(citizens); + + for (auto &item : world->items.other[items_other_id::SEEDS]) { + MaterialInfo mat(item); + if (!mat.isPlant()) + continue; + if ((bad_flags.whole & item->flags.whole) || !is_accessible_item(item->pos, citizens)) { + if (inaccessible_counts) + ++(*inaccessible_counts)[mat.plant->index]; + } else { + if (accessible_counts) + ++(*accessible_counts)[mat.plant->index]; + } + } +} + +static void do_cycle(color_ostream &out, int32_t *num_enabled_seed_types, int32_t *num_disabled_seed_types) { + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + if (num_enabled_seed_types) + *num_enabled_seed_types = 0; + if (num_disabled_seed_types) + *num_disabled_seed_types = 0; + + unordered_map accessible_counts; + scan_seeds(out, &accessible_counts); + + for (auto &entry : watched_seeds) { + int32_t id = entry.first; + int32_t target = get_config_val(entry.second, SEED_CONFIG_TARGET); + if (accessible_counts[id] <= target) { + DEBUG(cycle,out).print("disabling seed mat: %d\n", id); + if (num_disabled_seed_types) + ++*num_disabled_seed_types; + Kitchen::denyPlantSeedCookery(id); + } else if (target + TARGET_BUFFER < accessible_counts[id]) { + DEBUG(cycle,out).print("enabling seed mat: %d\n", id); + if (num_enabled_seed_types) + ++*num_enabled_seed_types; + Kitchen::allowPlantSeedCookery(id); + } + } +} + +///////////////////////////////////////////////////// +// Lua API +// core will already be suspended when coming in through here +// + +static void set_target(color_ostream &out, int32_t id, int32_t target) { + if (target == 0) + remove_seed_config(out, id); + else { + PersistentDataItem &c = ensure_seed_config(out, id); + set_config_val(c, SEED_CONFIG_TARGET, target); + } +} + +// searches abbreviations, returns expansion if so, returns original if not +static string searchAbbreviations(string in) { + if(abbreviations.count(in) > 0) + return abbreviations[in]; + return in; +}; + +static void seedwatch_setTarget(color_ostream &out, string name, int32_t num) { + DEBUG(config,out).print("entering seedwatch_setTarget\n"); + + if (num < 0) { + out.printerr("target must be at least 0\n"); + return; + } + + if (name == "all") { + for (auto &entry : world_plant_ids) { + set_target(out, entry.second, num); + } + return; + } + + string token = searchAbbreviations(name); + if (!world_plant_ids.count(token)) { + out.printerr("%s has not been found as a material.\n", token.c_str()); + return; + } + + set_target(out, world_plant_ids[token], num); +} + +static int seedwatch_getData(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(config,*out).print("entering seedwatch_getData\n"); + unordered_map watch_map, accessible_counts; + scan_seeds(*out, &accessible_counts); + for (auto &entry : watched_seeds) { + watch_map.emplace(entry.first, get_config_val(entry.second, SEED_CONFIG_TARGET)); + } + Lua::Push(L, watch_map); + Lua::Push(L, accessible_counts); + return 2; +} + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(seedwatch_setTarget), + DFHACK_LUA_END +}; + +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(seedwatch_getData), + DFHACK_LUA_END +}; From febb2bf03052a5d811bf2d766437de36bbd17817 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 04:24:42 -0800 Subject: [PATCH 0375/2222] use actual item and unit positions --- plugins/autochop.cpp | 8 +++++--- plugins/seedwatch.cpp | 11 +++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index e44b4f804..81c6196a1 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -7,6 +7,7 @@ #include "modules/Burrows.h" #include "modules/Designations.h" +#include "modules/Items.h" #include "modules/Maps.h" #include "modules/Persistence.h" #include "modules/Units.h" @@ -253,9 +254,10 @@ static command_result do_command(color_ostream &out, vector ¶meters) // cycle logic // -static bool is_accessible_item(const df::coord &pos, const vector &citizens) { +static bool is_accessible_item(df::item *item, const vector &citizens) { + const df::coord pos = Items::getPosition(item); for (auto &unit : citizens) { - if (Maps::canWalkBetween(unit->pos, pos)) + if (Maps::canWalkBetween(Units::getPosition(unit), pos)) return true; } return false; @@ -518,7 +520,7 @@ static void scan_logs(int32_t *usable_logs, const vector &citizens, if (!is_valid_item(item)) continue; - if (!is_accessible_item(item->pos, citizens)) { + if (!is_accessible_item(item, citizens)) { if (inaccessible_logs) ++*inaccessible_logs; } else if (usable_logs) { diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index c6482b1ac..03ea4f268 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -9,6 +9,7 @@ #include "PluginManager.h" #include "TileTypes.h" +#include "modules/Items.h" #include "modules/Kitchen.h" #include "modules/Maps.h" #include "modules/Persistence.h" @@ -269,9 +270,6 @@ static command_result do_command(color_ostream &out, vector ¶meters) struct BadFlags { uint32_t whole; - // TODO: maybe don't filter out seeds that are in_building. that would - // allow us to count seeds that are in workshops. are there any negative - // consequences? BadFlags() { df::item_flags flags; #define F(x) flags.bits.x = true; @@ -285,9 +283,10 @@ struct BadFlags { } }; -static bool is_accessible_item(const df::coord &pos, const vector &citizens) { +static bool is_accessible_item(df::item *item, const vector &citizens) { + const df::coord pos = Items::getPosition(item); for (auto &unit : citizens) { - if (Maps::canWalkBetween(unit->pos, pos)) + if (Maps::canWalkBetween(Units::getPosition(unit), pos)) return true; } return false; @@ -304,7 +303,7 @@ static void scan_seeds(color_ostream &out, unordered_map *acce MaterialInfo mat(item); if (!mat.isPlant()) continue; - if ((bad_flags.whole & item->flags.whole) || !is_accessible_item(item->pos, citizens)) { + if ((bad_flags.whole & item->flags.whole) || !is_accessible_item(item, citizens)) { if (inaccessible_counts) ++(*inaccessible_counts)[mat.plant->index]; } else { From aae5d5f41157fe31cb7247c3b1a36c8949a983e5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 04:28:07 -0800 Subject: [PATCH 0376/2222] reduce diff in Kitchen.h --- library/include/modules/Kitchen.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/include/modules/Kitchen.h b/library/include/modules/Kitchen.h index f2708c844..ed8eb95b1 100644 --- a/library/include/modules/Kitchen.h +++ b/library/include/modules/Kitchen.h @@ -38,21 +38,23 @@ namespace DFHack { namespace Kitchen { -// print the exclusion list, with the material index also translated into its token (for organics) - for debug really -DFHACK_EXPORT void debug_print(color_ostream &out); - /** * Kitchen exclusions manipulator. Currently geared towards plants and seeds. * \ingroup grp_modules * \ingroup grp_kitchen */ +// print the exclusion list, with the material index also translated into its token (for organics) - for debug really +DFHACK_EXPORT void debug_print(color_ostream &out); + // remove this plant from the exclusion list if it is in it DFHACK_EXPORT void allowPlantSeedCookery(int32_t plant_id); // add this plant to the exclusion list, if it is not already in it DFHACK_EXPORT void denyPlantSeedCookery(int32_t plant_id); +DFHACK_EXPORT std::size_t size(); + // Finds the index of a kitchen exclusion in plotinfo.kitchen.exc_types. Returns -1 if not found. DFHACK_EXPORT int findExclusion(df::kitchen_exc_type type, df::item_type item_type, int16_t item_subtype, @@ -68,7 +70,5 @@ DFHACK_EXPORT bool removeExclusion(df::kitchen_exc_type type, df::item_type item_type, int16_t item_subtype, int16_t mat_type, int32_t mat_index); -DFHACK_EXPORT std::size_t size(); - } } From b02405ea97556286571eb4846451ed83e0cddeee Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 04:30:45 -0800 Subject: [PATCH 0377/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index a78b14fc7..bf64efb88 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `getplants`: ID values will now be accepted regardless of case -@ New borders for DFHack tool windows -- tell us what you think! - `gui/control-panel`: new global hotkey: tilde (Shift-backtick on most keyboards) +- `seedwatch`: now persists enabled state in the savegame, automatically loads useful defaults, and respects reachability when counting available seeds ## Documentation -@ Quickstart guide has been updated with info on new window behavior and how to use the control panel From fd4b0d72330add10222fa8ef4a51ad2b9b3baf24 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 04:56:25 -0800 Subject: [PATCH 0378/2222] update and simplify nestboxes; persist state --- docs/changelog.txt | 1 + plugins/nestboxes.cpp | 201 ++++++++++++++++++++++++------------------ 2 files changed, 118 insertions(+), 84 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a78b14fc7..4b578f1da 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - A new cross-compile build script was added for building DFHack for Windows from a Linux Docker builder (see the `compile` instructions in the docs) - You can now configure whether DFHack tool windows should pause the game by default - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu +- `nestboxes`: now saves enabled state in your savegame - `gui/launcher`: sped up initialization time for faster load of the UI - `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open - `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 1908684d4..8feee65f7 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -1,120 +1,153 @@ -#include "Core.h" -#include "Console.h" -#include "Export.h" +#include "Debug.h" #include "PluginManager.h" -#include "DataDefs.h" +#include "modules/Persistence.h" +#include "modules/World.h" + #include "df/world.h" -#include "df/plotinfost.h" #include "df/building_nest_boxst.h" -#include "df/building_type.h" -#include "df/buildings_other_id.h" -#include "df/global_objects.h" #include "df/item.h" #include "df/unit.h" -#include "df/building.h" -#include "df/items_other_id.h" -#include "df/creature_raw.h" -#include "modules/MapCache.h" -#include "modules/Items.h" - -using std::vector; using std::string; -using std::endl; using namespace DFHack; using namespace df::enums; -using df::global::world; -using df::global::plotinfo; +DFHACK_PLUGIN("nestboxes"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); -static command_result nestboxes(color_ostream &out, vector & parameters); +REQUIRE_GLOBAL(world); -DFHACK_PLUGIN("nestboxes"); +namespace DFHack { + // for configuration-related logging + DBG_DECLARE(nestboxes, config, DebugCategory::LINFO); + // for logging during the periodic scan + DBG_DECLARE(nestboxes, cycle, DebugCategory::LINFO); +} -DFHACK_PLUGIN_IS_ENABLED(enabled); - -static void eggscan(color_ostream &out) -{ - CoreSuspender suspend; - - for (df::building *build : world->buildings.other[df::buildings_other_id::NEST_BOX]) - { - auto type = build->getType(); - if (df::enums::building_type::NestBox == type) - { - bool fertile = false; - df::building_nest_boxst *nb = virtual_cast(build); - if (nb->claimed_by != -1) - { - df::unit* u = df::unit::find(nb->claimed_by); - if (u && u->pregnancy_timer > 0) - fertile = true; - } - for (size_t j = 1; j < nb->contained_items.size(); j++) - { - df::item* item = nb->contained_items[j]->item; - if (item->flags.bits.forbid != fertile) - { - item->flags.bits.forbid = fertile; - out << item->getStackSize() << " eggs " << (fertile ? "forbidden" : "unforbidden.") << endl; - } - } - } - } +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static PersistentDataItem config; + +enum ConfigValues { + CONFIG_IS_ENABLED = 0, +}; + +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} + +static const int32_t CYCLE_TICKS = 100; // need to react quickly if eggs are unforbidden +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static void do_cycle(color_ostream &out, int32_t *num_enabled_seeds, int32_t *num_disabled_seeds); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(config,out).print("initializing %s\n", plugin_name); + + return CR_OK; } +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } -DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) -{ - if (world && plotinfo) { - commands.push_back( - PluginCommand( - "nestboxes", - "Protect fertile eggs incubating in a nestbox.", - nestboxes)); + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(config,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } else { + DEBUG(config,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); } return CR_OK; } -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); + return CR_OK; } -DFhackCExport command_result plugin_onupdate(color_ostream &out) -{ - if (!enabled) - return CR_OK; +DFhackCExport command_result plugin_load_data (color_ostream &out) { + config = World::GetPersistentData(CONFIG_KEY); - static unsigned cnt = 0; - if ((++cnt % 5) != 0) - return CR_OK; + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } - eggscan(out); + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); return CR_OK; } -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - enabled = enable; +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; + } + } return CR_OK; } -static command_result nestboxes(color_ostream &out, vector & parameters) -{ - CoreSuspender suspend; - - if (parameters.size() == 1) { - if (parameters[0] == "enable") - enabled = true; - else if (parameters[0] == "disable") - enabled = false; - else - return CR_WRONG_USAGE; - } else { - out << "Plugin " << (enabled ? "enabled" : "disabled") << "." << endl; +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int32_t num_enabled_seeds, num_disabled_seeds; + do_cycle(out, &num_enabled_seeds, &num_disabled_seeds); + if (0 < num_enabled_seeds) + out.print("%s: enabled %d seed types for cooking\n", + plugin_name, num_enabled_seeds); + if (0 < num_disabled_seeds) + out.print("%s: protected %d seed types from cooking\n", + plugin_name, num_disabled_seeds); } return CR_OK; } + +///////////////////////////////////////////////////// +// cycle logic +// + +static void do_cycle(color_ostream &out, int32_t *num_enabled_seed_types, int32_t *num_disabled_seed_types) { + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + for (df::building_nest_boxst *nb : world->buildings.other.NEST_BOX) { + bool fertile = false; + if (nb->claimed_by != -1) { + df::unit *u = df::unit::find(nb->claimed_by); + if (u && u->pregnancy_timer > 0) + fertile = true; + } + for (auto &contained_item : nb->contained_items) { + df::item *item = contained_item->item; + if (item->flags.bits.forbid != fertile) { + item->flags.bits.forbid = fertile; + out.print("%d eggs %s.\n", item->getStackSize(), fertile ? "forbidden" : "unforbidden"); + } + } + } +} From 88860f21ec097e458f22d1548dfed7ab2e3351af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 08:46:32 -0800 Subject: [PATCH 0379/2222] add defocusable attribute to ZScreen --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 6 ++++++ library/lua/gui.lua | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a78b14fc7..683db6359 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -68,6 +68,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom - ``dfhack.units.getCitizens()``: gets a list of citizens +- ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 658893ec1..dc33438d7 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4187,6 +4187,12 @@ ZScreen provides the following functions: ZScreen subclasses can set the following attributes: +* ``defocusable`` (default: ``true``) + + Whether the ZScreen loses keyboard focus when the player clicks on an area + of the screen other than the tool window. If the player clicks on a different + ZScreen window, focus still transfers to that other ZScreen. + * ``initial_pause`` (default: ``DEFAULT_INITIAL_PAUSE``) Whether to pause the game when the ZScreen is shown. ``DEFAULT_INITIAL_PAUSE`` diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 061444f5d..b0aa35c81 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -699,6 +699,7 @@ local zscreen_inhibit_mouse_l = false ZScreen = defclass(ZScreen, Screen) ZScreen.ATTRS{ + defocusable=true, initial_pause=DEFAULT_NIL, force_pause=false, pass_pause=true, @@ -793,7 +794,7 @@ function ZScreen:onInput(keys) end if self.pass_mouse_clicks and keys._MOUSE_L_DOWN and not has_mouse then - self.defocused = true + self.defocused = self.defocusable self:sendInputToParent(keys) return elseif keys.LEAVESCREEN or keys._MOUSE_R_DOWN then From 6ae771ecb49b7a30c9ac4c4ea998dbc7ebf99b7d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 14:01:29 -0800 Subject: [PATCH 0380/2222] display PAUSE FORCED instead of a pause icon if the window is forcing the game to pause. the icon looked too clickable --- docs/changelog.txt | 1 + library/lua/gui.lua | 17 +++-------------- library/lua/gui/widgets.lua | 20 -------------------- 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a78b14fc7..71bbd00a4 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `getplants`: ID values will now be accepted regardless of case -@ New borders for DFHack tool windows -- tell us what you think! - `gui/control-panel`: new global hotkey: tilde (Shift-backtick on most keyboards) +- Windows now display "PAUSE FORCED" on the lower border if the tool is forcing the game to pause ## Documentation -@ Quickstart guide has been updated with info on new window behavior and how to use the control panel diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 061444f5d..9e69f961c 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -886,7 +886,7 @@ local BASE_FRAME = { title_pen = to_pen{ fg=COLOR_BLACK, bg=COLOR_GREY }, inactive_title_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, signature_pen = to_pen{ fg=COLOR_GREY, bg=COLOR_BLACK }, - paused_pen = to_pen{tile=782, ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}, + paused_pen = to_pen{fg=COLOR_RED, bg=COLOR_BLACK}, } local function make_frame(name, double_line) @@ -946,19 +946,8 @@ function paint_frame(dc,rect,style,title,inactive,pause_forced,resizable) end if pause_forced then - -- get the tiles for the activated pause symbol - local pause_texpos_ul = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 18, 28) - local pause_texpos_ur = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 19, 28) - local pause_texpos_ll = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 18, 29) - local pause_texpos_lr = dfhack.screen.findGraphicsTile('INTERFACE_BITS', 19, 29) - if not pause_texpos_ul then - dscreen.paintTile(style.paused_pen, x2-1, y1) - else - dscreen.paintTile(style.paused_pen, x2-2, y1-1, nil, pause_texpos_ul) - dscreen.paintTile(style.paused_pen, x2-1, y1-1, nil, pause_texpos_ur) - dscreen.paintTile(style.paused_pen, x2-2, y1, nil, pause_texpos_ll) - dscreen.paintTile(style.paused_pen, x2-1, y1, nil, pause_texpos_lr) - end + dscreen.paintString(style.paused_pen or style.title_pen or pen, + x1+2, y2, ' PAUSE FORCED ') end end diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 0696459cd..ce8e27ff5 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -271,26 +271,6 @@ local function Panel_on_double_click(self) Panel_update_frame(self, frame, true) end -local function panel_mouse_is_on_pause_icon(self) - local frame_rect = self.frame_rect - local x,y = dscreen.getMousePos() - return (x == frame_rect.x2-2 or x == frame_rect.x2-1) - and (y == frame_rect.y1-1 or y == frame_rect.y1) -end - -local function panel_has_pause_icon(self) - return self.parent_view and self.parent_view.force_pause -end - -function Panel:getMouseFramePos() - local x,y = Panel.super.getMouseFramePos(self) - if x then return x, y end - if panel_has_pause_icon(self) and panel_mouse_is_on_pause_icon(self) then - local frame_rect = self.frame_rect - return frame_rect.width - 3, 0 - end -end - function Panel:onInput(keys) if self.kbd_get_pos then if keys.SELECT or keys.LEAVESCREEN or keys._MOUSE_R_DOWN then From 55d07a8cae86da7c05472ebb52ba5712e5f68b8a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 14:51:46 -0800 Subject: [PATCH 0381/2222] actually use the tile --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 4228bc9ad..21143f053 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1112,7 +1112,7 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled) if dc then local tile_pen = tonumber(token.tile) and to_pen{tile=token.tile} or token.tile - dc:char(nil, token.tile) + dc:char(nil, tile_pen) if token.width then dc:advance(token.width-1) end From 58be8cfd69b63b883889651578fa1a07df698003 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 17:39:32 -0800 Subject: [PATCH 0382/2222] support offset text in graphics mode for pens --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 6 ++++++ library/LuaApi.cpp | 2 ++ library/include/modules/Screen.h | 2 ++ library/modules/Screen.cpp | 20 ++++++++++++++++---- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a78b14fc7..396bf89d2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,6 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Buildings::containsTile()``: no longer takes a ``room`` parameter since that's not how rooms work anymore. If the building has extents, the extents will be checked. otherwise, the result just depends on whether the tile is within the building's bounding box. - ``Units::getCitizens()``: gets a list of citizens, which otherwise you'd have to iterate over all units the world to discover +- ``Screen::Pen``: now accepts ``top_of_text`` and ``bottom_of_text`` properties to support offset text in graphics mode ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 658893ec1..9e8604b15 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2292,6 +2292,12 @@ a table with the following possible fields: ``write_to_lower`` If set to true, the specified ``tile`` will be written to the background instead of the foreground. + ``top_of_text`` + If set to true, the specified ``tile`` will have the top half of the specified + ``ch`` character superimposed over the lower half of the tile. + ``bottom_of_text`` + If set to true, the specified ``tile`` will have the bottom half of the specified + ``ch`` character superimposed over the top half of the tile. Alternatively, it may be a pre-parsed native object with the following API: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 699c8aef7..1b674e0e8 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -222,6 +222,8 @@ static void decode_pen(lua_State *L, Pen &pen, int idx) get_bool_field(L, &pen.keep_lower, idx, "keep_lower", false); get_bool_field(L, &pen.write_to_lower, idx, "write_to_lower", false); + get_bool_field(L, &pen.top_of_text, idx, "top_of_text", false); + get_bool_field(L, &pen.bottom_of_text, idx, "bottom_of_text", false); } /************************************************** diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 48f34888f..0f0afd6e2 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -86,6 +86,8 @@ namespace DFHack bool write_to_lower = false; bool keep_lower = false; + bool top_of_text = false; + bool bottom_of_text = false; bool valid() const { return tile >= 0; } bool empty() const { return ch == 0 && tile == 0; } diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 5627c08da..13dbcb204 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -155,14 +155,18 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) long *texpos_lower = &gps->screentexpos_lower[index]; uint32_t *flag = &gps->screentexpos_flag[index]; + // keep SCREENTEXPOS_FLAG_ANCHOR_SUBORDINATE so occluded anchored textures + // don't appear corrupted + uint32_t flag_mask = 0x4; + if (pen.write_to_lower) + flag_mask |= 0x18; + *screen = 0; *texpos = 0; if (!pen.keep_lower) *texpos_lower = 0; gps->screentexpos_anchored[index] = 0; - // keep SCREENTEXPOS_FLAG_ANCHOR_SUBORDINATE so occluded anchored textures - // don't appear corrupted - *flag &= 4; + *flag &= flag_mask; if (gps->top_in_use) { screen = &gps->screen_top[index * 8]; @@ -175,7 +179,7 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) if (!pen.keep_lower) *texpos_lower = 0; gps->screentexpos_top_anchored[index] = 0; - *flag &= 4; // keep SCREENTEXPOS_FLAG_ANCHOR_SUBORDINATE + *flag &= flag_mask; } uint8_t fg = pen.fg | (pen.bold << 3); @@ -196,6 +200,14 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) *texpos_lower = pen.tile; else *texpos = pen.tile; + + if (pen.top_of_text || pen.bottom_of_text) { + screen[0] = uint8_t(pen.ch); + if (pen.top_of_text) + *flag |= 0x8; + if (pen.bottom_of_text) + *flag |= 0x10; + } } else if (pen.ch) { screen[0] = uint8_t(pen.ch); *texpos_lower = 909; // basic black background From 648f2649062aa20d68170f6ffacd23507fd28a02 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 2 Feb 2023 01:55:29 +0000 Subject: [PATCH 0383/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 936051185..e57e82a4c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9360511856f3f85664c39793a8c037b8d0ce5c53 +Subproject commit e57e82a4c059a197e775f14e0fec9946fe50eddd From ca2078c62d5b0c3a3a3011e6f14cf532c3c34da9 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 2 Feb 2023 05:00:25 +0000 Subject: [PATCH 0384/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index e57e82a4c..594bb2394 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e57e82a4c059a197e775f14e0fec9946fe50eddd +Subproject commit 594bb2394cf670b5ac0d7f414a32cf48ed86b26d From 5a1c3c7aa81d0ef25bd422fc53412f9d20da1637 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 1 Feb 2023 22:30:56 -0800 Subject: [PATCH 0385/2222] remove unused vars copypastad from seedwatch --- plugins/nestboxes.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 8feee65f7..98becf170 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -51,7 +51,7 @@ static void set_config_bool(PersistentDataItem &c, int index, bool value) { static const int32_t CYCLE_TICKS = 100; // need to react quickly if eggs are unforbidden static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle -static void do_cycle(color_ostream &out, int32_t *num_enabled_seeds, int32_t *num_disabled_seeds); +static void do_cycle(color_ostream &out); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { DEBUG(config,out).print("initializing %s\n", plugin_name); @@ -112,16 +112,8 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan } DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { - int32_t num_enabled_seeds, num_disabled_seeds; - do_cycle(out, &num_enabled_seeds, &num_disabled_seeds); - if (0 < num_enabled_seeds) - out.print("%s: enabled %d seed types for cooking\n", - plugin_name, num_enabled_seeds); - if (0 < num_disabled_seeds) - out.print("%s: protected %d seed types from cooking\n", - plugin_name, num_disabled_seeds); - } + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) + do_cycle(out); return CR_OK; } @@ -129,7 +121,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) { // cycle logic // -static void do_cycle(color_ostream &out, int32_t *num_enabled_seed_types, int32_t *num_disabled_seed_types) { +static void do_cycle(color_ostream &out) { DEBUG(cycle,out).print("running %s cycle\n", plugin_name); // mark that we have recently run From 75bb67cfc4735084b848039a15996bd0198fc555 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 2 Feb 2023 07:14:31 +0000 Subject: [PATCH 0386/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 594bb2394..a2ab2e52a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 594bb2394cf670b5ac0d7f414a32cf48ed86b26d +Subproject commit a2ab2e52aa1c96ba9d9f13e72c5749d451ae81f9 From 7d05a68c601ed3a2ff3549af39b41acdb4c18d1e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 12:53:20 -0800 Subject: [PATCH 0387/2222] clean up changelog, update refs --- docs/changelog.txt | 38 ++++++++++++++++++++++---------------- library/xml | 2 +- scripts | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d1155bea2..93a6586f9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,46 +34,52 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins -- `autoslab`: Automatically create work orders to engrave slabs for ghostly dwarves. +- `autoslab`: automatically create work orders to engrave slabs for ghostly dwarves ## Fixes --@ DF screens can no longer get "stuck" on transitions when DFHack tool windows are visible. Instead, those DF screens are force-paused while DFHack windows are visible so the player can close them first and not corrupt the screen sequence. The "force pause" indicator will appear on these DFHack windows to indicate what is happening. --@ ``Screen``: allow `gui/launcher` and `gui/quickcmd` to launch themselves without hanging the game --@ Fix issues with clicks "passing through" some DFHack window elements, like scrollbars +-@ DF screens can no longer get "stuck" on transitions when DFHack tool windows are visible. Instead, those DF screens are force-paused while DFHack windows are visible so the player can close them first and not corrupt the screen sequence. The "PAUSE FORCED" indicator will appear on these DFHack windows to indicate what is happening. +-@ allow launcher tools to launch themselves without hanging the game +-@ fix issues with clicks "passing through" some DFHack window elements to the screen below - `getplants`: trees are now designated correctly --@ Sample orders: fix orders that create bags +-@ `orders`: fix orders in library/basic that create bags +- `orders`: library/military now sticks to vanilla rules and does not add orders for normally-mood-only platinum weapons. A new library orders file ``library/military_include_artifact_materials`` is now offered as an alternate ``library/military`` set of orders that still includes the platinum weapons. ## Misc Improvements -- A new cross-compile build script was added for building DFHack for Windows from a Linux Docker builder (see the `compile` instructions in the docs) -- You can now configure whether DFHack tool windows should pause the game by default -- `hotkeys`: clicking on the DFHack logo no longer closes the popup menu -- `nestboxes`: now saves enabled state in your savegame -- `gui/launcher`: sped up initialization time for faster load of the UI -- `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open -- `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A -- Many DFHack windows can now be unfocused by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. -- `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file +- DFHack windows can now be "defocused" by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. - `getplants`: ID values will now be accepted regardless of case +- Windows now display "PAUSE FORCED" on the lower border if the tool is forcing the game to pause -@ New borders for DFHack tool windows -- tell us what you think! +- `automelt`: stockpile configuration can now be set from the commandline +- `channel-safely`: new monitoring for cave-in prevention +- `gui/control-panel`: you can now configure whether DFHack tool windows should pause the game by default - `gui/control-panel`: new global hotkey: tilde (Shift-backtick on most keyboards) -- Windows now display "PAUSE FORCED" on the lower border if the tool is forcing the game to pause +- `hotkeys`: clicking on the DFHack logo no longer closes the popup menu +- `nestboxes`: now saves enabled state in your savegame +- `gui/launcher`: sped up initialization time for faster window appearance +- `orders`: orders plugin functionality is now accessible via an `overlay` widget when the manager orders screen is open +- `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A - `seedwatch`: now persists enabled state in the savegame, automatically loads useful defaults, and respects reachability when counting available seeds +- `quickfort`: planned buildings are now properly attached to any pertinent overlapping zones ## Documentation +- `compile`: instructions added for cross-compiling DFHack for Windows from a Linux Docker builder -@ Quickstart guide has been updated with info on new window behavior and how to use the control panel ## API - ``Buildings::containsTile()``: no longer takes a ``room`` parameter since that's not how rooms work anymore. If the building has extents, the extents will be checked. otherwise, the result just depends on whether the tile is within the building's bounding box. - ``Units::getCitizens()``: gets a list of citizens, which otherwise you'd have to iterate over all units the world to discover - ``Screen::Pen``: now accepts ``top_of_text`` and ``bottom_of_text`` properties to support offset text in graphics mode +- `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file +- ``Lua::Push``: now supports ``std::unordered_map`` ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play -- `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` +- `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` and anything else that displays help text. - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom - ``dfhack.units.getCitizens()``: gets a list of citizens - ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked - ``Label``: token ``tile`` properties can now be either pens or numeric texture ids +- `tiletypes`: now has a Lua API! ``tiletypes_setTile`` ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system diff --git a/library/xml b/library/xml index 518c1a431..e53321478 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 518c1a431dcfa0ca23110d94223973853d640a11 +Subproject commit e5332147871bbf3293931d3ea268ec30b1023297 diff --git a/scripts b/scripts index a2ab2e52a..8c7b3ed7a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a2ab2e52aa1c96ba9d9f13e72c5749d451ae81f9 +Subproject commit 8c7b3ed7a9ebcd1988fd6381bc943cd86bd2a3f8 From ea22a23ab7b8763542750194cb8934675cea3336 Mon Sep 17 00:00:00 2001 From: Rose Date: Thu, 2 Feb 2023 13:06:14 -0800 Subject: [PATCH 0388/2222] Clean up autoclothing for release. --- docs/changelog.txt | 2 ++ plugins/autoclothing.cpp | 35 ++++++++++++++++------------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1174508e2..4e8af42ed 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ ``Screen``: allow `gui/launcher` and `gui/quickcmd` to launch themselves without hanging the game -@ Fix issues with clicks "passing through" some DFHack window elements, like scrollbars - `getplants`: tree are now designated correctly +- `autoclothing`: fixed a crash that can happen when units are holding invalid items. ## Misc Improvements - A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) @@ -52,6 +53,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - `getplants`: ID values will now be accepted regardless of case -@ New borders for DFHack tool windows -- tell us what you think! +- `autoclothing`: merged the two separate reports into the same command. ## Documentation diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index d7dee23ad..4aa835b8b 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -388,33 +388,30 @@ command_result autoclothing(color_ostream &out, vector & parameters) // be used by 'help your-command'. if (parameters.size() == 0) { + CoreSuspender suspend; out << "Currently set " << clothingOrders.size() << " automatic clothing orders" << endl; for (size_t i = 0; i < clothingOrders.size(); i++) { out << clothingOrders[i].ToReadableLabel() << endl; } - return CR_OK; - } - else if (parameters[0] == "strictness") - { - if (parameters.size() != 2) - { - out << "Wrong number of arguments." << endl; - return CR_WRONG_USAGE; - } - if (parameters[1] == "permissive") - strictnessSetting = STRICT_PERMISSIVE; - else if (parameters[1] == "type") - strictnessSetting = STRICT_TYPE; - else if (parameters[1] == "material") - strictnessSetting = STRICT_MATERIAL; - } - else if (parameters.size() == 1 && parameters[0] == "report") - { - CoreSuspender suspend; generate_report(out); return CR_OK; } + ////Disabled until I have time to fully implement it. + //else if (parameters[0] == "strictness") + //{ + // if (parameters.size() != 2) + // { + // out << "Wrong number of arguments." << endl; + // return CR_WRONG_USAGE; + // } + // if (parameters[1] == "permissive") + // strictnessSetting = STRICT_PERMISSIVE; + // else if (parameters[1] == "type") + // strictnessSetting = STRICT_TYPE; + // else if (parameters[1] == "material") + // strictnessSetting = STRICT_MATERIAL; + //} else if (parameters.size() < 2 || parameters.size() > 3) { out << "Wrong number of arguments." << endl; From 7d304dbaf87b761a9c9fcac1909d81d3dfe2fe61 Mon Sep 17 00:00:00 2001 From: Rose Date: Thu, 2 Feb 2023 13:08:23 -0800 Subject: [PATCH 0389/2222] Update autoclothing docs. --- docs/plugins/autoclothing.rst | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 9df013d19..6e8ae7df2 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -15,18 +15,15 @@ Usage autoclothing autoclothing [quantity] - autoclothing report ``material`` can be "cloth", "silk", "yarn", or "leather". The ``item`` can be anything your civilization can produce, such as "dress" or "mitten". When invoked without parameters, it shows a summary of all managed clothing -orders. When invoked with a material and item, but without a quantity, it shows +orders, and the overall clothing situation in your fort. +When invoked with a material and item, but without a quantity, it shows the current configuration for that material and item. -``report`` gives a list of how many units of each race in your fortress that are -missing clothing in each available slot, as well as how much spare clothing you -have per slot, per race. Examples -------- @@ -35,5 +32,3 @@ Examples Sets the desired number of cloth short skirts available per citizen to 10. ``autoclothing cloth dress`` Displays the currently set number of cloth dresses chosen per citizen. -``autoclothing report`` - Displays a report of your clothing situation. From 9c8903dfbf14a003e551969ebb74bf83baf2df03 Mon Sep 17 00:00:00 2001 From: Rose Date: Thu, 2 Feb 2023 13:32:44 -0800 Subject: [PATCH 0390/2222] Update docs/plugins/autoclothing.rst Co-authored-by: Myk --- docs/plugins/autoclothing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/autoclothing.rst b/docs/plugins/autoclothing.rst index 6e8ae7df2..4584791bd 100644 --- a/docs/plugins/autoclothing.rst +++ b/docs/plugins/autoclothing.rst @@ -20,7 +20,7 @@ Usage anything your civilization can produce, such as "dress" or "mitten". When invoked without parameters, it shows a summary of all managed clothing -orders, and the overall clothing situation in your fort. +orders, and the overall clothing situation in your fort. When invoked with a material and item, but without a quantity, it shows the current configuration for that material and item. From b36e5e1dffbc5cb7546482258ebce030c4d9412f Mon Sep 17 00:00:00 2001 From: Rose Date: Thu, 2 Feb 2023 13:33:20 -0800 Subject: [PATCH 0391/2222] Update plugins/autoclothing.cpp Co-authored-by: Myk --- plugins/autoclothing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index 4aa835b8b..258668499 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -782,7 +782,7 @@ static void generate_report(color_ostream& out) auto item = Items::findItemByID(itemId); if (!item) { - WARN(cycle).print("Invalid inventory item ID: %d\n", itemId); + WARN(cycle,out).print("Invalid inventory item ID: %d\n", itemId); continue; } if (item->getWear() >= 1) From 6e200b831eb55149359690fb6ffb66e2379335c6 Mon Sep 17 00:00:00 2001 From: Rose Date: Thu, 2 Feb 2023 13:33:30 -0800 Subject: [PATCH 0392/2222] Update plugins/autoclothing.cpp Co-authored-by: Myk --- plugins/autoclothing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index 258668499..fc8b1e43b 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -818,7 +818,7 @@ static void generate_report(color_ostream& out) missingGloves[unit->race]++; if (numPants == 0) missingPants[unit->race]++; - DEBUG(report) << Translation::TranslateName(Units::getVisibleName(unit)) << " has " << numArmor << " armor, " << numShoes << " shoes, " << numHelms << " helms, " << numGloves << " gloves, " << numPants << " pants" << endl; + DEBUG(report,out) << Translation::TranslateName(Units::getVisibleName(unit)) << " has " << numArmor << " armor, " << numShoes << " shoes, " << numHelms << " helms, " << numGloves << " gloves, " << numPants << " pants" << endl; } if (missingArmor.size() + missingShoes.size() + missingHelms.size() + missingGloves.size() + missingPants.size() == 0) { From d780dc684747f5cfb16310ed16e23c3f794bfdc0 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sun, 29 Jan 2023 22:55:11 -0600 Subject: [PATCH 0393/2222] reenable autolabor for testing minimum required to make work: * realign the job table * add code to flip/flop the work detail enable flag --- plugins/CMakeLists.txt | 2 +- plugins/autolabor/CMakeLists.txt | 2 +- plugins/autolabor/autolabor.cpp | 7 +++++++ plugins/autolabor/laborstatemap.h | 16 +++++++--------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index d5f35fb21..76c80b156 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -81,7 +81,7 @@ dfhack_plugin(autoclothing autoclothing.cpp) dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) -#add_subdirectory(autolabor) +add_subdirectory(autolabor) #dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) dfhack_plugin(automelt automelt.cpp LINK_LIBRARIES lua) #dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) diff --git a/plugins/autolabor/CMakeLists.txt b/plugins/autolabor/CMakeLists.txt index 554a02ecf..b443a156f 100644 --- a/plugins/autolabor/CMakeLists.txt +++ b/plugins/autolabor/CMakeLists.txt @@ -10,6 +10,6 @@ set_source_files_properties(${COMMON_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) # mash them together (headers are marked as headers and nothing will try to compile them) list(APPEND COMMON_SRCS ${COMMON_HDRS}) -dfhack_plugin(labormanager labormanager.cpp joblabormapper.cpp ${COMMON_SRCS}) +#dfhack_plugin(labormanager labormanager.cpp joblabormapper.cpp ${COMMON_SRCS}) dfhack_plugin(autolabor autolabor.cpp ${COMMON_SRCS}) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 0af4be5e2..66aaa2f75 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include @@ -49,6 +51,7 @@ using namespace df::enums; DFHACK_PLUGIN("autolabor"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); +REQUIRE_GLOBAL(game); #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) @@ -399,6 +402,8 @@ static void enable_plugin(color_ostream &out) cleanup_state(); init_state(); + + df::global::game->external_flag |= 1; // shut down DF's work detail system } DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) @@ -1065,6 +1070,8 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) enable_autolabor = false; setOptionEnabled(CF_ENABLED, false); + df::global::game->external_flag &= ~1; // reenable DF's work detail system + out << "Autolabor is disabled." << std::endl; } diff --git a/plugins/autolabor/laborstatemap.h b/plugins/autolabor/laborstatemap.h index 789685930..61cebdd9c 100644 --- a/plugins/autolabor/laborstatemap.h +++ b/plugins/autolabor/laborstatemap.h @@ -42,6 +42,8 @@ char const* state_names[] { const dwarf_state dwarf_states[] = { dwarf_state::BUSY /* CarveFortification */, + dwarf_state::BUSY /* SmoothWall */, + dwarf_state::BUSY /* SmoothFloor */, dwarf_state::BUSY /* DetailWall */, dwarf_state::BUSY /* DetailFloor */, dwarf_state::EXCLUSIVE /* Dig */, @@ -75,21 +77,17 @@ const dwarf_state dwarf_states[] = { dwarf_state::BUSY /* CatchLiveLandAnimal */, dwarf_state::BUSY /* CatchLiveFish */, dwarf_state::BUSY /* ReturnKill */, - dwarf_state::BUSY /* CheckChest */, dwarf_state::BUSY /* StoreOwnedItem */, dwarf_state::BUSY /* PlaceItemInTomb */, dwarf_state::BUSY /* StoreItemInStockpile */, dwarf_state::BUSY /* StoreItemInBag */, - dwarf_state::BUSY /* StoreItemInHospital */, - dwarf_state::BUSY /* StoreItemInChest */, - dwarf_state::BUSY /* StoreItemInCabinet */, + dwarf_state::BUSY /* StoreItemInLocation */, dwarf_state::BUSY /* StoreWeapon */, dwarf_state::BUSY /* StoreArmor */, dwarf_state::BUSY /* StoreItemInBarrel */, dwarf_state::BUSY /* StoreItemInBin */, dwarf_state::BUSY /* SeekArtifact */, dwarf_state::BUSY /* SeekInfant */, - dwarf_state::OTHER /* AttendParty */, dwarf_state::OTHER /* GoShopping */, dwarf_state::OTHER /* GoShopping2 */, dwarf_state::BUSY /* Clean */, @@ -117,6 +115,7 @@ const dwarf_state dwarf_states[] = { dwarf_state::BUSY /* ConstructCoffin */, dwarf_state::BUSY /* ConstructTable */, dwarf_state::BUSY /* ConstructChest */, + dwarf_state::BUSY /* ConstructBag */, dwarf_state::BUSY /* ConstructBin */, dwarf_state::BUSY /* ConstructArmorStand */, dwarf_state::BUSY /* ConstructWeaponRack */, @@ -153,7 +152,7 @@ const dwarf_state dwarf_states[] = { dwarf_state::BUSY /* MilkCreature */, dwarf_state::BUSY /* MakeCheese */, dwarf_state::BUSY /* ProcessPlants */, - dwarf_state::BUSY /* ProcessPlantsBag */, + dwarf_state::BUSY /* PolishStones */, dwarf_state::BUSY /* ProcessPlantsVial */, dwarf_state::BUSY /* ProcessPlantsBarrel */, dwarf_state::BUSY /* PrepareMeal */, @@ -165,7 +164,6 @@ const dwarf_state dwarf_states[] = { dwarf_state::BUSY /* MakeChain */, dwarf_state::BUSY /* MakeFlask */, dwarf_state::BUSY /* MakeGoblet */, - dwarf_state::BUSY /* MakeInstrument */, dwarf_state::BUSY /* MakeToy */, dwarf_state::BUSY /* MakeAnimalTrap */, dwarf_state::BUSY /* MakeBarrel */, @@ -188,10 +186,10 @@ const dwarf_state dwarf_states[] = { dwarf_state::BUSY /* LoadStoneTrap */, dwarf_state::BUSY /* LoadWeaponTrap */, dwarf_state::BUSY /* CleanTrap */, - dwarf_state::BUSY /* CastSpell */, + dwarf_state::BUSY /* EncrustWithStones */, dwarf_state::BUSY /* LinkBuildingToTrigger */, dwarf_state::BUSY /* PullLever */, - dwarf_state::BUSY /* BrewDrink */, + dwarf_state::OTHER /* _unk_0x94*/, dwarf_state::BUSY /* ExtractFromPlants */, dwarf_state::BUSY /* ExtractFromRawFish */, dwarf_state::BUSY /* ExtractFromLandAnimal */, From 16e6114a868176f81bad493432fcc92fd4fb1030 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 30 Jan 2023 11:30:51 -0600 Subject: [PATCH 0394/2222] autolabor: realign labors, improve tick handling realigned the labor table to v50 changed onChange handler to run every 60 in-game ticks instead of every 60 render frames (no reason to run while paused lol) --- plugins/autolabor/autolabor.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 66aaa2f75..4eac48ab9 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -175,9 +175,9 @@ static const struct labor_default default_labor_infos[] = { /* CLEAN */ {HAULERS, false, 1, 200, 0}, /* CUTWOOD */ {AUTOMATIC, true, 1, 200, 0}, /* CARPENTER */ {AUTOMATIC, false, 1, 200, 0}, - /* DETAIL */ {AUTOMATIC, false, 1, 200, 0}, + /* STONECUTTER */ {AUTOMATIC, false, 1, 200, 0}, + /* STONE_CARVER */ {AUTOMATIC, false, 1, 200, 0}, /* MASON */ {AUTOMATIC, false, 1, 200, 0}, - /* ARCHITECT */ {AUTOMATIC, false, 1, 200, 0}, /* ANIMALTRAIN */ {AUTOMATIC, false, 1, 200, 0}, /* ANIMALCARE */ {AUTOMATIC, false, 1, 200, 0}, /* DIAGNOSE */ {AUTOMATIC, false, 1, 200, 0}, @@ -245,7 +245,18 @@ static const struct labor_default default_labor_infos[] = { /* BUILD_ROAD */ {AUTOMATIC, false, 1, 200, 0}, /* BUILD_CONSTRUCTION */ {AUTOMATIC, false, 1, 200, 0}, /* PAPERMAKING */ {AUTOMATIC, false, 1, 200, 0}, - /* BOOKBINDING */ {AUTOMATIC, false, 1, 200, 0} + /* BOOKBINDING */ {AUTOMATIC, false, 1, 200, 0}, + /* ANON_LABOR_83 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_84 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_85 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_86 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_87 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_88 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_89 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_90 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_91 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_92 */ {DISABLE, false, 0, 0, 0}, + /* ANON_LABOR_93 */ {DISABLE, false, 0, 0, 0}, }; static const int responsibility_penalties[] = { @@ -716,15 +727,18 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { - static int step_count = 0; + static int last_run = 0; + static const int run_frequency = 60; + if(!world || !world->map.block_index || !enable_autolabor) { return CR_OK; } - if (++step_count < 60) + if (world->frame_counter - last_run <= run_frequency) return CR_OK; - step_count = 0; + + last_run = world->frame_counter; std::vector dwarfs; From 4df7898d18cbb2e86e6173a95438e6f700411ee4 Mon Sep 17 00:00:00 2001 From: Myk Date: Wed, 1 Feb 2023 14:25:32 -0800 Subject: [PATCH 0395/2222] link autolabor to lua for the overlay --- plugins/autolabor/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor/CMakeLists.txt b/plugins/autolabor/CMakeLists.txt index b443a156f..721654d20 100644 --- a/plugins/autolabor/CMakeLists.txt +++ b/plugins/autolabor/CMakeLists.txt @@ -12,4 +12,4 @@ list(APPEND COMMON_SRCS ${COMMON_HDRS}) #dfhack_plugin(labormanager labormanager.cpp joblabormapper.cpp ${COMMON_SRCS}) -dfhack_plugin(autolabor autolabor.cpp ${COMMON_SRCS}) +dfhack_plugin(autolabor autolabor.cpp ${COMMON_SRCS} LINK_LIBRARIES lua) From 987597e0af82f23e7627d2b7517547c2b630d885 Mon Sep 17 00:00:00 2001 From: Myk Date: Wed, 1 Feb 2023 14:26:47 -0800 Subject: [PATCH 0396/2222] add autolabor warning overlay widget --- plugins/lua/autolabor.lua | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 plugins/lua/autolabor.lua diff --git a/plugins/lua/autolabor.lua b/plugins/lua/autolabor.lua new file mode 100644 index 000000000..b91227650 --- /dev/null +++ b/plugins/lua/autolabor.lua @@ -0,0 +1,47 @@ +local _ENV = mkmodule('plugins.autolabor') + +local gui = require('gui') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +local function is_labor_panel_visible() + local info = df.global.game.main_interface.info + return info.open and info.current_mode == df.info_interface_mode_type.LABOR +end + +AutolaborOverlay = defclass(AutolaborOverlay, overlay.OverlayWidget) +AutolaborOverlay.ATTRS{ + default_pos={x=7,y=-13}, + default_enabled=true, + viewscreens='dwarfmode', + frame={w=29, h=5}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function AutolaborOverlay:init() + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text_pen=COLOR_RED, + text={ + 'DFHack autolabor is active!', NEWLINE, + 'Any changes made on this', NEWLINE, + 'screen will have no effect.' + }, + }, + } +end + +function AutolaborOverlay:render(dc) + if not is_labor_panel_visible() or not isEnabled() then + return false + end + AutolaborOverlay.super.render(self, dc) +end + +OVERLAY_WIDGETS = { + overlay=AutolaborOverlay, +} + +return _ENV From c6d63a54c535f8af0f4adf0d7c09364165aa6521 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 2 Feb 2023 16:18:28 -0600 Subject: [PATCH 0397/2222] update autolabor documentation --- docs/plugins/autolabor.rst | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 1fa859987..58e728484 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -18,8 +18,31 @@ untouched by autolabor. .. warning:: - autolabor will override any manual changes you make to labors while it is - enabled, including through other tools such as Dwarf Therapist. + ** This plugin is still being tested. Use at your own risk. ** + + The algorithms that autolabor uses to choose labor assignments have *not* been updated for version 50 of + Dwarf Fortress. There is no particular guarantee that the labor assignments autolabor is making are optimal, + and it is entirely possible that the assignments it makes will lead to unforeseen consequences. You should + monitor what your dwarves are doing, and more importantly not doing, when using autolabor. + + At this time there is no way to easily see what labors are being assigned to whom, as there is no longer + any vanilla means for seeing the labor assignment table. Until `manipulator` is once again available, + probably the best way to see what autolabor is doing is to use + `Dwarf Therapist `_. You can also increase autolabor's + logging level using the `debugfilter` command (setting either ``debug`` or ``trace`` level for the + ``cycle`` mode) but be warned that this may generate a large amount of console spam, especially in a large fort. + + When it is enabled, autolabor automatically disables the work detail system. You cannot + use autolabor and work details at the same time. If you attempt to open the work detail screen while + autohauler is active, a warning box should appear advising you that autolabor is managing labors and preventing + you from making any changes on that screen. + + Finally, should you disable autolabor, autolabor will automatically reenable the vanilla work detail system. + However, the work detail system only updates labors when the work detail screen is open and some change is + made on that screen. Therefore, if you choose to disable autolabor, you should probably immediately + thereafter open the work details screen and make some change to force the game to recompute all labor + assignments based on the vanilla algorithm. At this time, it is not possible for autolabor to do this + automatically. Usage ----- From 9579f11f351fae837a47d1402f3fe0307f127ca0 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 2 Feb 2023 17:09:57 -0600 Subject: [PATCH 0398/2222] autolabor: improve docs --- docs/plugins/autolabor.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 58e728484..13d545792 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -18,7 +18,7 @@ untouched by autolabor. .. warning:: - ** This plugin is still being tested. Use at your own risk. ** + **This plugin is still being tested. Use at your own risk.** The algorithms that autolabor uses to choose labor assignments have *not* been updated for version 50 of Dwarf Fortress. There is no particular guarantee that the labor assignments autolabor is making are optimal, @@ -29,7 +29,7 @@ untouched by autolabor. any vanilla means for seeing the labor assignment table. Until `manipulator` is once again available, probably the best way to see what autolabor is doing is to use `Dwarf Therapist `_. You can also increase autolabor's - logging level using the `debugfilter` command (setting either ``debug`` or ``trace`` level for the + logging level using the `debugfilter` command (setting either ``debug`` or ``trace`` level for the ``cycle`` mode) but be warned that this may generate a large amount of console spam, especially in a large fort. When it is enabled, autolabor automatically disables the work detail system. You cannot From 2a8578c4a7572642c0b92d91d017a7cdeaab25ed Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 2 Feb 2023 17:15:07 -0800 Subject: [PATCH 0399/2222] Update docs/plugins/autolabor.rst --- docs/plugins/autolabor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 13d545792..3ab5c4e09 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -34,7 +34,7 @@ untouched by autolabor. When it is enabled, autolabor automatically disables the work detail system. You cannot use autolabor and work details at the same time. If you attempt to open the work detail screen while - autohauler is active, a warning box should appear advising you that autolabor is managing labors and preventing + autolabor is active, a warning box should appear advising you that autolabor is managing labors and preventing you from making any changes on that screen. Finally, should you disable autolabor, autolabor will automatically reenable the vanilla work detail system. From aa2339d2aafe962d046d32cc2756d0e95a84d068 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 19:42:19 -0800 Subject: [PATCH 0400/2222] add cxxrandom back to the build and mark channel-safely as tested --- docs/plugins/channel-safely.rst | 2 +- docs/plugins/cxxrandom.rst | 2 +- plugins/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index 3acbe66cd..c5dbc37f6 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -3,7 +3,7 @@ channel-safely .. dfhack-tool:: :summary: Auto-manage channel designations to keep dwarves safe. - :tags: untested fort auto + :tags: fort auto Multi-level channel projects can be dangerous, and managing the safety of your dwarves throughout the completion of such projects can be difficult and time diff --git a/docs/plugins/cxxrandom.rst b/docs/plugins/cxxrandom.rst index d2775977d..19788e4a4 100644 --- a/docs/plugins/cxxrandom.rst +++ b/docs/plugins/cxxrandom.rst @@ -3,7 +3,7 @@ cxxrandom .. dfhack-tool:: :summary: Provides a Lua API for random distributions. - :tags: untested dev + :tags: dev :no-command: See `cxxrandom-api` for details. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 76c80b156..1c11cd797 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -100,7 +100,7 @@ dfhack_plugin(cleanowned cleanowned.cpp) #dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) #dfhack_plugin(createitem createitem.cpp) dfhack_plugin(cursecheck cursecheck.cpp) -#dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) +dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) #dfhack_plugin(deramp deramp.cpp) dfhack_plugin(debug debug.cpp LINK_LIBRARIES jsoncpp_static) #dfhack_plugin(dig dig.cpp) From 2cf985be733cc9df04f66984f2e854f07d05e8c4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 19:42:41 -0800 Subject: [PATCH 0401/2222] don't lose List scroll position when dragging --- library/lua/gui/widgets.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index b6e9b8da1..83b586421 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1609,15 +1609,15 @@ function List:getContentHeight() return #self.choices * self.row_height end -function List:postComputeFrame(body) - self.page_size = math.max(1, math.floor(body.height / self.row_height)) - self:moveCursor(0) -end - local function update_list_scrollbar(list) list.scrollbar:update(list.page_top, list.page_size, #list.choices) end +function List:postComputeFrame(body) + self.page_size = math.max(1, math.floor(body.height / self.row_height)) + update_list_scrollbar(self) +end + function List:postUpdateLayout() update_list_scrollbar(self) end From bfc27605cc9e2712ec2c860623f41cd24d4e2ce9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 21:03:23 -0800 Subject: [PATCH 0402/2222] use Ctrl-Shift-S instead of tilde for gui/control-panel since ~ conflicts with the :lua shortcut --- data/init/dfhack.keybindings.init | 2 +- docs/Quickstart.rst | 4 ++-- docs/changelog.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index a793e3248..e623a2327 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -17,7 +17,7 @@ keybinding add Ctrl-Shift-P "gui/launcher --minimal" keybinding add Ctrl-Shift-C hotkeys # control panel -keybinding add Shift-` gui/control-panel +keybinding add Ctrl-Shift-S gui/control-panel # on-screen keyboard keybinding add Ctrl-Shift-K gui/cp437-table diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 95b1fb0a1..f77a2c4c9 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -80,7 +80,7 @@ The second place to check is the DFHack control panel: `gui/control-panel`. It will give you an overview of which tools are currently enabled, and will allow you to toggle them on or off, see help text for them, or launch their dedicated configuration UIs. You can launch the control panel from anywhere with the -tilde key (Shift-\`) or from the logo hover list. +Ctrl-Shift-S hotkey or by selecting it from the logo hover list. In the control panel, you can also select which tools you'd like to be automatically enabled when you start a new fort. There are also system settings @@ -150,7 +150,7 @@ You can get to popular, relevant tools for the current context by hovering the mouse over the DFHack logo or by hitting Ctrl-Shift-C. You can enable DFHack tools and configure settings with `gui/control-panel`, -which you can access directly with the tilde key (Shift-\`). +which you can access directly with the Ctrl-Shift-S hotkey. You can get to the launcher and its integrated autocomplete, history search, and help text by hitting backtick (\`) or Ctrl-Shift-D, or, of course, by diff --git a/docs/changelog.txt b/docs/changelog.txt index 8ae58c74e..fb2cd57c3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,7 +54,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `automelt`: stockpile configuration can now be set from the commandline - `channel-safely`: new monitoring for cave-in prevention - `gui/control-panel`: you can now configure whether DFHack tool windows should pause the game by default -- `gui/control-panel`: new global hotkey: tilde (Shift-backtick on most keyboards) +- `gui/control-panel`: new global hotkey for quick access: Ctrl-Shift-S (S for Settings) - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `nestboxes`: now saves enabled state in your savegame - `gui/launcher`: sped up initialization time for faster window appearance From bb6824ec11cfb02357f493cf0bd2985e660c3afa Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 3 Feb 2023 05:07:52 +0000 Subject: [PATCH 0403/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index e53321478..632a5930e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e5332147871bbf3293931d3ea268ec30b1023297 +Subproject commit 632a5930ed40616d62043de1234bf007dbbbc634 diff --git a/scripts b/scripts index 8c7b3ed7a..3664fcc3c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 8c7b3ed7a9ebcd1988fd6381bc943cd86bd2a3f8 +Subproject commit 3664fcc3c6d062c150fb0ea346d4f6f29dace8ae From f991593a8042f3a7ac90511c114757b84011ffb6 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 3 Feb 2023 07:14:43 +0000 Subject: [PATCH 0404/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 3664fcc3c..a3cd4b1c0 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3664fcc3c6d062c150fb0ea346d4f6f29dace8ae +Subproject commit a3cd4b1c0202be68157cacb93f580f52dc9800a3 From 8c7be9a8c2c5c047eef2bf096536ea648c0e8025 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 23:33:24 -0800 Subject: [PATCH 0405/2222] don't use negative plant indices and clean up the Kitchen module a bit more --- library/include/modules/Kitchen.h | 3 +++ library/modules/Kitchen.cpp | 32 +++++++++++++++++++++++++------ plugins/seedwatch.cpp | 17 +++++++++++++--- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/library/include/modules/Kitchen.h b/library/include/modules/Kitchen.h index ed8eb95b1..ab064ac39 100644 --- a/library/include/modules/Kitchen.h +++ b/library/include/modules/Kitchen.h @@ -53,6 +53,9 @@ DFHACK_EXPORT void allowPlantSeedCookery(int32_t plant_id); // add this plant to the exclusion list, if it is not already in it DFHACK_EXPORT void denyPlantSeedCookery(int32_t plant_id); +DFHACK_EXPORT bool isPlantCookeryAllowed(int32_t plant_id); +DFHACK_EXPORT bool isSeedCookeryAllowed(int32_t plant_id); + DFHACK_EXPORT std::size_t size(); // Finds the index of a kitchen exclusion in plotinfo.kitchen.exc_types. Returns -1 if not found. diff --git a/library/modules/Kitchen.cpp b/library/modules/Kitchen.cpp index 5bbc2ee7c..4fedf0bab 100644 --- a/library/modules/Kitchen.cpp +++ b/library/modules/Kitchen.cpp @@ -28,12 +28,6 @@ using namespace df::enums; using df::global::world; using df::global::plotinfo; -// Special values used by "seedwatch" plugin to store seed limits -const df::enums::item_type::item_type SEEDLIMIT_ITEMTYPE = df::enums::item_type::BAR; -const int16_t SEEDLIMIT_ITEMSUBTYPE = 0; -const int16_t SEEDLIMIT_MAX = 400; // Maximum permitted seed limit -const df::kitchen_exc_type SEEDLIMIT_EXCTYPE = df::kitchen_exc_type(4); - void Kitchen::debug_print(color_ostream &out) { out.print("Kitchen Exclusions\n"); @@ -54,6 +48,9 @@ void Kitchen::debug_print(color_ostream &out) void Kitchen::allowPlantSeedCookery(int32_t plant_id) { + if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + return; + df::plant_raw *type = world->raws.plants.all[plant_id]; removeExclusion(df::kitchen_exc_type::Cook, item_type::SEEDS, -1, @@ -67,6 +64,9 @@ void Kitchen::allowPlantSeedCookery(int32_t plant_id) void Kitchen::denyPlantSeedCookery(int32_t plant_id) { + if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + return; + df::plant_raw *type = world->raws.plants.all[plant_id]; addExclusion(df::kitchen_exc_type::Cook, item_type::SEEDS, -1, @@ -78,6 +78,26 @@ void Kitchen::denyPlantSeedCookery(int32_t plant_id) type->material_defs.idx[plant_material_def::basic_mat]); } +bool Kitchen::isPlantCookeryAllowed(int32_t plant_id) { + if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + return false; + + df::plant_raw *type = world->raws.plants.all[plant_id]; + return findExclusion(df::kitchen_exc_type::Cook, item_type::PLANT, -1, + type->material_defs.type[plant_material_def::basic_mat], + type->material_defs.idx[plant_material_def::basic_mat]) < 0; +} + +bool Kitchen::isSeedCookeryAllowed(int32_t plant_id) { + if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + return false; + + df::plant_raw *type = world->raws.plants.all[plant_id]; + return findExclusion(df::kitchen_exc_type::Cook, item_type::SEEDS, -1, + type->material_defs.type[plant_material_def::seed], + type->material_defs.idx[plant_material_def::seed]) < 0; +} + size_t Kitchen::size() { return plotinfo->kitchen.item_types.size(); diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index 03ea4f268..fed563a84 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -301,7 +301,7 @@ static void scan_seeds(color_ostream &out, unordered_map *acce for (auto &item : world->items.other[items_other_id::SEEDS]) { MaterialInfo mat(item); - if (!mat.isPlant()) + if (mat.plant->index < 0 || !mat.isPlant()) continue; if ((bad_flags.whole & item->flags.whole) || !is_accessible_item(item, citizens)) { if (inaccessible_counts) @@ -329,13 +329,19 @@ static void do_cycle(color_ostream &out, int32_t *num_enabled_seed_types, int32_ for (auto &entry : watched_seeds) { int32_t id = entry.first; + if (id < 0 || id >= world->raws.plants.all.size()) + continue; int32_t target = get_config_val(entry.second, SEED_CONFIG_TARGET); - if (accessible_counts[id] <= target) { + if (accessible_counts[id] <= target && + (Kitchen::isPlantCookeryAllowed(id) || + Kitchen::isSeedCookeryAllowed(id))) { DEBUG(cycle,out).print("disabling seed mat: %d\n", id); if (num_disabled_seed_types) ++*num_disabled_seed_types; Kitchen::denyPlantSeedCookery(id); - } else if (target + TARGET_BUFFER < accessible_counts[id]) { + } else if (target + TARGET_BUFFER < accessible_counts[id] && + (!Kitchen::isPlantCookeryAllowed(id) || + !Kitchen::isSeedCookeryAllowed(id))) { DEBUG(cycle,out).print("enabling seed mat: %d\n", id); if (num_enabled_seed_types) ++*num_enabled_seed_types; @@ -353,6 +359,11 @@ static void set_target(color_ostream &out, int32_t id, int32_t target) { if (target == 0) remove_seed_config(out, id); else { + if (id < 0 || id >= world->raws.plants.all.size()) { + WARN(config,out).print( + "cannot set target for unknown plant id: %d\n", id); + return; + } PersistentDataItem &c = ensure_seed_config(out, id); set_config_val(c, SEED_CONFIG_TARGET, target); } From 49246a60f366e0b309c78ee66db18d5f96651176 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 23:57:52 -0800 Subject: [PATCH 0406/2222] properly cast for unsigned comparisons --- library/modules/Kitchen.cpp | 8 ++++---- plugins/seedwatch.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/modules/Kitchen.cpp b/library/modules/Kitchen.cpp index 4fedf0bab..7fed01b50 100644 --- a/library/modules/Kitchen.cpp +++ b/library/modules/Kitchen.cpp @@ -48,7 +48,7 @@ void Kitchen::debug_print(color_ostream &out) void Kitchen::allowPlantSeedCookery(int32_t plant_id) { - if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + if (plant_id < 0 || (size_t)plant_id >= world->raws.plants.all.size()) return; df::plant_raw *type = world->raws.plants.all[plant_id]; @@ -64,7 +64,7 @@ void Kitchen::allowPlantSeedCookery(int32_t plant_id) void Kitchen::denyPlantSeedCookery(int32_t plant_id) { - if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + if (plant_id < 0 || (size_t)plant_id >= world->raws.plants.all.size()) return; df::plant_raw *type = world->raws.plants.all[plant_id]; @@ -79,7 +79,7 @@ void Kitchen::denyPlantSeedCookery(int32_t plant_id) } bool Kitchen::isPlantCookeryAllowed(int32_t plant_id) { - if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + if (plant_id < 0 || (size_t)plant_id >= world->raws.plants.all.size()) return false; df::plant_raw *type = world->raws.plants.all[plant_id]; @@ -89,7 +89,7 @@ bool Kitchen::isPlantCookeryAllowed(int32_t plant_id) { } bool Kitchen::isSeedCookeryAllowed(int32_t plant_id) { - if (plant_id < 0 || plant_id >= world->raws.plants.all.size()) + if (plant_id < 0 || (size_t)plant_id >= world->raws.plants.all.size()) return false; df::plant_raw *type = world->raws.plants.all[plant_id]; diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index fed563a84..e88921653 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -329,7 +329,7 @@ static void do_cycle(color_ostream &out, int32_t *num_enabled_seed_types, int32_ for (auto &entry : watched_seeds) { int32_t id = entry.first; - if (id < 0 || id >= world->raws.plants.all.size()) + if (id < 0 || (size_t)id >= world->raws.plants.all.size()) continue; int32_t target = get_config_val(entry.second, SEED_CONFIG_TARGET); if (accessible_counts[id] <= target && @@ -359,7 +359,7 @@ static void set_target(color_ostream &out, int32_t id, int32_t target) { if (target == 0) remove_seed_config(out, id); else { - if (id < 0 || id >= world->raws.plants.all.size()) { + if (id < 0 || (size_t)id >= world->raws.plants.all.size()) { WARN(config,out).print( "cannot set target for unknown plant id: %d\n", id); return; From 76712a533c12f2e271e626b242cad0f1085051f1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 00:02:20 -0800 Subject: [PATCH 0407/2222] move control panel hotkey again to Ctrl-Shift-E Ctrl-Shift-S is too close to the macro save hotkey (Ctrl-S) and can trigger it sometimes if you're not super careful --- data/init/dfhack.keybindings.init | 2 +- docs/Quickstart.rst | 4 ++-- docs/changelog.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index e623a2327..5847a42c7 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -17,7 +17,7 @@ keybinding add Ctrl-Shift-P "gui/launcher --minimal" keybinding add Ctrl-Shift-C hotkeys # control panel -keybinding add Ctrl-Shift-S gui/control-panel +keybinding add Ctrl-Shift-E gui/control-panel # on-screen keyboard keybinding add Ctrl-Shift-K gui/cp437-table diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index f77a2c4c9..a91256a1f 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -80,7 +80,7 @@ The second place to check is the DFHack control panel: `gui/control-panel`. It will give you an overview of which tools are currently enabled, and will allow you to toggle them on or off, see help text for them, or launch their dedicated configuration UIs. You can launch the control panel from anywhere with the -Ctrl-Shift-S hotkey or by selecting it from the logo hover list. +Ctrl-Shift-E hotkey or by selecting it from the logo hover list. In the control panel, you can also select which tools you'd like to be automatically enabled when you start a new fort. There are also system settings @@ -150,7 +150,7 @@ You can get to popular, relevant tools for the current context by hovering the mouse over the DFHack logo or by hitting Ctrl-Shift-C. You can enable DFHack tools and configure settings with `gui/control-panel`, -which you can access directly with the Ctrl-Shift-S hotkey. +which you can access directly with the Ctrl-Shift-E hotkey. You can get to the launcher and its integrated autocomplete, history search, and help text by hitting backtick (\`) or Ctrl-Shift-D, or, of course, by diff --git a/docs/changelog.txt b/docs/changelog.txt index fb2cd57c3..dc8198c0d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,7 +54,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `automelt`: stockpile configuration can now be set from the commandline - `channel-safely`: new monitoring for cave-in prevention - `gui/control-panel`: you can now configure whether DFHack tool windows should pause the game by default -- `gui/control-panel`: new global hotkey for quick access: Ctrl-Shift-S (S for Settings) +- `gui/control-panel`: new global hotkey for quick access: Ctrl-Shift-E - `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `nestboxes`: now saves enabled state in your savegame - `gui/launcher`: sped up initialization time for faster window appearance From 6831b1a1ae3a9caa9de4ee14c0cf40704ffa2336 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 23:44:33 -0800 Subject: [PATCH 0408/2222] reset the saved frame counter on new world this allows the plugins to function normally even after one world is exited and a different world with a lower frame counter is loaded --- plugins/autobutcher.cpp | 1 + plugins/autochop.cpp | 1 + plugins/automelt.cpp | 1 + plugins/autonestbox.cpp | 1 + plugins/autoslab.cpp | 1 + plugins/buildingplan.cpp | 1 + plugins/examples/persistent_per_save_example.cpp | 1 + plugins/nestboxes.cpp | 1 + plugins/seedwatch.cpp | 2 ++ 9 files changed, 10 insertions(+) diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index 7fb785cc1..3e76796cb 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -122,6 +122,7 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 81c6196a1..5888574dd 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -158,6 +158,7 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 5b68bb10b..a5c288f6d 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -167,6 +167,7 @@ DFhackCExport command_result plugin_shutdown(color_ostream &out) DFhackCExport command_result plugin_load_data(color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) diff --git a/plugins/autonestbox.cpp b/plugins/autonestbox.cpp index 8b1ee5244..be7bdb2aa 100644 --- a/plugins/autonestbox.cpp +++ b/plugins/autonestbox.cpp @@ -96,6 +96,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 51c92b0f7..9b0848470 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -107,6 +107,7 @@ DFhackCExport command_result plugin_shutdown(color_ostream &out) DFhackCExport command_result plugin_load_data(color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 1407e5c46..039c83b0f 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -162,6 +162,7 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { diff --git a/plugins/examples/persistent_per_save_example.cpp b/plugins/examples/persistent_per_save_example.cpp index 603df1a3c..a8421949b 100644 --- a/plugins/examples/persistent_per_save_example.cpp +++ b/plugins/examples/persistent_per_save_example.cpp @@ -103,6 +103,7 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 98becf170..98e557075 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -85,6 +85,7 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index e88921653..aee167258 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -165,6 +165,8 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { } DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; + world_plant_ids.clear(); for (size_t i = 0; i < world->raws.plants.all.size(); ++i) { auto & plant = world->raws.plants.all[i]; From 2316615763d50d24cc304cb5623e11b2ef9d21c0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 20:24:40 -0800 Subject: [PATCH 0409/2222] react to double clicks on list items --- docs/changelog.txt | 3 ++- docs/dev/Lua API.rst | 4 ++++ library/lua/gui/widgets.lua | 26 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index dc8198c0d..369f0c620 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -78,9 +78,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play - `helpdb`: changed from auto-refreshing every 60 seconds to only refreshing on explicit call to ``helpdb.refresh()``. docs very rarely change during a play session, and the automatic database refreshes were slowing down the startup of `gui/launcher` and anything else that displays help text. - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom +- ``widgets.List``: new callbacks for double click and shift double click - ``dfhack.units.getCitizens()``: gets a list of citizens - ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked -- ``Label``: token ``tile`` properties can now be either pens or numeric texture ids +- ``widgets.Label``: token ``tile`` properties can now be either pens or numeric texture ids - `tiletypes`: now has a Lua API! ``tiletypes_setTile`` ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index deda0e31b..f0c33eb73 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4875,6 +4875,10 @@ It has the following attributes: key/click and calls the callback as ``on_submit(index,choice)``. :on_submit2: Shift-click callback; if specified, the list reacts to the click and calls the callback as ``on_submit2(index,choice)``. +:on_double_click: Mouse double click callback; if specified, the list reacts to the + click and calls the callback as ``on_double_click(index,choice)``. +:on_double_click2: Shift-double click callback; if specified, the list reacts to the click and + calls the callback as ``on_double_click2(index,choice)``. :row_height: Height of every row in text lines. :icon_width: If not *nil*, the specified number of character columns are reserved to the left of the list item for the icons. diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 83b586421..5b918a13b 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1537,6 +1537,8 @@ List.ATTRS{ on_select = DEFAULT_NIL, on_submit = DEFAULT_NIL, on_submit2 = DEFAULT_NIL, + on_double_click = DEFAULT_NIL, + on_double_click2 = DEFAULT_NIL, row_height = 1, scroll_keys = STANDARDSCROLL, icon_width = DEFAULT_NIL, @@ -1557,6 +1559,8 @@ function List:init(info) self.choices = {} self.selected = 1 end + + self.last_select_click_ms = 0 -- used to track double-clicking on an item end function List:setChoices(choices, selected) @@ -1765,6 +1769,16 @@ function List:submit2() end end +function List:double_click() + if #self.choices == 0 then return end + local cb = dfhack.internal.getModifiers().shift and + self.on_double_click2 or self.on_double_click + if cb then + cb(self:getSelected()) + return true + end +end + function List:onInput(keys) if self:inputToSubviews(keys) then return true @@ -1776,6 +1790,18 @@ function List:onInput(keys) elseif keys._MOUSE_L_DOWN then local idx = self:getIdxUnderMouse() if idx then + local now_ms = dfhack.getTickCount() + if idx ~= self:getSelected() then + self.last_select_click_ms = now_ms + else + if now_ms - self.last_select_click_ms <= DOUBLE_CLICK_MS then + self.last_select_click_ms = 0 + if self:double_click() then return true end + else + self.last_select_click_ms = now_ms + end + end + self:setSelected(idx) if dfhack.internal.getModifiers().shift then self:submit2() From 345b7d78cb6ac913dfa9946ee2c2221cb72ed074 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Feb 2023 20:42:23 -0800 Subject: [PATCH 0410/2222] pass attributes through FilteredList --- library/lua/gui/widgets.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 5b918a13b..f9c9bb27e 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1897,6 +1897,16 @@ function FilteredList:init(info) return info.on_submit2(self:getSelected()) end end + if info.on_double_click then + self.list.on_double_click = function() + return info.on_double_click(self:getSelected()) + end + end + if info.on_double_click2 then + self.list.on_double_click2 = function() + return info.on_double_click2(self:getSelected()) + end + end self.not_found = Label{ visible = true, text = info.not_found_label or 'No matches', From 86962601de4c3b3e185ef8500bfef5b9344d3710 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 3 Feb 2023 14:48:15 +0000 Subject: [PATCH 0411/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index a3cd4b1c0..dbf19eb5f 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a3cd4b1c0202be68157cacb93f580f52dc9800a3 +Subproject commit dbf19eb5ff045f7c21fee4c1bfef690461d1c315 From cddcf7850d148b9b790a58fed9275d02300dc981 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Tue, 31 Jan 2023 17:13:39 -0800 Subject: [PATCH 0412/2222] cross compile: build stonesense as well --- .github/workflows/build.yml | 2 +- build/build-win64-from-linux.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b8892882..a271082c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -145,7 +145,7 @@ jobs: - name: Cross-compile win64 artifacts run: | cd build - bash -x build-win64-from-linux.sh + env CMAKE_EXTRA_ARGS=-DBUILD_STONESENSE:BOOL=1 bash -x build-win64-from-linux.sh - name: Format artifact name id: artifactname run: | diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 2ba053434..11f4dbbc5 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -43,7 +43,7 @@ if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/b -e CCACHE_DIR=/src/build/ccache \ --name dfhack-win \ ghcr.io/dfhack/build-env:msvc \ - bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ + bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 $CMAKE_EXTRA_ARGS && dfhack-make -j$jobs install" \ ; then echo echo "Build failed" From 04f586b5c8a36a25e53740210621e4058bfa28ff Mon Sep 17 00:00:00 2001 From: Myk Date: Fri, 3 Feb 2023 10:12:25 -0800 Subject: [PATCH 0413/2222] move stonesense flag to the cross compile action --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5bd5ae9f3..04b467af5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,7 +87,7 @@ jobs: -DBUILD_DEV_PLUGINS:BOOL=${{ matrix.plugins == 'all' }} \ -DBUILD_SIZECHECK:BOOL=${{ matrix.plugins == 'all' }} \ -DBUILD_SKELETON:BOOL=${{ matrix.plugins == 'all' }} \ - -DBUILD_STONESENSE:BOOL=1 \ + -DBUILD_STONESENSE:BOOL=${{ matrix.plugins == 'all' }} \ -DBUILD_SUPPORTED:BOOL=1 \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ @@ -145,7 +145,7 @@ jobs: - name: Cross-compile win64 artifacts run: | cd build - bash -x build-win64-from-linux.sh + env EXTRA_CMAKE_ARGS="-DBUILD_STONESENSE:BOOL=1" bash -x build-win64-from-linux.sh - name: Format artifact name id: artifactname run: | From d980c7bdefaa2d8c80f077d422248ae0a33bd851 Mon Sep 17 00:00:00 2001 From: Myk Date: Fri, 3 Feb 2023 10:25:04 -0800 Subject: [PATCH 0414/2222] actually expand EXTRA_CMAKE_ARGS in the command --- build/build-win64-from-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 2ba053434..27249a15e 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -43,7 +43,7 @@ if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/b -e CCACHE_DIR=/src/build/ccache \ --name dfhack-win \ ghcr.io/dfhack/build-env:msvc \ - bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ + bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 ${EXTRA_CMAKE_ARGS} && dfhack-make -j$jobs install" \ ; then echo echo "Build failed" From e2095f9ae39ab290845c3e9b909802b4b9ced065 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 10:31:17 -0800 Subject: [PATCH 0415/2222] restore the file mode to executable --- build/build-win64-from-linux.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build/build-win64-from-linux.sh diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh old mode 100644 new mode 100755 From 81abd523134e1f02a192107b2d7730984590baec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 12:25:44 -0800 Subject: [PATCH 0416/2222] include TRACE messages in the build --- library/include/Debug.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/include/Debug.h b/library/include/Debug.h index 4cad178dc..48e661acc 100644 --- a/library/include/Debug.h +++ b/library/include/Debug.h @@ -102,10 +102,12 @@ namespace DFHack { #ifdef NDEBUG /*! - * Reduce minimum compiled in debug levels if NDEBUG is defined. This is LDEBUG - * and not LINFO so users can usefully increase logging levels for bug reports. + * This is here so we can reduce minimum compiled in debug levels if NDEBUG is + * defined if we want to. If LTRACE slows down the binary, we can change it to + * LDEBUG (but no lower than that so users can usefully increase logging levels + *for bug reports). */ -#define DBG_FILTER DFHack::DebugCategory::LDEBUG +#define DBG_FILTER DFHack::DebugCategory::LTRACE #else //! Set default compiled in debug levels to include all prints #define DBG_FILTER DFHack::DebugCategory::LTRACE From 972df67eee214bf65ec4ac86d915c7bec25cf59a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 12:26:01 -0800 Subject: [PATCH 0417/2222] add more instrumentation to autochop --- plugins/autochop.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 5888574dd..62433a1f0 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -394,6 +394,7 @@ static int32_t scan_trees(color_ostream & out, int32_t *expected_yield, int32_t *accessible_yield = NULL, map *tree_counts = NULL, map *designated_tree_counts = NULL) { + TRACE(cycle,out).print("scanning trees\n"); int32_t newly_marked = 0; if (accessible_trees) @@ -415,6 +416,9 @@ static int32_t scan_trees(color_ostream & out, int32_t *expected_yield, bucket_watched_burrows(out, clearcut_burrows, chop_burrows); for (auto plant : world->plants.all) { + TRACE(cycle,out).print(" scanning tree at %d,%d,%d\n", + plant->pos.x, plant->pos.y, plant->pos.z); + if (!is_valid_tree(plant)) continue; @@ -503,15 +507,18 @@ struct BadFlags } }; -static void scan_logs(int32_t *usable_logs, const vector &citizens, int32_t *inaccessible_logs = NULL) { +static void scan_logs(color_ostream &out, int32_t *usable_logs, + const vector &citizens, int32_t *inaccessible_logs = NULL) { static const BadFlags bad_flags; + TRACE(cycle,out).print("scanning logs\n"); if (usable_logs) *usable_logs = 0; if (inaccessible_logs) *inaccessible_logs = 0; for (auto &item : world->items.other[items_other_id::IN_PLAY]) { + TRACE(cycle,out).print(" scanning log %d\n", item->id); if (item->flags.whole & bad_flags.whole) continue; @@ -548,7 +555,7 @@ static int32_t do_cycle(color_ostream &out, bool force_designate) { // check how many logs we have already int32_t usable_logs; - scan_logs(&usable_logs, citizens); + scan_logs(out, &usable_logs, citizens); if (get_config_bool(config, CONFIG_WAITING_FOR_MIN) && usable_logs <= get_config_val(config, CONFIG_MIN_LOGS)) { @@ -631,7 +638,7 @@ static void autochop_printStatus(color_ostream &out) { map tree_counts, designated_tree_counts; vector citizens; Units::getCitizens(citizens); - scan_logs(&usable_logs, citizens, &inaccessible_logs); + scan_logs(out, &usable_logs, citizens, &inaccessible_logs); scan_trees(out, &expected_yield, NULL, false, citizens, &accessible_trees, &inaccessible_trees, &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); @@ -736,7 +743,7 @@ static int autochop_getLogCounts(lua_State *L) { int32_t usable_logs, inaccessible_logs; vector citizens; Units::getCitizens(citizens); - scan_logs(&usable_logs, citizens, &inaccessible_logs); + scan_logs(*out, &usable_logs, citizens, &inaccessible_logs); Lua::Push(L, usable_logs); Lua::Push(L, inaccessible_logs); return 2; From f2521c4a5c228deea80b1e1c000fa661754719a7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 12:52:50 -0800 Subject: [PATCH 0418/2222] protect against bad tree data --- plugins/autochop.cpp | 116 +++++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 62433a1f0..5b1cdf97e 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -327,16 +327,23 @@ static bool is_protected(const df::plant * plant, PersistentDataItem &c) { } static int32_t estimate_logs(const df::plant *plant) { + if (!plant->tree_info) + return 0; + //adapted from code by aljohnston112 @ github df::plant_tree_tile** tiles = plant->tree_info->body; - df::plant_tree_tile* tilesRow; - int trunks = 0; + if (!tiles) + return 0; + + int32_t trunks = 0; + const int32_t area = plant->tree_info->dim_y * plant->tree_info->dim_x; for (int i = 0; i < plant->tree_info->body_height; i++) { - tilesRow = tiles[i]; - for (int j = 0; j < plant->tree_info->dim_y*plant->tree_info->dim_x; j++) { + df::plant_tree_tile* tilesRow = tiles[i]; + if (!tilesRow) + return 0; // tree data is corrupt; let's not touch it + for (int j = 0; j < area; j++) trunks += tilesRow[j].bits.trunk; - } } return trunks; @@ -386,6 +393,55 @@ static void bucket_watched_burrows(color_ostream & out, typedef multimap> TreesBySize; +static int32_t scan_tree(color_ostream & out, df::plant *plant, int32_t *expected_yield, + TreesBySize *designatable_trees_by_size, bool designate_clearcut, + const vector &citizens, int32_t *accessible_trees, + int32_t *inaccessible_trees, int32_t *designated_trees, int32_t *accessible_yield, + map *tree_counts, + map *designated_tree_counts, + map &clearcut_burrows, + map &chop_burrows) { + TRACE(cycle,out).print(" scanning tree at %d,%d,%d\n", + plant->pos.x, plant->pos.y, plant->pos.z); + + if (!is_valid_tree(plant)) + return 0; + + bool accessible = is_accessible_tree(plant->pos, citizens); + int32_t yield = estimate_logs(plant); + + if (accessible) { + if (accessible_trees) + ++*accessible_trees; + if (accessible_yield) + *accessible_yield += yield; + } else { + if (inaccessible_trees) + ++*inaccessible_trees; + } + + bool can_chop = false; + bool designated = Designations::isPlantMarked(plant); + bool was_designated = designated; + bucket_tree(plant, designate_clearcut, &designated, &can_chop, tree_counts, + designated_tree_counts, clearcut_burrows, chop_burrows); + + int32_t ret = 0; + if (designated) { + if (!was_designated) + ret = 1; + if (designated_trees) + ++*designated_trees; + if (expected_yield) + *expected_yield += yield; + } else if (can_chop && accessible) { + if (designatable_trees_by_size) + designatable_trees_by_size->emplace(yield, plant); + } + + return ret; +} + // returns the number of trees that were newly marked static int32_t scan_trees(color_ostream & out, int32_t *expected_yield, TreesBySize *designatable_trees_by_size, bool designate_clearcut, @@ -415,44 +471,18 @@ static int32_t scan_trees(color_ostream & out, int32_t *expected_yield, map clearcut_burrows, chop_burrows; bucket_watched_burrows(out, clearcut_burrows, chop_burrows); - for (auto plant : world->plants.all) { - TRACE(cycle,out).print(" scanning tree at %d,%d,%d\n", - plant->pos.x, plant->pos.y, plant->pos.z); - - if (!is_valid_tree(plant)) - continue; - - bool accessible = is_accessible_tree(plant->pos, citizens); - int32_t yield = estimate_logs(plant); - - if (accessible) { - if (accessible_trees) - ++*accessible_trees; - if (accessible_yield) - *accessible_yield += yield; - } else { - if (inaccessible_trees) - ++*inaccessible_trees; - } - - bool can_chop = false; - bool designated = Designations::isPlantMarked(plant); - bool was_designated = designated; - bucket_tree(plant, designate_clearcut, &designated, &can_chop, tree_counts, - designated_tree_counts, clearcut_burrows, chop_burrows); - - if (designated) { - if (!was_designated) - ++newly_marked; - if (designated_trees) - ++*designated_trees; - if (expected_yield) - *expected_yield += yield; - } else if (can_chop && accessible) { - if (designatable_trees_by_size) - designatable_trees_by_size->emplace(yield, plant); - } - } + for (auto plant : world->plants.tree_dry) + newly_marked += scan_tree(out, plant, expected_yield, designatable_trees_by_size, + designate_clearcut, citizens, accessible_trees, + inaccessible_trees, designated_trees, accessible_yield, + tree_counts, designated_tree_counts, + clearcut_burrows, chop_burrows); + for (auto plant : world->plants.tree_wet) + newly_marked += scan_tree(out, plant, expected_yield, designatable_trees_by_size, + designate_clearcut, citizens, accessible_trees, + inaccessible_trees, designated_trees, accessible_yield, + tree_counts, designated_tree_counts, + clearcut_burrows, chop_burrows); return newly_marked; } From 5a4ab977a50034a7abc8946d8309fda41e8f4b8e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 12:53:59 -0800 Subject: [PATCH 0419/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 369f0c620..2bd7b8257 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autoclothing`: fixed a crash that can happen when units are holding invalid items. -@ `orders`: fix orders in library/basic that create bags - `orders`: library/military now sticks to vanilla rules and does not add orders for normally-mood-only platinum weapons. A new library orders file ``library/military_include_artifact_materials`` is now offered as an alternate ``library/military`` set of orders that still includes the platinum weapons. +- `autochop`: fixed a crash when processing trees with corrupt data structures (e.g. when a trunk tile fails to fall when the rest of the tree is chopped down) ## Misc Improvements - DFHack windows can now be "defocused" by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. From 667851d03764d71aefaf9286afcf5fb1a12c86a5 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 3 Feb 2023 21:50:53 +0000 Subject: [PATCH 0420/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index dbf19eb5f..986bf448e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit dbf19eb5ff045f7c21fee4c1bfef690461d1c315 +Subproject commit 986bf448e6584820f4d1186ec4e66451329cf7f2 From 1e904cfc11e1eac29a887b8f97a5b7362d98ba31 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Fri, 3 Feb 2023 14:08:56 -0800 Subject: [PATCH 0421/2222] GHA: use declarative env Rather than calling `env` as part of the commandline. --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a271082c2..595bde8ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,9 +143,11 @@ jobs: ccache-win64-cross-msvc-${{ github.ref_name }} ccache-win64-cross-msvc - name: Cross-compile win64 artifacts + env: + CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1' run: | cd build - env CMAKE_EXTRA_ARGS=-DBUILD_STONESENSE:BOOL=1 bash -x build-win64-from-linux.sh + bash -x build-win64-from-linux.sh - name: Format artifact name id: artifactname run: | From 4a21b0843f269f4820337d29afcf19877883645a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 14:28:17 -0800 Subject: [PATCH 0422/2222] bump to 50.05-alpha3 --- CMakeLists.txt | 2 +- docs/changelog.txt | 16 ++++++++++++++++ library/xml | 2 +- scripts | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 378ef57a6..0e6dc9c5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,7 +190,7 @@ endif() # set up versioning. set(DF_VERSION "50.05") -set(DFHACK_RELEASE "alpha2") +set(DFHACK_RELEASE "alpha3") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 2bd7b8257..9b7521325 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -33,6 +33,22 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future +## New Plugins + +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.05-alpha3 + ## New Plugins - `autoslab`: automatically create work orders to engrave slabs for ghostly dwarves diff --git a/library/xml b/library/xml index 632a5930e..0f12accaa 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 632a5930ed40616d62043de1234bf007dbbbc634 +Subproject commit 0f12accaa3e150363e446320e6daeceebb6c7022 diff --git a/scripts b/scripts index 986bf448e..6816c56cb 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 986bf448e6584820f4d1186ec4e66451329cf7f2 +Subproject commit 6816c56cbae3ccf85471fe6b574ba89643676723 From 43d1a1563b775e26b5e1580c399d813c0d1e3717 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 15:27:01 -0800 Subject: [PATCH 0423/2222] fix param parsing for setting targets --- plugins/lua/seedwatch.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/seedwatch.lua b/plugins/lua/seedwatch.lua index a72c65d47..991cd45bb 100644 --- a/plugins/lua/seedwatch.lua +++ b/plugins/lua/seedwatch.lua @@ -55,8 +55,8 @@ function parse_commandline(...) print_status() elseif command == 'clear' then set_target('all', 0) - elseif positionals[2] and positionals[3] then - set_target(positionals[2], positionals[3]) + elseif positionals[2] then + set_target(command, positionals[2]) else return false end From ce16aef29f5321d3143e52f6e5ea49bc39dc32b6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 15:27:44 -0800 Subject: [PATCH 0424/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 9b7521325..e56b79f50 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `seedwatch`: fix parameter parsing when setting targets ## Misc Improvements From 71deeb7a48d6d8f7d7f557abf10ffd90cbcbb34a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 3 Feb 2023 15:48:09 -0800 Subject: [PATCH 0425/2222] bump version to 50.05-alpha3.1 --- CMakeLists.txt | 2 +- docs/changelog.txt | 6 +++++- library/xml | 2 +- scripts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e6dc9c5b..b2cfe724e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,7 +190,7 @@ endif() # set up versioning. set(DF_VERSION "50.05") -set(DFHACK_RELEASE "alpha3") +set(DFHACK_RELEASE "alpha3.1") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index e56b79f50..bc79ac268 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,7 +36,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- `seedwatch`: fix parameter parsing when setting targets ## Misc Improvements @@ -48,6 +47,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Removed +# 50.05-alpha3.1 + +## Fixes +- `seedwatch`: fix parameter parsing when setting targets + # 50.05-alpha3 ## New Plugins diff --git a/library/xml b/library/xml index 0f12accaa..9f55f4781 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0f12accaa3e150363e446320e6daeceebb6c7022 +Subproject commit 9f55f4781cbf10c3248c4d11ddd52608ec511f21 diff --git a/scripts b/scripts index 6816c56cb..2459ea718 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6816c56cbae3ccf85471fe6b574ba89643676723 +Subproject commit 2459ea718bb5cd431b62cee09eb8e41ee18965ba From de04cf04e02848f68df76e3dd75751896b460f6d Mon Sep 17 00:00:00 2001 From: TaxiService Date: Sat, 4 Feb 2023 15:18:26 +0100 Subject: [PATCH 0426/2222] wrong scrollbar sprites mapping fix (issue #2777) i've taken a crack at it, and this seems to fix this "issue" i filed here: https://github.com/DFHack/dfhack/issues/2777 https://i.imgur.com/aFnq80M.mp4 --- library/lua/gui/widgets.lua | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index f9c9bb27e..429f28e34 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -855,18 +855,22 @@ local SCROLLBAR_UP_LEFT_PEN = to_pen{tile=922, ch=47, fg=COLOR_CYAN, bg=COLOR_BL local SCROLLBAR_UP_RIGHT_PEN = to_pen{tile=923, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_DOWN_LEFT_PEN = to_pen{tile=946, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_DOWN_RIGHT_PEN = to_pen{tile=947, ch=47, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_UP_LEFT_PEN = to_pen{tile=930, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_UP_RIGHT_PEN = to_pen{tile=931, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_LEFT_PEN = to_pen{tile=954, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_RIGHT_PEN = to_pen{tile=955, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_UP_LEFT_PEN = to_pen{tile=928, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_UP_RIGHT_PEN = to_pen{tile=929, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_LEFT_PEN = to_pen{tile=952, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_RIGHT_PEN = to_pen{tile=953, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_CENTER_UP_LEFT_PEN = to_pen{tile=932, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_CENTER_UP_RIGHT_PEN = to_pen{tile=933, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_DOWN_LEFT_PEN = to_pen{tile=960, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_DOWN_RIGHT_PEN = to_pen{tile=961, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_DOWN_LEFT_PEN = to_pen{tile=944, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_DOWN_RIGHT_PEN = to_pen{tile=945, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_CENTER_LEFT_PEN = to_pen{tile=940, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_CENTER_RIGHT_PEN = to_pen{tile=941, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_DOWN_LEFT_PEN = to_pen{tile=966, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_DOWN_RIGHT_PEN = to_pen{tile=967, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_DOWN_LEFT_PEN = to_pen{tile=964, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_DOWN_RIGHT_PEN = to_pen{tile=965, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_LEFT_PEN = to_pen{tile=948, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_RIGHT_PEN = to_pen{tile=949, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_LEFT_PEN = to_pen{tile=960, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_RIGHT_PEN = to_pen{tile=961, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_UP_LEFT_HOVER_PEN = to_pen{tile=924, ch=47, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} local SCROLLBAR_UP_RIGHT_HOVER_PEN = to_pen{tile=925, ch=92, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} local SCROLLBAR_DOWN_LEFT_HOVER_PEN = to_pen{tile=936, ch=92, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} @@ -883,6 +887,10 @@ local SCROLLBAR_BAR_CENTER_LEFT_HOVER_PEN = to_pen{tile=942, ch=219, fg=COLOR_LI local SCROLLBAR_BAR_CENTER_RIGHT_HOVER_PEN = to_pen{tile=943, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_DOWN_LEFT_HOVER_PEN = to_pen{tile=966, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_DOWN_RIGHT_HOVER_PEN = to_pen{tile=967, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_LEFT_HOVER_PEN = to_pen{tile=950, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_RIGHT_HOVER_PEN = to_pen{tile=951, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_LEFT_HOVER_PEN = to_pen{tile=962, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_RIGHT_HOVER_PEN = to_pen{tile=963, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} local SCROLLBAR_BAR_BG_LEFT_PEN = to_pen{tile=934, ch=176, fg=COLOR_DARKGREY, bg=COLOR_BLACK} local SCROLLBAR_BAR_BG_RIGHT_PEN = to_pen{tile=935, ch=176, fg=COLOR_DARKGREY, bg=COLOR_BLACK} @@ -915,6 +923,12 @@ function Scrollbar:onRenderBody(dc) if y == starty and y <= midy - 1 then dc:char(nil, hover_bar and SCROLLBAR_BAR_UP_LEFT_HOVER_PEN or SCROLLBAR_BAR_UP_LEFT_PEN) dc:char(nil, hover_bar and SCROLLBAR_BAR_UP_RIGHT_HOVER_PEN or SCROLLBAR_BAR_UP_RIGHT_PEN) + elseif y == midy - 0.5 and self.bar_height == 2 then + dc:char(nil, hover_bar and SCROLLBAR_BAR_2TALL_UP_LEFT_HOVER_PEN or SCROLLBAR_BAR_2TALL_UP_LEFT_PEN) + dc:char(nil, hover_bar and SCROLLBAR_BAR_2TALL_UP_RIGHT_HOVER_PEN or SCROLLBAR_BAR_2TALL_UP_RIGHT_PEN) + elseif y == midy + 0.5 and self.bar_height == 2 then + dc:char(nil, hover_bar and SCROLLBAR_BAR_2TALL_DOWN_LEFT_HOVER_PEN or SCROLLBAR_BAR_2TALL_DOWN_LEFT_PEN) + dc:char(nil, hover_bar and SCROLLBAR_BAR_2TALL_DOWN_RIGHT_HOVER_PEN or SCROLLBAR_BAR_2TALL_DOWN_RIGHT_PEN) elseif y == midy - 0.5 then dc:char(nil, hover_bar and SCROLLBAR_BAR_CENTER_UP_LEFT_HOVER_PEN or SCROLLBAR_BAR_CENTER_UP_LEFT_PEN) dc:char(nil, hover_bar and SCROLLBAR_BAR_CENTER_UP_RIGHT_HOVER_PEN or SCROLLBAR_BAR_CENTER_UP_RIGHT_PEN) From 2daf1ceca5b8f23f1e6b7f0efaf84bb3ec1c3efc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 4 Feb 2023 12:56:19 -0800 Subject: [PATCH 0427/2222] make automelt resistent to morphed building ids i.e. that no longer refer to valid stockiples, which can happen if the persisted data is from another game --- docs/changelog.txt | 1 + plugins/automelt.cpp | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bc79ac268..e0b749a51 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `automelt`: is now more resistent to savegame corruption ## Documentation diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index a5c288f6d..14c62a80a 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -115,13 +115,17 @@ static void remove_stockpile_config(color_ostream &out, int id) watched_stockpiles.erase(id); } +static bool isStockpile(df::building * building) { + return building->getType() == df::building_type::Stockpile; +} + static void validate_stockpile_configs(color_ostream &out) { for (auto &c : watched_stockpiles) { int id = get_config_val(c.second, STOCKPILE_CONFIG_ID); - if (!df::building::find(id)){ + auto bld = df::building::find(id); + if (!bld || !isStockpile(bld)) remove_stockpile_config(out, id); - } } } @@ -268,10 +272,6 @@ static inline bool is_metal_item(df::item *item) return (mat.getCraftClass() == craft_material_class::Metal); } -static bool isStockpile(df::building * building) { - return building->getType() == df::building_type::Stockpile; -} - struct BadFlagsCanMelt { uint32_t whole; @@ -305,7 +305,6 @@ struct BadFlagsMarkItem { // Copied from Kelly Martin's code static inline bool can_melt(df::item *item) { - static const BadFlagsCanMelt bad_flags; if (item->flags.whole & bad_flags.whole) @@ -356,7 +355,7 @@ static inline bool is_set_to_melt(df::item *item) static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_flags, int32_t stockpile_id, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { - DEBUG(perf,out).print("%s running mark_item\nshould_melt=%d\n", plugin_name,should_melt); + DEBUG(perf,out).print("%s running mark_item: should_melt=%d\n", plugin_name, should_melt); if (DBG_NAME(perf).isEnabled(DebugCategory::LDEBUG)) { string name = ""; From c345acd4172c46af82d0fff739a816a548ba4d75 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sat, 4 Feb 2023 18:48:41 -0800 Subject: [PATCH 0428/2222] Update Units::isFortControlled Add check for `agitated_wilderness_creature`. --- library/modules/Units.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index abb35b9e0..bc20cec8e 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -175,7 +175,8 @@ bool Units::isFortControlled(df::unit *unit) if (unit->flags2.bits.visitor || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.underworld || - unit->flags2.bits.resident) + unit->flags2.bits.resident || + unit->flags2.bits.agitated_wilderness_creature) return false; return unit->civ_id != -1 && unit->civ_id == plotinfo->civ_id; From 0f78509328a4b3ac10808d76084b46090d8aeb87 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sat, 4 Feb 2023 19:29:37 -0800 Subject: [PATCH 0429/2222] agitated_wilderness_creature is in unit->flags4 --- library/modules/Units.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index bc20cec8e..f2311d677 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -176,7 +176,7 @@ bool Units::isFortControlled(df::unit *unit) unit->flags2.bits.visitor_uninvited || unit->flags2.bits.underworld || unit->flags2.bits.resident || - unit->flags2.bits.agitated_wilderness_creature) + unit->flags4.bits.agitated_wilderness_creature) return false; return unit->civ_id != -1 && unit->civ_id == plotinfo->civ_id; From 1eb67ab239c2cbe4d8fef5daa0c0d32ac71bb4b5 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sat, 4 Feb 2023 19:56:37 -0800 Subject: [PATCH 0430/2222] Update Gui::resetDwarfmodeView * Set `plotinfo->main.mode` again now that alignment is fixed * Left note about unidentified global * DF resets the whole `selection_rect` struct * Comments for `ANNOUNCE_LINE_DURATION` and `ANNOUNCE_DISPLAY_TIME` constants reflect the default 50 GFPS --- library/modules/Gui.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index c661cf254..858014feb 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -87,8 +87,8 @@ using namespace DFHack; const size_t MAX_REPORTS_SIZE = 3000; // DF clears old reports to maintain this vector size const int32_t RECENT_REPORT_TICKS = 500; // used by UNIT_COMBAT_REPORT_ALL_ACTIVE -const int32_t ANNOUNCE_LINE_DURATION = 100; // time to display each line in announcement bar; 3.3 sec at 30 GFPS -const int16_t ANNOUNCE_DISPLAY_TIME = 2000; // DF uses this value for most announcements; 66.6 sec at 30 GFPS +const int32_t ANNOUNCE_LINE_DURATION = 100; // time to display each line in announcement bar; 2 sec at 50 GFPS +const int16_t ANNOUNCE_DISPLAY_TIME = 2000; // DF uses this value for most announcements; 40 sec at 50 GFPS namespace DFHack { @@ -1992,16 +1992,20 @@ void Gui::resetDwarfmodeView(bool pause) { plotinfo->follow_unit = -1; plotinfo->follow_item = -1; -/* TODO: understand how this changes for v50 plotinfo->main.mode = ui_sidebar_mode::Default; -*/ } if (selection_rect) { selection_rect->start_x = -30000; + selection_rect->start_y = -30000; + selection_rect->start_z = -30000; selection_rect->end_x = -30000; + selection_rect->end_y = -30000; + selection_rect->end_z = -30000; } + // NOTE: There's an unidentified global coord after selection_rect that is reset to -30000 here. + // This coord goes into game->main_interface.keyboard_last_track_s if the x value is not -30000. Probably okay to ignore? if (cursor) cursor->x = cursor->y = cursor->z = -30000; From 8c79ca5500a14ca8a2f676b2570064e90f63bb24 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 5 Feb 2023 07:13:47 +0000 Subject: [PATCH 0431/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 2459ea718..dc11839b6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 2459ea718bb5cd431b62cee09eb8e41ee18965ba +Subproject commit dc11839b673019e9dac0e63de0b05dedd3aea786 From 30a1f2f924553a4ec299c19a6c2cf7046ef9304d Mon Sep 17 00:00:00 2001 From: TaxiService Date: Sun, 5 Feb 2023 18:56:51 +0100 Subject: [PATCH 0432/2222] de-hardcoding scrollbar spritesheet definitions added a new variable "SBSO", and redefined all scrollbar tile definitions as this variable + an offset. this could make it easier to switch scrollbar graphics from within the game, if necessary. --- library/lua/gui/widgets.lua | 85 +++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 429f28e34..342ece201 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -851,48 +851,49 @@ local function scrollbar_is_visible(scrollbar) return scrollbar.elems_per_page < scrollbar.num_elems end -local SCROLLBAR_UP_LEFT_PEN = to_pen{tile=922, ch=47, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_UP_RIGHT_PEN = to_pen{tile=923, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_DOWN_LEFT_PEN = to_pen{tile=946, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_DOWN_RIGHT_PEN = to_pen{tile=947, ch=47, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_UP_LEFT_PEN = to_pen{tile=928, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_UP_RIGHT_PEN = to_pen{tile=929, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_LEFT_PEN = to_pen{tile=952, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_RIGHT_PEN = to_pen{tile=953, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_UP_LEFT_PEN = to_pen{tile=932, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_UP_RIGHT_PEN = to_pen{tile=933, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_DOWN_LEFT_PEN = to_pen{tile=944, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_DOWN_RIGHT_PEN = to_pen{tile=945, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_LEFT_PEN = to_pen{tile=940, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_RIGHT_PEN = to_pen{tile=941, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_DOWN_LEFT_PEN = to_pen{tile=964, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_DOWN_RIGHT_PEN = to_pen{tile=965, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_UP_LEFT_PEN = to_pen{tile=948, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_UP_RIGHT_PEN = to_pen{tile=949, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_DOWN_LEFT_PEN = to_pen{tile=960, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_DOWN_RIGHT_PEN = to_pen{tile=961, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} -local SCROLLBAR_UP_LEFT_HOVER_PEN = to_pen{tile=924, ch=47, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_UP_RIGHT_HOVER_PEN = to_pen{tile=925, ch=92, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_DOWN_LEFT_HOVER_PEN = to_pen{tile=936, ch=92, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_DOWN_RIGHT_HOVER_PEN = to_pen{tile=937, ch=47, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_UP_LEFT_HOVER_PEN = to_pen{tile=930, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_UP_RIGHT_HOVER_PEN = to_pen{tile=931, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_LEFT_HOVER_PEN = to_pen{tile=954, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_RIGHT_HOVER_PEN = to_pen{tile=955, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_UP_LEFT_HOVER_PEN = to_pen{tile=956, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_UP_RIGHT_HOVER_PEN = to_pen{tile=957, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_DOWN_LEFT_HOVER_PEN = to_pen{tile=968, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_DOWN_RIGHT_HOVER_PEN = to_pen{tile=969, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_LEFT_HOVER_PEN = to_pen{tile=942, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_CENTER_RIGHT_HOVER_PEN = to_pen{tile=943, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_DOWN_LEFT_HOVER_PEN = to_pen{tile=966, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_DOWN_RIGHT_HOVER_PEN = to_pen{tile=967, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_UP_LEFT_HOVER_PEN = to_pen{tile=950, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_UP_RIGHT_HOVER_PEN = to_pen{tile=951, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_DOWN_LEFT_HOVER_PEN = to_pen{tile=962, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_2TALL_DOWN_RIGHT_HOVER_PEN = to_pen{tile=963, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} -local SCROLLBAR_BAR_BG_LEFT_PEN = to_pen{tile=934, ch=176, fg=COLOR_DARKGREY, bg=COLOR_BLACK} -local SCROLLBAR_BAR_BG_RIGHT_PEN = to_pen{tile=935, ch=176, fg=COLOR_DARKGREY, bg=COLOR_BLACK} +local SBSO = 922 --Scroll Bar Spritesheet Offset / change this to point to a different spritesheet (ui themes, anyone? :p) +local SCROLLBAR_UP_LEFT_PEN = to_pen{tile=SBSO+0, ch=47, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_UP_RIGHT_PEN = to_pen{tile=SBSO+1, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_DOWN_LEFT_PEN = to_pen{tile=SBSO+24, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_DOWN_RIGHT_PEN = to_pen{tile=SBSO+25, ch=47, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_UP_LEFT_PEN = to_pen{tile=SBSO+6, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_UP_RIGHT_PEN = to_pen{tile=SBSO+7, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_LEFT_PEN = to_pen{tile=SBSO+30, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_RIGHT_PEN = to_pen{tile=SBSO+31, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_UP_LEFT_PEN = to_pen{tile=SBSO+10, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_UP_RIGHT_PEN = to_pen{tile=SBSO+11, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_DOWN_LEFT_PEN = to_pen{tile=SBSO+22, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_DOWN_RIGHT_PEN = to_pen{tile=SBSO+23, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_LEFT_PEN = to_pen{tile=SBSO+18, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_RIGHT_PEN = to_pen{tile=SBSO+19, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_DOWN_LEFT_PEN = to_pen{tile=SBSO+42, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_DOWN_RIGHT_PEN = to_pen{tile=SBSO+43, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_LEFT_PEN = to_pen{tile=SBSO+26, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_RIGHT_PEN = to_pen{tile=SBSO+27, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_LEFT_PEN = to_pen{tile=SBSO+38, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_RIGHT_PEN = to_pen{tile=SBSO+39, ch=219, fg=COLOR_CYAN, bg=COLOR_BLACK} +local SCROLLBAR_UP_LEFT_HOVER_PEN = to_pen{tile=SBSO+2, ch=47, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_UP_RIGHT_HOVER_PEN = to_pen{tile=SBSO+3, ch=92, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_DOWN_LEFT_HOVER_PEN = to_pen{tile=SBSO+14, ch=92, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_DOWN_RIGHT_HOVER_PEN = to_pen{tile=SBSO+15, ch=47, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_UP_LEFT_HOVER_PEN = to_pen{tile=SBSO+8, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_UP_RIGHT_HOVER_PEN = to_pen{tile=SBSO+9, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_LEFT_HOVER_PEN = to_pen{tile=SBSO+32, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_RIGHT_HOVER_PEN = to_pen{tile=SBSO+33, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_UP_LEFT_HOVER_PEN = to_pen{tile=SBSO+34, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_UP_RIGHT_HOVER_PEN = to_pen{tile=SBSO+35, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_DOWN_LEFT_HOVER_PEN = to_pen{tile=SBSO+46, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_DOWN_RIGHT_HOVER_PEN = to_pen{tile=SBSO+47, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_LEFT_HOVER_PEN = to_pen{tile=SBSO+20, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_CENTER_RIGHT_HOVER_PEN = to_pen{tile=SBSO+21, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_DOWN_LEFT_HOVER_PEN = to_pen{tile=SBSO+44, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_DOWN_RIGHT_HOVER_PEN = to_pen{tile=SBSO+45, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_LEFT_HOVER_PEN = to_pen{tile=SBSO+28, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_UP_RIGHT_HOVER_PEN = to_pen{tile=SBSO+29, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_LEFT_HOVER_PEN = to_pen{tile=SBSO+40, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_2TALL_DOWN_RIGHT_HOVER_PEN = to_pen{tile=SBSO+41, ch=219, fg=COLOR_LIGHTCYAN, bg=COLOR_BLACK} +local SCROLLBAR_BAR_BG_LEFT_PEN = to_pen{tile=SBSO+12, ch=176, fg=COLOR_DARKGREY, bg=COLOR_BLACK} +local SCROLLBAR_BAR_BG_RIGHT_PEN = to_pen{tile=SBSO+13, ch=176, fg=COLOR_DARKGREY, bg=COLOR_BLACK} function Scrollbar:onRenderBody(dc) -- don't draw if all elements are visible From b6196e91e4469ae4e0d05df905305d816a7a6ff0 Mon Sep 17 00:00:00 2001 From: TaxiService Date: Sun, 5 Feb 2023 19:00:38 +0100 Subject: [PATCH 0433/2222] increase min scrollbar height to 2 (from 1) rationale: - vanilla scrollbars dont get shorter than 2. - 2-tall 'bars are easier to click on than 1-tall 'bars. - this avoids having to make short custom graphics for 1-tall tiles, which most of the time look terrible... cons: - short scrollbars are cool : ( --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 429f28e34..71d89846d 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -808,7 +808,7 @@ local function scrollbar_get_max_pos_and_height(scrollbar) local frame_body = scrollbar.frame_body local scrollbar_body_height = (frame_body and frame_body.height or 3) - 2 - local height = math.max(1, math.floor( + local height = math.max(2, math.floor( (math.min(scrollbar.elems_per_page, scrollbar.num_elems) * scrollbar_body_height) / scrollbar.num_elems)) From 35c87dfe11ae89ba3f3359b688426bdac8814346 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sun, 5 Feb 2023 12:05:22 -0800 Subject: [PATCH 0434/2222] Update changelog.txt --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index e0b749a51..6bb01df4e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `Units::isFortControlled`: Account for agitated wildlife ## Misc Improvements - `automelt`: is now more resistent to savegame corruption From 8253505e4f07f6d09a0ce21884277e7a14a3998b Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sun, 5 Feb 2023 12:07:54 -0800 Subject: [PATCH 0435/2222] Update changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 6bb01df4e..ea140f451 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,7 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- `Units::isFortControlled`: Account for agitated wildlife +- ``Units::isFortControlled``: Account for agitated wildlife ## Misc Improvements - `automelt`: is now more resistent to savegame corruption From a770a4cae49d337423f8a108dc5eb78a42a0c75c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 27 Jan 2023 13:22:52 -0500 Subject: [PATCH 0436/2222] WIP --- docs/dev/Lua API.rst | 14 +- library/Core.cpp | 25 +- library/LuaApi.cpp | 11 +- library/include/modules/Gui.h | 13 +- library/include/modules/Screen.h | 4 +- library/lua/gui/dwarfmode.lua | 2 +- library/modules/Gui.cpp | 678 +++++++++---------------- library/modules/Screen.cpp | 2 +- plugins/CMakeLists.txt | 2 +- plugins/buildingplan/buildingplan.cpp | 2 +- plugins/confirm.cpp | 191 ++++--- plugins/dwarfmonitor.cpp | 6 +- plugins/embark-assistant/finder_ui.cpp | 2 +- plugins/embark-assistant/help_ui.cpp | 2 +- plugins/embark-tools.cpp | 2 +- plugins/hotkeys.cpp | 4 +- plugins/lua/confirm.lua | 58 +-- plugins/manipulator.cpp | 6 +- plugins/stocks.cpp | 4 +- plugins/uicommon.h | 11 - test/library/gui/dwarfmode.lua | 2 +- 21 files changed, 467 insertions(+), 574 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index f0c33eb73..58d9ea0b7 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -950,10 +950,18 @@ Screens Returns the topmost viewscreen. If ``skip_dismissed`` is *true*, ignores screens already marked to be removed. -* ``dfhack.gui.getFocusString(viewscreen)`` +* ``dfhack.gui.getFocusStrings(viewscreen)`` - Returns a string representation of the current focus position - in the ui. The string has a "screen/foo/bar/baz..." format. + Returns a table of string representations of the current UI focuses. + The strings have a "screen/foo/bar/baz..." format e.g..:: + + [1] = "dwarfmode/Info/CREATURES" + [2] = "dwardmode/Squads" + +* ``dfhack.gui.matchFocusString(focus_string)`` + + Returns ``true`` if the given ``focus_string`` is found in current focus, or ``false`` + if no match is found. Matching is case insensitive. * ``dfhack.gui.getCurFocus([skip_dismissed])`` diff --git a/library/Core.cpp b/library/Core.cpp index 98582d8cd..0a9d5f948 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -947,8 +947,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v << "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, `, or Enter)." << endl << "Context may be used to limit the scope of the binding, by" << endl << "requiring the current context to have a certain prefix." << endl - << "Current UI context is: " - << Gui::getFocusString(Core::getTopViewscreen()) << endl; + << "Current UI context is: " << endl + << join_strings("\n", Gui::getFocusStrings(Gui::getDFViewscreen())) << endl; } } else if (first == "alias") @@ -2419,11 +2419,22 @@ bool Core::SelectHotkey(int sym, int modifiers) binding.modifiers, modifiers); continue; } - string focusString = Gui::getFocusString(screen); - if (!binding.focus.empty() && !prefix_matches(binding.focus, focusString)) { - DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", - focusString.c_str(), binding.focus.c_str()); - continue; + if (!binding.focus.empty()) { + // TODO: understand more about this to figure out if this solution works + bool found = false; + std::vector focusStrings = Gui::getFocusStrings(Core::getTopViewscreen()); + // is there convention for when to use size_t vs int? + for (std::string focusString : focusStrings) { + if (prefix_matches(binding.focus, focusString)) { + found = true; + } + } + + if (!found) { + DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", + join_strings(", ", focusStrings), binding.focus.c_str()); + continue; + } } if (!plug_mgr->CanInvokeHotkey(binding.command[0], screen)) { DEBUG(keybinding).print("skipping keybinding due to hotkey guard rejection (command: '%s')\n", diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 39b7364bd..7cb8d9825 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1471,13 +1471,13 @@ static int gui_getMousePos(lua_State *L) static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getCurViewscreen), WRAPM(Gui, getDFViewscreen), - WRAPM(Gui, getFocusString), WRAPM(Gui, getCurFocus), WRAPM(Gui, getSelectedWorkshopJob), WRAPM(Gui, getSelectedJob), WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedItem), WRAPM(Gui, getSelectedBuilding), + WRAPM(Gui, getSelectedStockpile), WRAPM(Gui, getSelectedPlant), WRAPM(Gui, getAnyUnit), WRAPM(Gui, getAnyItem), @@ -1495,9 +1495,17 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, refreshSidebar), WRAPM(Gui, inRenameBuilding), WRAPM(Gui, getDepthAt), + WRAPM(Gui, matchFocusString), { NULL, NULL } }; +static int gui_getFocusStrings(lua_State *state) { + df::viewscreen *r = Lua::GetDFObject(state, 1); + std::vector focusStrings = Gui::getFocusStrings(r); + Lua::PushVector(state, focusStrings); + return 1; +} + static int gui_autoDFAnnouncement(lua_State *state) { bool rv; @@ -1623,6 +1631,7 @@ static const luaL_Reg dfhack_gui_funcs[] = { { "pauseRecenter", gui_pauseRecenter }, { "revealInDwarfmodeMap", gui_revealInDwarfmodeMap }, { "getMousePos", gui_getMousePos }, + { "getFocusStrings", gui_getFocusStrings }, { NULL, NULL } }; diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 25415ffa9..0ac48f7bb 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -36,6 +36,7 @@ distribution. #include "df/plotinfost.h" #include "df/announcement_type.h" #include "df/announcement_flags.h" +#include "df/building_stockpilest.h" #include "df/report_init.h" #include "df/report_zoom_type.h" #include "df/unit_report_type.h" @@ -65,7 +66,9 @@ namespace DFHack */ namespace Gui { - DFHACK_EXPORT std::string getFocusString(df::viewscreen *top); + DFHACK_EXPORT std::vector getFocusStrings(df::viewscreen *top); + DFHACK_EXPORT bool matchFocusString(std::string focusString); + // Full-screen item details view DFHACK_EXPORT bool item_details_hotkey(df::viewscreen *top); @@ -107,6 +110,10 @@ namespace DFHack DFHACK_EXPORT df::building *getAnyBuilding(df::viewscreen *top); DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false); + DFHACK_EXPORT bool any_stockpile_hotkey(df::viewscreen* top); + DFHACK_EXPORT df::building_stockpilest *getAnyStockpile(df::viewscreen* top); + DFHACK_EXPORT df::building_stockpilest *getSelectedStockpile(color_ostream& out, bool quiet = false); + // A plant is selected, e.g. via 'k' DFHACK_EXPORT bool any_plant_hotkey(df::viewscreen *top); DFHACK_EXPORT df::plant *getAnyPlant(df::viewscreen *top); @@ -191,8 +198,8 @@ namespace DFHack return strict_virtual_cast(getViewscreenByIdentity(T::_identity, n)); } - inline std::string getCurFocus(bool skip_dismissed = false) { - return getFocusString(getCurViewscreen(skip_dismissed)); + inline std::vector getCurFocus(bool skip_dismissed = false) { + return getFocusStrings(getCurViewscreen(skip_dismissed)); } /// get the size of the window buffer diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 0f0afd6e2..c4fa48f08 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -351,7 +351,7 @@ namespace DFHack virtual bool is_lua_screen() { return false; } - virtual std::string getFocusString() = 0; + virtual std::string getFocusStrings() = 0; virtual void onShow() {}; virtual void onDismiss() {}; virtual df::unit *getSelectedUnit() { return nullptr; } @@ -384,7 +384,7 @@ namespace DFHack static df::viewscreen *get_pointer(lua_State *L, int idx, bool make); virtual bool is_lua_screen() { return true; } - virtual std::string getFocusString() { return focus; } + virtual std::string getFocusStrings() { return focus; } virtual void render(); virtual void logic(); diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 675a7228e..1f449dab5 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -48,7 +48,7 @@ function enterSidebarMode(sidebar_mode, max_esc) local focus_string = '' while remaining_esc > 0 do local screen = dfhack.gui.getCurViewscreen(true) - focus_string = dfhack.gui.getFocusString(screen) + focus_string = dfhack.gui.getFocusStrings(screen) if df.global.plotinfo.main.mode == df.ui_sidebar_mode.Default and focus_string == 'dwarfmode/Default' then if #navkey > 0 then gui.simulateInput(screen, navkey) end diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 858014feb..fed4b0aca 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -130,479 +130,261 @@ static std::string getNameChunk(virtual_identity *id, int start, int end) * Classifying focus context by means of a string path. */ -typedef void (*getFocusStringHandler)(std::string &str, df::viewscreen *screen); -static std::map getFocusStringHandlers; +typedef void (*getFocusStringsHandler)(std::string &str, std::vector &strList, df::viewscreen *screen); +static std::map getFocusStringsHandlers; #define VIEWSCREEN(name) df::viewscreen_##name##st #define DEFINE_GET_FOCUS_STRING_HANDLER(screen_type) \ - static void getFocusString_##screen_type(std::string &focus, VIEWSCREEN(screen_type) *screen);\ + static void getFocusStrings_##screen_type(std::string &baseFocus, std::vector &focusStrings, VIEWSCREEN(screen_type) *screen);\ DFHACK_STATIC_ADD_TO_MAP(\ - &getFocusStringHandlers, &VIEWSCREEN(screen_type)::_identity, \ - (getFocusStringHandler)getFocusString_##screen_type \ + &getFocusStringsHandlers, &VIEWSCREEN(screen_type)::_identity, \ + (getFocusStringsHandler)getFocusStrings_##screen_type \ ); \ - static void getFocusString_##screen_type(std::string &focus, VIEWSCREEN(screen_type) *screen) + static void getFocusStrings_##screen_type(std::string &baseFocus, std::vector &focusStrings, VIEWSCREEN(screen_type) *screen) DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) { -/* TODO: understand how this changes for v50 - using namespace df::enums::ui_sidebar_mode; - - using df::global::ui_workshop_in_add; - using df::global::ui_build_selector; - using df::global::ui_selected_unit; - using df::global::ui_look_list; - using df::global::ui_look_cursor; - using df::global::ui_building_item_cursor; - using df::global::ui_building_assign_type; - using df::global::ui_building_assign_is_marked; - using df::global::ui_building_assign_units; - using df::global::ui_building_assign_items; - using df::global::ui_building_in_assign; - - focus += "/" + enum_item_key(plotinfo->main.mode); - - switch (plotinfo->main.mode) - { - case QueryBuilding: - if (df::building *selected = world->selected_building) - { - if (!selected->jobs.empty() && - selected->jobs[0]->job_type == job_type::DestroyBuilding) - { - focus += "/Destroying"; - break; - } - - focus += "/Some"; - - virtual_identity *id = virtual_identity::get(selected); - - bool jobs = false; - - if (id == &df::building_workshopst::_identity || - id == &df::building_furnacest::_identity) - { - focus += "/Workshop"; - jobs = true; - } - else if (id == &df::building_trapst::_identity) - { - auto trap = (df::building_trapst*)selected; - focus += "/" + enum_item_key(trap->trap_type); - if (trap->trap_type == trap_type::Lever) - jobs = true; - } - else if (ui_building_in_assign && *ui_building_in_assign && - ui_building_assign_type && ui_building_assign_units && - ui_building_assign_type->size() == ui_building_assign_units->size()) - { - focus += "/Assign"; - if (ui_building_item_cursor) - { - auto unit = vector_get(*ui_building_assign_units, *ui_building_item_cursor); - focus += unit ? "/Unit" : "/None"; - } - } - else - focus += "/" + enum_item_key(selected->getType()); - - if (jobs) - { - if (ui_workshop_in_add && *ui_workshop_in_add) - focus += "/AddJob"; - else if (!selected->jobs.empty()) - focus += "/Job"; - else - focus += "/Empty"; - } + std::string newFocusString; + + if (game->main_interface.info.open) { + newFocusString = baseFocus; + newFocusString += "/Info"; + newFocusString += "/" + enum_item_key(game->main_interface.info.current_mode); + focusStrings.push_back(newFocusString); + } + if (game->main_interface.view_sheets.open) { + newFocusString = baseFocus; + newFocusString += "/ViewSheets"; + newFocusString += "/" + enum_item_key(game->main_interface.view_sheets.active_sheet); + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::STOCKPILE) { + newFocusString = baseFocus; + // TODO: learn more about where /Some was used previously to ensure proper/consistent usage + if (game->main_interface.stockpile.cur_bld) { + newFocusString += "/Some"; } - else - focus += "/None"; - break; - - case Build: - if (ui_build_selector) - { - // Not selecting, or no choices? - if (ui_build_selector->building_type < 0) - focus += "/Type"; - else if (ui_build_selector->stage != 2) - { - if (ui_build_selector->stage != 1) - focus += "/NoMaterials"; - else - focus += "/Position"; - - focus += "/" + enum_item_key(ui_build_selector->building_type); - } - else - { - focus += "/Material"; - if (ui_build_selector->is_grouped) - focus += "/Groups"; - else - focus += "/Items"; - } - } - break; - - case ViewUnits: - if (ui_selected_unit) - { - if (vector_get(world->units.active, *ui_selected_unit)) - { - focus += "/Some"; - - using df::global::ui_unit_view_mode; - - if (ui_unit_view_mode) - focus += "/" + enum_item_key(ui_unit_view_mode->value); - } - else - focus += "/None"; - } - break; - - case LookAround: - if (ui_look_list && ui_look_cursor) - { - auto item = vector_get(ui_look_list->items, *ui_look_cursor); - if (item) - focus += "/" + enum_item_key(item->type); - else - focus += "/None"; - } - break; - - case BuildingItems: - if (VIRTUAL_CAST_VAR(selected, df::building_actual, world->selected_building)) - { - if (selected->contained_items.empty()) - focus += "/Some/Empty"; - else - focus += "/Some/Item"; - } - else - focus += "/None"; - break; - - case ZonesPenInfo: - if (ui_building_assign_type && ui_building_assign_units && - ui_building_assign_is_marked && ui_building_assign_items && - ui_building_assign_type->size() == ui_building_assign_units->size()) - { - focus += "/Assign"; - if (ui_building_item_cursor) - { - if (vector_get(*ui_building_assign_units, *ui_building_item_cursor)) - focus += "/Unit"; - else if (vector_get(*ui_building_assign_items, *ui_building_item_cursor)) - focus += "/Vermin"; - else - focus += "/None"; - } - } - break; - - case Burrows: - if (plotinfo->burrows.in_confirm_delete) - focus += "/ConfirmDelete"; - else if (plotinfo->burrows.in_add_units_mode) - focus += "/AddUnits"; - else if (plotinfo->burrows.in_edit_name_mode) - focus += "/EditName"; - else if (plotinfo->burrows.in_define_mode) - focus += "/Define"; - else - focus += "/List"; - break; - - case Hauling: - if (plotinfo->hauling.in_assign_vehicle) - { - auto vehicle = vector_get(plotinfo->hauling.vehicles, plotinfo->hauling.cursor_vehicle); - focus += "/AssignVehicle/" + std::string(vehicle ? "Some" : "None"); - } - else - { - int idx = plotinfo->hauling.cursor_top; - auto route = vector_get(plotinfo->hauling.view_routes, idx); - auto stop = vector_get(plotinfo->hauling.view_stops, idx); - std::string tag = stop ? "Stop" : (route ? "Route" : "None"); - - if (plotinfo->hauling.in_name) - focus += "/Rename/" + tag; - else if (plotinfo->hauling.in_stop) - { - int sidx = plotinfo->hauling.cursor_stop; - auto cond = vector_get(plotinfo->hauling.stop_conditions, sidx); - auto link = vector_get(plotinfo->hauling.stop_links, sidx); - - focus += "/DefineStop"; - - if (cond) - focus += "/Cond/" + enum_item_key(cond->mode); - else if (link) - { - focus += "/Link/"; - if (link->mode.bits.give) focus += "Give"; - if (link->mode.bits.take) focus += "Take"; - } - else - focus += "/None"; - } - else - focus += "/Select/" + tag; - } - break; - - default: - break; + newFocusString += "/Stockpile"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::HAULING) { + newFocusString = baseFocus; + newFocusString += "/Hauling"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.trade.open) { + newFocusString = baseFocus; + newFocusString += "/Trade"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.job_details.open) { + newFocusString = baseFocus; + newFocusString += "/JobDetails"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_trade.open) { + newFocusString = baseFocus; + newFocusString += "/AssignTrade"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.diplomacy.open) { + newFocusString = baseFocus; + newFocusString += "/Diplomacy"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.petitions.open) { + newFocusString = baseFocus; + newFocusString += "/Petitions"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.stocks.open) { + newFocusString = baseFocus; + newFocusString += "/Stocks"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_display_item.open) { + newFocusString = baseFocus; + newFocusString += "/AssignDisplayItem"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.name_creator.open) { + newFocusString = baseFocus; + newFocusString += "/NameCreator"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.image_creator.open) { + newFocusString = baseFocus; + newFocusString += "/ImageCreator"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.unit_selector.open) { + newFocusString = baseFocus; + newFocusString += "/UnitSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.announcement_alert.open) { + newFocusString = baseFocus; + newFocusString += "/AnnouncementAlert"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.custom_symbol.open) { + newFocusString = baseFocus; + newFocusString += "/CustomSymbol"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.patrol_routes.open) { + newFocusString = baseFocus; + newFocusString += "/PatrolRoutes"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_equipment.open) { + newFocusString = baseFocus; + newFocusString += "/SquadEquipment"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_schedule.open) { + newFocusString = baseFocus; + newFocusString += "/SquadSchedule"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_selector.open) { + newFocusString = baseFocus; + newFocusString += "/SquadSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.burrow_selector.open) { + newFocusString = baseFocus; + newFocusString += "/BurrowSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.location_selector.open) { + newFocusString = baseFocus; + newFocusString += "/LocationSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.location_details.open) { + newFocusString = baseFocus; + newFocusString += "/LocationDetails"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.hauling_stop_conditions.open) { + newFocusString = baseFocus; + newFocusString += "/HaulingStopConditions"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_vehicle.open) { + newFocusString = baseFocus; + newFocusString += "/AssignVehicle"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.stockpile_link.open) { + newFocusString = baseFocus; + newFocusString += "/StockpileLink"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.stockpile_tools.open) { + newFocusString = baseFocus; + newFocusString += "/StockpileTools"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.custom_stockpile.open) { + newFocusString = baseFocus; + newFocusString += "/CustomStockpile"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.create_squad.open) { + newFocusString = baseFocus; + newFocusString += "/CreateSquad"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_supplies.open) { + newFocusString = baseFocus; + newFocusString += "/SquadSupplies"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_uniform.open) { + newFocusString = baseFocus; + newFocusString += "/AssignUniform"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.create_work_order.open) { + newFocusString = baseFocus; + newFocusString += "/CreateWorkOrder"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.hotkey.open) { + newFocusString = baseFocus; + newFocusString += "/Hotkey"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.options.open) { + newFocusString = baseFocus; + newFocusString += "/Options"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.help.open) { + newFocusString = baseFocus; + newFocusString += "/Help"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.settings.open) { + newFocusString = baseFocus; + newFocusString += "/Settings"; + focusStrings.push_back(newFocusString); } -*/ -} - -/* TODO: understand how this changes for v50 -DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) -{ - using df::global::adventure; - - if (!adventure) - return; - - focus += "/" + enum_item_key(adventure->menu); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(unitlist) -{ - focus += "/" + enum_item_key(screen->page); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_military) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - auto list3 = getLayerList(screen, 2); - if (!list1 || !list2 || !list3) return; - - focus += "/" + enum_item_key(screen->page); - - int cur_list; - if (list1->active) cur_list = 0; - else if (list2->active) cur_list = 1; - else if (list3->active) cur_list = 2; - else return; - - switch (screen->page) - { - case df::viewscreen_layer_militaryst::Positions: - { - static const char *lists[] = { "/Squads", "/Positions", "/Candidates" }; - focus += lists[cur_list]; - break; - } - - case df::viewscreen_layer_militaryst::Equip: - { - focus += "/" + enum_item_key(screen->equip.mode); - - switch (screen->equip.mode) - { - case df::viewscreen_layer_militaryst::T_equip::Customize: - { - if (screen->equip.edit_mode < 0) - focus += "/View"; - else - focus += "/" + enum_item_key(screen->equip.edit_mode); - break; - } - case df::viewscreen_layer_militaryst::T_equip::Uniform: - break; - case df::viewscreen_layer_militaryst::T_equip::Priority: - { - if (screen->equip.prio_in_move >= 0) - focus += "/Move"; - else - focus += "/View"; - break; - } - } - - static const char *lists[] = { "/Squads", "/Positions", "/Choices" }; - focus += lists[cur_list]; - break; - } - - default: - break; - } -} - -DEFINE_GET_FOCUS_STRING_HANDLER(workshop_profile) -{ - typedef df::viewscreen_workshop_profilest::T_tab T_tab; - switch(screen->tab) - { - case T_tab::Workers: - focus += "/Unit"; - break; - case T_tab::Orders: - focus += "/Orders"; - break; - case T_tab::Restrictions: - focus += "/Restrictions"; - break; - } -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_noblelist) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - if (!list1 || !list2) return; - - focus += "/" + enum_item_key(screen->mode); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(pet) -{ - focus += "/" + enum_item_key(screen->mode); - - switch (screen->mode) - { - case df::viewscreen_petst::List: - focus += vector_get(screen->is_vermin, screen->cursor) ? "/Vermin" : "/Unit"; - break; - - case df::viewscreen_petst::SelectTrainer: - if (vector_get(screen->trainer_unit, screen->trainer_cursor)) - focus += "/Unit"; - break; - - default: - break; - } -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_overall_health) -{ - auto list1 = getLayerList(screen, 0); - if (!list1) return; - - focus += "/Units"; -} - -DEFINE_GET_FOCUS_STRING_HANDLER(tradegoods) -{ - if (!screen->has_traders || screen->is_unloading) - focus += "/NoTraders"; - else if (screen->in_edit_count) - focus += "/EditCount"; - else - focus += (screen->in_right_pane ? "/Items/Broker" : "/Items/Trader"); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_assigntrade) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - if (!list1 || !list2) return; - - int list_idx = vector_get(screen->visible_lists, list1->cursor, (int16_t)-1); - unsigned num_lists = sizeof(screen->lists)/sizeof(screen->lists[0]); - if (unsigned(list_idx) >= num_lists) - return; - - if (list1->active) - focus += "/Groups"; - else - focus += "/Items"; -} - -DEFINE_GET_FOCUS_STRING_HANDLER(stores) -{ - if (!screen->in_right_list) - focus += "/Categories"; - else if (screen->in_group_mode) - focus += "/Groups"; - else - focus += "/Items"; -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_stockpile) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - auto list3 = getLayerList(screen, 2); - if (!list1 || !list2 || !list3 || !screen->settings) return; - - auto group = screen->cur_group; - if (group != vector_get(screen->group_ids, list1->cursor)) - return; - - focus += "/" + enum_item_key(group); - - auto bits = vector_get(screen->group_bits, list1->cursor); - if (bits.whole && !(bits.whole & screen->settings->flags.whole)) - { - focus += "/Off"; - return; + if (game->main_interface.squad_equipment.open) { + newFocusString = baseFocus; + newFocusString += "/SquadEquipment"; + focusStrings.push_back(newFocusString); + } + // squads should be last because it's the only one not exclusive with the others? or something? + if (game->main_interface.squads.open) { + newFocusString = baseFocus; + newFocusString += "/Squads"; + focusStrings.push_back(newFocusString); } - focus += "/On"; - - if (list2->active || list3->active || screen->list_ids.empty()) { - focus += "/" + enum_item_key(screen->cur_list); - - if (list3->active) - focus += (screen->item_names.empty() ? "/None" : "/Item"); + if (!newFocusString.size()) { + focusStrings.push_back(baseFocus); } } -DEFINE_GET_FOCUS_STRING_HANDLER(locations) -{ - focus += "/" + enum_item_key(screen->menu); -} +bool Gui::matchFocusString(std::string focusString) { + focusString = toLower(focusString); + std::vector currentFocus = getFocusStrings(Core::getTopViewscreen()); -DEFINE_GET_FOCUS_STRING_HANDLER(jobmanagement) -{ - focus += (screen->in_max_workshops ? "/MaxWorkshops" : "/Main"); + return std::find_if(currentFocus.begin(), currentFocus.end(), [&focusString](std::string item) { + return focusString == toLower(item); + }) != currentFocus.end(); } -DEFINE_GET_FOCUS_STRING_HANDLER(workquota_condition) +std::vector Gui::getFocusStrings(df::viewscreen* top) { - focus += "/" + enum_item_key(screen->mode); - if (screen->item_count_edit) - focus += "/EditCount"; -} -*/ + std::vector focusStrings; -std::string Gui::getFocusString(df::viewscreen *top) -{ if (!top) - return ""; + return focusStrings; if (dfhack_viewscreen::is_instance(top)) { - auto name = static_cast(top)->getFocusString(); - return name.empty() ? "dfhack" : "dfhack/"+name; + auto name = static_cast(top)->getFocusStrings(); + focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); } else if (virtual_identity *id = virtual_identity::get(top)) { std::string name = getNameChunk(id, 11, 2); - auto handler = map_find(getFocusStringHandlers, id); + auto handler = map_find(getFocusStringsHandlers, id); if (handler) - handler(name, top); - - return name; + handler(name, focusStrings, top); } else { Core &core = Core::getInstance(); std::string name = core.p->readClassName(*(void**)top); - return name.substr(11, name.size()-11-2); + focusStrings.push_back(name.substr(11, name.size()-11-2)); } + return focusStrings; } // Predefined common guard functions @@ -1269,6 +1051,28 @@ df::item *Gui::getSelectedItem(color_ostream &out, bool quiet) return item; } +bool Gui::any_stockpile_hotkey(df::viewscreen* top) +{ + return getAnyStockpile(top) != NULL; +} + +df::building_stockpilest* Gui::getAnyStockpile(df::viewscreen* top) { + if (matchFocusString("dwarfmode/Some/Stockpile")) { + return game->main_interface.stockpile.cur_bld; + } + + return NULL; +} + +df::building_stockpilest* Gui::getSelectedStockpile(color_ostream& out, bool quiet) { + df::building_stockpilest* stockpile = getAnyStockpile(Core::getTopViewscreen()); + + if (!stockpile && !quiet) + out.printerr("No stockpile is selected in the UI.\n"); + + return stockpile; +} + df::building *Gui::getAnyBuilding(df::viewscreen *top) { using df::global::game; diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 13dbcb204..74d457140 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -1081,7 +1081,7 @@ using df::identity_traits; #define CUR_STRUCT dfhack_viewscreen static const struct_field_info dfhack_viewscreen_fields[] = { { METHOD(OBJ_METHOD, is_lua_screen), 0, 0 }, - { METHOD(OBJ_METHOD, getFocusString), 0, 0 }, + { METHOD(OBJ_METHOD, getFocusStrings), 0, 0 }, { METHOD(OBJ_METHOD, onShow), 0, 0 }, { METHOD(OBJ_METHOD, onDismiss), 0, 0 }, { METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 }, diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1c11cd797..9ce25a4cc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -97,7 +97,7 @@ add_subdirectory(channel-safely) dfhack_plugin(cleanconst cleanconst.cpp) dfhack_plugin(cleaners cleaners.cpp) dfhack_plugin(cleanowned cleanowned.cpp) -#dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) +dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) #dfhack_plugin(createitem createitem.cpp) dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index cd4e84a6e..d4471a888 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -56,7 +56,7 @@ public: void render(); - std::string getFocusString() { return "buildingplan_choosemat"; } + std::string getFocusStrings() { return "buildingplan_choosemat"; } private: ListColumn masks_column; diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 499d3f08f..80057ce4c 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -16,15 +16,11 @@ #include "uicommon.h" #include "df/building_tradedepotst.h" +#include "df/gamest.h" #include "df/general_ref.h" #include "df/general_ref_contained_in_itemst.h" #include "df/interfacest.h" #include "df/viewscreen_dwarfmodest.h" -#include "df/viewscreen_jobmanagementst.h" -#include "df/viewscreen_justicest.h" -#include "df/viewscreen_layer_militaryst.h" -#include "df/viewscreen_locationsst.h" -#include "df/viewscreen_tradegoodsst.h" using namespace DFHack; using namespace df::enums; @@ -32,11 +28,12 @@ using std::map; using std::queue; using std::string; using std::vector; +using df::global::game; DFHACK_PLUGIN("confirm"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); +REQUIRE_GLOBAL(game) REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(plotinfo); typedef std::set ikey_set; command_result df_confirm (color_ostream &out, vector & parameters); @@ -51,6 +48,8 @@ bool paused = false; // if set, confirm will unpause when this screen is no longer on the stack df::viewscreen *paused_screen = NULL; +std::string paused_focus = ""; + namespace DFHack { DBG_DECLARE(confirm,status); } @@ -121,25 +120,25 @@ public: }; namespace trade { - static bool goods_selected (const std::vector &selected) + static bool goods_selected (std::vector &selected) { - for (char c : selected) - if (c) + for (uint8_t sel : selected) + if (sel == 1) return true; return false; } - inline bool trader_goods_selected (df::viewscreen_tradegoodsst *screen) + inline bool trader_goods_selected (df::viewscreen_dwarfmodest *screen) { CHECK_NULL_POINTER(screen); - return goods_selected(screen->trader_selected); + return goods_selected(game->main_interface.trade.goodflag[0]); } - inline bool broker_goods_selected (df::viewscreen_tradegoodsst *screen) + inline bool broker_goods_selected (df::viewscreen_dwarfmodest*screen) { CHECK_NULL_POINTER(screen); - return goods_selected(screen->broker_selected); + return goods_selected(game->main_interface.trade.goodflag[1]); } - static bool goods_all_selected(const std::vector &selected, const std::vector &items) \ + /*static bool goods_all_selected(const std::vector& selected, const std::vector& items) \ { for (size_t i = 0; i < selected.size(); ++i) { @@ -162,16 +161,16 @@ namespace trade { } return true; } - inline bool trader_goods_all_selected(df::viewscreen_tradegoodsst *screen) + inline bool trader_goods_all_selected(df::viewscreen_dwarfmodest*screen) { CHECK_NULL_POINTER(screen); - return goods_all_selected(screen->trader_selected, screen->trader_items); + return false;// goods_all_selected(screen->trader_selected, screen->trader_items); } - inline bool broker_goods_all_selected(df::viewscreen_tradegoodsst *screen) + inline bool broker_goods_all_selected(df::viewscreen_dwarfmodest*screen) { CHECK_NULL_POINTER(screen); - return goods_all_selected(screen->broker_selected, screen->broker_items); - } + return false;// goods_all_selected(screen->broker_selected, screen->broker_items); + }*/ } namespace conf_lua { @@ -245,6 +244,7 @@ namespace conf_lua { DEBUG(status).print("unpausing\n"); paused = false; paused_screen = NULL; + paused_focus = ""; return 0; } int get_paused (lua_State *L) @@ -259,9 +259,9 @@ namespace conf_lua { DFHACK_PLUGIN_LUA_FUNCTIONS { CONF_LUA_FUNC( , set_conf_state), CONF_LUA_FUNC(trade, broker_goods_selected), - CONF_LUA_FUNC(trade, broker_goods_all_selected), + //CONF_LUA_FUNC(trade, broker_goods_all_selected), CONF_LUA_FUNC(trade, trader_goods_selected), - CONF_LUA_FUNC(trade, trader_goods_all_selected), + //CONF_LUA_FUNC(trade, trader_goods_all_selected), DFHACK_LUA_END }; @@ -306,16 +306,56 @@ public: return true; } bool feed (ikey_set *input) { + bool mouseExit = false; + if(df::global::enabler->mouse_rbut) { + mouseExit = true; + } + bool mouseSelect = false; + if(df::global::enabler->mouse_lbut) { + mouseSelect = true; + } + if (paused) { - // we can only detect that we've left the screen by intercepting the - // ESC key - if (!paused_screen && input->count(df::interface_key::LEAVESCREEN)) + // TODO: fix + if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) + if(mouseExit) { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + } conf_lua::api::unpause(NULL); return false; } else if (state == INACTIVE) { + if(mouseExit) { + if(intercept_key("MOUSE_RIGHT")) { + if (set_state(ACTIVE)) + { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); + last_key_is_right_click = true; + return true; + } + } + } else + last_key_is_right_click = false; + + if(mouseSelect) { + if(intercept_key("MOUSE_LEFT")) { + if (set_state(ACTIVE)) + { + df::global::enabler->mouse_lbut = 0; + df::global::enabler->mouse_lbut_down = 0; + mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); + last_key_is_left_click = true; + return true; + } + } + } else + last_key_is_left_click = false; + for (df::interface_key key : *input) { if (intercept_key(key)) @@ -331,20 +371,31 @@ public: } else if (state == ACTIVE) { - if (input->count(df::interface_key::LEAVESCREEN)) + if (input->count(df::interface_key::LEAVESCREEN) || mouseExit) { + if(mouseExit) { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + } set_state(INACTIVE); - else if (input->count(df::interface_key::SELECT)) + } else if (input->count(df::interface_key::SELECT)) set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) { + // TODO: fix DEBUG(status).print("pausing\n"); paused = true; // only record the screen when we're not at the top viewscreen // since this screen will *always* be on the stack. for // dwarfmode screens, use ESC detection to discover when to // unpause - if (!df::viewscreen_dwarfmodest::_identity.is_instance(screen)) - paused_screen = screen; + + std::vector focusStrings = Gui::getFocusStrings(Gui::getCurViewscreen()); + std::string current_focus = focusStrings[0]; + + if (current_focus != "dwarfmode") { + paused_focus = current_focus; + } + set_state(INACTIVE); } else if (input->count(df::interface_key::CUSTOM_S)) @@ -429,8 +480,27 @@ public: else if (state == SELECTED) { ikey_set tmp; - tmp.insert(last_key); - screen->feed(&tmp); + if(last_key_is_left_click) { + long prevx = df::global::gps->mouse_x; + long prevy = df::global::gps->mouse_y; + df::global::gps->mouse_x = mouse_pos.x; + df::global::gps->mouse_y = mouse_pos.y; + df::global::enabler->mouse_lbut = 1; + df::global::enabler->mouse_lbut_down = 1; + screen->feed(&tmp); + df::global::enabler->mouse_lbut = 0; + df::global::enabler->mouse_lbut_down = 0; + df::global::gps->mouse_x = prevx; + df::global::gps->mouse_y = prevy; + } + else if(last_key_is_right_click) { + tmp.insert(df::interface_key::LEAVESCREEN); + screen->feed(&tmp); + } + else { + tmp.insert(last_key); + screen->feed(&tmp); + } set_state(INACTIVE); } } @@ -445,6 +515,15 @@ public: else return false; }; + bool intercept_key (std::string mouse_button = "left") + { + CONF_LUA_START; + push(mouse_button); + if (call("intercept_key", 3, 1)) + return lua_toboolean(l_state, -1); + else + return false; + }; string get_title() { CONF_LUA_START; @@ -473,6 +552,9 @@ public: protected: cstate state; df::interface_key last_key; + bool last_key_is_left_click; + bool last_key_is_right_click; + df::coord2d mouse_pos; }; template @@ -501,18 +583,12 @@ struct cls##_hooks : cls::screen_type { \ INTERPOSE_NEXT(render)(); \ cls##_instance.render(); \ } \ - DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key)) \ - { \ - return cls##_instance.key_conflict(key) || INTERPOSE_NEXT(key_conflict)(key); \ - } \ }; \ IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, feed, prio); \ IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, render, prio); \ -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, key_conflict, prio); \ static int conf_register_##cls = conf_register(&cls##_instance, {\ &INTERPOSE_HOOK(cls##_hooks, feed), \ &INTERPOSE_HOOK(cls##_hooks, render), \ - &INTERPOSE_HOOK(cls##_hooks, key_conflict), \ }); #define DEFINE_CONFIRMATION(cls, screen) \ @@ -526,20 +602,21 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ IDs (used in the "confirm enable/disable" command, by Lua, and in the docs) are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION */ -DEFINE_CONFIRMATION(trade, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_cancel, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(haul_delete, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(squad_disband, viewscreen_layer_militaryst); -DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst); -DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst); -DEFINE_CONFIRMATION(convict, viewscreen_justicest); -DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst); +DEFINE_CONFIRMATION(trade, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst); +//DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst); +//DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst); +DEFINE_CONFIRMATION(haul_delete_route, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst); +//DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst); +//DEFINE_CONFIRMATION(convict, viewscreen_justicest); +//DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst); DFhackCExport command_result plugin_init (color_ostream &out, vector &commands) { @@ -587,18 +664,10 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) static bool screen_found(df::viewscreen *target_screen) { - if (!df::global::gview) + if (!&game->main_interface) return false; - df::viewscreen *screen = &df::global::gview->view; - while (screen) - { - if (screen == target_screen) - return true; - screen = screen->child; - } - - return false; + return target_screen == Gui::getCurViewscreen(); } DFhackCExport command_result plugin_onupdate (color_ostream &out) @@ -610,7 +679,7 @@ DFhackCExport command_result plugin_onupdate (color_ostream &out) } // if the screen that we paused on is no longer on the stack, unpause - if (paused_screen && !screen_found(paused_screen)) + if (paused_focus != "" && Gui::getFocusStrings(Gui::getCurViewscreen())[0] != paused_focus) conf_lua::api::unpause(NULL); return CR_OK; diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 2b546bfad..04c58c1e4 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -410,7 +410,7 @@ public: OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); } - std::string getFocusString() { return "dwarfmonitor_dwarfstats"; } + std::vector getFocusStrings() { return std::vector{"dwarfmonitor_dwarfstats"}; } private: ListColumn dwarves_column; @@ -1021,7 +1021,7 @@ public: OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); } - std::string getFocusString() { return "dwarfmonitor_fortstats"; } + std::vector getFocusStrings() { return std::vector{"dwarfmonitor_fortstats"}; } private: ListColumn fort_activity_column, category_breakdown_column; @@ -1652,7 +1652,7 @@ public: getSelectedUnit() ? COLOR_WHITE : COLOR_DARKGREY); } - std::string getFocusString() override { return "dwarfmonitor_preferences"; } + std::vector getFocusStrings() override { return std::vector{"dwarfmonitor_preferences"}; } private: ListColumn preferences_column; diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 6501f7f21..764b2bdb5 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -1613,7 +1613,7 @@ namespace embark_assist { void render(); - std::string getFocusString() { return "Finder UI"; } + std::vector getFocusStrings() { return std::vector{"Finder UI"}; } private: }; diff --git a/plugins/embark-assistant/help_ui.cpp b/plugins/embark-assistant/help_ui.cpp index a49a9b5e0..5e77f680b 100644 --- a/plugins/embark-assistant/help_ui.cpp +++ b/plugins/embark-assistant/help_ui.cpp @@ -31,7 +31,7 @@ namespace embark_assist{ void render(); - std::string getFocusString() { return "Help UI"; } + std::vector getFocusStrings() { return std::vector{"Help UI"}; } private: pages current_page = pages::Intro; diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 924def798..739840ab4 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -572,7 +572,7 @@ public: embark_tools_settings () { }; ~embark_tools_settings () { }; void help () { }; - std::string getFocusString () { return "embark-tools/options"; }; + std::vector getFocusStrings () { return std::vector{"embark-tools/options"}; }; void render () { parent->render(); diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 6bb6a1600..609941573 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -90,7 +90,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { valid_keys.push_back("`"); - current_focus = Gui::getFocusString(screen); + current_focus = Gui::getFocusStrings(screen)[0]; for (int shifted = 0; shifted < 2; shifted++) { for (int alt = 0; alt < 2; alt++) { for (int ctrl = 0; ctrl < 2; ctrl++) { @@ -158,7 +158,7 @@ static void list(color_ostream &out) { static bool invoke_command(color_ostream &out, const size_t index) { auto screen = Core::getTopViewscreen(); if (sorted_keys.size() <= index || - Gui::getFocusString(screen) != MENU_SCREEN_FOCUS_STRING) + !Gui::matchFocusString(MENU_SCREEN_FOCUS_STRING)) return false; auto cmd = current_bindings[sorted_keys[index]]; diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 736f13fe1..314c37eda 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -11,6 +11,9 @@ setmetatable(keys, { end, __newindex = function() error('Table is read-only') end }) +-- Mouse keys will be sent as a string instead of interface_key +local MOUSE_LEFT = "MOUSE_LEFT" +local MOUSE_RIGHT = "MOUSE_RIGHT" --[[ The screen where a confirmation has been triggered Note that this is *not* necessarily the topmost viewscreen, so do not use gui.getCurViewscreen() or related functions. ]] @@ -57,8 +60,7 @@ is equivalent to: trade = defconf('trade') function trade.intercept_key(key) - return screen.in_edit_count == 0 and - key == keys.TRADE_TRADE + return false--dfhack.gui.matchFocusString("dwarfmode/Trade") and key == MOUSE_LEFT and hovering over trade button? end trade.title = "Confirm trade" function trade.get_message() @@ -81,14 +83,14 @@ end trade_cancel = defconf('trade-cancel') function trade_cancel.intercept_key(key) - return screen.in_edit_count == 0 and - key == keys.LEAVESCREEN and - (trader_goods_selected(screen) or broker_goods_selected(screen)) + return dfhack.gui.matchFocusString("dwarfmode/Trade") and + (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and + (trader_goods_selected(screen) or broker_goods_selected(screen)) end trade_cancel.title = "Cancel trade" trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved." -trade_seize = defconf('trade-seize') +--[[trade_seize = defconf('trade-seize') function trade_seize.intercept_key(key) return screen.in_edit_count == 0 and trader_goods_selected(screen) and @@ -119,31 +121,28 @@ function trade_select_all.intercept_key(key) end trade_select_all.title = "Confirm selection" trade_select_all.message = "Selecting all goods will overwrite your current selection\n" .. - "and cannot be undone. Continue?" + "and cannot be undone. Continue?"--]] -haul_delete = defconf('haul-delete') -function haul_delete.intercept_key(key) - if ui.main.mode == df.ui_sidebar_mode.Hauling and - #ui.hauling.view_routes > 0 and - not ui.hauling.in_name and - not ui.hauling.in_stop and - not ui.hauling.in_assign_vehicle then - return key == keys.D_HAULING_REMOVE - end - return false +haul_delete_route = defconf('haul-delete-route') +function haul_delete_route.intercept_key(key) + return df.global.game.main_interface.current_hover == 180 and key == MOUSE_LEFT end -haul_delete.title = "Confirm deletion" -function haul_delete.get_message() - local t = ui.hauling.view_stops[ui.hauling.cursor_top] and "stop" or "route" - return "Are you sure you want to delete this " .. - (ui.hauling.view_stops[ui.hauling.cursor_top] and "stop" or "route") .. "?" +haul_delete_route.title = "Confirm deletion" +haul_delete_route.message = "Are you sure you want to delete this route?" + +haul_delete_stop = defconf('haul-delete-stop') +function haul_delete_stop.intercept_key(key) + return df.global.game.main_interface.current_hover == 185 and key == MOUSE_LEFT end +haul_delete_stop.title = "Confirm deletion" +haul_delete_stop.message = "Are you sure you want to delete this stop?" depot_remove = defconf('depot-remove') function depot_remove.intercept_key(key) - if df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) and - key == keys.DESTROYBUILDING then - for _, caravan in pairs(ui.caravans) do + if df.global.game.main_interface.current_hover == 299 and + key == MOUSE_LEFT and + df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) then + for _, caravan in pairs(df.global.plotinfo.caravans) do if caravan.time_remaining > 0 then return true end @@ -156,15 +155,12 @@ depot_remove.message = "Are you sure you want to remove this depot?\n" .. squad_disband = defconf('squad-disband') function squad_disband.intercept_key(key) - return key == keys.D_MILITARY_DISBAND_SQUAD and - screen.page == screen._type.T_page.Positions and - screen.num_squads > 0 and - not screen.in_rename_alert + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 341 end squad_disband.title = "Disband squad" squad_disband.message = "Are you sure you want to disband this squad?" -uniform_delete = defconf('uniform-delete') +--[[uniform_delete = defconf('uniform-delete') function uniform_delete.intercept_key(key) return key == keys.D_MILITARY_DELETE_UNIFORM and screen.page == screen._type.T_page.Uniforms and @@ -226,7 +222,7 @@ function order_remove.intercept_key(key) end order_remove.title = "Remove manager order" order_remove.message = "Are you sure you want to remove this order?" - +]]-- -- End of confirmation definitions function check() diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 6731aa513..dc8f52eed 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -864,7 +864,7 @@ public: } } } - std::string getFocusString() { return "unitlabors/batch"; } + std::vector getFocusStrings() { return std::vector{"unitlabors/batch"}; } void select_page (page p) { if (p == NICKNAME || p == PROFNAME) @@ -1034,7 +1034,7 @@ public: } } } - std::string getFocusString() { return "unitlabors/profession"; } + std::vector getFocusStrings() { return std::vector{"unitlabors/profession"}; } void feed(set *events) { if (events->count(interface_key::LEAVESCREEN)) @@ -1146,7 +1146,7 @@ public: void help() { } - std::string getFocusString() { return "unitlabors"; } + std::vector getFocusStrings() { return std::vector{"unitlabors"}; } df::unit *getSelectedUnit(); diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 8cc27067c..a901446db 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -620,7 +620,7 @@ public: for (auto line = lines.begin(); line != lines.end(); ++line) OutputString(COLOR_WHITE, x, y, line->c_str(), true, left_margin); } - std::string getFocusString() { return "stocks_view/search_help"; } + std::vector getFocusStrings() { return std::vector{"stocks_view/search_help"}; } }; class ViewscreenStocks : public dfhack_viewscreen @@ -973,7 +973,7 @@ public: OutputHotkeyString(x, y, "Search help", interface_key::HELP, true, left_margin); } - std::string getFocusString() { return "stocks_view"; } + std::vector getFocusStrings() { return std::vector{"stocks_view"}; } df::item *getSelectedItem() override { diff --git a/plugins/uicommon.h b/plugins/uicommon.h index 6179324d4..07bb34f54 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -196,17 +196,6 @@ static inline char get_string_input(const std::set *input) * Utility Functions */ -static inline df::building_stockpilest *get_selected_stockpile() -{ - if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || - df::global::plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) - { - return nullptr; - } - - return virtual_cast(df::global::world->selected_building); -} - static inline bool can_trade() { if (df::global::plotinfo->caravans.size() == 0) diff --git a/test/library/gui/dwarfmode.lua b/test/library/gui/dwarfmode.lua index 960397e02..52748f095 100644 --- a/test/library/gui/dwarfmode.lua +++ b/test/library/gui/dwarfmode.lua @@ -26,7 +26,7 @@ function test.enterSidebarMode() -- Simulate not being able to get to default from a screen via mocks. This -- failure can actually happen in-game in some situations, such as when -- naming a building with ctrl-N (no way to cancel changes). - mock.patch({{dfhack.gui, 'getFocusString', mock.func()}, + mock.patch({{dfhack.gui, 'getFocusStrings', mock.func()}, {gui, 'simulateInput', mock.func()}}, function() expect.error_match('Unable to get into target sidebar mode', From 6e442a26ca3159d4471524c3261c0d64be19e4b4 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 1 Feb 2023 17:22:56 -0500 Subject: [PATCH 0437/2222] More WIP --- library/Core.cpp | 3 +- library/modules/Gui.cpp | 11 ++- plugins/confirm.cpp | 34 +++++----- plugins/lua/confirm.lua | 146 ++++++++++++++++++++++------------------ 4 files changed, 106 insertions(+), 88 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 0a9d5f948..b480a47f2 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -948,7 +948,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v << "Context may be used to limit the scope of the binding, by" << endl << "requiring the current context to have a certain prefix." << endl << "Current UI context is: " << endl - << join_strings("\n", Gui::getFocusStrings(Gui::getDFViewscreen())) << endl; + << join_strings("\n", Gui::getFocusStrings(Gui::getCurViewscreen(true))) << endl; } } else if (first == "alias") @@ -2431,6 +2431,7 @@ bool Core::SelectHotkey(int sym, int modifiers) } if (!found) { + // TODO: fix error: format ‘%s’ expects argument of type ‘char*’, but argument 3 has type ‘std::string {aka std::basic_string}’ [-Werror=format=] DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", join_strings(", ", focusStrings), binding.focus.c_str()); continue; diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index fed4b0aca..f35db235d 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -172,6 +172,15 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) newFocusString += "/Hauling"; focusStrings.push_back(newFocusString); } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::ZONE) { + newFocusString = baseFocus; + newFocusString += "/Zone"; + if (game->main_interface.civzone.cur_bld) { + newFocusString += "/Some"; + newFocusString += "/" + enum_item_key(game->main_interface.civzone.cur_bld->type); + } + focusStrings.push_back(newFocusString); + } if (game->main_interface.trade.open) { newFocusString = baseFocus; newFocusString += "/Trade"; @@ -351,7 +360,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) bool Gui::matchFocusString(std::string focusString) { focusString = toLower(focusString); - std::vector currentFocus = getFocusStrings(Core::getTopViewscreen()); + std::vector currentFocus = getFocusStrings(getCurViewscreen(true)); return std::find_if(currentFocus.begin(), currentFocus.end(), [&focusString](std::string item) { return focusString == toLower(item); diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 80057ce4c..eecbaa83d 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -317,12 +317,7 @@ public: if (paused) { - // TODO: fix if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) - if(mouseExit) { - df::global::enabler->mouse_rbut = 0; - df::global::enabler->mouse_rbut_down = 0; - } conf_lua::api::unpause(NULL); return false; } @@ -381,16 +376,11 @@ public: set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) { - // TODO: fix DEBUG(status).print("pausing\n"); paused = true; - // only record the screen when we're not at the top viewscreen - // since this screen will *always* be on the stack. for - // dwarfmode screens, use ESC detection to discover when to - // unpause - std::vector focusStrings = Gui::getFocusStrings(Gui::getCurViewscreen()); - std::string current_focus = focusStrings[0]; + std::vector focusStrings = Gui::getFocusStrings(Gui::getCurViewscreen(true)); + std::string current_focus = focusStrings[0];// TODO: fix if (current_focus != "dwarfmode") { paused_focus = current_focus; @@ -602,21 +592,27 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ IDs (used in the "confirm enable/disable" command, by Lua, and in the docs) are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION */ -DEFINE_CONFIRMATION(trade, viewscreen_dwarfmodest); + DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest); -//DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst); -//DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst); -//DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst); DEFINE_CONFIRMATION(haul_delete_route, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest); -//DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst); +DEFINE_CONFIRMATION(order_remove, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest); + +// these are more complex to implement +//DEFINE_CONFIRMATION(convict, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(trade, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(trade_seize, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(trade_offer, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(trade_select_all, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(uniform_delete, viewscreen_dwarfmodest); //DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest); //DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest); + +// locations can't be retired currently //DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst); -//DEFINE_CONFIRMATION(convict, viewscreen_justicest); -//DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst); DFhackCExport command_result plugin_init (color_ostream &out, vector &commands) { diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 314c37eda..00a144466 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -58,9 +58,71 @@ is equivalent to: ]] +trade_cancel = defconf('trade-cancel') +function trade_cancel.intercept_key(key) + return dfhack.gui.matchFocusString("dwarfmode/Trade") and + (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and + (trader_goods_selected(screen) or broker_goods_selected(screen)) +end +trade_cancel.title = "Cancel trade" +trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved." + +haul_delete_route = defconf('haul-delete-route') +function haul_delete_route.intercept_key(key) + return df.global.game.main_interface.current_hover == 180 and key == MOUSE_LEFT +end +haul_delete_route.title = "Confirm deletion" +haul_delete_route.message = "Are you sure you want to delete this route?" + +haul_delete_stop = defconf('haul-delete-stop') +function haul_delete_stop.intercept_key(key) + return df.global.game.main_interface.current_hover == 185 and key == MOUSE_LEFT +end +haul_delete_stop.title = "Confirm deletion" +haul_delete_stop.message = "Are you sure you want to delete this stop?" + +depot_remove = defconf('depot-remove') +function depot_remove.intercept_key(key) + if df.global.game.main_interface.current_hover == 299 and + key == MOUSE_LEFT and + df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) then + for _, caravan in pairs(df.global.plotinfo.caravans) do + if caravan.time_remaining > 0 then + return true + end + end + end +end +depot_remove.title = "Confirm depot removal" +depot_remove.message = "Are you sure you want to remove this depot?\n" .. + "Merchants are present and will lose profits." + +squad_disband = defconf('squad-disband') +function squad_disband.intercept_key(key) + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 341 +end +squad_disband.title = "Disband squad" +squad_disband.message = "Are you sure you want to disband this squad?" + +order_remove = defconf('order-remove') +function order_remove.intercept_key(key) + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 222 +end +order_remove.title = "Remove manager order" +order_remove.message = "Are you sure you want to remove this order?" + +zone_remove = defconf('zone-remove') +function zone_remove.intercept_key(key) + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 130 +end +zone_remove.title = "Remove zone" +zone_remove.message = "Are you sure you want to remove this zone?" + +-- these confirmations have more complex button detection requirements +--[[ trade = defconf('trade') function trade.intercept_key(key) - return false--dfhack.gui.matchFocusString("dwarfmode/Trade") and key == MOUSE_LEFT and hovering over trade button? + dfhack.gui.matchFocusString("dwarfmode/Trade") and key == MOUSE_LEFT and hovering over trade button? end trade.title = "Confirm trade" function trade.get_message() @@ -81,16 +143,7 @@ function trade.get_message() end end -trade_cancel = defconf('trade-cancel') -function trade_cancel.intercept_key(key) - return dfhack.gui.matchFocusString("dwarfmode/Trade") and - (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and - (trader_goods_selected(screen) or broker_goods_selected(screen)) -end -trade_cancel.title = "Cancel trade" -trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved." - ---[[trade_seize = defconf('trade-seize') +trade_seize = defconf('trade-seize') function trade_seize.intercept_key(key) return screen.in_edit_count == 0 and trader_goods_selected(screen) and @@ -121,46 +174,9 @@ function trade_select_all.intercept_key(key) end trade_select_all.title = "Confirm selection" trade_select_all.message = "Selecting all goods will overwrite your current selection\n" .. - "and cannot be undone. Continue?"--]] + "and cannot be undone. Continue?" -haul_delete_route = defconf('haul-delete-route') -function haul_delete_route.intercept_key(key) - return df.global.game.main_interface.current_hover == 180 and key == MOUSE_LEFT -end -haul_delete_route.title = "Confirm deletion" -haul_delete_route.message = "Are you sure you want to delete this route?" - -haul_delete_stop = defconf('haul-delete-stop') -function haul_delete_stop.intercept_key(key) - return df.global.game.main_interface.current_hover == 185 and key == MOUSE_LEFT -end -haul_delete_stop.title = "Confirm deletion" -haul_delete_stop.message = "Are you sure you want to delete this stop?" - -depot_remove = defconf('depot-remove') -function depot_remove.intercept_key(key) - if df.global.game.main_interface.current_hover == 299 and - key == MOUSE_LEFT and - df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) then - for _, caravan in pairs(df.global.plotinfo.caravans) do - if caravan.time_remaining > 0 then - return true - end - end - end -end -depot_remove.title = "Confirm depot removal" -depot_remove.message = "Are you sure you want to remove this depot?\n" .. - "Merchants are present and will lose profits." - -squad_disband = defconf('squad-disband') -function squad_disband.intercept_key(key) - return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 341 -end -squad_disband.title = "Disband squad" -squad_disband.message = "Are you sure you want to disband this squad?" - ---[[uniform_delete = defconf('uniform-delete') +uniform_delete = defconf('uniform-delete') function uniform_delete.intercept_key(key) return key == keys.D_MILITARY_DELETE_UNIFORM and screen.page == screen._type.T_page.Uniforms and @@ -189,17 +205,6 @@ end route_delete.title = "Delete route" route_delete.message = "Are you sure you want to delete this route?" -location_retire = defconf('location-retire') -function location_retire.intercept_key(key) - return key == keys.LOCATION_RETIRE and - (screen.menu == df.viewscreen_locationsst.T_menu.Locations or - screen.menu == df.viewscreen_locationsst.T_menu.Occupations) and - screen.in_edit == df.viewscreen_locationsst.T_in_edit.None and - screen.locations[screen.location_idx] -end -location_retire.title = "Retire location" -location_retire.message = "Are you sure you want to retire this location?" - convict = defconf('convict') convict.title = "Confirm conviction" function convict.intercept_key(key) @@ -214,15 +219,22 @@ function convict.get_message() return "Are you sure you want to convict " .. name .. "?\n" .. "This action is irreversible." end +]]-- -order_remove = defconf('order-remove') -function order_remove.intercept_key(key) - return key == keys.MANAGER_REMOVE and - not screen.in_max_workshops +-- locations cannot be retired currently +--[[ +location_retire = defconf('location-retire') +function location_retire.intercept_key(key) + return key == keys.LOCATION_RETIRE and + (screen.menu == df.viewscreen_locationsst.T_menu.Locations or + screen.menu == df.viewscreen_locationsst.T_menu.Occupations) and + screen.in_edit == df.viewscreen_locationsst.T_in_edit.None and + screen.locations[screen.location_idx] end -order_remove.title = "Remove manager order" -order_remove.message = "Are you sure you want to remove this order?" +location_retire.title = "Retire location" +location_retire.message = "Are you sure you want to retire this location?" ]]-- + -- End of confirmation definitions function check() From 085a308e998fa1f3fd81da7e405c42197781e781 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 1 Feb 2023 17:29:33 -0500 Subject: [PATCH 0438/2222] Remove/readd comments --- library/Core.cpp | 1 - library/modules/Gui.cpp | 12 ++++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index b480a47f2..a098073c9 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2423,7 +2423,6 @@ bool Core::SelectHotkey(int sym, int modifiers) // TODO: understand more about this to figure out if this solution works bool found = false; std::vector focusStrings = Gui::getFocusStrings(Core::getTopViewscreen()); - // is there convention for when to use size_t vs int? for (std::string focusString : focusStrings) { if (prefix_matches(binding.focus, focusString)) { found = true; diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index f35db235d..2a5003a20 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -358,6 +358,18 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) } } +/* TODO: understand how this changes for v50 +DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) +{ + using df::global::adventure; + + if (!adventure) + return; + + focus += "/" + enum_item_key(adventure->menu); +} +*/ + bool Gui::matchFocusString(std::string focusString) { focusString = toLower(focusString); std::vector currentFocus = getFocusStrings(getCurViewscreen(true)); From 4a0d542b5e90f82ed478551fb8508c2dcde7132e Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 00:33:27 -0500 Subject: [PATCH 0439/2222] Add more confirms --- plugins/confirm.cpp | 5 ++++- plugins/lua/confirm.lua | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index eecbaa83d..87f8e4555 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -317,8 +317,9 @@ public: if (paused) { - if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) + if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) { conf_lua::api::unpause(NULL); + } return false; } else if (state == INACTIVE) @@ -372,6 +373,7 @@ public: df::global::enabler->mouse_rbut_down = 0; } set_state(INACTIVE); + Screen::invalidate(); } else if (input->count(df::interface_key::SELECT)) set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) @@ -600,6 +602,7 @@ DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(order_remove, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(burrow_remove, viewscreen_dwarfmodest); // these are more complex to implement //DEFINE_CONFIRMATION(convict, viewscreen_dwarfmodest); diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 00a144466..5e03200b8 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -118,6 +118,13 @@ end zone_remove.title = "Remove zone" zone_remove.message = "Are you sure you want to remove this zone?" +burrow_remove = defconf('burrow-remove') +function burrow_remove.intercept_key(key) + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 171 +end +burrow_remove.title = "Remove burrow" +burrow_remove.message = "Are you sure you want to remove this burrow?" + -- these confirmations have more complex button detection requirements --[[ trade = defconf('trade') From 2cb983040424d26814018658f10ce672f2b8c01c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 00:33:59 -0500 Subject: [PATCH 0440/2222] Fix default mouse_button value --- plugins/confirm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 87f8e4555..98c41e8fb 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -507,7 +507,7 @@ public: else return false; }; - bool intercept_key (std::string mouse_button = "left") + bool intercept_key (std::string mouse_button = "MOUSE_LEFT") { CONF_LUA_START; push(mouse_button); From dc40f7182c91478258f213ac60cca6f034eecd74 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 00:42:07 -0500 Subject: [PATCH 0441/2222] Fix warning --- library/Core.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index a098073c9..f4512cdbb 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2430,9 +2430,8 @@ bool Core::SelectHotkey(int sym, int modifiers) } if (!found) { - // TODO: fix error: format ‘%s’ expects argument of type ‘char*’, but argument 3 has type ‘std::string {aka std::basic_string}’ [-Werror=format=] DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", - join_strings(", ", focusStrings), binding.focus.c_str()); + join_strings(", ", focusStrings).c_str(), binding.focus.c_str()); continue; } } From 59db67e654ded9545fb848c59b64be4c182e4820 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 00:44:29 -0500 Subject: [PATCH 0442/2222] Remove unnecessary invalidate --- plugins/confirm.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 98c41e8fb..903829589 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -373,7 +373,6 @@ public: df::global::enabler->mouse_rbut_down = 0; } set_state(INACTIVE); - Screen::invalidate(); } else if (input->count(df::interface_key::SELECT)) set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) From 3da1964c5384cc7e31b12f591cf43f384beed666 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 01:05:56 -0500 Subject: [PATCH 0443/2222] Fix another warning, test force_full_display_count --- plugins/confirm.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 903829589..8a7f3e725 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -319,6 +319,8 @@ public: { if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) { conf_lua::api::unpause(NULL); + // testing + df::global::gps->force_full_display_count = 1; } return false; } @@ -373,6 +375,8 @@ public: df::global::enabler->mouse_rbut_down = 0; } set_state(INACTIVE); + // testing + df::global::gps->force_full_display_count = 1; } else if (input->count(df::interface_key::SELECT)) set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) @@ -660,6 +664,8 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) return CR_OK; } + +/* currently unused, leaving in until work is done in case it is needed again static bool screen_found(df::viewscreen *target_screen) { if (!&game->main_interface) @@ -667,6 +673,7 @@ static bool screen_found(df::viewscreen *target_screen) return target_screen == Gui::getCurViewscreen(); } +*/ DFhackCExport command_result plugin_onupdate (color_ostream &out) { From 86b57380a605894f9752327cea12edcb38f316de Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 01:20:42 -0500 Subject: [PATCH 0444/2222] fixup unnecessary changes, remove unnecessary stuff --- library/lua/gui/dwarfmode.lua | 40 -------------- library/modules/Screen.cpp | 2 +- plugins/buildingplan/buildingplan.cpp | 2 +- plugins/dwarfmonitor.cpp | 6 +-- plugins/embark-assistant/finder_ui.cpp | 2 +- plugins/embark-assistant/help_ui.cpp | 2 +- plugins/embark-tools.cpp | 2 +- plugins/manipulator.cpp | 6 +-- plugins/stocks.cpp | 4 +- test/library/gui/dwarfmode.lua | 75 -------------------------- 10 files changed, 13 insertions(+), 128 deletions(-) delete mode 100644 test/library/gui/dwarfmode.lua diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 1f449dab5..4c1a73f3a 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -29,46 +29,6 @@ SIDEBAR_MODE_KEYS = { [df.ui_sidebar_mode.ViewUnits]='D_VIEWUNIT', } --- Sends ESC keycodes until we get to dwarfmode/Default and then enters the --- specified sidebar mode with the corresponding keycode. If we don't get to --- Default after max_esc presses of ESC (default value is 10), we throw an --- error. The target sidebar mode must be a member of SIDEBAR_MODE_KEYS -function enterSidebarMode(sidebar_mode, max_esc) - local navkey = SIDEBAR_MODE_KEYS[sidebar_mode] - if not navkey then - error(('Invalid or unsupported sidebar mode: %s (%s)') - :format(sidebar_mode, df.ui_sidebar_mode[sidebar_mode])) - end - local max_esc_num = tonumber(max_esc) - if max_esc and (not max_esc_num or max_esc_num <= 0) then - error(('max_esc must be a positive number: got %s') - :format(tostring(max_esc))) - end - local remaining_esc = max_esc_num or 10 - local focus_string = '' - while remaining_esc > 0 do - local screen = dfhack.gui.getCurViewscreen(true) - focus_string = dfhack.gui.getFocusStrings(screen) - if df.global.plotinfo.main.mode == df.ui_sidebar_mode.Default and - focus_string == 'dwarfmode/Default' then - if #navkey > 0 then gui.simulateInput(screen, navkey) end - if navkey == 'D_DESIGNATE' then - -- if the z-level happens to be on the surface, the mode will be - -- set to DesignateChopTrees. we need an extra step to get to - -- DesignateMine - gui.simulateInput(dfhack.gui.getCurViewscreen(true), - 'DESIGNATE_DIG') - end - return - end - gui.simulateInput(screen, 'LEAVESCREEN') - remaining_esc = remaining_esc - 1 - end - error(('Unable to get into target sidebar mode (%s) from' .. - ' current UI viewscreen (%s).'):format( - df.ui_sidebar_mode[sidebar_mode], focus_string)) -end - function getPanelLayout() local dims = dfhack.gui.getDwarfmodeViewDims() return { diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 74d457140..13dbcb204 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -1081,7 +1081,7 @@ using df::identity_traits; #define CUR_STRUCT dfhack_viewscreen static const struct_field_info dfhack_viewscreen_fields[] = { { METHOD(OBJ_METHOD, is_lua_screen), 0, 0 }, - { METHOD(OBJ_METHOD, getFocusStrings), 0, 0 }, + { METHOD(OBJ_METHOD, getFocusString), 0, 0 }, { METHOD(OBJ_METHOD, onShow), 0, 0 }, { METHOD(OBJ_METHOD, onDismiss), 0, 0 }, { METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 }, diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index d4471a888..cd4e84a6e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -56,7 +56,7 @@ public: void render(); - std::string getFocusStrings() { return "buildingplan_choosemat"; } + std::string getFocusString() { return "buildingplan_choosemat"; } private: ListColumn masks_column; diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 04c58c1e4..2b546bfad 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -410,7 +410,7 @@ public: OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); } - std::vector getFocusStrings() { return std::vector{"dwarfmonitor_dwarfstats"}; } + std::string getFocusString() { return "dwarfmonitor_dwarfstats"; } private: ListColumn dwarves_column; @@ -1021,7 +1021,7 @@ public: OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); } - std::vector getFocusStrings() { return std::vector{"dwarfmonitor_fortstats"}; } + std::string getFocusString() { return "dwarfmonitor_fortstats"; } private: ListColumn fort_activity_column, category_breakdown_column; @@ -1652,7 +1652,7 @@ public: getSelectedUnit() ? COLOR_WHITE : COLOR_DARKGREY); } - std::vector getFocusStrings() override { return std::vector{"dwarfmonitor_preferences"}; } + std::string getFocusString() override { return "dwarfmonitor_preferences"; } private: ListColumn preferences_column; diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 764b2bdb5..6501f7f21 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -1613,7 +1613,7 @@ namespace embark_assist { void render(); - std::vector getFocusStrings() { return std::vector{"Finder UI"}; } + std::string getFocusString() { return "Finder UI"; } private: }; diff --git a/plugins/embark-assistant/help_ui.cpp b/plugins/embark-assistant/help_ui.cpp index 5e77f680b..a49a9b5e0 100644 --- a/plugins/embark-assistant/help_ui.cpp +++ b/plugins/embark-assistant/help_ui.cpp @@ -31,7 +31,7 @@ namespace embark_assist{ void render(); - std::vector getFocusStrings() { return std::vector{"Help UI"}; } + std::string getFocusString() { return "Help UI"; } private: pages current_page = pages::Intro; diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 739840ab4..924def798 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -572,7 +572,7 @@ public: embark_tools_settings () { }; ~embark_tools_settings () { }; void help () { }; - std::vector getFocusStrings () { return std::vector{"embark-tools/options"}; }; + std::string getFocusString () { return "embark-tools/options"; }; void render () { parent->render(); diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index dc8f52eed..6731aa513 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -864,7 +864,7 @@ public: } } } - std::vector getFocusStrings() { return std::vector{"unitlabors/batch"}; } + std::string getFocusString() { return "unitlabors/batch"; } void select_page (page p) { if (p == NICKNAME || p == PROFNAME) @@ -1034,7 +1034,7 @@ public: } } } - std::vector getFocusStrings() { return std::vector{"unitlabors/profession"}; } + std::string getFocusString() { return "unitlabors/profession"; } void feed(set *events) { if (events->count(interface_key::LEAVESCREEN)) @@ -1146,7 +1146,7 @@ public: void help() { } - std::vector getFocusStrings() { return std::vector{"unitlabors"}; } + std::string getFocusString() { return "unitlabors"; } df::unit *getSelectedUnit(); diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index a901446db..8cc27067c 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -620,7 +620,7 @@ public: for (auto line = lines.begin(); line != lines.end(); ++line) OutputString(COLOR_WHITE, x, y, line->c_str(), true, left_margin); } - std::vector getFocusStrings() { return std::vector{"stocks_view/search_help"}; } + std::string getFocusString() { return "stocks_view/search_help"; } }; class ViewscreenStocks : public dfhack_viewscreen @@ -973,7 +973,7 @@ public: OutputHotkeyString(x, y, "Search help", interface_key::HELP, true, left_margin); } - std::vector getFocusStrings() { return std::vector{"stocks_view"}; } + std::string getFocusString() { return "stocks_view"; } df::item *getSelectedItem() override { diff --git a/test/library/gui/dwarfmode.lua b/test/library/gui/dwarfmode.lua deleted file mode 100644 index 52748f095..000000000 --- a/test/library/gui/dwarfmode.lua +++ /dev/null @@ -1,75 +0,0 @@ -config = { - mode = 'fortress', -} - -local gui = require('gui') -local guidm = require('gui.dwarfmode') - -function test.enterSidebarMode() - expect.error_match('Invalid or unsupported sidebar mode', - function() guidm.enterSidebarMode('badmode') end) - expect.error_match('Invalid or unsupported sidebar mode', - function() guidm.enterSidebarMode( - df.ui_sidebar_mode.OrdersRefuse) end) - - expect.error_match('must be a positive number', - function() guidm.enterSidebarMode(0, 'gg') end) - expect.error_match('must be a positive number', - function() guidm.enterSidebarMode(0, 0) end) - expect.error_match('must be a positive number', - function() guidm.enterSidebarMode(0, '0') end) - expect.error_match('must be a positive number', - function() guidm.enterSidebarMode(0, -1) end) - expect.error_match('must be a positive number', - function() guidm.enterSidebarMode(0, '-1') end) - - -- Simulate not being able to get to default from a screen via mocks. This - -- failure can actually happen in-game in some situations, such as when - -- naming a building with ctrl-N (no way to cancel changes). - mock.patch({{dfhack.gui, 'getFocusStrings', mock.func()}, - {gui, 'simulateInput', mock.func()}}, - function() - expect.error_match('Unable to get into target sidebar mode', - function() - guidm.enterSidebarMode(df.ui_sidebar_mode.Default) - end) - end) - - -- verify expected starting state - expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) - expect.eq('dwarfmode/Default', dfhack.gui.getCurFocus(true)) - - -- get into the orders screen - gui.simulateInput(dfhack.gui.getCurViewscreen(true), 'D_JOBLIST') - gui.simulateInput(dfhack.gui.getCurViewscreen(true), 'UNITJOB_MANAGER') - expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) - expect.eq('jobmanagement/Main', dfhack.gui.getCurFocus(true)) - - -- get back into default from some deep screen - guidm.enterSidebarMode(df.ui_sidebar_mode.Default) - expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) - expect.eq('dwarfmode/Default', dfhack.gui.getCurFocus(true)) - - -- move from default to some other mode - guidm.enterSidebarMode(df.ui_sidebar_mode.QueryBuilding) - expect.eq(df.ui_sidebar_mode.QueryBuilding, df.global.plotinfo.main.mode) - expect.str_find('^dwarfmode/QueryBuilding', dfhack.gui.getCurFocus(true)) - - -- move between non-default modes - guidm.enterSidebarMode(df.ui_sidebar_mode.LookAround) - expect.eq(df.ui_sidebar_mode.LookAround, df.global.plotinfo.main.mode) - expect.str_find('^dwarfmode/LookAround', dfhack.gui.getCurFocus(true)) - - -- get back into default from a supported mode - guidm.enterSidebarMode(df.ui_sidebar_mode.Default) - expect.eq(df.ui_sidebar_mode.Default, df.global.plotinfo.main.mode) - expect.eq('dwarfmode/Default', dfhack.gui.getCurFocus(true)) - - -- verify that all supported modes lead where we say they'll go - for k,v in pairs(guidm.SIDEBAR_MODE_KEYS) do - guidm.enterSidebarMode(k) - expect.eq(k, df.global.plotinfo.main.mode, df.ui_sidebar_mode[k]) - end - -- end test back in default so the test harness doesn't have to autocorrect - guidm.enterSidebarMode(df.ui_sidebar_mode.Default) -end From e049bfee89d4905096772f5ecf9ee0a9a1d2cef9 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 01:29:37 -0500 Subject: [PATCH 0445/2222] Fixup docs --- docs/dev/Lua API.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 58d9ea0b7..a2c1541b7 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -960,7 +960,8 @@ Screens * ``dfhack.gui.matchFocusString(focus_string)`` - Returns ``true`` if the given ``focus_string`` is found in current focus, or ``false`` + Returns ``true`` if the given ``focus_string`` is found in the current + focus strings, or as a prefix to any of the focus strings, or ``false`` if no match is found. Matching is case insensitive. * ``dfhack.gui.getCurFocus([skip_dismissed])`` From 0c80eff087100ac337500a029e9a1c7e97a03c9c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 01:39:51 -0500 Subject: [PATCH 0446/2222] Remove unnecessary using --- plugins/confirm.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 8a7f3e725..4defc076f 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -28,7 +28,6 @@ using std::map; using std::queue; using std::string; using std::vector; -using df::global::game; DFHACK_PLUGIN("confirm"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); From 8f32f1ed7ba30d5b8e1426e9182e02b693d111ef Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 01:44:04 -0500 Subject: [PATCH 0447/2222] Fixup focusStrings->focusString --- library/include/modules/Screen.h | 4 ++-- library/modules/Gui.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index c4fa48f08..0f0afd6e2 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -351,7 +351,7 @@ namespace DFHack virtual bool is_lua_screen() { return false; } - virtual std::string getFocusStrings() = 0; + virtual std::string getFocusString() = 0; virtual void onShow() {}; virtual void onDismiss() {}; virtual df::unit *getSelectedUnit() { return nullptr; } @@ -384,7 +384,7 @@ namespace DFHack static df::viewscreen *get_pointer(lua_State *L, int idx, bool make); virtual bool is_lua_screen() { return true; } - virtual std::string getFocusStrings() { return focus; } + virtual std::string getFocusString() { return focus; } virtual void render(); virtual void logic(); diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 2a5003a20..a52453647 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -388,7 +388,7 @@ std::vector Gui::getFocusStrings(df::viewscreen* top) if (dfhack_viewscreen::is_instance(top)) { - auto name = static_cast(top)->getFocusStrings(); + auto name = static_cast(top)->getFocusString(); focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); } else if (virtual_identity *id = virtual_identity::get(top)) From a14de11a13afcef8f970ecf72c95d66a7bb49666 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 02:08:23 -0500 Subject: [PATCH 0448/2222] Clean up artifacts during render --- plugins/confirm.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 4defc076f..7d375e77e 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -318,8 +318,6 @@ public: { if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) { conf_lua::api::unpause(NULL); - // testing - df::global::gps->force_full_display_count = 1; } return false; } @@ -374,8 +372,6 @@ public: df::global::enabler->mouse_rbut_down = 0; } set_state(INACTIVE); - // testing - df::global::gps->force_full_display_count = 1; } else if (input->count(df::interface_key::SELECT)) set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) @@ -497,6 +493,8 @@ public: } set_state(INACTIVE); } + // clean up any artifacts + df::global::gps->force_full_display_count = 1; } virtual string get_id() override = 0; #define CONF_LUA_START using namespace conf_lua; Lua::StackUnwinder unwind(l_state); push(screen); push(get_id()); From 576fcfbeabf924d2480f8378a623bfb9c6757f66 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 02:22:49 -0500 Subject: [PATCH 0449/2222] Clean up some nesting --- plugins/confirm.cpp | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 7d375e77e..9caf7c19d 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -324,42 +324,33 @@ public: else if (state == INACTIVE) { if(mouseExit) { - if(intercept_key("MOUSE_RIGHT")) { - if (set_state(ACTIVE)) - { - df::global::enabler->mouse_rbut = 0; - df::global::enabler->mouse_rbut_down = 0; - mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); - last_key_is_right_click = true; - return true; - } + if(intercept_key("MOUSE_RIGHT") && set_state(ACTIVE)) { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); + last_key_is_right_click = true; + return true; } } else last_key_is_right_click = false; if(mouseSelect) { - if(intercept_key("MOUSE_LEFT")) { - if (set_state(ACTIVE)) - { - df::global::enabler->mouse_lbut = 0; - df::global::enabler->mouse_lbut_down = 0; - mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); - last_key_is_left_click = true; - return true; - } + if(intercept_key("MOUSE_LEFT") && set_state(ACTIVE)) { + df::global::enabler->mouse_lbut = 0; + df::global::enabler->mouse_lbut_down = 0; + mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); + last_key_is_left_click = true; + return true; } } else last_key_is_left_click = false; for (df::interface_key key : *input) { - if (intercept_key(key)) + if (intercept_key(key) && set_state(ACTIVE)) { - if (set_state(ACTIVE)) - { - last_key = key; - return true; - } + last_key = key; + return true; } } return false; From fdbe91e35b9d598003d4f5bad5a1806135bb394c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 02:45:50 -0500 Subject: [PATCH 0450/2222] Add the rest of the bottom modes --- library/modules/Gui.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index a52453647..5720f9325 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -167,6 +167,11 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) newFocusString += "/Stockpile"; focusStrings.push_back(newFocusString); } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::STOCKPILE_PAINT) { + newFocusString = baseFocus; + newFocusString += "/Stockpile/Paint"; + focusStrings.push_back(newFocusString); + } if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::HAULING) { newFocusString = baseFocus; newFocusString += "/Hauling"; @@ -181,6 +186,36 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) } focusStrings.push_back(newFocusString); } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::ZONE_PAINT) { + newFocusString = baseFocus; + newFocusString += "/Zone/Paint"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BURROW) { + newFocusString = baseFocus; + newFocusString += "/Burrow"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BURROW_PAINT) { + newFocusString = baseFocus; + newFocusString += "/Burrow/Paint"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BUILDING) { + newFocusString = baseFocus; + newFocusString += "/Building"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BUILDING_PLACEMENT) { + newFocusString = baseFocus; + newFocusString += "/Building/Placement"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BUILDING_PICK_MATERIALS) { + newFocusString = baseFocus; + newFocusString += "/Building/PickMaterials"; + focusStrings.push_back(newFocusString); + } if (game->main_interface.trade.open) { newFocusString = baseFocus; newFocusString += "/Trade"; From a5de0fb0de041923bb2941ec8ae25a1572284536 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 02:50:33 -0500 Subject: [PATCH 0451/2222] Add stockpile removal confirm --- plugins/confirm.cpp | 1 + plugins/lua/confirm.lua | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 9caf7c19d..752287749 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -594,6 +594,7 @@ DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(order_remove, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(burrow_remove, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(stockpile_remove, viewscreen_dwarfmodest); // these are more complex to implement //DEFINE_CONFIRMATION(convict, viewscreen_dwarfmodest); diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 5e03200b8..1d4955f99 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -125,6 +125,13 @@ end burrow_remove.title = "Remove burrow" burrow_remove.message = "Are you sure you want to remove this burrow?" +stockpile_remove = defconf('stockpile-remove') +function stockpile_remove.intercept_key(key) + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 118 +end +stockpile_remove.title = "Remove stockpile" +stockpile_remove.message = "Are you sure you want to remove this stockpile?" + -- these confirmations have more complex button detection requirements --[[ trade = defconf('trade') From f7df51587c9b77818dc3981028c28d1ba35761eb Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 13:21:38 -0500 Subject: [PATCH 0452/2222] More focus strings, correct docs --- docs/dev/Lua API.rst | 2 +- library/modules/Gui.cpp | 179 ++++++++++++++++++++++++++++------------ 2 files changed, 126 insertions(+), 55 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index a2c1541b7..242e84037 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -955,7 +955,7 @@ Screens Returns a table of string representations of the current UI focuses. The strings have a "screen/foo/bar/baz..." format e.g..:: - [1] = "dwarfmode/Info/CREATURES" + [1] = "dwarfmode/Info/CREATURES/CITIZEN" [2] = "dwardmode/Squads" * ``dfhack.gui.matchFocusString(focus_string)`` diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 5720f9325..cde3b40fe 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -146,10 +146,34 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) { std::string newFocusString; + if(game->main_interface.main_designation_selected != -1) { + newFocusString = baseFocus; + newFocusString += "/Designate/" + enum_item_key(game->main_interface.main_designation_selected); + focusStrings.push_back(newFocusString); + } if (game->main_interface.info.open) { newFocusString = baseFocus; newFocusString += "/Info"; newFocusString += "/" + enum_item_key(game->main_interface.info.current_mode); + + switch(game->main_interface.info.current_mode) { + case df::enums::info_interface_mode_type::CREATURES: + newFocusString += "/" + enum_item_key(game->main_interface.info.creatures.current_mode); + break; + case df::enums::info_interface_mode_type::BUILDINGS: + newFocusString += "/" + enum_item_key(game->main_interface.info.buildings.mode); + break; + case df::enums::info_interface_mode_type::LABOR: + newFocusString += "/" + enum_item_key(game->main_interface.info.labor.mode); + break; + case df::enums::info_interface_mode_type::ARTIFACTS: + newFocusString += "/" + enum_item_key(game->main_interface.info.artifacts.mode); + break; + case df::enums::info_interface_mode_type::JUSTICE: + newFocusString += "/" + enum_item_key(game->main_interface.info.justice.current_mode); + break; + } + focusStrings.push_back(newFocusString); } if (game->main_interface.view_sheets.open) { @@ -158,64 +182,111 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) newFocusString += "/" + enum_item_key(game->main_interface.view_sheets.active_sheet); focusStrings.push_back(newFocusString); } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::STOCKPILE) { - newFocusString = baseFocus; - // TODO: learn more about where /Some was used previously to ensure proper/consistent usage - if (game->main_interface.stockpile.cur_bld) { - newFocusString += "/Some"; - } - newFocusString += "/Stockpile"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::STOCKPILE_PAINT) { - newFocusString = baseFocus; - newFocusString += "/Stockpile/Paint"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::HAULING) { - newFocusString = baseFocus; - newFocusString += "/Hauling"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::ZONE) { + + if(game->main_interface.bottom_mode_selected != -1) { newFocusString = baseFocus; - newFocusString += "/Zone"; - if (game->main_interface.civzone.cur_bld) { - newFocusString += "/Some"; - newFocusString += "/" + enum_item_key(game->main_interface.civzone.cur_bld->type); + + switch(game->main_interface.bottom_mode_selected) { + case df::enums::main_bottom_mode_type::STOCKPILE: + if (game->main_interface.stockpile.cur_bld) { + newFocusString += "/Some"; + } + newFocusString += "/Stockpile"; + break; + case df::enums::main_bottom_mode_type::STOCKPILE_PAINT: + newFocusString += "/Stockpile/Paint"; + break; + case df::enums::main_bottom_mode_type::HAULING: + newFocusString += "/Hauling"; + break; + case df::enums::main_bottom_mode_type::ZONE: + newFocusString += "/Zone"; + if (game->main_interface.civzone.cur_bld) { + newFocusString += "/Some"; + newFocusString += "/" + enum_item_key(game->main_interface.civzone.cur_bld->type); + } + break; + case df::enums::main_bottom_mode_type::ZONE_PAINT: + newFocusString += "/Zone/Paint"; + + // TODO: figure out why enum_item_key doesn't work on this? + switch(game->main_interface.civzone.adding_new_type) { + case df::enums::civzone_type::MeetingHall: + newFocusString += "/MeetingHall"; + break; + case df::enums::civzone_type::Bedroom: + newFocusString += "/Bedroom"; + break; + case df::enums::civzone_type::DiningHall: + newFocusString += "/DiningHall"; + break; + case df::enums::civzone_type::Pen: + newFocusString += "/Pen"; + break; + case df::enums::civzone_type::Pond: + newFocusString += "/Pond"; + break; + case df::enums::civzone_type::WaterSource: + newFocusString += "/WaterSource"; + break; + case df::enums::civzone_type::Dungeon: + newFocusString += "/Dungeon"; + break; + case df::enums::civzone_type::FishingArea: + newFocusString += "/FishingArea"; + break; + case df::enums::civzone_type::SandCollection: + newFocusString += "/SandCollection"; + break; + case df::enums::civzone_type::Office: + newFocusString += "/Office"; + break; + case df::enums::civzone_type::Dormitory: + newFocusString += "/Dormitory"; + break; + case df::enums::civzone_type::Barracks: + newFocusString += "/Barracks"; + break; + case df::enums::civzone_type::ArcheryRange: + newFocusString += "/ArcheryRange"; + break; + case df::enums::civzone_type::Dump: + newFocusString += "/Dump"; + break; + case df::enums::civzone_type::AnimalTraining: + newFocusString += "/AnimalTraining"; + break; + case df::enums::civzone_type::Tomb: + newFocusString += "/Tomb"; + break; + case df::enums::civzone_type::PlantGathering: + newFocusString += "/PlantGathering"; + break; + case df::enums::civzone_type::ClayCollection: + newFocusString += "/ClayCollection"; + break; + } + break; + case df::enums::main_bottom_mode_type::BURROW: + newFocusString += "/Burrow"; + break; + case df::enums::main_bottom_mode_type::BURROW_PAINT: + newFocusString += "/Burrow/Paint"; + break; + case df::enums::main_bottom_mode_type::BUILDING: + newFocusString += "/Building"; + break; + case df::enums::main_bottom_mode_type::BUILDING_PLACEMENT: + newFocusString += "/Building/Placement"; + break; + case df::enums::main_bottom_mode_type::BUILDING_PICK_MATERIALS: + newFocusString += "/Building/PickMaterials"; + break; } + focusStrings.push_back(newFocusString); } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::ZONE_PAINT) { - newFocusString = baseFocus; - newFocusString += "/Zone/Paint"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BURROW) { - newFocusString = baseFocus; - newFocusString += "/Burrow"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BURROW_PAINT) { - newFocusString = baseFocus; - newFocusString += "/Burrow/Paint"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BUILDING) { - newFocusString = baseFocus; - newFocusString += "/Building"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BUILDING_PLACEMENT) { - newFocusString = baseFocus; - newFocusString += "/Building/Placement"; - focusStrings.push_back(newFocusString); - } - if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::BUILDING_PICK_MATERIALS) { - newFocusString = baseFocus; - newFocusString += "/Building/PickMaterials"; - focusStrings.push_back(newFocusString); - } + if (game->main_interface.trade.open) { newFocusString = baseFocus; newFocusString += "/Trade"; From 14f1e4b52fe3093491344e54af66733a5f4ca024 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 20:53:58 -0500 Subject: [PATCH 0453/2222] Per confirm pause maybe? --- library/include/modules/Gui.h | 2 +- library/modules/Gui.cpp | 10 +-- plugins/confirm.cpp | 113 ++++++++++++++++++++-------------- 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 0ac48f7bb..cfb8925e3 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -67,7 +67,7 @@ namespace DFHack namespace Gui { DFHACK_EXPORT std::vector getFocusStrings(df::viewscreen *top); - DFHACK_EXPORT bool matchFocusString(std::string focusString); + DFHACK_EXPORT bool matchFocusString(std::string focusString, bool prefixMatch = true); // Full-screen item details view diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index cde3b40fe..3529d0d87 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -476,13 +476,13 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) } */ -bool Gui::matchFocusString(std::string focusString) { +bool Gui::matchFocusString(std::string focusString, bool prefixMatch) { focusString = toLower(focusString); - std::vector currentFocus = getFocusStrings(getCurViewscreen(true)); + std::vector currentFocusStrings = getFocusStrings(getCurViewscreen(true)); - return std::find_if(currentFocus.begin(), currentFocus.end(), [&focusString](std::string item) { - return focusString == toLower(item); - }) != currentFocus.end(); + return std::find_if(currentFocusStrings.begin(), currentFocusStrings.end(), [&focusString, &prefixMatch](std::string item) { + return prefixMatch ? toLower(item).rfind(focusString, 0) == 0 : focusString == toLower(item); + }) != currentFocusStrings.end(); } std::vector Gui::getFocusStrings(df::viewscreen* top) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 752287749..419df5443 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -42,12 +42,8 @@ static map confirmations; string active_id; queue cmds; -// true when confirm is paused -bool paused = false; -// if set, confirm will unpause when this screen is no longer on the stack -df::viewscreen *paused_screen = NULL; - -std::string paused_focus = ""; +// TODO: move to confirmation instance with paused_focus etc + df::viewscreen *paused_screen = NULL; namespace DFHack { DBG_DECLARE(confirm,status); @@ -70,11 +66,13 @@ string char_replace (string s, char a, char b) } bool set_conf_state (string name, bool state); +bool set_conf_paused (string name, bool pause); class confirmation_base { public: enum cstate { INACTIVE, ACTIVE, SELECTED }; virtual string get_id() = 0; + virtual string get_focus_string() = 0; virtual bool set_state(cstate) = 0; static bool set_state(string id, cstate state) @@ -94,6 +92,7 @@ confirmation_base *confirmation_base::active = nullptr; struct conf_wrapper { private: bool enabled; + bool paused; std::set hooks; public: conf_wrapper() @@ -115,7 +114,12 @@ public: enabled = state; return true; } + bool set_paused (bool pause) { + paused = pause; + return true; + } inline bool is_enabled() { return enabled; } + inline bool is_paused() { return paused; } }; namespace trade { @@ -226,6 +230,7 @@ namespace conf_lua { lua_newtable(L); Lua::TableInsert(L, "id", item.first); Lua::TableInsert(L, "enabled", item.second->is_enabled()); + Lua::TableInsert(L, "paused", item.second->is_paused()); lua_settable(L, -3); } return 1; @@ -238,25 +243,13 @@ namespace conf_lua { lua_pushnil(L); return 1; } - int unpause(lua_State *) - { - DEBUG(status).print("unpausing\n"); - paused = false; - paused_screen = NULL; - paused_focus = ""; - return 0; - } - int get_paused (lua_State *L) - { - Lua::Push(L, paused); - return 1; - } } } #define CONF_LUA_FUNC(ns, name) {#name, df::wrap_function(ns::name, true)} DFHACK_PLUGIN_LUA_FUNCTIONS { CONF_LUA_FUNC( , set_conf_state), + CONF_LUA_FUNC( , set_conf_paused), CONF_LUA_FUNC(trade, broker_goods_selected), //CONF_LUA_FUNC(trade, broker_goods_all_selected), CONF_LUA_FUNC(trade, trader_goods_selected), @@ -269,8 +262,6 @@ DFHACK_PLUGIN_LUA_COMMANDS { CONF_LUA_CMD(get_ids), CONF_LUA_CMD(get_conf_data), CONF_LUA_CMD(get_active_id), - CONF_LUA_CMD(unpause), - CONF_LUA_CMD(get_paused), DFHACK_LUA_END }; @@ -284,6 +275,7 @@ class confirmation : public confirmation_base { public: typedef T screen_type; screen_type *screen; + bool tryUnpauseOnRender; bool set_state (cstate s) override { @@ -314,11 +306,12 @@ public: mouseSelect = true; } - if (paused) + conf_wrapper *wrapper = confirmations[this->get_id()]; + if(wrapper->is_paused()) { - if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) { - conf_lua::api::unpause(NULL); - } + if ((input->count(df::interface_key::LEAVESCREEN) || mouseExit)) + tryUnpauseOnRender = true; + return false; } else if (state == INACTIVE) @@ -368,15 +361,8 @@ public: else if (input->count(df::interface_key::CUSTOM_P)) { DEBUG(status).print("pausing\n"); - paused = true; - - std::vector focusStrings = Gui::getFocusStrings(Gui::getCurViewscreen(true)); - std::string current_focus = focusStrings[0];// TODO: fix - - if (current_focus != "dwarfmode") { - paused_focus = current_focus; - } + wrapper->set_paused(true); set_state(INACTIVE); } else if (input->count(df::interface_key::CUSTOM_S)) @@ -392,6 +378,15 @@ public: return state == ACTIVE; } void render() { + if(tryUnpauseOnRender) { + tryUnpauseOnRender = false; + conf_wrapper *wrapper = confirmations[this->get_id()]; + std::string concernedFocus = this->get_focus_string(); + bool prefixMatch = concernedFocus.find("*") != std::string::npos; + + if(!Gui::matchFocusString(this->get_focus_string(), prefixMatch)) + wrapper->set_paused(false); + } static vector lines; static const std::string pause_message = "Pause confirmations until you exit this screen"; @@ -487,7 +482,8 @@ public: // clean up any artifacts df::global::gps->force_full_display_count = 1; } - virtual string get_id() override = 0; + string get_id() override = 0; + string get_focus_string() override = 0; #define CONF_LUA_START using namespace conf_lua; Lua::StackUnwinder unwind(l_state); push(screen); push(get_id()); bool intercept_key (df::interface_key key) { @@ -574,9 +570,10 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ &INTERPOSE_HOOK(cls##_hooks, render), \ }); -#define DEFINE_CONFIRMATION(cls, screen) \ +#define DEFINE_CONFIRMATION(cls, screen, focusString) \ class confirmation_##cls : public confirmation { \ virtual string get_id() { static string id = char_replace(#cls, '_', '-'); return id; } \ + virtual string get_focus_string() { return focusString; } \ }; \ IMPLEMENT_CONFIRMATION_HOOKS(confirmation_##cls, 0); @@ -584,17 +581,19 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ implemented in plugins/lua/confirm.lua. IDs (used in the "confirm enable/disable" command, by Lua, and in the docs) are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION + + TODO: document focus string stuff and how * does prefix matching */ -DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(haul_delete_route, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(order_remove, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(burrow_remove, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(stockpile_remove, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest, "dwarfmode/Trade"); +DEFINE_CONFIRMATION(haul_delete_route, viewscreen_dwarfmodest, "dwarfmode/Hauling"); +DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest, "dwarfmode/Hauling"); +DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest, "dwarfmode/ViewSheets/BUILDING"); +DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest, "dwarfmode/Squads"); +DEFINE_CONFIRMATION(order_remove, viewscreen_dwarfmodest, "dwarfmode/Info/WORK_ORDERS"); +DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest, "dwarfmode/Zone*"); +DEFINE_CONFIRMATION(burrow_remove, viewscreen_dwarfmodest, "dwarfmode/Burrow"); +DEFINE_CONFIRMATION(stockpile_remove, viewscreen_dwarfmodest, "dwarfmode/Some/Stockpile"); // these are more complex to implement //DEFINE_CONFIRMATION(convict, viewscreen_dwarfmodest); @@ -672,9 +671,10 @@ DFhackCExport command_result plugin_onupdate (color_ostream &out) cmds.pop(); } + // TODO: reimplement // if the screen that we paused on is no longer on the stack, unpause - if (paused_focus != "" && Gui::getFocusStrings(Gui::getCurViewscreen())[0] != paused_focus) - conf_lua::api::unpause(NULL); + //if (paused_focus != "" && Gui::getFocusStrings(Gui::getCurViewscreen())[0] != paused_focus) + //conf_lua::api::unpause(NULL); return CR_OK; } @@ -700,6 +700,27 @@ bool set_conf_state (string name, bool state) return found; } +bool set_conf_paused (string name, bool pause) +{ + bool found = false; + for (auto it : confirmations) + { + if (it.first == name) + { + found = true; + it.second->set_paused(pause); + } + } + + if (pause == true) + { + // dismiss the confirmation too + confirmation_base::set_state(name, confirmation_base::INACTIVE); + } + + return found; +} + void enable_conf (color_ostream &out, string name, bool state) { if (!set_conf_state(name, state)) From 1c08b56a899cb1f3b8d5335bc60e334cdabe2935 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 21:35:46 -0500 Subject: [PATCH 0454/2222] Proper prefix matching in hotkeys? --- plugins/hotkeys.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 609941573..133325cce 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -26,7 +26,6 @@ static const string INVOKE_HOTKEYS_COMMAND = "hotkeys"; static const std::string MENU_SCREEN_FOCUS_STRING = "dfhack/lua/hotkeys/menu"; static bool valid = false; // whether the following two vars contain valid data -static string current_focus; static map current_bindings; static vector sorted_keys; @@ -38,14 +37,13 @@ static bool can_invoke(const string &cmdline, df::viewscreen *screen) { } static int cleanupHotkeys(lua_State *) { - DEBUG(log).print("cleaning up old stub keybindings for %s\n", current_focus.c_str()); + DEBUG(log).print("cleaning up old stub keybindings for:\n %s\n", join_strings("\n", Gui::getFocusStrings(Gui::getCurViewscreen(true))).c_str()); std::for_each(sorted_keys.begin(), sorted_keys.end(), [](const string &sym) { string keyspec = sym + "@" + MENU_SCREEN_FOCUS_STRING; DEBUG(log).print("clearing keybinding: %s\n", keyspec.c_str()); Core::getInstance().ClearKeyBindings(keyspec); }); valid = false; - current_focus = ""; sorted_keys.clear(); current_bindings.clear(); return 0; @@ -89,8 +87,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { } valid_keys.push_back("`"); - - current_focus = Gui::getFocusStrings(screen)[0]; + vector focusStrings = Gui::getFocusStrings(screen); for (int shifted = 0; shifted < 2; shifted++) { for (int alt = 0; alt < 2; alt++) { for (int ctrl = 0; ctrl < 2; ctrl++) { @@ -112,9 +109,11 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { vector tokens; split_string(&tokens, *invoke_cmd, ":"); string focus = tokens[0].substr(1); - if (prefix_matches(focus, current_focus)) { - auto cmdline = trim(tokens[1]); - add_binding_if_valid(sym, cmdline, screen, filtermenu); + for(string focusString : focusStrings) { + if (prefix_matches(toLower(focus), toLower(focusString))) { + auto cmdline = trim(tokens[1]); + add_binding_if_valid(sym, cmdline, screen, filtermenu); + } } } } @@ -145,8 +144,8 @@ static void list(color_ostream &out) { if (!valid) find_active_keybindings(Gui::getCurViewscreen(true), false); - out.print("Valid keybindings for the current screen (%s)\n", - current_focus.c_str()); + out.print("Valid keybindings for the current focus:\n %s\n", + join_strings("\n", Gui::getFocusStrings(Gui::getCurViewscreen(true))).c_str()); std::for_each(sorted_keys.begin(), sorted_keys.end(), [&](const string &sym) { out.print("%s: %s\n", sym.c_str(), current_bindings[sym].c_str()); }); From cece0bfca18ca7ca708059b9ddb2290eb9d9ff8a Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 21:39:01 -0500 Subject: [PATCH 0455/2222] Remove unnecessary comment --- library/Core.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index f4512cdbb..1f2f6e159 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2420,7 +2420,6 @@ bool Core::SelectHotkey(int sym, int modifiers) continue; } if (!binding.focus.empty()) { - // TODO: understand more about this to figure out if this solution works bool found = false; std::vector focusStrings = Gui::getFocusStrings(Core::getTopViewscreen()); for (std::string focusString : focusStrings) { From 36e4bba77932a6f8aaa08eab1efccf76f760ced3 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 22:46:32 -0500 Subject: [PATCH 0456/2222] Fixup prefix matching --- library/Core.cpp | 6 ++---- library/modules/Gui.cpp | 2 +- plugins/hotkeys.cpp | 8 +++----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 1f2f6e159..536ab3f20 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2422,10 +2422,8 @@ bool Core::SelectHotkey(int sym, int modifiers) if (!binding.focus.empty()) { bool found = false; std::vector focusStrings = Gui::getFocusStrings(Core::getTopViewscreen()); - for (std::string focusString : focusStrings) { - if (prefix_matches(binding.focus, focusString)) { - found = true; - } + if(Gui::matchFocusString(binding.focus)) { + found = true; } if (!found) { diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 3529d0d87..d3615f17d 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -481,7 +481,7 @@ bool Gui::matchFocusString(std::string focusString, bool prefixMatch) { std::vector currentFocusStrings = getFocusStrings(getCurViewscreen(true)); return std::find_if(currentFocusStrings.begin(), currentFocusStrings.end(), [&focusString, &prefixMatch](std::string item) { - return prefixMatch ? toLower(item).rfind(focusString, 0) == 0 : focusString == toLower(item); + return prefixMatch ? prefix_matches(focusString, toLower(item)) : focusString == toLower(item); }) != currentFocusStrings.end(); } diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 133325cce..324a972b5 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -109,11 +109,9 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { vector tokens; split_string(&tokens, *invoke_cmd, ":"); string focus = tokens[0].substr(1); - for(string focusString : focusStrings) { - if (prefix_matches(toLower(focus), toLower(focusString))) { - auto cmdline = trim(tokens[1]); - add_binding_if_valid(sym, cmdline, screen, filtermenu); - } + if(Gui::matchFocusString(focus)) { + auto cmdline = trim(tokens[1]); + add_binding_if_valid(sym, cmdline, screen, filtermenu); } } } From 2bf9b86c7b77edc9d1f7e724f0457cecfaae018c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 2 Feb 2023 23:15:49 -0500 Subject: [PATCH 0457/2222] Fix getCurFocus lua, use where appropriate in c++ --- library/Core.cpp | 4 ++-- library/LuaApi.cpp | 9 ++++++++- plugins/hotkeys.cpp | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 536ab3f20..d56519a9c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -948,7 +948,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v << "Context may be used to limit the scope of the binding, by" << endl << "requiring the current context to have a certain prefix." << endl << "Current UI context is: " << endl - << join_strings("\n", Gui::getFocusStrings(Gui::getCurViewscreen(true))) << endl; + << join_strings("\n", Gui::getCurFocus(true)) << endl; } } else if (first == "alias") @@ -2421,7 +2421,7 @@ bool Core::SelectHotkey(int sym, int modifiers) } if (!binding.focus.empty()) { bool found = false; - std::vector focusStrings = Gui::getFocusStrings(Core::getTopViewscreen()); + std::vector focusStrings = Gui::getCurFocus(true); if(Gui::matchFocusString(binding.focus)) { found = true; } diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 7cb8d9825..45ea7d84d 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1471,7 +1471,6 @@ static int gui_getMousePos(lua_State *L) static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getCurViewscreen), WRAPM(Gui, getDFViewscreen), - WRAPM(Gui, getCurFocus), WRAPM(Gui, getSelectedWorkshopJob), WRAPM(Gui, getSelectedJob), WRAPM(Gui, getSelectedUnit), @@ -1506,6 +1505,13 @@ static int gui_getFocusStrings(lua_State *state) { return 1; } +static int gui_getCurFocus(lua_State *state) { + bool skip_dismissed = lua_toboolean(state, 1); + std::vector cur_focus = Gui::getCurFocus(skip_dismissed); + Lua::PushVector(state, cur_focus); + return 1; +} + static int gui_autoDFAnnouncement(lua_State *state) { bool rv; @@ -1632,6 +1638,7 @@ static const luaL_Reg dfhack_gui_funcs[] = { { "revealInDwarfmodeMap", gui_revealInDwarfmodeMap }, { "getMousePos", gui_getMousePos }, { "getFocusStrings", gui_getFocusStrings }, + { "getCurFocus", gui_getCurFocus }, { NULL, NULL } }; diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 324a972b5..f5a4710e8 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -37,7 +37,7 @@ static bool can_invoke(const string &cmdline, df::viewscreen *screen) { } static int cleanupHotkeys(lua_State *) { - DEBUG(log).print("cleaning up old stub keybindings for:\n %s\n", join_strings("\n", Gui::getFocusStrings(Gui::getCurViewscreen(true))).c_str()); + DEBUG(log).print("cleaning up old stub keybindings for:\n %s\n", join_strings("\n", Gui::getCurFocus(true)).c_str()); std::for_each(sorted_keys.begin(), sorted_keys.end(), [](const string &sym) { string keyspec = sym + "@" + MENU_SCREEN_FOCUS_STRING; DEBUG(log).print("clearing keybinding: %s\n", keyspec.c_str()); @@ -143,7 +143,7 @@ static void list(color_ostream &out) { find_active_keybindings(Gui::getCurViewscreen(true), false); out.print("Valid keybindings for the current focus:\n %s\n", - join_strings("\n", Gui::getFocusStrings(Gui::getCurViewscreen(true))).c_str()); + join_strings("\n", Gui::getCurFocus(true)).c_str()); std::for_each(sorted_keys.begin(), sorted_keys.end(), [&](const string &sym) { out.print("%s: %s\n", sym.c_str(), current_bindings[sym].c_str()); }); From 6657fb59e55908ca9cf74f2670df5c3188939906 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 3 Feb 2023 00:24:13 -0500 Subject: [PATCH 0458/2222] Fix pausing and warnings --- library/modules/Gui.cpp | 4 ++++ plugins/confirm.cpp | 23 ++++++++--------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index d3615f17d..538331450 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -172,6 +172,8 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) case df::enums::info_interface_mode_type::JUSTICE: newFocusString += "/" + enum_item_key(game->main_interface.info.justice.current_mode); break; + default: + break; } focusStrings.push_back(newFocusString); @@ -282,6 +284,8 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) case df::enums::main_bottom_mode_type::BUILDING_PICK_MATERIALS: newFocusString += "/Building/PickMaterials"; break; + default: + break; } focusStrings.push_back(newFocusString); diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 419df5443..a55d567b8 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -73,6 +73,7 @@ public: enum cstate { INACTIVE, ACTIVE, SELECTED }; virtual string get_id() = 0; virtual string get_focus_string() = 0; + virtual bool match_prefix() = 0; virtual bool set_state(cstate) = 0; static bool set_state(string id, cstate state) @@ -275,7 +276,6 @@ class confirmation : public confirmation_base { public: typedef T screen_type; screen_type *screen; - bool tryUnpauseOnRender; bool set_state (cstate s) override { @@ -308,12 +308,7 @@ public: conf_wrapper *wrapper = confirmations[this->get_id()]; if(wrapper->is_paused()) - { - if ((input->count(df::interface_key::LEAVESCREEN) || mouseExit)) - tryUnpauseOnRender = true; - return false; - } else if (state == INACTIVE) { if(mouseExit) { @@ -378,15 +373,10 @@ public: return state == ACTIVE; } void render() { - if(tryUnpauseOnRender) { - tryUnpauseOnRender = false; - conf_wrapper *wrapper = confirmations[this->get_id()]; - std::string concernedFocus = this->get_focus_string(); - bool prefixMatch = concernedFocus.find("*") != std::string::npos; - - if(!Gui::matchFocusString(this->get_focus_string(), prefixMatch)) - wrapper->set_paused(false); - } + conf_wrapper *wrapper = confirmations[this->get_id()]; + std::string concernedFocus = this->get_focus_string(); + if(!Gui::matchFocusString(this->get_focus_string(), this->match_prefix())) + wrapper->set_paused(false); static vector lines; static const std::string pause_message = "Pause confirmations until you exit this screen"; @@ -484,6 +474,7 @@ public: } string get_id() override = 0; string get_focus_string() override = 0; + bool match_prefix() override = 0; #define CONF_LUA_START using namespace conf_lua; Lua::StackUnwinder unwind(l_state); push(screen); push(get_id()); bool intercept_key (df::interface_key key) { @@ -574,6 +565,7 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ class confirmation_##cls : public confirmation { \ virtual string get_id() { static string id = char_replace(#cls, '_', '-'); return id; } \ virtual string get_focus_string() { return focusString; } \ + virtual bool match_prefix() { return focusString[strlen(focusString) - 1] == '*'; } \ }; \ IMPLEMENT_CONFIRMATION_HOOKS(confirmation_##cls, 0); @@ -583,6 +575,7 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION TODO: document focus string stuff and how * does prefix matching + or just add a 4th param? that's probably the move */ DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest, "dwarfmode/Trade"); From d5ada27ed4a80eb118912ae70591fb7ef88c0ebb Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 3 Feb 2023 00:30:41 -0500 Subject: [PATCH 0459/2222] Remove unused variable --- plugins/hotkeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index f5a4710e8..da8506b32 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -87,7 +87,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { } valid_keys.push_back("`"); - vector focusStrings = Gui::getFocusStrings(screen); + for (int shifted = 0; shifted < 2; shifted++) { for (int alt = 0; alt < 2; alt++) { for (int ctrl = 0; ctrl < 2; ctrl++) { From 717b133d58da5da0326634dd6f7666c5980fa17b Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 3 Feb 2023 00:58:48 -0500 Subject: [PATCH 0460/2222] Move unpause detection back to feed --- plugins/confirm.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index a55d567b8..3e19d1c04 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -307,9 +307,12 @@ public: } conf_wrapper *wrapper = confirmations[this->get_id()]; - if(wrapper->is_paused()) + if(wrapper->is_paused()) { + std::string concernedFocus = this->get_focus_string(); + if(!Gui::matchFocusString(this->get_focus_string(), this->match_prefix())) + wrapper->set_paused(false); return false; - else if (state == INACTIVE) + } else if (state == INACTIVE) { if(mouseExit) { if(intercept_key("MOUSE_RIGHT") && set_state(ACTIVE)) { @@ -373,10 +376,6 @@ public: return state == ACTIVE; } void render() { - conf_wrapper *wrapper = confirmations[this->get_id()]; - std::string concernedFocus = this->get_focus_string(); - if(!Gui::matchFocusString(this->get_focus_string(), this->match_prefix())) - wrapper->set_paused(false); static vector lines; static const std::string pause_message = "Pause confirmations until you exit this screen"; From c8d672eb05713221c4a25af370753232da7043a9 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 3 Feb 2023 12:06:42 -0500 Subject: [PATCH 0461/2222] Remove duplicate SquadEquipment entry --- library/modules/Gui.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 538331450..7d56977a6 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -356,11 +356,6 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) newFocusString += "/PatrolRoutes"; focusStrings.push_back(newFocusString); } - if (game->main_interface.squad_equipment.open) { - newFocusString = baseFocus; - newFocusString += "/SquadEquipment"; - focusStrings.push_back(newFocusString); - } if (game->main_interface.squad_schedule.open) { newFocusString = baseFocus; newFocusString += "/SquadSchedule"; From 8a08878fd661a559e32fb9ad8688b2b10e8df077 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 14:08:20 -0500 Subject: [PATCH 0462/2222] Less messy pause display --- plugins/lua/confirm.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 1d4955f99..9e7917191 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -1,7 +1,5 @@ local _ENV = mkmodule('plugins.confirm') -local ui = df.global.plotinfo - local confs = {} -- Wraps df.interface_key[foo] functionality but fails with invalid keys keys = {} From c621c33b93f93e120a3d43093e12ced6042590d2 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 14:25:27 -0500 Subject: [PATCH 0463/2222] Initialize paused to false, cleanup comments --- plugins/confirm.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 3e19d1c04..d0da6ade5 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -42,9 +42,6 @@ static map confirmations; string active_id; queue cmds; -// TODO: move to confirmation instance with paused_focus etc - df::viewscreen *paused_screen = NULL; - namespace DFHack { DBG_DECLARE(confirm,status); } @@ -97,7 +94,8 @@ private: std::set hooks; public: conf_wrapper() - :enabled(false) + :enabled(false), + paused(false) {} void add_hook(VMethodInterposeLinkBase *hook) { @@ -573,8 +571,14 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ IDs (used in the "confirm enable/disable" command, by Lua, and in the docs) are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION - TODO: document focus string stuff and how * does prefix matching - or just add a 4th param? that's probably the move + The second argument to DEFINE_CONFIRMATION determines the viewscreen that any + intercepted input will be fed to. + + The third argument to DEFINE_CONFIRMATION determines the focus string that will + be used to determine if the confirmation should be unpaused. If a confirmation is paused + and the focus string is no longer found in the current focus, the confirmation will be + unpaused. Focus strings ending in "*" will use prefix matching e.g. "dwarfmode/Info*" would + match "dwarfmode/Info/Foo", "dwarfmode/Info/Bar" and so on. All matching is case insensitive. */ DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest, "dwarfmode/Trade"); @@ -663,11 +667,6 @@ DFhackCExport command_result plugin_onupdate (color_ostream &out) cmds.pop(); } - // TODO: reimplement - // if the screen that we paused on is no longer on the stack, unpause - //if (paused_focus != "" && Gui::getFocusStrings(Gui::getCurViewscreen())[0] != paused_focus) - //conf_lua::api::unpause(NULL); - return CR_OK; } From 4e57464f21902afa30d191674e153295393d5e5e Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 14:29:16 -0500 Subject: [PATCH 0464/2222] Single line debug message --- plugins/hotkeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index da8506b32..30309c6bf 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -37,7 +37,7 @@ static bool can_invoke(const string &cmdline, df::viewscreen *screen) { } static int cleanupHotkeys(lua_State *) { - DEBUG(log).print("cleaning up old stub keybindings for:\n %s\n", join_strings("\n", Gui::getCurFocus(true)).c_str()); + DEBUG(log).print("cleaning up old stub keybindings for:\n %s\n", join_strings(", ", Gui::getCurFocus(true)).c_str()); std::for_each(sorted_keys.begin(), sorted_keys.end(), [](const string &sym) { string keyspec = sym + "@" + MENU_SCREEN_FOCUS_STRING; DEBUG(log).print("clearing keybinding: %s\n", keyspec.c_str()); From edbc9300d8aebf71a9ca155dc855f0919359cac8 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 14:42:00 -0500 Subject: [PATCH 0465/2222] Remove unused function --- plugins/confirm.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index d0da6ade5..cc79421f0 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -648,17 +648,6 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) return CR_OK; } - -/* currently unused, leaving in until work is done in case it is needed again -static bool screen_found(df::viewscreen *target_screen) -{ - if (!&game->main_interface) - return false; - - return target_screen == Gui::getCurViewscreen(); -} -*/ - DFhackCExport command_result plugin_onupdate (color_ostream &out) { while (!cmds.empty()) From b5e6da35681652741ae499f87b3b0af92b000f12 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 14:44:27 -0500 Subject: [PATCH 0466/2222] Remove unused import --- plugins/confirm.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index cc79421f0..a80b7c22b 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -15,7 +15,6 @@ #include "modules/Gui.h" #include "uicommon.h" -#include "df/building_tradedepotst.h" #include "df/gamest.h" #include "df/general_ref.h" #include "df/general_ref_contained_in_itemst.h" From 89761bca0cf679acf82b020678e81ab714fcb169 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 14:54:56 -0500 Subject: [PATCH 0467/2222] Remove unnecessary screen params --- plugins/confirm.cpp | 19 +++++++++---------- plugins/lua/confirm.lua | 16 ++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index a80b7c22b..1d0521a7d 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -123,19 +123,20 @@ public: namespace trade { static bool goods_selected (std::vector &selected) { + if(!game->main_interface.trade.open) + return false; + for (uint8_t sel : selected) if (sel == 1) return true; return false; } - inline bool trader_goods_selected (df::viewscreen_dwarfmodest *screen) + inline bool trader_goods_selected () { - CHECK_NULL_POINTER(screen); return goods_selected(game->main_interface.trade.goodflag[0]); } - inline bool broker_goods_selected (df::viewscreen_dwarfmodest*screen) + inline bool broker_goods_selected () { - CHECK_NULL_POINTER(screen); return goods_selected(game->main_interface.trade.goodflag[1]); } @@ -162,15 +163,13 @@ namespace trade { } return true; } - inline bool trader_goods_all_selected(df::viewscreen_dwarfmodest*screen) + inline bool trader_goods_all_selected() { - CHECK_NULL_POINTER(screen); - return false;// goods_all_selected(screen->trader_selected, screen->trader_items); + return goods_all_selected(screen->trader_selected, screen->trader_items); } - inline bool broker_goods_all_selected(df::viewscreen_dwarfmodest*screen) + inline bool broker_goods_all_selected() { - CHECK_NULL_POINTER(screen); - return false;// goods_all_selected(screen->broker_selected, screen->broker_items); + return goods_all_selected(screen->broker_selected, screen->broker_items); }*/ } diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 9e7917191..13964645b 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -60,7 +60,7 @@ trade_cancel = defconf('trade-cancel') function trade_cancel.intercept_key(key) return dfhack.gui.matchFocusString("dwarfmode/Trade") and (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and - (trader_goods_selected(screen) or broker_goods_selected(screen)) + (trader_goods_selected() or broker_goods_selected()) end trade_cancel.title = "Cancel trade" trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved." @@ -138,13 +138,13 @@ function trade.intercept_key(key) end trade.title = "Confirm trade" function trade.get_message() - if trader_goods_selected(screen) and broker_goods_selected(screen) then + if trader_goods_selected() and broker_goods_selected() then return "Are you sure you want to trade the selected goods?" - elseif trader_goods_selected(screen) then + elseif trader_goods_selected() then return "You are not giving any items. This is likely\n" .. "to irritate the merchants.\n" .. "Attempt to trade anyway?" - elseif broker_goods_selected(screen) then + elseif broker_goods_selected() then return "You are not receiving any items. You may want to\n" .. "offer these items instead or choose items to receive.\n" .. "Attempt to trade anyway?" @@ -158,7 +158,7 @@ end trade_seize = defconf('trade-seize') function trade_seize.intercept_key(key) return screen.in_edit_count == 0 and - trader_goods_selected(screen) and + trader_goods_selected() and key == keys.TRADE_SEIZE end trade_seize.title = "Confirm seize" @@ -167,7 +167,7 @@ trade_seize.message = "Are you sure you want to seize these goods?" trade_offer = defconf('trade-offer') function trade_offer.intercept_key(key) return screen.in_edit_count == 0 and - broker_goods_selected(screen) and + broker_goods_selected() and key == keys.TRADE_OFFER end trade_offer.title = "Confirm offer" @@ -176,9 +176,9 @@ trade_offer.message = "Are you sure you want to offer these goods?\nYou will rec trade_select_all = defconf('trade-select-all') function trade_select_all.intercept_key(key) if screen.in_edit_count == 0 and key == keys.SEC_SELECT then - if screen.in_right_pane and broker_goods_selected(screen) and not broker_goods_all_selected(screen) then + if screen.in_right_pane and broker_goods_selected() and not broker_goods_all_selected() then return true - elseif not screen.in_right_pane and trader_goods_selected(screen) and not trader_goods_all_selected(screen) then + elseif not screen.in_right_pane and trader_goods_selected() and not trader_goods_all_selected() then return true end end From 2c817ec4ef641a46fdfc6f6309ff33e72c24bace Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 15:04:34 -0500 Subject: [PATCH 0468/2222] Enable confirm by default --- data/init/dfhack.tools.init | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index e2abf3dbe..50272cfa2 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -97,8 +97,8 @@ enable automelt #enable automaterial # Other interface improvement tools -#enable \ -# confirm \ +enable \ + confirm # dwarfmonitor \ # mousequery \ # autogems \ From 300692750acfb01a6c6dfd4540b89fb03225083d Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 4 Feb 2023 15:44:56 -0500 Subject: [PATCH 0469/2222] Add missing ; --- plugins/confirm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 1d0521a7d..a46a8ddac 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -30,7 +30,7 @@ using std::vector; DFHACK_PLUGIN("confirm"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(game) +REQUIRE_GLOBAL(game); REQUIRE_GLOBAL(gps); typedef std::set ikey_set; From c09690bb5cb76d2c3549f009d6ffaf8ed5faafa2 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sun, 5 Feb 2023 07:33:26 -0500 Subject: [PATCH 0470/2222] Remove newline in debug --- plugins/hotkeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 30309c6bf..788a4c02b 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -37,7 +37,7 @@ static bool can_invoke(const string &cmdline, df::viewscreen *screen) { } static int cleanupHotkeys(lua_State *) { - DEBUG(log).print("cleaning up old stub keybindings for:\n %s\n", join_strings(", ", Gui::getCurFocus(true)).c_str()); + DEBUG(log).print("cleaning up old stub keybindings for: %s\n", join_strings(", ", Gui::getCurFocus(true)).c_str()); std::for_each(sorted_keys.begin(), sorted_keys.end(), [](const string &sym) { string keyspec = sym + "@" + MENU_SCREEN_FOCUS_STRING; DEBUG(log).print("clearing keybinding: %s\n", keyspec.c_str()); From 23fa38a993a391de36a89efb81b5b1e0e37c261e Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sun, 5 Feb 2023 07:57:58 -0500 Subject: [PATCH 0471/2222] Update confirm docs --- docs/plugins/confirm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index dcf5a4554..db4acd122 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -5,7 +5,7 @@ confirm :summary: Adds confirmation dialogs for destructive actions. :tags: untested fort interface -Now you can get the chance to avoid seizing goods from traders or deleting a +Now you can get the chance disbanding a squad or deleting a hauling route in case you hit the key accidentally. Usage From 52fed5d9ad2c82482f7a97706eebc6250069165c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sun, 5 Feb 2023 19:02:01 -0500 Subject: [PATCH 0472/2222] Update options command --- plugins/confirm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index a46a8ddac..9b3f78285 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -264,7 +264,7 @@ DFHACK_PLUGIN_LUA_COMMANDS { void show_options() { - cmds.push("gui/confirm-opts"); + cmds.push("gui/confirm"); } template From a3c03a83ad802419855a55ac67a34f3fa53f32a2 Mon Sep 17 00:00:00 2001 From: Rob Goodberry Date: Sun, 5 Feb 2023 19:07:35 -0500 Subject: [PATCH 0473/2222] Update docs/plugins/confirm.rst Co-authored-by: Myk --- docs/plugins/confirm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index db4acd122..aaa586202 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -5,7 +5,7 @@ confirm :summary: Adds confirmation dialogs for destructive actions. :tags: untested fort interface -Now you can get the chance disbanding a squad or deleting a +Now you can get the chance to avoid accidentally disbanding a squad or deleting a hauling route in case you hit the key accidentally. Usage From 443fe5d8130cef19b4ee4436a682047975a2f7c5 Mon Sep 17 00:00:00 2001 From: Rob Goodberry Date: Sun, 5 Feb 2023 19:07:49 -0500 Subject: [PATCH 0474/2222] Update library/Core.cpp Co-authored-by: Myk --- library/Core.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index d56519a9c..da560e17d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2420,13 +2420,8 @@ bool Core::SelectHotkey(int sym, int modifiers) continue; } if (!binding.focus.empty()) { - bool found = false; - std::vector focusStrings = Gui::getCurFocus(true); - if(Gui::matchFocusString(binding.focus)) { - found = true; - } - - if (!found) { + if (!Gui::matchFocusString(binding.focus)) { + std::vector focusStrings = Gui::getCurFocus(true); DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", join_strings(", ", focusStrings).c_str(), binding.focus.c_str()); continue; From 526d7c1726a08dc34eabd65906dcfe6bdc6b14be Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Feb 2023 18:01:46 -0800 Subject: [PATCH 0475/2222] allow focus string generation to fall through unfocuses ZScreens --- docs/dev/Lua API.rst | 5 +++-- library/include/modules/Gui.h | 2 +- library/include/modules/Screen.h | 3 +++ library/modules/Gui.cpp | 31 +++++++++++++++++++++++++------ library/modules/Screen.cpp | 4 ++++ 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 242e84037..5a1b562ee 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -974,10 +974,11 @@ Screens the specified type (e.g. ``df.viewscreen_titlest``), or ``nil`` if none match. If ``depth`` is not specified or is less than 1, all viewscreens are checked. -* ``dfhack.gui.getDFViewscreen([skip_dismissed])`` +* ``dfhack.gui.getDFViewscreen([skip_dismissed[, viewscreen]])`` Returns the topmost viewscreen not owned by DFHack. If ``skip_dismissed`` is - ``true``, ignores screens already marked to be removed. + ``true``, ignores screens already marked to be removed. If ``viewscreen`` is + specified, starts the scan at the given viewscreen. General-purpose selections ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index cfb8925e3..aeb763e4a 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -189,7 +189,7 @@ namespace DFHack DFHACK_EXPORT df::viewscreen *getViewscreenByIdentity(virtual_identity &id, int n = 1); /// Get the top-most underlying DF viewscreen (not owned by DFHack) - DFHACK_EXPORT df::viewscreen *getDFViewscreen(bool skip_dismissed = false); + DFHACK_EXPORT df::viewscreen *getDFViewscreen(bool skip_dismissed = false, df::viewscreen *top = NULL); /// Get the top-most viewscreen of the given type from the top `n` viewscreens (or all viewscreens if n < 1) /// returns NULL if none match diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 0f0afd6e2..c21e3ad32 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -351,6 +351,7 @@ namespace DFHack virtual bool is_lua_screen() { return false; } + virtual bool isFocused() { return true; } virtual std::string getFocusString() = 0; virtual void onShow() {}; virtual void onDismiss() {}; @@ -365,6 +366,7 @@ namespace DFHack class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen { std::string focus; + bool defocused = false; void update_focus(lua_State *L, int idx); @@ -384,6 +386,7 @@ namespace DFHack static df::viewscreen *get_pointer(lua_State *L, int idx, bool make); virtual bool is_lua_screen() { return true; } + virtual bool isFocused() { return !defocused; } virtual std::string getFocusString() { return focus; } virtual void render(); diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 7d56977a6..1008b0972 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -484,6 +484,12 @@ bool Gui::matchFocusString(std::string focusString, bool prefixMatch) { }) != currentFocusStrings.end(); } +static void push_dfhack_focus_string(dfhack_viewscreen *vs, std::vector &focusStrings) +{ + auto name = vs->getFocusString(); + focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); +} + std::vector Gui::getFocusStrings(df::viewscreen* top) { std::vector focusStrings; @@ -493,10 +499,21 @@ std::vector Gui::getFocusStrings(df::viewscreen* top) if (dfhack_viewscreen::is_instance(top)) { - auto name = static_cast(top)->getFocusString(); - focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); + dfhack_viewscreen *vs = static_cast(top); + if (vs->isFocused()) + { + push_dfhack_focus_string(vs, focusStrings); + return focusStrings; + } + top = Gui::getDFViewscreen(top); + if (dfhack_viewscreen::is_instance(top)) + { + push_dfhack_focus_string(static_cast(top), focusStrings); + return focusStrings; + } } - else if (virtual_identity *id = virtual_identity::get(top)) + + if (virtual_identity *id = virtual_identity::get(top)) { std::string name = getNameChunk(id, 11, 2); @@ -504,7 +521,8 @@ std::vector Gui::getFocusStrings(df::viewscreen* top) if (handler) handler(name, focusStrings, top); } - else + + if (!focusStrings.size()) { Core &core = Core::getInstance(); std::string name = core.p->readClassName(*(void**)top); @@ -1865,8 +1883,9 @@ df::viewscreen *Gui::getViewscreenByIdentity (virtual_identity &id, int n) return NULL; } -df::viewscreen *Gui::getDFViewscreen(bool skip_dismissed) { - df::viewscreen *screen = Gui::getCurViewscreen(skip_dismissed); +df::viewscreen *Gui::getDFViewscreen(bool skip_dismissed, df::viewscreen *screen) { + if (!screen) + screen = Gui::getCurViewscreen(skip_dismissed); while (screen && dfhack_viewscreen::is_instance(screen)) { screen = screen->parent; if (skip_dismissed) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 13dbcb204..a78a36a3f 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -865,6 +865,9 @@ void dfhack_lua_viewscreen::update_focus(lua_State *L, int idx) lua_getfield(L, idx, "allow_options"); allow_options = lua_toboolean(L, -1); lua_pop(L, 1); + lua_getfield(L, idx, "defocused"); + defocused = lua_toboolean(L, -1); + lua_pop(L, 1); lua_getfield(L, idx, "focus_path"); auto str = lua_tostring(L, -1); @@ -1081,6 +1084,7 @@ using df::identity_traits; #define CUR_STRUCT dfhack_viewscreen static const struct_field_info dfhack_viewscreen_fields[] = { { METHOD(OBJ_METHOD, is_lua_screen), 0, 0 }, + { METHOD(OBJ_METHOD, isFocused), 0, 0 }, { METHOD(OBJ_METHOD, getFocusString), 0, 0 }, { METHOD(OBJ_METHOD, onShow), 0, 0 }, { METHOD(OBJ_METHOD, onDismiss), 0, 0 }, From 09e7c142102a25ff7f40270bcf3229eef419e4a2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Feb 2023 18:27:31 -0800 Subject: [PATCH 0476/2222] hide DFHack logo when it's in the way --- docs/changelog.txt | 1 + plugins/lua/hotkeys.lua | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ea140f451..584f84d1a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `automelt`: is now more resistent to savegame corruption +- `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) ## Documentation diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 163091318..09b1cea75 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -14,7 +14,22 @@ HotspotMenuWidget.ATTRS{ default_pos={x=2,y=2}, default_enabled=true, hotspot=true, - viewscreens='all', + viewscreens={ + -- 'choose_start_site', -- conflicts with vanilla panel layouts + 'choose_game_type', + 'dwarfmode', + 'export_region', + 'game_cleaner', + 'initial_prep', + 'legends', + 'loadgame', + -- 'new_region', -- conflicts with vanilla panel layouts + 'savegame', + 'setupdwarfgame', + 'title', + 'update_region', + 'world' + }, overlay_onupdate_max_freq_seconds=0, frame={w=4, h=3} } From d6c4d4417e99ac16106b83dc76de6f4175bf975e Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 5 Feb 2023 21:11:07 -0800 Subject: [PATCH 0477/2222] Add dig to the build Ref: #2743 --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9ce25a4cc..3389303fd 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -103,7 +103,7 @@ dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) #dfhack_plugin(deramp deramp.cpp) dfhack_plugin(debug debug.cpp LINK_LIBRARIES jsoncpp_static) -#dfhack_plugin(dig dig.cpp) +dfhack_plugin(dig dig.cpp) dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) #dfhack_plugin(digFlood digFlood.cpp) #add_subdirectory(diggingInvaders) From 032b62dcbece4fa7bd09986de95d3b7b687ee642 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 6 Feb 2023 07:14:56 +0000 Subject: [PATCH 0478/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index dc11839b6..88e7eb472 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit dc11839b673019e9dac0e63de0b05dedd3aea786 +Subproject commit 88e7eb47291c25623bd8ef97d726fbfab3bd66d2 From 2ddd23e45ddda1baa21fe7c257054ffb60b13942 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 00:57:33 -0800 Subject: [PATCH 0479/2222] prevent rclick from bleeding through (again) --- docs/changelog.txt | 1 + library/lua/gui.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ea140f451..3ff4233b3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - ``Units::isFortControlled``: Account for agitated wildlife +- Fix right click sometimes closing both a DFHack window and a vanilla panel ## Misc Improvements - `automelt`: is now more resistent to savegame corruption diff --git a/library/lua/gui.lua b/library/lua/gui.lua index a5489c5a6..c571c2389 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -789,6 +789,7 @@ function ZScreen:onInput(keys) end if keys._MOUSE_R_DOWN then df.global.enabler.mouse_rbut_down = 0 + df.global.enabler.mouse_rbut = 0 end return end From 9e318842a429a6a85392fe146cc911a505eee9a6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 00:58:57 -0800 Subject: [PATCH 0480/2222] implement some Gui module hotkey guards and clean up focus string matching logic --- docs/dev/Lua API.rst | 7 +- library/include/modules/Gui.h | 2 +- library/modules/Gui.cpp | 204 ++++++++-------------------------- plugins/confirm.cpp | 7 +- 4 files changed, 56 insertions(+), 164 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 5a1b562ee..8dc448660 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -958,17 +958,18 @@ Screens [1] = "dwarfmode/Info/CREATURES/CITIZEN" [2] = "dwardmode/Squads" -* ``dfhack.gui.matchFocusString(focus_string)`` +* ``dfhack.gui.matchFocusString(focus_string[, viewscreen])`` Returns ``true`` if the given ``focus_string`` is found in the current focus strings, or as a prefix to any of the focus strings, or ``false`` - if no match is found. Matching is case insensitive. + if no match is found. Matching is case insensitive. If ``viewscreen`` is + specified, gets the focus strings to match from the given viewscreen. * ``dfhack.gui.getCurFocus([skip_dismissed])`` Returns the focus string of the current viewscreen. -* ``dfhack.gui.getViewscreenByType(type [, depth])`` +* ``dfhack.gui.getViewscreenByType(type[, depth])`` Returns the topmost viewscreen out of the top ``depth`` viewscreens with the specified type (e.g. ``df.viewscreen_titlest``), or ``nil`` if none match. diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index aeb763e4a..b579dcc97 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -67,7 +67,7 @@ namespace DFHack namespace Gui { DFHACK_EXPORT std::vector getFocusStrings(df::viewscreen *top); - DFHACK_EXPORT bool matchFocusString(std::string focusString, bool prefixMatch = true); + DFHACK_EXPORT bool matchFocusString(std::string focus_string, df::viewscreen *top = NULL); // Full-screen item details view diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 1008b0972..56f8bc301 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -57,6 +57,7 @@ using namespace DFHack; #include "df/building_trapst.h" #include "df/building_type.h" #include "df/building_workshopst.h" +#include "df/cri_unitst.h" #include "df/d_init.h" #include "df/game_mode.h" #include "df/general_ref.h" @@ -459,7 +460,7 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) } if (!newFocusString.size()) { - focusStrings.push_back(baseFocus); + focusStrings.push_back(baseFocus + "/Default"); } } @@ -475,12 +476,14 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) } */ -bool Gui::matchFocusString(std::string focusString, bool prefixMatch) { - focusString = toLower(focusString); - std::vector currentFocusStrings = getFocusStrings(getCurViewscreen(true)); +bool Gui::matchFocusString(std::string focus_string, df::viewscreen *top) { + focus_string = toLower(focus_string); + if (!top) + top = getCurViewscreen(true); + std::vector currentFocusStrings = getFocusStrings(top); - return std::find_if(currentFocusStrings.begin(), currentFocusStrings.end(), [&focusString, &prefixMatch](std::string item) { - return prefixMatch ? prefix_matches(focusString, toLower(item)) : focusString == toLower(item); + return std::find_if(currentFocusStrings.begin(), currentFocusStrings.end(), [&focus_string](std::string item) { + return prefix_matches(focus_string, toLower(item)); }) != currentFocusStrings.end(); } @@ -535,17 +538,7 @@ std::vector Gui::getFocusStrings(df::viewscreen* top) bool Gui::default_hotkey(df::viewscreen *top) { - // Default hotkey guard function - for (;top ;top = top->parent) - { - if (strict_virtual_cast(top)) - return true; -/* TODO: understand how this changes for v50 - if (strict_virtual_cast(top)) - return true; -*/ - } - return false; + return World::isFortressMode() || World::isAdventureMode(); } bool Gui::anywhere_hotkey(df::viewscreen *) { @@ -553,24 +546,7 @@ bool Gui::anywhere_hotkey(df::viewscreen *) { } bool Gui::dwarfmode_hotkey(df::viewscreen *top) { - return World::isFortressMode(); -} - -bool Gui::unitjobs_hotkey(df::viewscreen *top) -{ -/* TODO: understand how this changes for v50 - // Require the unit or jobs list - return !!strict_virtual_cast(top) || - !!strict_virtual_cast(top); -*/ return false; -} - -bool Gui::item_details_hotkey(df::viewscreen *top) -{ -/* TODO: understand how this changes for v50 - // Require the main dwarf mode screen - return !!strict_virtual_cast(top); -*/ return false; + return matchFocusString("dwarfmode", top); } static bool has_cursor() @@ -595,164 +571,82 @@ bool Gui::workshop_job_hotkey(df::viewscreen *top) if (!dwarfmode_hotkey(top)) return false; -/* TODO: understand how this changes for v50 - using namespace ui_sidebar_mode; - using df::global::ui_workshop_in_add; - using df::global::ui_workshop_job_cursor; - - switch (plotinfo->main.mode) { - case QueryBuilding: - { - if (!ui_workshop_job_cursor) // allow missing - return false; - - df::building *selected = world->selected_building; - if (!virtual_cast(selected) && - !virtual_cast(selected)) - return false; - - // No jobs? - if (selected->jobs.empty() || - selected->jobs[0]->job_type == job_type::DestroyBuilding) - return false; - - // Add job gui activated? - if (ui_workshop_in_add && *ui_workshop_in_add) - return false; + df::building *selected = getAnyBuilding(top); + if (!virtual_cast(selected) && + !virtual_cast(selected)) + return false; - return true; - }; - default: + if (selected->jobs.empty() || + selected->jobs[0]->job_type == job_type::DestroyBuilding) return false; - } -*/ return false; + + return true; } bool Gui::build_selector_hotkey(df::viewscreen *top) { + using df::global::buildreq; + if (!dwarfmode_hotkey(top)) return false; -/* TODO: understand how this changes for v50 - using namespace ui_sidebar_mode; - using df::global::ui_build_selector; - - switch (plotinfo->main.mode) { - case Build: - { - if (!ui_build_selector) // allow missing - return false; - - // Not selecting, or no choices? - if (ui_build_selector->building_type < 0 || - ui_build_selector->stage != 2 || - ui_build_selector->choices.empty()) - return false; - - return true; - }; - default: + if (buildreq->building_type < 0 || + buildreq->stage != 2 || + buildreq->choices.empty()) return false; - } -*/ return false; + + return true; } bool Gui::view_unit_hotkey(df::viewscreen *top) { if (!dwarfmode_hotkey(top)) return false; -/* TODO: understand how this changes for v50 - using df::global::ui_selected_unit; - - if (plotinfo->main.mode != ui_sidebar_mode::ViewUnits) - return false; - if (!ui_selected_unit) // allow missing - return false; - return vector_get(world->units.active, *ui_selected_unit) != NULL; -*/ return false; + return !!getAnyUnit(top); } -bool Gui::unit_inventory_hotkey(df::viewscreen *top) +bool Gui::any_job_hotkey(df::viewscreen *top) { - using df::global::ui_unit_view_mode; - - if (!view_unit_hotkey(top)) - return false; - if (!ui_unit_view_mode) - return false; - - return ui_unit_view_mode->value == df::ui_unit_view_mode::Inventory; + return matchFocusString("dwarfmode/Info/JOBS", top) + || matchFocusString("dwarfmode/Info/CREATURES/CITIZEN", top) + || workshop_job_hotkey(top); } df::job *Gui::getSelectedWorkshopJob(color_ostream &out, bool quiet) { - using df::global::ui_workshop_job_cursor; - - if (!workshop_job_hotkey(Core::getTopViewscreen())) { - if (!quiet) - out.printerr("Not in a workshop, or no job is highlighted.\n"); - return NULL; - } - - df::building *selected = world->selected_building; - int idx = *ui_workshop_job_cursor; - - if (size_t(idx) >= selected->jobs.size()) - { - out.printerr("Invalid job cursor index: %d\n", idx); + auto bld = getSelectedBuilding(out, true); + if (!bld) return NULL; - } - - return selected->jobs[idx]; -} - -bool Gui::any_job_hotkey(df::viewscreen *top) -{ -/* TODO: understand how this changes for v50 - if (VIRTUAL_CAST_VAR(screen, df::viewscreen_joblistst, top)) - return vector_get(screen->jobs, screen->cursor_pos) != NULL; - if (VIRTUAL_CAST_VAR(screen, df::viewscreen_unitlistst, top)) - return vector_get(screen->jobs[screen->page], screen->cursor_pos[screen->page]) != NULL; - - return workshop_job_hotkey(top); -*/ return false; + // no way to select a specific job; just get the first one + return bld->jobs.size() ? bld->jobs[0] : NULL; } df::job *Gui::getSelectedJob(color_ostream &out, bool quiet) { -/* TODO: understand how this changes for v50 - df::viewscreen *top = Core::getTopViewscreen(); - - if (VIRTUAL_CAST_VAR(screen, df::viewscreen_jobst, top)) - { - return screen->job; - } - if (VIRTUAL_CAST_VAR(joblist, df::viewscreen_joblistst, top)) - { - df::job *job = vector_get(joblist->jobs, joblist->cursor_pos); + using df::global::game; - if (!job && !quiet) - out.printerr("Selected unit has no job\n"); + auto top = Core::getTopViewscreen(); + if (auto dfscreen = dfhack_viewscreen::try_cast(top)) + return dfscreen->getSelectedJob(); - return job; + if (matchFocusString("dwarfmode/Info/JOBS")) { + auto &cri_job = game->main_interface.info.jobs.cri_job; + // no way to select specific jobs; just get the first one + return cri_job.size() ? cri_job[0]->jb : NULL; } - else if (VIRTUAL_CAST_VAR(unitlist, df::viewscreen_unitlistst, top)) - { - int page = unitlist->page; - df::job *job = vector_get(unitlist->jobs[page], unitlist->cursor_pos[page]); + + if (auto unit = getAnyUnit(top)) { + df::job *job = unit->job.current_job; if (!job && !quiet) out.printerr("Selected unit has no job\n"); return job; } - else if (auto dfscreen = dfhack_viewscreen::try_cast(top)) - return dfscreen->getSelectedJob(); - else - return getSelectedWorkshopJob(out, quiet); -*/ return getSelectedWorkshopJob(out, quiet); + + return getSelectedWorkshopJob(out, quiet); } df::unit *Gui::getAnyUnit(df::viewscreen *top) @@ -767,7 +661,7 @@ df::unit *Gui::getAnyUnit(df::viewscreen *top) return df::unit::find(game->main_interface.view_sheets.active_id); /* TODO: understand how this changes for v50 - using namespace ui_sidebar_mode; + using namespace ui_sidebar_mode; using df::global::ui_look_cursor; using df::global::ui_look_list; using df::global::ui_selected_unit; diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 9b3f78285..aad1c17bc 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -69,7 +69,6 @@ public: enum cstate { INACTIVE, ACTIVE, SELECTED }; virtual string get_id() = 0; virtual string get_focus_string() = 0; - virtual bool match_prefix() = 0; virtual bool set_state(cstate) = 0; static bool set_state(string id, cstate state) @@ -305,7 +304,7 @@ public: conf_wrapper *wrapper = confirmations[this->get_id()]; if(wrapper->is_paused()) { std::string concernedFocus = this->get_focus_string(); - if(!Gui::matchFocusString(this->get_focus_string(), this->match_prefix())) + if(!Gui::matchFocusString(this->get_focus_string())) wrapper->set_paused(false); return false; } else if (state == INACTIVE) @@ -469,7 +468,6 @@ public: } string get_id() override = 0; string get_focus_string() override = 0; - bool match_prefix() override = 0; #define CONF_LUA_START using namespace conf_lua; Lua::StackUnwinder unwind(l_state); push(screen); push(get_id()); bool intercept_key (df::interface_key key) { @@ -560,7 +558,6 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ class confirmation_##cls : public confirmation { \ virtual string get_id() { static string id = char_replace(#cls, '_', '-'); return id; } \ virtual string get_focus_string() { return focusString; } \ - virtual bool match_prefix() { return focusString[strlen(focusString) - 1] == '*'; } \ }; \ IMPLEMENT_CONFIRMATION_HOOKS(confirmation_##cls, 0); @@ -585,7 +582,7 @@ DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest, "dwarfmode/Hau DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest, "dwarfmode/ViewSheets/BUILDING"); DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest, "dwarfmode/Squads"); DEFINE_CONFIRMATION(order_remove, viewscreen_dwarfmodest, "dwarfmode/Info/WORK_ORDERS"); -DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest, "dwarfmode/Zone*"); +DEFINE_CONFIRMATION(zone_remove, viewscreen_dwarfmodest, "dwarfmode/Zone"); DEFINE_CONFIRMATION(burrow_remove, viewscreen_dwarfmodest, "dwarfmode/Burrow"); DEFINE_CONFIRMATION(stockpile_remove, viewscreen_dwarfmodest, "dwarfmode/Some/Stockpile"); From 5747e9f3f083cea51687b80098e572cdfa3e29b9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 01:23:52 -0800 Subject: [PATCH 0481/2222] set up some keybindings that are ready to go and reinstate autodump-destroy-item --- data/init/dfhack.keybindings.init | 19 ++++++------------- docs/changelog.txt | 4 ++++ plugins/autodump.cpp | 6 ++---- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index 5847a42c7..3a94aea2a 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -25,9 +25,6 @@ keybinding add Ctrl-Shift-K gui/cp437-table # customizable quick command list keybinding add Ctrl-Shift-A gui/quickcmd -# an in-game init file editor -#keybinding add Alt-S@title|dwarfmode/Default|dungeonmode gui/settings-manager - ###################### # dwarfmode bindings # @@ -36,25 +33,21 @@ keybinding add Ctrl-Shift-A gui/quickcmd # quicksave keybinding add Ctrl-Alt-S@dwarfmode quicksave -# toggle the display of water level as 1-7 tiles -#keybinding add Ctrl-W@dwarfmode|dungeonmode twaterlvl - # designate the whole vein for digging -#keybinding add Ctrl-V@dwarfmode digv -#keybinding add Ctrl-Shift-V@dwarfmode "digv x" +keybinding add Ctrl-V@dwarfmode digv +keybinding add Ctrl-Shift-V@dwarfmode "digv x" # clean the selected tile of blood etc -#keybinding add Ctrl-C spotclean +keybinding add Ctrl-C spotclean # destroy the selected item -#keybinding add Ctrl-K@dwarfmode autodump-destroy-item +keybinding add Ctrl-K@dwarfmode autodump-destroy-item # destroy items designated for dump in the selected tile -#keybinding add Ctrl-Shift-K@dwarfmode autodump-destroy-here +keybinding add Ctrl-H@dwarfmode autodump-destroy-here -# apply blueprints to the map (Alt-F for compatibility with LNP Quickfort) +# apply blueprints to the map keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort -#keybinding add Alt-F@dwarfmode gui/quickfort # show information collected by dwarfmonitor #keybinding add Alt-M@dwarfmode/Default "dwarfmonitor prefs" diff --git a/docs/changelog.txt b/docs/changelog.txt index ea140f451..29fc72cf0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `automelt`: is now more resistent to savegame corruption +- `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K +- `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H +- `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) +- `clean`: new hotkey for `spotclean`: Ctrl-C ## Documentation diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index aed6fa0ea..61e83d63e 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -287,13 +287,11 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector & parame return CR_WRONG_USAGE; } - //DFHack::VersionInfo *mem = Core::getInstance().vinfo; if (!Maps::IsValid()) { out.printerr("Map is not available!\n"); @@ -461,10 +458,11 @@ static int last_frame = 0; command_result df_autodump_destroy_item(color_ostream &out, vector & parameters) { - // HOTKEY COMMAND; CORE ALREADY SUSPENDED if (!parameters.empty()) return CR_WRONG_USAGE; + CoreSuspender suspend; + df::item *item = Gui::getSelectedItem(out); if (!item) return CR_FAILURE; From bf91ffb1fe3081a8ff32cebc43627f990a8970c6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 02:37:32 -0800 Subject: [PATCH 0482/2222] support focus paths for overlay widgets and migrate existing widgets to use them --- docs/changelog.txt | 1 + docs/dev/overlay-dev-guide.rst | 6 +++- plugins/lua/autolabor.lua | 11 ++----- plugins/lua/orders.lua | 17 +--------- plugins/lua/overlay.lua | 59 +++++++++++++++++++++++++++------- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ea140f451..d52212f1b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- `overlay`: overlay widgets can now specify focus paths for the viewscreens they attach to so they only appear in specific contexts. see `overlay-dev-guide` for details. ## Removed diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index c7e094299..d31ce246d 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -109,7 +109,11 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes: ``dwarfmode`` and the adventure mode map viewscreen would be ``dungeonmode``. If there is only one viewscreen that this widget is associated with, it can be specified as a string instead of a list of - strings with a single element. + strings with a single element. If you only want your widget to appear in + certain contexts, you can specify a focus path, in the same syntax as the + `keybinding` command. For example, ``dwarfmode/Info/CREATURES/CITIZEN`` will + ensure the overlay widget is only displayed when the "Citizens" subtab under + the "Units" panel is active. - ``hotspot`` (default: ``false``) If set to ``true``, your widget's ``overlay_onupdate`` function will be called whenever the `overlay` plugin's ``plugin_onupdate()`` function is diff --git a/plugins/lua/autolabor.lua b/plugins/lua/autolabor.lua index b91227650..6ef4d7279 100644 --- a/plugins/lua/autolabor.lua +++ b/plugins/lua/autolabor.lua @@ -4,16 +4,11 @@ local gui = require('gui') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') -local function is_labor_panel_visible() - local info = df.global.game.main_interface.info - return info.open and info.current_mode == df.info_interface_mode_type.LABOR -end - AutolaborOverlay = defclass(AutolaborOverlay, overlay.OverlayWidget) AutolaborOverlay.ATTRS{ default_pos={x=7,y=-13}, default_enabled=true, - viewscreens='dwarfmode', + viewscreens='dwarfmode/Info/LABOR', frame={w=29, h=5}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, @@ -34,9 +29,7 @@ function AutolaborOverlay:init() end function AutolaborOverlay:render(dc) - if not is_labor_panel_visible() or not isEnabled() then - return false - end + if not isEnabled() then return false end AutolaborOverlay.super.render(self, dc) end diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index 972edcfea..72bb6185e 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -9,11 +9,6 @@ local widgets = require('gui.widgets') -- OrdersOverlay -- -local function is_orders_panel_visible() - local info = df.global.game.main_interface.info - return info.open and info.current_mode == df.info_interface_mode_type.WORK_ORDERS -end - local function do_sort() dfhack.run_command('orders', 'sort') end @@ -49,7 +44,7 @@ OrdersOverlay = defclass(OrdersOverlay, overlay.OverlayWidget) OrdersOverlay.ATTRS{ default_pos={x=53,y=-6}, default_enabled=true, - viewscreens='dwarfmode', + viewscreens='dwarfmode/Info/WORK_ORDERS', frame={w=30, h=4}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, @@ -84,16 +79,6 @@ function OrdersOverlay:init() } end -function OrdersOverlay:render(dc) - if not is_orders_panel_visible() then return false end - OrdersOverlay.super.render(self, dc) -end - -function OrdersOverlay:onInput(keys) - if not is_orders_panel_visible() then return false end - OrdersOverlay.super.onInput(self, keys) -end - OVERLAY_WIDGETS = { overlay=OrdersOverlay, } diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index bf944d7e9..c3522c204 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -81,19 +81,18 @@ function normalize_list(element_or_list) return {element_or_list} end --- normalize "short form" viewscreen names to "long form" +-- normalize "short form" viewscreen names to "long form" and remove any focus local function normalize_viewscreen_name(vs_name) - if vs_name == 'all' or vs_name:match('viewscreen_.*st') then - return vs_name + if vs_name == 'all' or vs_name:match('^viewscreen_.*st') then + return vs_name:match('^[^/]+') end - return 'viewscreen_' .. vs_name .. 'st' + return 'viewscreen_' .. vs_name:match('^[^/]+') .. 'st' end --- reduce "long form" viewscreen names to "short form" +-- reduce "long form" viewscreen names to "short form"; keep focus function simplify_viewscreen_name(vs_name) - _,_,short_name = vs_name:find('^viewscreen_(.*)st$') - if short_name then return short_name end - return vs_name + local short_name = vs_name:match('^viewscreen_([^/]+)st') + return short_name or vs_name end local function is_empty(tbl) @@ -241,10 +240,23 @@ local function do_list(args) end end +local function get_focus_strings(viewscreens) + local focus_strings = nil + for _,vs in ipairs(viewscreens) do + if vs:match('/') then + focus_strings = focus_strings or {} + vs = simplify_viewscreen_name(vs) + table.insert(focus_strings, vs) + end + end + return focus_strings +end + local function load_widget(name, widget_class) local widget = widget_class{name=name} widget_db[name] = { widget=widget, + focus_strings=get_focus_strings(normalize_list(widget.viewscreens)), next_update_ms=widget.overlay_onupdate and 0 or math.huge, } if not overlay_config[name] then overlay_config[name] = {} end @@ -426,12 +438,30 @@ function update_hotspot_widgets() end end +local function matches_focus_strings(db_entry, vs_name) + if not db_entry.focus_strings then return true end + local matched = true + local simple_vs_name = simplify_viewscreen_name(vs_name) + for _,fs in ipairs(db_entry.focus_strings) do + if fs:startswith(simple_vs_name) then + matched = false + if dfhack.gui.matchFocusString(fs, vs) then + return true + end + end + end + return matched +end + local function _update_viewscreen_widgets(vs_name, vs, now_ms) local vs_widgets = active_viewscreen_widgets[vs_name] if not vs_widgets then return end now_ms = now_ms or dfhack.getTickCount() for name,db_entry in pairs(vs_widgets) do - if do_update(name, db_entry, now_ms, vs) then return end + if matches_focus_strings(db_entry, vs_name) and + do_update(name, db_entry, now_ms, vs) then + return + end end return now_ms end @@ -439,7 +469,9 @@ end function update_viewscreen_widgets(vs_name, vs) if triggered_screen_has_lock() then return end local now_ms = _update_viewscreen_widgets(vs_name, vs, nil) - _update_viewscreen_widgets('all', vs, now_ms) + if now_ms then + _update_viewscreen_widgets('all', vs, now_ms) + end end local function _feed_viewscreen_widgets(vs_name, keys) @@ -447,7 +479,8 @@ local function _feed_viewscreen_widgets(vs_name, keys) if not vs_widgets then return false end for _,db_entry in pairs(vs_widgets) do local w = db_entry.widget - if detect_frame_change(w, function() return w:onInput(keys) end) then + if matches_focus_strings(db_entry, vs_name) and + detect_frame_change(w, function() return w:onInput(keys) end) then return true end end @@ -465,7 +498,9 @@ local function _render_viewscreen_widgets(vs_name, dc) dc = dc or gui.Painter.new() for _,db_entry in pairs(vs_widgets) do local w = db_entry.widget - detect_frame_change(w, function() w:render(dc) end) + if matches_focus_strings(db_entry, vs_name) then + detect_frame_change(w, function() w:render(dc) end) + end end end From 397a64c4b599236f20c27fb58489fdc4748378df Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 02:58:11 -0800 Subject: [PATCH 0483/2222] don't print out how many items there are in the world --- plugins/cleanowned.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/cleanowned.cpp b/plugins/cleanowned.cpp index 86ef0a2e1..3fece8bfe 100644 --- a/plugins/cleanowned.cpp +++ b/plugins/cleanowned.cpp @@ -74,8 +74,6 @@ command_result df_cleanowned (color_ostream &out, vector & parameters) return CR_FAILURE; } - out.print("Found total %zd items.\n", world->items.all.size()); - for (std::size_t i=0; i < world->items.all.size(); i++) { df::item * item = world->items.all[i]; From e285ee31a406b4aba936615d4788e6b9f129980e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 03:28:19 -0800 Subject: [PATCH 0484/2222] tweak defaults, load initial races immediately --- docs/changelog.txt | 2 ++ docs/plugins/autobutcher.rst | 7 +++---- plugins/autobutcher.cpp | 12 +++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ea140f451..626191f9f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `automelt`: is now more resistent to savegame corruption +- `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair +- `autobutcher`: now immediately loads races available at game start into the watchlist ## Documentation diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index 1a4aa4f91..7e5b6b024 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -18,8 +18,8 @@ watch list. Units will be ignored if they are: Creatures who will not reproduce (because they're not interested in the opposite sex or have been gelded) will be butchered before those who will. Older adults and younger children will be butchered first if the population -is above the target (defaults are: 1 male kid, 5 female kids, 1 male adult, -5 female adults). Note that you may need to set a target above 1 to have a +is above the target (defaults are: 2 male kids, 4 female kids, 2 male adults, +4 female adults). Note that you may need to set a target above 1 to have a reliable breeding population due to asexuality etc. See `fix-ster` if this is a problem. @@ -34,7 +34,7 @@ Usage ``autobutcher autowatch`` Automatically add all new races (animals you buy from merchants, tame yourself, or get from migrants) to the watch list using the default target - counts. + counts. This option is enabled by default. ``autobutcher noautowatch`` Stop auto-adding new races to the watch list. ``autobutcher target all|new| [ ...]`` @@ -108,4 +108,3 @@ fortress:: autobutcher target 2 2 4 2 ALPACA SHEEP LLAMA autobutcher target 5 5 6 2 PIG autobutcher target 0 0 0 0 new - autobutcher autowatch diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index 3e76796cb..28667bbe7 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -107,6 +107,8 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { DEBUG(status,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(CONFIG_IS_ENABLED, is_enabled); + if (enable) + autobutcher_cycle(out); } else { DEBUG(status,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", @@ -130,11 +132,11 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { config = World::AddPersistentData(CONFIG_KEY); set_config_bool(CONFIG_IS_ENABLED, is_enabled); set_config_val(CONFIG_CYCLE_TICKS, 6000); - set_config_bool(CONFIG_AUTOWATCH, false); - set_config_val(CONFIG_DEFAULT_FK, 5); - set_config_val(CONFIG_DEFAULT_MK, 1); - set_config_val(CONFIG_DEFAULT_FA, 5); - set_config_val(CONFIG_DEFAULT_MA, 1); + set_config_bool(CONFIG_AUTOWATCH, true); + set_config_val(CONFIG_DEFAULT_FK, 4); + set_config_val(CONFIG_DEFAULT_MK, 2); + set_config_val(CONFIG_DEFAULT_FA, 4); + set_config_val(CONFIG_DEFAULT_MA, 2); } // we have to copy our enabled flag into the global plugin variable, but From 6dbf7b83bd51a9f0f5ed23f86d04dea507c2d6c3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 04:02:18 -0800 Subject: [PATCH 0485/2222] update auto plugin example, use new best practices --- plugins/autochop.cpp | 2 + plugins/autoslab.cpp | 2 + .../examples/persistent_per_save_example.cpp | 72 ++++++++++++++----- plugins/nestboxes.cpp | 2 + plugins/seedwatch.cpp | 23 +++--- 5 files changed, 74 insertions(+), 27 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 5b1cdf97e..bf7540e29 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -143,6 +143,8 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { DEBUG(status,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out, true); } else { DEBUG(status,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 9b0848470..e17ce72c8 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -90,6 +90,8 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) is_enabled = enable; DEBUG(status, out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); } else { diff --git a/plugins/examples/persistent_per_save_example.cpp b/plugins/examples/persistent_per_save_example.cpp index a8421949b..f066be6ef 100644 --- a/plugins/examples/persistent_per_save_example.cpp +++ b/plugins/examples/persistent_per_save_example.cpp @@ -6,6 +6,7 @@ // savegame that had this plugin enabled is loaded. #include +#include #include #include "df/world.h" @@ -18,6 +19,7 @@ #include "modules/World.h" using std::string; +using std::unordered_map; using std::vector; using namespace DFHack; @@ -38,25 +40,50 @@ namespace DFHack { } static const string CONFIG_KEY = string(plugin_name) + "/config"; +static const string ELEM_CONFIG_KEY_PREFIX = string(plugin_name) + "/elem/"; static PersistentDataItem config; +static unordered_map elems; + enum ConfigValues { CONFIG_IS_ENABLED = 0, CONFIG_SOMETHING_ELSE = 1, }; -static int get_config_val(int index) { - if (!config.isValid()) + +enum ElemConfigValues { + ELEM_CONFIG_ID = 0, + ELEM_CONFIG_SOMETHING_ELSE = 1, +}; + +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) return -1; - return config.ival(index); + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; } -static bool get_config_bool(int index) { - return get_config_val(index) == 1; +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; } -static void set_config_val(int index, int value) { - if (config.isValid()) - config.ival(index) = value; +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); } -static void set_config_bool(int index, bool value) { - set_config_val(index, value ? 1 : 0); + +static PersistentDataItem & ensure_elem_config(color_ostream &out, int id) { + if (elems.count(id)) + return elems[id]; + string keyname = ELEM_CONFIG_KEY_PREFIX + int_to_string(id); + DEBUG(config,out).print("creating new persistent key for elem id %d\n", id); + elems.emplace(id, World::GetPersistentData(keyname, NULL)); + return elems[id]; +} +static void remove_elem_config(color_ostream &out, int id) { + if (!elems.count(id)) + return; + DEBUG(config,out).print("removing persistent key for elem id %d\n", id); + World::DeletePersistentData(elems[id]); + elems.erase(id); } static const int32_t CYCLE_TICKS = 1200; // one day @@ -87,7 +114,9 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { is_enabled = enable; DEBUG(config,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); - set_config_bool(CONFIG_IS_ENABLED, is_enabled); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); } else { DEBUG(config,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", @@ -109,16 +138,27 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { if (!config.isValid()) { DEBUG(config,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); - set_config_bool(CONFIG_IS_ENABLED, is_enabled); - set_config_val(CONFIG_SOMETHING_ELSE, 6000); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + set_config_val(config, CONFIG_SOMETHING_ELSE, 6000); } // we have to copy our enabled flag into the global plugin variable, but // all the other state we can directly read/modify from the persistent // data structure. - is_enabled = get_config_bool(CONFIG_IS_ENABLED); + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); DEBUG(config,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); + + // load other config elements, if applicable + elems.clear(); + vector elem_configs; + World::GetPersistentData(&elem_configs, ELEM_CONFIG_KEY_PREFIX, true); + const size_t num_elem_configs = elem_configs.size(); + for (size_t idx = 0; idx < num_elem_configs; ++idx) { + auto &c = elem_configs[idx]; + elems.emplace(get_config_val(c, ELEM_CONFIG_ID), c); + } + return CR_OK; } @@ -150,8 +190,8 @@ static command_result do_command(color_ostream &out, vector ¶meters) // TODO: configuration logic // simple commandline parsing can be done in C++, but there are lua libraries - // that can easily handle more complex commandlines. see the blueprint plugin - // for an example. + // that can easily handle more complex commandlines. see the seedwatch plugin + // for a simple example. return CR_OK; } diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 98e557075..b67101614 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -70,6 +70,8 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { DEBUG(config,out).print("%s from the API; persisting\n", is_enabled ? "enabled" : "disabled"); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); } else { DEBUG(config,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index aee167258..551ec36dc 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -99,7 +99,7 @@ static const int32_t CYCLE_TICKS = 1200; static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); -static void do_cycle(color_ostream &out, int32_t *num_enabled_seeds, int32_t *num_disabled_seeds); +static void do_cycle(color_ostream &out, int32_t *num_enabled_seeds = NULL, int32_t *num_disabled_seeds = NULL); static void seedwatch_setTarget(color_ostream &out, string name, int32_t num); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { @@ -149,7 +149,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { is_enabled ? "enabled" : "disabled"); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); if (enable) - seedwatch_setTarget(out, "all", DEFAULT_TARGET); + do_cycle(out); } else { DEBUG(config,out).print("%s from the API, but already %s; no action\n", is_enabled ? "enabled" : "disabled", @@ -174,26 +174,27 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { world_plant_ids[plant->id] = i; } + watched_seeds.clear(); + vector seed_configs; + World::GetPersistentData(&seed_configs, SEED_CONFIG_KEY_PREFIX, true); + const size_t num_seed_configs = seed_configs.size(); + for (size_t idx = 0; idx < num_seed_configs; ++idx) { + auto &c = seed_configs[idx]; + watched_seeds.emplace(get_config_val(c, SEED_CONFIG_ID), c); + } + config = World::GetPersistentData(CONFIG_KEY); if (!config.isValid()) { DEBUG(config,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + seedwatch_setTarget(out, "all", DEFAULT_TARGET); } is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); DEBUG(config,out).print("loading persisted enabled state: %s\n", is_enabled ? "true" : "false"); - watched_seeds.clear(); - vector seed_configs; - World::GetPersistentData(&seed_configs, SEED_CONFIG_KEY_PREFIX, true); - const size_t num_seed_configs = seed_configs.size(); - for (size_t idx = 0; idx < num_seed_configs; ++idx) { - auto &c = seed_configs[idx]; - watched_seeds.emplace(get_config_val(c, SEED_CONFIG_ID), c); - } - return CR_OK; } From d7d6c5aea66842bb09ff53f2da0502df6ba43f77 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 08:55:46 -0500 Subject: [PATCH 0486/2222] Backwards options for cycle hotkey working --- library/lua/gui/widgets.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 342ece201..cc71df9bf 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1440,6 +1440,7 @@ CycleHotkeyLabel = defclass(CycleHotkeyLabel, Label) CycleHotkeyLabel.ATTRS{ key=DEFAULT_NIL, + key_back=DEFAULT_NIL, label=DEFAULT_NIL, label_width=DEFAULT_NIL, options=DEFAULT_NIL, @@ -1451,7 +1452,8 @@ function CycleHotkeyLabel:init() self:setOption(self.initial_option) self:setText{ - {key=self.key, key_sep=': ', text=self.label, width=self.label_width, + {key=self.key_back, key_sep='', width=0, on_activate=self.key_back and self:callback('cycle', true)}, + {key=self.key, key_sep=self.key_back and '' or ': ', text=self.label, width=self.key_back and 0 or self.label_width, on_activate=self:callback('cycle')}, ' ', {text=self:callback('getOptionLabel'), @@ -1459,12 +1461,14 @@ function CycleHotkeyLabel:init() } end -function CycleHotkeyLabel:cycle() +function CycleHotkeyLabel:cycle(backwards) local old_option_idx = self.option_idx - if self.option_idx == #self.options then + if self.option_idx == #self.options and not backwards then self.option_idx = 1 + elseif self.option_idx == 1 and backwards then + self.option_idx = #self.options else - self.option_idx = self.option_idx + 1 + self.option_idx = self.option_idx + (not backwards and 1 or -1) end if self.on_change then self.on_change(self:getOptionValue(), From 36b76d709c76e89c690a2f15f533962e2936454b Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 08:59:55 -0500 Subject: [PATCH 0487/2222] Update documentation for CycleHotkeyLabel --- docs/dev/Lua API.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 5a1b562ee..c63be53f4 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4816,6 +4816,7 @@ cycle through by pressing a specified hotkey or clicking on the text. It has the following attributes: :key: The hotkey keycode to display, e.g. ``'CUSTOM_A'``. +:key_back: Similar to `key`, but will cycle backwards (optional) :label: The string (or a function that returns a string) to display after the hotkey. :label_width: The number of spaces to allocate to the ``label`` (for use in From 4e366790c0c7a91e0a90b70c3f7f368ae2ea033a Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 09:29:31 -0500 Subject: [PATCH 0488/2222] Changelog/documentation for key_back --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ea140f451..b024271e9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- ``widgets.CycleHotkeyLabel``: Added ``key_back`` optional parameter to cycle backwards. ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index c63be53f4..ad1dc6a46 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4816,7 +4816,7 @@ cycle through by pressing a specified hotkey or clicking on the text. It has the following attributes: :key: The hotkey keycode to display, e.g. ``'CUSTOM_A'``. -:key_back: Similar to `key`, but will cycle backwards (optional) +:key_back: Similar to ``key``, but will cycle backwards (optional) :label: The string (or a function that returns a string) to display after the hotkey. :label_width: The number of spaces to allocate to the ``label`` (for use in From c0cd37ff6fb65f9e3474aebf2b8175e68441c0fc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 09:04:50 -0800 Subject: [PATCH 0489/2222] protect against NULLs and invalid stockpiles --- plugins/automelt.cpp | 66 +++++++++++----------------------------- plugins/lua/automelt.lua | 8 +++-- 2 files changed, 23 insertions(+), 51 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 14c62a80a..e8001c48b 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -1,37 +1,21 @@ - #include "Debug.h" #include "LuaTools.h" #include "PluginManager.h" -#include "TileTypes.h" #include "modules/Buildings.h" -#include "modules/Maps.h" #include "modules/Items.h" #include "modules/World.h" -#include "modules/Designations.h" #include "modules/Persistence.h" -#include "modules/Units.h" -#include "modules/Screen.h" #include "modules/Gui.h" -// #include "uicommon.h" - #include "df/world.h" -#include "df/building.h" -#include "df/world_raws.h" -#include "df/building_def.h" -#include "df/viewscreen_dwarfmodest.h" #include "df/building_stockpilest.h" -#include "df/plotinfost.h" #include "df/item_quality.h" #include #include -using df::building_stockpilest; using std::map; -using std::multimap; -using std::pair; using std::string; using std::unordered_map; using std::vector; @@ -41,10 +25,7 @@ using namespace df::enums; DFHACK_PLUGIN("automelt"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(cursor); -REQUIRE_GLOBAL(plotinfo); namespace DFHack { @@ -57,16 +38,12 @@ static const string CONFIG_KEY = string(plugin_name) + "/config"; static const string STOCKPILE_CONFIG_KEY_PREFIX = string(plugin_name) + "/stockpile/"; static PersistentDataItem config; -// static vector watched_stockpiles; -// static unordered_map watched_stockpiles_indices; - static unordered_map watched_stockpiles; enum StockpileConfigValues { STOCKPILE_CONFIG_ID = 0, STOCKPILE_CONFIG_MONITORED = 1, - }; static int get_config_val(PersistentDataItem &c, int index) @@ -115,8 +92,8 @@ static void remove_stockpile_config(color_ostream &out, int id) watched_stockpiles.erase(id); } -static bool isStockpile(df::building * building) { - return building->getType() == df::building_type::Stockpile; +static bool isStockpile(df::building * bld) { + return bld && bld->getType() == df::building_type::Stockpile; } static void validate_stockpile_configs(color_ostream &out) @@ -124,7 +101,7 @@ static void validate_stockpile_configs(color_ostream &out) for (auto &c : watched_stockpiles) { int id = get_config_val(c.second, STOCKPILE_CONFIG_ID); auto bld = df::building::find(id); - if (!bld || !isStockpile(bld)) + if (!isStockpile(bld)) remove_stockpile_config(out, id); } } @@ -135,7 +112,7 @@ static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); static int32_t do_cycle(color_ostream &out); -DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_init(color_ostream &out, vector &commands) { DEBUG(status, out).print("initializing %s\n", plugin_name); @@ -222,7 +199,6 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) return CR_OK; } - static bool call_automelt_lua(color_ostream *out, const char *fn_name, int nargs = 0, int nres = 0, Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, @@ -268,6 +244,8 @@ static command_result do_command(color_ostream &out, vector ¶meters) static inline bool is_metal_item(df::item *item) { + if (!item) + return false; MaterialInfo mat(item); return (mat.getCraftClass() == craft_material_class::Metal); } @@ -307,6 +285,9 @@ static inline bool can_melt(df::item *item) { static const BadFlagsCanMelt bad_flags; + if (!is_metal_item(item)) + return false; + if (item->flags.whole & bad_flags.whole) return false; @@ -315,9 +296,6 @@ static inline bool can_melt(df::item *item) if (t == df::enums::item_type::BOX || t == df::enums::item_type::BAR) return false; - if (!is_metal_item(item)) - return false; - for (auto &g : item->general_refs) { switch (g->getType()) @@ -372,7 +350,7 @@ static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_fl { DEBUG(perf,out).print("assignedToStockpile\n"); size_t marked_count = 0; - std::vector contents; + vector contents; Items::getContainedItems(item, &contents); for (auto child = contents.begin(); child != contents.end(); child++) { @@ -414,7 +392,6 @@ static int mark_item(color_ostream &out, df::item *item, BadFlagsMarkItem bad_fl } - static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & stockpile, int32_t &premarked_item_count, int32_t &item_count, map &tracked_item_map, bool should_melt) { DEBUG(perf,out).print("%s running mark_all_in_stockpile\nshould_melt=%d\n", plugin_name, should_melt); @@ -429,10 +406,8 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st int spid = get_config_val(stockpile, STOCKPILE_CONFIG_ID); auto found = df::building::find(spid); - if (!isStockpile(found)){ - + if (!isStockpile(found)) return 0; - } df::building_stockpilest * pile_cast = virtual_cast(found); @@ -451,7 +426,6 @@ static int32_t mark_all_in_stockpile(color_ostream &out, PersistentDataItem & st return marked_count; } - static int32_t scan_stockpiles(color_ostream &out, bool should_melt, map &item_count_piles, map &premarked_item_count_piles, map &marked_item_count_piles, map &tracked_item_map) { DEBUG(perf,out).print("running scan_stockpiles\n"); @@ -518,8 +492,6 @@ static int32_t scan_count_all(color_ostream &out, bool should_melt, int32_t &mar map tracked_item_map_piles; - tracked_item_map_piles.clear(); - newly_marked_items_piles = scan_stockpiles(out, should_melt, item_count_piles, premarked_item_count_piles, marked_item_count_piles, tracked_item_map_piles); marked_item_count_global = scan_all_melt_designated(out, tracked_item_map_piles); @@ -557,20 +529,18 @@ static int32_t do_cycle(color_ostream &out) { } static int getSelectedStockpile(color_ostream &out) { - df::building *selected_bldg = NULL; - selected_bldg = Gui::getSelectedBuilding(out, true); - if (selected_bldg->getType() != df::building_type::Stockpile) { + df::building *bld = Gui::getSelectedBuilding(out, true); + if (!isStockpile(bld)) { DEBUG(status,out).print("Selected building is not stockpile\n"); return -1; } - return selected_bldg->id; + return bld->id; } static PersistentDataItem *getSelectedStockpileConfig(color_ostream &out) { int32_t bldg_id = getSelectedStockpile(out); if (bldg_id == -1) { - DEBUG(status,out).print("Selected bldg invalid\n"); return NULL; } @@ -579,11 +549,10 @@ static PersistentDataItem *getSelectedStockpileConfig(color_ostream &out) { if (watched_stockpiles.count(bldg_id)) { c = &(watched_stockpiles[bldg_id]); return c; - } else { - DEBUG(status,out).print("No existing config\n"); - return NULL; } + DEBUG(status,out).print("No existing config\n"); + return NULL; } static void push_stockpile_config(lua_State *L, int id, bool monitored) { @@ -671,7 +640,7 @@ static void automelt_setStockpileConfig(color_ostream &out, int id, bool monitor DEBUG(status,out).print("entering automelt_setStockpileConfig for id=%d and monitored=%d\n", id, monitored); validate_stockpile_configs(out); auto bldg = df::building::find(id); - bool isInvalidStockpile = !bldg || !isStockpile(bldg); + bool isInvalidStockpile = !isStockpile(bldg); bool hasNoData = !monitored; if (isInvalidStockpile || hasNoData) { DEBUG(cycle,out).print("calling remove_stockpile_config with id=%d monitored=%d\n", id, monitored); @@ -767,7 +736,6 @@ static int automelt_getSelectedStockpileConfig(lua_State *L){ return 1; } -//TODO static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 4f1d82ad9..b49a434a1 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -17,8 +17,12 @@ end local function do_set_stockpile_config(var_name, val, stockpiles) for _,bspec in ipairs(argparse.stringList(stockpiles)) do local config = automelt_getStockpileConfig(bspec) - config[var_name] = val - automelt_setStockpileConfig(config.id, config.monitor, config.melt) + if not config then + dfhack.printerr('invalid stockpile: '..tostring(bspec)) + else + config[var_name] = val + automelt_setStockpileConfig(config.id, config.monitor, config.melt) + end end end From d09f8553a097a1de05a0d5e4a5b4b36b0fd24714 Mon Sep 17 00:00:00 2001 From: ElsaTheHobo Date: Mon, 6 Feb 2023 13:34:31 -0500 Subject: [PATCH 0490/2222] Delete makeown.lua --- library/lua/makeown.lua | 302 ---------------------------------------- 1 file changed, 302 deletions(-) delete mode 100644 library/lua/makeown.lua diff --git a/library/lua/makeown.lua b/library/lua/makeown.lua deleted file mode 100644 index 3d646b5fc..000000000 --- a/library/lua/makeown.lua +++ /dev/null @@ -1,302 +0,0 @@ -local _ENV = mkmodule('makeown') ---[[ - 'tweak makeown' as a lua include - make_own(unit) -- removes foreign flags, sets civ_id to fort civ_id, and sets clothes ownership - make_citizen(unit) -- called by make_own if unit.race == fort race - - eventually ought to migrate to hack/lua/plugins/tweak.lua - and local _ENV = mkmodule('plugin.tweak') - in order to link to functions in the compiled plugin (when/if they become available to lua) ---]] -local utils = require 'utils' - - -local function fix_clothing_ownership(unit) - -- extracted/translated from tweak makeown plugin - -- to be called by tweak-fixmigrant/makeown - -- units forced into the fort by removing the flags do not own their clothes - -- which has the result that they drop all their clothes and become unhappy because they are naked - -- so we need to make them own their clothes and add them to their uniform - local fixcount = 0 --int fixcount = 0; - for j=0,#unit.inventory-1 do --for(size_t j=0; jinventory.size(); j++) - local inv_item = unit.inventory[j] --unidf::unit_inventory_item* inv_item = unit->inventory[j]; - local item = inv_item.item --df::item* item = inv_item->item; - -- unforbid items (for the case of kidnapping caravan escorts who have their stuff forbidden by default) - -- moved forbid false to inside if so that armor/weapons stay equiped - if inv_item.mode == df.unit_inventory_item.T_mode.Worn then --if(inv_item->mode == df::unit_inventory_item::T_mode::Worn) - -- ignore armor? - -- it could be leather boots, for example, in which case it would not be nice to forbid ownership - --if(item->getEffectiveArmorLevel() != 0) - -- continue; - - if not dfhack.items.getOwner(item) then --if(!Items::getOwner(item)) - if dfhack.items.setOwner(item,unit) then --if(Items::setOwner(item, unit)) - item.flags.forbid = false --inv_item->item->flags.bits.forbid = 0; - -- add to uniform, so they know they should wear their clothes - unit.military.uniforms[0]:insert('#',item.id) --insert_into_vector(unit->military.uniforms[0], item->id); - fixcount = fixcount + 1 --fixcount++; - else - ----out << "could not change ownership for item!" << endl; - print("Makeown: could not change ownership for an item!") - end - end - end - end - -- clear uniform_drop (without this they would drop their clothes and pick them up some time later) - -- dirty? - unit.military.uniform_drop:resize(0) --unit->military.uniform_drop.clear(); - ----out << "ownership for " << fixcount << " clothes fixed" << endl; - print("Makeown: claimed ownership for "..tostring(fixcount).." worn items") - --return true --return CR_OK; -end - -local function entity_link(hf, eid, do_event, add, replace_idx) - do_event = (do_event == nil) and true or do_event - add = (add == nil) and true or add - replace_idx = replace_idx or -1 - - local link = add and df.histfig_entity_link_memberst:new() or df.histfig_entity_link_former_memberst:new() - link.entity_id = eid - if replace_idx > -1 then - local e = hf.entity_links[replace_idx] - link.link_strength = (e.link_strength > 3) and (e.link_strength - 2) or e.link_strength - hf.entity_links[replace_idx] = link -- replace member link with former member link - e:delete() - else - link.link_strength = 100 - hf.entity_links:insert('#', link) - end - if do_event then - event = add and df.history_event_add_hf_entity_linkst:new() or df.history_event_remove_hf_entity_linkst:new() - event.year = df.global.cur_year - event.seconds = df.global.cur_year_tick - event.civ = eid - event.histfig = hf.id - event.link_type = 0 - event.position_id = -1 - event.id = df.global.hist_event_next_id - df.global.world.history.events:insert('#',event) - df.global.hist_event_next_id = df.global.hist_event_next_id + 1 - end -end - -local function change_state(hf, site_id, pos) - hf.info.unk_14.unk_0 = 3 -- state? arrived? - hf.info.unk_14.region:assign(pos) - hf.info.unk_14.site = site_id - event = df.history_event_change_hf_statest:new() - event.year = df.global.cur_year - event.seconds = df.global.cur_year_tick - event.hfid = hf.id - event.state = 3 - event.site = site_id - event.region_pos:assign(pos) - event.substate = -1; event.region = -1; event.layer = -1; - event.id = df.global.hist_event_next_id - df.global.world.history.events:insert('#',event) - df.global.hist_event_next_id = df.global.hist_event_next_id + 1 -end - - -function make_citizen(unit) - local dfg = df.global - local civ_id = dfg.ui.civ_id - local group_id = dfg.ui.group_id - local events = dfg.world.history.events - local fortent = dfg.ui.main.fortress_entity - local civent = fortent and df.historical_entity.find(fortent.entity_links[0].target) - -- utils.binsearch(dfg.world.entities.all, fortent.entity_links[0].target, 'id') - local event - local region_pos = df.world_site.find(dfg.ui.site_id).pos -- used with state events and hf state - - local hf - -- assume that hf id 1 and hf id 2 are equal. I am unaware of instances of when they are not. - -- occationally a unit does not have both flags set (missing flags1.important_historical_figure) - -- and I don't know what that means yet. - if unit.flags1.important_historical_figure and unit.flags2.important_historical_figure then - -- aready hf, find it (unlikely to happen) - hf = utils.binsearch(dfg.world.history.figures, unit.hist_figure_id, 'id') - --elseif unit.flags1.important_historical_figure or unit.flags2.important_historical_figure then - -- something wrong, try to fix it? - --[[ - if unit.hist_figure_id == -1 then - unit.hist_figure_id = unit.hist_figure_id2 - end - if unit.hist_figure_id > -1 then - unit.hist_figure_id2 = unit.hist_figure_id - unit.flags1.important_historical_figure = true - unit.flags2.important_historical_figure = true - hf = utils.binsearch(dfg.world.history.figures, unit.hist_figure_id, 'id') - else - unit.flags1.important_historical_figure = false - unit.flags2.important_historical_figure = false - end - --]] - --else - -- make one - end - --local new_hf = false - if not hf then - --new_hf = true - hf = df.historical_figure:new() - hf.profession = unit.profession - hf.race = unit.race - hf.caste = unit.caste - hf.sex = unit.sex - hf.appeared_year = dfg.cur_year - hf.born_year = unit.birth_year - hf.born_seconds = unit.birth_time - hf.curse_year = unit.curse_year - hf.curse_seconds = unit.curse_time - hf.birth_year_bias=unit.bias_birth_bias - hf.birth_time_bias=unit.birth_time_bias - hf.old_year = unit.old_year - hf.old_seconds = unit.old_time - hf.died_year = -1 - hf.died_seconds = -1 - hf.name:assign(unit.name) - hf.civ_id = unit.civ_id - hf.population_id = unit.population_id - hf.breed_id = -1 - hf.unit_id = unit.id - hf.id = dfg.hist_figure_next_id -- id must be set before adding links (for the events) - - --history_event_add_hf_entity_linkst not reported for civ on starting 7 - entity_link(hf, civ_id, false) -- so lets skip event here - entity_link(hf, group_id) - - hf.info = df.historical_figure_info:new() - hf.info.unk_14 = df.historical_figure_info.T_unk_14:new() -- hf state? - --unk_14.region_id = -1; unk_14.beast_id = -1; unk_14.unk_14 = 0 - hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1 - -- set values that seem related to state and do event - change_state(hf, dfg.ui.site_id, region_pos) - - - --lets skip skills for now - --local skills = df.historical_figure_info.T_skills:new() -- skills snap shot - -- ... - --info.skills = skills - - dfg.world.history.figures:insert('#', hf) - dfg.hist_figure_next_id = dfg.hist_figure_next_id + 1 - - --new_hf_loc = df.global.world.history.figures[#df.global.world.history.figures - 1] - fortent.histfig_ids:insert('#', hf.id) - fortent.hist_figures:insert('#', hf) - civent.histfig_ids:insert('#', hf.id) - civent.hist_figures:insert('#', hf) - - unit.flags1.important_historical_figure = true - unit.flags2.important_historical_figure = true - unit.hist_figure_id = hf.id - unit.hist_figure_id2 = hf.id - print("Makeown-citizen: created historical figure") - else - -- only insert into civ/fort if not already there - -- Migrants change previous histfig_entity_link_memberst to histfig_entity_link_former_memberst - -- for group entities, add link_member for new group, and reports events for remove from group, - -- remove from civ, change state, add civ, and add group - - hf.civ_id = civ_id -- ensure current civ_id - - local found_civlink = false - local found_fortlink = false - local v = hf.entity_links - for k=#v-1,0,-1 do - if df.histfig_entity_link_memberst:is_instance(v[k]) then - entity_link(hf, v[k].entity_id, true, false, k) - end - end - - if hf.info and hf.info.unk_14 then - change_state(hf, dfg.ui.site_id, region_pos) - -- leave info nil if not found for now - end - - if not found_civlink then entity_link(hf,civ_id) end - if not found_fortlink then entity_link(hf,group_id) end - - --change entity_links - local found = false - for _,v in ipairs(civent.histfig_ids) do - if v == hf.id then found = true; break end - end - if not found then - civent.histfig_ids:insert('#', hf.id) - civent.hist_figures:insert('#', hf) - end - found = false - for _,v in ipairs(fortent.histfig_ids) do - if v == hf.id then found = true; break end - end - if not found then - fortent.histfig_ids:insert('#', hf.id) - fortent.hist_figures:insert('#', hf) - end - print("Makeown-citizen: migrated historical figure") - end -- hf - - local nemesis = dfhack.units.getNemesis(unit) - if not nemesis then - nemesis = df.nemesis_record:new() - nemesis.figure = hf - nemesis.unit = unit - nemesis.unit_id = unit.id - nemesis.save_file_id = civent.save_file_id - nemesis.unk10, nemesis.unk11, nemesis.unk12 = -1, -1, -1 - --group_leader_id = -1 - nemesis.id = dfg.nemesis_next_id - nemesis.member_idx = civent.next_member_idx - civent.next_member_idx = civent.next_member_idx + 1 - - dfg.world.nemesis.all:insert('#', nemesis) - dfg.nemesis_next_id = dfg.nemesis_next_id + 1 - - nemesis_link = df.general_ref_is_nemesisst:new() - nemesis_link.nemesis_id = nemesis.id - unit.general_refs:insert('#', nemesis_link) - - --new_nemesis_loc = df.global.world.nemesis.all[#df.global.world.nemesis.all - 1] - fortent.nemesis_ids:insert('#', nemesis.id) - fortent.nemesis:insert('#', nemesis) - civent.nemesis_ids:insert('#', nemesis.id) - civent.nemesis:insert('#', nemesis) - print("Makeown-citizen: created nemesis entry") - else-- only insert into civ/fort if not already there - local found = false - for _,v in ipairs(civent.nemesis_ids) do - if v == nemesis.id then found = true; break end - end - if not found then - civent.nemesis_ids:insert('#', nemesis.id) - civent.nemesis:insert('#', nemesis) - end - found = false - for _,v in ipairs(fortent.nemesis_ids) do - if v == nemesis.id then found = true; break end - end - if not found then - fortent.nemesis_ids:insert('#', nemesis.id) - fortent.nemesis:insert('#', nemesis) - end - print("Makeown-citizen: migrated nemesis entry") - end -- nemesis -end - - -function make_own(unit) - --tweak makeown - unit.flags2.resident = false; unit.flags1.merchant = false; unit.flags1.forest = false; - unit.civ_id = df.global.plotinfo.civ_id - if unit.profession == df.profession.MERCHANT then unit.profession = df.profession.TRADER end - if unit.profession2 == df.profession.MERCHANT then unit.profession2 = df.profession.TRADER end - fix_clothing_ownership(unit) - if unit.race == df.global.plotinfo.race_id then - make_citizen(unit) - end -end - - - -return _ENV From e1f74ab068eaec6a606dcf07a8c95d53a2c78469 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 14:53:26 -0500 Subject: [PATCH 0491/2222] Tweak to completely nil the key_back table for setText if not set --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index cc71df9bf..13c84f3c7 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1452,7 +1452,7 @@ function CycleHotkeyLabel:init() self:setOption(self.initial_option) self:setText{ - {key=self.key_back, key_sep='', width=0, on_activate=self.key_back and self:callback('cycle', true)}, + self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self.key_back and self:callback('cycle', true)} or {}, {key=self.key, key_sep=self.key_back and '' or ': ', text=self.label, width=self.key_back and 0 or self.label_width, on_activate=self:callback('cycle')}, ' ', From 39dc0ccc8ac2c68c6f0e8d29b3afe8aa189f0d61 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 15:03:28 -0500 Subject: [PATCH 0492/2222] Cleanup --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 13c84f3c7..9d7c62fd2 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1453,7 +1453,7 @@ function CycleHotkeyLabel:init() self:setText{ self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self.key_back and self:callback('cycle', true)} or {}, - {key=self.key, key_sep=self.key_back and '' or ': ', text=self.label, width=self.key_back and 0 or self.label_width, + {key=self.key, key_sep=': ', text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, ' ', {text=self:callback('getOptionLabel'), From 36391af27ca7f6ba5d973d6b4baf099ff3b075c2 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 15:12:47 -0500 Subject: [PATCH 0493/2222] More cleanup --- library/lua/gui/widgets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 9d7c62fd2..b0893ea52 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1452,7 +1452,7 @@ function CycleHotkeyLabel:init() self:setOption(self.initial_option) self:setText{ - self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self.key_back and self:callback('cycle', true)} or {}, + self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self:callback('cycle', true)} or {}, {key=self.key, key_sep=': ', text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, ' ', From 0ed4a1c5402490fbf456373fb4a59cbe46e7e489 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 15:44:04 -0500 Subject: [PATCH 0494/2222] Add documentation to cycle() for new parameter --- docs/dev/Lua API.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 133093dae..aceb25b5d 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4835,9 +4835,10 @@ the ``option_idx`` instance variable. The CycleHotkeyLabel widget implements the following methods: -* ``cyclehotkeylabel:cycle()`` +* ``cyclehotkeylabel:cycle(backwards)`` Cycles the selected option and triggers the ``on_change`` callback. + If ``backwards`` is defined and is truthy, the cycle direction will be reversed * ``cyclehotkeylabel:setOption(value_or_index, call_on_change)`` From f91555d5e3123b94d65a307cf150eae33723a3ee Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 6 Feb 2023 15:44:50 -0500 Subject: [PATCH 0495/2222] Add brackets to indicate optional param --- docs/dev/Lua API.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index aceb25b5d..503f4bece 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4835,7 +4835,7 @@ the ``option_idx`` instance variable. The CycleHotkeyLabel widget implements the following methods: -* ``cyclehotkeylabel:cycle(backwards)`` +* ``cyclehotkeylabel:cycle([backwards])`` Cycles the selected option and triggers the ``on_change`` callback. If ``backwards`` is defined and is truthy, the cycle direction will be reversed From 0b9b258224216764bbe6516c603b31abde7e3096 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 12:54:05 -0800 Subject: [PATCH 0496/2222] ensure scrollable lists are centered in the visible viewport --- docs/changelog.txt | 1 + library/lua/gui/widgets.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ff9ad46a7..6d0fda5fc 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - ``Units::isFortControlled``: Account for agitated wildlife - Fix right click sometimes closing both a DFHack window and a vanilla panel +- Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible ## Misc Improvements - `automelt`: is now more resistent to savegame corruption diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index abbd70f7d..91f0188dd 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1634,6 +1634,7 @@ end function List:postComputeFrame(body) self.page_size = math.max(1, math.floor(body.height / self.row_height)) + self.page_top = math.max(1, math.min(#self.choices - self.page_size + 1)) update_list_scrollbar(self) end From a38246aaa4a00d7b69a29987765dfa21dc833327 Mon Sep 17 00:00:00 2001 From: Roxy <75404941+TealSeer@users.noreply.github.com> Date: Mon, 6 Feb 2023 15:37:27 -0500 Subject: [PATCH 0497/2222] Update logic for zone type checks --- library/modules/Buildings.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 828768a54..12180fa52 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1589,27 +1589,23 @@ bool Buildings::isPenPasture(df::building * building) if (!isActivityZone(building)) return false; -/* TODO: understand how this changes for v50 - return ((df::building_civzonest*) building)->zone_flags.bits.pen_pasture != 0; -*/ return false; + return ((df::building_civzonest*)building)->type == civzone_type::Pen; } bool Buildings::isPitPond(df::building * building) { if (!isActivityZone(building)) return false; -/* TODO: understand how this changes for v50 - return ((df::building_civzonest*) building)->zone_flags.bits.pit_pond != 0; -*/ return false; + + return ((df::building_civzonest*)building)->type == civzone_type::Pond; } bool Buildings::isActive(df::building * building) { if (!isActivityZone(building)) return false; -/* TODO: understand how this changes for v50 - return ((df::building_civzonest*) building)->zone_flags.bits.active != 0; -*/ return false; + + return ((df::building_civzonest*)building)->is_active == 8; } bool Buildings::isHospital(df::building * building) @@ -1625,9 +1621,8 @@ bool Buildings::isHospital(df::building * building) { if (!isActivityZone(building)) return false; -/* TODO: understand how this changes for v50 - return ((df::building_civzonest*) building)->zone_flags.bits.animal_training != 0; -*/ return false; + + return ((df::building_civzonest*)building)->type == civzone_type::AnimalTraining; } // returns building of pen/pit at cursor position (NULL if nothing found) From 0043e05895901fdb4639725aee910136f4ad120a Mon Sep 17 00:00:00 2001 From: Roxy <75404941+TealSeer@users.noreply.github.com> Date: Mon, 6 Feb 2023 15:38:59 -0500 Subject: [PATCH 0498/2222] enable autonestbox --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3389303fd..df49c3d59 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -84,7 +84,7 @@ dfhack_plugin(autofarm autofarm.cpp) add_subdirectory(autolabor) #dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) dfhack_plugin(automelt automelt.cpp LINK_LIBRARIES lua) -#dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) +dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) From ca5a17a7995154d2bbf089fc7b5337d3b23e5b7b Mon Sep 17 00:00:00 2001 From: Roxy <75404941+TealSeer@users.noreply.github.com> Date: Mon, 6 Feb 2023 19:09:47 -0500 Subject: [PATCH 0499/2222] explanatory comment for magic number --- library/modules/Buildings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 12180fa52..0a901d621 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1604,7 +1604,7 @@ bool Buildings::isActive(df::building * building) { if (!isActivityZone(building)) return false; - + // 8 is the value obtained by reverse engineering return ((df::building_civzonest*)building)->is_active == 8; } From 826f918954267c5e08fd200833144fe7322888cb Mon Sep 17 00:00:00 2001 From: Roxy <75404941+TealSeer@users.noreply.github.com> Date: Mon, 6 Feb 2023 19:11:00 -0500 Subject: [PATCH 0500/2222] remove isHospital as hospitals are no longer civzones --- library/include/modules/Buildings.h | 1 - library/modules/Buildings.cpp | 9 --------- 2 files changed, 10 deletions(-) diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index cb9091861..66745abe6 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -275,7 +275,6 @@ DFHACK_EXPORT bool isActivityZone(df::building * building); DFHACK_EXPORT bool isPenPasture(df::building * building); DFHACK_EXPORT bool isPitPond(df::building * building); DFHACK_EXPORT bool isActive(df::building * building); -DFHACK_EXPORT bool isHospital(df::building * building); DFHACK_EXPORT bool isAnimalTraining(df::building * building); DFHACK_EXPORT df::building* findPenPitAt(df::coord coord); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 0a901d621..389113483 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1608,15 +1608,6 @@ bool Buildings::isActive(df::building * building) return ((df::building_civzonest*)building)->is_active == 8; } -bool Buildings::isHospital(df::building * building) - { - if (!isActivityZone(building)) - return false; -/* TODO: understand how this changes for v50 - return ((df::building_civzonest*) building)->zone_flags.bits.hospital != 0; -*/ return false; - } - bool Buildings::isAnimalTraining(df::building * building) { if (!isActivityZone(building)) From bc1adc030059c490b024936f87cc45c517dea889 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 01:38:55 +0000 Subject: [PATCH 0501/2222] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.19.2 → 0.21.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.19.2...0.21.0) - [github.com/Lucas-C/pre-commit-hooks: v1.3.1 → v1.4.2](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.3.1...v1.4.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 669a08db9..a4068bc44 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: args: ['--fix=lf'] - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.19.2 + rev: 0.21.0 hooks: - id: check-github-workflows - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.3.1 + rev: v1.4.2 hooks: - id: forbid-tabs exclude_types: From df5184d7515db43e4d039fb30e0371a00c6c2380 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 7 Feb 2023 07:14:45 +0000 Subject: [PATCH 0502/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 9f55f4781..eb58cda0f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9f55f4781cbf10c3248c4d11ddd52608ec511f21 +Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d diff --git a/scripts b/scripts index 88e7eb472..52736994e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 88e7eb47291c25623bd8ef97d726fbfab3bd66d2 +Subproject commit 52736994e550247aec6a1fb44eb8bc8b3c53b1b0 From b2ecb8aaabd766249f03e1fca52f8806cb78819f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 7 Feb 2023 20:53:12 +0000 Subject: [PATCH 0503/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index eb58cda0f..68f8bfa92 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d +Subproject commit 68f8bfa92b68ef9bc30da7f5a8ea5cf91f69ea16 diff --git a/scripts b/scripts index 52736994e..112d69ed3 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 52736994e550247aec6a1fb44eb8bc8b3c53b1b0 +Subproject commit 112d69ed3e0efb22e3ec6a6c8e58ad3135b80351 From ef81a91181ace4bd045d6229be20c424b37b13b4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Feb 2023 14:12:52 -0800 Subject: [PATCH 0504/2222] only realign out-of-alignment list positions --- library/lua/gui/widgets.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d8c605b93..dfc584fff 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1638,7 +1638,9 @@ end function List:postComputeFrame(body) self.page_size = math.max(1, math.floor(body.height / self.row_height)) - self.page_top = math.max(1, math.min(#self.choices - self.page_size + 1)) + if #self.choices - self.page_size < 0 then + self.page_top = 1 + end update_list_scrollbar(self) end From 468e9875e5880c726e5734c9914fdb640c77f189 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 8 Feb 2023 00:00:31 -0500 Subject: [PATCH 0505/2222] Update DFHack version to 50.07-alpha0, update xml --- CMakeLists.txt | 4 ++-- library/xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b2cfe724e..6854b3300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,8 +189,8 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.05") -set(DFHACK_RELEASE "alpha3.1") +set(DF_VERSION "50.07") +set(DFHACK_RELEASE "alpha0") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/library/xml b/library/xml index 68f8bfa92..e3c3d6a75 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 68f8bfa92b68ef9bc30da7f5a8ea5cf91f69ea16 +Subproject commit e3c3d6a755de05c36bb7f2ac873d6ccb70410589 From 057d2d8c5d3a3fd5d748c85e36bf195e9b9cce16 Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 8 Feb 2023 00:00:52 -0500 Subject: [PATCH 0506/2222] Update scripts (hermit) --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 112d69ed3..f969699a4 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 112d69ed3e0efb22e3ec6a6c8e58ad3135b80351 +Subproject commit f969699a447fde05b0bd3397a8b1e2ce668bc54d From 8ee05af6efa0260457a87bff05333a9a858180ac Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Feb 2023 15:11:17 -0800 Subject: [PATCH 0507/2222] update logo to crisper version provided by TaxiService --- data/art/dfhack.png | Bin 1465 -> 372 bytes docs/changelog.txt | 1 + 2 files changed, 1 insertion(+) diff --git a/data/art/dfhack.png b/data/art/dfhack.png index 17719f084a45a3b037bab5fd4355d5f76f674fbd..7cbb5dbf6201a921aae295aa71fbfa45297636fb 100644 GIT binary patch delta 356 zcmV-q0h|803-khz8Gix*003u+ueAUG02y>eSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM008_+L_t(|+Qe5u4#Xe`ti-?WrO)Z5uXUSkVhDpMR=Olj3_+j+jG9U* zTcqUcJB~BR`@Vm2T`l*1=S^F%ga^oVl~S@zYddr;xiO)tC4bd#`H66cN(CZyn;OGT zobOs+1CbMXH_R7#P%e}(q=3#1QTHc!wcHR*qaRXaj6s9Azeg3`rlQdFJ8g=tCuYJ; zcBo+t``Cb@`2ZgQVyu z)K`E305ylW-7c57cK`qY4rN$LW=%~1DgXcg2mk;800000(o>TF0000R{AK$GU=4uJ=cBK$ zkEy9C#>dA=CX?9hb`@l`TG8wOi`8T@aq!?lnwpwevt|td@87@Y?c2A6!(ry;=7>Zh z#N%=Net+JHo12?Cefl)W|LoPPR|06Ry1F_!ckZ0#z0GEmg@uL8aQX4$ht$>8<;>C9 z*(r^Ujal!UPJgF}h$sLeB5iGLn!z5AM-~?s<>t+s8ed;u-}2$|`0-;+%!?NpWn}>D+qVzD-%n|2X?`VKIaw|eO-@c~Vlq`(w{Bf7 zj;Z^Vlm8KbWHPDl)9EyWgM(RqlgUI)O-)_^@p$~tdqFT5T(tr&my6!s-Yoz1>(|x9 zxt1pui~Tu(PoF+%0=!-?0Dixp_V#v-zrMacXMeRaR!~sz`(^~NxVXr`zC+%x7%fD zX-Pyxd_JG__xH>0-Ma;l!-o$`b#=8G*VEG@A3uJ~5}=IfL`3H2=U0`?!oorf1_MP! zMSnS2p3#AVf&xlPO3>@|h=^Je5m7l-n(qH`CQK%i7z_q=UdUJ^64C6GtcXx3#Kgpe zN|H*YlyB$Gos^fC<8rx(#bU}=RaM1~9XoKjTzvoj9k18R-Me?WeEBkEWo3B1UYeSk za5|lIcX#vr`SbkED4kAAZ*Q*va_7z+)qh@FTU#ZSN{NU_XJ=;?FMK|qYCY&I*2jz**0xN#$^syPp(nVuex#}&EK=`;W%BO`b`9)D~$ z8&y?RSq*3V_U%+wRuYTFsIRYQ{rdGvdXFDJPFGhKwY9aBl$7M>Kn6KLTU#63wrvAY zs2@Ij2*BC1XPKOw#N+W0jYb(B9_GM-1L*a7YHDh**=!US7pq)n&YWRpW(KR(N+1wW z%}c|9j3xlm(a|Aq-n(64=dNVr>92%`TY5_ z3=ItlAQKZ4`8lw>B_BI>jOyxYrIMjgNb$t&b~7|Iq~aSI8kAfwxAO9GH6~N~mCC3A zkU3N&5(xz+9*?U|`SRsU#>U2|sHjlewXv~L{U78yPbQN|)u^J;=;{D6#DDwx^{XOZ zC=^mEXt7vmZf@q(sZ#*_`t=L5*^JR> Date: Tue, 7 Feb 2023 15:40:23 -0800 Subject: [PATCH 0508/2222] add light AA --- data/art/dfhack.png | Bin 372 -> 997 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/art/dfhack.png b/data/art/dfhack.png index 7cbb5dbf6201a921aae295aa71fbfa45297636fb..133dc5aaf8c98921bcc0203ebd986d4915c726cd 100644 GIT binary patch delta 906 zcmV;519klL0_6vgNPh!uNklIMuJ-o!v_=t}TK@FQbar-Dencvj z!dNbsrOxQ+Xp5lg4mvwKVuhlhuM ztZb{*sz`>1hX)4--EMa%6heXNG+KS4a4AVf6_Mj;G=KW;93UX8tE)T>1Ogb_+S;U! zGJT%|e6^fTPfu$TMsT9zKvAOU*4CChJ`zX?pqMb3OeTG>J3T#>3&4rp-Cb!N(Vv-_ zA^!02P%0}$RR!!6Y>@_JMUaX`pHBK%RG$b-lF1}7OzZ3GRYJ76 zxruKC9Dgw~gGXerDgy!`kQuQeO&Xm}C#^!xSX~pYVH74L2uoy!GrkM}-6T9XIN)1B z>p%@rM@I);ji?!7VzC&nK0Q6{@9(3uu!=8cROf!4n3w?iqBmC0_YV-5+Hk*xF8o+wK0M5_PF^-RqQ!8CxUo$|#0a*t_`a?rQ0|Ns(&lW-g9G?o2 z;)68a-QAI0ATt=qv8p^|AVCb3>qyOk#bV(TF%^qN+6e;g)5Wr}vB3(2-U>je4({*o zF@HWjK5Eh5<#HiPtTQz=Mb(wd<>X#=Aff3`h){fhHo6fxM2m}yNH|V+c6Meqn@NsF zMS&h29c^!Kqim4>_V#wj=_oxtJu3En1dHTzhzQM0ZHTAQN3lq@7!#g3D6QAmSAH6O g)$6)(|3R7?{TJ$|00RKEP1L&_iGFGT0FzJwBvOyD5&!@I delta 275 zcmV+u0qp+e2lN7vNPhtQNklpPA!$osy3a$PO=e&u5fhrM(!ILoC~>82Y#lgA?%Yd@eB5k0J_jn%(!&A9 zBTpnmPf|wohPsH`856YfLo+t``Cb@`2Z ZgQVyu)K`E300=dQxZN(7xObDk119bWfv^Ao From be0cec95206eb19dca075cbec611a81a9d848bcf Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Wed, 8 Feb 2023 01:59:57 -0500 Subject: [PATCH 0509/2222] Current attempt to fix lua stack smashing --- plugins/automelt.cpp | 37 ++++++++++++++++++++++++++++++++----- plugins/lua/automelt.lua | 11 ++++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index e8001c48b..1df7f9a68 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -567,6 +567,25 @@ static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { get_config_bool(c, STOCKPILE_CONFIG_MONITORED)); } +static void emplace_bulk_stockpile_configs(lua_State *L, PersistentDataItem &c, map> &stockpiles) { + int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); + bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); + map stockpile_config; + stockpile_config.emplace("id", id); + stockpile_config.emplace("monitored", monitored); + + stockpiles.emplace(id, stockpile_config); +} + +static void emplace_bulk_stockpile_configs(lua_State *L, int id, bool monitored, map> &stockpiles) { + + map stockpile_config; + stockpile_config.emplace("id", id); + stockpile_config.emplace("monitored", monitored); + + stockpiles.emplace(id, stockpile_config); +} + static void automelt_designate(color_ostream &out) { DEBUG(status, out).print("entering automelt designate\n"); out.print("designated %d item(s) for melting\n", do_cycle(out)); @@ -736,6 +755,8 @@ static int automelt_getSelectedStockpileConfig(lua_State *L){ return 1; } + + static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) @@ -762,24 +783,30 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { Lua::Push(L, item_count_piles); Lua::Push(L, marked_item_count_piles); Lua::Push(L, premarked_item_count_piles); - int32_t bldg_count = 0; + + map> stockpile_config_map; for (auto pile : world->buildings.other.STOCKPILE) { if (!isStockpile(pile)) continue; - bldg_count++; int id = pile->id; if (watched_stockpiles.count(id)) { DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); - push_stockpile_config(L, watched_stockpiles[id]); + emplace_bulk_stockpile_configs(L, watched_stockpiles[id], stockpile_config_map); + } else { - push_stockpile_config(L, id, false); + emplace_bulk_stockpile_configs(L, id, false, stockpile_config_map); } } + + Lua::Push(L, stockpile_config_map); + + DEBUG(cycle, *out).print("confmap_length: %d\n", stockpile_config_map.size()); + DEBUG(perf, *out).print("exit automelt_getItemCountsAndStockpileConfigs\n"); - return 4+bldg_count; + return 5; } DFHACK_PLUGIN_LUA_FUNCTIONS{ diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index b49a434a1..535c056e6 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -64,8 +64,12 @@ function getItemCountsAndStockpileConfigs() ret.item_counts = table.remove(data, 1) ret.marked_item_counts = table.remove(data, 1) ret.premarked_item_counts = table.remove(data, 1) - ret.stockpile_configs = data - for _,c in ipairs(ret.stockpile_configs) do + local unparsed_stockpile_configs = table.remove(data, 1) + ret.stockpile_configs = {} + + for idx,c in pairs(unparsed_stockpile_configs) do + print(c.id) + print(c.monitored) if not c.id or c.id == -1 then c.name = "ERROR" c.monitored = false @@ -76,7 +80,8 @@ function getItemCountsAndStockpileConfigs() end c.monitored = c.monitored ~= 0 end - + table.insert(ret.stockpile_configs, c) + end return ret end From 585b8da03a932dcf784218338b5dc2d42165c1a7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 8 Feb 2023 07:15:14 +0000 Subject: [PATCH 0510/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f969699a4..54f5d5f3d 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f969699a447fde05b0bd3397a8b1e2ce668bc54d +Subproject commit 54f5d5f3d3b68b07323e88109b9a44d98abe52b1 From d0b4b1a4a5b3aa0fe55f2e954a26ffb072a9092f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 8 Feb 2023 07:36:55 +0000 Subject: [PATCH 0511/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index e3c3d6a75..cd5baf4ea 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e3c3d6a755de05c36bb7f2ac873d6ccb70410589 +Subproject commit cd5baf4ea82fead3883368da526ac2b606a6209b From c1b9de87d2303acecd763d71c020d70046049dc8 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Mon, 6 Feb 2023 22:40:59 -0500 Subject: [PATCH 0512/2222] Add case_sensitive attr to FilteredList --- library/lua/gui/widgets.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d8c605b93..61cd92cfa 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1866,6 +1866,7 @@ end FilteredList = defclass(FilteredList, Widget) FilteredList.ATTRS { + case_sensitive = true, edit_below = false, edit_key = DEFAULT_NIL, edit_ignore_keys = DEFAULT_NIL, @@ -2026,11 +2027,17 @@ function FilteredList:setFilter(filter, pos) -- start matches at non-space or non-punctuation. this allows -- punctuation itself to be matched if that is useful (e.g. -- filenames or parameter names) - if key ~= '' and + if key ~= '' then + if self.case_sensitive and not search_key:match('%f[^%p\x00]'..key) and not search_key:match('%f[^%s\x00]'..key) then - ok = false - break + ok = false + break + elseif not string.lower(search_key):match('%f[^%p\x00]'..string.lower(key)) and + not string.lower(search_key):match('%f[^%s\x00]'..string.lower(key)) then + ok = false + break + end end end if ok then From 13440d18a85f4fb2de4f0d4f71137ff84f76dd9c Mon Sep 17 00:00:00 2001 From: Robob27 Date: Mon, 6 Feb 2023 22:48:18 -0500 Subject: [PATCH 0513/2222] Add case_sensitive to FilteredList docs --- docs/dev/Lua API.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 503f4bece..3580b5244 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4958,6 +4958,7 @@ construction that allows filtering the list by subwords of its items. In addition to passing through all attributes supported by List, it supports: +:case_sensitive: If true, matching is case sensitive. Defaults to true. :edit_pen: If specified, used instead of ``cursor_pen`` for the edit field. :edit_below: If true, the edit field is placed below the list instead of above. :edit_key: If specified, the edit field is disabled until this key is pressed. From 54560bc5dbab1b0b1ddaf26d5e73487aaf8f15fa Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 8 Feb 2023 02:02:50 -0500 Subject: [PATCH 0514/2222] Don't duplicate regex --- library/lua/gui/widgets.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 61cd92cfa..c249b579f 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2028,15 +2028,15 @@ function FilteredList:setFilter(filter, pos) -- punctuation itself to be matched if that is useful (e.g. -- filenames or parameter names) if key ~= '' then - if self.case_sensitive and - not search_key:match('%f[^%p\x00]'..key) and + if not self.case_sensitive then + search_key = string.lower(search_key) + key = string.lower(key) + end + + if not search_key:match('%f[^%p\x00]'..key) and not search_key:match('%f[^%s\x00]'..key) then ok = false break - elseif not string.lower(search_key):match('%f[^%p\x00]'..string.lower(key)) and - not string.lower(search_key):match('%f[^%s\x00]'..string.lower(key)) then - ok = false - break end end end From dd10451c2f66b16f0097ef622290803151dd0919 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Wed, 8 Feb 2023 17:42:42 +1100 Subject: [PATCH 0515/2222] Remove library/military silver crossbow work-order It is not possible to create a work-order for making silver crossbows, so the silver crossbow recipe belongs in the alternative military library with other artifact/non-craftable weapon recipes. It's already present in the library/military_include_artifact_materials work-orders, so simply needed removal from the vanilla library/military orders version. --- data/orders/military.json | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/data/orders/military.json b/data/orders/military.json index 5b7604dac..0d06dd124 100644 --- a/data/orders/military.json +++ b/data/orders/military.json @@ -1138,42 +1138,6 @@ "job" : "MakeWeapon", "material" : "INORGANIC:SILVER" }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 64, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, { "amount_left" : 1, "amount_total" : 1, From 24827eabe268df2d762e4403f0261e2cf376b1c5 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Wed, 8 Feb 2023 20:25:52 +1100 Subject: [PATCH 0516/2222] Remove checks for silver from library/military for crossbows. --- data/orders/military.json | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/data/orders/military.json b/data/orders/military.json index 0d06dd124..0e53747b6 100644 --- a/data/orders/military.json +++ b/data/orders/military.json @@ -1620,12 +1620,6 @@ "item_type" : "WEAPON", "material" : "INORGANIC:STEEL", "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 } ], "item_subtype" : "ITEM_WEAPON_CROSSBOW", @@ -2321,12 +2315,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3032,12 +3020,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -3820,12 +3802,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : @@ -4698,12 +4674,6 @@ "material" : "INORGANIC:STEEL", "value" : 30 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "flags" : From 1f38b294cbdb34b0eedd6be503d0b034bbe559f3 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Wed, 8 Feb 2023 20:32:32 +1100 Subject: [PATCH 0517/2222] Add note in changelog about silver crossbows --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..9faa0ab4f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair - `autobutcher`: now immediately loads races available at game start into the watchlist +- `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` ## Documentation From 22b31bd7f1d970e65397edb5cde72dc3c4b82a24 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 8 Feb 2023 12:37:35 -0500 Subject: [PATCH 0518/2222] Update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..fbc26f062 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,6 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - `overlay`: overlay widgets can now specify focus paths for the viewscreens they attach to so they only appear in specific contexts. see `overlay-dev-guide` for details. - ``widgets.CycleHotkeyLabel``: Added ``key_back`` optional parameter to cycle backwards. +- ``widgets.FilteredList``: Added ``case_sensitive`` optional paramter to determine if filtering is case sensitive. ## Removed From b950b569267888a6f0a61c81adc08406c805d9a5 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 8 Feb 2023 13:00:13 -0500 Subject: [PATCH 0519/2222] Add methods to HotkeyLabel --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 10 ++++++++++ library/lua/gui/widgets.lua | 14 ++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..7ba027f76 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,6 +57,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - `overlay`: overlay widgets can now specify focus paths for the viewscreens they attach to so they only appear in specific contexts. see `overlay-dev-guide` for details. - ``widgets.CycleHotkeyLabel``: Added ``key_back`` optional parameter to cycle backwards. +- ``widgets.HotkeyLabel``: Added ``setLabel`` method to allow easily updating the label text without mangling the keyboard shortcut. +- ``widgets.HotkeyLabel``: Added ``setOnActivate`` method to allow easily updating the ``on_activate`` callback. ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 503f4bece..43ed9c935 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4808,6 +4808,16 @@ It has the following attributes: :on_activate: If specified, it is the callback that will be called whenever the hotkey is pressed or the label is clicked. +The HotkeyLabel widget implements the following methods: + +* ``hotkeylabel:setLabel(label)`` + + Updates the label without altering the hotkey text. + +* ``hotkeylabel:setOnActivate(on_activate)`` + + Updates the on_activate callback. + CycleHotkeyLabel class ---------------------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d8c605b93..580273dc8 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1419,6 +1419,20 @@ HotkeyLabel.ATTRS{ } function HotkeyLabel:init() + self:initializeLabel() +end + +function HotkeyLabel:setOnActivate(on_activate) + self.on_activate = on_activate + self:initializeLabel() +end + +function HotkeyLabel:setLabel(label) + self.label = label + self:initializeLabel() +end + +function HotkeyLabel:initializeLabel() self:setText{{key=self.key, key_sep=self.key_sep, text=self.label, on_activate=self.on_activate}} end From 02a249fdee4181318a24672c14b4213ccaec5824 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Wed, 8 Feb 2023 14:01:32 -0500 Subject: [PATCH 0520/2222] Fixes the lua stack smashing issue --- plugins/autochop.cpp | 40 +++++++++++++++++++++++++++++++++++++--- plugins/automelt.cpp | 22 ++++++++-------------- plugins/lua/autochop.lua | 7 +++++-- plugins/lua/automelt.lua | 2 -- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index bf7540e29..7a16a79b4 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -803,6 +803,33 @@ static void push_burrow_config(lua_State *L, PersistentDataItem &c) { get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE)); } +static void emplace_bulk_burrow_config(lua_State *L, map> &burrows, int id, bool chop = false, + bool clearcut = false, bool protect_brewable = false, + bool protect_edible = false, bool protect_cookable = false) { + + map burrow_config; + burrow_config.emplace("id", id); + burrow_config.emplace("chop", chop); + burrow_config.emplace("clearcut", clearcut); + burrow_config.emplace("protect_brewable", protect_brewable); + burrow_config.emplace("protect_edible", protect_edible); + burrow_config.emplace("protect_cookable", protect_cookable); + + burrows.emplace(id, burrow_config); + +} + +static void emplace_bulk_burrow_config(lua_State *L, map> &burrows, PersistentDataItem &c) { + emplace_bulk_burrow_config(L, burrows, get_config_val(c, BURROW_CONFIG_ID), + get_config_bool(c, BURROW_CONFIG_CHOP), + get_config_bool(c, BURROW_CONFIG_CLEARCUT), + get_config_bool(c, BURROW_CONFIG_PROTECT_BREWABLE), + get_config_bool(c, BURROW_CONFIG_PROTECT_EDIBLE), + get_config_bool(c, BURROW_CONFIG_PROTECT_COOKABLE)); + + +} + static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) @@ -818,6 +845,9 @@ static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { &designated_trees, &accessible_yield, &tree_counts, &designated_tree_counts); map summary; + + map> burrow_config_map; + summary.emplace("accessible_trees", accessible_trees); summary.emplace("inaccessible_trees", inaccessible_trees); summary.emplace("designated_trees", designated_trees); @@ -831,13 +861,17 @@ static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { for (auto &burrow : plotinfo->burrows.list) { int id = burrow->id; if (watched_burrows_indices.count(id)) { - push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); + // push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); + emplace_bulk_burrow_config(L, burrow_config_map, watched_burrows[watched_burrows_indices[id]]); } else { - push_burrow_config(L, id); + // push_burrow_config(L, id); + emplace_bulk_burrow_config(L, burrow_config_map, id); } } - return 3 + plotinfo->burrows.list.size(); + Lua::Push(L, burrow_config_map); + + return 4; } static int autochop_getBurrowConfig(lua_State *L) { diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 1df7f9a68..ca0cf1626 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -567,9 +567,8 @@ static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { get_config_bool(c, STOCKPILE_CONFIG_MONITORED)); } -static void emplace_bulk_stockpile_configs(lua_State *L, PersistentDataItem &c, map> &stockpiles) { - int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); - bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); +static void emplace_bulk_stockpile_config(lua_State *L, int id, bool monitored, map> &stockpiles) { + map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); @@ -577,13 +576,10 @@ static void emplace_bulk_stockpile_configs(lua_State *L, PersistentDataItem &c, stockpiles.emplace(id, stockpile_config); } -static void emplace_bulk_stockpile_configs(lua_State *L, int id, bool monitored, map> &stockpiles) { - - map stockpile_config; - stockpile_config.emplace("id", id); - stockpile_config.emplace("monitored", monitored); - - stockpiles.emplace(id, stockpile_config); +static void emplace_bulk_stockpile_config(lua_State *L, PersistentDataItem &c, map> &stockpiles) { + int32_t id = get_config_val(c, STOCKPILE_CONFIG_ID); + bool monitored = get_config_bool(c, STOCKPILE_CONFIG_MONITORED); + emplace_bulk_stockpile_config(L, id, monitored, stockpiles); } static void automelt_designate(color_ostream &out) { @@ -792,17 +788,15 @@ static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { int id = pile->id; if (watched_stockpiles.count(id)) { - DEBUG(cycle,*out).print("indexed_id=%d\n", get_config_val(watched_stockpiles[id], STOCKPILE_CONFIG_ID)); - emplace_bulk_stockpile_configs(L, watched_stockpiles[id], stockpile_config_map); + emplace_bulk_stockpile_config(L, watched_stockpiles[id], stockpile_config_map); } else { - emplace_bulk_stockpile_configs(L, id, false, stockpile_config_map); + emplace_bulk_stockpile_config(L, id, false, stockpile_config_map); } } Lua::Push(L, stockpile_config_map); - DEBUG(cycle, *out).print("confmap_length: %d\n", stockpile_config_map.size()); DEBUG(perf, *out).print("exit automelt_getItemCountsAndStockpileConfigs\n"); diff --git a/plugins/lua/autochop.lua b/plugins/lua/autochop.lua index 6b8c9f943..e9bad2eef 100644 --- a/plugins/lua/autochop.lua +++ b/plugins/lua/autochop.lua @@ -96,14 +96,17 @@ function getTreeCountsAndBurrowConfigs() ret.summary = table.remove(data, 1) ret.tree_counts = table.remove(data, 1) ret.designated_tree_counts = table.remove(data, 1) - ret.burrow_configs = data - for _,c in ipairs(ret.burrow_configs) do + local unparsed_burrow_configs = table.remove(data, 1) + + ret.burrow_configs = {} + for idx,c in pairs(unparsed_burrow_configs) do c.name = df.burrow.find(c.id).name c.chop = c.chop ~= 0 c.clearcut = c.clearcut ~= 0 c.protect_brewable = c.protect_brewable ~= 0 c.protect_edible = c.protect_edible ~= 0 c.protect_cookable = c.protect_cookable ~= 0 + table.insert(ret.burrow_configs, c) end return ret end diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 535c056e6..395934867 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -68,8 +68,6 @@ function getItemCountsAndStockpileConfigs() ret.stockpile_configs = {} for idx,c in pairs(unparsed_stockpile_configs) do - print(c.id) - print(c.monitored) if not c.id or c.id == -1 then c.name = "ERROR" c.monitored = false From bf0b3f8845d44f85aa899083d5feb335ac507f7d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 19:06:33 +0000 Subject: [PATCH 0521/2222] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- plugins/automelt.cpp | 2 +- plugins/lua/automelt.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index ca0cf1626..862507413 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -568,7 +568,7 @@ static void push_stockpile_config(lua_State *L, PersistentDataItem &c) { } static void emplace_bulk_stockpile_config(lua_State *L, int id, bool monitored, map> &stockpiles) { - + map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); diff --git a/plugins/lua/automelt.lua b/plugins/lua/automelt.lua index 395934867..cbd1bd538 100644 --- a/plugins/lua/automelt.lua +++ b/plugins/lua/automelt.lua @@ -66,7 +66,7 @@ function getItemCountsAndStockpileConfigs() ret.premarked_item_counts = table.remove(data, 1) local unparsed_stockpile_configs = table.remove(data, 1) ret.stockpile_configs = {} - + for idx,c in pairs(unparsed_stockpile_configs) do if not c.id or c.id == -1 then c.name = "ERROR" @@ -79,7 +79,7 @@ function getItemCountsAndStockpileConfigs() c.monitored = c.monitored ~= 0 end table.insert(ret.stockpile_configs, c) - + end return ret end From 5d8d378917c75c61fed72a6e37009838edee4ec5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 8 Feb 2023 12:03:40 -0800 Subject: [PATCH 0522/2222] Implements plugin: channel-safely v1.2.3 - Revises a few log lines - Adds d_assert macro to replace assert usage - prints an error to indicate d_assert failed in Release builds as well as Debug builds - could be added to the Debug utilities to allow use of assertions on necessary code without needing to buffer the results for use in the assert statement - Fixes bug wherein designations are never put into marker mode (related to the assert statements) --- plugins/channel-safely/channel-manager.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/channel-safely/channel-manager.cpp b/plugins/channel-safely/channel-manager.cpp index c22bd8a8e..aa7a24461 100644 --- a/plugins/channel-safely/channel-manager.cpp +++ b/plugins/channel-safely/channel-manager.cpp @@ -5,7 +5,17 @@ #include //hash function for df::coord #include +#define NUMARGS(...) std::tuple_size::value +#define d_assert(condition, ...) \ + static_assert(NUMARGS(__VA_ARGS__) >= 1, "d_assert(condition, format, ...) requires at least up to format as arguments"); \ + if (!condition) { \ + DFHack::Core::getInstance().getConsole().printerr(__VA_ARGS__); \ + assert(0); \ + } + + df::unit* find_dwarf(const df::coord &map_pos) { + df::unit* nearest = nullptr; uint32_t distance; for (auto unit : df::global::world->units.active) { @@ -57,6 +67,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool // cavein prevention bool cavein_possible = false; uint8_t least_access = 100; + std::unordered_map cavein_candidates; if (!marker_mode) { /* To prevent cave-ins we're looking at accessibility of tiles with open space below them @@ -111,7 +122,7 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool // if no cave-in is possible [or we don't check for], we'll just execute normally and move on if (!cavein_possible) { TRACE(manager).print("cave-in evaluated false\n"); - assert(manage_one(pos, true, marker_mode)); + d_assert(manage_one(pos, true, marker_mode), "manage_one() is failing under !cavein"); continue; } // cavein is only possible if marker_mode is false @@ -136,16 +147,16 @@ void ChannelManager::manage_group(const Group &group, bool set_marker_mode, bool evT->priority[Coord(local)] = v; } } - assert(manage_one(pos, true, false)); + d_assert(manage_one(pos, true, false), "manage_one() is failing for cavein "); continue; } // cavein possible, but we failed to meet the criteria for activation if (cavein_candidates.count(pos)) { - DEBUG(manager).print("cave-in evaluated true and no dignow and (%d > %d)\n", cavein_candidates[pos], least_access+OFFSET); + DEBUG(manager).print("cave-in evaluated true and the cavein candidate's accessibility check was made as (%d <= %d)\n", cavein_candidates[pos], least_access+OFFSET); } else { - DEBUG(manager).print("cave-in evaluated true and no dignow and pos is not a candidate\n"); + DEBUG(manager).print("cave-in evaluated true and the position was not a candidate, nor was it set for dignow\n"); } - assert(manage_one(pos, true, true)); + d_assert(manage_one(pos, true, true), "manage_one() is failing to set a cave-in causing designation to marker mode"); } INFO(manager).print("manage_group() is done\n"); } From caeb6d2f84786b953b1d7400669cc9306b5879af Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 8 Feb 2023 12:09:32 -0800 Subject: [PATCH 0523/2222] Updates changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ece6f04e1..f8d8e041b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Units::isFortControlled``: Account for agitated wildlife - Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible +- `channel-safely`: fixed bug resulting in marker mode never being set for any designation ## Misc Improvements - `automelt`: is now more resistent to savegame corruption From cd12f95849be5c519b83a15cc8bf1ceb6a64cea0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 12:15:19 -0800 Subject: [PATCH 0524/2222] add const & to Push overloads; remove circular def --- library/LuaApi.cpp | 2 +- library/LuaTools.cpp | 8 ++++---- library/include/LuaTools.h | 39 +++++++++++++++++++++----------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 45ea7d84d..69d43821e 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -478,7 +478,7 @@ static void OpenPersistent(lua_State *state) static int DFHACK_MATINFO_TOKEN = 0; -void Lua::Push(lua_State *state, MaterialInfo &info) +void Lua::Push(lua_State *state, const MaterialInfo &info) { if (!info.isValid()) { diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index b4d009c7b..ed0b0699d 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -101,7 +101,7 @@ void DFHack::Lua::Push(lua_State *state, const Units::NoblePosition &pos) lua_setfield(state, -2, "position"); } -void DFHack::Lua::Push(lua_State *state, df::coord pos) +void DFHack::Lua::Push(lua_State *state, const df::coord &pos) { lua_createtable(state, 0, 3); lua_pushinteger(state, pos.x); @@ -112,7 +112,7 @@ void DFHack::Lua::Push(lua_State *state, df::coord pos) lua_setfield(state, -2, "z"); } -void DFHack::Lua::Push(lua_State *state, df::coord2d pos) +void DFHack::Lua::Push(lua_State *state, const df::coord2d &pos) { lua_createtable(state, 0, 2); lua_pushinteger(state, pos.x); @@ -191,7 +191,7 @@ void DFHack::Lua::PushInterfaceKeys(lua_State *L, } } -int DFHack::Lua::PushPosXYZ(lua_State *state, df::coord pos) +int DFHack::Lua::PushPosXYZ(lua_State *state, const df::coord &pos) { if (!pos.isValid()) { @@ -207,7 +207,7 @@ int DFHack::Lua::PushPosXYZ(lua_State *state, df::coord pos) } } -int DFHack::Lua::PushPosXY(lua_State *state, df::coord2d pos) +int DFHack::Lua::PushPosXY(lua_State *state, const df::coord2d &pos) { if (!pos.isValid()) { diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index ca9aac788..86d3d0c7e 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -325,10 +325,10 @@ namespace DFHack {namespace Lua { inline void Push(lua_State *state, const std::string &str) { lua_pushlstring(state, str.data(), str.size()); } - DFHACK_EXPORT void Push(lua_State *state, df::coord obj); - DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); + DFHACK_EXPORT void Push(lua_State *state, const df::coord &obj); + DFHACK_EXPORT void Push(lua_State *state, const df::coord2d &obj); void Push(lua_State *state, const Units::NoblePosition &pos); - DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info); + DFHACK_EXPORT void Push(lua_State *state, const MaterialInfo &info); DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info); template inline void Push(lua_State *state, T *ptr) { PushDFObject(state, ptr); @@ -361,29 +361,34 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec); - DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); - DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); - - template - inline void TableInsert(lua_State *state, T_Key key, T_Value value) - { - Lua::Push(state, key); - Lua::Push(state, value); - lua_settable(state, -3); - } + DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos); + DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos); template void Push(lua_State *L, const std::map &pmap) { lua_createtable(L, 0, pmap.size()); - for (auto &entry : pmap) - TableInsert(L, entry.first, entry.second); + for (auto &entry : pmap) { + Lua::Push(L, entry.first); + Lua::Push(L, entry.second); + lua_settable(L, -3); + } } template void Push(lua_State *L, const std::unordered_map &pmap) { lua_createtable(L, 0, pmap.size()); - for (auto &entry : pmap) - TableInsert(L, entry.first, entry.second); + for (auto &entry : pmap) { + Lua::Push(L, entry.first); + Lua::Push(L, entry.second); + lua_settable(L, -3); + } + } + + template + inline void TableInsert(lua_State *state, const T_Key &key, const T_Value &value) { + Lua::Push(state, key); + Lua::Push(state, value); + lua_settable(state, -3); } DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true); From 1e4a73007ebd68c1fea81b01d90fe2752378c9fe Mon Sep 17 00:00:00 2001 From: Eamon Bode Date: Wed, 8 Feb 2023 15:44:55 -0500 Subject: [PATCH 0525/2222] Apply suggestions from code review Whitespace fixes Co-authored-by: Myk --- plugins/autochop.cpp | 4 ---- plugins/automelt.cpp | 3 --- 2 files changed, 7 deletions(-) diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 7a16a79b4..3a433f7a9 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -816,7 +816,6 @@ static void emplace_bulk_burrow_config(lua_State *L, map> &burrows, PersistentDataItem &c) { @@ -826,8 +825,6 @@ static void emplace_bulk_burrow_config(lua_State *L, map> &stockpiles) { - map stockpile_config; stockpile_config.emplace("id", id); stockpile_config.emplace("monitored", monitored); @@ -751,8 +750,6 @@ static int automelt_getSelectedStockpileConfig(lua_State *L){ return 1; } - - static int automelt_getItemCountsAndStockpileConfigs(lua_State *L) { color_ostream *out = Lua::GetOutput(L); if (!out) From 5a5fcbd1346b49734deecc895b4115d27c23b343 Mon Sep 17 00:00:00 2001 From: eamondo2 Date: Wed, 8 Feb 2023 15:52:58 -0500 Subject: [PATCH 0526/2222] Add changes to changelog.txt --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index daf0e8e1d..18635ca2b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible - `channel-safely`: fixed bug resulting in marker mode never being set for any designation +- `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs +- `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs ## Misc Improvements - `automelt`: is now more resistent to savegame corruption From 9f76d64e420a1e78557bfadb4760f369afc8dbec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:02:44 -0800 Subject: [PATCH 0527/2222] update misery; persist state --- docs/plugins/misery.rst | 33 +++- plugins/CMakeLists.txt | 2 +- plugins/lua/misery.lua | 43 +++++ plugins/misery.cpp | 352 ++++++++++++++++++++++++---------------- 4 files changed, 285 insertions(+), 145 deletions(-) create mode 100644 plugins/lua/misery.lua diff --git a/docs/plugins/misery.rst b/docs/plugins/misery.rst index f2c4d1952..8a65d8419 100644 --- a/docs/plugins/misery.rst +++ b/docs/plugins/misery.rst @@ -2,18 +2,35 @@ misery ====== .. dfhack-tool:: - :summary: Increase the intensity of negative dwarven thoughts. - :tags: fort armok auto units + :summary: Increase the intensity of your citizens' negative thoughts. + :tags: fort gameplay units -When enabled, negative thoughts that your dwarves have will multiply by the -specified factor. +When enabled, negative thoughts that your citizens have will multiply by the +specified factor. This makes it more challenging to keep them happy. Usage ----- +:: + + enable misery + misery [status] + misery + misery clear + +The default misery factor is ``2``, meaning that your dwarves will become +miserable twice as fast. + +Examples +-------- + ``enable misery`` - Start multiplying negative thoughts. -``misery `` - Change the multiplicative factor of bad thoughts. The default is ``2``. + Start multiplying bad thoughts for your citizens! + +``misery 5`` + Make dwarves become unhappy 5 times faster than normal -- this is quite + challenging to handle! + ``misery clear`` - Clear away negative thoughts added by ``misery``. + Clear away negative thoughts added by ``misery``. Note that this will not + clear negative thoughts that your dwarves accumulated "naturally". diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index df49c3d59..89733bf20 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -130,7 +130,7 @@ dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) #dfhack_plugin(manipulator manipulator.cpp) #dfhack_plugin(map-render map-render.cpp LINK_LIBRARIES lua) -dfhack_plugin(misery misery.cpp) +dfhack_plugin(misery misery.cpp LINK_LIBRARIES lua) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) diff --git a/plugins/lua/misery.lua b/plugins/lua/misery.lua new file mode 100644 index 000000000..cd507c9b3 --- /dev/null +++ b/plugins/lua/misery.lua @@ -0,0 +1,43 @@ +local _ENV = mkmodule('plugins.misery') + +local argparse = require('argparse') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +function status() + print(('misery is %s'):format(isEnabled() and "enabled" or "disabled")) + print(('misery factor is: %d'):format(misery_getFactor())) +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = table.remove(positionals, 1) + if not command or command == 'status' then + status() + elseif command == 'factor' then + misery_setFactor(positionals[1]) + elseif command == 'clear' then + misery_clear() + else + return false + end + + return true +end + +return _ENV diff --git a/plugins/misery.cpp b/plugins/misery.cpp index 870c4480c..f241394ae 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -1,14 +1,7 @@ #include -#include #include #include -#include "DataDefs.h" -#include "Export.h" -#include "PluginManager.h" - -#include "modules/Units.h" - #include "df/emotion_type.h" #include "df/plotinfost.h" #include "df/unit.h" @@ -17,179 +10,266 @@ #include "df/unit_thought_type.h" #include "df/world.h" -using namespace std; +#include "modules/Persistence.h" +#include "modules/Units.h" +#include "modules/World.h" + +#include "Core.h" +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" + +using std::string; +using std::vector; + using namespace DFHack; DFHACK_PLUGIN("misery"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(cur_year); REQUIRE_GLOBAL(cur_year_tick); +REQUIRE_GLOBAL(world); -typedef df::unit_personality::T_emotions Emotion; - -static int factor = 1; -static int tick = 0; -const int INTERVAL = 1000; +namespace DFHack { + DBG_DECLARE(misery, cycle, DebugCategory::LINFO); + DBG_DECLARE(misery, config, DebugCategory::LINFO); +} -command_result misery(color_ostream& out, vector& parameters); -void add_misery(df::unit *unit); -void clear_misery(df::unit *unit); +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static PersistentDataItem config; -const int FAKE_EMOTION_FLAG = (1 << 30); -const int STRENGTH_MULTIPLIER = 100; +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_FACTOR = 1, +}; -bool is_valid_unit (df::unit *unit) { - if (!Units::isOwnRace(unit) || !Units::isOwnCiv(unit)) - return false; - if (!Units::isActive(unit)) - return false; - return true; +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); } -inline bool is_fake_emotion (Emotion *e) { - return e->flags.whole & FAKE_EMOTION_FLAG; +static const int32_t CYCLE_TICKS = 1200; // one day +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, vector ¶meters); +static void do_cycle(color_ostream &out); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(config,out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Increase the intensity of negative dwarven thoughts.", + do_command)); + + return CR_OK; } -void add_misery (df::unit *unit) { - // Add a fake miserable thought - // Remove any fake ones that already exist - if (!unit || !unit->status.current_soul) - return; - clear_misery(unit); - auto &emotions = unit->status.current_soul->personality.emotions; - Emotion *e = new Emotion; - e->type = df::emotion_type::MISERY; - e->thought = df::unit_thought_type::SoapyBath; - e->flags.whole |= FAKE_EMOTION_FLAG; - emotions.push_back(e); +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } - for (Emotion *e : emotions) { - if (is_fake_emotion(e)) { - e->year = *cur_year; - e->year_tick = *cur_year_tick; - e->strength = STRENGTH_MULTIPLIER * factor; - e->severity = STRENGTH_MULTIPLIER * factor; - } + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(config,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); + } else { + DEBUG(config,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); } + return CR_OK; } -void clear_misery (df::unit *unit) { - if (!unit || !unit->status.current_soul) - return; - auto &emotions = unit->status.current_soul->personality.emotions; - auto it = remove_if(emotions.begin(), emotions.end(), [](Emotion *e) { - if (is_fake_emotion(e)) { - delete e; - return true; - } - return false; - }); - emotions.erase(it, emotions.end()); -} +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); -DFhackCExport command_result plugin_shutdown(color_ostream& out) { - factor = 0; return CR_OK; } -DFhackCExport command_result plugin_onupdate(color_ostream& out) { - static bool wasLoaded = false; - if ( factor == 0 || !world || !world->map.block_index ) { - if ( wasLoaded ) { - //we just unloaded the game: clear all data - factor = 0; - is_enabled = false; - wasLoaded = false; - } - return CR_OK; - } +DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; + config = World::GetPersistentData(CONFIG_KEY); - if ( !wasLoaded ) { - wasLoaded = true; + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + set_config_val(config, CONFIG_FACTOR, 2); } - if ( tick < INTERVAL ) { - tick++; - return CR_OK; - } - tick = 0; + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); - //TODO: consider units.active - for (df::unit *unit : world->units.all) { - if (is_valid_unit(unit)) { - add_misery(unit); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; } } - return CR_OK; } -DFhackCExport command_result plugin_init(color_ostream& out, vector &commands) { - commands.push_back(PluginCommand( - "misery", - "Increase the intensity of negative dwarven thoughts.", - misery)); +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) + do_cycle(out); return CR_OK; } -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (enable != is_enabled) - { - is_enabled = enable; - factor = enable ? 1 : 0; - tick = INTERVAL; - } +static bool call_misery_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(config).print("calling misery lua function: '%s'\n", fn_name); - return CR_OK; + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.misery", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); } -command_result misery(color_ostream &out, vector& parameters) { - if ( !world || !world->map.block_index ) { - out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); return CR_FAILURE; } - if ( parameters.size() < 1 || parameters.size() > 2 ) { - return CR_WRONG_USAGE; + bool show_help = false; + if (!call_misery_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; } - if ( parameters[0] == "disable" ) { - if ( parameters.size() > 1 ) { - return CR_WRONG_USAGE; - } - factor = 0; - is_enabled = false; - return CR_OK; - } else if ( parameters[0] == "enable" ) { - is_enabled = true; - factor = 1; - if ( parameters.size() == 2 ) { - int a = atoi(parameters[1].c_str()); - if ( a < 1 ) { - out.printerr("Second argument must be a positive integer.\n"); - return CR_WRONG_USAGE; - } - factor = a; - } - tick = INTERVAL; - } else if ( parameters[0] == "clear" ) { - for (df::unit *unit : world->units.all) { - if (is_valid_unit(unit)) { - clear_misery(unit); - } - } - } else { - int a = atoi(parameters[0].c_str()); - if ( a < 0 ) { - return CR_WRONG_USAGE; + return show_help ? CR_WRONG_USAGE : CR_OK; +} + +///////////////////////////////////////////////////// +// cycle logic +// + +const int FAKE_EMOTION_FLAG = (1 << 30); +const int STRENGTH_MULTIPLIER = 100; + +typedef df::unit_personality::T_emotions Emotion; + +static bool is_fake_emotion(Emotion *e) { + return e->flags.whole & FAKE_EMOTION_FLAG; +} + +static void clear_misery(df::unit *unit) { + if (!unit || !unit->status.current_soul) + return; + auto &emotions = unit->status.current_soul->personality.emotions; + auto it = std::remove_if(emotions.begin(), emotions.end(), [](Emotion *e) { + if (is_fake_emotion(e)) { + delete e; + return true; } - factor = a; - is_enabled = factor > 0; + return false; + }); + emotions.erase(it, emotions.end()); +} +// clears fake negative thoughts then runs the given lambda +static void affect_units( + std::function &&process_unit = [](df::unit *){}) { + for (auto unit : world->units.active) { + if (!Units::isCitizen(unit) || !unit->status.current_soul) + continue; + + clear_misery(unit); + std::forward &&>(process_unit)(unit); } +} - return CR_OK; +static void do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; + + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); + + int strength = STRENGTH_MULTIPLIER * get_config_val(config, CONFIG_FACTOR); + + affect_units([&](df::unit *unit) { + Emotion *e = new Emotion; + e->type = df::emotion_type::MISERY; + e->thought = df::unit_thought_type::SoapyBath; + e->flags.whole |= FAKE_EMOTION_FLAG; + e->year = *cur_year; + e->year_tick = *cur_year_tick; + e->strength = strength; + e->severity = strength; + unit->status.current_soul->personality.emotions.push_back(e); + }); } + +///////////////////////////////////////////////////// +// Lua API +// + +static void misery_clear(color_ostream &out) { + DEBUG(config,out).print("entering misery_clear\n"); + affect_units(); +} + +static void misery_setFactor(color_ostream &out, int32_t factor) { + DEBUG(config,out).print("entering misery_setFactor\n"); + if (1 >= factor) { + out.printerr("factor must be at least 2\n"); + return; + } + set_config_val(config, CONFIG_FACTOR, factor); + if (is_enabled) + do_cycle(out); +} + +static int misery_getFactor(color_ostream &out) { + DEBUG(config,out).print("entering tailor_getFactor\n"); + return get_config_val(config, CONFIG_FACTOR); +} + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(misery_clear), + DFHACK_LUA_FUNCTION(misery_setFactor), + DFHACK_LUA_FUNCTION(misery_getFactor), + DFHACK_LUA_END +}; From 6a04b577b0140eec1d514a3c3d6a0f94c6a7338b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:03:22 -0800 Subject: [PATCH 0528/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 18635ca2b..ad8763899 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `automelt`: is now more resistent to savegame corruption - `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) +- `misery`: now persists state with the fort - `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K - `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H - `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) From 5c84d180011c06f4f4f36032f023984953aa0044 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Feb 2023 18:38:16 -0800 Subject: [PATCH 0529/2222] update tailor, persist state, use best practices --- docs/plugins/tailor.rst | 28 ++- plugins/CMakeLists.txt | 2 +- plugins/lua/tailor.lua | 56 +++++ plugins/tailor.cpp | 527 +++++++++++++++++++++------------------- 4 files changed, 350 insertions(+), 263 deletions(-) create mode 100644 plugins/lua/tailor.lua diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index 4dc4f53a4..0e8980948 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -5,16 +5,15 @@ tailor :summary: Automatically keep your dwarves in fresh clothing. :tags: fort auto workorders -Whenever the bookkeeper updates stockpile records, this plugin will scan the -fort. If there are fresh cloths available, dwarves who are wearing tattered -clothing will have their rags confiscated (in the same manner as the -`cleanowned` tool) so that they'll reequip with replacement clothes. +Once a day, this plugin will scan the clothing situation in the fort. If there +are fresh cloths available, dwarves who are wearing tattered clothing will have +their rags confiscated (in the same manner as the `cleanowned` tool) so that +they'll reequip with replacement clothes. -If there are not enough clothes available, manager orders will be generated -to manufacture some more. ``tailor`` will intelligently create orders using -raw materials that you have on hand in the fort. For example, if you have -lots of silk, but no cloth, then ``tailor`` will order only silk clothing to -be made. +If there are not enough clothes available, manager orders will be generated to +manufacture some more. ``tailor`` will intelligently create orders using raw +materials that you have on hand in the fort. For example, if you have lots of +silk, but no cloth, then ``tailor`` will order only silk clothing to be made. Usage ----- @@ -22,7 +21,8 @@ Usage :: enable tailor - tailor status + tailor [status] + tailor now tailor materials [ ...] By default, ``tailor`` will prefer using materials in this order:: @@ -32,12 +32,16 @@ By default, ``tailor`` will prefer using materials in this order:: but you can use the ``tailor materials`` command to restrict which materials are used, and in what order. -Example -------- +Examples +-------- ``enable tailor`` Start replacing tattered clothes with default settings. +``tailor now`` + Run a scan and order cycle right now, regardless of whether the plugin is + enabled. + ``tailor materials silk cloth yarn`` Restrict the materials used for automatically manufacturing clothing to silk, cloth, and yarn, preferred in that order. This saves leather for diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index df49c3d59..7e8258aeb 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -159,7 +159,7 @@ dfhack_plugin(showmood showmood.cpp) #add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(strangemood strangemood.cpp) -dfhack_plugin(tailor tailor.cpp) +dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(title-version title-version.cpp) diff --git a/plugins/lua/tailor.lua b/plugins/lua/tailor.lua new file mode 100644 index 000000000..5c748fbfa --- /dev/null +++ b/plugins/lua/tailor.lua @@ -0,0 +1,56 @@ +local _ENV = mkmodule('plugins.tailor') + +local argparse = require('argparse') +local utils = require('utils') + +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true + return + end + + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + }) +end + +function status() + print(('tailor is %s'):format(enabled and "enabled" or "disabled")) + print('materials preference order:') + for _,name in ipairs(tailor_getMaterialPreferences()) do + print((' %s'):format(name)) + end +end + +function setMaterials(names) + local idxs = utils.invert(names) + tailor_setMaterialPreferences( + idxs.silk or -1, + idxs.cloth or -1, + idxs.yarn or -1, + idxs.leather or -1) +end + +function parse_commandline(...) + local args, opts = {...}, {} + local positionals = process_args(opts, args) + + if opts.help then + return false + end + + local command = table.remove(positionals, 1) + if not command or command == 'status' then + status() + elseif command == 'now' then + tailor_doCycle() + elseif command == 'materials' then + setMaterials(positionals) + else + return false + end + + return true +end + +return _ENV diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 2cca8a2c8..b35f434b8 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -1,136 +1,161 @@ /* * Tailor plugin. Automatically manages keeping your dorfs clothed. - * For best effect, place "tailor enable" in your dfhack.init configuration, - * or set AUTOENABLE to true. */ -#include "Core.h" -#include "DataDefs.h" -#include "Debug.h" -#include "PluginManager.h" +#include +#include +#include #include "df/creature_raw.h" -#include "df/global_objects.h" #include "df/historical_entity.h" +#include "df/item.h" +#include "df/item_flags.h" #include "df/itemdef_armorst.h" #include "df/itemdef_glovesst.h" #include "df/itemdef_helmst.h" #include "df/itemdef_pantsst.h" #include "df/itemdef_shoesst.h" #include "df/items_other_id.h" -#include "df/job.h" -#include "df/job_type.h" #include "df/manager_order.h" #include "df/plotinfost.h" #include "df/world.h" -#include "modules/Maps.h" -#include "modules/Units.h" +#include "Core.h" +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" + +#include "modules/Materials.h" +#include "modules/Persistence.h" #include "modules/Translation.h" +#include "modules/Units.h" #include "modules/World.h" -using namespace DFHack; +using std::string; +using std::vector; -using df::global::world; -using df::global::plotinfo; +using namespace DFHack; DFHACK_PLUGIN("tailor"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); -#define AUTOENABLE false -DFHACK_PLUGIN_IS_ENABLED(enabled); - -REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(standing_orders_use_dyed_cloth); +REQUIRE_GLOBAL(world); namespace DFHack { DBG_DECLARE(tailor, cycle, DebugCategory::LINFO); DBG_DECLARE(tailor, config, DebugCategory::LINFO); } -class Tailor { - // ARMOR, SHOES, HELM, GLOVES, PANTS +static const string CONFIG_KEY = string(plugin_name) + "/config"; +static PersistentDataItem config; - // ah, if only STL had a bimap +enum ConfigValues { + CONFIG_IS_ENABLED = 0, + CONFIG_SILK_IDX = 1, + CONFIG_CLOTH_IDX = 2, + CONFIG_YARN_IDX = 3, + CONFIG_LEATHER_IDX = 4, +}; -private: +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) + return -1; + return c.ival(index); +} +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} - const std::map jobTypeMap = { - { df::job_type::MakeArmor, df::item_type::ARMOR }, - { df::job_type::MakePants, df::item_type::PANTS }, - { df::job_type::MakeHelm, df::item_type::HELM }, - { df::job_type::MakeGloves, df::item_type::GLOVES }, - { df::job_type::MakeShoes, df::item_type::SHOES } - }; - - const std::map itemTypeMap = { - { df::item_type::ARMOR, df::job_type::MakeArmor }, - { df::item_type::PANTS, df::job_type::MakePants }, - { df::item_type::HELM, df::job_type::MakeHelm }, - { df::item_type::GLOVES, df::job_type::MakeGloves }, - { df::item_type::SHOES, df::job_type::MakeShoes } - }; - -#define F(x) df::item_flags::mask_##x - const df::item_flags bad_flags = { - ( - F(dump) | F(forbid) | F(garbage_collect) | - F(hostile) | F(on_fire) | F(rotten) | F(trader) | - F(in_building) | F(construction) | F(owned) - ) - #undef F - }; - - class MatType { - - public: - std::string name; - df::job_material_category job_material; - df::armor_general_flags armor_flag; - - bool operator==(const MatType& m) const - { - return name == m.name; - } +static const int32_t CYCLE_TICKS = 1200; // one day +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - // operator< is required to use this as a std::map key - bool operator<(const MatType& m) const - { - return name < m.name; - } +// ah, if only STL had a bimap +static const std::map jobTypeMap = { + { df::job_type::MakeArmor, df::item_type::ARMOR }, + { df::job_type::MakePants, df::item_type::PANTS }, + { df::job_type::MakeHelm, df::item_type::HELM }, + { df::job_type::MakeGloves, df::item_type::GLOVES }, + { df::job_type::MakeShoes, df::item_type::SHOES } +}; - MatType(std::string& n, df::job_material_category jm, df::armor_general_flags af) - : name(n), job_material(jm), armor_flag(af) {}; - MatType(const char* n, df::job_material_category jm, df::armor_general_flags af) - : name(std::string(n)), job_material(jm), armor_flag(af) {}; +static const std::map itemTypeMap = { + { df::item_type::ARMOR, df::job_type::MakeArmor }, + { df::item_type::PANTS, df::job_type::MakePants }, + { df::item_type::HELM, df::job_type::MakeHelm }, + { df::item_type::GLOVES, df::job_type::MakeGloves }, + { df::item_type::SHOES, df::job_type::MakeShoes } +}; + +class MatType { +public: + const std::string name; + const df::job_material_category job_material; + const df::armor_general_flags armor_flag; + + bool operator==(const MatType& m) const { + return name == m.name; + } - }; + // operator< is required to use this as a std::map key + bool operator<(const MatType& m) const { + return name < m.name; + } - const MatType - M_SILK = MatType("silk", df::job_material_category::mask_silk, df::armor_general_flags::SOFT), - M_CLOTH = MatType("cloth", df::job_material_category::mask_cloth, df::armor_general_flags::SOFT), - M_YARN = MatType("yarn", df::job_material_category::mask_yarn, df::armor_general_flags::SOFT), - M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER); + MatType(std::string& n, df::job_material_category jm, df::armor_general_flags af) + : name(n), job_material(jm), armor_flag(af) {}; + MatType(const char* n, df::job_material_category jm, df::armor_general_flags af) + : name(std::string(n)), job_material(jm), armor_flag(af) {}; +}; - std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; +static const MatType + M_SILK = MatType("silk", df::job_material_category::mask_silk, df::armor_general_flags::SOFT), + M_CLOTH = MatType("cloth", df::job_material_category::mask_cloth, df::armor_general_flags::SOFT), + M_YARN = MatType("yarn", df::job_material_category::mask_yarn, df::armor_general_flags::SOFT), + M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER); + +static const std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; +static std::list material_order = all_materials; + +static struct BadFlags { + uint32_t whole; + + BadFlags() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(owned); + F(in_chest); F(removed); F(encased); + F(spider_web); + #undef F + whole = flags.whole; + } +} badFlags; +class Tailor { +private: std::map, int> available; // key is item type & size std::map, int> needed; // same std::map, int> queued; // same std::map sizes; // this maps body size to races - std::map, int> orders; // key is item type, item subtype, size std::map supply; - - color_ostream* out; - - std::list material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; std::map reserves; int default_reserve = 10; +public: void reset() { available.clear(); @@ -145,9 +170,7 @@ private: { for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" { - if (i->flags.whole & bad_flags.whole) - continue; - if (i->flags.bits.owned) + if (i->flags.whole & badFlags.whole) continue; if (i->getWear() >= 1) continue; @@ -164,7 +187,7 @@ private: for (auto i : world->items.other[df::items_other_id::CLOTH]) { - if (i->flags.whole & bad_flags.whole) + if (i->flags.whole & badFlags.whole) continue; if (require_dyed && !i->hasImprovements()) @@ -197,7 +220,7 @@ private: for (auto i : world->items.other[df::items_other_id::SKIN_TANNED]) { - if (i->flags.whole & bad_flags.whole) + if (i->flags.whole & badFlags.whole) continue; supply[M_LEATHER] += i->getStackSize(); } @@ -369,8 +392,9 @@ private: } - void place_orders() + int place_orders() { + int ordered = 0; auto entity = world->entities.all[plotinfo->civ_id]; for (auto& o : orders) @@ -477,6 +501,7 @@ private: ); count -= c; + ordered += c; } else { @@ -486,215 +511,217 @@ private: } } } + return ordered; } +}; -public: - void do_scan(color_ostream& o) - { - out = &o; - - reset(); - - // scan for useable clothing +static std::unique_ptr tailor_instance; - scan_clothing(); +static command_result do_command(color_ostream &out, vector ¶meters); +static int do_cycle(color_ostream &out); - // scan for clothing raw materials +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(config,out).print("initializing %s\n", plugin_name); - scan_materials(); + tailor_instance = dts::make_unique(); - // scan for units who need replacement clothing + // provide a configuration interface for the plugin + commands.push_back(PluginCommand( + plugin_name, + "Automatically keep your dwarves in fresh clothing.", + do_command)); - scan_replacements(); + return CR_OK; +} - // create new orders +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } - create_orders(); + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(config,out).print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) + do_cycle(out); + } else { + DEBUG(config,out).print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} - // scan existing orders and subtract +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + DEBUG(config,out).print("shutting down %s\n", plugin_name); - scan_existing_orders(); + tailor_instance.release(); - // place orders + return CR_OK; +} - place_orders(); +static void set_material_order() { + material_order.clear(); + for (int i = 0; i < all_materials.size(); ++i) { + if (i == get_config_val(config, CONFIG_SILK_IDX)) + material_order.push_back(M_SILK); + else if (i == get_config_val(config, CONFIG_CLOTH_IDX)) + material_order.push_back(M_CLOTH); + else if (i == get_config_val(config, CONFIG_YARN_IDX)) + material_order.push_back(M_YARN); + else if (i == get_config_val(config, CONFIG_LEATHER_IDX)) + material_order.push_back(M_LEATHER); } + if (!material_order.size()) + std::copy(all_materials.begin(), all_materials.end(), std::back_inserter(material_order)); +} -public: - command_result set_materials(color_ostream& out, std::vector& parameters) - { - std::list newmat; - newmat.clear(); - - for (auto m = parameters.begin() + 1; m != parameters.end(); m++) - { - auto nameMatch = [m](MatType& m1) { return *m == m1.name; }; - auto mm = std::find_if(all_materials.begin(), all_materials.end(), nameMatch); - if (mm == all_materials.end()) - { - WARN(config,out).print("tailor: material %s not recognized\n", m->c_str()); - return CR_WRONG_USAGE; - } - else { - newmat.push_back(*mm); - } - } - - material_order = newmat; - INFO(config,out).print("tailor: material list set to %s\n", get_material_list().c_str()); +DFhackCExport command_result plugin_load_data (color_ostream &out) { + cycle_timestamp = 0; + config = World::GetPersistentData(CONFIG_KEY); - return CR_OK; + if (!config.isValid()) { + DEBUG(config,out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); } -public: - std::string get_material_list() - { - std::string s; - for (const auto& m : material_order) - { - if (!s.empty()) s += ", "; - s += m.name; - } - return s; - } + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(config,out).print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + set_material_order(); -public: - void process(color_ostream& out) - { - bool found = false; + return CR_OK; +} - for (df::job_list_link* link = &world->jobs.list; link != NULL; link = link->next) - { - if (link->item == NULL) continue; - if (link->item->job_type == df::enums::job_type::UpdateStockpileRecords) - { - found = true; - break; - } +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(config,out).print("world unloaded; disabling %s\n", + plugin_name); + is_enabled = false; } + } + return CR_OK; +} - if (found) - { - do_scan(out); - } +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int ordered = do_cycle(out); + if (0 < ordered) + out.print("tailor: ordered %d items of clothing\n", ordered); } -}; + return CR_OK; +} -static std::unique_ptr tailor_instance; +static bool call_tailor_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(config).print("calling tailor lua function: '%s'\n", fn_name); -#define DELTA_TICKS 50 + CoreSuspender guard; -DFhackCExport command_result plugin_onupdate(color_ostream& out) -{ - if (!enabled || !tailor_instance) - return CR_OK; + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); - if (!Maps::IsValid()) - return CR_OK; + if (!out) + out = &Core::getInstance().getConsole(); - if (DFHack::World::ReadPauseState()) - return CR_OK; + return Lua::CallLuaModuleFunction(*out, L, "plugins.tailor", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} - if (world->frame_counter % DELTA_TICKS != 0) - return CR_OK; +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; - { - CoreSuspender suspend; - tailor_instance->process(out); + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot run %s without a loaded world.\n", plugin_name); + return CR_FAILURE; } - return CR_OK; + bool show_help = false; + if (!call_tailor_lua(&out, "parse_commandline", parameters.size(), 1, + [&](lua_State *L) { + for (const string ¶m : parameters) + Lua::Push(L, param); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; + } + + return show_help ? CR_WRONG_USAGE : CR_OK; } -static command_result tailor_cmd(color_ostream& out, std::vector & parameters) { - bool desired = enabled; - if (parameters.size() == 1 && (parameters[0] == "enable" || parameters[0] == "on" || parameters[0] == "1")) - { - desired = true; - } - else if (parameters.size() == 1 && (parameters[0] == "disable" || parameters[0] == "off" || parameters[0] == "0")) - { - desired = false; - } - else if (parameters.size() == 1 && (parameters[0] == "usage" || parameters[0] == "help" || parameters[0] == "?")) - { - return CR_WRONG_USAGE; - } - else if (parameters.size() == 1 && parameters[0] == "test") - { - if (tailor_instance) - { - tailor_instance->do_scan(out); - return CR_OK; - } - else - { - out.print("%s: not instantiated\n", plugin_name); - return CR_FAILURE; - } - } - else if (parameters.size() > 1 && parameters[0] == "materials") - { - if (tailor_instance) - { - return tailor_instance->set_materials(out, parameters); - } - else - { - out.print("%s: not instantiated\n", plugin_name); - return CR_FAILURE; - } - } - else if (parameters.size() == 1 && parameters[0] != "status") - { - return CR_WRONG_USAGE; - } +///////////////////////////////////////////////////// +// cycle logic +// - out.print("Tailor is %s %s.\n", (desired == enabled) ? "currently" : "now", desired ? "enabled" : "disabled"); - if (tailor_instance) - { - out.print("Material list is: %s\n", tailor_instance->get_material_list().c_str()); - } - else - { - out.print("%s: not instantiated\n", plugin_name); - } +static int do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; - enabled = desired; + DEBUG(cycle,out).print("running %s cycle\n", plugin_name); - return CR_OK; + tailor_instance->reset(); + tailor_instance->scan_clothing(); + tailor_instance->scan_materials(); + tailor_instance->scan_replacements(); + tailor_instance->create_orders(); + tailor_instance->scan_existing_orders(); + return tailor_instance->place_orders(); } +///////////////////////////////////////////////////// +// Lua API +// -DFhackCExport command_result plugin_onstatechange(color_ostream& out, state_change_event event) -{ - return CR_OK; +static void tailor_doCycle(color_ostream &out) { + DEBUG(config,out).print("entering tailor_doCycle\n"); + out.print("ordered %d items of clothing\n", do_cycle(out)); } -DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) -{ - enabled = enable; - return CR_OK; -} +// remember, these are ONE-based indices from Lua +static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx, + int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx) { + DEBUG(config,out).print("entering tailor_setMaterialPreferences\n"); -DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) -{ - tailor_instance = std::move(dts::make_unique()); + // it doesn't really matter if these are invalid. set_material_order will do + // the right thing. + set_config_val(config, CONFIG_SILK_IDX, silkIdx); + set_config_val(config, CONFIG_CLOTH_IDX, clothIdx); + set_config_val(config, CONFIG_YARN_IDX, yarnIdx); + set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx); - if (AUTOENABLE) { - enabled = true; - } + set_material_order(); +} - commands.push_back(PluginCommand( - plugin_name, - "Automatically keep your dwarves in fresh clothing.", - tailor_cmd)); - return CR_OK; +static int tailor_getMaterialPreferences(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(config,*out).print("entering tailor_getMaterialPreferences\n"); + vector names; + for (const auto& m : material_order) + names.emplace_back(m.name); + Lua::PushVector(L, names); + return 1; } -DFhackCExport command_result plugin_shutdown(color_ostream& out) -{ - tailor_instance.release(); +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(tailor_doCycle), + DFHACK_LUA_FUNCTION(tailor_setMaterialPreferences), + DFHACK_LUA_END +}; - return plugin_enable(out, false); -} +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(tailor_getMaterialPreferences), + DFHACK_LUA_END +}; From 4d4e94c44993b7ee3609a008267d0f6a6a1012eb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:01:38 -0800 Subject: [PATCH 0530/2222] convert index --- plugins/lua/tailor.lua | 2 +- plugins/tailor.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/lua/tailor.lua b/plugins/lua/tailor.lua index 5c748fbfa..bcfd8bc10 100644 --- a/plugins/lua/tailor.lua +++ b/plugins/lua/tailor.lua @@ -15,7 +15,7 @@ local function process_args(opts, args) end function status() - print(('tailor is %s'):format(enabled and "enabled" or "disabled")) + print(('tailor is %s'):format(isEnabled() and "enabled" or "disabled")) print('materials preference order:') for _,name in ipairs(tailor_getMaterialPreferences()) do print((' %s'):format(name)) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index b35f434b8..8b132397a 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -3,7 +3,6 @@ */ #include -#include #include #include "df/creature_raw.h" @@ -695,10 +694,10 @@ static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx, // it doesn't really matter if these are invalid. set_material_order will do // the right thing. - set_config_val(config, CONFIG_SILK_IDX, silkIdx); - set_config_val(config, CONFIG_CLOTH_IDX, clothIdx); - set_config_val(config, CONFIG_YARN_IDX, yarnIdx); - set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx); + set_config_val(config, CONFIG_SILK_IDX, silkIdx - 1); + set_config_val(config, CONFIG_CLOTH_IDX, clothIdx - 1); + set_config_val(config, CONFIG_YARN_IDX, yarnIdx - 1); + set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx - 1); set_material_order(); } From 8bd985359702e8cb388d09841b4b5b11eefbcdab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 14:16:11 -0800 Subject: [PATCH 0531/2222] make gcc happy --- library/xml | 2 +- plugins/tailor.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/xml b/library/xml index cd5baf4ea..eb58cda0f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit cd5baf4ea82fead3883368da526ac2b606a6209b +Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 8b132397a..5511e2b5d 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -564,14 +564,14 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { static void set_material_order() { material_order.clear(); - for (int i = 0; i < all_materials.size(); ++i) { - if (i == get_config_val(config, CONFIG_SILK_IDX)) + for (size_t i = 0; i < all_materials.size(); ++i) { + if (i == (size_t)get_config_val(config, CONFIG_SILK_IDX)) material_order.push_back(M_SILK); - else if (i == get_config_val(config, CONFIG_CLOTH_IDX)) + else if (i == (size_t)get_config_val(config, CONFIG_CLOTH_IDX)) material_order.push_back(M_CLOTH); - else if (i == get_config_val(config, CONFIG_YARN_IDX)) + else if (i == (size_t)get_config_val(config, CONFIG_YARN_IDX)) material_order.push_back(M_YARN); - else if (i == get_config_val(config, CONFIG_LEATHER_IDX)) + else if (i == (size_t)get_config_val(config, CONFIG_LEATHER_IDX)) material_order.push_back(M_LEATHER); } if (!material_order.size()) From 252a05ba11c51636c27d14b45f35146b0e910c73 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 9 Feb 2023 00:02:46 +0000 Subject: [PATCH 0532/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index eb58cda0f..eff493010 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit eb58cda0f106c20776c273516f010ce21c45f89d +Subproject commit eff493010d11358fc8243239dbf8d07024eedb0c diff --git a/scripts b/scripts index 54f5d5f3d..8807e7a78 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 54f5d5f3d3b68b07323e88109b9a44d98abe52b1 +Subproject commit 8807e7a7845611b2cc613fb3903f6f8763e91300 From a41afb88fc2649e65cf89e3659f81ff8ad507cfb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 16:37:46 -0800 Subject: [PATCH 0533/2222] remove in-progress jobs when protecting fert eggs --- docs/changelog.txt | 1 + plugins/nestboxes.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ad8763899..5470e1bd6 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `channel-safely`: fixed bug resulting in marker mode never being set for any designation - `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs - `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs +- `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg ## Misc Improvements - `automelt`: is now more resistent to savegame corruption diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index b67101614..5ce894802 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -1,6 +1,8 @@ #include "Debug.h" #include "PluginManager.h" +#include "modules/Items.h" +#include "modules/Job.h" #include "modules/Persistence.h" #include "modules/World.h" @@ -141,6 +143,13 @@ static void do_cycle(color_ostream &out) { df::item *item = contained_item->item; if (item->flags.bits.forbid != fertile) { item->flags.bits.forbid = fertile; + if (fertile && item->flags.bits.in_job) { + // cancel any job involving the egg + df::specific_ref *sref = Items::getSpecificRef( + item, df::specific_ref_type::JOB); + if (sref && sref->data.job) + Job::removeJob(sref->data.job); + } out.print("%d eggs %s.\n", item->getStackSize(), fertile ? "forbidden" : "unforbidden"); } } From 5e09a1cbf1aff08c63bd6c371d53ef2fb59fc1ee Mon Sep 17 00:00:00 2001 From: John Cosker Date: Wed, 8 Feb 2023 22:05:08 -0500 Subject: [PATCH 0534/2222] Change autoslab to use slab's "topic" field to compare historical id of slab instead of using str comp --- plugins/autoslab.cpp | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index e17ce72c8..7cb67beb3 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -19,6 +19,7 @@ #include "df/historical_figure.h" #include "df/item.h" +#include "df/item_slabst.h" #include "df/manager_order.h" #include "df/plotinfost.h" #include "df/unit.h" @@ -168,25 +169,6 @@ static std::string get_last_name(df::unit *unit) return Translation::capitalize(ret); } -// Couldn't figure out any other way to do this besides look for the dwarf name in -// the slab item description. -// Ideally, we could get the historical figure id from the slab but I didn't -// see anything like that in the item struct. This seems to work based on testing. -// Confirmed nicknames don't show up in engraved slab names, so this should probably work okay -bool engravedSlabItemExists(df::unit *unit, std::vector slabs) -{ - for (auto slab : slabs) - { - std::string desc = ""; - slab->getItemDescription(&desc, 0); - auto fullName = get_first_name(unit) + " " + get_last_name(unit); - if (desc.find(fullName) != std::string::npos) - return true; - } - - return false; -} - // Queue up a single order to engrave the slab for the given unit static void createSlabJob(df::unit *unit) { @@ -228,7 +210,14 @@ static void checkslabs(color_ostream &out) for (auto ghost : ghosts) { // Only create a job is the map has no existing jobs for that historical figure or no existing engraved slabs - if (histToJob.count(ghost->hist_figure_id) == 0 && !engravedSlabItemExists(ghost, engravedSlabs)) + if (histToJob.count(ghost->hist_figure_id) == 0 && + !std::any_of(engravedSlabs.begin(), + engravedSlabs.end(), + [&ghost](const auto &slab){ + auto slabst = virtual_cast(slab); + return slabst->topic == ghost->hist_figure_id; + }) + ) { createSlabJob(ghost); auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); From 1fd807fc4d657ebfff30c658260014ed99937a4d Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 8 Feb 2023 22:16:16 -0500 Subject: [PATCH 0535/2222] Mark as dev-only changes: #2848, #2842, #2816, #2792 --- docs/changelog.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ad8763899..c151e2ebc 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,12 +40,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible - `channel-safely`: fixed bug resulting in marker mode never being set for any designation -- `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs -- `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs +-@ `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs +-@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs ## Misc Improvements - `automelt`: is now more resistent to savegame corruption -- `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) +-@ `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) - `misery`: now persists state with the fort - `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K - `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H @@ -53,7 +53,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair - `autobutcher`: now immediately loads races available at game start into the watchlist -- replaced DFHack logo used for the hover hotspot with a crisper image +-@ replaced DFHack logo used for the hover hotspot with a crisper image - `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` ## Documentation @@ -72,7 +72,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # 50.05-alpha3.1 ## Fixes -- `seedwatch`: fix parameter parsing when setting targets +-@ `seedwatch`: fix parameter parsing when setting targets # 50.05-alpha3 From fed3b4ddb4a43308fc5093ef5139c5839d4edfc4 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Wed, 8 Feb 2023 22:25:35 -0500 Subject: [PATCH 0536/2222] Hopefully fix CI linux build --- plugins/autoslab.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 7cb67beb3..5f9c0732a 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -213,7 +213,7 @@ static void checkslabs(color_ostream &out) if (histToJob.count(ghost->hist_figure_id) == 0 && !std::any_of(engravedSlabs.begin(), engravedSlabs.end(), - [&ghost](const auto &slab){ + [&ghost](df::item *slab){ auto slabst = virtual_cast(slab); return slabst->topic == ghost->hist_figure_id; }) @@ -223,6 +223,10 @@ static void checkslabs(color_ostream &out) auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); out.print("Added slab order for ghost %s\n", fullName.c_str()); } + else { + auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); + out.print("%s doesn't need slab\n", fullName.c_str()); + } } } From 27ff6c9649553fc9f3c9291a95a6e0c75fba638c Mon Sep 17 00:00:00 2001 From: John Cosker Date: Wed, 8 Feb 2023 22:41:56 -0500 Subject: [PATCH 0537/2222] Remove debug logging --- plugins/autoslab.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 5f9c0732a..ec40227cd 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -223,10 +223,6 @@ static void checkslabs(color_ostream &out) auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); out.print("Added slab order for ghost %s\n", fullName.c_str()); } - else { - auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); - out.print("%s doesn't need slab\n", fullName.c_str()); - } } } From d27041e206125a24009b3c1f24de82fbff6b3be0 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Wed, 8 Feb 2023 22:42:28 -0500 Subject: [PATCH 0538/2222] Remove debug logging --- plugins/autoslab.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 5f9c0732a..ec40227cd 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -223,10 +223,6 @@ static void checkslabs(color_ostream &out) auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); out.print("Added slab order for ghost %s\n", fullName.c_str()); } - else { - auto fullName = get_first_name(ghost) + " " + get_last_name(ghost); - out.print("%s doesn't need slab\n", fullName.c_str()); - } } } From 7cc3c08926c60bcc88240940a1f82cb87f66d0f7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 9 Feb 2023 05:14:15 +0000 Subject: [PATCH 0539/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index eff493010..06b0ed2e9 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit eff493010d11358fc8243239dbf8d07024eedb0c +Subproject commit 06b0ed2e996860ac29419405b6668f1cb4fd3b97 diff --git a/scripts b/scripts index 8807e7a78..95a159d2a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 8807e7a7845611b2cc613fb3903f6f8763e91300 +Subproject commit 95a159d2af2038243776f5f8b6e5de1b77d52543 From 6795a4a2c87fb0503e2e52054b43da69e2991503 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Thu, 9 Feb 2023 08:28:33 -0500 Subject: [PATCH 0540/2222] Further simplify check for engraved slabs --- plugins/autoslab.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index ec40227cd..06c7d1c0f 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -193,13 +193,6 @@ static void checkslabs(color_ostream &out) histToJob[order->hist_figure_id] = order->id; } - // Get list of engraved slab items on map - std::vector engravedSlabs; - std::copy_if(world->items.all.begin(), world->items.all.end(), - std::back_inserter(engravedSlabs), - [](df::item *item) - { return item->getType() == df::item_type::SLAB && item->getSlabEngravingType() == df::slab_engraving_type::Memorial; }); - // Build list of ghosts std::vector ghosts; std::copy_if(world->units.all.begin(), world->units.all.end(), @@ -211,11 +204,10 @@ static void checkslabs(color_ostream &out) { // Only create a job is the map has no existing jobs for that historical figure or no existing engraved slabs if (histToJob.count(ghost->hist_figure_id) == 0 && - !std::any_of(engravedSlabs.begin(), - engravedSlabs.end(), - [&ghost](df::item *slab){ - auto slabst = virtual_cast(slab); - return slabst->topic == ghost->hist_figure_id; + !std::any_of(world->items.other.SLAB.begin(), + world->items.other.SLAB.end(), + [&ghost,&out](df::item_slabst *slab){ + return slab->engraving_type == df::slab_engraving_type::Memorial && slab->topic == ghost->hist_figure_id; }) ) { From f6ab1630b11cb9a2cb0a07cb83af35bac5ffe29c Mon Sep 17 00:00:00 2001 From: John Cosker Date: Thu, 9 Feb 2023 08:30:09 -0500 Subject: [PATCH 0541/2222] Cleanup --- plugins/autoslab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autoslab.cpp b/plugins/autoslab.cpp index 06c7d1c0f..e78314bfd 100644 --- a/plugins/autoslab.cpp +++ b/plugins/autoslab.cpp @@ -206,7 +206,7 @@ static void checkslabs(color_ostream &out) if (histToJob.count(ghost->hist_figure_id) == 0 && !std::any_of(world->items.other.SLAB.begin(), world->items.other.SLAB.end(), - [&ghost,&out](df::item_slabst *slab){ + [&ghost](df::item_slabst *slab){ return slab->engraving_type == df::slab_engraving_type::Memorial && slab->topic == ghost->hist_figure_id; }) ) From 991c6d89e8bd7696ec967523b485419613025785 Mon Sep 17 00:00:00 2001 From: vallode <18506096+vallode@users.noreply.github.com> Date: Thu, 9 Feb 2023 23:50:11 +0100 Subject: [PATCH 0542/2222] Fix annotation parse error --- library/lua/dfhack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index b931b2ff4..c8b15c191 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -659,7 +659,7 @@ function Script:get_flags() local f = io.open(self.path) local contents = f:read('*all') f:close() - for line in contents:gmatch('%-%-@([^\n]+)') do + for line in contents:gmatch('^%-%-@([^\n]+)') do local chunk = load(line, self.path, 't', self._flags) if chunk then chunk() From e160bd060735afdca05a98b9f673a7d7173da0ca Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 10 Feb 2023 07:15:13 +0000 Subject: [PATCH 0543/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 06b0ed2e9..f2f2ba3cd 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 06b0ed2e996860ac29419405b6668f1cb4fd3b97 +Subproject commit f2f2ba3cd8b4a45919bc597cb47cbb5994dc359c diff --git a/scripts b/scripts index 95a159d2a..d1ea2c1e8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 95a159d2af2038243776f5f8b6e5de1b77d52543 +Subproject commit d1ea2c1e835b89428c157f3121ef0e8858caccf6 From 533ccee0994aba9d88b3f465f0ca3a6dce94d5ad Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 10 Feb 2023 02:51:52 -0500 Subject: [PATCH 0544/2222] Detect manual save --- docs/changelog.txt | 1 + library/Core.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c54d929a9..813dbbb2e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs -@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs - `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg +- Fix persisted data not being written on manual save ## Misc Improvements - `automelt`: is now more resistent to savegame corruption diff --git a/library/Core.cpp b/library/Core.cpp index da560e17d..5375daa3c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -146,6 +146,7 @@ struct Core::Private std::thread hotkeythread; bool last_autosave_request{false}; + bool last_manual_save_request{false}; bool was_load_save{false}; }; @@ -1847,7 +1848,8 @@ void Core::doUpdate(color_ostream &out) strict_virtual_cast(screen); // save data (do this before updating last_world_data_ptr and triggering unload events) - if ((df::global::plotinfo->main.autosave_request && !d->last_autosave_request) || + if ((df::global::game->main_interface.options.do_manual_save && !d->last_manual_save_request) || + (df::global::plotinfo->main.autosave_request && !d->last_autosave_request) || (is_load_save && !d->was_load_save && strict_virtual_cast(screen))) { doSaveData(out); @@ -1910,6 +1912,7 @@ void Core::doUpdate(color_ostream &out) onUpdate(out); d->last_autosave_request = df::global::plotinfo->main.autosave_request; + d->last_manual_save_request = df::global::game->main_interface.options.do_manual_save; d->was_load_save = is_load_save; out << std::flush; From 960bfaca867c3c7e57a6954d57c3bc7907dfced7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Feb 2023 08:15:44 -0800 Subject: [PATCH 0545/2222] fix annotation parsing ref: #2857 --- library/lua/dfhack.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index c8b15c191..2ee03dfed 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -657,16 +657,18 @@ function Script:get_flags() self.flags_mtime = mtime self._flags = {} local f = io.open(self.path) - local contents = f:read('*all') - f:close() - for line in contents:gmatch('^%-%-@([^\n]+)') do - local chunk = load(line, self.path, 't', self._flags) + for line in f:lines() do + local at_tag = line:match('^%-%-@(.+)') + if not at_tag then goto continue end + local chunk = load(at_tag, self.path, 't', self._flags) if chunk then chunk() else dfhack.printerr('Parse error: ' .. line) end + ::continue:: end + f:close() end return self._flags end From 13f96b9ac81f3ce41e78751fca678b8d935809f5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Feb 2023 08:20:11 -0800 Subject: [PATCH 0546/2222] increase frequency of nestbox scanning --- docs/changelog.txt | 6 +++--- plugins/nestboxes.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c54d929a9..e876f7113 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,16 +42,16 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `channel-safely`: fixed bug resulting in marker mode never being set for any designation -@ `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs -@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs -- `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg +- `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg ## Misc Improvements -- `automelt`: is now more resistent to savegame corruption +- `automelt`: is now more resistent to vanilla savegame corruption -@ `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) - `misery`: now persists state with the fort - `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K - `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H - `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) -- `clean`: new hotkey for `spotclean`: Ctrl-C +-@ `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair - `autobutcher`: now immediately loads races available at game start into the watchlist -@ replaced DFHack logo used for the hover hotspot with a crisper image diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index 5ce894802..d723f8a5c 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -50,7 +50,7 @@ static void set_config_bool(PersistentDataItem &c, int index, bool value) { set_config_val(c, index, value ? 1 : 0); } -static const int32_t CYCLE_TICKS = 100; // need to react quickly if eggs are unforbidden +static const int32_t CYCLE_TICKS = 50; // need to react quickly when eggs are laid/unforbidden static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static void do_cycle(color_ostream &out); From 823e7e37d829e530f81eb1a2abb811e66837b334 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Feb 2023 09:41:20 -0800 Subject: [PATCH 0547/2222] dev-ify changelog --- docs/changelog.txt | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 080d298f5..a164c4ad5 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,27 +37,27 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - ``Units::isFortControlled``: Account for agitated wildlife -- Fix right click sometimes closing both a DFHack window and a vanilla panel +-@ Fix right click sometimes closing both a DFHack window and a vanilla panel - Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible - `channel-safely`: fixed bug resulting in marker mode never being set for any designation -@ `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs -@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs - `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg -@ Fix persisted data not being written on manual save -- `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg +-@ `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg ## Misc Improvements -- `automelt`: is now more resistent to vanilla savegame corruption +-@ `automelt`: is now more resistent to vanilla savegame corruption -@ `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) - `misery`: now persists state with the fort -- `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K -- `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H -- `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) +-@ `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K +-@ `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H +-@ `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) -@ `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair - `autobutcher`: now immediately loads races available at game start into the watchlist -@ replaced DFHack logo used for the hover hotspot with a crisper image -- `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` +-@ `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` ## Documentation @@ -93,22 +93,22 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autochop`: fixed a crash when processing trees with corrupt data structures (e.g. when a trunk tile fails to fall when the rest of the tree is chopped down) ## Misc Improvements -- DFHack windows can now be "defocused" by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. +-@ DFHack windows can now be "defocused" by clicking somewhere not over the tool window. This has the same effect as pinning previously did, but without the extra clicking. - `getplants`: ID values will now be accepted regardless of case -- Windows now display "PAUSE FORCED" on the lower border if the tool is forcing the game to pause +-@ Windows now display "PAUSE FORCED" on the lower border if the tool is forcing the game to pause -@ New borders for DFHack tool windows -- tell us what you think! -- `autoclothing`: merged the two separate reports into the same command. +-@ `autoclothing`: merged the two separate reports into the same command. - `automelt`: stockpile configuration can now be set from the commandline - `channel-safely`: new monitoring for cave-in prevention -- `gui/control-panel`: you can now configure whether DFHack tool windows should pause the game by default +-@ `gui/control-panel`: you can now configure whether DFHack tool windows should pause the game by default - `gui/control-panel`: new global hotkey for quick access: Ctrl-Shift-E -- `hotkeys`: clicking on the DFHack logo no longer closes the popup menu +-@ `hotkeys`: clicking on the DFHack logo no longer closes the popup menu - `nestboxes`: now saves enabled state in your savegame - `gui/launcher`: sped up initialization time for faster window appearance - `orders`: orders plugin functionality is now accessible via an `overlay` widget when the manager orders screen is open - `gui/quickcmd`: now has its own global keybinding for your convenience: Ctrl-Shift-A - `seedwatch`: now persists enabled state in the savegame, automatically loads useful defaults, and respects reachability when counting available seeds -- `quickfort`: planned buildings are now properly attached to any pertinent overlapping zones +-@ `quickfort`: planned buildings are now properly attached to any pertinent overlapping zones ## Documentation - `compile`: instructions added for cross-compiling DFHack for Windows from a Linux Docker builder @@ -127,7 +127,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.Label``: ``label.scroll()`` now understands ``home`` and ``end`` keywords for scrolling to the top or bottom - ``widgets.List``: new callbacks for double click and shift double click - ``dfhack.units.getCitizens()``: gets a list of citizens -- ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked +-@ ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked - ``widgets.Label``: token ``tile`` properties can now be either pens or numeric texture ids - `tiletypes`: now has a Lua API! ``tiletypes_setTile`` @@ -138,7 +138,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `autofarm`: don't duplicate status line entries for crops with no current supply -- `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) +-@ `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) - `tailor`: now respects the setting of the "used dyed clothing" standing order toggle # 50.05-alpha1 From ee10aa91724d91a680d75d9af128f5230a7d6584 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Feb 2023 09:51:50 -0800 Subject: [PATCH 0548/2222] sync tags from spreadsheet --- docs/plugins/autonestbox.rst | 2 +- docs/plugins/confirm.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/autonestbox.rst b/docs/plugins/autonestbox.rst index a107dff9b..27074fe1c 100644 --- a/docs/plugins/autonestbox.rst +++ b/docs/plugins/autonestbox.rst @@ -3,7 +3,7 @@ autonestbox .. dfhack-tool:: :summary: Auto-assign egg-laying female pets to nestbox zones. - :tags: untested fort auto animals + :tags: fort auto animals To use this feature, you must create pen/pasture zones on the same tiles as built nestboxes. If the pen is bigger than 1x1, the nestbox must be in the top diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index aaa586202..f0a34b909 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -3,7 +3,7 @@ confirm .. dfhack-tool:: :summary: Adds confirmation dialogs for destructive actions. - :tags: untested fort interface + :tags: fort interface Now you can get the chance to avoid accidentally disbanding a squad or deleting a hauling route in case you hit the key accidentally. From 588df3eec89a916c9aa912bc3e37f9322584859b Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 10 Feb 2023 12:57:08 -0500 Subject: [PATCH 0549/2222] Update stonesense, changelog for dfhack/stonesense#97 --- docs/changelog.txt | 1 + plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a164c4ad5..fd647b296 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -58,6 +58,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autobutcher`: now immediately loads races available at game start into the watchlist -@ replaced DFHack logo used for the hover hotspot with a crisper image -@ `orders`: recipe for silver crossbows removed from ``library/military`` as it is not a vanilla recipe, but is available in ``library/military_include_artifact_materials`` +- `stonesense`: added an ``INVERT_MOUSE_Z`` option to invert the mouse wheel direction ## Documentation diff --git a/plugins/stonesense b/plugins/stonesense index a045369db..6570fe010 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit a045369db6728979e709625908df3f4f36e868ca +Subproject commit 6570fe01081f7e402495bc5339b4ff7a1aabf305 From dd925f783885e8dddf115604d56da959e197a2b2 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 10 Feb 2023 13:02:14 -0500 Subject: [PATCH 0550/2222] Un-dev-ify two changes that appear to be new to end-users since 0.47.05-r8 --- docs/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fd647b296..901325ecb 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,14 +44,14 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs - `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg -@ Fix persisted data not being written on manual save --@ `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg +- `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg ## Misc Improvements -@ `automelt`: is now more resistent to vanilla savegame corruption -@ `hotkeys`: DFHack logo is now hidden on screens where it covers important information when in the default position (e.g. when choosing an embark site) - `misery`: now persists state with the fort -@ `autodump`: reinstate ``autodump-destroy-item``, hotkey: Ctrl-K --@ `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H +- `autodump`: new hotkey for ``autodump-destroy-here``: Ctrl-H -@ `dig`: new hotkeys for vein designation on z-level (Ctrl-V) and vein designation across z-levels (Ctrl-Shift-V) -@ `clean`: new hotkey for `spotclean`: Ctrl-C - `autobutcher`: changed defaults from 5 females / 1 male to 4 females / 2 males so a single unfortunate accident doesn't leave players without a mating pair From 616d8d1bfc795f81acbd7bcec8e057098e8c7cf2 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 10 Feb 2023 18:09:10 +0000 Subject: [PATCH 0551/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index d1ea2c1e8..cc8d34121 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d1ea2c1e835b89428c157f3121ef0e8858caccf6 +Subproject commit cc8d3412146b02e6bfef02f0a7881add6b6ef60a From 5100a7ea40bd9d5fd9bfa9c33e49df4df5b31b1a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Feb 2023 10:26:37 -0800 Subject: [PATCH 0552/2222] bump to 50.07-alpha1 --- CMakeLists.txt | 2 +- docs/changelog.txt | 16 ++++++++++++++-- library/xml | 2 +- scripts | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6854b3300..38eb6c92b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,7 +190,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "alpha0") +set(DFHACK_RELEASE "alpha1") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 901325ecb..2d442bae4 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,20 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.07-alpha1 + ## Fixes - ``Units::isFortControlled``: Account for agitated wildlife -@ Fix right click sometimes closing both a DFHack window and a vanilla panel @@ -71,8 +85,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.HotkeyLabel``: Added ``setOnActivate`` method to allow easily updating the ``on_activate`` callback. - ``widgets.FilteredList``: Added ``case_sensitive`` optional paramter to determine if filtering is case sensitive. -## Removed - # 50.05-alpha3.1 ## Fixes diff --git a/library/xml b/library/xml index f2f2ba3cd..0c8d9a8cd 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f2f2ba3cd8b4a45919bc597cb47cbb5994dc359c +Subproject commit 0c8d9a8cdeeecd1ec3cd4f47a9d2682fec8f93e6 diff --git a/scripts b/scripts index cc8d34121..aae5d8601 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit cc8d3412146b02e6bfef02f0a7881add6b6ef60a +Subproject commit aae5d860106017bb8a7a3bb8bcaa8a9252f3839e From ad784271304f9998bd4a37b11e8901568c5dd573 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Feb 2023 11:35:34 -0800 Subject: [PATCH 0553/2222] mark autolabor as tested and update scripts --- docs/plugins/autolabor.rst | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/autolabor.rst b/docs/plugins/autolabor.rst index 3ab5c4e09..73d5feac5 100644 --- a/docs/plugins/autolabor.rst +++ b/docs/plugins/autolabor.rst @@ -3,7 +3,7 @@ autolabor .. dfhack-tool:: :summary: Automatically manage dwarf labors. - :tags: untested fort auto labors + :tags: fort auto labors Autolabor attempts to keep as many dwarves as possible busy while allowing dwarves to specialize in specific skills. diff --git a/scripts b/scripts index aae5d8601..e5a286cb7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit aae5d860106017bb8a7a3bb8bcaa8a9252f3839e +Subproject commit e5a286cb745f76bf5377d59d93b984cd465e2f13 From ecce87ed507bae23276c4fa756df59ed67fd9c01 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 10 Feb 2023 15:29:51 -0500 Subject: [PATCH 0554/2222] Update authors (changes since 50.05-alpha0) --- docs/about/Authors.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index b0031607a..7c299b995 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -12,14 +12,17 @@ Name Github Other 8Z 8Z Abel abstern acwatkins acwatkins +Alexander Collins gearsix Alexander Gavrilov angavrilov ag Amber Brown hawkowl Amostubal Amostubal Andrea Cattaneo acattaneo88 AndreasPK AndreasPK +Andriel Chaoti AndrielChaoti Angus Mezick amezick Antalia tamarakorr Anuradha Dissanayake falconne +Ariphaos Ariphaos arzyu arzyu Atkana Atkana AtomicChicken AtomicChicken @@ -37,8 +40,10 @@ Cameron Ewell Ozzatron Carter Bray Qartar Chris Dombroski cdombroski Chris Parsons chrismdp +cjhammel cjhammel Clayton Hughes Clément Vuchener cvuchener +Corey CoreyJ87 daedsidog daedsidog Dan Amlund danamlund Daniel Brooks db48x @@ -48,12 +53,15 @@ David Seguin dseguin David Timm dtimm Deon dikbut Tjudge1 +Dmitrii Kurkin Kurkin DoctorVanGogh DoctorVanGogh Donald Ruegsegger hashaash doomchild doomchild DwarvenM DwarvenM +Eamon Bode eamondo2 Baron Von Munchhausen EarthPulseAcademy EarthPulseAcademy ElMendukol ElMendukol +ElsaTheHobo ElsaTheHobo Elsa enjia2000 Eric Wald eswald Erik Youngren Artanis @@ -62,6 +70,7 @@ expwnent expwnent Feng figment figment Gabe Rau gaberau +Gaelmare Gaelmare gchristopher gchristopher George Murray GitOnUp grubsteak grubsteak @@ -75,6 +84,7 @@ IndigoFenix James 20k James Gilles kazimuth James Logsdon jlogsdon +Janeene Beeforth dawnmist Jared Adams Jeremy Apthorp nornagon Jim Lisi stonetoad @@ -83,6 +93,7 @@ jimcarreer jimcarreer jj jjyg jj\`\` Joel Meador janxious John Beisley huin +John Cosker johncosker John Shade gsvslto Jonas Ask Jonathan Clark AridTag @@ -90,6 +101,7 @@ Josh Cooper cppcooper coope jowario jowario kane-t kane-t Kelly Kinkade ab9rf +Kelvie Wong kelvie Kib Arekatír arekatir KlonZK KlonZK Kris Parker kaypy @@ -131,6 +143,7 @@ Nikolay Amiantov abbradar nocico nocico NotRexButCaesar NotRexButCaesar Nuno Fernandes UnknowableCoder +nuvu vallode Omniclasm oorzkws oorzkws OwnageIsMagic OwnageIsMagic @@ -139,11 +152,14 @@ PassionateAngler PassionateAngler Patrik Lundell PatrikLundell Paul Fenwick pjf PeridexisErrant PeridexisErrant +Peter Hansen previsualconsent Petr Mrázek peterix Pfhreak Pfhreak Pierre Lulé plule +Pierre Lulé plule Pierre-David Bélanger pierredavidbelanger potato +ppaawwll ppaawwll ðŸ‡ðŸ‡ðŸ‡ðŸ‡ Priit Laes plaes Putnam Putnam3145 quarque2 quarque2 @@ -160,6 +176,7 @@ Rich Rauenzahn rrauenza Rinin Rinin rndmvar rndmvar Rob Bailey actionninja +Rob Goodberry robob27 Robert Heinrich rh73 Robert Janetzko robertjanetzko Rocco Moretti roccomoretti @@ -170,6 +187,7 @@ Rose RosaryMala Roses Pheosics Ross M RossM rout +Roxy TealSeer gallowsCalibrator rubybrowncoat rubybrowncoat Rumrusher rumrusher RusAnon RusAnon @@ -177,7 +195,9 @@ Ryan Bennitt ryanbennitt Ryan Williams Bumber64 Bumber sami scamtank scamtank +Scott Ellis StormCrow42 Sebastian Wolfertz Enkrod +SeerSkye SeerSkye seishuuu seishuuu Seth Woodworth sethwoodworth Shim Panze Shim-Panze @@ -191,6 +211,7 @@ suokko suokko shrieker sv-esk sv-esk Tachytaenius wolfboyft Tacomagic +TaxiService TaxiService thefriendlyhacker thefriendlyhacker TheHologram TheHologram Theo Kalfas teolandon @@ -216,6 +237,7 @@ Warmist warmist Wes Malone wesQ3 Will Rogers wjrogers WoosterUK WoosterUK +XianMaeve XianMaeve ZechyW ZechyW Zhentar Zhentar zilpin zilpin From 9434cd3d646fc4de51688c1baf933d8b0cc2fe29 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 10 Feb 2023 15:58:18 -0500 Subject: [PATCH 0555/2222] Fix and detect duplicates in Authors.rst --- ci/authors-rst.py | 2 ++ docs/about/Authors.rst | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/authors-rst.py b/ci/authors-rst.py index b9ef48d7c..6f3717276 100755 --- a/ci/authors-rst.py +++ b/ci/authors-rst.py @@ -35,6 +35,8 @@ def main(): error(line_number, 'bad table divider') if line != lines[first_div_index]: error(line_number, 'malformed table divider') + if line == lines[first_div_index + i - 1]: + error(line_number, 'duplicate of previous line') if len(div_indices) < 3: error(len(lines), 'missing table divider(s)') for i in div_indices[3:]: diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index 7c299b995..07909b15c 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -156,7 +156,6 @@ Peter Hansen previsualconsent Petr Mrázek peterix Pfhreak Pfhreak Pierre Lulé plule -Pierre Lulé plule Pierre-David Bélanger pierredavidbelanger potato ppaawwll ppaawwll ðŸ‡ðŸ‡ðŸ‡ðŸ‡ From 1390e724b6c55f725e29f2218af11a369f101011 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 10 Feb 2023 22:52:01 -0500 Subject: [PATCH 0556/2222] nestboxes: avoid forbidding non-eggs The rewrite in #2773 introduced a bug (identified by KMFrench on Discord) where nestboxes themselves would be forbidden, which prevents egglaying citizens from using them since the nestboxes would be claimed before use. The previous version of the plugin handled this by skipping the first item in `contained_items`, which is the building material (in this case, the nestbox). This fix limits the `forbid` flag manipulation to egg items only, which should be a reasonable limitation that still allows the plugin to serve its purpose. --- plugins/nestboxes.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/nestboxes.cpp b/plugins/nestboxes.cpp index d723f8a5c..f8758a0d9 100644 --- a/plugins/nestboxes.cpp +++ b/plugins/nestboxes.cpp @@ -9,6 +9,7 @@ #include "df/world.h" #include "df/building_nest_boxst.h" #include "df/item.h" +#include "df/item_eggst.h" #include "df/unit.h" using std::string; @@ -140,8 +141,8 @@ static void do_cycle(color_ostream &out) { fertile = true; } for (auto &contained_item : nb->contained_items) { - df::item *item = contained_item->item; - if (item->flags.bits.forbid != fertile) { + auto *item = virtual_cast(contained_item->item); + if (item && item->flags.bits.forbid != fertile) { item->flags.bits.forbid = fertile; if (fertile && item->flags.bits.in_job) { // cancel any job involving the egg From de1a4fb5be813594b10d3bb283c2514778aa1015 Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 10 Feb 2023 23:30:35 -0500 Subject: [PATCH 0557/2222] Update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2d442bae4..c667417b7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +-@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. ## Misc Improvements From 3fb172f2e76526efc68eeb428fafa3e079f3f707 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 11 Feb 2023 07:13:47 +0000 Subject: [PATCH 0558/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index e5a286cb7..3caa35156 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e5a286cb745f76bf5377d59d93b984cd465e2f13 +Subproject commit 3caa35156baa75ee804eaf64de445bfa8f6301b2 From 3756d59919136c02594ee626d870e09e767d076a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Feb 2023 01:20:17 -0800 Subject: [PATCH 0559/2222] copy edit overlay dev guide and update examples --- docs/dev/overlay-dev-guide.rst | 41 +++++++++++++--------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index d31ce246d..1a53e7f65 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -150,8 +150,8 @@ declared like this:: When the `overlay` plugin is enabled, it scans all plugins and scripts for this table and registers the widgets on your behalf. Plugin lua code is loaded -with ``require()`` and script lua code is loaded with `reqscript`. If your -widget is in a script, ensure your script can be +with ``require()`` and script lua code is loaded with ``reqscript()``. +If your widget is in a script, ensure your script can be `loaded as a module `, or else the widget will not be discoverable. The widget is enabled on load if it was enabled the last time the `overlay` plugin was loaded, and the widget's position is restored according to the state @@ -173,7 +173,8 @@ script. Note that reloading a script does not clear its global environment. This is fine if you are changing existing functions or adding new ones. If you remove a global function or other variable from the source, though, it will stick around -in your script's global environment until you restart DF. +in your script's global environment until you restart DF or run +`devel/clear-script-env`. Scripts ******* @@ -204,10 +205,10 @@ check that:** #. ``OVERLAY_WIDGETS`` is declared, is global (not ``local``), and contains your widget class -#. (if a script> your script is `declared as a module ` +#. (if a script) your script is `declared as a module ` (``--@ module = true``) and it does not have side effects when loaded as a module (i.e. you check ``dfhack_flags.module`` and return before executing - any statements if the value is ``true`` + any statements if the value is ``true``) #. your code does not have syntax errors -- run ``:lua ~reqscript('myscriptname')`` (if a script) or ``:lua ~require('plugins.mypluginname')`` (if a plugin) and make sure there @@ -237,13 +238,17 @@ the :kbd:`Alt`:kbd:`Z` hotkey is hit:: } function MessageWidget:init() - self.label = widgets.Label{text=''} - self:addviews{self.label} + self:addviews{ + widgets.Label{ + view_id='label', + text='', + }, + } end function MessageWidget:overlay_onupdate() local text = getImportantMessage() -- defined in the host script/plugin - self.label:setText(text) + self.subviews.label:setText(text) self.frame.w = #text end @@ -330,7 +335,7 @@ screen (by default, but the player can move it wherever). OVERLAY_WIDGETS = {menu=HotspotMenuWidget} - MenuScreen = defclass(MenuScreen, gui.Screen) + MenuScreen = defclass(MenuScreen, gui.ZScreen) MenuScreen.ATTRS{ focus_path='hotspot/menu', hotspot_frame=DEFAULT_NIL, @@ -345,11 +350,9 @@ screen (by default, but the player can move it wherever). -- ... self:addviews{ - widgets.ResizingPanel{ - autoarrange_subviews=true, + widgets.Window{ frame=frame, - frame_style=gui.GREY_LINE_FRAME, - frame_background=gui.CLEAR_PEN, + autoarrange_subviews=true, subviews={ -- ... }, @@ -357,15 +360,3 @@ screen (by default, but the player can move it wherever). }, } end - - function MenuScreen:onInput(keys) - if keys.LEAVESCREEN then - self:dismiss() - return true - end - return self:inputToSubviews(keys) - end - - function MenuScreen:onRenderFrame(dc, rect) - self:renderParent() - end From 65f38ec75c9846beace1c1ed943678dde594d69b Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 11 Feb 2023 03:59:31 -0500 Subject: [PATCH 0560/2222] Add isGeldable, isMarkedForGelding, isPet --- docs/changelog.txt | 5 +++++ docs/dev/Lua API.rst | 4 +++- library/LuaApi.cpp | 3 +++ library/include/modules/Units.h | 3 +++ library/modules/Units.cpp | 26 ++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c667417b7..2ee4a66d0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API +- Units module: added new predicates for: + - ``isGeldable()`` + - ``isMarkedForGelding()`` + - ``isPet()`` + ## Lua ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 4e900bea5..b8177043c 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1343,6 +1343,8 @@ Units module * ``dfhack.units.isTamable(unit)`` * ``dfhack.units.isDomesticated(unit)`` * ``dfhack.units.isMarkedForSlaughter(unit)`` +* ``dfhack.units.isMarkedForGelding(unit)`` +* ``dfhack.units.isGeldable(unit)`` * ``dfhack.units.isGelded(unit)`` * ``dfhack.units.isEggLayer(unit)`` * ``dfhack.units.isGrazer(unit)`` @@ -1362,7 +1364,7 @@ Units module The unit is available for adoption. - +* ``dfhack.units.isPet(unit)`` * ``dfhack.units.isOpposedToLife(unit)`` * ``dfhack.units.hasExtravision(unit)`` * ``dfhack.units.isBloodsucker(unit)`` diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 69d43821e..ce1f66091 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1756,6 +1756,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isTamable), WRAPM(Units, isDomesticated), WRAPM(Units, isMarkedForSlaughter), + WRAPM(Units, isMarkedForGelding), + WRAPM(Units, isGeldable), WRAPM(Units, isGelded), WRAPM(Units, isEggLayer), WRAPM(Units, isGrazer), @@ -1763,6 +1765,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isForest), WRAPM(Units, isMischievous), WRAPM(Units, isAvailableForAdoption), + WRAPM(Units, isPet), WRAPM(Units, hasExtravision), WRAPM(Units, isOpposedToLife), WRAPM(Units, isBloodsucker), diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 442f3d009..0edebacdc 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -110,6 +110,8 @@ DFHACK_EXPORT bool isTame(df::unit* unit); DFHACK_EXPORT bool isTamable(df::unit* unit); DFHACK_EXPORT bool isDomesticated(df::unit* unit); DFHACK_EXPORT bool isMarkedForSlaughter(df::unit* unit); +DFHACK_EXPORT bool isMarkedForGelding(df::unit* unit); +DFHACK_EXPORT bool isGeldable(df::unit* unit); DFHACK_EXPORT bool isGelded(df::unit* unit); DFHACK_EXPORT bool isEggLayer(df::unit* unit); DFHACK_EXPORT bool isGrazer(df::unit* unit); @@ -117,6 +119,7 @@ DFHACK_EXPORT bool isMilkable(df::unit* unit); DFHACK_EXPORT bool isForest(df::unit* unit); DFHACK_EXPORT bool isMischievous(df::unit *unit); DFHACK_EXPORT bool isAvailableForAdoption(df::unit* unit); +DFHACK_EXPORT bool isPet(df::unit* unit); DFHACK_EXPORT bool hasExtravision(df::unit *unit); DFHACK_EXPORT bool isOpposedToLife(df::unit *unit); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index f2311d677..9a415fb34 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -511,6 +511,23 @@ bool Units::isMarkedForSlaughter(df::unit* unit) return unit->flags2.bits.slaughter == 1; } +bool Units::isMarkedForGelding(df::unit* unit) +{ + CHECK_NULL_POINTER(unit); + return unit->flags3.bits.marked_for_gelding == 1; +} + +bool Units::isGeldable(df::unit* unit) +{ + CHECK_NULL_POINTER(unit); + + if(world->raws.creatures.all[unit->race]->caste[unit->caste]->flags.is_set(caste_raw_flags::GELDABLE)) { + return true; + } + + return false; +} + bool Units::isGelded(df::unit* unit) { CHECK_NULL_POINTER(unit); @@ -587,6 +604,15 @@ bool Units::isAvailableForAdoption(df::unit* unit) return false; } +bool Units::isPet(df::unit* unit) +{ + CHECK_NULL_POINTER(unit); + + if(unit->relationship_ids[df::unit_relationship_type::Pet] != -1) + return true; + + return false; +} bool Units::hasExtravision(df::unit *unit) { From 6b4c1e77875fcfae1f5bbfe8e07b0061056da13a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 12 Feb 2023 01:53:03 +0000 Subject: [PATCH 0561/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 0c8d9a8cd..ad8d87989 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0c8d9a8cdeeecd1ec3cd4f47a9d2682fec8f93e6 +Subproject commit ad8d87989b796b453b0707963e30ef9dfff10c2e From 4b5836b99672eb4fc66d9adcb9a07c1fb2177977 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Sat, 11 Feb 2023 23:04:50 +1100 Subject: [PATCH 0562/2222] Fix for #2871 * Add check for unit->name.nickname field is not an empty string * Refactor repeated checks for inappropriate and protected units --- docs/changelog.txt | 1 + plugins/autobutcher.cpp | 100 +++++++++++++++------------------------- 2 files changed, 37 insertions(+), 64 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c667417b7..03ff8f5bc 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. +- `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. ## Misc Improvements diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index 28667bbe7..bee3a4503 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -732,6 +732,30 @@ static bool isInBuiltCageRoom(df::unit *unit) { return false; } +// This can be used to identify completely inappropriate units (dead, undead, not belonging to the fort, ...) +// that autobutcher should be ignoring. +static bool isInappropriateUnit(df::unit *unit) { + return !Units::isActive(unit) + || Units::isUndead(unit) + || Units::isMerchant(unit) // ignore merchants' draft animals + || Units::isForest(unit) // ignore merchants' caged animals + || !Units::isOwnCiv(unit); +} + +// This can be used to identify protected units that should be counted towards fort totals, but not scheduled +// for butchering. This way they count towards target quota, so if you order that you want 1 female adult cat +// and have 2 cats, one of them being a pet, the other gets butchered +static bool isProtectedUnit(df::unit *unit) { + return Units::isWar(unit) // ignore war dogs etc + || Units::isHunter(unit) // ignore hunting dogs etc + // ignore creatures in built cages which are defined as rooms to leave zoos alone + // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) + || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() + || Units::isAvailableForAdoption(unit) + || unit->name.has_name + || !unit->name.nickname.empty(); +} + static void autobutcher_cycle(color_ostream &out) { // mark that we have recently run cycle_timestamp = world->frame_counter; @@ -757,14 +781,9 @@ static void autobutcher_cycle(color_ostream &out) { // then let autowatch add units to the watchlist which will probably start breeding (owned pets, war animals, ...) // then process units counting those which can't be butchered (war animals, named pets, ...) // so that they are treated as "own stock" as well and count towards the target quota - if ( !Units::isActive(unit) - || Units::isUndead(unit) + if (isInappropriateUnit(unit) || Units::isMarkedForSlaughter(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - || !Units::isTame(unit) - ) + || !Units::isTame(unit)) continue; // found a bugged unit which had invalid coordinates but was not in a cage. @@ -794,13 +813,7 @@ static void autobutcher_cycle(color_ostream &out) { // don't butcher protected units, but count them as stock as well // this way they count towards target quota, so if you order that you want 1 female adult cat // and have 2 cats, one of them being a pet, the other gets butchered - if( Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name) + if(isProtectedUnit(unit)) w->PushProtectedUnit(unit); else if ( Units::isGay(unit) || Units::isGelded(unit)) @@ -833,12 +846,7 @@ static WatchedRace * checkRaceStocksTotal(color_ostream &out, int race) { if (unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - ) + if (isInappropriateUnit(unit)) continue; if(!isContainedInItem(unit) && !hasValidMapPos(unit)) @@ -855,12 +863,7 @@ WatchedRace * checkRaceStocksProtected(color_ostream &out, int race) { if (unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - ) + if (isInappropriateUnit(unit)) continue; // found a bugged unit which had invalid coordinates but was not in a cage. @@ -868,14 +871,8 @@ WatchedRace * checkRaceStocksProtected(color_ostream &out, int race) { if (!isContainedInItem(unit) && !hasValidMapPos(unit)) continue; - if ( !Units::isTame(unit) - || Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name ) + if ( !Units::isTame(unit) + || isProtectedUnit(unit)) w->PushUnit(unit); } return w; @@ -887,19 +884,9 @@ WatchedRace * checkRaceStocksButcherable(color_ostream &out, int race) { if (unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) + if ( isInappropriateUnit(unit) || !Units::isTame(unit) - || Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name + || isProtectedUnit(unit) ) continue; @@ -917,12 +904,7 @@ WatchedRace * checkRaceStocksButcherFlag(color_ostream &out, int race) { if(unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - ) + if (isInappropriateUnit(unit)) continue; if (!isContainedInItem(unit) && !hasValidMapPos(unit)) @@ -1013,19 +995,9 @@ static void autobutcher_butcherRace(color_ostream &out, int id) { if(unit->race != id) continue; - if( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draught animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) + if( isInappropriateUnit(unit) || !Units::isTame(unit) - || Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name + || isProtectedUnit(unit) ) continue; From 0c989634abe1712a99323cc46edda1d431dc7a46 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Feb 2023 03:06:53 -0800 Subject: [PATCH 0563/2222] properly store the id of seed targets in persisted state --- docs/changelog.txt | 1 + plugins/seedwatch.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index f91c39c58..14acc3eea 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. +- `seedwatch`: fix saving and loading of seed stock targets ## Misc Improvements diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index 551ec36dc..4a2a03f3a 100644 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -85,6 +85,7 @@ static PersistentDataItem & ensure_seed_config(color_ostream &out, int id) { string keyname = SEED_CONFIG_KEY_PREFIX + int_to_string(id); DEBUG(config,out).print("creating new persistent key for seed type %d\n", id); watched_seeds.emplace(id, World::GetPersistentData(keyname, NULL)); + set_config_val(watched_seeds[id], SEED_CONFIG_ID, id); return watched_seeds[id]; } static void remove_seed_config(color_ostream &out, int id) { From 5c457fc96f36ec47cec2039be2f320279c06c27f Mon Sep 17 00:00:00 2001 From: Batt Mush Date: Sun, 12 Feb 2023 15:49:40 -0600 Subject: [PATCH 0564/2222] [issue-2888] autodump now conditionally removes an item's dump flag and sets forbid flag IFF the item is successfully dumped. Otherwise, the item's original flags are kept intact. This avoids impacting any tasks associated with the item. --- docs/about/Authors.rst | 1 + docs/changelog.txt | 1 + plugins/autodump.cpp | 18 +++++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index 07909b15c..75b9eb4f7 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -26,6 +26,7 @@ Ariphaos Ariphaos arzyu arzyu Atkana Atkana AtomicChicken AtomicChicken +Batt Mush hobotron-df Bearskie Bearskie belal jimhester Ben Lubar BenLubar diff --git a/docs/changelog.txt b/docs/changelog.txt index f91c39c58..5f147de55 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. +- `autodump`: changed behaviour to only change `dump` and `forbid` flags if an item is successfully dumped. ## Misc Improvements diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 61e83d63e..2514dd91a 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -396,18 +396,22 @@ static command_result autodump_main(color_ostream &out, vector & parame if (!need_forbidden && itm->flags.bits.forbid) continue; - if(!destroy) // move to cursor + if (!destroy) // move to cursor { - // Change flags to indicate the dump was completed, as if by super-dwarfs - itm->flags.bits.dump = false; - itm->flags.bits.forbid = true; - // Don't move items if they're already at the cursor if (pos_cursor != pos_item) { - if (!Items::moveToGround(MC, itm, pos_cursor)) + if (Items::moveToGround(MC, itm, pos_cursor)) + { + // Change flags to indicate the dump was completed, as if by super-dwarfs + itm->flags.bits.dump = false; + itm->flags.bits.forbid = true; + } + else + { out.print("Could not move item: %s\n", - Items::getDescription(itm, 0, true).c_str()); + Items::getDescription(itm, 0, true).c_str()); + } } } else // destroy From 1b8565544e2a7acbf817fad7b097886f62d75ab6 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 12 Feb 2023 14:28:37 -0800 Subject: [PATCH 0565/2222] Update docs/changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5f147de55..73695e2c0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,7 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. -- `autodump`: changed behaviour to only change `dump` and `forbid` flags if an item is successfully dumped. +- `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. ## Misc Improvements From 7651f6a91e0274e7063748490af4cd3b429b11bf Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sun, 12 Feb 2023 17:44:19 -0500 Subject: [PATCH 0566/2222] Add edit_on_change to FilteredList --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 1 + library/lua/gui/widgets.lua | 11 ++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 73695e2c0..6952f7d57 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -53,6 +53,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua +- ``widgets.FilteredList``: Added ``edit_on_change`` optional paramter to allow a custom callback on filter edit change. + ## Removed # 50.07-alpha1 diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index b8177043c..1785afa8e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4975,6 +4975,7 @@ supports: :edit_below: If true, the edit field is placed below the list instead of above. :edit_key: If specified, the edit field is disabled until this key is pressed. :edit_ignore_keys: If specified, will be passed to the filter edit field as its ``ignore_keys`` attribute. +:edit_on_change: If specified, will be passed to the filter edit field as its ``on_change`` attribute. :edit_on_char: If specified, will be passed to the filter edit field as its ``on_char`` attribute. :not_found_label: Specifies the text of the label shown when no items match the filter. diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index a5840a1b1..ffe26936d 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1887,6 +1887,7 @@ FilteredList.ATTRS { edit_key = DEFAULT_NIL, edit_ignore_keys = DEFAULT_NIL, edit_on_char = DEFAULT_NIL, + edit_on_change = DEFAULT_NIL, } function FilteredList:init(info) @@ -1897,10 +1898,18 @@ function FilteredList:init(info) end end + local on_change = self:callback('onFilterChange') + if self.edit_on_change then + on_change = function(text) + self.edit_on_change(text) + self:onFilterChange(text) + end + end + self.edit = EditField{ text_pen = info.edit_pen or info.cursor_pen, frame = { l = info.icon_width, t = 0, h = 1 }, - on_change = self:callback('onFilterChange'), + on_change = on_change, on_char = on_char, key = self.edit_key, ignore_keys = self.edit_ignore_keys, From 2a04abb1cbb16c1622af8bc69fece10e9273a11f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 12 Feb 2023 22:47:20 +0000 Subject: [PATCH 0567/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index ad8d87989..785ffbd40 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ad8d87989b796b453b0707963e30ef9dfff10c2e +Subproject commit 785ffbd4086a15cfd1716b8944cb62ca0ed6ed32 diff --git a/scripts b/scripts index 3caa35156..9401e9e43 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3caa35156baa75ee804eaf64de445bfa8f6301b2 +Subproject commit 9401e9e43d44c04842c36530f97ecc00c12e6c9e From 8d63757eea68d630e218a36c86da1903437d29fc Mon Sep 17 00:00:00 2001 From: Rob Goodberry Date: Sun, 12 Feb 2023 18:01:35 -0500 Subject: [PATCH 0568/2222] Update docs/changelog.txt Co-authored-by: Myk --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 6952f7d57..60bbba067 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -53,7 +53,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua -- ``widgets.FilteredList``: Added ``edit_on_change`` optional paramter to allow a custom callback on filter edit change. +- ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. ## Removed From 766738e10277bd165f3f4f45aa43fe5e3f82aa6d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Feb 2023 15:18:39 -0800 Subject: [PATCH 0569/2222] generate names for nameless burrows; check for nil --- docs/changelog.txt | 3 ++- plugins/autochop.cpp | 9 ++++----- plugins/lua/autochop.lua | 8 +++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2e987a93d..0b8a73421 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,8 +38,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. -- `seedwatch`: fix saving and loading of seed stock targets +-@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. +-@ `autochop`: generate default names for burrows with no assigned names ## Misc Improvements diff --git a/plugins/autochop.cpp b/plugins/autochop.cpp index 3a433f7a9..cb703cf47 100644 --- a/plugins/autochop.cpp +++ b/plugins/autochop.cpp @@ -857,12 +857,11 @@ static int autochop_getTreeCountsAndBurrowConfigs(lua_State *L) { for (auto &burrow : plotinfo->burrows.list) { int id = burrow->id; - if (watched_burrows_indices.count(id)) { - // push_burrow_config(L, watched_burrows[watched_burrows_indices[id]]); - emplace_bulk_burrow_config(L, burrow_config_map, watched_burrows[watched_burrows_indices[id]]); - } else { + if (watched_burrows_indices.count(id)) + emplace_bulk_burrow_config(L, burrow_config_map, + watched_burrows[watched_burrows_indices[id]]); + else emplace_bulk_burrow_config(L, burrow_config_map, id); - } } Lua::Push(L, burrow_config_map); diff --git a/plugins/lua/autochop.lua b/plugins/lua/autochop.lua index e9bad2eef..cda91b32d 100644 --- a/plugins/lua/autochop.lua +++ b/plugins/lua/autochop.lua @@ -100,13 +100,19 @@ function getTreeCountsAndBurrowConfigs() ret.burrow_configs = {} for idx,c in pairs(unparsed_burrow_configs) do - c.name = df.burrow.find(c.id).name + local burrow = df.burrow.find(c.id) + if not burrow then goto continue end + c.name = burrow.name + if #c.name == 0 then + c.name = ('Burrow %d'):format(c.id + 1) + end c.chop = c.chop ~= 0 c.clearcut = c.clearcut ~= 0 c.protect_brewable = c.protect_brewable ~= 0 c.protect_edible = c.protect_edible ~= 0 c.protect_cookable = c.protect_cookable ~= 0 table.insert(ret.burrow_configs, c) + ::continue:: end return ret end From 68d314c9c06d5ac7c70bd3d4e96fd39b40d21ecf Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 00:41:23 -0800 Subject: [PATCH 0570/2222] show more understandable units for bars and cloths --- docs/changelog.txt | 1 + plugins/showmood.cpp | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1d9f8e0f8..41871ed25 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. ## Misc Improvements +- `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) ## Documentation diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index abeb2e98e..79a733b7b 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -275,17 +275,29 @@ command_result df_showmood (color_ostream &out, vector & parameters) // count how many items of this type the crafter already collected { int count_got = 0; + int dimension_got = 0; + int divisor = 1; + bool has_dims = false; + if (item->item_type == item_type::BAR) { + divisor = 150; + has_dims = true; + } else if (item->item_type == item_type::CLOTH) { + divisor = 10000; + has_dims = true; + } for (size_t j = 0; j < job->items.size(); j++) { if(job->items[j]->job_item_idx == int32_t(i)) { - if (item->item_type == item_type::BAR || item->item_type == item_type::CLOTH) - count_got += job->items[j]->item->getTotalDimension(); - else - count_got += 1; + if (has_dims) + dimension_got += job->items[j]->item->getTotalDimension(); + count_got += 1; } } - out.print(", quantity %i (got %i)\n", item->quantity, count_got); + out.print(", got %i of %i", count_got, item->quantity/divisor); + if (has_dims) + out.print(" (%i of %i sub-units)", dimension_got, item->quantity); + out.print("\n"); } } } From 2b3dcee4b0ff68083ded3c0a0744b5cd07406fc1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 01:18:45 -0800 Subject: [PATCH 0571/2222] get strangemood compiling and running --- docs/plugins/strangemood.rst | 8 ++++---- plugins/CMakeLists.txt | 2 +- plugins/strangemood.cpp | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index a5b2b6e1f..def862a4b 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -22,18 +22,18 @@ Examples Options ------- -``-force`` +``--force`` Ignore normal strange mood preconditions (no recent mood, minimum moodable population, artifact limit not reached, etc.). -``-unit`` +``--unit`` Make the strange mood strike the selected unit instead of picking one randomly. Unit eligibility is still enforced (unless ``-force`` is also specified). -``-type `` +``--type `` Force the mood to be of a particular type instead of choosing randomly based on happiness. Valid values are "fey", "secretive", "possessed", "fell", and "macabre". -``-skill `` +``--skill `` Force the mood to use a specific skill instead of choosing the highest moodable skill. Valid values are "miner", "carpenter", "engraver", "mason", "tanner", "weaver", "clothier", "weaponsmith", "armorsmith", "metalsmith", diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7d4ff760e..7940c3994 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -158,7 +158,7 @@ dfhack_plugin(showmood showmood.cpp) #dfhack_plugin(stockflow stockflow.cpp LINK_LIBRARIES lua) #add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) -#dfhack_plugin(strangemood strangemood.cpp) +dfhack_plugin(strangemood strangemood.cpp) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) diff --git a/plugins/strangemood.cpp b/plugins/strangemood.cpp index f6dd1f747..b300eb795 100644 --- a/plugins/strangemood.cpp +++ b/plugins/strangemood.cpp @@ -80,7 +80,7 @@ df::job_skill getMoodSkill (df::unit *unit) { case job_skill::MINING: case job_skill::CARPENTRY: - case job_skill::DETAILSTONE: + case job_skill::ENGRAVE_STONE: case job_skill::MASONRY: case job_skill::TANNER: case job_skill::WEAVING: @@ -288,15 +288,15 @@ command_result df_strangemood (color_ostream &out, vector & parameters) { if(parameters[i] == "help" || parameters[i] == "?") return CR_WRONG_USAGE; - else if(parameters[i] == "-force") + else if(parameters[i] == "--force") force = true; - else if(parameters[i] == "-unit") + else if(parameters[i] == "--unit") { unit = DFHack::Gui::getSelectedUnit(out); if (!unit) return CR_FAILURE; } - else if (parameters[i] == "-type") + else if (parameters[i] == "--type") { i++; if (i == parameters.size()) @@ -320,7 +320,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) return CR_WRONG_USAGE; } } - else if (parameters[i] == "-skill") + else if (parameters[i] == "--skill") { i++; if (i == parameters.size()) @@ -333,7 +333,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) else if (parameters[i] == "carpenter") skill = job_skill::CARPENTRY; else if (parameters[i] == "engraver") - skill = job_skill::DETAILSTONE; + skill = job_skill::ENGRAVE_STONE; else if (parameters[i] == "mason") skill = job_skill::MASONRY; else if (parameters[i] == "tanner") @@ -549,9 +549,9 @@ command_result df_strangemood (color_ostream &out, vector & parameters) if (type == mood_type::None) { if (soul && ( - (soul->personality.stress_level >= 500000) || - (soul->personality.stress_level >= 250000 && !rng.df_trandom(2)) || - (soul->personality.stress_level >= 100000 && !rng.df_trandom(10)) + (soul->personality.stress >= 500000) || + (soul->personality.stress >= 250000 && !rng.df_trandom(2)) || + (soul->personality.stress >= 100000 && !rng.df_trandom(10)) )) { switch (rng.df_trandom(2)) @@ -639,7 +639,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) case job_skill::CARPENTRY: job->job_type = job_type::StrangeMoodCarpenter; break; - case job_skill::DETAILSTONE: + case job_skill::ENGRAVE_STONE: case job_skill::WOODCRAFT: case job_skill::STONECRAFT: case job_skill::BONECARVE: @@ -749,7 +749,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) switch (skill) { case job_skill::MINING: - case job_skill::DETAILSTONE: + case job_skill::ENGRAVE_STONE: case job_skill::MASONRY: case job_skill::STONECRAFT: case job_skill::MECHANICS: @@ -1040,7 +1040,7 @@ command_result df_strangemood (color_ostream &out, vector & parameters) switch (skill) { case job_skill::MINING: - case job_skill::DETAILSTONE: + case job_skill::ENGRAVE_STONE: case job_skill::MASONRY: case job_skill::STONECRAFT: avoid_type = item_type::BLOCKS; From 853859e119e61bd07fbe97daf0d8275b3bbd8f3e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 09:09:39 -0800 Subject: [PATCH 0572/2222] refine ZScreen behavior around pausing, update docs --- docs/Quickstart.rst | 37 ++++++++++++++++++++----------------- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 35 ++++++++++++++++++++++------------- library/lua/gui.lua | 3 ++- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index a91256a1f..55cfe6f36 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -79,7 +79,7 @@ hover list. The second place to check is the DFHack control panel: `gui/control-panel`. It will give you an overview of which tools are currently enabled, and will allow you to toggle them on or off, see help text for them, or launch their dedicated -configuration UIs. You can launch the control panel from anywhere with the +configuration UIs. You can open the control panel from anywhere with the Ctrl-Shift-E hotkey or by selecting it from the logo hover list. In the control panel, you can also select which tools you'd like to be @@ -105,7 +105,7 @@ The bottom panel will show the full help text for the command you are running, allowing you to refer to the usage documentation and examples when you are typing your command. After you run a command, the bottom panel switches to command output mode, but you can get back to the help text by hitting Ctrl-T or clicking on the -``Showing`` selector. +``Help`` tab. How do DFHack in-game windows work? ----------------------------------- @@ -134,12 +134,12 @@ game is unpaused, it can continue to run while a DFHack window is open. If confi to do so in `gui/control-panel`, tools will initially pause the game to let you focus on the task at hand, but you can unpause like normal if you want. You can also interact with the map, scrolling it with the keyboard or mouse and selecting -units, buildings, and items. Some tools will capture all keyboard input, such as -tools with editable text fields, and some will force-pause the game if it makes -sense to, like `gui/quickfort`, since you cannot interact with the map normally -while trying to apply a blueprint. Windows for tools that force-pause the game -will have a pause icon in their upper right corner to indicate which tool is -preventing you from unpausing. +units, buildings, and items. Some tools will intercept all mouse clicks to allow +you to select regions on the map. When these tools have focus, you will not be able +to use the mouse to interact with map elements or pause/unpause the game. Therefore, +these tools will pause the game when they open, regardless of your settings in +`gui/control-panel`. You can still unpause with the keyboard (spacebar by default), +though. Where do I go next? ------------------- @@ -156,7 +156,7 @@ You can get to the launcher and its integrated autocomplete, history search, and help text by hitting backtick (\`) or Ctrl-Shift-D, or, of course, by running it from the logo hover list. -With those three tools, you have the complete DFHack tool suite at your +With those three interfaces, you have the complete DFHack tool suite at your fingertips. So what to run first? Here are a few commands to get you started. You can run them all from the launcher. @@ -168,13 +168,13 @@ using the DFHack `overlay` widget at the bottom of the manager orders panel. Next, try setting up `autochop` to automatically designate trees for chopping when you get low on usable logs. Run `gui/control-panel` and select ``autochop`` in the -list. Click on the button to the left of the name or hit Enter to enable it. You -can then click on the ``[configure]`` button to launch `gui/autochop` if you'd -like to customize its settings. If you have the extra screen space, you can go -ahead and set the `gui/autochop` window to minimal mode (click on the hint near -the upper right corner of the window or hit Alt-M) and click on the map so the -window loses keyboard focus. As you play the game, you can glance at the live -status panel to check on your stocks of wood. +``Fort`` list. Click on the button to the left of the name or hit Enter to enable +it. You can then click on the configure button (the gear icon) to launch +`gui/autochop` if you'd like to customize its settings. If you have the extra +screen space, you can go ahead and set the `gui/autochop` window to minimal mode +(click on the hint near the upper right corner of the window or hit Alt-M) and +click on the map so the window loses keyboard focus. As you play the game, you can +glance at the live status panel to check on your stocks of wood. Finally, let's do some fort design copy-pasting. Go to some bedrooms that you have set up in your fort. Run `gui/blueprint`, set a name for your blueprint by @@ -197,6 +197,9 @@ had designated the tiles yourself. Once the area is dug out, run `gui/quickfort` again and select the "/build" blueprint this time. Apply the blueprint in the dug-out area, and your furniture will be -designated. It's just that easy! +designated. It's just that easy! Note that `quickfort` uses `buildingplan` to place +buildings, so you don't even need to have the relevant furniture or building +materials in stock. The planned furniture/buildings will get built whenever you are +able to produce the building materials. There are many, many more tools to explore. Have fun! diff --git a/docs/changelog.txt b/docs/changelog.txt index 1d9f8e0f8..c2d958d28 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. ## Misc Improvements +- DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 1785afa8e..72c6a85ab 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4149,7 +4149,7 @@ underlying map, or even other DFHack ZScreen windows! That is, even when the DFHack tool window is visible, players will be able to use vanilla designation tools, select units, and scan/drag the map around. -At most one ZScreen can have keyboard focus at a time. That ZScreen's widgets +At most one ZScreen can have input focus at a time. That ZScreen's widgets will have a chance to handle the input before anything else. If unhandled, the input skips all unfocused ZScreens under that ZScreen and is passed directly to the first non-ZScreen viewscreen. There are class attributes that can be set to @@ -4168,8 +4168,8 @@ is dismissed. All this behavior is implemented in ``ZScreen:onInput()``, which subclasses **must not override**. Instead, ZScreen subclasses should delegate all input -processing to subviews. Consider using a `Window class`_ widget as your top -level input processor. +processing to subviews. Consider using a `Window class`_ widget subview as your +top level input processor. When rendering, the parent viewscreen is automatically rendered first, so subclasses do not have to call ``self:renderParent()``. Calls to ``logic()`` @@ -4179,8 +4179,8 @@ that passing ``logic()`` calls through to the underlying map is required for allowing the player to drag the map with the mouse. ZScreen subclasses can set attributes that control whether the game is paused when the ZScreen is shown and whether the game is forced to continue being paused while the ZScreen is shown. -If pausing is forced, child ``Window`` widgets will show a force-pause icon to -indicate which tool is forcing the pausing. +If pausing is forced, child ``Window`` widgets will show a force-pause indicator +to show which tool is forcing the pausing. ZScreen provides the following functions: @@ -4195,8 +4195,9 @@ ZScreen provides the following functions: * ``zscreen:isMouseOver()`` The default implementation iterates over the direct subviews of the ZScreen - subclass and sees if ``getMouseFramePos()`` returns a position for any of - them. Subclasses can override this function if that logic is not appropriate. + subclass (which usually only includes a single Window subview) and sees if + ``getMouseFramePos()`` returns a position for any of them. Subclasses can + override this function if that logic is not appropriate. * ``zscreen:hasFocus()`` @@ -4212,16 +4213,23 @@ ZScreen subclasses can set the following attributes: of the screen other than the tool window. If the player clicks on a different ZScreen window, focus still transfers to that other ZScreen. -* ``initial_pause`` (default: ``DEFAULT_INITIAL_PAUSE``) +* ``initial_pause`` (default: ``DEFAULT_INITIAL_PAUSE or not pass_mouse_clicks``) - Whether to pause the game when the ZScreen is shown. ``DEFAULT_INITIAL_PAUSE`` - defaults to ``true`` but can be set via running a command like:: + Whether to pause the game when the ZScreen is shown. If not explicitly set, + this attribute will be true if the system-wide ``DEFAULT_INITIAL_PAUSE`` is + ``true`` (which is its default value) or if the ``pass_mouse_clicks`` attribute + is ``false`` (see below). It depends on ``pass_mouse_clicks`` because if the + player normally pauses/unpauses the game with the mouse, they will not be able + to pause the game like they usually do while the ZScreen has focus. + ``DEFAULT_INITIAL_PAUSE`` can be customized permanently via `gui/control-panel` + or set for the session by running a command like:: :lua require('gui.widgets').DEFAULT_INITIAL_PAUSE = false * ``force_pause`` (default: ``false``) - Whether to ensure the game *stays* paused while the ZScreen is shown. + Whether to ensure the game *stays* paused while the ZScreen is shown, + regardless of whether it has input focus. * ``pass_pause`` (default: ``true``) @@ -4230,7 +4238,7 @@ ZScreen subclasses can set the following attributes: * ``pass_movement_keys`` (default: ``false``) - Whether to pass the map movement keys to the lower viewscreens if they ar not + Whether to pass the map movement keys to the lower viewscreens if they are not handled by this ZScreen. * ``pass_mouse_clicks`` (default: ``true``) @@ -4238,7 +4246,7 @@ ZScreen subclasses can set the following attributes: Whether to pass mouse clicks to the lower viewscreens if they are not handled by this ZScreen. -Here is an example skeleton for a ZScreen tool dialog:: +Here is an example skeleton for a ZScreen tool window:: local gui = require('gui') local widgets = require('gui.widgets') @@ -4265,6 +4273,7 @@ Here is an example skeleton for a ZScreen tool dialog:: MyScreen.ATTRS { focus_path='myscreen', -- set pause and passthrough attributes as appropriate + -- (but most tools can use the defaults) } function MyScreen:init() diff --git a/library/lua/gui.lua b/library/lua/gui.lua index c571c2389..a139ce570 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -709,7 +709,8 @@ ZScreen.ATTRS{ function ZScreen:preinit(args) if args.initial_pause == nil then - args.initial_pause = DEFAULT_INITIAL_PAUSE + args.initial_pause = DEFAULT_INITIAL_PAUSE or + (args.pass_mouse_clicks == false) end end From e5072a4f64fb3d6add4c4b6e08e89571ff56fe09 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 09:26:13 -0800 Subject: [PATCH 0573/2222] check for settings in the ATTRS, not the args --- library/lua/gui.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index a139ce570..25076aa6b 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -708,9 +708,9 @@ ZScreen.ATTRS{ } function ZScreen:preinit(args) - if args.initial_pause == nil then + if self.ATTRS.initial_pause == nil then args.initial_pause = DEFAULT_INITIAL_PAUSE or - (args.pass_mouse_clicks == false) + (self.ATTRS.pass_mouse_clicks == false) end end From 2fe0d8932ee3259cecabe5f491ae03814e38140a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 13 Feb 2023 21:29:24 +0000 Subject: [PATCH 0574/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 785ffbd40..6c67ce51a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 785ffbd4086a15cfd1716b8944cb62ca0ed6ed32 +Subproject commit 6c67ce51ab2f051bd07c5044dbbb308a375a970e diff --git a/scripts b/scripts index 9401e9e43..a7579ca14 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9401e9e43d44c04842c36530f97ecc00c12e6c9e +Subproject commit a7579ca14a7590803250bdfe83a3f0afa79881a1 From 6202b29c56474e92e4538caa3f98a09dbec05c8b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 13:39:28 -0800 Subject: [PATCH 0575/2222] adapt autolabor to new split game structure --- library/xml | 2 +- plugins/autolabor/autolabor.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/xml b/library/xml index 6c67ce51a..c48cd1d9f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 6c67ce51ab2f051bd07c5044dbbb308a375a970e +Subproject commit c48cd1d9f1c9aa285ed8712c2122085c00d22c80 diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 4eac48ab9..47ff6b0c6 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include @@ -51,7 +51,7 @@ using namespace df::enums; DFHACK_PLUGIN("autolabor"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(game); +REQUIRE_GLOBAL(game_extra); #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) @@ -414,7 +414,7 @@ static void enable_plugin(color_ostream &out) cleanup_state(); init_state(); - df::global::game->external_flag |= 1; // shut down DF's work detail system + df::global::game_extra->external_flag |= 1; // shut down DF's work detail system } DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) @@ -1084,7 +1084,7 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) enable_autolabor = false; setOptionEnabled(CF_ENABLED, false); - df::global::game->external_flag &= ~1; // reenable DF's work detail system + df::global::game_extra->external_flag &= ~1; // reenable DF's work detail system out << "Autolabor is disabled." << std::endl; } From f8d94afb7d7d7c44f9257c2cc3e36134833d0efd Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Mon, 13 Feb 2023 15:21:43 -0800 Subject: [PATCH 0576/2222] Add getSelectedCivZone to dfhack.gui Mostly helpful for my own automation in my custom lua scripts. Tested it with barracks, meeting areas, and pastures. --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 4 ++++ library/LuaApi.cpp | 1 + library/include/modules/Gui.h | 8 +++++++- library/modules/Gui.cpp | 25 +++++++++++++++++++++++-- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index df78568b5..d6e9a2695 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Documentation ## API +- ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for: - ``isGeldable()`` @@ -56,6 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``isPet()`` ## Lua +- ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 72c6a85ab..1a46cbff1 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1009,6 +1009,10 @@ General-purpose selections Returns the building selected via :kbd:`q`, :kbd:`t`, :kbd:`k` or :kbd:`i`. +* ``dfhack.gui.getSelectedCivZone([silent])`` + + Returns the zone currently selected via :kbd:`z` + * ``dfhack.gui.getSelectedPlant([silent])`` Returns the plant selected via :kbd:`k`. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index ce1f66091..2dc2ddf8b 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1476,6 +1476,7 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedItem), WRAPM(Gui, getSelectedBuilding), + WRAPM(Gui, getSelectedCivZone), WRAPM(Gui, getSelectedStockpile), WRAPM(Gui, getSelectedPlant), WRAPM(Gui, getAnyUnit), diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index b579dcc97..bbdaa0d39 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -105,11 +105,17 @@ namespace DFHack DFHACK_EXPORT df::item *getAnyItem(df::viewscreen *top); DFHACK_EXPORT df::item *getSelectedItem(color_ostream &out, bool quiet = false); - // A building is selected via 'q', 't' or 'i' (civzone) + // A building is selected via 'q', 't' or 'i' (?) DFHACK_EXPORT bool any_building_hotkey(df::viewscreen *top); DFHACK_EXPORT df::building *getAnyBuilding(df::viewscreen *top); DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false); + // A (civ)zone is selected via 'z' + DFHACK_EXPORT bool any_civzone_hotkey(df::viewscreen* top); + DFHACK_EXPORT df::building_civzonest *getAnyCivZone(df::viewscreen* top); + DFHACK_EXPORT df::building_civzonest *getSelectedCivZone(color_ostream& out, bool quiet = false); + + // A stockpile is selected via 'p' DFHACK_EXPORT bool any_stockpile_hotkey(df::viewscreen* top); DFHACK_EXPORT df::building_stockpilest *getAnyStockpile(df::viewscreen* top); DFHACK_EXPORT df::building_stockpilest *getSelectedStockpile(color_ostream& out, bool quiet = false); diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 56f8bc301..e3a7ba983 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -98,14 +98,15 @@ namespace DFHack using namespace df::enums; +using df::building_civzonest; +using df::global::game; using df::global::gamemode; using df::global::gps; using df::global::gview; using df::global::init; -using df::global::selection_rect; using df::global::plotinfo; +using df::global::selection_rect; using df::global::ui_menu_width; -using df::global::game; using df::global::world; /* TODO: understand how this changes for v50 @@ -1111,6 +1112,26 @@ df::building_stockpilest* Gui::getSelectedStockpile(color_ostream& out, bool qui return stockpile; } +bool Gui::any_civzone_hotkey(df::viewscreen* top) { + return getAnyCivZone(top) != NULL; +} + +df::building_civzonest *Gui::getAnyCivZone(df::viewscreen* top) { + if (matchFocusString("dwarfmode/Zone")) { + return game->main_interface.civzone.cur_bld; + } + return NULL; +} + +df::building_civzonest *Gui::getSelectedCivZone(color_ostream &out, bool quiet) { + df::building_civzonest *civzone = getAnyCivZone(Core::getTopViewscreen()); + + if (!civzone && !quiet) + out.printerr("No zone is selected in the UI"); + + return civzone; +} + df::building *Gui::getAnyBuilding(df::viewscreen *top) { using df::global::game; From bc76fd02e2fb32eceec14298bd1014f680a48411 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 17:35:02 -0800 Subject: [PATCH 0577/2222] prevent mouse clicks from bleeding through when a window is dismissed --- docs/changelog.txt | 1 + library/lua/gui.lua | 28 +++++++++++++++------------- plugins/lua/hotkeys.lua | 2 ++ plugins/lua/overlay.lua | 8 ++++++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d6e9a2695..66a136d1f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,6 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) +-@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window ## Documentation diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 25076aa6b..4a30d947a 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -697,6 +697,19 @@ DEFAULT_INITIAL_PAUSE = true local zscreen_inhibit_mouse_l = false +-- ensure underlying DF screens don't also react to handled clicks +function markMouseClicksHandled(keys) + if keys._MOUSE_L_DOWN then + -- note we can't clear mouse_lbut here. otherwise we break dragging, + df.global.enabler.mouse_lbut_down = 0 + zscreen_inhibit_mouse_l = true + end + if keys._MOUSE_R_DOWN then + df.global.enabler.mouse_rbut_down = 0 + df.global.enabler.mouse_rbut = 0 + end +end + ZScreen = defclass(ZScreen, Screen) ZScreen.ATTRS{ defocusable=true, @@ -782,16 +795,7 @@ function ZScreen:onInput(keys) end if ZScreen.super.onInput(self, keys) then - -- ensure underlying DF screens don't also react to handled clicks - if keys._MOUSE_L_DOWN then - -- note we can't clear mouse_lbut here. otherwise we break dragging, - df.global.enabler.mouse_lbut_down = 0 - zscreen_inhibit_mouse_l = true - end - if keys._MOUSE_R_DOWN then - df.global.enabler.mouse_rbut_down = 0 - df.global.enabler.mouse_rbut = 0 - end + markMouseClicksHandled(keys) return end @@ -801,9 +805,7 @@ function ZScreen:onInput(keys) return elseif keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self:dismiss() - -- ensure underlying DF screens don't also react to the rclick - df.global.enabler.mouse_rbut_down = 0 - df.global.enabler.mouse_rbut = 0 + markMouseClicksHandled(keys) return else if zscreen_inhibit_mouse_l then diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 09b1cea75..0eb09d244 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -262,12 +262,14 @@ function Menu:onInput(keys) local x = list:getMousePos() if x == 0 then -- clicked on icon self:onSubmit2(list:getSelected()) + df.global.enabler.mouse_lbut = 0 return true end if not self:getMouseFramePos() and not self.hotspot:getMousePos() then self.parent_view:dismiss() return true end + df.global.enabler.mouse_lbut = 0 end self:inputToSubviews(keys) return true -- we're modal diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index c3522c204..56ad72372 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -488,8 +488,12 @@ local function _feed_viewscreen_widgets(vs_name, keys) end function feed_viewscreen_widgets(vs_name, keys) - return _feed_viewscreen_widgets(vs_name, keys) or - _feed_viewscreen_widgets('all', keys) + if not _feed_viewscreen_widgets(vs_name, keys) and + not _feed_viewscreen_widgets('all', keys) then + return false + end + gui.markMouseClicksHandled(keys) + return true end local function _render_viewscreen_widgets(vs_name, dc) From 66229bb6691ee63cb465dea5715a411f5b1f81a8 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 14 Feb 2023 07:15:30 +0000 Subject: [PATCH 0578/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index c48cd1d9f..8e60e1c8e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c48cd1d9f1c9aa285ed8712c2122085c00d22c80 +Subproject commit 8e60e1c8e2de56882ab79dad008734b98a62e861 diff --git a/scripts b/scripts index a7579ca14..b13f351b2 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a7579ca14a7590803250bdfe83a3f0afa79881a1 +Subproject commit b13f351b22de50210b9b71d34806ebc2b84351ca From c4aa936c0df9c38fb319fb53e92b644cd319ea72 Mon Sep 17 00:00:00 2001 From: Christopher J Hammel Date: Tue, 14 Feb 2023 11:37:22 -0500 Subject: [PATCH 0579/2222] Update autolabor.cpp --- plugins/autolabor/autolabor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 47ff6b0c6..00b98d233 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -761,7 +761,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { df::building_tradedepotst* depot = (df::building_tradedepotst*) build; trader_requested = trader_requested || depot->trade_flags.bits.trader_requested; - INFO(cycle,out).print(trader_requested + TRACE(cycle,out).print(trader_requested ? "Trade depot found and trader requested, trader will be excluded from all labors.\n" : "Trade depot found but trader is not requested.\n" ); From d8afb6c6827a637f750d2aeb76648300a685455a Mon Sep 17 00:00:00 2001 From: silverflyone Date: Wed, 15 Feb 2023 15:31:50 +1100 Subject: [PATCH 0580/2222] Update Buildings.cpp Changed the StockpileIterator::operator++ to checks block based on <= 16 rather than <16. Previously it was not returning items at stockpile tiles at the block boundary. Identified as part of combine.lua changes. --- library/modules/Buildings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 389113483..2b6fc8ec8 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1643,10 +1643,10 @@ StockpileIterator& StockpileIterator::operator++() { while (current >= block->items.size()) { // Out of items in this block; find the next block to search. - if (block->map_pos.x + 16 < stockpile->x2) { + if (block->map_pos.x + 16 <= stockpile->x2) { block = Maps::getTileBlock(block->map_pos.x + 16, block->map_pos.y, stockpile->z); current = 0; - } else if (block->map_pos.y + 16 < stockpile->y2) { + } else if (block->map_pos.y + 16 <= stockpile->y2) { block = Maps::getTileBlock(stockpile->x1, block->map_pos.y + 16, stockpile->z); current = 0; } else { From 39f13b642f190e58e9eb8020157e26a3364380bc Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 15 Feb 2023 07:15:04 +0000 Subject: [PATCH 0581/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 8e60e1c8e..7639896c6 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8e60e1c8e2de56882ab79dad008734b98a62e861 +Subproject commit 7639896c68508d968c36d00bd165bbd513171048 diff --git a/scripts b/scripts index b13f351b2..9b6340be8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit b13f351b22de50210b9b71d34806ebc2b84351ca +Subproject commit 9b6340be8044989d562c19d7b8bd64dbf4af5e88 From 82cc4864422630f62bd1a9f39dd0f6a17cf72d3a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 15 Feb 2023 06:46:38 -0600 Subject: [PATCH 0582/2222] correct autolabor for structure update df-structures#561 changed def'n of `activity_info` which autolabor used --- plugins/autolabor/autolabor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 47ff6b0c6..c71b6aa67 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -835,8 +835,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) for (auto& act : plotinfo->activities) { if (!act) continue; - bool p1 = act->unit_actor == dwarfs[dwarf]; - bool p2 = act->unit_noble == dwarfs[dwarf]; + bool p1 = act->unit_actor == dwarfs[dwarf]->id; + bool p2 = act->unit_noble == dwarfs[dwarf]->id; if (p1 || p2) { From 8e577e7469b5f453d83d578e307f42a81e58ea08 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Mon, 13 Feb 2023 17:38:16 -0500 Subject: [PATCH 0583/2222] Persist confirm config --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 66a136d1f..5b718a37f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window +- `confirm`: configuration data is now persisted globally. ## Documentation From b4bd806134910ec4709dc8f836887591b9a6fa46 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Wed, 15 Feb 2023 16:10:22 -0500 Subject: [PATCH 0584/2222] Don't enable confirms on plugin enable --- plugins/confirm.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index aad1c17bc..a15b83055 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -612,15 +612,7 @@ DFhackCExport command_result plugin_init (color_ostream &out, vector apply(enable)) - return CR_FAILURE; - } - is_enabled = enable; - } + is_enabled = enable; if (is_enabled) { conf_lua::simple_call("check"); From 212026861fc9fb80455df21f14ed5a641465bd50 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Tue, 14 Feb 2023 20:33:33 -0500 Subject: [PATCH 0585/2222] WIP list fix --- library/lua/gui/widgets.lua | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index ffe26936d..9343be548 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1611,6 +1611,15 @@ function List:setChoices(choices, selected) end self:setSelected(selected) + + -- Check if page_top needs to be adjusted + if #self.choices - self.page_size < 0 then + self.page_top = 1 + elseif self.selected <= math.floor(self.page_size / 2) then + self.page_top = 1 + elseif self.selected >= #self.choices - math.floor(self.page_size / 2) then + self.page_top = #self.choices - self.page_size + 1 + end end function List:setSelected(selected) @@ -1651,10 +1660,26 @@ local function update_list_scrollbar(list) end function List:postComputeFrame(body) - self.page_size = math.max(1, math.floor(body.height / self.row_height)) - if #self.choices - self.page_size < 0 then + local row_count = math.floor(body.height / self.row_height) + self.page_size = math.max(1, row_count) + + local num_choices = #self.choices + if num_choices == 0 then self.page_top = 1 + update_list_scrollbar(self) + return end + + local max_page_top = math.max(1, num_choices - row_count + 1) + + if self.selected > num_choices - row_count then + self.page_top = max_page_top + elseif self.selected < self.page_top then + self.page_top = self.selected + else + self.page_top = math.max(1, self.selected - row_count + 1) + end + update_list_scrollbar(self) end From 8f190247e95052566eae7ac93489a88155a4f87a Mon Sep 17 00:00:00 2001 From: silverflyone Date: Thu, 16 Feb 2023 14:55:00 +1100 Subject: [PATCH 0586/2222] Buildings::StockpileIterator operator change added to FIxes. --- docs/changelog.txt | 1 + scripts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 66a136d1f..f3a2c0d25 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names +- ``Buildings::StockpileIterator``: check for stockpile items on block boundary. ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` diff --git a/scripts b/scripts index b13f351b2..3b23f3adf 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit b13f351b22de50210b9b71d34806ebc2b84351ca +Subproject commit 3b23f3adfeeb3e7e9541320d3fed4259ee12093d From 80972466a4f8393ae61e94bce2767b4cc8c0fa88 Mon Sep 17 00:00:00 2001 From: silverflyone Date: Thu, 16 Feb 2023 15:01:11 +1100 Subject: [PATCH 0587/2222] Revert "Buildings::StockpileIterator operator change added to FIxes." This reverts commit 8f190247e95052566eae7ac93489a88155a4f87a. --- docs/changelog.txt | 1 - scripts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index f3a2c0d25..66a136d1f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,7 +41,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names -- ``Buildings::StockpileIterator``: check for stockpile items on block boundary. ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` diff --git a/scripts b/scripts index 3b23f3adf..b13f351b2 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3b23f3adfeeb3e7e9541320d3fed4259ee12093d +Subproject commit b13f351b22de50210b9b71d34806ebc2b84351ca From 648af1c81c5ac007ac6490ef3198d7afd213afed Mon Sep 17 00:00:00 2001 From: silverflyone Date: Thu, 16 Feb 2023 15:03:09 +1100 Subject: [PATCH 0588/2222] Buildings::StockpileIterator operator change added to FIxes. --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 66a136d1f..f3a2c0d25 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names +- ``Buildings::StockpileIterator``: check for stockpile items on block boundary. ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` From 36935056ea7b1687e045ab047fa6a533154e69ae Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 16 Feb 2023 07:15:08 +0000 Subject: [PATCH 0589/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 7639896c6..54d3f896c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7639896c68508d968c36d00bd165bbd513171048 +Subproject commit 54d3f896c9938f79758fed07c20da78c78094723 diff --git a/scripts b/scripts index 9b6340be8..db797cbe8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9b6340be8044989d562c19d7b8bd64dbf4af5e88 +Subproject commit db797cbe8806ca3b56f86ba6b1784d3085064e1e From 5154eb181af73bc46c4f1e67a6093363bd8797d5 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Sat, 11 Feb 2023 08:44:34 -0500 Subject: [PATCH 0590/2222] Add Tab/TabBar to widgets --- docs/changelog.txt | 3 +- docs/dev/Lua API.rst | 35 +++++++++ library/lua/gui/widgets.lua | 145 ++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index f3a2c0d25..a222a6ee7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,7 +52,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - - Units module: added new predicates for: - ``isGeldable()`` - ``isMarkedForGelding()`` @@ -60,8 +59,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently - - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. +- Added ``widgets.TabBar`` and ``widgets.Tab`` (migrated from control-panel.lua) ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 1a46cbff1..7ed2d153b 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5028,6 +5028,41 @@ The widget implements: Same as with an ordinary list. +TabBar class +------------ + +This widget implements a set of one or more tabs to allow navigation between groups of content. Tabs automatically wrap on +the width of the window and will continue rendering on the next line(s) if all tabs cannot fit on a single line. + +:key: Specifies a keybinding that can be used to switch to the next tab. +:key_back: Specifies a keybinding that can be used to switch to the previous tab. +:labels: A table of strings; entry representing the label text for a single tab. The order of the entries + determines the order the tabs will appear in. +:get_cur_page: Returns the current "page". This function does not have a default implementation; you must provide + an implementation that returns the current value of whichever variable your script uses to keep track of the + current "page" (this does not need to relate to an actual Pages widget). +:on_select: Callback executed when a tab is selected. It receives the selected tab index as an argument. Your implementation + should likely update the value of whichever variable your script uses to keep track of the current page. +:active_tab_pens: A table of pens used to render active tabs. See the default implementation in widgets.lua for an example + of how to construct the table. Leave unspecified to use the default pens. +:inactive_tab_pens: A table of pens used to render inactive tabs. See the default implementation in widgets.lua for an example + of how to construct the table. Leave unspecified to use the default pens. +:get_pens: A function used to determine which pens should be used to render a tab. Receives the index of the tab as the first + argument and the TabBar widget itself as the second. The default implementation, which will handle most situations, + returns ``self.active_tab_pens``, if ``self.get_cur_page() == idx``, otherwise returns ``self.inactive_tab_pens``. + +Tab class +--------- + +This widget implements a single clickable tab and is the main component of the TabBar widget. Usage of the ``TabBar`` +widget does not require direct usage of ``Tab``. + +:id: The id of the tab. +:label: The text displayed on the tab. +:on_select: Callback executed when the tab is selected. +:get_pens: A function that is used during ``Tab:onRenderBody`` to determine the pens that should be used for drawing. See the + usage of ``Tab`` in ``TabBar:init()`` for an example. See the default value of ``active_tab_pens`` or ``inactive_tab_pens`` + in ``TabBar`` for an example of how to construct pens. .. _lua-plugins: diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index ffe26936d..7276efefe 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2091,4 +2091,149 @@ function FilteredList:onFilterChar(char, text) return true end +local DEFAULT_ACTIVE_TAB_PENS = { + text_mode_tab_pen=to_pen{fg=COLOR_YELLOW}, + text_mode_label_pen=to_pen{fg=COLOR_WHITE}, + lt=to_pen{tile=1005, write_to_lower=true}, + lt2=to_pen{tile=1006, write_to_lower=true}, + t=to_pen{tile=1007, fg=COLOR_BLACK, write_to_lower=true, top_of_text=true}, + rt2=to_pen{tile=1008, write_to_lower=true}, + rt=to_pen{tile=1009, write_to_lower=true}, + lb=to_pen{tile=1015, write_to_lower=true}, + lb2=to_pen{tile=1016, write_to_lower=true}, + b=to_pen{tile=1017, fg=COLOR_BLACK, write_to_lower=true, bottom_of_text=true}, + rb2=to_pen{tile=1018, write_to_lower=true}, + rb=to_pen{tile=1019, write_to_lower=true}, +} + +local DEFAULT_INACTIVE_TAB_PENS = { + text_mode_tab_pen=to_pen{fg=COLOR_BROWN}, + text_mode_label_pen=to_pen{fg=COLOR_DARKGREY}, + lt=to_pen{tile=1000, write_to_lower=true}, + lt2=to_pen{tile=1001, write_to_lower=true}, + t=to_pen{tile=1002, fg=COLOR_WHITE, write_to_lower=true, top_of_text=true}, + rt2=to_pen{tile=1003, write_to_lower=true}, + rt=to_pen{tile=1004, write_to_lower=true}, + lb=to_pen{tile=1010, write_to_lower=true}, + lb2=to_pen{tile=1011, write_to_lower=true}, + b=to_pen{tile=1012, fg=COLOR_WHITE, write_to_lower=true, bottom_of_text=true}, + rb2=to_pen{tile=1013, write_to_lower=true}, + rb=to_pen{tile=1014, write_to_lower=true}, +} + +--------- +-- Tab -- +--------- + +Tab = defclass(Tabs, Widget) +Tab.ATTRS{ + id=DEFAULT_NIL, + label=DEFAULT_NIL, + on_select=DEFAULT_NIL, + get_pens=DEFAULT_NIL, +} + +function Tab:preinit(init_table) + init_table.frame = init_table.frame or {} + init_table.frame.w = #init_table.label + 4 + init_table.frame.h = 2 +end + +function Tab:onRenderBody(dc) + local pens = self.get_pens() + dc:seek(0, 0) + if dfhack.screen.inGraphicsMode() then + dc:char(nil, pens.lt):char(nil, pens.lt2) + for i=1,#self.label do + dc:char(self.label:sub(i,i), pens.t) + end + dc:char(nil, pens.rt2):char(nil, pens.rt) + dc:seek(0, 1) + dc:char(nil, pens.lb):char(nil, pens.lb2) + for i=1,#self.label do + dc:char(self.label:sub(i,i), pens.b) + end + dc:char(nil, pens.rb2):char(nil, pens.rb) + else + local tp = pens.text_mode_tab_pen + dc:char(' ', tp):char('/', tp) + for i=1,#self.label do + dc:char('-', tp) + end + dc:char('\\', tp):char(' ', tp) + dc:seek(0, 1) + dc:char('/', tp):char('-', tp) + dc:string(self.label, pens.text_mode_label_pen) + dc:char('-', tp):char('\\', tp) + end +end + +function Tab:onInput(keys) + if Tab.super.onInput(self, keys) then return true end + if keys._MOUSE_L_DOWN and self:getMousePos() then + self.on_select(self.id) + return true + end +end + +------------- +-- Tab Bar -- +------------- + +TabBar = defclass(TabBar, ResizingPanel) +TabBar.ATTRS{ + labels=DEFAULT_NIL, + on_select=DEFAULT_NIL, + get_cur_page=DEFAULT_NIL, + active_tab_pens=DEFAULT_ACTIVE_TAB_PENS, + inactive_tab_pens=DEFAULT_INACTIVE_TAB_PENS, + get_pens=DEFAULT_NIL, + switch_tab_key=DEFAULT_NIL, +} + +function TabBar:init() + for idx,label in ipairs(self.labels) do + self:addviews{ + Tab{ + frame={t=0, l=0}, + id=idx, + label=label, + on_select=self.on_select, + get_pens=self.get_pens and function() + return self.get_pens(idx, self) + end or function() + if self.get_cur_page() == idx then + return self.active_tab_pens + end + + return self.inactive_tab_pens + end, + } + } + end +end + +function TabBar:postComputeFrame(body) + local t, l, width = 0, 0, body.width + for _,tab in ipairs(self.subviews) do + if l > 0 and l + tab.frame.w > width then + t = t + 2 + l = 0 + end + tab.frame.t = t + tab.frame.l = l + l = l + tab.frame.w + end +end + +function TabBar:onInput(keys) + if TabBar.super.onInput(self, keys) then return true end + if self.switch_tab_key and keys[self.switch_tab_key] then + local zero_idx = self.get_cur_page() - 1 + local next_zero_idx = (zero_idx + 1) % #self.labels + self.on_select(next_zero_idx + 1) + return true + end +end + return _ENV From 4167c2e65252ce9287bcd57468b1a934197af894 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 16 Feb 2023 02:56:52 -0500 Subject: [PATCH 0591/2222] Add key_back, rename switch_tab_key to key --- library/lua/gui/widgets.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 7276efefe..77c6c2c28 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2188,7 +2188,8 @@ TabBar.ATTRS{ active_tab_pens=DEFAULT_ACTIVE_TAB_PENS, inactive_tab_pens=DEFAULT_INACTIVE_TAB_PENS, get_pens=DEFAULT_NIL, - switch_tab_key=DEFAULT_NIL, + key=DEFAULT_NIL, + key_back=DEFAULT_NIL, } function TabBar:init() @@ -2228,12 +2229,18 @@ end function TabBar:onInput(keys) if TabBar.super.onInput(self, keys) then return true end - if self.switch_tab_key and keys[self.switch_tab_key] then + if self.key and keys[self.key] then local zero_idx = self.get_cur_page() - 1 local next_zero_idx = (zero_idx + 1) % #self.labels self.on_select(next_zero_idx + 1) return true end + if self.key_back and keys[self.key_back] then + local zero_idx = self.get_cur_page() - 1 + local prev_zero_idx = (zero_idx - 1) % #self.labels + self.on_select(prev_zero_idx + 1) + return true + end end return _ENV From bc47a0b2e442e84f7f35b62822fe2d54003760a3 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Thu, 16 Feb 2023 03:14:05 -0500 Subject: [PATCH 0592/2222] More prescriptive docs attempt --- docs/dev/Lua API.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 7ed2d153b..02260513f 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5038,11 +5038,11 @@ the width of the window and will continue rendering on the next line(s) if all t :key_back: Specifies a keybinding that can be used to switch to the previous tab. :labels: A table of strings; entry representing the label text for a single tab. The order of the entries determines the order the tabs will appear in. -:get_cur_page: Returns the current "page". This function does not have a default implementation; you must provide - an implementation that returns the current value of whichever variable your script uses to keep track of the - current "page" (this does not need to relate to an actual Pages widget). -:on_select: Callback executed when a tab is selected. It receives the selected tab index as an argument. Your implementation - should likely update the value of whichever variable your script uses to keep track of the current page. +:on_select: Callback executed when a tab is selected. It receives the selected tab index as an argument. The provided function + should update the value of whichever variable your script uses to keep track of the currently selected tab. +:get_cur_page: The function used by the TabBar to determine which Tab is currently selected. The function you provide should + return an integer that corresponds to the non-zero index of the currently selected Tab (i.e. whatever variable + you update in your ``on_select`` callback) :active_tab_pens: A table of pens used to render active tabs. See the default implementation in widgets.lua for an example of how to construct the table. Leave unspecified to use the default pens. :inactive_tab_pens: A table of pens used to render inactive tabs. See the default implementation in widgets.lua for an example From 4d9deb8eca71674c20b534b227cdc5af598870ee Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 16 Feb 2023 14:37:10 -0600 Subject: [PATCH 0593/2222] tailor: try to squash toad clothing bug this adds a test for an unmapped clothing size which will at least stop the making of toad-sized clothing. a diagnostic is issued when this happens as it is a bug --- plugins/tailor.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 5511e2b5d..49153fd3b 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -405,6 +405,13 @@ public: std::tie(ty, sub, size) = o.first; int count = o.second; + if (sizes.count(size) == 0) + { + WARN(cycle).print("tailor: cannot determine race for clothing of size %d, skipped\n", + size); + continue; + } + if (count > 0) { std::vector v; From 54013b4400a5568527c71e4dfb116237c207be64 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 16 Feb 2023 15:33:55 -0600 Subject: [PATCH 0594/2222] add support for adamantine cloth off by default because really now also downgraded "weird cloth item" message from WARN to DEBUG --- plugins/tailor.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 49153fd3b..4355c2081 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -56,6 +56,7 @@ enum ConfigValues { CONFIG_CLOTH_IDX = 2, CONFIG_YARN_IDX = 3, CONFIG_LEATHER_IDX = 4, + CONFIG_ADAMANTINE_IDX = 5, }; static int get_config_val(PersistentDataItem &c, int index) { @@ -119,10 +120,11 @@ static const MatType M_SILK = MatType("silk", df::job_material_category::mask_silk, df::armor_general_flags::SOFT), M_CLOTH = MatType("cloth", df::job_material_category::mask_cloth, df::armor_general_flags::SOFT), M_YARN = MatType("yarn", df::job_material_category::mask_yarn, df::armor_general_flags::SOFT), - M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER); + M_LEATHER = MatType("leather", df::job_material_category::mask_leather, df::armor_general_flags::LEATHER), + M_ADAMANTINE = MatType("adamantine", df::job_material_category::mask_strand, df::armor_general_flags::SOFT); -static const std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; -static std::list material_order = all_materials; +static const std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER, M_ADAMANTINE }; +static std::list material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; // M_ADAMANTINE is not included by default static struct BadFlags { uint32_t whole; @@ -208,11 +210,13 @@ public: supply[M_CLOTH] += ss; else if (mat.material->flags.is_set(df::material_flags::YARN)) supply[M_YARN] += ss; + else if (mat.material->flags.is_set(df::material_flags::STOCKPILE_THREAD_METAL)) + supply[M_ADAMANTINE] += ss; else { std::string d; i->getItemDescription(&d, 0); - WARN(cycle).print("tailor: weird cloth item found: %s (%d)\n", d.c_str(), i->id); + DEBUG(cycle).print("tailor: weird cloth item found: %s (%d), material_flags = %0x\n", d.c_str(), i->id); } } } @@ -224,7 +228,8 @@ public: supply[M_LEATHER] += i->getStackSize(); } - DEBUG(cycle).print("tailor: available silk %d yarn %d cloth %d leather %d\n", supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER]); + DEBUG(cycle).print("tailor: available silk %d yarn %d cloth %d leather %d adamantine %d\n", + supply[M_SILK], supply[M_YARN], supply[M_CLOTH], supply[M_LEATHER], supply[M_ADAMANTINE]); } void scan_replacements() From f437a83dba1d34890531ebd4e9850fe64a53fb9d Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 16 Feb 2023 15:38:18 -0600 Subject: [PATCH 0595/2222] add changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 85750890c..17db6ce03 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: check for stockpile items on block boundary. +- `tailor`: block making clothing sized for toads, add support for adamantine cloth (off by default), and reduce logging spam ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` From f6df3ff3354880310b35785fec883609828329a5 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 16 Feb 2023 15:39:10 -0600 Subject: [PATCH 0596/2222] remove thing i thought i already removed --- plugins/tailor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 4355c2081..f0f2c0b68 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -216,7 +216,7 @@ public: { std::string d; i->getItemDescription(&d, 0); - DEBUG(cycle).print("tailor: weird cloth item found: %s (%d), material_flags = %0x\n", d.c_str(), i->id); + DEBUG(cycle).print("tailor: weird cloth item found: %s (%d)\n", d.c_str(), i->id); } } } From d222092b30940e31289fc91d6b4831722eb215e3 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 17 Feb 2023 07:15:06 +0000 Subject: [PATCH 0597/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index db797cbe8..e4ba126f6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit db797cbe8806ca3b56f86ba6b1784d3085064e1e +Subproject commit e4ba126f6e1193d0a18386c1f1efedb9faf1458f From 456020fb383da038fd13a2a37499f58faa5cece3 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 29 Jan 2023 13:53:50 -0800 Subject: [PATCH 0598/2222] Adds todo comments to dig-now.cpp for issue #2720 --- plugins/dig-now.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 9945ee2b0..6836a6331 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -320,8 +320,10 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, std::vector &dug_tiles) { df::tiletype tt = map.tiletypeAt(pos); - if (!is_diggable(map, pos, tt)) + if (!is_diggable(map, pos, tt)) { + out.print("dig_tile: not diggable\n"); return false; + } df::tiletype target_type = df::tiletype::Void; switch(designation) { @@ -339,19 +341,23 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, case df::tile_dig_designation::Channel: { DFCoord pos_below(pos.x, pos.y, pos.z-1); + // todo: does can_dig_channel return false? if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); - if (map.ensureBlockAt(pos_above)) + if (map.ensureBlockAt(pos_above)) { remove_ramp_top(map, pos_above); - df::tile_dig_designation td_below = - map.designationAt(pos_below).bits.dig; - if (dig_tile(out, map, pos_below, - df::tile_dig_designation::Ramp, dug_tiles)) { + } + df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; + + // todo: what is this chain of dig_tile(below)? + if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); - if (td_below == df::tile_dig_designation::Default) + if (td_below == df::tile_dig_designation::Default) { + // todo: removing ramp? dig_tile(out, map, pos_below, td_below, dug_tiles); + } return true; } } @@ -407,7 +413,7 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, if (target_type == df::tiletype::Void || target_type == tt) return false; - dug_tiles.push_back(dug_tile_info(map, pos)); + dug_tiles.emplace_back(map, pos); dig_type(map, pos, target_type); // let light filter down to newly exposed tiles @@ -606,14 +612,17 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, if (!Maps::getTileBlock(x, y, z)) continue; + // todo: check if tile is in the job list with a dig type + DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (td.bits.dig != df::tile_dig_designation::No && - !to.bits.dig_marked) { + if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { std::vector dug_tiles; + + // todo: check why dig_tile doesn't dig the second layer of channels if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { - for (auto info : dug_tiles) { + for (auto info: dug_tiles) { td = map.designationAt(info.pos); td.bits.dig = df::tile_dig_designation::No; map.setDesignationAt(info.pos, td); @@ -629,6 +638,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, } } } + // todo: check mark mode of smooth designations } else if (td.bits.smooth == 1) { if (smooth_tile(out, map, pos)) { to = map.occupancyAt(pos); @@ -636,9 +646,9 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, map.setDesignationAt(pos, td); } } else if (to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { if (carve_tile(map, pos, to)) { to = map.occupancyAt(pos); to.bits.carve_track_north = 0; From 838acfdf223270c9cdd2936a69cb256d8576287e Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 7 Feb 2023 16:32:13 -0800 Subject: [PATCH 0599/2222] Adds a few log lines to dig-now --- plugins/dig-now.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 6836a6331..9f961c817 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -6,6 +6,7 @@ #include "PluginManager.h" #include "TileTypes.h" #include "LuaTools.h" +#include "Debug.h" #include "modules/Buildings.h" #include "modules/Gui.h" @@ -26,10 +27,23 @@ #include #include +#include + DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); +// Debugging +namespace DFHack { + DBG_DECLARE(dignow, general, DebugCategory::LINFO); + DBG_DECLARE(dignow, channels, DebugCategory::LINFO); +} + +#define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 +#define COORDARGS(id) id.x, id.y, id.z + +// todo: integrate logging for debugging the layered channel problem + using namespace DFHack; struct boulder_percent_options { @@ -321,10 +335,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, df::tiletype tt = map.tiletypeAt(pos); if (!is_diggable(map, pos, tt)) { - out.print("dig_tile: not diggable\n"); + DEBUG(general).print("dig_tile: not diggable\n"); return false; } + /** The algorithm process seems to be: + * for each tile + * check for a designation + * if a designation exists send it to dig_tile + * + * dig_tile (below) then digs the layer below the channel designated tile + * thereby changing it and causing its designation to be lost + * */ + df::tiletype target_type = df::tiletype::Void; switch(designation) { case df::tile_dig_designation::Default: @@ -344,6 +367,7 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, // todo: does can_dig_channel return false? if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { + TRACE(channels).print("dig_tile: channeling at (" COORD ")\n",COORDARGS(pos_below)); target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); if (map.ensureBlockAt(pos_above)) { @@ -360,6 +384,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, } return true; } + } else { + DEBUG(channels).print("dig_tile: failed to channel at (" COORD ")\n", COORDARGS(pos_below)); } break; } @@ -414,6 +440,7 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, return false; dug_tiles.emplace_back(map, pos); + TRACE(general).print("dig_tile: digging the designation tile at (" COORD ")\n",COORDARGS(pos)); dig_type(map, pos, target_type); // let light filter down to newly exposed tiles @@ -613,6 +640,8 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, continue; // todo: check if tile is in the job list with a dig type + // todo: if it is cancel the job. Then check if the designation is removed on the map + // todo: if the designation does disappear on the map, just rewrite things to queue the designation info that needs to be processed DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); From c990def894429c43df0f977661287489a3d3c406 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 13 Feb 2023 17:35:17 -0800 Subject: [PATCH 0600/2222] Adds a designation buffer in dig-now's processing algorithm This may fix the issue seen when channeling consecutive layers, needs to be tested --- plugins/dig-now.cpp | 123 ++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 9f961c817..2f63c54da 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -15,6 +15,7 @@ #include "modules/Random.h" #include "modules/Units.h" #include "modules/World.h" +#include "modules/EventManager.h" #include #include @@ -28,6 +29,7 @@ #include #include +#include DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); @@ -46,6 +48,31 @@ namespace DFHack { using namespace DFHack; +struct designation{ + df::coord pos; + df::tile_designation type; + df::tile_occupancy occupancy; + designation(const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), type(td), occupancy(to) {} + + bool operator==(const designation &rhs) const { + return pos == rhs.pos; + } + + bool operator!=(const designation &rhs) const { + return !(rhs == *this); + } +}; + +namespace std { + template<> + struct hash { + std::size_t operator()(const designation &c) const { + std::hash hash_coord; + return hash_coord(c.pos); + } + }; +} + struct boulder_percent_options { // percent chance ([0..100]) for creating a boulder for the given rock type uint32_t layer; @@ -367,7 +394,7 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, // todo: does can_dig_channel return false? if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { - TRACE(channels).print("dig_tile: channeling at (" COORD ")\n",COORDARGS(pos_below)); + TRACE(channels).print("dig_tile: channeling at (" COORD ") [can_dig_channel: true]\n",COORDARGS(pos_below)); target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); if (map.ensureBlockAt(pos_above)) { @@ -375,17 +402,16 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, } df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; - // todo: what is this chain of dig_tile(below)? + // todo: digging to floor below? if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); if (td_below == df::tile_dig_designation::Default) { - // todo: removing ramp? dig_tile(out, map, pos_below, td_below, dug_tiles); } return true; } } else { - DEBUG(channels).print("dig_tile: failed to channel at (" COORD ")\n", COORDARGS(pos_below)); + DEBUG(channels).print("dig_tile: failed to channel at (" COORD ") [can_dig_channel: false]\n", COORDARGS(pos_below)); } break; } @@ -630,6 +656,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, rng.init(); + std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { for (int16_t y = options.start.y; y <= options.end.y; ++y) { @@ -646,48 +673,58 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { - std::vector dug_tiles; - - // todo: check why dig_tile doesn't dig the second layer of channels - if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { - for (auto info: dug_tiles) { - td = map.designationAt(info.pos); - td.bits.dig = df::tile_dig_designation::No; - map.setDesignationAt(info.pos, td); - - dug_coords.push_back(info.pos); - refresh_adjacent_smooth_walls(map, info.pos); - if (info.imat < 0) - continue; - if (produces_item(options.boulder_percents, - map, rng, info)) { - auto k = std::make_pair(info.itype, info.imat); - item_coords[k].push_back(info.pos); - } - } - } - // todo: check mark mode of smooth designations - } else if (td.bits.smooth == 1) { - if (smooth_tile(out, map, pos)) { - to = map.occupancyAt(pos); - td.bits.smooth = 0; - map.setDesignationAt(pos, td); - } - } else if (to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { - if (carve_tile(map, pos, to)) { - to = map.occupancyAt(pos); - to.bits.carve_track_north = 0; - to.bits.carve_track_east = 0; - to.bits.carve_track_south = 0; - to.bits.carve_track_west = 0; - map.setOccupancyAt(pos, to); + + // we're only buffering designations, so that processing doesn't affect what we're buffering + buffer.emplace(pos, td, to); + } + } + } + + // process designations + for(auto &d : buffer) { + auto pos = d.pos; + auto td = d.type; + auto to = d.occupancy; + + if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { + std::vector dug_tiles; + + if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { + for (auto info: dug_tiles) { + td = map.designationAt(info.pos); + td.bits.dig = df::tile_dig_designation::No; + map.setDesignationAt(info.pos, td); + + dug_coords.push_back(info.pos); + refresh_adjacent_smooth_walls(map, info.pos); + if (info.imat < 0) + continue; + if (produces_item(options.boulder_percents, + map, rng, info)) { + auto k = std::make_pair(info.itype, info.imat); + item_coords[k].push_back(info.pos); } } } + // todo: check mark mode of smooth designations + } else if (td.bits.smooth == 1) { + if (smooth_tile(out, map, pos)) { + to = map.occupancyAt(pos); + td.bits.smooth = 0; + map.setDesignationAt(pos, td); + } + } else if (to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + if (carve_tile(map, pos, to)) { + to = map.occupancyAt(pos); + to.bits.carve_track_north = 0; + to.bits.carve_track_east = 0; + to.bits.carve_track_south = 0; + to.bits.carve_track_west = 0; + map.setOccupancyAt(pos, to); + } } } From 1e21f1ece954e3ff470c0a592228a58acc68db13 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 13 Feb 2023 18:02:33 -0800 Subject: [PATCH 0601/2222] Adds validity checks before adding to the dig-now buffer also adds job cancellation for designation jobs, for testing a solution to issue 2471 --- plugins/dig-now.cpp | 75 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 2f63c54da..0c9a79462 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -16,6 +16,7 @@ #include "modules/Units.h" #include "modules/World.h" #include "modules/EventManager.h" +#include "modules/Job.h" #include #include @@ -30,6 +31,7 @@ #include #include +#include DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); @@ -73,6 +75,56 @@ namespace std { }; } +class DigJobs { +private: + std::unordered_map designations; + std::unordered_map jobs; +public: + void load() { + designations.clear(); + df::job_list_link* node = df::global::world->jobs.list.next; + while (node) { + df::job* job = node->item; + jobs.emplace(job->pos, job); + node = node->next; + switch (job->job_type){ + case df::enums::job_type::Dig: + designations.emplace(job->pos, df::tile_dig_designation::Default); + break; + case df::enums::job_type::DigChannel: + designations.emplace(job->pos, df::tile_dig_designation::Channel); + break; + case df::enums::job_type::CarveRamp: + designations.emplace(job->pos, df::tile_dig_designation::Ramp); + break; + case df::enums::job_type::CarveUpwardStaircase: + designations.emplace(job->pos, df::tile_dig_designation::UpStair); + break; + case df::enums::job_type::CarveDownwardStaircase: + designations.emplace(job->pos, df::tile_dig_designation::DownStair); + break; + case df::enums::job_type::CarveUpDownStaircase: + designations.emplace(job->pos, df::tile_dig_designation::UpDownStair); + break; + default: + break; + } + } + } + void remove(const df::coord &pos) { + if(jobs.count(pos)) { + Job::removeJob(jobs[pos]); + jobs.erase(pos); + } + } + df::tile_dig_designation get(const df::coord &pos) { + if (designations.count(pos)) { + return designations[pos]; + } + return df::enums::tile_dig_designation::No; + } +}; + struct boulder_percent_options { // percent chance ([0..100]) for creating a boulder for the given rock type uint32_t layer; @@ -653,7 +705,9 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, item_coords_t &item_coords, const dig_now_options &options) { MapExtras::MapCache map; Random::MersenneRNG rng; + DigJobs jobs; + jobs.load(); rng.init(); std::unordered_set buffer; @@ -666,16 +720,25 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, if (!Maps::getTileBlock(x, y, z)) continue; - // todo: check if tile is in the job list with a dig type - // todo: if it is cancel the job. Then check if the designation is removed on the map - // todo: if the designation does disappear on the map, just rewrite things to queue the designation info that needs to be processed - DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); + if (jobs.get(pos) != df::enums::tile_dig_designation::No) { + // todo: check if the designation is removed from the map + jobs.remove(pos); + // if it does get removed, then we're gonna buffer the jobs info then remove the job + } + + if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) + || td.bits.smooth == 1 + || to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { - // we're only buffering designations, so that processing doesn't affect what we're buffering - buffer.emplace(pos, td, to); + // we're only buffering designations, so that processing doesn't affect what we're buffering + buffer.emplace(pos, td, to); + } } } } From 8beb947c82494bf1c08b74b48ef668cc3ace0ad9 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 15 Feb 2023 13:03:31 -0800 Subject: [PATCH 0602/2222] Revises job scanning for dig-now --- plugins/dig-now.cpp | 74 ++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 0c9a79462..68a5361a5 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -54,6 +54,7 @@ struct designation{ df::coord pos; df::tile_designation type; df::tile_occupancy occupancy; + designation() = default; designation(const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), type(td), occupancy(to) {} bool operator==(const designation &rhs) const { @@ -75,40 +76,62 @@ namespace std { }; } -class DigJobs { +class DesignationJobs { private: - std::unordered_map designations; + std::unordered_map designations; std::unordered_map jobs; public: - void load() { + void load(MapExtras::MapCache &map) { designations.clear(); df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; jobs.emplace(job->pos, job); node = node->next; + df::tile_designation td = map.designationAt(job->pos); + df::tile_occupancy to = map.occupancyAt(job->pos); + const auto ctd = td.whole; + const auto cto = to.whole; switch (job->job_type){ - case df::enums::job_type::Dig: - designations.emplace(job->pos, df::tile_dig_designation::Default); + case job_type::Dig: + td.bits.dig = tile_dig_designation::Default; break; - case df::enums::job_type::DigChannel: - designations.emplace(job->pos, df::tile_dig_designation::Channel); + case job_type::DigChannel: + td.bits.dig = tile_dig_designation::Channel; break; - case df::enums::job_type::CarveRamp: - designations.emplace(job->pos, df::tile_dig_designation::Ramp); + case job_type::CarveRamp: + td.bits.dig = tile_dig_designation::Ramp; break; - case df::enums::job_type::CarveUpwardStaircase: - designations.emplace(job->pos, df::tile_dig_designation::UpStair); + case job_type::CarveUpwardStaircase: + td.bits.dig = tile_dig_designation::UpStair; break; - case df::enums::job_type::CarveDownwardStaircase: - designations.emplace(job->pos, df::tile_dig_designation::DownStair); + case job_type::CarveDownwardStaircase: + td.bits.dig = tile_dig_designation::DownStair; break; - case df::enums::job_type::CarveUpDownStaircase: - designations.emplace(job->pos, df::tile_dig_designation::UpDownStair); + case job_type::CarveUpDownStaircase: + td.bits.dig = tile_dig_designation::UpDownStair; + break; + case job_type::DetailWall: + case job_type::DetailFloor: { + df::tiletype tt = map.tiletypeAt(job->pos); + if (tileSpecial(tt) != df::tiletype_special::SMOOTH) { + td.bits.smooth = 1; + } + break; + } + case job_type::CarveTrack: + to.bits.carve_track_north = 0 < (job->item_category.whole & 18); + to.bits.carve_track_south = 0 < (job->item_category.whole & 19); + to.bits.carve_track_west = 0 < (job->item_category.whole & 20); + to.bits.carve_track_east = 0 < (job->item_category.whole & 21); break; default: break; } + if (ctd != td.whole || cto != to.whole) { + // we found a designation job + designations.emplace(job->pos, designation(job->pos, td, to)); + } } } void remove(const df::coord &pos) { @@ -117,11 +140,14 @@ public: jobs.erase(pos); } } - df::tile_dig_designation get(const df::coord &pos) { + designation get(const df::coord &pos) { if (designations.count(pos)) { return designations[pos]; } - return df::enums::tile_dig_designation::No; + return {}; + } + bool count(const df::coord &pos) { + return jobs.count(pos); } }; @@ -705,9 +731,9 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, item_coords_t &item_coords, const dig_now_options &options) { MapExtras::MapCache map; Random::MersenneRNG rng; - DigJobs jobs; + DesignationJobs jobs; - jobs.load(); + jobs.load(map); rng.init(); std::unordered_set buffer; @@ -723,13 +749,11 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (jobs.get(pos) != df::enums::tile_dig_designation::No) { - // todo: check if the designation is removed from the map + if (jobs.count(pos)) { + buffer.emplace(jobs.get(pos)); jobs.remove(pos); // if it does get removed, then we're gonna buffer the jobs info then remove the job - } - - if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) + } else if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) || td.bits.smooth == 1 || to.bits.carve_track_north == 1 || to.bits.carve_track_east == 1 @@ -772,7 +796,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, // todo: check mark mode of smooth designations } else if (td.bits.smooth == 1) { if (smooth_tile(out, map, pos)) { - to = map.occupancyAt(pos); + td = map.designationAt(pos); td.bits.smooth = 0; map.setDesignationAt(pos, td); } From 249c6590069c10eaee251fa2b1b0feb5b056b9f1 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 15 Feb 2023 16:16:22 -0800 Subject: [PATCH 0603/2222] Adds coord validity check for job scanning in dig-now --- plugins/dig-now.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 68a5361a5..4084ca279 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -86,6 +86,9 @@ public: df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; + if(!Maps::isValidTilePos(job->pos)) + continue; + jobs.emplace(job->pos, job); node = node->next; df::tile_designation td = map.designationAt(job->pos); From 1fe0bab9d40832fed6f76262d3e8dc4ab3b02163 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 16 Feb 2023 17:33:39 -0800 Subject: [PATCH 0604/2222] Incorporates code review into dig-now PR --- plugins/dig-now.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 4084ca279..e94cb41da 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -46,8 +46,6 @@ namespace DFHack { #define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 #define COORDARGS(id) id.x, id.y, id.z -// todo: integrate logging for debugging the layered channel problem - using namespace DFHack; struct designation{ @@ -86,10 +84,9 @@ public: df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; - if(!Maps::isValidTilePos(job->pos)) + if(!job || !Maps::isValidTilePos(job->pos)) continue; - jobs.emplace(job->pos, job); node = node->next; df::tile_designation td = map.designationAt(job->pos); df::tile_occupancy to = map.occupancyAt(job->pos); @@ -123,10 +120,10 @@ public: break; } case job_type::CarveTrack: - to.bits.carve_track_north = 0 < (job->item_category.whole & 18); - to.bits.carve_track_south = 0 < (job->item_category.whole & 19); - to.bits.carve_track_west = 0 < (job->item_category.whole & 20); - to.bits.carve_track_east = 0 < (job->item_category.whole & 21); + to.bits.carve_track_north = (job->item_category.whole >> 18) & 1; + to.bits.carve_track_south = (job->item_category.whole >> 19) & 1; + to.bits.carve_track_west = (job->item_category.whole >> 20) & 1; + to.bits.carve_track_east = (job->item_category.whole >> 21) & 1; break; default: break; @@ -134,6 +131,7 @@ public: if (ctd != td.whole || cto != to.whole) { // we found a designation job designations.emplace(job->pos, designation(job->pos, td, to)); + jobs.emplace(job->pos, job); } } } @@ -472,7 +470,6 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, case df::tile_dig_designation::Channel: { DFCoord pos_below(pos.x, pos.y, pos.z-1); - // todo: does can_dig_channel return false? if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { TRACE(channels).print("dig_tile: channeling at (" COORD ") [can_dig_channel: true]\n",COORDARGS(pos_below)); @@ -482,8 +479,6 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, remove_ramp_top(map, pos_above); } df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; - - // todo: digging to floor below? if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); if (td_below == df::tile_dig_designation::Default) { @@ -796,7 +791,6 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, } } } - // todo: check mark mode of smooth designations } else if (td.bits.smooth == 1) { if (smooth_tile(out, map, pos)) { td = map.designationAt(pos); From 7e584df0406d1d5f9074e4ab8a504b221ff66e21 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 17 Feb 2023 12:10:23 -0600 Subject: [PATCH 0605/2222] misc tailor updates related to adamantine cloth --- docs/plugins/tailor.rst | 15 ++++++++++++++- plugins/lua/tailor.lua | 3 ++- plugins/tailor.cpp | 3 +++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/plugins/tailor.rst b/docs/plugins/tailor.rst index 0e8980948..7396102f8 100644 --- a/docs/plugins/tailor.rst +++ b/docs/plugins/tailor.rst @@ -30,7 +30,11 @@ By default, ``tailor`` will prefer using materials in this order:: silk cloth yarn leather but you can use the ``tailor materials`` command to restrict which materials -are used, and in what order. +are used, and in what order. ``tailor`` supports adamantine cloth (using the +keyword ``adamantine``) but does not use it by default, as most players find +adamantine too precious to routinely make into cloth. ``tailor`` does not +support modded "cloth" types which utilize custom reactions to making clothing +out of those cloth types. Examples -------- @@ -46,3 +50,12 @@ Examples Restrict the materials used for automatically manufacturing clothing to silk, cloth, and yarn, preferred in that order. This saves leather for other uses, like making armor. + +Note +---- + +The reason for the limitation on modded cloth-like materials is +because custom reactions do not support the in-game mechanic +which allows a manager order to specify a different size for clothing items. +This mechanic only works for reactions that use the default make-clothing or +make-armor reactions, and is a limitation of the game itself. diff --git a/plugins/lua/tailor.lua b/plugins/lua/tailor.lua index bcfd8bc10..e9a88bfe1 100644 --- a/plugins/lua/tailor.lua +++ b/plugins/lua/tailor.lua @@ -28,7 +28,8 @@ function setMaterials(names) idxs.silk or -1, idxs.cloth or -1, idxs.yarn or -1, - idxs.leather or -1) + idxs.leather or -1, + idxs.adamantine or -1) end function parse_commandline(...) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index f0f2c0b68..a50af1939 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -585,6 +585,8 @@ static void set_material_order() { material_order.push_back(M_YARN); else if (i == (size_t)get_config_val(config, CONFIG_LEATHER_IDX)) material_order.push_back(M_LEATHER); + else if (i == (size_t)get_config_val(config, CONFIG_ADAMANTINE_IDX)) + material_order.push_back(M_ADAMANTINE); } if (!material_order.size()) std::copy(all_materials.begin(), all_materials.end(), std::back_inserter(material_order)); @@ -710,6 +712,7 @@ static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx, set_config_val(config, CONFIG_CLOTH_IDX, clothIdx - 1); set_config_val(config, CONFIG_YARN_IDX, yarnIdx - 1); set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx - 1); + set_config_val(config, CONFIG_ADAMANTINE_IDX, leatherIdx - 1); set_material_order(); } From 4eb3ae566d1234c3eb1fc89e475a3a3fc218b718 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 17 Feb 2023 12:58:48 -0600 Subject: [PATCH 0606/2222] unshadow unit size variable --- plugins/tailor.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index a50af1939..d26559a10 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -258,8 +258,8 @@ public: wearing.insert(inv->item->getType()); } - int size = world->raws.creatures.all[u->race]->adultsize; - sizes[size] = u->race; + int usize = world->raws.creatures.all[u->race]->adultsize; + sizes[usize] = u->race; for (auto ty : std::set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) { @@ -267,9 +267,9 @@ public: { TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n", ENUM_KEY_STR(item_type, ty).c_str(), - size, + usize, Translation::TranslateName(&u->name, false).c_str()); - needed[std::make_pair(ty, size)] += 1; + needed[std::make_pair(ty, usize)] += 1; } } @@ -283,11 +283,11 @@ public: } const df::job_type o = oo->second; - int size = world->raws.creatures.all[w->getMakerRace()]->adultsize; + int isize = world->raws.creatures.all[w->getMakerRace()]->adultsize; std::string description; w->getItemDescription(&description, 0); - if (available[std::make_pair(ty, size)] > 0) + if (available[std::make_pair(ty, usize)] > 0) { if (w->flags.bits.owned) { @@ -303,10 +303,10 @@ public: if (wearing.count(ty) == 0) { - DEBUG(cycle).print("tailor: allocating a %s to %s\n", - ENUM_KEY_STR(item_type, ty).c_str(), + DEBUG(cycle).print("tailor: allocating a %s (size %d) to %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), usize, Translation::TranslateName(&u->name, false).c_str()); - available[std::make_pair(ty, size)] -= 1; + available[std::make_pair(ty, usize)] -= 1; } if (w->getWear() > 1) @@ -314,10 +314,10 @@ public: } else { - DEBUG(cycle).print ("tailor: %s worn by %s needs replacement, but none available\n", - description.c_str(), - Translation::TranslateName(&u->name, false).c_str()); - orders[std::make_tuple(o, w->getSubtype(), size)] += 1; + DEBUG(cycle).print ("tailor: %s (size %d) worn by %s (size %d) needs replacement, but none available\n", + description.c_str(), isize, + Translation::TranslateName(&u->name, false).c_str(), usize); + orders[std::make_tuple(o, w->getSubtype(), usize)] += 1; } } } From 6a0ac8b14244668e783cb7ad88c44ec2f546673f Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 17 Feb 2023 13:08:02 -0600 Subject: [PATCH 0607/2222] update changelog --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 17db6ce03..bf168ac3a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,7 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: check for stockpile items on block boundary. -- `tailor`: block making clothing sized for toads, add support for adamantine cloth (off by default), and reduce logging spam +- `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment; add support for adamantine cloth (off by default); improve logging ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` From f73634d009d5edfb9e690e9a3a078ecbf264abd0 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 17 Feb 2023 14:03:42 -0600 Subject: [PATCH 0608/2222] add missing parameter --- plugins/tailor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index d26559a10..19237429e 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -703,7 +703,8 @@ static void tailor_doCycle(color_ostream &out) { // remember, these are ONE-based indices from Lua static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx, - int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx) { + int32_t clothIdx, int32_t yarnIdx, int32_t leatherIdx, + int32_t adamantineIdx) { DEBUG(config,out).print("entering tailor_setMaterialPreferences\n"); // it doesn't really matter if these are invalid. set_material_order will do @@ -712,7 +713,7 @@ static void tailor_setMaterialPreferences(color_ostream &out, int32_t silkIdx, set_config_val(config, CONFIG_CLOTH_IDX, clothIdx - 1); set_config_val(config, CONFIG_YARN_IDX, yarnIdx - 1); set_config_val(config, CONFIG_LEATHER_IDX, leatherIdx - 1); - set_config_val(config, CONFIG_ADAMANTINE_IDX, leatherIdx - 1); + set_config_val(config, CONFIG_ADAMANTINE_IDX, adamantineIdx - 1); set_material_order(); } From 5244fce4696bb45dec8474a5774ff2ffb543e7f9 Mon Sep 17 00:00:00 2001 From: Robob27 Date: Fri, 17 Feb 2023 18:41:20 -0500 Subject: [PATCH 0609/2222] Fix confirm performance --- docs/changelog.txt | 1 + plugins/confirm.cpp | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bf168ac3a..93aca95f8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: check for stockpile items on block boundary. - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment; add support for adamantine cloth (off by default); improve logging +- `confirm`: fix fps drop when enabled ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index a15b83055..1dfb6809d 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -66,6 +66,7 @@ bool set_conf_paused (string name, bool pause); class confirmation_base { public: + bool dirty = false; enum cstate { INACTIVE, ACTIVE, SELECTED }; virtual string get_id() = 0; virtual string get_focus_string() = 0; @@ -281,6 +282,7 @@ public: } state = s; + dirty = true; if (s == INACTIVE) { active_id = ""; confirmation_base::active = nullptr; @@ -371,17 +373,18 @@ public: return state == ACTIVE; } void render() { - static vector lines; - static const std::string pause_message = - "Pause confirmations until you exit this screen"; - Screen::Pen corner_ul = Screen::Pen((char)201, COLOR_GREY, COLOR_BLACK); - Screen::Pen corner_ur = Screen::Pen((char)187, COLOR_GREY, COLOR_BLACK); - Screen::Pen corner_dl = Screen::Pen((char)200, COLOR_GREY, COLOR_BLACK); - Screen::Pen corner_dr = Screen::Pen((char)188, COLOR_GREY, COLOR_BLACK); - Screen::Pen border_ud = Screen::Pen((char)205, COLOR_GREY, COLOR_BLACK); - Screen::Pen border_lr = Screen::Pen((char)186, COLOR_GREY, COLOR_BLACK); if (state == ACTIVE) { + static vector lines; + static const std::string pause_message = + "Pause confirmations until you exit this screen"; + Screen::Pen corner_ul = Screen::Pen((char)201, COLOR_GREY, COLOR_BLACK); + Screen::Pen corner_ur = Screen::Pen((char)187, COLOR_GREY, COLOR_BLACK); + Screen::Pen corner_dl = Screen::Pen((char)200, COLOR_GREY, COLOR_BLACK); + Screen::Pen corner_dr = Screen::Pen((char)188, COLOR_GREY, COLOR_BLACK); + Screen::Pen border_ud = Screen::Pen((char)205, COLOR_GREY, COLOR_BLACK); + Screen::Pen border_lr = Screen::Pen((char)186, COLOR_GREY, COLOR_BLACK); + split_string(&lines, get_message(), "\n"); size_t max_length = 40; for (string line : lines) @@ -463,8 +466,10 @@ public: } set_state(INACTIVE); } - // clean up any artifacts - df::global::gps->force_full_display_count = 1; + if(dirty) { + dirty = false; + df::global::gps->force_full_display_count = 1; + } } string get_id() override = 0; string get_focus_string() override = 0; From 016f4dfae30f37493e2752d5b31c082047e9815e Mon Sep 17 00:00:00 2001 From: Rob Goodberry Date: Fri, 17 Feb 2023 19:15:01 -0500 Subject: [PATCH 0610/2222] Update docs/changelog.txt Co-authored-by: Myk --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 93aca95f8..f70fdb6f4 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,7 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: check for stockpile items on block boundary. - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment; add support for adamantine cloth (off by default); improve logging -- `confirm`: fix fps drop when enabled +-@ `confirm`: fix fps drop when enabled ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` From 4164e6e80dff0cf64e66a11d0df99988518d6a7c Mon Sep 17 00:00:00 2001 From: lethosor Date: Fri, 17 Feb 2023 23:52:32 -0500 Subject: [PATCH 0611/2222] Update scripts (forbid, unforbid, devel/query) --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index e4ba126f6..e114639c6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e4ba126f6e1193d0a18386c1f1efedb9faf1458f +Subproject commit e114639c6fa1f856887c8a699ee7c89fe6ea5e15 From 8c70c3bf0617fe7d2f51474bef5e26ea2ecac6fc Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 18 Feb 2023 07:14:10 +0000 Subject: [PATCH 0612/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index e114639c6..7e9613e21 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e114639c6fa1f856887c8a699ee7c89fe6ea5e15 +Subproject commit 7e9613e21df4ec75c4f392f1e461693b52a9228a From 2d68b21547b8c08cf0726026cf5ad8d233420f9a Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Tue, 14 Feb 2023 21:20:18 -0800 Subject: [PATCH 0613/2222] Show mouse hover on HotkeyLabels Labels show the hover colour when on_click is set, HotkeyLabels should also do the same when they are clickable. --- library/lua/gui/widgets.lua | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 77c6c2c28..3a6fd8494 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1274,9 +1274,15 @@ function Label:getTextWidth() return self.text_width end +-- Overridden by subclasses that also want to add new mouse handlers, see +-- HotkeyLabel. +function Label:shouldHover() + return self.on_click or self.on_rclick +end + function Label:onRenderBody(dc) local text_pen = self.text_pen - if self:getMousePos() and (self.on_click or self.on_rclick) then + if self:getMousePos() and self:shouldHover() then text_pen = self.text_hpen end render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self)) @@ -1432,6 +1438,11 @@ function HotkeyLabel:setLabel(label) self:initializeLabel() end +function HotkeyLabel:shouldHover() + -- When on_activate is set, text should also hover on mouseover + return HotkeyLabel.super.shouldHover(self) or self.on_activate +end + function HotkeyLabel:initializeLabel() self:setText{{key=self.key, key_sep=self.key_sep, text=self.label, on_activate=self.on_activate}} @@ -1475,6 +1486,12 @@ function CycleHotkeyLabel:init() } end +-- CycleHotkeyLabels are always clickable and therefore should always change +-- color when hovered. +function CycleHotkeyLabel:shouldHover() + return true +end + function CycleHotkeyLabel:cycle(backwards) local old_option_idx = self.option_idx if self.option_idx == #self.options and not backwards then From 0b48471607ba0c74daf147232c5efbca18c9ee5a Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Wed, 15 Feb 2023 21:19:44 -0800 Subject: [PATCH 0614/2222] Invert brightness of the background as well This required some tinkering. --- library/lua/gui/widgets.lua | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 3a6fd8494..15d9064c5 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1224,9 +1224,23 @@ function Label:init(args) -- use existing saved text if no explicit text was specified. this avoids -- overwriting pre-formatted text that subclasses may have already set self:setText(args.text or self.text) + + -- Inverts the brightness of the color + invert = function(color) + return (color + 8) % 16 + end + -- default pen is an inverted foreground/background if not self.text_hpen then - self.text_hpen = ((tonumber(self.text_pen) or tonumber(self.text_pen.fg) or 0) + 8) % 16 + local text_pen = dfhack.pen.parse(self.text_pen) + self.text_hpen = dfhack.pen.make(invert(text_pen.fg), nil, invert(text_pen.bg)) end + + -- text_hpen needs a character in order to paint the background using + -- Painter:fill(), so let's make it paint a space to show the background + -- color + local hpen_parsed = dfhack.pen.parse(self.text_hpen) + hpen_parsed.ch = string.byte(' ') + self.text_hpen = hpen_parsed end local function update_label_scrollbar(label) @@ -1280,6 +1294,14 @@ function Label:shouldHover() return self.on_click or self.on_rclick end +function Label:onRenderFrame(dc, rect) + Label.super.onRenderFrame(self, dc, rect) + -- Fill the background with text_hpen on hover + if self:getMousePos() and self:shouldHover() then + dc:fill(rect, self.text_hpen) + end +end + function Label:onRenderBody(dc) local text_pen = self.text_pen if self:getMousePos() and self:shouldHover() then From 0897ca913a46a61c41f655ed60b66a6f0c827006 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Wed, 15 Feb 2023 22:33:31 -0800 Subject: [PATCH 0615/2222] Support mouse-hover on lists as well --- library/lua/gui/widgets.lua | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 15d9064c5..ab551be1c 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1226,7 +1226,7 @@ function Label:init(args) self:setText(args.text or self.text) -- Inverts the brightness of the color - invert = function(color) + local invert = function(color) return (color + 8) % 16 end -- default pen is an inverted foreground/background @@ -1604,6 +1604,7 @@ List = defclass(List, Widget) List.ATTRS{ text_pen = COLOR_CYAN, + text_hpen = DEFAULT_NIL, -- pen to render list item when mouse is hovered over; defaults to text_pen with inverted brightness cursor_pen = COLOR_LIGHTCYAN, inactive_pen = DEFAULT_NIL, on_select = DEFAULT_NIL, @@ -1633,6 +1634,23 @@ function List:init(info) end self.last_select_click_ms = 0 -- used to track double-clicking on an item + + -- Inverts the brightness of the color + invert = function(color) + return (color + 8) % 16 + end + -- default pen is an inverted foreground/background + if not self.text_hpen then + local text_pen = dfhack.pen.parse(self.text_pen) + self.text_hpen = dfhack.pen.make(invert(text_pen.fg), nil, invert(text_pen.bg)) + end + + -- text_hpen needs a character in order to paint the background using + -- Painter:fill(), so let's make it paint a space to show the background + -- color + local hpen_parsed = dfhack.pen.parse(self.text_hpen) + hpen_parsed.ch = string.byte(' ') + self.text_hpen = hpen_parsed end function List:setChoices(choices, selected) @@ -1784,12 +1802,19 @@ function List:onRenderBody(dc) end end + local hoveridx = self:getIdxUnderMouse() for i = top,iend do local obj = choices[i] local current = (i == self.selected) - local cur_pen = self.cursor_pen - local cur_dpen = self.text_pen - local active_pen = current and cur_pen or cur_dpen + local hovered = (i == hoveridx) + local cur_pen = to_pen(self.cursor_pen) + local cur_dpen = to_pen(self.text_pen) + local active_pen = (current and cur_pen or cur_dpen) + + -- when mouse is over, always highlight it + if hovered then + cur_dpen = self.text_hpen + end if not getval(self.active) then cur_pen = self.inactive_pen or self.cursor_pen From 3e8d0f0f1efbe9a3d6ed648aa51195b09bca4e18 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Thu, 16 Feb 2023 21:38:27 -0800 Subject: [PATCH 0616/2222] Properly reverse BG/FG and apply per letter This puts pen creation deeper into the loop in render_text. Lists are current coloured completely wrong, though, and need fixing (and probably anywhere else where disabled is set). --- library/lua/gui/widgets.lua | 92 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index ab551be1c..e0f01701d 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1080,7 +1080,26 @@ local function is_disabled(token) (token.enabled ~= nil and not getval(token.enabled)) end -function render_text(obj,dc,x0,y0,pen,dpen,disabled) +-- Make the hover pen -- that is a pen that should render elements that has the +-- mouse hovering over it. if hpen is specified, it just checks the fields and +-- returns it (in parsed pen form) +local function make_hpen(pen, hpen) + if not hpen then + pen = dfhack.pen.parse(pen) + + -- Swap the foreground and background + hpen = dfhack.pen.make(pen.bg, nil, pen.fg + (pen.bold and 8 or 0)) + end + + -- text_hpen needs a character in order to paint the background using + -- Painter:fill(), so let's make it paint a space to show the background + -- color + local hpen_parsed = dfhack.pen.parse(hpen) + hpen_parsed.ch = string.byte(' ') + return hpen_parsed +end + +function render_text(obj,dc,x0,y0,pen,dpen,disabled,hpen,hovered) local width = 0 for iline = dc and obj.start_line_num or 1, #obj.text_lines do local x, line = 0, obj.text_lines[iline] @@ -1120,16 +1139,25 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled) if dc then local tpen = getval(token.pen) + local dcpen = tpen or pen + + -- If disabled, figure out which dpen to use if disabled or is_disabled(token) then - dc:pen(getval(token.dpen) or tpen or dpen) + dccpen = getval(token.dpen) or tpen or dpen if keypen.fg ~= COLOR_BLACK then keypen.bold = false end - else - dc:pen(tpen or pen) + + -- if hovered *and* disabled, combine both effects + if hovered then + dcpen = make_hpen(dcpen) + end + elseif hovered then + dcpen = make_hpen(dcpen, getval(token.hpen) or hpen) end - end + dc:pen(dcpen) + end local width = getval(token.width) local padstr if width then @@ -1221,26 +1249,9 @@ function Label:init(args) self:addviews{self.scrollbar} - -- use existing saved text if no explicit text was specified. this avoids - -- overwriting pre-formatted text that subclasses may have already set self:setText(args.text or self.text) - -- Inverts the brightness of the color - local invert = function(color) - return (color + 8) % 16 - end - -- default pen is an inverted foreground/background - if not self.text_hpen then - local text_pen = dfhack.pen.parse(self.text_pen) - self.text_hpen = dfhack.pen.make(invert(text_pen.fg), nil, invert(text_pen.bg)) - end - - -- text_hpen needs a character in order to paint the background using - -- Painter:fill(), so let's make it paint a space to show the background - -- color - local hpen_parsed = dfhack.pen.parse(self.text_hpen) - hpen_parsed.ch = string.byte(' ') - self.text_hpen = hpen_parsed + -- self.text_hpen = make_hpen(self.text_pen, self.text_hpen) end local function update_label_scrollbar(label) @@ -1298,16 +1309,15 @@ function Label:onRenderFrame(dc, rect) Label.super.onRenderFrame(self, dc, rect) -- Fill the background with text_hpen on hover if self:getMousePos() and self:shouldHover() then - dc:fill(rect, self.text_hpen) + local hpen = make_hpen(self.text_pen, self.text_hpen) + dc:fill(rect, hpen) end end function Label:onRenderBody(dc) local text_pen = self.text_pen - if self:getMousePos() and self:shouldHover() then - text_pen = self.text_hpen - end - render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self)) + local hovered = self:getMousePos() and self:shouldHover() + render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self), self.text_hpen, hovered) end function Label:on_scrollbar(scroll_spec) @@ -1635,22 +1645,7 @@ function List:init(info) self.last_select_click_ms = 0 -- used to track double-clicking on an item - -- Inverts the brightness of the color - invert = function(color) - return (color + 8) % 16 - end - -- default pen is an inverted foreground/background - if not self.text_hpen then - local text_pen = dfhack.pen.parse(self.text_pen) - self.text_hpen = dfhack.pen.make(invert(text_pen.fg), nil, invert(text_pen.bg)) - end - - -- text_hpen needs a character in order to paint the background using - -- Painter:fill(), so let's make it paint a space to show the background - -- color - local hpen_parsed = dfhack.pen.parse(self.text_hpen) - hpen_parsed.ch = string.byte(' ') - self.text_hpen = hpen_parsed + -- self.text_hpen = make_hpen(self.text_pen, self.text_hpen) end function List:setChoices(choices, selected) @@ -1807,15 +1802,12 @@ function List:onRenderBody(dc) local obj = choices[i] local current = (i == self.selected) local hovered = (i == hoveridx) + -- cur_pen and cur_dpen can't be integers or background colors get + -- messed up in render_text for subsequent renders local cur_pen = to_pen(self.cursor_pen) local cur_dpen = to_pen(self.text_pen) local active_pen = (current and cur_pen or cur_dpen) - -- when mouse is over, always highlight it - if hovered then - cur_dpen = self.text_hpen - end - if not getval(self.active) then cur_pen = self.inactive_pen or self.cursor_pen end @@ -1828,7 +1820,7 @@ function List:onRenderBody(dc) paint_icon(icon, obj) end - render_text(obj, dc, iw or 0, y, cur_pen, cur_dpen, not current) + render_text(obj, dc, iw or 0, y, cur_pen, cur_dpen, not current, self.text_hpen, hovered) local ip = dc.width From 94ae9973cf00c5f88b228ea0d254cb92214b6e3a Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Thu, 16 Feb 2023 21:43:03 -0800 Subject: [PATCH 0617/2222] Re-add the invert_color function As requested, but it's not used anymore. --- library/lua/gui.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 4a30d947a..9a5038e69 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -987,4 +987,10 @@ function FramedScreen:onRenderFrame(dc, rect) paint_frame(dc,rect,self.frame_style,self.frame_title) end +-- Inverts the brightness of the color, optionally taking a "bold" parameter, +-- which you should include if you're reading the fg color of a pen. +function invert_color(color, bold) + color = bold and (color + 8) or color + return (color + 8) % 16 +end return _ENV From 61227eeca1a59f13fb228864517f6955526529e1 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Thu, 16 Feb 2023 21:54:44 -0800 Subject: [PATCH 0618/2222] Fix use of pens in render_text If you ever pass in a number to `dc:pen` rather than a pen table, it will assume the old pen's other attributes, such as `bg` and `bold`. To workaround this, we just never pass in a number, and always call `to_pen` aka `dfhack.pen.parse` first. --- library/lua/gui/widgets.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index e0f01701d..142e928d3 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1139,11 +1139,11 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled,hpen,hovered) if dc then local tpen = getval(token.pen) - local dcpen = tpen or pen + local dcpen = to_pen(tpen or pen) -- If disabled, figure out which dpen to use if disabled or is_disabled(token) then - dccpen = getval(token.dpen) or tpen or dpen + dcpen = to_pen(getval(token.dpen) or tpen or dpen) if keypen.fg ~= COLOR_BLACK then keypen.bold = false end From 697f15224c8b3f421bcbf5a667a00bff47d88363 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sat, 18 Feb 2023 16:06:03 -0800 Subject: [PATCH 0619/2222] Address PR comments, and remove BG fill BG fill eats up a lot of cycles anyway, and there's not a real tangible benefit in all cases, as it relies on the text label being sized appropriately (width-wise) to the container, or would otherwise require padding. --- docs/dev/Lua API.rst | 16 +++++++++++++++- library/lua/gui/widgets.lua | 17 ++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 02260513f..43c9d0cb0 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4662,7 +4662,9 @@ It has the following attributes: :text_pen: Specifies the pen for active text. :text_dpen: Specifies the pen for disabled text. -:text_hpen: Specifies the pen for text hovered over by the mouse, if a click handler is registered. +:text_hpen: Specifies the pen for text hovered over by the mouse, if a click + handler is registered. By default, this will invert the foreground + and background colors. :disabled: Boolean or a callback; if true, the label is disabled. :enabled: Boolean or a callback; if false, the label is disabled. :auto_height: Sets self.frame.h from the text height. @@ -4769,6 +4771,18 @@ The Label widget implements the following methods: ``+halfpage``, ``-halfpage``, ``home``, or ``end``. It returns the number of lines that were actually scrolled (negative for scrolling up). +* ``label:shouldHover()`` + + This method returns whether or not this widget should show a hover effect, + generally you want to return ``true`` if there is some type of mouse handler + present. For example, for a ``HotKeyLabel``:: + + function HotkeyLabel:shouldHover() + -- When on_activate is set, text should also hover on mouseover + return HotkeyLabel.super.shouldHover(self) or self.on_activate + end + + WrappedLabel class ------------------ diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 142e928d3..6a1726115 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1232,7 +1232,7 @@ Label = defclass(Label, Widget) Label.ATTRS{ text_pen = COLOR_WHITE, text_dpen = COLOR_DARKGREY, -- disabled - text_hpen = DEFAULT_NIL, -- highlight - default is text_pen with reversed brightness + text_hpen = DEFAULT_NIL, -- hover - default is to invert the fg/bg colors disabled = DEFAULT_NIL, enabled = DEFAULT_NIL, auto_height = true, @@ -1250,8 +1250,6 @@ function Label:init(args) self:addviews{self.scrollbar} self:setText(args.text or self.text) - - -- self.text_hpen = make_hpen(self.text_pen, self.text_hpen) end local function update_label_scrollbar(label) @@ -1305,15 +1303,6 @@ function Label:shouldHover() return self.on_click or self.on_rclick end -function Label:onRenderFrame(dc, rect) - Label.super.onRenderFrame(self, dc, rect) - -- Fill the background with text_hpen on hover - if self:getMousePos() and self:shouldHover() then - local hpen = make_hpen(self.text_pen, self.text_hpen) - dc:fill(rect, hpen) - end -end - function Label:onRenderBody(dc) local text_pen = self.text_pen local hovered = self:getMousePos() and self:shouldHover() @@ -1614,7 +1603,7 @@ List = defclass(List, Widget) List.ATTRS{ text_pen = COLOR_CYAN, - text_hpen = DEFAULT_NIL, -- pen to render list item when mouse is hovered over; defaults to text_pen with inverted brightness + text_hpen = DEFAULT_NIL, -- hover color, defaults to inverting the FG/BG pens for each text object cursor_pen = COLOR_LIGHTCYAN, inactive_pen = DEFAULT_NIL, on_select = DEFAULT_NIL, @@ -1644,8 +1633,6 @@ function List:init(info) end self.last_select_click_ms = 0 -- used to track double-clicking on an item - - -- self.text_hpen = make_hpen(self.text_pen, self.text_hpen) end function List:setChoices(choices, selected) From d18700c96462049f381eefc66e9ad447f4377aef Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sat, 18 Feb 2023 16:15:16 -0800 Subject: [PATCH 0620/2222] Update List docs as well. --- docs/dev/Lua API.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 43c9d0cb0..1786c9028 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4917,6 +4917,8 @@ item to call the ``on_submit`` callback for that item. It has the following attributes: :text_pen: Specifies the pen for deselected list entries. +:text_hpen: Specifies the pen for entries that the mouse is hovered over. + Defaults to swapping the background/foreground colors. :cursor_pen: Specifies the pen for the selected entry. :inactive_pen: If specified, used for the cursor when the widget is not active. :icon_pen: Default pen for icons. From ab74cf0af3041071cca106e4154d3543897bad43 Mon Sep 17 00:00:00 2001 From: silverflyone Date: Sun, 19 Feb 2023 13:31:33 +1100 Subject: [PATCH 0621/2222] tombstone combine-drinks and combine-plants --- docs/about/Removed.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 5309372d8..954aa2ce2 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -18,6 +18,20 @@ An automated labor management tool that only addressed hauling labors, leaving t of skilled labors entirely up to the player. Fundamentally incompatible with the work detail system of labor management in v50 of Dwarf Fortress. +.. _combine-drinks: + +combine-drinks +============== +Replaced by the new `combine` script. Run +``combine here --types=drink`` + +.. _combine-plants: + +combine-plants +============== +Replaced by the new `combine` script. Run +``combine here --types=plants`` + .. _command-prompt: command-prompt From 13e4d327f8a8b94933146e8fdf51fe1fe8515a21 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 19 Feb 2023 03:34:02 +0000 Subject: [PATCH 0622/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 54d3f896c..ad1f90747 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 54d3f896c9938f79758fed07c20da78c78094723 +Subproject commit ad1f90747a1e9d942a9e8ec23366d9ed8cf17411 diff --git a/scripts b/scripts index 7e9613e21..62057505e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7e9613e21df4ec75c4f392f1e461693b52a9228a +Subproject commit 62057505ed6daa8aaafaf16d18296331aea7d269 From ce79b8f0c4a9a9115f2342517aef93660b6f1a8f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 19 Feb 2023 03:38:55 +0000 Subject: [PATCH 0623/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 62057505e..f2c2f6aa7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 62057505ed6daa8aaafaf16d18296331aea7d269 +Subproject commit f2c2f6aa7e7fe94871adf0a22d6966ddcac38afc From 81db7f09a27c808577c15583966db0ee66808840 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 21 Feb 2023 07:15:14 +0000 Subject: [PATCH 0624/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index ad1f90747..7917f062c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ad1f90747a1e9d942a9e8ec23366d9ed8cf17411 +Subproject commit 7917f062c403a47d4d190bafc2470b247c8aa642 From d18911ca5a78bb8422f6acff01aae4024a3ad0d2 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 21 Feb 2023 00:01:15 -0800 Subject: [PATCH 0625/2222] Update changelog.txt --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 85750890c..031e4ed62 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: check for stockpile items on block boundary. +- `dig-now`: fixed designations being invalidated after digging prior designations by buffering all designations +- `dig-now`: added scanning the job list for designations that can't be found in the map data ## Misc Improvements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` From 15b00587a0d37f041badf27e2c4d88f2ce2b8ebb Mon Sep 17 00:00:00 2001 From: PopnROFL <126013417+PopnROFL@users.noreply.github.com> Date: Tue, 21 Feb 2023 20:13:57 -0700 Subject: [PATCH 0626/2222] Update CMakeLists.txt Increased MSVC version to the latest, and updated the error messaging so you know what version you have (if it's installed at all) --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38eb6c92b..0fbdaf19b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,8 +61,10 @@ if(UNIX) endif() if(WIN32) - if((NOT MSVC) OR (MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1934)) - message(SEND_ERROR "MSVC 2022 is required") + if(NOT MSVC) + message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1935 is required.") + elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1935)) + message(SEND_ERROR "MSVC 2022 version 1930 to 1935 is required, Version Found: ${MSVC_VERSION}") endif() endif() From 7901fdf6ec70610cf5d624ecac44c3276a226248 Mon Sep 17 00:00:00 2001 From: PopnROFL <126013417+PopnROFL@users.noreply.github.com> Date: Tue, 21 Feb 2023 20:19:37 -0700 Subject: [PATCH 0627/2222] Update CMakeLists.txt N++ defaulted to tabs. Fixing. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fbdaf19b..b58c54fe4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,8 +62,8 @@ endif() if(WIN32) if(NOT MSVC) - message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1935 is required.") - elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1935)) + message(SEND_ERROR "No MSVC found! MSVC 2022 version 1930 to 1935 is required.") + elseif((MSVC_VERSION LESS 1930) OR (MSVC_VERSION GREATER 1935)) message(SEND_ERROR "MSVC 2022 version 1930 to 1935 is required, Version Found: ${MSVC_VERSION}") endif() endif() From 3c24e67a9ae600139b96a48f395e0da6fd9916f2 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Wed, 22 Feb 2023 17:22:04 -0800 Subject: [PATCH 0628/2222] Address additional PR comments on_activate is likely to happen first so we shouldn't need to check the other. --- docs/dev/Lua API.rst | 6 ++++++ library/lua/gui/widgets.lua | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 1786c9028..7ead7e374 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -3757,6 +3757,12 @@ Misc Wraps ``dfhack.screen.getKeyDisplay`` in order to allow using strings for the keycode argument. +* ``invert_color(color, bold)`` + + This inverts the brightness of ``color``. If this color is coming from a pen's + foreground color, include ``pen.bold`` in ``bold`` for this to work properly. + + ViewRect class -------------- diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 6a1726115..3ef27a651 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1461,7 +1461,7 @@ end function HotkeyLabel:shouldHover() -- When on_activate is set, text should also hover on mouseover - return HotkeyLabel.super.shouldHover(self) or self.on_activate + return self.on_activate or HotkeyLabel.super.shouldHover(self) end function HotkeyLabel:initializeLabel() From 1ed0a41dd199ccbc17338548b35c32a759e6ef1d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 23 Feb 2023 07:15:08 +0000 Subject: [PATCH 0629/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 7917f062c..0cc481beb 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 7917f062c403a47d4d190bafc2470b247c8aa642 +Subproject commit 0cc481bebfc02b88c7d1b0e6d70a79cfba72d7f1 diff --git a/scripts b/scripts index f2c2f6aa7..9b122d25e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f2c2f6aa7e7fe94871adf0a22d6966ddcac38afc +Subproject commit 9b122d25e5a5980da966f8016ed57e5fee4b2628 From b976097ccfab2ddee69104e42b188a0b6ebf19af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Feb 2023 21:01:58 -0800 Subject: [PATCH 0630/2222] sync spreadsheet to tags --- docs/plugins/strangemood.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index def862a4b..a863943e9 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -3,7 +3,7 @@ strangemood .. dfhack-tool:: :summary: Trigger a strange mood. - :tags: untested fort armok units + :tags: fort armok units Usage ----- From 29e069817761b13452a48ef84d7d224771db4782 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Feb 2023 21:14:37 -0800 Subject: [PATCH 0631/2222] re-mark channel-safely as untested --- docs/plugins/channel-safely.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index c5dbc37f6..3acbe66cd 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -3,7 +3,7 @@ channel-safely .. dfhack-tool:: :summary: Auto-manage channel designations to keep dwarves safe. - :tags: fort auto + :tags: untested fort auto Multi-level channel projects can be dangerous, and managing the safety of your dwarves throughout the completion of such projects can be difficult and time From 6dbc22350f716c2d7550e6b8be485984b7960023 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Feb 2023 21:32:48 -0800 Subject: [PATCH 0632/2222] log to console instead of announcements --- docs/changelog.txt | 1 + plugins/autobutcher.cpp | 17 ++++++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 51b81de0e..5508549c9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `confirm`: fix fps drop when enabled ## Misc Improvements +- `autobutcher`: logs activity to the console terminal instead of making disruptive in-game announcements - DFHack tool windows that capture mouse clicks (and therefore prevent you from clicking on the "pause" button) now unconditionally pause the game when they open (but you can still unpause with the keyboard if you want to). Examples of this behavior: `gui/quickfort`, `gui/blueprint`, `gui/liquids` - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index bee3a4503..536c74f0f 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -19,8 +19,6 @@ #include "LuaTools.h" #include "PluginManager.h" -#include "modules/Gui.h" -#include "modules/Maps.h" #include "modules/Persistence.h" #include "modules/Units.h" #include "modules/World.h" @@ -805,8 +803,8 @@ static void autobutcher_cycle(color_ostream &out) { w->UpdateConfig(out); watched_races.emplace(unit->race, w); - string announce = "New race added to autobutcher watchlist: " + Units::getRaceNamePluralById(unit->race); - Gui::showAnnouncement(announce, 2, false); + INFO(cycle,out).print("New race added to autobutcher watchlist: %s\n", + Units::getRaceNamePluralById(unit->race).c_str()); } if (w->isWatched) { @@ -828,9 +826,8 @@ static void autobutcher_cycle(color_ostream &out) { if (slaughter_count) { std::stringstream ss; ss << slaughter_count; - string announce = Units::getRaceNamePluralById(w.first) + " marked for slaughter: " + ss.str(); - DEBUG(cycle,out).print("%s\n", announce.c_str()); - Gui::showAnnouncement(announce, 2, false); + INFO(cycle,out).print("%s marked for slaughter: %s\n", + Units::getRaceNamePluralById(w.first).c_str(), ss.str().c_str()); } } } @@ -954,10 +951,8 @@ static void autobutcher_setWatchListRace(color_ostream &out, unsigned id, unsign WatchedRace * w = new WatchedRace(out, id, watched, fk, mk, fa, ma); w->UpdateConfig(out); watched_races.emplace(id, w); - - string announce; - announce = "New race added to autobutcher watchlist: " + Units::getRaceNamePluralById(id); - Gui::showAnnouncement(announce, 2, false); + INFO(status,out).print("New race added to autobutcher watchlist: %s\n", + Units::getRaceNamePluralById(id).c_str()); } // remove entry from watchlist From be5444017777658b9bb1075c595bbc0b9fa74ec9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Feb 2023 22:33:46 -0800 Subject: [PATCH 0633/2222] fix up changelog --- docs/changelog.txt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 51b81de0e..856e6143c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,9 +41,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names -- ``Buildings::StockpileIterator``: check for stockpile items on block boundary. +- ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. - `dig-now`: fixed multi-layer channel designations only channeling every second layer -- `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment; add support for adamantine cloth (off by default); improve logging +- `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled ## Misc Improvements @@ -52,20 +52,18 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. - `dig-now`: added handling of dig designations that have been converted into active jobs +- `tailor`: add support for adamantine cloth (off by default); improve logging ## Documentation ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system -- Units module: added new predicates for: - - ``isGeldable()`` - - ``isMarkedForGelding()`` - - ``isPet()`` +- Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` ## Lua - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. -- Added ``widgets.TabBar`` and ``widgets.Tab`` (migrated from control-panel.lua) +- ``widgets.TabBar``: new library widget (migrated from control-panel.lua) ## Removed From d8758fdfb735aaf9d30589b729f47b82b118790f Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 24 Feb 2023 07:15:12 +0000 Subject: [PATCH 0634/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 9b122d25e..e70393ff2 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 9b122d25e5a5980da966f8016ed57e5fee4b2628 +Subproject commit e70393ff2132e2f5d00ffeb154e8d9242f89a284 From 69b89e9a6b8653358a5d47f2d42fbc44687d3b0e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 12:44:06 -0800 Subject: [PATCH 0635/2222] revert code changes to dig-now (causing lockups) --- plugins/dig-now.cpp | 250 ++++++++------------------------------------ 1 file changed, 45 insertions(+), 205 deletions(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index e94cb41da..6227f44f5 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -6,7 +6,6 @@ #include "PluginManager.h" #include "TileTypes.h" #include "LuaTools.h" -#include "Debug.h" #include "modules/Buildings.h" #include "modules/Gui.h" @@ -15,8 +14,6 @@ #include "modules/Random.h" #include "modules/Units.h" #include "modules/World.h" -#include "modules/EventManager.h" -#include "modules/Job.h" #include #include @@ -29,129 +26,12 @@ #include #include -#include -#include -#include - DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); -// Debugging -namespace DFHack { - DBG_DECLARE(dignow, general, DebugCategory::LINFO); - DBG_DECLARE(dignow, channels, DebugCategory::LINFO); -} - -#define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 -#define COORDARGS(id) id.x, id.y, id.z - using namespace DFHack; -struct designation{ - df::coord pos; - df::tile_designation type; - df::tile_occupancy occupancy; - designation() = default; - designation(const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), type(td), occupancy(to) {} - - bool operator==(const designation &rhs) const { - return pos == rhs.pos; - } - - bool operator!=(const designation &rhs) const { - return !(rhs == *this); - } -}; - -namespace std { - template<> - struct hash { - std::size_t operator()(const designation &c) const { - std::hash hash_coord; - return hash_coord(c.pos); - } - }; -} - -class DesignationJobs { -private: - std::unordered_map designations; - std::unordered_map jobs; -public: - void load(MapExtras::MapCache &map) { - designations.clear(); - df::job_list_link* node = df::global::world->jobs.list.next; - while (node) { - df::job* job = node->item; - if(!job || !Maps::isValidTilePos(job->pos)) - continue; - - node = node->next; - df::tile_designation td = map.designationAt(job->pos); - df::tile_occupancy to = map.occupancyAt(job->pos); - const auto ctd = td.whole; - const auto cto = to.whole; - switch (job->job_type){ - case job_type::Dig: - td.bits.dig = tile_dig_designation::Default; - break; - case job_type::DigChannel: - td.bits.dig = tile_dig_designation::Channel; - break; - case job_type::CarveRamp: - td.bits.dig = tile_dig_designation::Ramp; - break; - case job_type::CarveUpwardStaircase: - td.bits.dig = tile_dig_designation::UpStair; - break; - case job_type::CarveDownwardStaircase: - td.bits.dig = tile_dig_designation::DownStair; - break; - case job_type::CarveUpDownStaircase: - td.bits.dig = tile_dig_designation::UpDownStair; - break; - case job_type::DetailWall: - case job_type::DetailFloor: { - df::tiletype tt = map.tiletypeAt(job->pos); - if (tileSpecial(tt) != df::tiletype_special::SMOOTH) { - td.bits.smooth = 1; - } - break; - } - case job_type::CarveTrack: - to.bits.carve_track_north = (job->item_category.whole >> 18) & 1; - to.bits.carve_track_south = (job->item_category.whole >> 19) & 1; - to.bits.carve_track_west = (job->item_category.whole >> 20) & 1; - to.bits.carve_track_east = (job->item_category.whole >> 21) & 1; - break; - default: - break; - } - if (ctd != td.whole || cto != to.whole) { - // we found a designation job - designations.emplace(job->pos, designation(job->pos, td, to)); - jobs.emplace(job->pos, job); - } - } - } - void remove(const df::coord &pos) { - if(jobs.count(pos)) { - Job::removeJob(jobs[pos]); - jobs.erase(pos); - } - } - designation get(const df::coord &pos) { - if (designations.count(pos)) { - return designations[pos]; - } - return {}; - } - bool count(const df::coord &pos) { - return jobs.count(pos); - } -}; - struct boulder_percent_options { // percent chance ([0..100]) for creating a boulder for the given rock type uint32_t layer; @@ -440,19 +320,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, std::vector &dug_tiles) { df::tiletype tt = map.tiletypeAt(pos); - if (!is_diggable(map, pos, tt)) { - DEBUG(general).print("dig_tile: not diggable\n"); + if (!is_diggable(map, pos, tt)) return false; - } - - /** The algorithm process seems to be: - * for each tile - * check for a designation - * if a designation exists send it to dig_tile - * - * dig_tile (below) then digs the layer below the channel designated tile - * thereby changing it and causing its designation to be lost - * */ df::tiletype target_type = df::tiletype::Void; switch(designation) { @@ -472,22 +341,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, DFCoord pos_below(pos.x, pos.y, pos.z-1); if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { - TRACE(channels).print("dig_tile: channeling at (" COORD ") [can_dig_channel: true]\n",COORDARGS(pos_below)); target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); - if (map.ensureBlockAt(pos_above)) { + if (map.ensureBlockAt(pos_above)) remove_ramp_top(map, pos_above); - } - df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; - if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { + df::tile_dig_designation td_below = + map.designationAt(pos_below).bits.dig; + if (dig_tile(out, map, pos_below, + df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); - if (td_below == df::tile_dig_designation::Default) { + if (td_below == df::tile_dig_designation::Default) dig_tile(out, map, pos_below, td_below, dug_tiles); - } return true; } - } else { - DEBUG(channels).print("dig_tile: failed to channel at (" COORD ") [can_dig_channel: false]\n", COORDARGS(pos_below)); } break; } @@ -541,8 +407,7 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, if (target_type == df::tiletype::Void || target_type == tt) return false; - dug_tiles.emplace_back(map, pos); - TRACE(general).print("dig_tile: digging the designation tile at (" COORD ")\n",COORDARGS(pos)); + dug_tiles.push_back(dug_tile_info(map, pos)); dig_type(map, pos, target_type); // let light filter down to newly exposed tiles @@ -729,12 +594,9 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, item_coords_t &item_coords, const dig_now_options &options) { MapExtras::MapCache map; Random::MersenneRNG rng; - DesignationJobs jobs; - jobs.load(map); rng.init(); - std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { for (int16_t y = options.start.y; y <= options.end.y; ++y) { @@ -747,68 +609,46 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (jobs.count(pos)) { - buffer.emplace(jobs.get(pos)); - jobs.remove(pos); - // if it does get removed, then we're gonna buffer the jobs info then remove the job - } else if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) - || td.bits.smooth == 1 - || to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { - - // we're only buffering designations, so that processing doesn't affect what we're buffering - buffer.emplace(pos, td, to); - } - } - } - } - - // process designations - for(auto &d : buffer) { - auto pos = d.pos; - auto td = d.type; - auto to = d.occupancy; - - if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { - std::vector dug_tiles; - - if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { - for (auto info: dug_tiles) { - td = map.designationAt(info.pos); - td.bits.dig = df::tile_dig_designation::No; - map.setDesignationAt(info.pos, td); - - dug_coords.push_back(info.pos); - refresh_adjacent_smooth_walls(map, info.pos); - if (info.imat < 0) - continue; - if (produces_item(options.boulder_percents, - map, rng, info)) { - auto k = std::make_pair(info.itype, info.imat); - item_coords[k].push_back(info.pos); + if (td.bits.dig != df::tile_dig_designation::No && + !to.bits.dig_marked) { + std::vector dug_tiles; + if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { + for (auto info : dug_tiles) { + td = map.designationAt(info.pos); + td.bits.dig = df::tile_dig_designation::No; + map.setDesignationAt(info.pos, td); + + dug_coords.push_back(info.pos); + refresh_adjacent_smooth_walls(map, info.pos); + if (info.imat < 0) + continue; + if (produces_item(options.boulder_percents, + map, rng, info)) { + auto k = std::make_pair(info.itype, info.imat); + item_coords[k].push_back(info.pos); + } + } + } + } else if (td.bits.smooth == 1) { + if (smooth_tile(out, map, pos)) { + td = map.designationAt(pos); + td.bits.smooth = 0; + map.setDesignationAt(pos, td); + } + } else if (to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + if (carve_tile(map, pos, to)) { + to = map.occupancyAt(pos); + to.bits.carve_track_north = 0; + to.bits.carve_track_east = 0; + to.bits.carve_track_south = 0; + to.bits.carve_track_west = 0; + map.setOccupancyAt(pos, to); } } } - } else if (td.bits.smooth == 1) { - if (smooth_tile(out, map, pos)) { - td = map.designationAt(pos); - td.bits.smooth = 0; - map.setDesignationAt(pos, td); - } - } else if (to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { - if (carve_tile(map, pos, to)) { - to = map.occupancyAt(pos); - to.bits.carve_track_north = 0; - to.bits.carve_track_east = 0; - to.bits.carve_track_south = 0; - to.bits.carve_track_west = 0; - map.setOccupancyAt(pos, to); - } } } From 934422264e5a54e9800a5a73066d007050a9c8c0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 12:44:26 -0800 Subject: [PATCH 0636/2222] remove entries for reverted code from changelog --- docs/changelog.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 57daef38c..9ee7ff102 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,7 +42,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. -- `dig-now`: fixed multi-layer channel designations only channeling every second layer - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled @@ -52,7 +51,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. -- `dig-now`: added handling of dig designations that have been converted into active jobs - `tailor`: add support for adamantine cloth (off by default); improve logging ## Documentation From f922be87690b2e84dc8ea1fb973ae55c621b4441 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 13:25:04 -0800 Subject: [PATCH 0637/2222] fix more autolabor chattiness --- plugins/autolabor/autolabor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 86fa9114b..53a01a6a6 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -841,7 +841,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (p1 || p2) { dwarf_info[dwarf].diplomacy = true; - INFO(cycle, out).print("Dwarf %i \"%s\" has a meeting, will be cleared of all labors\n", + DEBUG(cycle, out).print("Dwarf %i \"%s\" has a meeting, will be cleared of all labors\n", dwarf, dwarfs[dwarf]->name.first_name.c_str()); break; } From f84299bc463c403737fd5a26a5dd28c1effa0f6d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 24 Feb 2023 23:31:20 +0000 Subject: [PATCH 0638/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 0cc481beb..e7143ec5e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0cc481bebfc02b88c7d1b0e6d70a79cfba72d7f1 +Subproject commit e7143ec5e29d88a114fb8e72091c54413944df1d From b8fdc985ec359d02d18e5b084dafe754175b2648 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 15:41:54 -0800 Subject: [PATCH 0639/2222] bump version and changelog to 50.07-alpha2 --- CMakeLists.txt | 2 +- docs/changelog.txt | 18 ++++++++++++++---- library/xml | 2 +- scripts | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b58c54fe4..56c81ba72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "alpha1") +set(DFHACK_RELEASE "alpha2") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 9ee7ff102..d83417933 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,20 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.07-alpha2 + ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. @@ -53,8 +67,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `confirm`: configuration data is now persisted globally. - `tailor`: add support for adamantine cloth (off by default); improve logging -## Documentation - ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` @@ -64,8 +76,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. - ``widgets.TabBar``: new library widget (migrated from control-panel.lua) -## Removed - # 50.07-alpha1 ## Fixes diff --git a/library/xml b/library/xml index e7143ec5e..d4170eacf 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e7143ec5e29d88a114fb8e72091c54413944df1d +Subproject commit d4170eacfc0f82fcb7364e558d0e782dc497f7d5 diff --git a/scripts b/scripts index e70393ff2..c7345f6fe 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e70393ff2132e2f5d00ffeb154e8d9242f89a284 +Subproject commit c7345f6fe096bc6ce1700b70b4f7d4c65b2a3e57 From 4bf0849d51d96c67f16ad8efbedcfb8f7c529587 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 15:50:56 -0800 Subject: [PATCH 0640/2222] fix usage of squad equipment vector --- library/modules/Items.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 11d170725..bfdd525be 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1647,5 +1647,5 @@ bool Items::isSquadEquipment(df::item *item) return false; auto &vec = plotinfo->equipment.items_assigned[item->getType()]; - return binsearch_index(vec, &df::item::id, item->id) >= 0; + return binsearch_index(vec, item->id) >= 0; } From 00eb02c1bccf04f8399d762a827fad17f9451a00 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 24 Feb 2023 15:51:11 -0800 Subject: [PATCH 0641/2222] Implements plugin: channel-safely v1.2.4 - changes report* lookup in `NewReportEvent()` - adds a nullptr check - adds df::coord bound checking in various places - where the `get_*neighbours()` functions are used - `simulate_fall()` - `is_safe_to_dig_down()` and `is_safe_fall()` - adds nullptr checks to the `is_*job()` functions - added todo comments for `is_safe_to_dig_down()` --- plugins/channel-safely/channel-groups.cpp | 38 ++++++++++--------- .../channel-safely/channel-safely-plugin.cpp | 14 +++++-- plugins/channel-safely/include/inlines.h | 30 +++++++++++---- 3 files changed, 54 insertions(+), 28 deletions(-) diff --git a/plugins/channel-safely/channel-groups.cpp b/plugins/channel-safely/channel-groups.cpp index 2650d92d0..c1a5b5953 100644 --- a/plugins/channel-safely/channel-groups.cpp +++ b/plugins/channel-safely/channel-groups.cpp @@ -21,24 +21,27 @@ void ChannelJobs::load_channel_jobs() { } bool ChannelJobs::has_cavein_conditions(const df::coord &map_pos) { - auto p = map_pos; - auto ttype = *Maps::getTileType(p); - if (!DFHack::isOpenTerrain(ttype)) { - // check shared neighbour for cave-in conditions - df::coord neighbours[4]; - get_connected_neighbours(map_pos, neighbours); - int connectedness = 4; - for (auto n: neighbours) { - if (active.count(n) || DFHack::isOpenTerrain(*Maps::getTileType(n))) { - connectedness--; + if likely(Maps::isValidTilePos(map_pos)) { + auto p = map_pos; + auto ttype = *Maps::getTileType(p); + if (!DFHack::isOpenTerrain(ttype)) { + // check shared neighbour for cave-in conditions + df::coord neighbours[4]; + get_connected_neighbours(map_pos, neighbours); + int connectedness = 4; + for (auto n: neighbours) { + if (!Maps::isValidTilePos(n) || active.count(n) || DFHack::isOpenTerrain(*Maps::getTileType(n))) { + connectedness--; + } } - } - if (!connectedness) { - // do what? - p.z--; - ttype = *Maps::getTileType(p); - if (DFHack::isOpenTerrain(ttype) || DFHack::isFloorTerrain(ttype)) { - return true; + if (!connectedness) { + // do what? + p.z--; + if (!Maps::isValidTilePos(p)) return false; + ttype = *Maps::getTileType(p); + if (DFHack::isOpenTerrain(ttype) || DFHack::isFloorTerrain(ttype)) { + return true; + } } } } @@ -88,6 +91,7 @@ void ChannelGroups::add(const df::coord &map_pos) { DEBUG(groups).print(" add(" COORD ")\n", COORDARGS(map_pos)); // and so we begin iterating the neighbours for (auto &neighbour: neighbors) { + if unlikely(!Maps::isValidTilePos(neighbour)) continue; // go to the next neighbour if this one doesn't have a group if (!groups_map.count(neighbour)) { TRACE(groups).print(" -> neighbour is not designated\n"); diff --git a/plugins/channel-safely/channel-safely-plugin.cpp b/plugins/channel-safely/channel-safely-plugin.cpp index adb668468..910e0ee7c 100644 --- a/plugins/channel-safely/channel-safely-plugin.cpp +++ b/plugins/channel-safely/channel-safely-plugin.cpp @@ -112,6 +112,10 @@ enum SettingConfigData { // dig-now.cpp df::coord simulate_fall(const df::coord &pos) { + if unlikely(!Maps::isValidTilePos(pos)) { + ERR(plugin).print("Error: simulate_fall(" COORD ") - invalid coordinate\n", COORDARGS(pos)); + return {}; + } df::coord resting_pos(pos); while (Maps::ensureTileBlock(resting_pos)) { @@ -130,6 +134,7 @@ df::coord simulate_area_fall(const df::coord &pos) { get_neighbours(pos, neighbours); df::coord lowest = simulate_fall(pos); for (auto p : neighbours) { + if unlikely(!Maps::isValidTilePos(p)) continue; auto nlow = simulate_fall(p); if (nlow.z < lowest.z) { lowest = nlow; @@ -299,10 +304,11 @@ namespace CSP { int32_t tick = df::global::world->frame_counter; auto report_id = (int32_t)(intptr_t(r)); if (df::global::world) { - std::vector &reports = df::global::world->status.reports; - size_t idx = -1; - idx = df::report::binsearch_index(reports, report_id); - df::report* report = reports.at(idx); + df::report* report = df::report::find(report_id); + if (!report) { + WARN(plugin).print("Error: NewReportEvent() received an invalid report_id - a report* cannot be found\n"); + return; + } switch (report->type) { case announcement_type::CANCEL_JOB: if (config.insta_dig) { diff --git a/plugins/channel-safely/include/inlines.h b/plugins/channel-safely/include/inlines.h index 172275778..a29f5a04d 100644 --- a/plugins/channel-safely/include/inlines.h +++ b/plugins/channel-safely/include/inlines.h @@ -64,11 +64,13 @@ inline uint8_t count_accessibility(const df::coord &unit_pos, const df::coord &m get_connected_neighbours(map_pos, connections); uint8_t accessibility = Maps::canWalkBetween(unit_pos, map_pos) ? 1 : 0; for (auto n: neighbours) { + if unlikely(!Maps::isValidTilePos(n)) continue; if (Maps::canWalkBetween(unit_pos, n)) { accessibility++; } } for (auto n : connections) { + if unlikely(Maps::isValidTilePos(n)) continue; if (Maps::canWalkBetween(unit_pos, n)) { accessibility++; } @@ -77,22 +79,22 @@ inline uint8_t count_accessibility(const df::coord &unit_pos, const df::coord &m } inline bool isEntombed(const df::coord &unit_pos, const df::coord &map_pos) { - if (Maps::canWalkBetween(unit_pos, map_pos)) { + if likely(Maps::canWalkBetween(unit_pos, map_pos)) { return false; } df::coord neighbours[8]; get_neighbours(map_pos, neighbours); return std::all_of(neighbours+0, neighbours+8, [&unit_pos](df::coord n) { - return !Maps::canWalkBetween(unit_pos, n); + return !Maps::isValidTilePos(n) || !Maps::canWalkBetween(unit_pos, n); }); } inline bool is_dig_job(const df::job* job) { - return job->job_type == df::job_type::Dig || job->job_type == df::job_type::DigChannel; + return job && (job->job_type == df::job_type::Dig || job->job_type == df::job_type::DigChannel); } inline bool is_channel_job(const df::job* job) { - return job->job_type == df::job_type::DigChannel; + return job && (job->job_type == df::job_type::DigChannel); } inline bool is_group_job(const ChannelGroups &groups, const df::job* job) { @@ -111,34 +113,48 @@ inline bool is_safe_fall(const df::coord &map_pos) { df::coord below(map_pos); for (uint8_t zi = 0; zi < config.fall_threshold; ++zi) { below.z--; + // falling out of bounds is probably considerably unsafe for a dwarf + if unlikely(!Maps::isValidTilePos(below)) { + return false; + } + // if we require vision, and we can't see below.. we'll need to assume it's safe to get anything done if (config.require_vision && Maps::getTileDesignation(below)->bits.hidden) { - return true; //we require vision, and we can't see below.. so we gotta assume it's safe + return true; } + // finally, if we're not looking at open space (air to fall through) it's safe to fall to df::tiletype type = *Maps::getTileType(below); if (!DFHack::isOpenTerrain(type)) { return true; } } + // we exceeded the fall threshold, so it's not a safe fall return false; } inline bool is_safe_to_dig_down(const df::coord &map_pos) { df::coord pos(map_pos); + // todo: probably should rely on is_safe_fall, it looks like it could be simplified a great deal for (uint8_t zi = 0; zi <= config.fall_threshold; ++zi) { - // assume safe if we can't see and need vision + // if we're digging out of bounds, the game can handle that (hopefully) + if unlikely(!Maps::isValidTilePos(pos)) { + return true; + } + // if we require vision, and we can't see the tiles in question.. we'll need to assume it's safe to dig to get anything done if (config.require_vision && Maps::getTileDesignation(pos)->bits.hidden) { return true; } + df::tiletype type = *Maps::getTileType(pos); if (zi == 0 && DFHack::isOpenTerrain(type)) { + // todo: remove? this is probably not useful.. and seems like the only considerable difference to is_safe_fall (aside from where each stops looking) // the starting tile is open space, that's obviously not safe return false; } else if (!DFHack::isOpenTerrain(type)) { // a tile after the first one is not open space return true; } - pos.z--; + pos.z--; // todo: this can probably move to the beginning of the loop } return false; } From 4813a15b35ebd0084e9ac892fa0b874990ebf256 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 24 Feb 2023 15:51:23 -0800 Subject: [PATCH 0642/2222] Updates changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 9ee7ff102..fab27741a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled +- `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's ## Misc Improvements - `autobutcher`: logs activity to the console terminal instead of making disruptive in-game announcements From 30ea58374cf7191dfb0f4ba55b90169a84dcfad5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 00:55:14 -0800 Subject: [PATCH 0643/2222] better detection of fire and magma safety --- docs/changelog.txt | 1 + library/modules/Materials.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4eb816c29..62fb484a0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,6 +54,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. +- ``dfhack.job.isSuitableMaterial``: now properly detects lack of fire and magma safety for vulnerable materials with high melting points -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 0854a85ce..a6141f1d8 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -513,8 +513,14 @@ void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &ma TEST(sewn_imageless, is_cloth); TEST(glass_making, MAT_FLAG(CRYSTAL_GLASSABLE)); - TEST(fire_safe, material->heat.melting_point > 11000); - TEST(magma_safe, material->heat.melting_point > 12000); + TEST(fire_safe, material->heat.melting_point > 11000 + && material->heat.boiling_point > 11000 + && material->heat.ignite_point > 11000 + && material->heat.heatdam_point > 11000); + TEST(magma_safe, material->heat.melting_point > 12000 + && material->heat.boiling_point > 12000 + && material->heat.ignite_point > 12000 + && material->heat.heatdam_point > 12000); TEST(deep_material, FLAG(inorganic, inorganic_flags::SPECIAL)); TEST(non_economic, !inorganic || !(plotinfo && vector_get(plotinfo->economic_stone, index))); From 472cab846fa60d1b798f193daee6c782a8f57900 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 16:58:17 -0800 Subject: [PATCH 0644/2222] move changelog entry to next version --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 62fb484a0..90439bf79 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- ``dfhack.job.isSuitableMaterial``: now properly detects lack of fire and magma safety for vulnerable materials with high melting points ## Misc Improvements @@ -54,7 +55,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -- ``dfhack.job.isSuitableMaterial``: now properly detects lack of fire and magma safety for vulnerable materials with high melting points -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment From a684f294c5027a960831f2940e449c6ac9eeb4b6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:05:08 -0800 Subject: [PATCH 0645/2222] add templated version of join_strings --- library/Core.cpp | 2 -- library/include/MiscUtils.h | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 5375daa3c..478693dbf 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -61,8 +61,6 @@ using namespace std; #include "LuaTools.h" #include "DFHackVersion.h" -#include "MiscUtils.h" - using namespace DFHack; #include "df/plotinfost.h" diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index c9a5f66d6..d14bdb6e9 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -404,6 +404,22 @@ DFHACK_EXPORT bool split_string(std::vector *out, bool squash_empty = false); DFHACK_EXPORT std::string join_strings(const std::string &separator, const std::vector &items); +template +inline std::string join_strings(const std::string &separator, T &items) { + std::stringstream ss; + + bool first = true; + for (auto &item : items) { + if (first) + first = false; + else + ss << separator; + ss << item; + } + + return ss.str(); +} + DFHACK_EXPORT std::string toUpper(const std::string &str); DFHACK_EXPORT std::string toLower(const std::string &str); DFHACK_EXPORT std::string to_search_normalized(const std::string &str); From 75b1cd748a085a698caba910d69c99a39359d05b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 18:58:50 -0800 Subject: [PATCH 0646/2222] convert otherwise unused THIN_FRAME to INTERIOR_FRAME without a signature --- library/lua/gui.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 9a5038e69..7791f3685 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -916,7 +916,8 @@ end WINDOW_FRAME = make_frame('Window', true) PANEL_FRAME = make_frame('Panel', false) MEDIUM_FRAME = make_frame('Medium', false) -THIN_FRAME = make_frame('Thin', false) +INTERIOR_FRAME = make_frame('Thin', false) +INTERIOR_FRAME.signature_pen = false -- for compatibility with pre-steam code GREY_LINE_FRAME = WINDOW_FRAME From 0febce5e8f6f28119aec489025defe91fe2cd8f0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:09:11 -0800 Subject: [PATCH 0647/2222] add docs --- docs/dev/Lua API.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 7ead7e374..bee602456 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4325,9 +4325,11 @@ There are the following predefined frame style tables: A frame suitable for overlay widget panels. -* ``THIN_FRAME`` +* ``INTERIOR_FRAME`` - A frame suitable for light accent elements. + A frame suitable for light interior accent elements. This frame does *not* have + a visible ``DFHack`` signature on it, so it must not be used as the most external + frame for a DFHack-owned UI. gui.widgets =========== From dafafefe112053010b6e56e675e7ccd4d4d665ed Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:11:20 -0800 Subject: [PATCH 0648/2222] update changelog --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4eb816c29..49a218183 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,8 +44,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +-@ ``gui.INTERIOR_FRAME``: a panel frame style for use in highlighting off interior areas of a UI ## Removed +-@ ``gui.THIN_FRAME``: replaced by ``gui.INTERIOR_FRAME`` # 50.07-alpha2 From 8b378735faeedfcac884c4b2db6b67f50aacb1b2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Feb 2023 14:23:07 -0800 Subject: [PATCH 0649/2222] don't fire HotkeyLabel if the label is disabled --- library/lua/gui/widgets.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 3ef27a651..9fbae2cee 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1472,7 +1472,8 @@ end function HotkeyLabel:onInput(keys) if HotkeyLabel.super.onInput(self, keys) then return true - elseif keys._MOUSE_L_DOWN and self:getMousePos() and self.on_activate then + elseif keys._MOUSE_L_DOWN and self:getMousePos() and self.on_activate + and not is_disabled(self) then self.on_activate() return true end From 2e53c5bc6df67664317b183a9f97c0f5870b7545 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:14:27 -0800 Subject: [PATCH 0650/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4eb816c29..d8b86e46a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +-@ ``widgets.HotkeyLabel``: don't trigger on click if the widget is disabled ## Misc Improvements From 1cacc526e3520cd65d2e2cd5a247754d9ef636ec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:16:30 -0800 Subject: [PATCH 0651/2222] allow token.tile to be a function --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 4 ++-- library/lua/gui/widgets.lua | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4eb816c29..ca83717ce 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- ``widgets.Label``: token ``tile`` properties can now be functions that return a value ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 7ead7e374..0c91ece19 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4704,8 +4704,8 @@ containing newlines, or a table with the following possible fields: * ``token.tile = pen`` - Specifies a pen or texture index to paint as one tile before the main part of - the token. + Specifies a pen or texture index (or a function that returns a pen or texture + index) to paint as one tile before the main part of the token. * ``token.width = ...`` diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 3ef27a651..46b047254 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1124,8 +1124,8 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled,hpen,hovered) if token.tile then x = x + 1 if dc then - local tile_pen = tonumber(token.tile) and - to_pen{tile=token.tile} or token.tile + local tile = getval(token.tile) + local tile_pen = tonumber(tile) and to_pen{tile=tile} or tile dc:char(nil, tile_pen) if token.width then dc:advance(token.width-1) From d7d3dcb0beae631370dc4d824fdd8ce44f561dc2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 23:27:16 -0800 Subject: [PATCH 0652/2222] keep focus strings if they are already labeled i.e. don't add a "dfhack/" prefix if the focus string already has the string "dfhack" in it --- library/modules/Gui.cpp | 7 ++++++- library/modules/Screen.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index e3a7ba983..6ea0a5ff0 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -491,7 +491,12 @@ bool Gui::matchFocusString(std::string focus_string, df::viewscreen *top) { static void push_dfhack_focus_string(dfhack_viewscreen *vs, std::vector &focusStrings) { auto name = vs->getFocusString(); - focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); + if (name.empty()) + name = "dfhack"; + else if (string::npos == name.find("dfhack/")) + name = "dfhack/" + name; + + focusStrings.push_back(name); } std::vector Gui::getFocusStrings(df::viewscreen* top) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index a78a36a3f..ca0876904 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -877,7 +877,7 @@ void dfhack_lua_viewscreen::update_focus(lua_State *L, int idx) if (focus.empty()) focus = "lua"; - else + else if (string::npos == focus.find("lua/")) focus = "lua/"+focus; } From ab4af88c92bbd3fa5666f1c8560abfd0f05fd8c9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:21:38 -0800 Subject: [PATCH 0653/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4eb816c29..a3cd76aa9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Documentation ## API +- Gui focus strings will no longer get the "dfhack/" prefix if the string "dfhack/" already exists in the focus string ## Lua From 88516a899afbfea08e080b282831e4574fb67df6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Feb 2023 13:04:41 -0800 Subject: [PATCH 0654/2222] allow map interface tiles to be cleared --- library/modules/Screen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index a78a36a3f..1fbc6bd65 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -130,7 +130,7 @@ static bool doSetTile_map(const Pen &pen, int x, int y) { long texpos = pen.tile; if (!texpos && pen.ch) texpos = init->font.large_font_texpos[(uint8_t)pen.ch]; - if (texpos) + else vp->screentexpos_interface[index] = texpos; return true; } From f1d5551e51e4ec6389ade405c0872570a6a2f769 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Feb 2023 18:03:13 -0800 Subject: [PATCH 0655/2222] fix on-map character rendering --- library/modules/Screen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 1fbc6bd65..b49db0d7e 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -130,8 +130,7 @@ static bool doSetTile_map(const Pen &pen, int x, int y) { long texpos = pen.tile; if (!texpos && pen.ch) texpos = init->font.large_font_texpos[(uint8_t)pen.ch]; - else - vp->screentexpos_interface[index] = texpos; + vp->screentexpos_interface[index] = texpos; return true; } From 49331384dfc72c93f0211979654ebacc5d66f2e1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Feb 2023 17:24:20 -0800 Subject: [PATCH 0656/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4eb816c29..d0e23d136 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- ``dfhack.screen.paintTile()``: you can now explicitly clear the interface cursor from a map tile by passing ``0`` as the tile value ## Removed From cfa649b4ac7646076d5d0fe6b2c7c78d2742c2c7 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 24 Feb 2023 20:47:58 -0600 Subject: [PATCH 0657/2222] clean up code for C++20 readiness two change: * remove use of `register` in `md5.cpp` * remove use of `using namespace std` in `Core.cpp` (which causes an ambiguous name resolution error between `byte` and `std::byte`). while there are other ways to resolve this, `using namespace std` is a code smell anyway, so eliminating it is the best option --- depends/md5/md5.cpp | 2 +- library/Core.cpp | 289 ++++++++++++++++++++++---------------------- 2 files changed, 145 insertions(+), 146 deletions(-) diff --git a/depends/md5/md5.cpp b/depends/md5/md5.cpp index 044df259e..8aa9ba38c 100644 --- a/depends/md5/md5.cpp +++ b/depends/md5/md5.cpp @@ -158,7 +158,7 @@ void MD5Final(unsigned char digest[16], MD5Context *ctx) */ void MD5Transform(uint32_t buf[4], uint32_t in[16]) { - register uint32_t a, b, c, d; + uint32_t a, b, c, d; a = buf[0]; b = buf[1]; diff --git a/library/Core.cpp b/library/Core.cpp index 5375daa3c..fd7626f17 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -35,7 +35,6 @@ distribution. #include #include #include -using namespace std; #include "Error.h" #include "MemAccess.h" @@ -99,7 +98,7 @@ using df::global::world; // FIXME: A lot of code in one file, all doing different things... there's something fishy about it. static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL); -size_t loadScriptFiles(Core* core, color_ostream& out, const vector& prefix, const std::string& folder); +size_t loadScriptFiles(Core* core, color_ostream& out, const std::vector& prefix, const std::string& folder); namespace DFHack { @@ -160,9 +159,9 @@ struct CommandDepthCounter }; thread_local int CommandDepthCounter::depth = 0; -void Core::cheap_tokenise(string const& input, vector &output) +void Core::cheap_tokenise(std::string const& input, std::vector& output) { - string *cur = NULL; + std::string *cur = NULL; size_t i = 0; // Check the first non-space character @@ -234,13 +233,13 @@ void fHKthread(void * iodata) PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr; if(plug_mgr == 0 || core == 0) { - cerr << "Hotkey thread has croaked." << endl; + std::cerr << "Hotkey thread has croaked." << std::endl; return; } bool keep_going = true; while(keep_going) { - std::string stuff = core->getHotkeyCmd(keep_going); // waits on mutex! + std::string stuff = core->getHotkeyCmd(keep_going); // waits on std::mutex! if(!stuff.empty()) { color_ostream_proxy out(core->getConsole()); @@ -256,10 +255,10 @@ void fHKthread(void * iodata) struct sortable { bool recolor; - string name; - string description; + std::string name; + std::string description; //FIXME: Nuke when MSVC stops failing at being C++11 compliant - sortable(bool recolor_,const string& name_,const string & description_): recolor(recolor_), name(name_), description(description_){}; + sortable(bool recolor_,const std::string& name_,const std::string & description_): recolor(recolor_), name(name_), description(description_){}; bool operator <(const sortable & rhs) const { if( name < rhs.name ) @@ -268,9 +267,9 @@ struct sortable }; }; -static string dfhack_version_desc() +static std::string dfhack_version_desc() { - stringstream s; + std::stringstream s; s << Version::dfhack_version() << " "; if (Version::is_release()) s << "(release)"; @@ -284,11 +283,11 @@ static string dfhack_version_desc() namespace { struct ScriptArgs { - const string *pcmd; - vector *pargs; + const std::string *pcmd; + std::vector *pargs; }; struct ScriptEnableState { - const string *pcmd; + const std::string *pcmd; bool pstate; }; } @@ -307,7 +306,7 @@ static bool init_run_script(color_ostream &out, lua_State *state, void *info) return true; } -static command_result runLuaScript(color_ostream &out, std::string name, vector &args) +static command_result runLuaScript(color_ostream &out, std::string name, std::vector &args) { ScriptArgs data; data.pcmd = &name; @@ -346,25 +345,25 @@ command_result Core::runCommand(color_ostream &out, const std::string &command) { if (!command.empty()) { - vector parts; + std::vector parts; Core::cheap_tokenise(command,parts); if(parts.size() == 0) return CR_NOT_IMPLEMENTED; - string first = parts[0]; + std::string first = parts[0]; parts.erase(parts.begin()); if (first[0] == '#') return CR_OK; - cerr << "Invoking: " << command << endl; + std::cerr << "Invoking: " << command << std::endl; return runCommand(out, first, parts); } else return CR_NOT_IMPLEMENTED; } -bool is_builtin(color_ostream &con, const string &command) { +bool is_builtin(color_ostream &con, const std::string &command) { CoreSuspender suspend; auto L = Lua::Core::State; Lua::StackUnwinder top(L); @@ -385,7 +384,7 @@ bool is_builtin(color_ostream &con, const string &command) { return lua_toboolean(L, -1); } -void get_commands(color_ostream &con, vector &commands) { +void get_commands(color_ostream &con, std::vector &commands) { CoreSuspender suspend; auto L = Lua::Core::State; Lua::StackUnwinder top(L); @@ -431,10 +430,10 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std:: return false; } -bool Core::addScriptPath(string path, bool search_before) +bool Core::addScriptPath(std::string path, bool search_before) { - lock_guard lock(script_path_mutex); - vector &vec = script_paths[search_before ? 0 : 1]; + std::lock_guard lock(script_path_mutex); + std::vector &vec = script_paths[search_before ? 0 : 1]; if (std::find(vec.begin(), vec.end(), path) != vec.end()) return false; if (!Filesystem::isdir(path)) @@ -443,13 +442,13 @@ bool Core::addScriptPath(string path, bool search_before) return true; } -bool Core::removeScriptPath(string path) +bool Core::removeScriptPath(std::string path) { - lock_guard lock(script_path_mutex); + std::lock_guard lock(script_path_mutex); bool found = false; for (int i = 0; i < 2; i++) { - vector &vec = script_paths[i]; + std::vector &vec = script_paths[i]; while (1) { auto it = std::find(vec.begin(), vec.end(), path); @@ -464,14 +463,14 @@ bool Core::removeScriptPath(string path) void Core::getScriptPaths(std::vector *dest) { - lock_guard lock(script_path_mutex); + std::lock_guard lock(script_path_mutex); dest->clear(); - string df_path = this->p->getPath() + "/"; + std::string df_path = this->p->getPath() + "/"; for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it) dest->push_back(*it); dest->push_back(df_path + CONFIG_PATH + "scripts"); if (df::global::world && isWorldLoaded()) { - string save = World::ReadWorldFolder(); + std::string save = World::ReadWorldFolder(); if (save.size()) dest->push_back(df_path + "/save/" + save + "/scripts"); } @@ -481,13 +480,13 @@ void Core::getScriptPaths(std::vector *dest) } -string Core::findScript(string name) +std::string Core::findScript(std::string name) { - vector paths; + std::vector paths; getScriptPaths(&paths); for (auto it = paths.begin(); it != paths.end(); ++it) { - string path = *it + "/" + name; + std::string path = *it + "/" + name; if (Filesystem::isfile(path)) return path; } @@ -497,7 +496,7 @@ string Core::findScript(string name) bool loadScriptPaths(color_ostream &out, bool silent = false) { using namespace std; - string filename(CONFIG_PATH + "script-paths.txt"); + std::string filename(CONFIG_PATH + "script-paths.txt"); ifstream file(filename); if (!file) { @@ -505,7 +504,7 @@ bool loadScriptPaths(color_ostream &out, bool silent = false) out.printerr("Could not load %s\n", filename.c_str()); return false; } - string raw; + std::string raw; int line = 0; while (getline(file, raw)) { @@ -516,7 +515,7 @@ bool loadScriptPaths(color_ostream &out, bool silent = false) if (!(ss >> ch) || ch == '#') continue; ss >> ws; // discard whitespace - string path; + std::string path; getline(ss, path); if (ch == '+' || ch == '-') { @@ -565,7 +564,7 @@ static std::string sc_event_name (state_change_event id) { return "SC_UNKNOWN"; } -void help_helper(color_ostream &con, const string &entry_name) { +void help_helper(color_ostream &con, const std::string &entry_name) { CoreSuspender suspend; auto L = Lua::Core::State; Lua::StackUnwinder top(L); @@ -583,7 +582,7 @@ void help_helper(color_ostream &con, const string &entry_name) { } } -void tags_helper(color_ostream &con, const string &tag) { +void tags_helper(color_ostream &con, const std::string &tag) { CoreSuspender suspend; auto L = Lua::Core::State; Lua::StackUnwinder top(L); @@ -601,11 +600,11 @@ void tags_helper(color_ostream &con, const string &tag) { } } -void ls_helper(color_ostream &con, const vector ¶ms) { - vector filter; +void ls_helper(color_ostream &con, const std::vector ¶ms) { + std::vector filter; bool skip_tags = false; bool show_dev_commands = false; - string exclude_strs = ""; + std::string exclude_strs = ""; bool in_exclude = false; for (auto str : params) { @@ -641,7 +640,7 @@ void ls_helper(color_ostream &con, const vector ¶ms) { } } -command_result Core::runCommand(color_ostream &con, const std::string &first_, vector &parts) +command_result Core::runCommand(color_ostream &con, const std::string &first_, std::vector &parts) { std::string first = first_; CommandDepthCounter counter; @@ -717,7 +716,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { if (p->size() && (*p)[0] == '-') { - if (p->find('a') != string::npos) + if (p->find('a') != std::string::npos) all = true; } } @@ -876,7 +875,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } con << parts[0]; bool builtin = is_builtin(con, parts[0]); - string lua_path = findScript(parts[0] + ".lua"); + std::string lua_path = findScript(parts[0] + ".lua"); Plugin *plug = plug_mgr->getPluginByCommand(parts[0]); if (builtin) { @@ -933,31 +932,31 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { std::vector list = ListKeyBindings(parts[1]); if (list.empty()) - con << "No bindings." << endl; + con << "No bindings." << std::endl; for (size_t i = 0; i < list.size(); i++) - con << " " << list[i] << endl; + con << " " << list[i] << std::endl; } else { - con << "Usage:" << endl - << " keybinding list " << endl - << " keybinding clear [@context]..." << endl - << " keybinding set [@context] \"cmdline\" \"cmdline\"..." << endl - << " keybinding add [@context] \"cmdline\" \"cmdline\"..." << endl - << "Later adds, and earlier items within one command have priority." << endl - << "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, `, or Enter)." << endl - << "Context may be used to limit the scope of the binding, by" << endl - << "requiring the current context to have a certain prefix." << endl - << "Current UI context is: " << endl - << join_strings("\n", Gui::getCurFocus(true)) << endl; + con << "Usage:" << std::endl + << " keybinding list " << std::endl + << " keybinding clear [@context]..." << std::endl + << " keybinding set [@context] \"cmdline\" \"cmdline\"..." << std::endl + << " keybinding add [@context] \"cmdline\" \"cmdline\"..." << std::endl + << "Later adds, and earlier items within one command have priority." << std::endl + << "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, `, or Enter)." << std::endl + << "Context may be used to limit the scope of the binding, by" << std::endl + << "requiring the current context to have a certain prefix." << std::endl + << "Current UI context is: " << std::endl + << join_strings("\n", Gui::getCurFocus(true)) << std::endl; } } else if (first == "alias") { if (parts.size() >= 3 && (parts[0] == "add" || parts[0] == "replace")) { - const string &name = parts[1]; - vector cmd(parts.begin() + 2, parts.end()); + const std::string &name = parts[1]; + std::vector cmd(parts.begin() + 2, parts.end()); if (!AddAlias(name, cmd, parts[0] == "replace")) { con.printerr("Could not add alias %s - already exists\n", name.c_str()); @@ -977,15 +976,15 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v auto aliases = ListAliases(); for (auto p : aliases) { - con << p.first << ": " << join_strings(" ", p.second) << endl; + con << p.first << ": " << join_strings(" ", p.second) << std::endl; } } else { - con << "Usage: " << endl - << " alias add|replace " << endl - << " alias delete|clear " << endl - << " alias list" << endl; + con << "Usage: " << std::endl + << " alias add|replace " << std::endl + << " alias delete|clear " << std::endl + << " alias list" << std::endl; } } else if (first == "fpause") @@ -1038,8 +1037,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } else { - con << "Usage:" << endl - << " script " << endl; + con << "Usage:" << std::endl + << " script " << std::endl; return CR_WRONG_USAGE; } } @@ -1065,13 +1064,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { if (parts.empty() || parts[0] == "help" || parts[0] == "?") { - con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl; - con << "Valid event names (SC_ prefix is optional):" << endl; + con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << std::endl; + con << "Valid event names (SC_ prefix is optional):" << std::endl; for (int i = SC_WORLD_LOADED; i <= SC_UNPAUSED; i++) { std::string name = sc_event_name((state_change_event)i); if (name != "SC_UNKNOWN") - con << " " << name << endl; + con << " " << name << std::endl; } return CR_OK; } @@ -1081,7 +1080,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v parts.push_back(""); if (parts[1].size() && sc_event_id(parts[1]) == SC_UNKNOWN) { - con << "Unrecognized event name: " << parts[1] << endl; + con << "Unrecognized event name: " << parts[1] << std::endl; return CR_WRONG_USAGE; } for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it) @@ -1100,13 +1099,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save")) { - con << "Usage: sc-script add EVENT path-to-script [-save]" << endl; + con << "Usage: sc-script add EVENT path-to-script [-save]" << std::endl; return CR_WRONG_USAGE; } state_change_event evt = sc_event_id(parts[1]); if (evt == SC_UNKNOWN) { - con << "Unrecognized event: " << parts[1] << endl; + con << "Unrecognized event: " << parts[1] << std::endl; return CR_FAILURE; } bool save_specific = (parts.size() >= 4 && parts[3] == "-save"); @@ -1115,7 +1114,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { if (script == *it) { - con << "Script already registered" << endl; + con << "Script already registered" << std::endl; return CR_FAILURE; } } @@ -1126,13 +1125,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v { if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save")) { - con << "Usage: sc-script remove EVENT path-to-script [-save]" << endl; + con << "Usage: sc-script remove EVENT path-to-script [-save]" << std::endl; return CR_WRONG_USAGE; } state_change_event evt = sc_event_id(parts[1]); if (evt == SC_UNKNOWN) { - con << "Unrecognized event: " << parts[1] << endl; + con << "Unrecognized event: " << parts[1] << std::endl; return CR_FAILURE; } bool save_specific = (parts.size() >= 4 && parts[3] == "-save"); @@ -1145,13 +1144,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } else { - con << "Unrecognized script" << endl; + con << "Unrecognized script" << std::endl; return CR_FAILURE; } } else { - con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl; + con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << std::endl; return CR_WRONG_USAGE; } } @@ -1173,13 +1172,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v if (!svc) continue; - file << "// Plugin: " << plug->getName() << endl; + file << "// Plugin: " << plug->getName() << std::endl; svc->dumpMethods(file); } } else { - con << "Usage: devel/dump-rpc \"filename\"" << endl; + con << "Usage: devel/dump-rpc \"filename\"" << std::endl; return CR_WRONG_USAGE; } } @@ -1196,8 +1195,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } else if (res == CR_NOT_IMPLEMENTED) { - string completed; - string filename = findScript(first + ".lua"); + std::string completed; + std::string filename = findScript(first + ".lua"); bool lua = filename != ""; if ( !lua ) { filename = findScript(first + ".rb"); @@ -1234,22 +1233,22 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v return CR_OK; } -bool Core::loadScriptFile(color_ostream &out, string fname, bool silent) +bool Core::loadScriptFile(color_ostream &out, std::string fname, bool silent) { if(!silent) { INFO(script,out) << "Loading script: " << fname << std::endl; - cerr << "Loading script: " << fname << std::endl; + std::cerr << "Loading script: " << fname << std::endl; } - ifstream script(fname.c_str()); + std::ifstream script(fname.c_str()); if ( !script.good() ) { if(!silent) out.printerr("Error loading script: %s\n", fname.c_str()); return false; } - string command; + std::string command; while(script.good()) { - string temp; + std::string temp; getline(script,temp); bool doMore = false; if ( temp.length() > 0 ) { @@ -1333,18 +1332,18 @@ void fIOthread(void * iodata) while (true) { - string command = ""; + std::string command = ""; int ret; while ((ret = con.lineedit("[DFHack]# ",command, main_history)) == Console::RETRY); if(ret == Console::SHUTDOWN) { - cerr << "Console is shutting down properly." << endl; + std::cerr << "Console is shutting down properly." << std::endl; return; } else if(ret == Console::FAILURE) { - cerr << "Console caught an unspecified error." << endl; + std::cerr << "Console caught an unspecified error." << std::endl; continue; } else if(ret) @@ -1405,7 +1404,7 @@ Core::Core() : void Core::fatal (std::string output) { errorstate = true; - stringstream out; + std::stringstream out; out << output ; if (output[output.size() - 1] != '\n') out << '\n'; @@ -1421,7 +1420,7 @@ void Core::fatal (std::string output) out << "Check file stderr.log for details\n"; MessageBox(0,out.str().c_str(),"DFHack error!", MB_OK | MB_ICONERROR); #else - cout << "DFHack fatal error: " << out.str() << std::endl; + std::cout << "DFHack fatal error: " << out.str() << std::endl; #endif bool is_headless = bool(getenv("DFHACK_HEADLESS")); @@ -1459,16 +1458,16 @@ bool Core::Init() // this is handled as appropriate in Console-posix.cpp fprintf(stdout, "dfhack: redirecting stdout to stdout.log (again)\n"); if (!freopen("stdout.log", "w", stdout)) - cerr << "Could not redirect stdout to stdout.log" << endl; + std::cerr << "Could not redirect stdout to stdout.log" << std::endl; #endif fprintf(stderr, "dfhack: redirecting stderr to stderr.log\n"); if (!freopen("stderr.log", "w", stderr)) - cerr << "Could not redirect stderr to stderr.log" << endl; + std::cerr << "Could not redirect stderr to stderr.log" << std::endl; Filesystem::init(); - cerr << "DFHack build: " << Version::git_description() << "\n" - << "Starting with working directory: " << Filesystem::getcwd() << endl; + std::cerr << "DFHack build: " << Version::git_description() << "\n" + << "Starting with working directory: " << Filesystem::getcwd() << std::endl; // find out what we are... #ifdef LINUX_BUILD @@ -1477,7 +1476,7 @@ bool Core::Init() const char * path = "hack\\symbols.xml"; #endif auto local_vif = dts::make_unique(); - cerr << "Identifying DF version.\n"; + std::cerr << "Identifying DF version.\n"; try { local_vif->loadFile(path); @@ -1518,8 +1517,8 @@ bool Core::Init() "recompile.\n" "More details can be found in stderr.log in this folder.\n" ); - cout << msg << endl; - cerr << msg << endl; + std::cout << msg << std::endl; + std::cerr << msg << std::endl; fatal("Not a known DF version - XML version mismatch (see console or stderr.log)"); } else @@ -1529,13 +1528,13 @@ bool Core::Init() errorstate = true; return false; } - cerr << "Version: " << vinfo->getVersion() << endl; + std::cerr << "Version: " << vinfo->getVersion() << std::endl; p = std::move(local_p); // Init global object pointers df::global::InitGlobals(); - cerr << "Initializing Console.\n"; + std::cerr << "Initializing Console.\n"; // init the console. bool is_text_mode = (init && init->display.flag.is_set(init_display_flags::TEXT)); bool is_headless = bool(getenv("DFHACK_HEADLESS")); @@ -1551,29 +1550,29 @@ bool Core::Init() } else { - cerr << "endwin(): bind failed" << endl; + std::cerr << "endwin(): bind failed" << std::endl; } } else { - cerr << "Headless mode requires PRINT_MODE:TEXT" << endl; + std::cerr << "Headless mode requires PRINT_MODE:TEXT" << std::endl; } #else - cerr << "Headless mode not supported on Windows" << endl; + std::cerr << "Headless mode not supported on Windows" << std::endl; #endif } if (is_text_mode && !is_headless) { - cerr << "Console is not available. Use dfhack-run to send commands.\n"; + std::cerr << "Console is not available. Use dfhack-run to send commands.\n"; if (!is_text_mode) { - cout << "Console disabled.\n"; + std::cout << "Console disabled.\n"; } } else if(con.init(false)) - cerr << "Console is running.\n"; + std::cerr << "Console is running.\n"; else - cerr << "Console has failed to initialize!\n"; + std::cerr << "Console has failed to initialize!\n"; /* // dump offsets to a file std::ofstream dump("offsets.log"); @@ -1642,19 +1641,19 @@ bool Core::Init() return false; } - cerr << "Binding to SDL.\n"; + std::cerr << "Binding to SDL.\n"; if (!DFSDL::init(con)) { fatal("cannot bind SDL libraries"); return false; } - cerr << "Initializing textures.\n"; + std::cerr << "Initializing textures.\n"; Textures::init(con); - // create mutex for syncing with interactive tasks - cerr << "Initializing plugins.\n"; + // create std::mutex for syncing with interactive tasks + std::cerr << "Initializing plugins.\n"; // create plugin manager plug_mgr = new PluginManager(this); plug_mgr->init(); - cerr << "Starting the TCP listener.\n"; + std::cerr << "Starting the TCP listener.\n"; auto listen = ServerMain::listen(RemoteClient::GetDefaultPort()); IODATA *temp = new IODATA; temp->core = this; @@ -1662,7 +1661,7 @@ bool Core::Init() if (!is_text_mode || is_headless) { - cerr << "Starting IO thread.\n"; + std::cerr << "Starting IO thread.\n"; // create IO thread d->iothread = std::thread{fIOthread, (void*)temp}; } @@ -1672,19 +1671,19 @@ bool Core::Init() d->iothread = std::thread{fInitthread, (void*)temp}; } - cerr << "Starting DF input capture thread.\n"; + std::cerr << "Starting DF input capture thread.\n"; // set up hotkey capture d->hotkeythread = std::thread(fHKthread, (void *) temp); started = true; modstate = 0; if (!listen.get()) - cerr << "TCP listen failed.\n"; + std::cerr << "TCP listen failed.\n"; if (df::global::game) { - vector args; - const string & raw = df::global::game->command_line.original; + std::vector args; + const std::string & raw = df::global::game->command_line.original; size_t offset = 0; while (offset < raw.size()) { @@ -1698,7 +1697,7 @@ bool Core::Init() else { size_t next = raw.find(" ", offset); - if (next == string::npos) + if (next == std::string::npos) { args.push_back(raw.substr(offset)); offset = raw.size(); @@ -1712,12 +1711,12 @@ bool Core::Init() } for (auto it = args.begin(); it != args.end(); ) { - const string & first = *it; + const std::string & first = *it; if (first.length() > 0 && first[0] == '+') { - vector cmd; + std::vector cmd; for (it++; it != args.end(); it++) { - const string & arg = *it; + const std::string & arg = *it; if (arg.length() > 0 && arg[0] == '+') { break; @@ -1727,12 +1726,12 @@ bool Core::Init() if (runCommand(con, first.substr(1), cmd) != CR_OK) { - cerr << "Error running command: " << first.substr(1); + std::cerr << "Error running command: " << first.substr(1); for (auto it2 = cmd.begin(); it2 != cmd.end(); it2++) { - cerr << " \"" << *it2 << "\""; + std::cerr << " \"" << *it2 << "\""; } - cerr << "\n"; + std::cerr << "\n"; } } else @@ -1742,7 +1741,7 @@ bool Core::Init() } } - cerr << "DFHack is running.\n"; + std::cerr << "DFHack is running.\n"; onStateChange(con, SC_CORE_INITIALIZED); @@ -1761,7 +1760,7 @@ bool Core::setHotkeyCmd( std::string cmd ) /// removes the hotkey command and gives it to the caller thread std::string Core::getHotkeyCmd( bool &keep_going ) { - string returner; + std::string returner; std::unique_lock lock(HotkeyMutex); HotkeyCond.wait(lock, [this]() -> bool {return this->hotkey_set;}); if (hotkey_set == SHUTDOWN) { @@ -1871,20 +1870,20 @@ void Core::doUpdate(color_ostream &out) // if the world changes if (new_wdata != last_world_data_ptr) { - // we check for map change too + // we check for std::map change too bool had_map = isMapLoaded(); last_world_data_ptr = new_wdata; last_local_map_ptr = new_mapdata; - // and if the world is going away, we report the map change first + // and if the world is going away, we report the std::map change first if(had_map) onStateChange(out, SC_MAP_UNLOADED); - // and if the world is appearing, we report map change after that + // and if the world is appearing, we report std::map change after that onStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); if(isMapLoaded()) onStateChange(out, SC_MAP_LOADED); } - // otherwise just check for map change... + // otherwise just check for std::map change... else if (new_mapdata != last_local_map_ptr) { bool had_map = isMapLoaded(); @@ -1986,22 +1985,22 @@ void getFilesWithPrefixAndSuffix(const std::string& folder, const std::string& p return; } -size_t loadScriptFiles(Core* core, color_ostream& out, const vector& prefix, const std::string& folder) { - static const string suffix = ".init"; - vector scriptFiles; +size_t loadScriptFiles(Core* core, color_ostream& out, const std::vector& prefix, const std::string& folder) { + static const std::string suffix = ".init"; + std::vector scriptFiles; for ( size_t a = 0; a < prefix.size(); a++ ) { getFilesWithPrefixAndSuffix(folder, prefix[a], ".init", scriptFiles); } std::sort(scriptFiles.begin(), scriptFiles.end(), - [&](const string &a, const string &b) { - string a_base = a.substr(0, a.size() - suffix.size()); - string b_base = b.substr(0, b.size() - suffix.size()); + [&](const std::string &a, const std::string &b) { + std::string a_base = a.substr(0, a.size() - suffix.size()); + std::string b_base = b.substr(0, b.size() - suffix.size()); return a_base < b_base; }); size_t result = 0; for ( size_t a = 0; a < scriptFiles.size(); a++ ) { result++; - string path = ""; + std::string path = ""; if (folder != ".") path = folder + "/"; core->loadScriptFile(out, path + scriptFiles[a], false); @@ -2012,10 +2011,10 @@ size_t loadScriptFiles(Core* core, color_ostream& out, const vector namespace DFHack { namespace X { typedef state_change_event Key; - typedef vector Val; - typedef pair Entry; - typedef vector EntryVector; - typedef map InitVariationTable; + typedef std::vector Val; + typedef std::pair Entry; + typedef std::vector EntryVector; + typedef std::map InitVariationTable; EntryVector computeInitVariationTable(void* none, ...) { va_list list; @@ -2030,7 +2029,7 @@ namespace DFHack { const char *v = va_arg(list, const char *); if (!v || !v[0]) break; - val.push_back(string(v)); + val.push_back(std::string(v)); } result.push_back(Entry(key,val)); } @@ -2165,7 +2164,7 @@ void Core::onStateChange(color_ostream &out, state_change_event event) if (event == SC_WORLD_LOADED && Version::is_prerelease()) { runCommand(out, "gui/prerelease-warning"); - std::cerr << "loaded map in prerelease build" << std::endl; + std::cerr << "loaded std::map in prerelease build" << std::endl; } if (event == SC_WORLD_LOADED) @@ -2425,7 +2424,7 @@ bool Core::SelectHotkey(int sym, int modifiers) if (!binding.focus.empty()) { if (!Gui::matchFocusString(binding.focus)) { std::vector focusStrings = Gui::getCurFocus(true); - DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", + DEBUG(keybinding).print("skipping keybinding due to focus std::string mismatch: '%s' !~ '%s'\n", join_strings(", ", focusStrings).c_str(), binding.focus.c_str()); continue; } @@ -2647,8 +2646,8 @@ bool Core::RunAlias(color_ostream &out, const std::string &name, return false; } - const string &first = aliases[name][0]; - vector parts(aliases[name].begin() + 1, aliases[name].end()); + const std::string &first = aliases[name][0]; + std::vector parts(aliases[name].begin() + 1, aliases[name].end()); parts.insert(parts.end(), parameters.begin(), parameters.end()); result = runCommand(out, first, parts); return true; From 87e06cf96038fe55e3b49a2251d4c7a39e8ec893 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sat, 25 Feb 2023 02:42:28 -0600 Subject: [PATCH 0658/2222] deoops --- library/Core.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index fd7626f17..abd5a05f6 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1648,7 +1648,7 @@ bool Core::Init() } std::cerr << "Initializing textures.\n"; Textures::init(con); - // create std::mutex for syncing with interactive tasks + // create mutex for syncing with interactive tasks std::cerr << "Initializing plugins.\n"; // create plugin manager plug_mgr = new PluginManager(this); @@ -1883,7 +1883,7 @@ void Core::doUpdate(color_ostream &out) if(isMapLoaded()) onStateChange(out, SC_MAP_LOADED); } - // otherwise just check for std::map change... + // otherwise just check for map change... else if (new_mapdata != last_local_map_ptr) { bool had_map = isMapLoaded(); @@ -2029,7 +2029,7 @@ namespace DFHack { const char *v = va_arg(list, const char *); if (!v || !v[0]) break; - val.push_back(std::string(v)); + val.emplace_back(v); } result.push_back(Entry(key,val)); } @@ -2164,7 +2164,7 @@ void Core::onStateChange(color_ostream &out, state_change_event event) if (event == SC_WORLD_LOADED && Version::is_prerelease()) { runCommand(out, "gui/prerelease-warning"); - std::cerr << "loaded std::map in prerelease build" << std::endl; + std::cerr << "loaded map in prerelease build" << std::endl; } if (event == SC_WORLD_LOADED) @@ -2424,7 +2424,7 @@ bool Core::SelectHotkey(int sym, int modifiers) if (!binding.focus.empty()) { if (!Gui::matchFocusString(binding.focus)) { std::vector focusStrings = Gui::getCurFocus(true); - DEBUG(keybinding).print("skipping keybinding due to focus std::string mismatch: '%s' !~ '%s'\n", + DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", join_strings(", ", focusStrings).c_str(), binding.focus.c_str()); continue; } From 0a65c423cef63f0dc3dc17dea3e37e3135aeb47f Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sat, 25 Feb 2023 04:07:24 -0600 Subject: [PATCH 0659/2222] a squirrel distracted me --- library/Core.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index abd5a05f6..af048d9c2 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1870,15 +1870,15 @@ void Core::doUpdate(color_ostream &out) // if the world changes if (new_wdata != last_world_data_ptr) { - // we check for std::map change too + // we check for map change too bool had_map = isMapLoaded(); last_world_data_ptr = new_wdata; last_local_map_ptr = new_mapdata; - // and if the world is going away, we report the std::map change first + // and if the world is going away, we report the map change first if(had_map) onStateChange(out, SC_MAP_UNLOADED); - // and if the world is appearing, we report std::map change after that + // and if the world is appearing, we report map change after that onStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); if(isMapLoaded()) onStateChange(out, SC_MAP_LOADED); From c7f6ee57d7d4f2e567ff3cb905ab4cdd759dd472 Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 25 Feb 2023 11:00:51 -0800 Subject: [PATCH 0660/2222] Update library/Core.cpp --- library/Core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index af048d9c2..3cccd15f1 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -239,7 +239,7 @@ void fHKthread(void * iodata) bool keep_going = true; while(keep_going) { - std::string stuff = core->getHotkeyCmd(keep_going); // waits on std::mutex! + std::string stuff = core->getHotkeyCmd(keep_going); // waits on mutex! if(!stuff.empty()) { color_ostream_proxy out(core->getConsole()); From 2b59d6ee3d0fdf7557b5aa5860c948e75c4fcb6c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 18 Feb 2023 01:09:54 -0800 Subject: [PATCH 0661/2222] make dfhack.job.attachJobItem available to Lua --- docs/dev/Lua API.rst | 9 +++++++++ library/LuaApi.cpp | 1 + 2 files changed, 10 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ef9b35f05..c2cc7f5cd 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1219,6 +1219,15 @@ Job module if there are any jobs with ``first_id <= id < job_next_id``, a lua list containing them. +* ``dfhack.job.attachJobItem(job, item, role, filter_idx, insert_idx)`` + + Attach a real item to this job. If the item is intended to satisfy a job_item + filter, the index of that filter should be passed in ``filter_idx``; otherwise, + pass ``-1``. Similarly, if you don't care where the item is inserted, pass + ``-1`` for ``insert_idx``. The ``role`` param is a ``df.job_item_ref.T_role``. + If the item needs to be brought to the job site, then the value should be + ``df.job_item_ref.T_role.Hauled``. + * ``dfhack.job.isSuitableItem(job_item, item_type, item_subtype)`` Does basic sanity checks to verify if the suggested item type matches diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 2dc2ddf8b..426c87566 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1659,6 +1659,7 @@ static bool jobItemEqual(const df::job_item *job1, const df::job_item *job2) } static const LuaWrapper::FunctionReg dfhack_job_module[] = { + WRAPM(Job,attachJobItem), WRAPM(Job,cloneJobStruct), WRAPM(Job,printItemDetails), WRAPM(Job,printJobDetails), From a536396bd8b7ede12cfa1cc65fe59c762b16cbac Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 09:41:22 -0800 Subject: [PATCH 0662/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1192fb03d..dd109a218 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Gui focus strings will no longer get the "dfhack/" prefix if the string "dfhack/" already exists in the focus string ## Lua +- ``dfhack.job.attachJobItem()``: allows you to attach specific items to a job - ``dfhack.screen.paintTile()``: you can now explicitly clear the interface cursor from a map tile by passing ``0`` as the tile value - ``widgets.Label``: token ``tile`` properties can now be functions that return a value -@ ``gui.INTERIOR_FRAME``: a panel frame style for use in highlighting off interior areas of a UI From 656a26504ae5251d1c543a5a03a9dfebff8dc683 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 09:47:15 -0800 Subject: [PATCH 0663/2222] make FilteredList searching case insensitive by default --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 2 +- library/lua/gui/widgets.lua | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1192fb03d..4e06d0e67 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``dfhack.screen.paintTile()``: you can now explicitly clear the interface cursor from a map tile by passing ``0`` as the tile value - ``widgets.Label``: token ``tile`` properties can now be functions that return a value +-@ ``widgets.FilteredList``: search key matching is now case insensitive by default -@ ``gui.INTERIOR_FRAME``: a panel frame style for use in highlighting off interior areas of a UI ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ef9b35f05..3b91d0ea9 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5007,7 +5007,7 @@ construction that allows filtering the list by subwords of its items. In addition to passing through all attributes supported by List, it supports: -:case_sensitive: If true, matching is case sensitive. Defaults to true. +:case_sensitive: If ``true``, matching is case sensitive. Defaults to ``false``. :edit_pen: If specified, used instead of ``cursor_pen`` for the edit field. :edit_below: If true, the edit field is placed below the list instead of above. :edit_key: If specified, the edit field is disabled until this key is pressed. diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index d5d0ea7bf..ab018a50a 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1926,7 +1926,7 @@ end FilteredList = defclass(FilteredList, Widget) FilteredList.ATTRS { - case_sensitive = true, + case_sensitive = false, edit_below = false, edit_key = DEFAULT_NIL, edit_ignore_keys = DEFAULT_NIL, From b443f81ecdf0710fbf231c9bb9e4bcbf8975395a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 18:47:10 -0800 Subject: [PATCH 0664/2222] print out more status info for buildingplan --- docs/plugins/buildingplan.rst | 49 +++++++++---------------------- plugins/buildingplan.cpp | 54 +++++++++++++++++++++++++++-------- plugins/lua/buildingplan.lua | 11 +++++-- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 1eb18b1d5..c51f79721 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -11,11 +11,14 @@ available, and they will be created in a suspended state. Buildingplan will periodically scan for appropriate items, and the jobs will be unsuspended when the items are available. -This is very useful when combined with manager work orders or `workflow` -- you -can set a constraint to always have one or two doors/beds/tables/chairs/etc. -available, and place as many as you like. Materials are used to build the -planned buildings as they are produced, with minimal space dedicated to -stockpiles. +This is very powerful when used with tools like `quickfort`, which allow you to +set a building plan according to a blueprint, and the buildings will simply be +built when you can build them. + +You can use manager work orders or `workflow` to ensure you always have one or +two doors/beds/tables/chairs/etc. available, and place as many as you like. +Materials are used to build the planned buildings as they are produced, with +minimal space dedicated to stockpiles. Usage ----- @@ -23,37 +26,27 @@ Usage :: enable buildingplan - buildingplan set + buildingplan [status] buildingplan set true|false -Running ``buildingplan set`` without parameters displays the current settings. - .. _buildingplan-settings: Global settings --------------- -The buildingplan plugin has global settings that can be set from the UI -(:kbd:`G` from any building placement screen, for example: -:kbd:`b`:kbd:`a`:kbd:`G`). These settings can also be set via the -``buildingplan set`` command. The available settings are: +The buildingplan plugin has several global settings that affect what materials +can be chosen when attaching items to planned buildings: -``all_enabled`` (default: false) - Enable planning mode for all building types. ``blocks``, ``boulders``, ``logs``, ``bars`` (defaults: true, true, true, false) Allow blocks, boulders, logs, or bars to be matched for generic "building material" items. -``quickfort_mode`` (default: false) - Enable compatibility mode for the legacy Python Quickfort (this setting is - not required for DFHack `quickfort`) -The settings for ``blocks``, ``boulders``, ``logs``, and ``bars`` are saved with -your fort, so you only have to set them once and they will be persisted in your -save. +These settings are saved with your fort, so you only have to set them once and +they will be persisted in your save. If you normally embark with some blocks on hand for early workshops, you might want to add this line to your ``dfhack-config/init/onMapLoad.init`` file to -always configure buildingplan to just use blocks for buildings and +always configure `buildingplan` to just use blocks for buildings and constructions:: on-new-fortress buildingplan set boulders false; buildingplan set logs false @@ -76,17 +69,3 @@ keep the filter values that were set when the building was placed. For example, you can be sure that all your constructed walls are the same color by setting a filter to accept only certain types of stone. - -Quickfort mode --------------- - -If you use the external Python Quickfort to apply building blueprints instead of -the native DFHack `quickfort` script, you must enable Quickfort mode. This -temporarily enables buildingplan for all building types and adds an extra blank -screen after every building placement. This "dummy" screen is needed for Python -Quickfort to interact successfully with Dwarf Fortress. - -Note that Quickfort mode is only for compatibility with the legacy Python -Quickfort. The DFHack `quickfort` script does not need this Quickfort mode to be -enabled. The `quickfort` script will successfully integrate with buildingplan as -long as the buildingplan plugin itself is enabled. diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 039c83b0f..81f026cbc 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -15,14 +15,14 @@ #include "df/job_item.h" #include "df/world.h" -#include +#include #include #include #include using std::map; using std::pair; -using std::queue; +using std::deque; using std::string; using std::unordered_map; using std::vector; @@ -34,11 +34,8 @@ DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(world); -// logging levels can be dynamically controlled with the `debugfilter` command. namespace DFHack { - // for configuration-related logging DBG_DECLARE(buildingplan, status, DebugCategory::LINFO); - // for logging during the periodic scan DBG_DECLARE(buildingplan, cycle, DebugCategory::LINFO); } @@ -108,7 +105,7 @@ static PersistentDataItem config; // building id -> PlannedBuilding unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) -map>>> tasks; +map>>> tasks; // note that this just removes the PlannedBuilding. the tasks will get dropped // as we discover them in the tasks queues and they fail to be found in planned_buildings. @@ -359,7 +356,7 @@ static void finalizeBuilding(color_ostream &out, df::building * bld) { Job::checkBuildingsNow(); } -static df::building * popInvalidTasks(color_ostream &out, queue> & task_queue) { +static df::building * popInvalidTasks(color_ostream &out, deque> & task_queue) { while (!task_queue.empty()) { auto & task = task_queue.front(); auto id = task.first; @@ -369,13 +366,13 @@ static df::building * popInvalidTasks(color_ostream &out, queue>> & buckets) { + map>> & buckets) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); auto item_vector = df::global::world->items.other[other_id]; DEBUG(cycle,out).print("matching %zu item(s) in vector %s against %zu filter bucket(s)\n", @@ -423,7 +420,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, // items so if buildingplan is turned off, the building will // be completed with the correct number of items. --job->job_items[filter_idx]->quantity; - task_queue.pop(); + task_queue.pop_front(); if (isJobReady(out, job)) { finalizeBuilding(out, bld); planned_buildings.at(id).remove(out); @@ -586,7 +583,7 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { // as invalid for (auto vector_id : vector_ids) { for (int item_num = 0; item_num < job_item->quantity; ++item_num) { - tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx)); + tasks[vector_id][bucket].push_back(std::make_pair(id, job_item_idx)); DEBUG(status,out).print("added task: %s/%s/%d,%d; " "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), @@ -609,13 +606,46 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { static void printStatus(color_ostream &out) { DEBUG(status,out).print("entering buildingplan_printStatus\n"); out.print("buildingplan is %s\n\n", is_enabled ? "enabled" : "disabled"); - out.print(" finding materials for %zd buildings\n", planned_buildings.size()); out.print("Current settings:\n"); out.print(" use blocks: %s\n", get_config_bool(config, CONFIG_BLOCKS) ? "yes" : "no"); out.print(" use boulders: %s\n", get_config_bool(config, CONFIG_BOULDERS) ? "yes" : "no"); out.print(" use logs: %s\n", get_config_bool(config, CONFIG_LOGS) ? "yes" : "no"); out.print(" use bars: %s\n", get_config_bool(config, CONFIG_BARS) ? "yes" : "no"); out.print("\n"); + + map counts; + int32_t total = 0; + for (auto &buckets : tasks) { + for (auto &bucket_queue : buckets.second) { + deque> &tqueue = bucket_queue.second; + for (auto it = tqueue.begin(); it != tqueue.end();) { + auto & task = *it; + auto id = task.first; + df::building *bld = NULL; + if (!planned_buildings.count(id) || + !(bld = planned_buildings.at(id).getBuildingIfValidOrRemoveIfNot(out))) { + DEBUG(status,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n", + id, task.second); + it = tqueue.erase(it); + continue; + } + auto *jitem = bld->jobs[0]->job_items[task.second]; + int32_t quantity = jitem->quantity; + if (quantity) { + string desc = toLower(ENUM_KEY_STR(item_type, jitem->item_type)); + counts[desc] += quantity; + total += quantity; + } + ++it; + } + } + } + + out.print("Waiting for %d item(s) to be produced or %zd building(s):\n", + total, planned_buildings.size()); + for (auto &count : counts) + out.print(" %3d %s\n", count.second, count.first.c_str()); + out.print("\n"); } static bool setSetting(color_ostream &out, string name, bool value) { diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 9b953dd7c..4420f8534 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -4,8 +4,6 @@ local _ENV = mkmodule('plugins.buildingplan') Native functions: - * void setSetting(string name, boolean value) - * bool isPlanModeEnabled(df::building_type type, int16_t subtype, int32_t custom) * bool isPlannableBuilding(df::building_type type, int16_t subtype, int32_t custom) * bool isPlannedBuilding(df::building *bld) * void addPlannedBuilding(df::building *bld) @@ -36,6 +34,15 @@ function parse_commandline(...) return false end + local command = table.remove(positionals, 1) + if not command or command == 'status' then + printStatus() + elseif command == 'set' then + setSetting(positionals[1], positionals[2] == 'true') + else + return false + end + return true end From 0cb1c09549574daefe92035e16dba656def022bb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Feb 2023 19:26:39 -0800 Subject: [PATCH 0665/2222] implement skeletons for buildingplan overlays --- plugins/lua/buildingplan.lua | 78 ++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 4420f8534..86f8699eb 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -53,6 +53,84 @@ function get_num_filters(btype, subtype, custom) return 0 end +local gui = require('gui') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) +PlannerOverlay.ATTRS{ + default_pos={x=46,y=18}, + default_enabled=true, + viewscreens='dwarfmode/Building/Placement', + frame={w=30, h=4}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function PlannerOverlay:init() + self:addviews{ + widgets.ToggleHotkeyLabel{ + frame={t=0, l=0}, + label='build when materials are available', + key='CUSTOM_CTRL_B', + }, + widgets.HotkeyLabel{ + frame={t=1, l=0}, + label='configure materials', + key='CUSTOM_CTRL_E', + on_activate=do_export, + }, + } +end + +InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) +InspectorOverlay.ATTRS{ + default_pos={x=-41,y=14}, + default_enabled=true, + viewscreens='dwarfmode/ViewSheets/BUILDING', + frame={w=30, h=5}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function InspectorOverlay:init() + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text='Waiting for items:', + }, + widgets.Label{ + frame={t=1, l=0}, + text='items', + }, + widgets.HotkeyLabel{ + frame={t=2, l=0}, + label='make top priority', + key='CUSTOM_CTRL_T', + }, + } +end + +function InspectorOverlay:onInput(keys) + if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + return false + end + return InspectorOverlay.super.onInput(self, keys) +end + +function InspectorOverlay:render(dc) + if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + return + end + InspectorOverlay.super.render(self, dc) +end + +OVERLAY_WIDGETS = { + planner=PlannerOverlay, + inspector=InspectorOverlay, +} + + local dialogs = require('gui.dialogs') local guidm = require('gui.dwarfmode') From 1c3a5fa1700c1fb9e45f24942689fd1f529ee74e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 9 Feb 2023 00:13:53 -0800 Subject: [PATCH 0666/2222] initial building placement code --- plugins/lua/buildingplan.lua | 371 ++++++++++++++++++++++++----------- 1 file changed, 260 insertions(+), 111 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 86f8699eb..4167ebb41 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -13,6 +13,11 @@ local _ENV = mkmodule('plugins.buildingplan') --]] local argparse = require('argparse') +local gui = require('gui') +local guidm = require('gui.dwarfmode') +local overlay = require('plugins.overlay') +local utils = require('utils') +local widgets = require('gui.widgets') require('dfhack.buildings') local function process_args(opts, args) @@ -47,42 +52,278 @@ function parse_commandline(...) end function get_num_filters(btype, subtype, custom) - local filters = dfhack.buildings.getFiltersByType( - {}, btype, subtype, custom) - if filters then return #filters end - return 0 + local filters = dfhack.buildings.getFiltersByType({}, btype, subtype, custom) + return filters and #filters or 0 end -local gui = require('gui') -local overlay = require('plugins.overlay') -local widgets = require('gui.widgets') +-------------------------------- +-- Planner Overlay +-- + +local uibs = df.global.buildreq + +local function cur_building_has_no_area() + if uibs.building_type == df.building_type.Construction then return false end + local filters = dfhack.buildings.getFiltersByType({}, + uibs.building_type, uibs.building_subtype, uibs.custom_type) + -- this works because all variable-size buildings have either no item + -- filters or a quantity of -1 for their first (and only) item + return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) +end + +local function is_choosing_area() + return uibs.selection_pos.x >= 0 +end + +local function get_cur_area_dims() + if not is_choosing_area() then return 1, 1 end + return math.abs(uibs.selection_pos.x - uibs.pos.x) + 1, + math.abs(uibs.selection_pos.y - uibs.pos.y) + 1 +end + +local function get_cur_filters() + return dfhack.buildings.getFiltersByType({}, uibs.building_type, + uibs.building_subtype, uibs.custom_type) +end + +local function is_plannable() + return get_cur_filters() and + not (uibs.building_type == df.building_type.Construction + and uibs.building_subtype == df.construction_type.TrackNSEW) +end + +local direction_panel_frame = {t=4, h=13, w=46, r=28} + +local direction_panel_types = utils.invert{ + df.building_type.Bridge, + df.building_type.ScrewPump, + df.building_type.WaterWheel, + df.building_type.AxleHorizontal, + df.building_type.Rollers, +} + +local function has_direction_panel() + return direction_panel_types[uibs.building_type] + or (uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.TrackStop) +end + +local function is_over_direction_panel() + if not has_direction_panel() then return false end + local v = widgets.Widget{frame=direction_panel_frame} + local rect = gui.mkdims_wh(0, 0, dfhack.screen.getWindowSize()) + v:updateLayout(gui.ViewRect{rect=rect}) + return v:getMousePos() +end + +local function to_title_case(str) + str = str:gsub('(%a)([%w_]*)', + function (first, rest) return first:upper()..rest:lower() end) + str = str:gsub('_', ' ') + return str +end + +-- returns a reasonable label for the item based on the qualities of the filter +function get_item_label(idx) + local filter = get_cur_filters()[idx] + local desc = 'Unknown' + if filter.has_tool_use then + desc = to_title_case(df.tool_uses[filter.has_tool_use]) + end + if filter.item_type then + desc = to_title_case(df.item_type[filter.item_type]) + end + if filter.flags2 and filter.flags2.building_material then + desc = "Generic building material"; + if filter.flags2.fire_safe then + desc = "Fire-safe building material"; + end + if filter.flags2.magma_safe then + desc = "Magma-safe building material"; + end + elseif filter.vector_id then + desc = to_title_case(df.job_item_vector_id[filter.vector_id]) + end + + local quantity = filter.quantity or 1 + local dimx, dimy = get_cur_area_dims() + if quantity < 1 then + quantity = ((dimx * dimy) // 4) + 1 + else + quantity = quantity * dimx * dimy + end + return ('%s (need: %d)'):format(desc, quantity) +end + +ItemLine = defclass(ItemLine, widgets.Panel) +ItemLine.ATTRS{ + idx=DEFAULT_NIL, +} + +function ItemLine:init() + self.frame.h = 1 + self.visible = function() return #get_cur_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text={{text=function() return get_item_label(self.idx) end}} + }, + } +end PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) PlannerOverlay.ATTRS{ - default_pos={x=46,y=18}, + default_pos={x=6,y=9}, default_enabled=true, viewscreens='dwarfmode/Building/Placement', - frame={w=30, h=4}, - frame_style=gui.MEDIUM_FRAME, + frame={w=54, h=9}, + frame_style=gui.PANEL_FRAME, frame_background=gui.CLEAR_PEN, } function PlannerOverlay:init() self:addviews{ - widgets.ToggleHotkeyLabel{ - frame={t=0, l=0}, - label='build when materials are available', - key='CUSTOM_CTRL_B', + widgets.Label{ + frame={}, + auto_width=true, + text='No items required.', + visible=function() return #get_cur_filters() == 0 end, }, - widgets.HotkeyLabel{ - frame={t=1, l=0}, - label='configure materials', - key='CUSTOM_CTRL_E', - on_activate=do_export, + ItemLine{frame={t=0, l=0}, idx=1}, + ItemLine{frame={t=2, l=0}, idx=2}, + ItemLine{frame={t=4, l=0}, idx=3}, + ItemLine{frame={t=6, l=0}, idx=4}, + widgets.Label{ + frame={b=0, l=17}, + text={ + 'Selected area: ', + {text=function() + return ('%d x %d'):format(get_cur_area_dims()) + end + }, + }, + visible=is_choosing_area, }, } end +function PlannerOverlay:do_config() + dfhack.run_script('gui/buildingplan') +end + +function PlannerOverlay:onInput(keys) + if not is_plannable() then return false end + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + return false + end + if PlannerOverlay.super.onInput(self, keys) then + return true + end + if keys._MOUSE_L_DOWN then + if is_over_direction_panel() then return false end + if self:getMouseFramePos() then return true end + if #uibs.errors > 0 then return true end + local pos = dfhack.gui.getMousePos() + if pos then + if is_choosing_area() or cur_building_has_no_area() then + if #get_cur_filters() == 0 then + return false -- we don't add value; let the game place it + end + self:place_building() + uibs.selection_pos:clear() + return true + elseif not is_choosing_area() then + return false + end + end + end + return keys._MOUSE_L +end + +function PlannerOverlay:render(dc) + if not is_plannable() then return end + PlannerOverlay.super.render(self, dc) +end + +local to_pen = dfhack.pen.parse +local GOOD_PEN = to_pen{ch='o', fg=COLOR_GREEN, + tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} +local BAD_PEN = to_pen{ch='X', fg=COLOR_RED, + tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} + +function PlannerOverlay:onRenderFrame(dc, rect) + PlannerOverlay.super.onRenderFrame(self, dc, rect) + + if not is_choosing_area() then return end + + local bounds = { + x1 = math.min(uibs.selection_pos.x, uibs.pos.x), + x2 = math.max(uibs.selection_pos.x, uibs.pos.x), + y1 = math.min(uibs.selection_pos.y, uibs.pos.y), + y2 = math.max(uibs.selection_pos.y, uibs.pos.y), + } + + local pen = #uibs.errors > 0 and BAD_PEN or GOOD_PEN + + local function get_overlay_pen(pos) + return pen + end + + guidm.renderMapOverlay(get_overlay_pen, bounds) +end + +function PlannerOverlay:place_building() + local direction = uibs.direction + local has_selection = is_choosing_area() + local width = has_selection and math.abs(uibs.selection_pos.x - uibs.pos.x) + 1 or 1 + local height = has_selection and math.abs(uibs.selection_pos.y - uibs.pos.y) + 1 or 1 + local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( + width, height, uibs.building_type, uibs.building_subtype, + uibs.custom_type, direction) + -- get the upper-left corner of the building/area + local pos = xyz2pos( + has_selection and math.min(uibs.selection_pos.x, uibs.pos.x) or uibs.pos.x - adjusted_width//2, + has_selection and math.min(uibs.selection_pos.y, uibs.pos.y) or uibs.pos.y - adjusted_height//2, + uibs.pos.z + ) + local min_x, max_x = pos.x, pos.x + local min_y, max_y = pos.y, pos.y + if adjusted_width == 1 and adjusted_height == 1 and (width > 1 or height > 1) then + min_x = math.ceil(pos.x - width/2) + max_x = min_x + width - 1 + min_y = math.ceil(pos.y - height/2) + max_y = min_y + height - 1 + end + local blds = {} + for y=min_y,max_y do for x=min_x,max_x do + local bld, err = dfhack.buildings.constructBuilding{ + type=uibs.building_type, subtype=uibs.building_subtype, + custom=uibs.custom_type, pos=xyz2pos(x, y, pos.z), + width=adjusted_width, height=adjusted_height, direction=direction} + if err then + for _,b in ipairs(blds) do + dfhack.buildings.deconstruct(b) + end + dfhack.printerr(err) + return + end + -- assign fields for the types that need them. we can't pass them all in + -- to the call to constructBuilding since attempting to assign unrelated + -- fields to building types that don't support them causes errors. + for k,v in pairs(bld) do + if k == 'friction' then bld.friction = uibs.friction end + if k == 'use_dump' then bld.use_dump = uibs.use_dump end + if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end + if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end + if k == 'speed' then bld.speed = uibs.speed end + end + table.insert(blds, bld) + end end + for _,bld in ipairs(blds) do + addPlannedBuilding(bld) + end +end + InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) InspectorOverlay.ATTRS{ default_pos={x=-41,y=14}, @@ -134,48 +375,6 @@ OVERLAY_WIDGETS = { local dialogs = require('gui.dialogs') local guidm = require('gui.dwarfmode') -local function to_title_case(str) - str = str:gsub('(%a)([%w_]*)', - function (first, rest) return first:upper()..rest:lower() end) - str = str:gsub('_', ' ') - return str -end - -local function get_filter(btype, subtype, custom, reverse_idx) - local filters = dfhack.buildings.getFiltersByType( - {}, btype, subtype, custom) - if not filters or reverse_idx < 0 or reverse_idx >= #filters then - error(string.format('invalid index: %d', reverse_idx)) - end - return filters[#filters-reverse_idx] -end - --- returns a reasonable label for the item based on the qualities of the filter --- does not need the core suspended --- reverse_idx is 0-based and is expected to be counted from the *last* filter -function get_item_label(btype, subtype, custom, reverse_idx) - local filter = get_filter(btype, subtype, custom, reverse_idx) - if filter.has_tool_use then - return to_title_case(df.tool_uses[filter.has_tool_use]) - end - if filter.item_type then - return to_title_case(df.item_type[filter.item_type]) - end - if filter.flags2 and filter.flags2.building_material then - if filter.flags2.fire_safe then - return "Fire-safe building material"; - end - if filter.flags2.magma_safe then - return "Magma-safe building material"; - end - return "Generic building material"; - end - if filter.vector_id then - return to_title_case(df.job_item_vector_id[filter.vector_id]) - end - return "Unknown"; -end - -- returns whether the items matched by the specified filter can have a quality -- rating. This also conveniently indicates whether an item can be decorated. -- does not need the core suspended @@ -191,56 +390,6 @@ function item_can_be_improved(btype, subtype, custom, reverse_idx) filter.item_type ~= df.item_type.BOULDER end --- needs the core suspended --- returns a vector of constructed buildings (usually of size 1, but potentially --- more for constructions) -function construct_buildings_from_ui_state() - local uibs = df.global.buildreq - local world = df.global.world - local direction = world.selected_direction - local _, width, height = dfhack.buildings.getCorrectSize( - world.building_width, world.building_height, uibs.building_type, - uibs.building_subtype, uibs.custom_type, direction) - -- the cursor is at the center of the building; we need the upper-left - -- corner of the building - local pos = guidm.getCursorPos() - pos.x = pos.x - math.floor(width/2) - pos.y = pos.y - math.floor(height/2) - local min_x, max_x = pos.x, pos.x - local min_y, max_y = pos.y, pos.y - if width == 1 and height == 1 and - (world.building_width > 1 or world.building_height > 1) then - min_x = math.ceil(pos.x - world.building_width/2) - max_x = min_x + world.building_width - 1 - min_y = math.ceil(pos.y - world.building_height/2) - max_y = min_y + world.building_height - 1 - end - local blds = {} - for y=min_y,max_y do for x=min_x,max_x do - local bld, err = dfhack.buildings.constructBuilding{ - type=uibs.building_type, subtype=uibs.building_subtype, - custom=uibs.custom_type, pos=xyz2pos(x, y, pos.z), - width=width, height=height, direction=direction} - if err then - for _,b in ipairs(blds) do - dfhack.buildings.deconstruct(b) - end - error(err) - end - -- assign fields for the types that need them. we can't pass them all in - -- to the call to constructBuilding since attempting to assign unrelated - -- fields to building types that don't support them causes errors. - for k,v in pairs(bld) do - if k == 'friction' then bld.friction = uibs.friction end - if k == 'use_dump' then bld.use_dump = uibs.use_dump end - if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end - if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end - if k == 'speed' then bld.speed = uibs.speed end - end - table.insert(blds, bld) - end end - return blds -end -- -- GlobalSettings dialog From dd6f71c665f568c04751fcff0d864ada1620bc0c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Feb 2023 02:10:07 -0800 Subject: [PATCH 0667/2222] handle stairs and 3 dimensions --- plugins/lua/buildingplan.lua | 133 ++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 27 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 4167ebb41..c06ddbc15 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -76,9 +76,10 @@ local function is_choosing_area() end local function get_cur_area_dims() - if not is_choosing_area() then return 1, 1 end + if not is_choosing_area() then return 1, 1, 1 end return math.abs(uibs.selection_pos.x - uibs.pos.x) + 1, - math.abs(uibs.selection_pos.y - uibs.pos.y) + 1 + math.abs(uibs.selection_pos.y - uibs.pos.y) + 1, + math.abs(uibs.selection_pos.z - uibs.pos.z) + 1 end local function get_cur_filters() @@ -92,6 +93,11 @@ local function is_plannable() and uibs.building_subtype == df.construction_type.TrackNSEW) end +local function is_stairs() + return uibs.building_type == df.building_type.Construction + and uibs.building_subtype == df.construction_type.UpDownStair +end + local direction_panel_frame = {t=4, h=13, w=46, r=28} local direction_panel_types = utils.invert{ @@ -108,9 +114,23 @@ local function has_direction_panel() and uibs.building_subtype == df.trap_type.TrackStop) end -local function is_over_direction_panel() - if not has_direction_panel() then return false end - local v = widgets.Widget{frame=direction_panel_frame} +local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} + +local function has_pressure_plate_panel() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.PressurePlate +end + +local function is_over_options_panel() + local frame = nil + if has_direction_panel() then + frame = direction_panel_frame + elseif has_pressure_plate_panel() then + frame = pressure_plate_panel_frame + else + return false + end + local v = widgets.Widget{frame=frame} local rect = gui.mkdims_wh(0, 0, dfhack.screen.getWindowSize()) v:updateLayout(gui.ViewRect{rect=rect}) return v:getMousePos() @@ -146,11 +166,11 @@ function get_item_label(idx) end local quantity = filter.quantity or 1 - local dimx, dimy = get_cur_area_dims() + local dimx, dimy, dimz = get_cur_area_dims() if quantity < 1 then - quantity = ((dimx * dimy) // 4) + 1 + quantity = (((dimx * dimy) // 4) + 1) * dimz else - quantity = quantity * dimx * dimy + quantity = quantity * dimx * dimy * dimz end return ('%s (need: %d)'):format(desc, quantity) end @@ -193,12 +213,36 @@ function PlannerOverlay:init() ItemLine{frame={t=2, l=0}, idx=2}, ItemLine{frame={t=4, l=0}, idx=3}, ItemLine{frame={t=6, l=0}, idx=4}, + widgets.CycleHotkeyLabel{ + view_id="stairs_top_subtype", + frame={t=2, l=0}, + key="CUSTOM_R", + label="Top Stair Type: ", + visible=is_stairs, + options={ + {label='Auto', value='auto'}, + {label='UpDown', value=df.construction_type.UpDownStair}, + {label='Down', value=df.construction_type.DownStair}, + }, + }, + widgets.CycleHotkeyLabel { + view_id="stairs_bottom_subtype", + frame={t=3, l=0}, + key="CUSTOM_B", + label="Bottom Stair Type: ", + visible=is_stairs, + options={ + {label='Auto', value='auto'}, + {label='UpDown', value=df.construction_type.UpDownStair}, + {label='Up', value=df.construction_type.UpStair}, + }, + }, widgets.Label{ frame={b=0, l=17}, text={ 'Selected area: ', {text=function() - return ('%d x %d'):format(get_cur_area_dims()) + return ('%d x %d x %d'):format(get_cur_area_dims()) end }, }, @@ -220,7 +264,7 @@ function PlannerOverlay:onInput(keys) return true end if keys._MOUSE_L_DOWN then - if is_over_direction_panel() then return false end + if is_over_options_panel() then return false end if self:getMouseFramePos() then return true end if #uibs.errors > 0 then return true end local pos = dfhack.gui.getMousePos() @@ -274,37 +318,72 @@ end function PlannerOverlay:place_building() local direction = uibs.direction - local has_selection = is_choosing_area() - local width = has_selection and math.abs(uibs.selection_pos.x - uibs.pos.x) + 1 or 1 - local height = has_selection and math.abs(uibs.selection_pos.y - uibs.pos.y) + 1 or 1 + local width, height, depth = get_cur_area_dims() local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( width, height, uibs.building_type, uibs.building_subtype, uibs.custom_type, direction) - -- get the upper-left corner of the building/area - local pos = xyz2pos( + -- get the upper-left corner of the building/area at min z-level + local has_selection = is_choosing_area() + local start_pos = xyz2pos( has_selection and math.min(uibs.selection_pos.x, uibs.pos.x) or uibs.pos.x - adjusted_width//2, has_selection and math.min(uibs.selection_pos.y, uibs.pos.y) or uibs.pos.y - adjusted_height//2, - uibs.pos.z + has_selection and math.min(uibs.selection_pos.z, uibs.pos.z) or uibs.pos.z ) - local min_x, max_x = pos.x, pos.x - local min_y, max_y = pos.y, pos.y - if adjusted_width == 1 and adjusted_height == 1 and (width > 1 or height > 1) then - min_x = math.ceil(pos.x - width/2) + if uibs.building_type == df.building_type.ScrewPump then + if direction == df.screw_pump_direction.FromSouth then + start_pos.y = start_pos.y + 1 + elseif direction == df.screw_pump_direction.FromEast then + start_pos.x = start_pos.x + 1 + end + end + local min_x, max_x = start_pos.x, start_pos.x + local min_y, max_y = start_pos.y, start_pos.y + local min_z, max_z = start_pos.z, start_pos.z + if adjusted_width == 1 and adjusted_height == 1 + and (width > 1 or height > 1 or depth > 1) then max_x = min_x + width - 1 - min_y = math.ceil(pos.y - height/2) max_y = min_y + height - 1 + max_z = math.max(uibs.selection_pos.z, uibs.pos.z) end local blds = {} - for y=min_y,max_y do for x=min_x,max_x do - local bld, err = dfhack.buildings.constructBuilding{ - type=uibs.building_type, subtype=uibs.building_subtype, - custom=uibs.custom_type, pos=xyz2pos(x, y, pos.z), + local subtype = uibs.building_subtype + for z=min_z,max_z do for y=min_y,max_y do for x=min_x,max_x do + local pos = xyz2pos(x, y, z) + if is_stairs() then + if z == min_z then + subtype = self.subviews.stairs_bottom_subtype:getOptionValue() + if subtype == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape == df.tiletype_shape.STAIR_DOWN then + subtype = uibs.building_subtype + else + subtype = df.construction_type.UpStair + end + end + elseif z == max_z then + subtype = self.subviews.stairs_top_subtype:getOptionValue() + if subtype == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape == df.tiletype_shape.STAIR_UP then + subtype = uibs.building_subtype + else + subtype = df.construction_type.DownStair + end + end + else + subtype = uibs.building_subtype + end + end + local bld, err = dfhack.buildings.constructBuilding{pos=pos, + type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, width=adjusted_width, height=adjusted_height, direction=direction} if err then for _,b in ipairs(blds) do dfhack.buildings.deconstruct(b) end - dfhack.printerr(err) + dfhack.printerr(err .. (' (%d, %d, %d)'):format(pos.x, pos.y, pos.z)) return end -- assign fields for the types that need them. we can't pass them all in @@ -318,7 +397,7 @@ function PlannerOverlay:place_building() if k == 'speed' then bld.speed = uibs.speed end end table.insert(blds, bld) - end end + end end end for _,bld in ipairs(blds) do addPlannedBuilding(bld) end From 584e891154239ae26bdeaa03fd9a2f1eadcc19b3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Feb 2023 02:21:19 -0800 Subject: [PATCH 0668/2222] more skeleton for inspector --- plugins/lua/buildingplan.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index c06ddbc15..d9bcdb0d3 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -197,7 +197,7 @@ PlannerOverlay.ATTRS{ default_enabled=true, viewscreens='dwarfmode/Building/Placement', frame={w=54, h=9}, - frame_style=gui.PANEL_FRAME, + frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } @@ -408,7 +408,7 @@ InspectorOverlay.ATTRS{ default_pos={x=-41,y=14}, default_enabled=true, viewscreens='dwarfmode/ViewSheets/BUILDING', - frame={w=30, h=5}, + frame={w=30, h=9}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } @@ -421,10 +421,27 @@ function InspectorOverlay:init() }, widgets.Label{ frame={t=1, l=0}, - text='items', + text='item1', }, - widgets.HotkeyLabel{ + widgets.Label{ frame={t=2, l=0}, + text='item2', + }, + widgets.Label{ + frame={t=3, l=0}, + text='item3', + }, + widgets.Label{ + frame={t=4, l=0}, + text='item4', + }, + widgets.HotkeyLabel{ + frame={t=5, l=0}, + label='adjust filters', + key='CUSTOM_CTRL_F', + }, + widgets.HotkeyLabel{ + frame={t=6, l=0}, label='make top priority', key='CUSTOM_CTRL_T', }, From c490be0271eb1a2ba5362bef55d73773aac16cee Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Feb 2023 17:53:22 -0800 Subject: [PATCH 0669/2222] mark as tested to facilitate testing the commandline --- docs/plugins/buildingplan.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index c51f79721..a331e9eb8 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -3,7 +3,7 @@ buildingplan .. dfhack-tool:: :summary: Plan building construction before you have materials. - :tags: untested fort design buildings + :tags: fort design buildings This plugin adds a planning mode for building placement. You can then place furniture, constructions, and other buildings before the required materials are From a9d9e0e50c24d41dc27774b90142ac4e56b2c9ef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 16:24:10 -0800 Subject: [PATCH 0670/2222] skeleton for quantity scanning --- plugins/buildingplan.cpp | 6 ++++++ plugins/lua/buildingplan.lua | 38 ++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 81f026cbc..a76e81d4a 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -707,6 +707,11 @@ static void scheduleCycle(color_ostream &out) { cycle_requested = true; } +static int countAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering countAvailableItems\n"); + return 10; +} + DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(printStatus), DFHACK_LUA_FUNCTION(setSetting), @@ -715,5 +720,6 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(addPlannedBuilding), DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), + DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_END }; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index d9bcdb0d3..5da03e38f 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -143,8 +143,7 @@ local function to_title_case(str) return str end --- returns a reasonable label for the item based on the qualities of the filter -function get_item_label(idx) +function get_item_line_text(idx) local filter = get_cur_filters()[idx] local desc = 'Unknown' if filter.has_tool_use then @@ -154,17 +153,24 @@ function get_item_label(idx) desc = to_title_case(df.item_type[filter.item_type]) end if filter.flags2 and filter.flags2.building_material then - desc = "Generic building material"; + desc = "Generic material"; if filter.flags2.fire_safe then - desc = "Fire-safe building material"; + desc = "Fire-safe material"; end if filter.flags2.magma_safe then - desc = "Magma-safe building material"; + desc = "Magma-safe material"; end elseif filter.vector_id then desc = to_title_case(df.job_item_vector_id[filter.vector_id]) end + if desc:endswith('s') then + desc = desc:sub(1,-2) + end + if desc == 'Trappart' then + desc = 'Mechanism' + end + local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims() if quantity < 1 then @@ -172,7 +178,14 @@ function get_item_label(idx) else quantity = quantity * dimx * dimy * dimz end - return ('%s (need: %d)'):format(desc, quantity) + desc = ('%d %s%s'):format(quantity, desc, quantity == 1 and '' or 's') + + local available = countAvailableItems(uibs.building_type, + uibs.building_subtype, uibs.custom_type, idx - 1) + local note = available >= quantity and + 'Can build now' or 'Will wait for item' + + return ('%-21s%s%s'):format(desc:sub(1,21), (' '):rep(13), note) end ItemLine = defclass(ItemLine, widgets.Panel) @@ -186,7 +199,11 @@ function ItemLine:init() self:addviews{ widgets.Label{ frame={t=0, l=0}, - text={{text=function() return get_item_label(self.idx) end}} + text={{text=function() return get_item_line_text(self.idx) end}}, + }, + widgets.Label{ + frame={t=0, l=22}, + text='[filter][x]', }, } end @@ -215,7 +232,7 @@ function PlannerOverlay:init() ItemLine{frame={t=6, l=0}, idx=4}, widgets.CycleHotkeyLabel{ view_id="stairs_top_subtype", - frame={t=2, l=0}, + frame={t=3, l=0}, key="CUSTOM_R", label="Top Stair Type: ", visible=is_stairs, @@ -227,7 +244,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel { view_id="stairs_bottom_subtype", - frame={t=3, l=0}, + frame={t=4, l=0}, key="CUSTOM_B", label="Bottom Stair Type: ", visible=is_stairs, @@ -242,7 +259,7 @@ function PlannerOverlay:init() text={ 'Selected area: ', {text=function() - return ('%d x %d x %d'):format(get_cur_area_dims()) + return ('%dx%dx%d'):format(get_cur_area_dims()) end }, }, @@ -401,6 +418,7 @@ function PlannerOverlay:place_building() for _,bld in ipairs(blds) do addPlannedBuilding(bld) end + scheduleCycle() end InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) From 4b7bc937a41c9eb889e59b19f93837629e249d06 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 17:43:03 -0800 Subject: [PATCH 0671/2222] remove old buildingplan files --- plugins/buildingplan/buildingplan-planner.cpp | 1074 --------------- plugins/buildingplan/buildingplan-planner.h | 140 -- plugins/buildingplan/buildingplan-rooms.cpp | 226 ---- plugins/buildingplan/buildingplan-rooms.h | 51 - plugins/buildingplan/buildingplan.cpp | 1168 ----------------- plugins/buildingplan/buildingplan.h | 8 - 6 files changed, 2667 deletions(-) delete mode 100644 plugins/buildingplan/buildingplan-planner.cpp delete mode 100644 plugins/buildingplan/buildingplan-planner.h delete mode 100644 plugins/buildingplan/buildingplan-rooms.cpp delete mode 100644 plugins/buildingplan/buildingplan-rooms.h delete mode 100644 plugins/buildingplan/buildingplan.cpp delete mode 100644 plugins/buildingplan/buildingplan.h diff --git a/plugins/buildingplan/buildingplan-planner.cpp b/plugins/buildingplan/buildingplan-planner.cpp deleted file mode 100644 index 07f23150a..000000000 --- a/plugins/buildingplan/buildingplan-planner.cpp +++ /dev/null @@ -1,1074 +0,0 @@ -#include -#include // for CHAR_BIT - -#include "df/building_design.h" -#include "df/building_doorst.h" -#include "df/building_type.h" -#include "df/general_ref_building_holderst.h" -#include "df/job_item.h" -#include "df/buildreq.h" - -#include "modules/Buildings.h" -#include "modules/Gui.h" -#include "modules/Job.h" - -#include "LuaTools.h" -#include "../uicommon.h" - -#include "buildingplan.h" - -static const std::string planned_building_persistence_key_v1 = "buildingplan/constraints"; -static const std::string planned_building_persistence_key_v2 = "buildingplan/constraints2"; -static const std::string global_settings_persistence_key = "buildingplan/global"; - -/* - * ItemFilter - */ - -ItemFilter::ItemFilter() -{ - clear(); -} - -void ItemFilter::clear() -{ - min_quality = df::item_quality::Ordinary; - max_quality = df::item_quality::Masterful; - decorated_only = false; - clearMaterialMask(); - materials.clear(); -} - -bool ItemFilter::deserialize(std::string ser) -{ - clear(); - - std::vector tokens; - split_string(&tokens, ser, "/"); - if (tokens.size() != 5) - { - debug("invalid ItemFilter serialization: '%s'", ser.c_str()); - return false; - } - - if (!deserializeMaterialMask(tokens[0]) || !deserializeMaterials(tokens[1])) - return false; - - setMinQuality(atoi(tokens[2].c_str())); - setMaxQuality(atoi(tokens[3].c_str())); - decorated_only = static_cast(atoi(tokens[4].c_str())); - return true; -} - -bool ItemFilter::deserializeMaterialMask(std::string ser) -{ - if (ser.empty()) - return true; - - if (!parseJobMaterialCategory(&mat_mask, ser)) - { - debug("invalid job material category serialization: '%s'", ser.c_str()); - return false; - } - return true; -} - -bool ItemFilter::deserializeMaterials(std::string ser) -{ - if (ser.empty()) - return true; - - std::vector mat_names; - split_string(&mat_names, ser, ","); - for (auto m = mat_names.begin(); m != mat_names.end(); m++) - { - DFHack::MaterialInfo material; - if (!material.find(*m) || !material.isValid()) - { - debug("invalid material name serialization: '%s'", ser.c_str()); - return false; - } - materials.push_back(material); - } - return true; -} - -// format: mat,mask,elements/materials,list/minq/maxq/decorated -std::string ItemFilter::serialize() const -{ - std::ostringstream ser; - ser << bitfield_to_string(mat_mask, ",") << "/"; - if (!materials.empty()) - { - ser << materials[0].getToken(); - for (size_t i = 1; i < materials.size(); ++i) - ser << "," << materials[i].getToken(); - } - ser << "/" << static_cast(min_quality); - ser << "/" << static_cast(max_quality); - ser << "/" << static_cast(decorated_only); - return ser.str(); -} - -void ItemFilter::clearMaterialMask() -{ - mat_mask.whole = 0; -} - -void ItemFilter::addMaterialMask(uint32_t mask) -{ - mat_mask.whole |= mask; -} - -void ItemFilter::setMaterials(std::vector materials) -{ - this->materials = materials; -} - -static void clampItemQuality(df::item_quality *quality) -{ - if (*quality > item_quality::Artifact) - { - debug("clamping quality to Artifact"); - *quality = item_quality::Artifact; - } - if (*quality < item_quality::Ordinary) - { - debug("clamping quality to Ordinary"); - *quality = item_quality::Ordinary; - } -} - -void ItemFilter::setMinQuality(int quality) -{ - min_quality = static_cast(quality); - clampItemQuality(&min_quality); - if (max_quality < min_quality) - max_quality = min_quality; -} - -void ItemFilter::setMaxQuality(int quality) -{ - max_quality = static_cast(quality); - clampItemQuality(&max_quality); - if (max_quality < min_quality) - min_quality = max_quality; -} - -void ItemFilter::incMinQuality() { setMinQuality(min_quality + 1); } -void ItemFilter::decMinQuality() { setMinQuality(min_quality - 1); } -void ItemFilter::incMaxQuality() { setMaxQuality(max_quality + 1); } -void ItemFilter::decMaxQuality() { setMaxQuality(max_quality - 1); } - -void ItemFilter::toggleDecoratedOnly() { decorated_only = !decorated_only; } - -static std::string material_to_string_fn(const MaterialInfo &m) { return m.toString(); } - -uint32_t ItemFilter::getMaterialMask() const { return mat_mask.whole; } - -std::vector ItemFilter::getMaterials() const -{ - std::vector descriptions; - transform_(materials, descriptions, material_to_string_fn); - - if (descriptions.size() == 0) - bitfield_to_string(&descriptions, mat_mask); - - if (descriptions.size() == 0) - descriptions.push_back("any"); - - return descriptions; -} - -std::string ItemFilter::getMinQuality() const -{ - return ENUM_KEY_STR(item_quality, min_quality); -} - -std::string ItemFilter::getMaxQuality() const -{ - return ENUM_KEY_STR(item_quality, max_quality); -} - -bool ItemFilter::getDecoratedOnly() const -{ - return decorated_only; -} - -bool ItemFilter::matchesMask(DFHack::MaterialInfo &mat) const -{ - return mat_mask.whole ? mat.matches(mat_mask) : true; -} - -bool ItemFilter::matches(df::dfhack_material_category mask) const -{ - return mask.whole & mat_mask.whole; -} - -bool ItemFilter::matches(DFHack::MaterialInfo &material) const -{ - for (auto it = materials.begin(); it != materials.end(); ++it) - if (material.matches(*it)) - return true; - return false; -} - -bool ItemFilter::matches(df::item *item) const -{ - if (item->getQuality() < min_quality || item->getQuality() > max_quality) - return false; - - if (decorated_only && !item->hasImprovements()) - return false; - - auto imattype = item->getActualMaterial(); - auto imatindex = item->getActualMaterialIndex(); - auto item_mat = DFHack::MaterialInfo(imattype, imatindex); - - return (materials.size() == 0) ? matchesMask(item_mat) : matches(item_mat); -} - - -/* - * PlannedBuilding - */ - -// format: itemfilterser|itemfilterser|... -static std::string serializeFilters(const std::vector &filters) -{ - std::ostringstream ser; - if (!filters.empty()) - { - ser << filters[0].serialize(); - for (size_t i = 1; i < filters.size(); ++i) - ser << "|" << filters[i].serialize(); - } - return ser.str(); -} - -static std::vector deserializeFilters(std::string ser) -{ - std::vector isers; - split_string(&isers, ser, "|"); - std::vector ret; - for (auto & iser : isers) - { - ItemFilter filter; - if (filter.deserialize(iser)) - ret.push_back(filter); - } - return ret; -} - -static size_t getNumFilters(BuildingTypeKey key) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 4) || !Lua::PushModulePublic( - out, L, "plugins.buildingplan", "get_num_filters")) - { - debug("failed to push the lua method on the stack"); - return 0; - } - - Lua::Push(L, std::get<0>(key)); - Lua::Push(L, std::get<1>(key)); - Lua::Push(L, std::get<2>(key)); - - if (!Lua::SafeCall(out, L, 3, 1)) - { - debug("lua call failed"); - return 0; - } - - int num_filters = lua_tonumber(L, -1); - lua_pop(L, 1); - return num_filters; -} - -PlannedBuilding::PlannedBuilding(df::building *building, const std::vector &filters) - : building(building), - building_id(building->id), - filters(filters) -{ - config = DFHack::World::AddPersistentData(planned_building_persistence_key_v2); - config.ival(0) = building_id; - config.val() = serializeFilters(filters); -} - -PlannedBuilding::PlannedBuilding(PersistentDataItem &config) - : config(config), - building(df::building::find(config.ival(0))), - building_id(config.ival(0)), - filters(deserializeFilters(config.val())) -{ - if (building) - { - if (filters.size() != - getNumFilters(toBuildingTypeKey(building))) - { - debug("invalid ItemFilter vector serialization: '%s'", - config.val().c_str()); - building = NULL; - } - } -} - -// Ensure the building still exists and is in a valid state. It can disappear -// for lots of reasons, such as running the game with the buildingplan plugin -// disabled, manually removing the building, modifying it via the API, etc. -bool PlannedBuilding::isValid() const -{ - return building && df::building::find(building_id) - && building->getBuildStage() == 0; -} - -void PlannedBuilding::remove() -{ - DFHack::World::DeletePersistentData(config); - building = NULL; -} - -df::building * PlannedBuilding::getBuilding() -{ - return building; -} - -const std::vector & PlannedBuilding::getFilters() const -{ - // if we want to be able to dynamically change the filters, we'll need to - // re-bucket the tasks in Planner. - return filters; -} - - -/* - * BuildingTypeKey - */ - -BuildingTypeKey toBuildingTypeKey( - df::building_type btype, int16_t subtype, int32_t custom) -{ - return std::make_tuple(btype, subtype, custom); -} - -BuildingTypeKey toBuildingTypeKey(df::building *bld) -{ - return std::make_tuple( - bld->getType(), bld->getSubtype(), bld->getCustomType()); -} - -BuildingTypeKey toBuildingTypeKey(df::ui_build_selector *uibs) -{ - return std::make_tuple( - uibs->building_type, uibs->building_subtype, uibs->custom_type); -} - -// rotates a size_t value left by count bits -// assumes count is not 0 or >= size_t_bits -// replace this with std::rotl when we move to C++20 -static std::size_t rotl_size_t(size_t val, uint32_t count) -{ - static const int size_t_bits = CHAR_BIT * sizeof(std::size_t); - return val << count | val >> (size_t_bits - count); -} - -std::size_t BuildingTypeKeyHash::operator() (const BuildingTypeKey & key) const -{ - // cast first param to appease gcc-4.8, which is missing the enum - // specializations for std::hash - std::size_t h1 = std::hash()(static_cast(std::get<0>(key))); - std::size_t h2 = std::hash()(std::get<1>(key)); - std::size_t h3 = std::hash()(std::get<2>(key)); - - return h1 ^ rotl_size_t(h2, 8) ^ rotl_size_t(h3, 16); -} - - -/* - * Planner - */ - -// convert v1 persistent data into v2 format -// we can remove this conversion code once v2 has been live for a while -void migrateV1ToV2() -{ - std::vector configs; - DFHack::World::GetPersistentData(&configs, planned_building_persistence_key_v1); - if (configs.empty()) - return; - - debug("migrating %zu persisted configs to new format", configs.size()); - for (auto config : configs) - { - df::building *bld = df::building::find(config.ival(1)); - if (!bld) - { - debug("buliding no longer exists; removing config"); - DFHack::World::DeletePersistentData(config); - continue; - } - - if (bld->getBuildStage() != 0 || bld->jobs.size() != 1 - || bld->jobs[0]->job_items.size() != 1) - { - debug("building in invalid state; removing config"); - DFHack::World::DeletePersistentData(config); - continue; - } - - // fix up the building so we can set the material properties later - bld->mat_type = -1; - bld->mat_index = -1; - - // the v1 filters are not initialized correctly and will match any item. - // we need to fix them up a bit. - auto filter = bld->jobs[0]->job_items[0]; - df::item_type type; - switch (bld->getType()) - { - case df::building_type::Armorstand: type = df::item_type::ARMORSTAND; break; - case df::building_type::Bed: type = df::item_type::BED; break; - case df::building_type::Chair: type = df::item_type::CHAIR; break; - case df::building_type::Coffin: type = df::item_type::COFFIN; break; - case df::building_type::Door: type = df::item_type::DOOR; break; - case df::building_type::Floodgate: type = df::item_type::FLOODGATE; break; - case df::building_type::Hatch: type = df::item_type::HATCH_COVER; break; - case df::building_type::GrateWall: type = df::item_type::GRATE; break; - case df::building_type::GrateFloor: type = df::item_type::GRATE; break; - case df::building_type::BarsVertical: type = df::item_type::BAR; break; - case df::building_type::BarsFloor: type = df::item_type::BAR; break; - case df::building_type::Cabinet: type = df::item_type::CABINET; break; - case df::building_type::Box: type = df::item_type::BOX; break; - case df::building_type::Weaponrack: type = df::item_type::WEAPONRACK; break; - case df::building_type::Statue: type = df::item_type::STATUE; break; - case df::building_type::Slab: type = df::item_type::SLAB; break; - case df::building_type::Table: type = df::item_type::TABLE; break; - case df::building_type::WindowGlass: type = df::item_type::WINDOW; break; - case df::building_type::AnimalTrap: type = df::item_type::ANIMALTRAP; break; - case df::building_type::Chain: type = df::item_type::CHAIN; break; - case df::building_type::Cage: type = df::item_type::CAGE; break; - case df::building_type::TractionBench: type = df::item_type::TRACTION_BENCH; break; - default: - debug("building has unhandled type; removing config"); - DFHack::World::DeletePersistentData(config); - continue; - } - filter->item_type = type; - filter->item_subtype = -1; - filter->mat_type = -1; - filter->mat_index = -1; - filter->flags1.whole = 0; - filter->flags2.whole = 0; - filter->flags2.bits.allow_artifact = true; - filter->flags3.whole = 0; - filter->flags4 = 0; - filter->flags5 = 0; - filter->metal_ore = -1; - filter->min_dimension = -1; - filter->has_tool_use = df::tool_uses::NONE; - filter->quantity = 1; - - std::vector tokens; - split_string(&tokens, config.val(), "/"); - if (tokens.size() != 2) - { - debug("invalid v1 format; removing config"); - DFHack::World::DeletePersistentData(config); - continue; - } - - ItemFilter item_filter; - item_filter.deserializeMaterialMask(tokens[0]); - item_filter.deserializeMaterials(tokens[1]); - item_filter.setMinQuality(config.ival(2) - 1); - item_filter.setMaxQuality(config.ival(4) - 1); - if (config.ival(3) - 1) - item_filter.toggleDecoratedOnly(); - - // create the v2 record - std::vector item_filters; - item_filters.push_back(item_filter); - PlannedBuilding pb(bld, item_filters); - - // remove the v1 record - DFHack::World::DeletePersistentData(config); - debug("v1 %s(%d) record successfully migrated", - ENUM_KEY_STR(building_type, bld->getType()).c_str(), - bld->id); - } -} - -// assumes no setting has '=' or '|' characters -static std::string serialize_settings(std::map & settings) -{ - std::ostringstream ser; - for (auto & entry : settings) - { - ser << entry.first << "=" << (entry.second ? "1" : "0") << "|"; - } - return ser.str(); -} - -static void deserialize_settings(std::map & settings, - std::string ser) -{ - std::vector tokens; - split_string(&tokens, ser, "|"); - for (auto token : tokens) - { - if (token.empty()) - continue; - - std::vector parts; - split_string(&parts, token, "="); - if (parts.size() != 2) - { - debug("invalid serialized setting format: '%s'", token.c_str()); - continue; - } - std::string key = parts[0]; - if (settings.count(key) == 0) - { - debug("unknown serialized setting: '%s", key.c_str()); - continue; - } - settings[key] = static_cast(atoi(parts[1].c_str())); - debug("deserialized setting: %s = %d", key.c_str(), settings[key]); - } -} - -static DFHack::PersistentDataItem init_global_settings( - std::map & settings) -{ - settings.clear(); - settings["blocks"] = true; - settings["boulders"] = true; - settings["logs"] = true; - settings["bars"] = false; - - // load persistent global settings if they exist; otherwise create them - std::vector items; - DFHack::World::GetPersistentData(&items, global_settings_persistence_key); - if (items.size() == 1) - { - DFHack::PersistentDataItem & config = items[0]; - deserialize_settings(settings, config.val()); - return config; - } - - debug("initializing persistent global settings"); - DFHack::PersistentDataItem config = - DFHack::World::AddPersistentData(global_settings_persistence_key); - config.val() = serialize_settings(settings); - return config; -} - -const std::map & Planner::getGlobalSettings() const -{ - return global_settings; -} - -bool Planner::setGlobalSetting(std::string name, bool value) -{ - if (global_settings.count(name) == 0) - { - debug("attempted to set invalid setting: '%s'", name.c_str()); - return false; - } - debug("global setting '%s' %d -> %d", - name.c_str(), global_settings[name], value); - global_settings[name] = value; - if (config.isValid()) - config.val() = serialize_settings(global_settings); - return true; -} - -void Planner::reset() -{ - debug("resetting Planner state"); - default_item_filters.clear(); - planned_buildings.clear(); - tasks.clear(); - - config = init_global_settings(global_settings); - - migrateV1ToV2(); - - std::vector items; - DFHack::World::GetPersistentData(&items, planned_building_persistence_key_v2); - debug("found data for %zu planned building(s)", items.size()); - - for (auto i = items.begin(); i != items.end(); i++) - { - PlannedBuilding pb(*i); - if (!pb.isValid()) - { - debug("discarding invalid planned building"); - pb.remove(); - continue; - } - - if (registerTasks(pb)) - planned_buildings.insert(std::make_pair(pb.getBuilding()->id, pb)); - } -} - -void Planner::addPlannedBuilding(df::building *bld) -{ - auto item_filters = getItemFilters(toBuildingTypeKey(bld)).get(); - // not a supported type - if (item_filters.empty()) - { - debug("failed to add building: unsupported type"); - return; - } - - // protect against multiple registrations - if (planned_buildings.count(bld->id) != 0) - { - debug("failed to add building: already registered"); - return; - } - - PlannedBuilding pb(bld, item_filters); - if (pb.isValid() && registerTasks(pb)) - { - for (auto job : bld->jobs) - job->flags.bits.suspend = true; - - planned_buildings.insert(std::make_pair(bld->id, pb)); - } - else - { - pb.remove(); - } -} - -static std::string getBucket(const df::job_item & ji, - const std::vector & item_filters) -{ - std::ostringstream ser; - - // pull out and serialize only known relevant fields. if we miss a few, then - // the filter bucket will be slighly less specific than it could be, but - // that's probably ok. we'll just end up bucketing slightly different items - // together. this is only a problem if the different filter at the front of - // the queue doesn't match any available items and blocks filters behind it - // that could be matched. - ser << ji.item_type << ':' << ji.item_subtype << ':' << ji.mat_type << ':' - << ji.mat_index << ':' << ji.flags1.whole << ':' << ji.flags2.whole - << ':' << ji.flags3.whole << ':' << ji.flags4 << ':' << ji.flags5 << ':' - << ji.metal_ore << ':' << ji.has_tool_use; - - for (auto & item_filter : item_filters) - { - ser << ':' << item_filter.serialize(); - } - - return ser.str(); -} - -// get a list of item vectors that we should search for matches -static std::vector getVectorIds(df::job_item *job_item, - const std::map & global_settings) -{ - std::vector ret; - - // if the filter already has the vector_id set to something specific, use it - if (job_item->vector_id > df::job_item_vector_id::IN_PLAY) - { - debug("using vector_id from job_item: %s", - ENUM_KEY_STR(job_item_vector_id, job_item->vector_id).c_str()); - ret.push_back(job_item->vector_id); - return ret; - } - - // if the filer is for building material, refer to our global settings for - // which vectors to search - if (job_item->flags2.bits.building_material) - { - if (global_settings.at("blocks")) - ret.push_back(df::job_item_vector_id::BLOCKS); - if (global_settings.at("boulders")) - ret.push_back(df::job_item_vector_id::BOULDER); - if (global_settings.at("logs")) - ret.push_back(df::job_item_vector_id::WOOD); - if (global_settings.at("bars")) - ret.push_back(df::job_item_vector_id::BAR); - } - - // fall back to IN_PLAY if no other vector was appropriate - if (ret.empty()) - ret.push_back(df::job_item_vector_id::IN_PLAY); - return ret; -} - -bool Planner::registerTasks(PlannedBuilding & pb) -{ - df::building * bld = pb.getBuilding(); - if (bld->jobs.size() != 1) - { - debug("unexpected number of jobs: want 1, got %zu", bld->jobs.size()); - return false; - } - auto job_items = bld->jobs[0]->job_items; - int num_job_items = job_items.size(); - if (num_job_items < 1) - { - debug("unexpected number of job items: want >0, got %d", num_job_items); - return false; - } - int32_t id = bld->id; - for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) - { - auto job_item = job_items[job_item_idx]; - auto bucket = getBucket(*job_item, pb.getFilters()); - auto vector_ids = getVectorIds(job_item, global_settings); - - // if there are multiple vector_ids, schedule duplicate tasks. after - // the correct number of items are matched, the extras will get popped - // as invalid - for (auto vector_id : vector_ids) - { - for (int item_num = 0; item_num < job_item->quantity; ++item_num) - { - tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx)); - debug("added task: %s/%s/%d,%d; " - "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket.c_str(), id, job_item_idx, tasks.size(), - tasks[vector_id].size(), tasks[vector_id][bucket].size()); - } - } - } - return true; -} - -PlannedBuilding * Planner::getPlannedBuilding(df::building *bld) -{ - if (!bld || planned_buildings.count(bld->id) == 0) - return NULL; - return &planned_buildings.at(bld->id); -} - -bool Planner::isPlannableBuilding(BuildingTypeKey key) -{ - return getNumFilters(key) >= 1; -} - -Planner::ItemFiltersWrapper Planner::getItemFilters(BuildingTypeKey key) -{ - static std::vector empty_vector; - static const ItemFiltersWrapper empty_ret(empty_vector); - - size_t nfilters = getNumFilters(key); - if (nfilters < 1) - return empty_ret; - while (default_item_filters[key].size() < nfilters) - default_item_filters[key].push_back(ItemFilter()); - return ItemFiltersWrapper(default_item_filters[key]); -} - -// precompute a bitmask with bad item flags -struct BadFlags -{ - uint32_t whole; - - BadFlags() - { - df::item_flags flags; - #define F(x) flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(in_job); - F(owned); F(in_chest); F(removed); F(encased); - #undef F - whole = flags.whole; - } -}; - -static bool itemPassesScreen(df::item * item) -{ - static BadFlags bad_flags; - return !(item->flags.whole & bad_flags.whole) - && !item->isAssignedToStockpile() - // TODO: make this configurable - && !(item->getType() == df::item_type::BOX && item->isBag()); -} - -static bool matchesFilters(df::item * item, - df::job_item * job_item, - const ItemFilter & item_filter) -{ - // check the properties that are not checked by Job::isSuitableItem() - if (job_item->item_type > -1 && job_item->item_type != item->getType()) - return false; - - if (job_item->item_subtype > -1 && - job_item->item_subtype != item->getSubtype()) - return false; - - if (job_item->flags2.bits.building_material && !item->isBuildMat()) - return false; - - if (job_item->metal_ore > -1 && !item->isMetalOre(job_item->metal_ore)) - return false; - - if (job_item->has_tool_use > df::tool_uses::NONE - && !item->hasToolUse(job_item->has_tool_use)) - return false; - - return DFHack::Job::isSuitableItem( - job_item, item->getType(), item->getSubtype()) - && DFHack::Job::isSuitableMaterial( - job_item, item->getMaterial(), item->getMaterialIndex(), - item->getType()) - && item_filter.matches(item); -} - -// note that this just removes the PlannedBuilding. the tasks will get dropped -// as we discover them in the tasks queues and they fail their isValid() check. -// this "lazy" task cleaning algorithm works because there is no way to -// re-register a building once it has been removed -- if it fails isValid() -// then it has either been built or desroyed. therefore there is no chance of -// duplicate tasks getting added to the tasks queues. -void Planner::unregisterBuilding(int32_t id) -{ - if (planned_buildings.count(id) > 0) - { - planned_buildings.at(id).remove(); - planned_buildings.erase(id); - } -} - -static bool isJobReady(df::job * job) -{ - int needed_items = 0; - for (auto job_item : job->job_items) { needed_items += job_item->quantity; } - if (needed_items) - { - debug("building needs %d more item(s)", needed_items); - return false; - } - return true; -} - -static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b) -{ - // we want the items in the opposite order of the filters - return a->job_item_idx > b->job_item_idx; -} - -// this function does not remove the job_items since their quantity fields are -// now all at 0, so there is no risk of having extra items attached. we don't -// remove them to keep the "finalize with buildingplan active" path as similar -// as possible to the "finalize with buildingplan disabled" path. -static void finalizeBuilding(df::building * bld) -{ - debug("finalizing building %d", bld->id); - auto job = bld->jobs[0]; - - // sort the items so they get added to the structure in the correct order - std::sort(job->items.begin(), job->items.end(), job_item_idx_lt); - - // derive the material properties of the building and job from the first - // applicable item, though if any boulders are involved, it makes the whole - // structure "rough". - bool rough = false; - for (auto attached_item : job->items) - { - df::item *item = attached_item->item; - rough = rough || item->getType() == item_type::BOULDER; - if (bld->mat_type == -1) - { - bld->mat_type = item->getMaterial(); - job->mat_type = bld->mat_type; - } - if (bld->mat_index == -1) - { - bld->mat_index = item->getMaterialIndex(); - job->mat_index = bld->mat_index; - } - } - - if (bld->needsDesign()) - { - auto act = (df::building_actual *)bld; - if (!act->design) - act->design = new df::building_design(); - act->design->flags.bits.rough = rough; - } - - // we're good to go! - job->flags.bits.suspend = false; - Job::checkBuildingsNow(); -} - -void Planner::popInvalidTasks(std::queue> & task_queue) -{ - while (!task_queue.empty()) - { - auto & task = task_queue.front(); - auto id = task.first; - if (planned_buildings.count(id) > 0) - { - PlannedBuilding & pb = planned_buildings.at(id); - if (pb.isValid() && - pb.getBuilding()->jobs[0]->job_items[task.second]->quantity) - { - break; - } - } - debug("discarding invalid task: bld=%d, job_item_idx=%d", - id, task.second); - task_queue.pop(); - unregisterBuilding(id); - } -} - -void Planner::doVector(df::job_item_vector_id vector_id, - std::map>> & buckets) -{ - auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); - auto item_vector = df::global::world->items.other[other_id]; - debug("matching %zu item(s) in vector %s against %zu filter bucket(s)", - item_vector.size(), - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - buckets.size()); - for (auto item_it = item_vector.rbegin(); - item_it != item_vector.rend(); - ++item_it) - { - auto item = *item_it; - if (!itemPassesScreen(item)) - continue; - for (auto bucket_it = buckets.begin(); bucket_it != buckets.end();) - { - auto & task_queue = bucket_it->second; - popInvalidTasks(task_queue); - if (task_queue.empty()) - { - debug("removing empty bucket: %s/%s; %zu bucket(s) left", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket_it->first.c_str(), - buckets.size() - 1); - bucket_it = buckets.erase(bucket_it); - continue; - } - auto & task = task_queue.front(); - auto id = task.first; - auto & pb = planned_buildings.at(id); - auto building = pb.getBuilding(); - auto job = building->jobs[0]; - auto filter_idx = task.second; - if (matchesFilters(item, job->job_items[filter_idx], - pb.getFilters()[filter_idx]) - && DFHack::Job::attachJobItem(job, item, - df::job_item_ref::Hauled, filter_idx)) - { - MaterialInfo material; - material.decode(item); - ItemTypeInfo item_type; - item_type.decode(item); - debug("attached %s %s to filter %d for %s(%d): %s/%s", - material.toString().c_str(), - item_type.toString().c_str(), - filter_idx, - ENUM_KEY_STR(building_type, building->getType()).c_str(), - id, - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket_it->first.c_str()); - // keep quantity aligned with the actual number of remaining - // items so if buildingplan is turned off, the building will - // be completed with the correct number of items. - --job->job_items[filter_idx]->quantity; - task_queue.pop(); - if (isJobReady(job)) - { - finalizeBuilding(building); - unregisterBuilding(id); - } - if (task_queue.empty()) - { - debug( - "removing empty item bucket: %s/%s; %zu left", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket_it->first.c_str(), - buckets.size() - 1); - buckets.erase(bucket_it); - } - // we found a home for this item; no need to look further - break; - } - ++bucket_it; - } - if (buckets.empty()) - break; - } -} - -struct VectorsToScanLast -{ - std::vector vectors; - VectorsToScanLast() - { - // order is important here. we want to match boulders before wood and - // everything before bars. blocks are not listed here since we'll have - // already scanned them when we did the first pass through the buckets. - vectors.push_back(df::job_item_vector_id::BOULDER); - vectors.push_back(df::job_item_vector_id::WOOD); - vectors.push_back(df::job_item_vector_id::BAR); - } -}; - -void Planner::doCycle() -{ - debug("running cycle for %zu registered building(s)", - planned_buildings.size()); - static const VectorsToScanLast vectors_to_scan_last; - for (auto it = tasks.begin(); it != tasks.end();) - { - auto vector_id = it->first; - // we could make this a set, but it's only three elements - if (std::find(vectors_to_scan_last.vectors.begin(), - vectors_to_scan_last.vectors.end(), - vector_id) != vectors_to_scan_last.vectors.end()) - { - ++it; - continue; - } - - auto & buckets = it->second; - doVector(vector_id, buckets); - if (buckets.empty()) - { - debug("removing empty vector: %s; %zu vector(s) left", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - tasks.size() - 1); - it = tasks.erase(it); - } - else - ++it; - } - for (auto vector_id : vectors_to_scan_last.vectors) - { - if (tasks.count(vector_id) == 0) - continue; - auto & buckets = tasks[vector_id]; - doVector(vector_id, buckets); - if (buckets.empty()) - { - debug("removing empty vector: %s; %zu vector(s) left", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - tasks.size() - 1); - tasks.erase(vector_id); - } - } - debug("cycle done; %zu registered building(s) left", - planned_buildings.size()); -} - -Planner planner; diff --git a/plugins/buildingplan/buildingplan-planner.h b/plugins/buildingplan/buildingplan-planner.h deleted file mode 100644 index 7b1615704..000000000 --- a/plugins/buildingplan/buildingplan-planner.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include -#include - -#include "df/building.h" -#include "df/dfhack_material_category.h" -#include "df/item_quality.h" -#include "df/job_item.h" - -#include "modules/Materials.h" -#include "modules/Persistence.h" - -class ItemFilter -{ -public: - ItemFilter(); - - void clear(); - bool deserialize(std::string ser); - std::string serialize() const; - - void addMaterialMask(uint32_t mask); - void clearMaterialMask(); - void setMaterials(std::vector materials); - - void incMinQuality(); - void decMinQuality(); - void incMaxQuality(); - void decMaxQuality(); - void toggleDecoratedOnly(); - - uint32_t getMaterialMask() const; - std::vector getMaterials() const; - std::string getMinQuality() const; - std::string getMaxQuality() const; - bool getDecoratedOnly() const; - - bool matches(df::dfhack_material_category mask) const; - bool matches(DFHack::MaterialInfo &material) const; - bool matches(df::item *item) const; - -private: - // remove friend declaration when we no longer need v1 deserialization - friend void migrateV1ToV2(); - - df::dfhack_material_category mat_mask; - std::vector materials; - df::item_quality min_quality; - df::item_quality max_quality; - bool decorated_only; - - bool deserializeMaterialMask(std::string ser); - bool deserializeMaterials(std::string ser); - void setMinQuality(int quality); - void setMaxQuality(int quality); - bool matchesMask(DFHack::MaterialInfo &mat) const; -}; - -class PlannedBuilding -{ -public: - PlannedBuilding(df::building *building, const std::vector &filters); - PlannedBuilding(DFHack::PersistentDataItem &config); - - bool isValid() const; - void remove(); - - df::building * getBuilding(); - const std::vector & getFilters() const; - -private: - DFHack::PersistentDataItem config; - df::building *building; - const df::building::key_field_type building_id; - const std::vector filters; -}; - -// building type, subtype, custom -typedef std::tuple BuildingTypeKey; - -BuildingTypeKey toBuildingTypeKey( - df::building_type btype, int16_t subtype, int32_t custom); -BuildingTypeKey toBuildingTypeKey(df::building *bld); -BuildingTypeKey toBuildingTypeKey(df::ui_build_selector *uibs); - -struct BuildingTypeKeyHash -{ - std::size_t operator() (const BuildingTypeKey & key) const; -}; - -class Planner -{ -public: - class ItemFiltersWrapper - { - public: - ItemFiltersWrapper(std::vector & item_filters) - : item_filters(item_filters) { } - std::vector::reverse_iterator rbegin() const { return item_filters.rbegin(); } - std::vector::reverse_iterator rend() const { return item_filters.rend(); } - const std::vector & get() const { return item_filters; } - private: - std::vector &item_filters; - }; - - const std::map & getGlobalSettings() const; - bool setGlobalSetting(std::string name, bool value); - - void reset(); - - void addPlannedBuilding(df::building *bld); - PlannedBuilding *getPlannedBuilding(df::building *bld); - - bool isPlannableBuilding(BuildingTypeKey key); - - // returns an empty vector if the type is not supported - ItemFiltersWrapper getItemFilters(BuildingTypeKey key); - - void doCycle(); - -private: - DFHack::PersistentDataItem config; - std::map global_settings; - std::unordered_map, - BuildingTypeKeyHash> default_item_filters; - // building id -> PlannedBuilding - std::unordered_map planned_buildings; - // vector id -> filter bucket -> queue of (building id, job_item index) - std::map>>> tasks; - - bool registerTasks(PlannedBuilding &plannedBuilding); - void unregisterBuilding(int32_t id); - void popInvalidTasks(std::queue> &task_queue); - void doVector(df::job_item_vector_id vector_id, - std::map>> & buckets); -}; - -extern Planner planner; diff --git a/plugins/buildingplan/buildingplan-rooms.cpp b/plugins/buildingplan/buildingplan-rooms.cpp deleted file mode 100644 index a08c85804..000000000 --- a/plugins/buildingplan/buildingplan-rooms.cpp +++ /dev/null @@ -1,226 +0,0 @@ -#include "buildingplan.h" - -#include -#include -#include - -#include -#include -#include - -using namespace DFHack; - -bool canReserveRoom(df::building *building) -{ - if (!building) - return false; - - if (building->jobs.size() > 0 && building->jobs[0]->job_type == df::job_type::DestroyBuilding) - return false; - - return building->is_room; -} - -std::vector getUniqueNoblePositions(df::unit *unit) -{ - std::vector np; - Units::getNoblePositions(&np, unit); - for (auto iter = np.begin(); iter != np.end(); iter++) - { - if (iter->position->code == "MILITIA_CAPTAIN") - { - np.erase(iter); - break; - } - } - - return np; -} - -/* - * ReservedRoom - */ - -ReservedRoom::ReservedRoom(df::building *building, std::string noble_code) -{ - this->building = building; - config = DFHack::World::AddPersistentData("buildingplan/reservedroom"); - config.val() = noble_code; - config.ival(1) = building->id; - pos = df::coord(building->centerx, building->centery, building->z); -} - -ReservedRoom::ReservedRoom(PersistentDataItem &config, color_ostream &) -{ - this->config = config; - - building = df::building::find(config.ival(1)); - if (!building) - return; - pos = df::coord(building->centerx, building->centery, building->z); -} - -bool ReservedRoom::checkRoomAssignment() -{ - if (!isValid()) - return false; - - auto np = getOwnersNobleCode(); - bool correctOwner = false; - for (auto iter = np.begin(); iter != np.end(); iter++) - { - if (iter->position->code == getCode()) - { - correctOwner = true; - break; - } - } - - if (correctOwner) - return true; - - for (auto iter = df::global::world->units.active.begin(); iter != df::global::world->units.active.end(); iter++) - { - df::unit* unit = *iter; - if (!Units::isCitizen(unit)) - continue; - - if (!Units::isActive(unit)) - continue; - - np = getUniqueNoblePositions(unit); - for (auto iter = np.begin(); iter != np.end(); iter++) - { - if (iter->position->code == getCode()) - { - Buildings::setOwner(building, unit); - break; - } - } - } - - return true; -} - -void ReservedRoom::remove() { DFHack::World::DeletePersistentData(config); } - -bool ReservedRoom::isValid() -{ - if (!building) - return false; - - if (Buildings::findAtTile(pos) != building) - return false; - - return canReserveRoom(building); -} - -int32_t ReservedRoom::getId() -{ - if (!isValid()) - return 0; - - return building->id; -} - -std::string ReservedRoom::getCode() { return config.val(); } - -void ReservedRoom::setCode(const std::string &noble_code) { config.val() = noble_code; } - -std::vector ReservedRoom::getOwnersNobleCode() -{ - if (!building->owner) - return std::vector (); - - return getUniqueNoblePositions(building->owner); -} - -/* - * RoomMonitor - */ - -std::string RoomMonitor::getReservedNobleCode(int32_t buildingId) -{ - for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++) - { - if (buildingId == iter->getId()) - return iter->getCode(); - } - - return ""; -} - -void RoomMonitor::toggleRoomForPosition(int32_t buildingId, std::string noble_code) -{ - bool found = false; - for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++) - { - if (buildingId != iter->getId()) - { - continue; - } - else - { - if (noble_code == iter->getCode()) - { - iter->remove(); - reservedRooms.erase(iter); - } - else - { - iter->setCode(noble_code); - } - found = true; - break; - } - } - - if (!found) - { - ReservedRoom room(df::building::find(buildingId), noble_code); - reservedRooms.push_back(room); - } -} - -void RoomMonitor::doCycle() -{ - for (auto iter = reservedRooms.begin(); iter != reservedRooms.end();) - { - if (iter->checkRoomAssignment()) - { - ++iter; - } - else - { - iter->remove(); - iter = reservedRooms.erase(iter); - } - } -} - -void RoomMonitor::reset(color_ostream &out) -{ - reservedRooms.clear(); - std::vector items; - DFHack::World::GetPersistentData(&items, "buildingplan/reservedroom"); - - for (auto i = items.begin(); i != items.end(); i++) - { - ReservedRoom rr(*i, out); - if (rr.isValid()) - addRoom(rr); - } -} - -void RoomMonitor::addRoom(ReservedRoom &rr) -{ - for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++) - { - if (iter->getId() == rr.getId()) - return; - } - - reservedRooms.push_back(rr); -} - -RoomMonitor roomMonitor; diff --git a/plugins/buildingplan/buildingplan-rooms.h b/plugins/buildingplan/buildingplan-rooms.h deleted file mode 100644 index 3880dbe06..000000000 --- a/plugins/buildingplan/buildingplan-rooms.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "modules/Persistence.h" -#include "modules/Units.h" - -class ReservedRoom -{ -public: - ReservedRoom(df::building *building, std::string noble_code); - - ReservedRoom(DFHack::PersistentDataItem &config, DFHack::color_ostream &out); - - bool checkRoomAssignment(); - void remove(); - bool isValid(); - - int32_t getId(); - std::string getCode(); - void setCode(const std::string &noble_code); - -private: - df::building *building; - DFHack::PersistentDataItem config; - df::coord pos; - - std::vector getOwnersNobleCode(); -}; - -class RoomMonitor -{ -public: - RoomMonitor() { } - - std::string getReservedNobleCode(int32_t buildingId); - - void toggleRoomForPosition(int32_t buildingId, std::string noble_code); - - void doCycle(); - - void reset(DFHack::color_ostream &out); - -private: - std::vector reservedRooms; - - void addRoom(ReservedRoom &rr); -}; - -bool canReserveRoom(df::building *building); -std::vector getUniqueNoblePositions(df::unit *unit); - -extern RoomMonitor roomMonitor; diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp deleted file mode 100644 index cd4e84a6e..000000000 --- a/plugins/buildingplan/buildingplan.cpp +++ /dev/null @@ -1,1168 +0,0 @@ -#include "df/construction_type.h" -#include "df/entity_position.h" -#include "df/interface_key.h" -#include "df/buildreq.h" -#include "df/viewscreen_dwarfmodest.h" - -#include "modules/Gui.h" -#include "modules/Maps.h" -#include "modules/World.h" - -#include "Core.h" -#include "LuaTools.h" -#include "PluginManager.h" - -#include "../uicommon.h" -#include "../listcolumn.h" -#include "buildingplan.h" - -DFHACK_PLUGIN("buildingplan"); -#define PLUGIN_VERSION "2.0" -REQUIRE_GLOBAL(plotinfo); -REQUIRE_GLOBAL(ui_build_selector); -REQUIRE_GLOBAL(world); // used in buildingplan library - -#define MAX_MASK 10 -#define MAX_MATERIAL 21 - -bool show_help = false; -bool quickfort_mode = false; -bool all_enabled = false; -bool in_dummy_screen = false; -std::unordered_map planmode_enabled; - -bool show_debugging = false; - -void debug(const char *fmt, ...) -{ - if (!show_debugging) - return; - - color_ostream_proxy out(Core::getInstance().getConsole()); - out.print("DEBUG(buildingplan): "); - va_list args; - va_start(args, fmt); - out.vprint(fmt, args); - va_end(args); - out.print("\n"); -} - -class ViewscreenChooseMaterial : public dfhack_viewscreen -{ -public: - ViewscreenChooseMaterial(ItemFilter &filter); - - void feed(set *input); - - void render(); - - std::string getFocusString() { return "buildingplan_choosemat"; } - -private: - ListColumn masks_column; - ListColumn materials_column; - int selected_column; - ItemFilter &filter; - - void addMaskEntry(df::dfhack_material_category &mask, const std::string &text) - { - auto entry = ListEntry(pad_string(text, MAX_MASK, false), mask); - if (filter.matches(mask)) - entry.selected = true; - - masks_column.add(entry); - } - - void populateMasks() - { - masks_column.clear(); - df::dfhack_material_category mask; - - mask.whole = 0; - mask.bits.stone = true; - addMaskEntry(mask, "Stone"); - - mask.whole = 0; - mask.bits.wood = true; - addMaskEntry(mask, "Wood"); - - mask.whole = 0; - mask.bits.metal = true; - addMaskEntry(mask, "Metal"); - - mask.whole = 0; - mask.bits.soap = true; - addMaskEntry(mask, "Soap"); - - masks_column.filterDisplay(); - } - - void populateMaterials() - { - materials_column.clear(); - df::dfhack_material_category selected_category; - std::vector selected_masks = masks_column.getSelectedElems(); - if (selected_masks.size() == 1) - selected_category = selected_masks[0]; - else if (selected_masks.size() > 1) - return; - - df::world_raws &raws = world->raws; - for (int i = 1; i < DFHack::MaterialInfo::NUM_BUILTIN; i++) - { - auto obj = raws.mat_table.builtin[i]; - if (obj) - { - MaterialInfo material; - material.decode(i, -1); - addMaterialEntry(selected_category, material, material.toString()); - } - } - - for (size_t i = 0; i < raws.inorganics.size(); i++) - { - MaterialInfo material; - material.decode(0, i); - addMaterialEntry(selected_category, material, material.toString()); - } - - decltype(selected_category) wood_flag; - wood_flag.bits.wood = true; - if (!selected_category.whole || selected_category.bits.wood) - { - for (size_t i = 0; i < raws.plants.all.size(); i++) - { - df::plant_raw *p = raws.plants.all[i]; - for (size_t j = 0; p->material.size() > 1 && j < p->material.size(); j++) - { - if (p->material[j]->id != "WOOD") - continue; - - MaterialInfo material; - material.decode(DFHack::MaterialInfo::PLANT_BASE+j, i); - auto name = material.toString(); - ListEntry entry(pad_string(name, MAX_MATERIAL, false), material); - if (filter.matches(material)) - entry.selected = true; - - materials_column.add(entry); - } - } - } - materials_column.sort(); - } - - void addMaterialEntry(df::dfhack_material_category &selected_category, - MaterialInfo &material, std::string name) - { - if (!selected_category.whole || material.matches(selected_category)) - { - ListEntry entry(pad_string(name, MAX_MATERIAL, false), material); - if (filter.matches(material)) - entry.selected = true; - - materials_column.add(entry); - } - } - - void validateColumn() - { - set_to_limit(selected_column, 1); - } - - void resize(int32_t x, int32_t y) - { - dfhack_viewscreen::resize(x, y); - masks_column.resize(); - materials_column.resize(); - } -}; - -const DFHack::MaterialInfo &material_info_identity_fn(const DFHack::MaterialInfo &m) { return m; } - -ViewscreenChooseMaterial::ViewscreenChooseMaterial(ItemFilter &filter) - : filter(filter) -{ - selected_column = 0; - masks_column.setTitle("Type"); - masks_column.multiselect = true; - masks_column.allow_search = false; - masks_column.left_margin = 2; - materials_column.left_margin = MAX_MASK + 3; - materials_column.setTitle("Material"); - materials_column.multiselect = true; - - masks_column.changeHighlight(0); - - populateMasks(); - populateMaterials(); - - masks_column.selectDefaultEntry(); - materials_column.selectDefaultEntry(); - materials_column.changeHighlight(0); -} - -void ViewscreenChooseMaterial::feed(set *input) -{ - bool key_processed = false; - switch (selected_column) - { - case 0: - key_processed = masks_column.feed(input); - if (input->count(interface_key::SELECT)) - populateMaterials(); // Redo materials lists based on category selection - break; - case 1: - key_processed = materials_column.feed(input); - break; - } - - if (key_processed) - return; - - if (input->count(interface_key::LEAVESCREEN)) - { - input->clear(); - Screen::dismiss(this); - return; - } - if (input->count(interface_key::CUSTOM_SHIFT_C)) - { - filter.clear(); - masks_column.clearSelection(); - materials_column.clearSelection(); - populateMaterials(); - } - else if (input->count(interface_key::SEC_SELECT)) - { - // Convert list selections to material filters - filter.clearMaterialMask(); - - // Category masks - auto masks = masks_column.getSelectedElems(); - for (auto it = masks.begin(); it != masks.end(); ++it) - filter.addMaterialMask(it->whole); - - // Specific materials - auto materials = materials_column.getSelectedElems(); - std::vector materialInfos; - transform_(materials, materialInfos, material_info_identity_fn); - filter.setMaterials(materialInfos); - - Screen::dismiss(this); - } - else if (input->count(interface_key::STANDARDSCROLL_LEFT)) - { - --selected_column; - validateColumn(); - } - else if (input->count(interface_key::STANDARDSCROLL_RIGHT)) - { - selected_column++; - validateColumn(); - } - else if (enabler->tracking_on && enabler->mouse_lbut) - { - if (masks_column.setHighlightByMouse()) - selected_column = 0; - else if (materials_column.setHighlightByMouse()) - selected_column = 1; - - enabler->mouse_lbut = enabler->mouse_rbut = 0; - } -} - -void ViewscreenChooseMaterial::render() -{ - if (Screen::isDismissed(this)) - return; - - dfhack_viewscreen::render(); - - Screen::clear(); - Screen::drawBorder(" Building Material "); - - masks_column.display(selected_column == 0); - materials_column.display(selected_column == 1); - - int32_t y = gps->dimy - 3; - int32_t x = 2; - OutputHotkeyString(x, y, "Toggle", interface_key::SELECT); - x += 3; - OutputHotkeyString(x, y, "Save", interface_key::SEC_SELECT); - x += 3; - OutputHotkeyString(x, y, "Clear", interface_key::CUSTOM_SHIFT_C); - x += 3; - OutputHotkeyString(x, y, "Cancel", interface_key::LEAVESCREEN); -} - -//START Viewscreen Hook -static bool is_planmode_enabled(BuildingTypeKey key) -{ - return planmode_enabled[key] || quickfort_mode || all_enabled; -} - -static std::string get_item_label(const BuildingTypeKey &key, int item_idx) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 5) || - !Lua::PushModulePublic( - out, L, "plugins.buildingplan", "get_item_label")) - return "Failed push"; - - Lua::Push(L, std::get<0>(key)); - Lua::Push(L, std::get<1>(key)); - Lua::Push(L, std::get<2>(key)); - Lua::Push(L, item_idx); - - if (!Lua::SafeCall(out, L, 4, 1)) - return "Failed call"; - - const char *s = lua_tostring(L, -1); - if (!s) - return "No string"; - - return s; -} - -static bool item_can_be_improved(const BuildingTypeKey &key, int item_idx) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 5) || - !Lua::PushModulePublic( - out, L, "plugins.buildingplan", "item_can_be_improved")) - return false; - - Lua::Push(L, std::get<0>(key)); - Lua::Push(L, std::get<1>(key)); - Lua::Push(L, std::get<2>(key)); - Lua::Push(L, item_idx); - - if (!Lua::SafeCall(out, L, 4, 1)) - return false; - - return lua_toboolean(L, -1); -} - -static bool construct_planned_building() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top(L); - - if (!(lua_checkstack(L, 1) && - Lua::PushModulePublic(out, L, "plugins.buildingplan", - "construct_buildings_from_ui_state") && - Lua::SafeCall(out, L, 0, 1))) - { - return false; - } - - // register all returned buildings with planner - lua_pushnil(L); - while (lua_next(L, -2) != 0) - { - auto bld = Lua::GetDFObject(L, -1); - if (!bld) - { - out.printerr( - "buildingplan: construct_buildings_from_ui_state() failed\n"); - return false; - } - - planner.addPlannedBuilding(bld); - lua_pop(L, 1); - } - - return true; -} - -static void show_global_settings_dialog() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 2) || - !Lua::PushModulePublic( - out, L, "plugins.buildingplan", "show_global_settings_dialog")) - { - debug("Failed to push the module"); - return; - } - - lua_newtable(L); - int ctable = lua_gettop(L); - Lua::SetField(L, quickfort_mode, ctable, "quickfort_mode"); - Lua::SetField(L, all_enabled, ctable, "all_enabled"); - - for (auto & setting : planner.getGlobalSettings()) - { - Lua::SetField(L, setting.second, ctable, setting.first.c_str()); - } - - if (!Lua::SafeCall(out, L, 1, 0)) - { - debug("Failed call to show_global_settings_dialog"); - return; - } -} - -static bool is_automaterial_enabled() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!(lua_checkstack(L, 1) && - Lua::PushModulePublic(out, L, "plugins.automaterial", "isEnabled") && - Lua::SafeCall(out, L, 0, 1))) - { - return false; - } - - return lua_toboolean(L, -1); -} - -static bool is_automaterial_managed(df::building_type type, int16_t subtype) -{ - return is_automaterial_enabled() - && type == df::building_type::Construction - && subtype < df::construction_type::TrackN; -} - -struct buildingplan_query_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - - // no non-static fields allowed (according to VTableInterpose.h) - static df::building *bld; - static PlannedBuilding *pb; - static int filter_count; - static int filter_idx; - - // logic is reversed since we're starting at the last filter - bool hasNextFilter() const { return filter_idx > 0; } - bool hasPrevFilter() const { return filter_idx + 1 < filter_count; } - - bool isInPlannedBuildingQueryMode() - { - return (plotinfo->main.mode == df::ui_sidebar_mode::QueryBuilding || - plotinfo->main.mode == df::ui_sidebar_mode::BuildingItems) && - planner.getPlannedBuilding(world->selected_building); - } - - // reinit static fields when selected building changes - void initStatics() - { - df::building *cur_bld = world->selected_building; - if (bld != cur_bld) - { - bld = cur_bld; - pb = planner.getPlannedBuilding(bld); - filter_count = pb->getFilters().size(); - filter_idx = filter_count - 1; - } - } - - static void invalidateStatics() - { - bld = NULL; - } - - bool handleInput(set *input) - { - if (!isInPlannedBuildingQueryMode() || Gui::inRenameBuilding()) - return false; - - initStatics(); - - if (input->count(interface_key::SUSPENDBUILDING)) - return true; // Don't unsuspend planned buildings - if (input->count(interface_key::DESTROYBUILDING)) - { - // remove persistent data - pb->remove(); - // still allow the building to be removed - return false; - } - - // ctrl+Right - if (input->count(interface_key::A_MOVE_E_DOWN) && hasNextFilter()) - --filter_idx; - // ctrl+Left - else if (input->count(interface_key::A_MOVE_W_DOWN) && hasPrevFilter()) - ++filter_idx; - else - return false; - return true; - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!handleInput(input)) - INTERPOSE_NEXT(feed)(input); - } - - static bool is_filter_satisfied(df::building *bld, int filter_idx) - { - if (!bld - || bld->jobs.size() < 1 - || int(bld->jobs[0]->job_items.size()) <= filter_idx) - return false; - - // if all items for this filter are attached, the quantity will be 0 - return bld->jobs[0]->job_items[filter_idx]->quantity == 0; - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - if (!isInPlannedBuildingQueryMode()) - return; - - initStatics(); - - // Hide suspend toggle option - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = 20; - Screen::Pen pen(' ', COLOR_BLACK); - Screen::fillRect(pen, x, y, dims.menu_x2, y); - - bool attached = is_filter_satisfied(pb->getBuilding(), filter_idx); - - auto & filter = pb->getFilters()[filter_idx]; - y = 24; - std::string item_label = - stl_sprintf("Item %d of %d (%s)", filter_count - filter_idx, filter_count, attached ? "attached" : "pending"); - OutputString(COLOR_WHITE, x, y, "Planned Building Filter", true, left_margin + 1); - OutputString(COLOR_WHITE, x, y, item_label.c_str(), true, left_margin + 1); - OutputString(COLOR_WHITE, x, y, get_item_label(toBuildingTypeKey(bld), filter_idx).c_str(), true, left_margin); - ++y; - if (item_can_be_improved(toBuildingTypeKey(bld), filter_idx)) - { - OutputString(COLOR_BROWN, x, y, "Min Quality: ", false, left_margin); - OutputString(COLOR_BLUE, x, y, filter.getMinQuality(), true, left_margin); - OutputString(COLOR_BROWN, x, y, "Max Quality: ", false, left_margin); - OutputString(COLOR_BLUE, x, y, filter.getMaxQuality(), true, left_margin); - if (filter.getDecoratedOnly()) - OutputString(COLOR_BLUE, x, y, "Decorated Only", true, left_margin); - } - - OutputString(COLOR_BROWN, x, y, "Materials:", true, left_margin); - auto filters = filter.getMaterials(); - for (auto it = filters.begin(); it != filters.end(); ++it) - OutputString(COLOR_BLUE, x, y, "*" + *it, true, left_margin); - - ++y; - if (hasPrevFilter()) - OutputHotkeyString(x, y, "Prev Item", "Ctrl+Left", true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); - if (hasNextFilter()) - OutputHotkeyString(x, y, "Next Item", "Ctrl+Right", true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); - } -}; - -df::building * buildingplan_query_hook::bld; -PlannedBuilding * buildingplan_query_hook::pb; -int buildingplan_query_hook::filter_count; -int buildingplan_query_hook::filter_idx; - -struct buildingplan_place_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - - // no non-static fields allowed (according to VTableInterpose.h) - static BuildingTypeKey key; - static std::vector::reverse_iterator filter_rbegin; - static std::vector::reverse_iterator filter_rend; - static std::vector::reverse_iterator filter; - static int filter_count; - static int filter_idx; - - bool hasNextFilter() const { return filter + 1 != filter_rend; } - bool hasPrevFilter() const { return filter != filter_rbegin; } - - bool isInPlannedBuildingPlacementMode() - { - return plotinfo->main.mode == ui_sidebar_mode::Build && - df::global::ui_build_selector && - df::global::ui_build_selector->stage < 2 && - planner.isPlannableBuilding(toBuildingTypeKey(ui_build_selector)); - } - - // reinit static fields when selected building type changes - void initStatics() - { - BuildingTypeKey cur_key = toBuildingTypeKey(ui_build_selector); - if (key != cur_key) - { - key = cur_key; - auto wrapper = planner.getItemFilters(key); - filter_rbegin = wrapper.rbegin(); - filter_rend = wrapper.rend(); - filter = filter_rbegin; - filter_count = wrapper.get().size(); - filter_idx = filter_count - 1; - } - } - - static void invalidateStatics() - { - key = BuildingTypeKey(); - } - - bool handleInput(set *input) - { - if (!isInPlannedBuildingPlacementMode()) - { - show_help = false; - return false; - } - - initStatics(); - - if (in_dummy_screen) - { - if (input->count(interface_key::SELECT) || input->count(interface_key::SEC_SELECT) - || input->count(interface_key::LEAVESCREEN)) - { - in_dummy_screen = false; - // pass LEAVESCREEN up to parent view - input->clear(); - input->insert(interface_key::LEAVESCREEN); - return false; - } - return true; - } - - if (input->count(interface_key::CUSTOM_P) || - input->count(interface_key::CUSTOM_G) || - input->count(interface_key::CUSTOM_D) || - input->count(interface_key::CUSTOM_Q) || - input->count(interface_key::CUSTOM_W) || - input->count(interface_key::CUSTOM_A) || - input->count(interface_key::CUSTOM_S) || - input->count(interface_key::CUSTOM_M)) - { - show_help = true; - } - - if (!quickfort_mode && !all_enabled - && input->count(interface_key::CUSTOM_SHIFT_P)) - { - planmode_enabled[key] = !planmode_enabled[key]; - if (!is_planmode_enabled(key)) - Gui::refreshSidebar(); - return true; - } - if (input->count(interface_key::CUSTOM_SHIFT_G)) - { - show_global_settings_dialog(); - return true; - } - - if (!is_planmode_enabled(key)) - return false; - - // if automaterial is enabled, let it handle building allocation and - // registration with planner - if (input->count(interface_key::SELECT) && - !is_automaterial_managed(ui_build_selector->building_type, - ui_build_selector->building_subtype)) - { - if (ui_build_selector->errors.size() == 0 && construct_planned_building()) - { - Gui::refreshSidebar(); - if (quickfort_mode) - in_dummy_screen = true; - } - return true; - } - - - - if (input->count(interface_key::CUSTOM_SHIFT_M)) - Screen::show(dts::make_unique(*filter), plugin_self); - - if (item_can_be_improved(key, filter_idx)) - { - if (input->count(interface_key::CUSTOM_SHIFT_Q)) - filter->decMinQuality(); - else if (input->count(interface_key::CUSTOM_SHIFT_W)) - filter->incMinQuality(); - else if (input->count(interface_key::CUSTOM_SHIFT_A)) - filter->decMaxQuality(); - else if (input->count(interface_key::CUSTOM_SHIFT_S)) - filter->incMaxQuality(); - else if (input->count(interface_key::CUSTOM_SHIFT_D)) - filter->toggleDecoratedOnly(); - } - - // ctrl+Right - if (input->count(interface_key::A_MOVE_E_DOWN) && hasNextFilter()) - { - ++filter; - --filter_idx; - } - // ctrl+Left - else if (input->count(interface_key::A_MOVE_W_DOWN) && hasPrevFilter()) - { - --filter; - ++filter_idx; - } - else - return false; - return true; - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!handleInput(input)) - INTERPOSE_NEXT(feed)(input); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - initStatics(); - - bool plannable = isInPlannedBuildingPlacementMode(); - if (plannable && is_planmode_enabled(key)) - { - if (ui_build_selector->stage < 1) - // No materials but turn on cursor - ui_build_selector->stage = 1; - - for (auto iter = ui_build_selector->errors.begin(); - iter != ui_build_selector->errors.end();) - { - // FIXME Hide bags - if (((*iter)->find("Needs") != string::npos - && **iter != "Needs adjacent wall") - || (*iter)->find("No access") != string::npos) - iter = ui_build_selector->errors.erase(iter); - else - ++iter; - } - } - - INTERPOSE_NEXT(render)(); - - if (!plannable) - return; - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - - if (in_dummy_screen) - { - Screen::Pen pen(' ',COLOR_BLACK); - int y = dims.y1 + 1; - Screen::fillRect(pen, x, y, dims.menu_x2, y + 20); - - ++y; - - OutputString(COLOR_BROWN, x, y, - "Placeholder for legacy Quickfort. This screen is not required for DFHack native quickfort.", - true, left_margin); - OutputString(COLOR_WHITE, x, y, "Enter, Shift-Enter or Esc", true, left_margin); - return; - } - - int y = 23; - - if (is_automaterial_managed(ui_build_selector->building_type, - ui_build_selector->building_subtype)) - { - // avoid conflict with the automaterial plugin UI - y = 36; - } - - if (show_help) - { - OutputString(COLOR_BROWN, x, y, "Note: "); - OutputString(COLOR_WHITE, x, y, "Use Shift-Keys here", true, left_margin); - } - - OutputHotkeyString(x, y, "Planning Mode", interface_key::CUSTOM_SHIFT_P); - OutputString(COLOR_WHITE, x, y, ": "); - if (quickfort_mode) - OutputString(COLOR_YELLOW, x, y, "Quickfort", true, left_margin); - else if (all_enabled) - OutputString(COLOR_YELLOW, x, y, "All", true, left_margin); - else if (planmode_enabled[key]) - OutputString(COLOR_GREEN, x, y, "On", true, left_margin); - else - OutputString(COLOR_GREY, x, y, "Off", true, left_margin); - OutputHotkeyString(x, y, "Global Settings", interface_key::CUSTOM_SHIFT_G, - true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); - - if (!is_planmode_enabled(key)) - return; - - y += 2; - std::string title = - stl_sprintf("Filter for Item %d of %d:", - filter_count - filter_idx, filter_count); - OutputString(COLOR_WHITE, x, y, title.c_str(), true, left_margin + 1); - OutputString(COLOR_WHITE, x, y, get_item_label(key, filter_idx).c_str(), true, left_margin); - - if (item_can_be_improved(key, filter_idx)) - { - OutputHotkeyString(x, y, "Min Quality: ", "QW", false, 0, COLOR_WHITE, COLOR_LIGHTRED); - OutputString(COLOR_BROWN, x, y, filter->getMinQuality(), true, left_margin); - - OutputHotkeyString(x, y, "Max Quality: ", "AS", false, 0, COLOR_WHITE, COLOR_LIGHTRED); - OutputString(COLOR_BROWN, x, y, filter->getMaxQuality(), true, left_margin); - - OutputToggleString(x, y, "Decorated Only", interface_key::CUSTOM_SHIFT_D, - filter->getDecoratedOnly(), true, left_margin, COLOR_WHITE, COLOR_LIGHTRED); - } - - OutputHotkeyString(x, y, "Material Filter:", interface_key::CUSTOM_SHIFT_M, true, - left_margin, COLOR_WHITE, COLOR_LIGHTRED); - auto filter_descriptions = filter->getMaterials(); - for (auto it = filter_descriptions.begin(); - it != filter_descriptions.end(); ++it) - OutputString(COLOR_BROWN, x, y, " *" + *it, true, left_margin); - - y += 2; - if (hasPrevFilter()) - OutputHotkeyString(x, y, "Prev Item", "Ctrl+Left", true, - left_margin, COLOR_WHITE, COLOR_LIGHTRED); - if (hasNextFilter()) - OutputHotkeyString(x, y, "Next Item", "Ctrl+Right", true, - left_margin, COLOR_WHITE, COLOR_LIGHTRED); - } -}; - -BuildingTypeKey buildingplan_place_hook::key; -std::vector::reverse_iterator buildingplan_place_hook::filter_rbegin; -std::vector::reverse_iterator buildingplan_place_hook::filter_rend; -std::vector::reverse_iterator buildingplan_place_hook::filter; -int buildingplan_place_hook::filter_count; -int buildingplan_place_hook::filter_idx; - -struct buildingplan_room_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - - std::vector getNoblePositionOfSelectedBuildingOwner() - { - std::vector np; - if (plotinfo->main.mode != df::ui_sidebar_mode::QueryBuilding || - !world->selected_building || - !world->selected_building->owner) - { - return np; - } - - switch (world->selected_building->getType()) - { - case building_type::Bed: - case building_type::Chair: - case building_type::Table: - break; - default: - return np; - } - - return getUniqueNoblePositions(world->selected_building->owner); - } - - bool isInNobleRoomQueryMode() - { - if (getNoblePositionOfSelectedBuildingOwner().size() > 0) - return canReserveRoom(world->selected_building); - else - return false; - } - - bool handleInput(set *input) - { - if (!isInNobleRoomQueryMode()) - return false; - - if (Gui::inRenameBuilding()) - return false; - auto np = getNoblePositionOfSelectedBuildingOwner(); - df::interface_key last_token = get_string_key(input); - if (last_token >= Screen::charToKey('1') - && last_token <= Screen::charToKey('9')) - { - size_t index = last_token - Screen::charToKey('1'); - if (index >= np.size()) - return false; - roomMonitor.toggleRoomForPosition(world->selected_building->id, np.at(index).position->code); - return true; - } - - return false; - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (!handleInput(input)) - INTERPOSE_NEXT(feed)(input); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - if (!isInNobleRoomQueryMode()) - return; - - auto np = getNoblePositionOfSelectedBuildingOwner(); - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = 24; - OutputString(COLOR_BROWN, x, y, "DFHack", true, left_margin); - OutputString(COLOR_WHITE, x, y, "Auto-allocate to:", true, left_margin); - for (size_t i = 0; i < np.size() && i < 9; i++) - { - bool enabled = - roomMonitor.getReservedNobleCode(world->selected_building->id) - == np[i].position->code; - OutputToggleString(x, y, np[i].position->name[0].c_str(), - int_to_string(i+1).c_str(), enabled, true, left_margin); - } - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_query_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_place_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_room_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_query_hook, render); -IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_place_hook, render); -IMPLEMENT_VMETHOD_INTERPOSE(buildingplan_room_hook, render); - -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - -static bool setSetting(std::string name, bool value); - -static bool isTrue(std::string val) -{ - val = toLower(val); - return val == "on" || val == "true" || val == "y" || val == "yes" - || val == "1"; -} - -static command_result buildingplan_cmd(color_ostream &out, vector & parameters) -{ - if (parameters.empty()) - return CR_OK; - - std::string cmd = toLower(parameters[0]); - - if (cmd.size() >= 1 && cmd[0] == 'v') - { - out.print("buildingplan version: %s\n", PLUGIN_VERSION); - } - else if (parameters.size() >= 2 && cmd == "debug") - { - show_debugging = isTrue(parameters[1]); - out.print("buildingplan debugging: %s\n", - show_debugging ? "enabled" : "disabled"); - } - else if (cmd == "set") - { - if (!is_enabled) - { - out.printerr( - "ERROR: buildingplan must be enabled before you can" - " read or set buildingplan global settings."); - return CR_FAILURE; - } - - if (!DFHack::Core::getInstance().isMapLoaded()) - { - out.printerr( - "ERROR: A map must be loaded before you can read or set" - "buildingplan global settings. Try adding your" - "'buildingplan set' commands to the onMapLoad.init file.\n"); - return CR_FAILURE; - } - - if (parameters.size() == 1) - { - // display current settings - out.print("active settings:\n"); - - out.print(" all_enabled = %s\n", all_enabled ? "true" : "false"); - for (auto & setting : planner.getGlobalSettings()) - { - out.print(" %s = %s\n", setting.first.c_str(), - setting.second ? "true" : "false"); - } - - out.print(" quickfort_mode = %s\n", - quickfort_mode ? "true" : "false"); - } - else if (parameters.size() == 3) - { - // set a setting - std::string setting = toLower(parameters[1]); - bool val = isTrue(parameters[2]); - if (!setSetting(setting, val)) - { - out.printerr("ERROR: invalid parameter: '%s'\n", - parameters[1].c_str()); - } - } - else - { - out.printerr("ERROR: invalid syntax\n"); - } - } - - return CR_OK; -} - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) -{ - if (!gps) - return CR_FAILURE; - - if (enable != is_enabled) - { - if (DFHack::Core::getInstance().isMapLoaded()) - planner.reset(); - - if (!INTERPOSE_HOOK(buildingplan_query_hook, feed).apply(enable) || - !INTERPOSE_HOOK(buildingplan_place_hook, feed).apply(enable) || - !INTERPOSE_HOOK(buildingplan_room_hook, feed).apply(enable) || - !INTERPOSE_HOOK(buildingplan_query_hook, render).apply(enable) || - !INTERPOSE_HOOK(buildingplan_place_hook, render).apply(enable) || - !INTERPOSE_HOOK(buildingplan_room_hook, render).apply(enable)) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - commands.push_back( - PluginCommand("buildingplan", - "Plan building construction before you have materials.", - buildingplan_cmd)); - - return CR_OK; -} - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_MAP_LOADED: - buildingplan_place_hook::invalidateStatics(); - buildingplan_query_hook::invalidateStatics(); - planner.reset(); - roomMonitor.reset(out); - break; - default: - break; - } - - return CR_OK; -} - -static bool is_paused() -{ - return World::ReadPauseState() || - plotinfo->main.mode > df::ui_sidebar_mode::Squads || - !strict_virtual_cast(Gui::getCurViewscreen(true)); -} - -static bool cycle_requested = false; - -#define DAY_TICKS 1200 -DFhackCExport command_result plugin_onupdate(color_ostream &) -{ - if (Maps::IsValid() && !is_paused() - && (cycle_requested || world->frame_counter % (DAY_TICKS/2) == 0)) - { - planner.doCycle(); - roomMonitor.doCycle(); - cycle_requested = false; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown(color_ostream &) -{ - return CR_OK; -} - -// Lua API section - -static bool isPlanModeEnabled(df::building_type type, - int16_t subtype, - int32_t custom) { - return is_planmode_enabled(toBuildingTypeKey(type, subtype, custom)); -} - -static bool isPlannableBuilding(df::building_type type, - int16_t subtype, - int32_t custom) { - return planner.isPlannableBuilding( - toBuildingTypeKey(type, subtype, custom)); -} - -static bool isPlannedBuilding(df::building *bld) { - return !!planner.getPlannedBuilding(bld); -} - -static void addPlannedBuilding(df::building *bld) { - planner.addPlannedBuilding(bld); -} - -static void doCycle() { - planner.doCycle(); -} - -static void scheduleCycle() { - cycle_requested = true; -} - -static bool setSetting(std::string name, bool value) { - if (name == "quickfort_mode") - { - debug("setting quickfort_mode %d -> %d", quickfort_mode, value); - quickfort_mode = value; - return true; - } - if (name == "all_enabled") - { - debug("setting all_enabled %d -> %d", all_enabled, value); - all_enabled = value; - return true; - } - return planner.setGlobalSetting(name, value); -} - -DFHACK_PLUGIN_LUA_FUNCTIONS { - DFHACK_LUA_FUNCTION(isPlanModeEnabled), - DFHACK_LUA_FUNCTION(isPlannableBuilding), - DFHACK_LUA_FUNCTION(isPlannedBuilding), - DFHACK_LUA_FUNCTION(addPlannedBuilding), - DFHACK_LUA_FUNCTION(doCycle), - DFHACK_LUA_FUNCTION(scheduleCycle), - DFHACK_LUA_FUNCTION(setSetting), - DFHACK_LUA_END -}; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h deleted file mode 100644 index e906ef1a7..000000000 --- a/plugins/buildingplan/buildingplan.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "buildingplan-planner.h" -#include "buildingplan-rooms.h" - -void debug(const char *fmt, ...) Wformat(printf,1,2); - -extern bool show_debugging; From 0faa160eaac6ff244ed5e5058f9657e01e028784 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Feb 2023 18:45:26 -0800 Subject: [PATCH 0672/2222] split buildingplan into a project --- plugins/CMakeLists.txt | 2 +- plugins/buildingplan/CMakeLists.txt | 10 +- plugins/{ => buildingplan}/buildingplan.cpp | 343 ++------------------ plugins/buildingplan/buildingplan.h | 28 ++ plugins/buildingplan/buildingplan_cycle.cpp | 275 ++++++++++++++++ plugins/buildingplan/itemfilter.cpp | 0 plugins/buildingplan/itemfilter.h | 1 + plugins/buildingplan/plannedbuilding.cpp | 36 ++ plugins/buildingplan/plannedbuilding.h | 25 ++ 9 files changed, 395 insertions(+), 325 deletions(-) rename plugins/{ => buildingplan}/buildingplan.cpp (55%) create mode 100644 plugins/buildingplan/buildingplan.h create mode 100644 plugins/buildingplan/buildingplan_cycle.cpp create mode 100644 plugins/buildingplan/itemfilter.cpp create mode 100644 plugins/buildingplan/itemfilter.h create mode 100644 plugins/buildingplan/plannedbuilding.cpp create mode 100644 plugins/buildingplan/plannedbuilding.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7940c3994..82722f16a 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -89,7 +89,7 @@ dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) -dfhack_plugin(buildingplan buildingplan.cpp LINK_LIBRARIES lua) +add_subdirectory(buildingplan) #dfhack_plugin(changeitem changeitem.cpp) dfhack_plugin(changelayer changelayer.cpp) dfhack_plugin(changevein changevein.cpp) diff --git a/plugins/buildingplan/CMakeLists.txt b/plugins/buildingplan/CMakeLists.txt index 1d34b169a..85475edaa 100644 --- a/plugins/buildingplan/CMakeLists.txt +++ b/plugins/buildingplan/CMakeLists.txt @@ -2,10 +2,12 @@ project(buildingplan) set(COMMON_HDRS buildingplan.h - buildingplan-planner.h - buildingplan-rooms.h + itemfilter.h + plannedbuilding.h ) set_source_files_properties(${COMMON_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) -dfhack_plugin(buildingplan buildingplan.cpp buildingplan-planner.cpp - buildingplan-rooms.cpp ${COMMON_HDRS} LINK_LIBRARIES lua) +dfhack_plugin(buildingplan + buildingplan.cpp buildingplan_cycle.cpp itemfilter.cpp plannedbuilding.cpp + ${COMMON_HDRS} + LINK_LIBRARIES lua) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp similarity index 55% rename from plugins/buildingplan.cpp rename to plugins/buildingplan/buildingplan.cpp index a76e81d4a..3d26a7c24 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -1,28 +1,17 @@ -#include "Core.h" +#include "plannedbuilding.h" +#include "buildingplan.h" + #include "Debug.h" #include "LuaTools.h" #include "PluginManager.h" -#include "modules/Items.h" -#include "modules/Job.h" -#include "modules/Materials.h" -#include "modules/Persistence.h" #include "modules/World.h" -#include "df/building.h" -#include "df/building_design.h" #include "df/item.h" #include "df/job_item.h" #include "df/world.h" -#include -#include -#include -#include - using std::map; -using std::pair; -using std::deque; using std::string; using std::unordered_map; using std::vector; @@ -40,72 +29,29 @@ namespace DFHack { } static const string CONFIG_KEY = string(plugin_name) + "/config"; -static const string BLD_CONFIG_KEY = string(plugin_name) + "/building"; - -enum ConfigValues { - CONFIG_BLOCKS = 1, - CONFIG_BOULDERS = 2, - CONFIG_LOGS = 3, - CONFIG_BARS = 4, -}; - -enum BuildingConfigValues { - BLD_CONFIG_ID = 0, -}; +const string BLD_CONFIG_KEY = string(plugin_name) + "/building"; -static int get_config_val(PersistentDataItem &c, int index) { +int get_config_val(PersistentDataItem &c, int index) { if (!c.isValid()) return -1; return c.ival(index); } -static bool get_config_bool(PersistentDataItem &c, int index) { +bool get_config_bool(PersistentDataItem &c, int index) { return get_config_val(c, index) == 1; } -static void set_config_val(PersistentDataItem &c, int index, int value) { +void set_config_val(PersistentDataItem &c, int index, int value) { if (c.isValid()) c.ival(index) = value; } -static void set_config_bool(PersistentDataItem &c, int index, bool value) { +void set_config_bool(PersistentDataItem &c, int index, bool value) { set_config_val(c, index, value ? 1 : 0); } -class PlannedBuilding { -public: - const df::building::key_field_type id; - - PlannedBuilding(color_ostream &out, df::building *building) : id(building->id) { - DEBUG(status,out).print("creating persistent data for building %d\n", id); - bld_config = DFHack::World::AddPersistentData(BLD_CONFIG_KEY); - set_config_val(bld_config, BLD_CONFIG_ID, id); - } - - PlannedBuilding(DFHack::PersistentDataItem &bld_config) - : id(get_config_val(bld_config, BLD_CONFIG_ID)), bld_config(bld_config) { } - - void remove(color_ostream &out); - - // Ensure the building still exists and is in a valid state. It can disappear - // for lots of reasons, such as running the game with the buildingplan plugin - // disabled, manually removing the building, modifying it via the API, etc. - df::building * getBuildingIfValidOrRemoveIfNot(color_ostream &out) { - auto bld = df::building::find(id); - bool valid = bld && bld->getBuildStage() == 0; - if (!valid) { - remove(out); - return NULL; - } - return bld; - } - -private: - DFHack::PersistentDataItem bld_config; -}; - static PersistentDataItem config; // building id -> PlannedBuilding unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) -map>>> tasks; +Tasks tasks; // note that this just removes the PlannedBuilding. the tasks will get dropped // as we discover them in the tasks queues and they fail to be found in planned_buildings. @@ -115,7 +61,7 @@ map>>> tasks; // no chance of duplicate tasks getting added to the tasks queues. void PlannedBuilding::remove(color_ostream &out) { DEBUG(status,out).print("removing persistent data for building %d\n", id); - DFHack::World::DeletePersistentData(config); + World::DeletePersistentData(config); if (planned_buildings.count(id) > 0) planned_buildings.erase(id); } @@ -124,7 +70,9 @@ static const int32_t CYCLE_TICKS = 600; // twice per game day static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle static command_result do_command(color_ostream &out, vector ¶meters); -static void do_cycle(color_ostream &out); +void buildingplan_cycle(color_ostream &out, Tasks &tasks, + unordered_map &planned_buildings); + static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { @@ -186,7 +134,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { } DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { - if (event == DFHack::SC_WORLD_UNLOADED) { + if (event == SC_WORLD_UNLOADED) { DEBUG(status,out).print("world unloaded; clearing state for %s\n", plugin_name); planned_buildings.clear(); tasks.clear(); @@ -196,6 +144,14 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan static bool cycle_requested = false; +static void do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; + cycle_requested = false; + + buildingplan_cycle(out, tasks, planned_buildings); +} + DFhackCExport command_result plugin_onupdate(color_ostream &out) { if (!Core::getInstance().isWorldLoaded()) return CR_OK; @@ -249,259 +205,6 @@ static command_result do_command(color_ostream &out, vector ¶meters) return show_help ? CR_WRONG_USAGE : CR_OK; } -///////////////////////////////////////////////////// -// cycle logic -// - -struct BadFlags { - uint32_t whole; - - BadFlags() { - df::item_flags flags; - #define F(x) flags.bits.x = true; - F(dump); F(forbid); F(garbage_collect); - F(hostile); F(on_fire); F(rotten); F(trader); - F(in_building); F(construction); F(in_job); - F(owned); F(in_chest); F(removed); F(encased); - F(spider_web); - #undef F - whole = flags.whole; - } -}; - -static bool itemPassesScreen(df::item * item) { - static const BadFlags bad_flags; - return !(item->flags.whole & bad_flags.whole) - && !item->isAssignedToStockpile(); -} - -static bool matchesFilters(df::item * item, df::job_item * job_item) { - // check the properties that are not checked by Job::isSuitableItem() - if (job_item->item_type > -1 && job_item->item_type != item->getType()) - return false; - - if (job_item->item_subtype > -1 && - job_item->item_subtype != item->getSubtype()) - return false; - - if (job_item->flags2.bits.building_material && !item->isBuildMat()) - return false; - - if (job_item->metal_ore > -1 && !item->isMetalOre(job_item->metal_ore)) - return false; - - if (job_item->has_tool_use > df::tool_uses::NONE - && !item->hasToolUse(job_item->has_tool_use)) - return false; - - return DFHack::Job::isSuitableItem( - job_item, item->getType(), item->getSubtype()) - && DFHack::Job::isSuitableMaterial( - job_item, item->getMaterial(), item->getMaterialIndex(), - item->getType()); -} - -static bool isJobReady(color_ostream &out, df::job * job) { - int needed_items = 0; - for (auto job_item : job->job_items) { needed_items += job_item->quantity; } - if (needed_items) { - DEBUG(cycle,out).print("building needs %d more item(s)\n", needed_items); - return false; - } - return true; -} - -static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b) { - // we want the items in the opposite order of the filters - return a->job_item_idx > b->job_item_idx; -} - -// this function does not remove the job_items since their quantity fields are -// now all at 0, so there is no risk of having extra items attached. we don't -// remove them to keep the "finalize with buildingplan active" path as similar -// as possible to the "finalize with buildingplan disabled" path. -static void finalizeBuilding(color_ostream &out, df::building * bld) { - DEBUG(cycle,out).print("finalizing building %d\n", bld->id); - auto job = bld->jobs[0]; - - // sort the items so they get added to the structure in the correct order - std::sort(job->items.begin(), job->items.end(), job_item_idx_lt); - - // derive the material properties of the building and job from the first - // applicable item. if any boulders are involved, it makes the whole - // structure "rough". - bool rough = false; - for (auto attached_item : job->items) { - df::item *item = attached_item->item; - rough = rough || item->getType() == df::item_type::BOULDER; - if (bld->mat_type == -1) { - bld->mat_type = item->getMaterial(); - job->mat_type = bld->mat_type; - } - if (bld->mat_index == -1) { - bld->mat_index = item->getMaterialIndex(); - job->mat_index = bld->mat_index; - } - } - - if (bld->needsDesign()) { - auto act = (df::building_actual *)bld; - if (!act->design) - act->design = new df::building_design(); - act->design->flags.bits.rough = rough; - } - - // we're good to go! - job->flags.bits.suspend = false; - Job::checkBuildingsNow(); -} - -static df::building * popInvalidTasks(color_ostream &out, deque> & task_queue) { - while (!task_queue.empty()) { - auto & task = task_queue.front(); - auto id = task.first; - if (planned_buildings.count(id) > 0) { - auto bld = planned_buildings.at(id).getBuildingIfValidOrRemoveIfNot(out); - if (bld && bld->jobs[0]->job_items[task.second]->quantity) - return bld; - } - DEBUG(cycle,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n", id, task.second); - task_queue.pop_front(); - } - return NULL; -} - -static void doVector(color_ostream &out, df::job_item_vector_id vector_id, - map>> & buckets) { - auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); - auto item_vector = df::global::world->items.other[other_id]; - DEBUG(cycle,out).print("matching %zu item(s) in vector %s against %zu filter bucket(s)\n", - item_vector.size(), - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - buckets.size()); - for (auto item_it = item_vector.rbegin(); - item_it != item_vector.rend(); - ++item_it) { - auto item = *item_it; - if (!itemPassesScreen(item)) - continue; - for (auto bucket_it = buckets.begin(); bucket_it != buckets.end(); ) { - auto & task_queue = bucket_it->second; - auto bld = popInvalidTasks(out, task_queue); - if (!bld) { - DEBUG(cycle,out).print("removing empty bucket: %s/%s; %zu bucket(s) left\n", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket_it->first.c_str(), - buckets.size() - 1); - bucket_it = buckets.erase(bucket_it); - continue; - } - auto & task = task_queue.front(); - auto id = task.first; - auto job = bld->jobs[0]; - auto filter_idx = task.second; - if (matchesFilters(item, job->job_items[filter_idx]) - && DFHack::Job::attachJobItem(job, item, - df::job_item_ref::Hauled, filter_idx)) - { - MaterialInfo material; - material.decode(item); - ItemTypeInfo item_type; - item_type.decode(item); - DEBUG(cycle,out).print("attached %s %s to filter %d for %s(%d): %s/%s\n", - material.toString().c_str(), - item_type.toString().c_str(), - filter_idx, - ENUM_KEY_STR(building_type, bld->getType()).c_str(), - id, - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket_it->first.c_str()); - // keep quantity aligned with the actual number of remaining - // items so if buildingplan is turned off, the building will - // be completed with the correct number of items. - --job->job_items[filter_idx]->quantity; - task_queue.pop_front(); - if (isJobReady(out, job)) { - finalizeBuilding(out, bld); - planned_buildings.at(id).remove(out); - } - if (task_queue.empty()) { - DEBUG(cycle,out).print( - "removing empty item bucket: %s/%s; %zu left\n", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket_it->first.c_str(), - buckets.size() - 1); - buckets.erase(bucket_it); - } - // we found a home for this item; no need to look further - break; - } - ++bucket_it; - } - if (buckets.empty()) - break; - } -} - -struct VectorsToScanLast { - std::vector vectors; - VectorsToScanLast() { - // order is important here. we want to match boulders before wood and - // everything before bars. blocks are not listed here since we'll have - // already scanned them when we did the first pass through the buckets. - vectors.push_back(df::job_item_vector_id::BOULDER); - vectors.push_back(df::job_item_vector_id::WOOD); - vectors.push_back(df::job_item_vector_id::BAR); - } -}; - -static void do_cycle(color_ostream &out) { - static const VectorsToScanLast vectors_to_scan_last; - - // mark that we have recently run - cycle_timestamp = world->frame_counter; - cycle_requested = false; - - DEBUG(cycle,out).print("running %s cycle for %zu registered buildings\n", - plugin_name, planned_buildings.size()); - - for (auto it = tasks.begin(); it != tasks.end(); ) { - auto vector_id = it->first; - // we could make this a set, but it's only three elements - if (std::find(vectors_to_scan_last.vectors.begin(), - vectors_to_scan_last.vectors.end(), - vector_id) != vectors_to_scan_last.vectors.end()) { - ++it; - continue; - } - - auto & buckets = it->second; - doVector(out, vector_id, buckets); - if (buckets.empty()) { - DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - tasks.size() - 1); - it = tasks.erase(it); - } - else - ++it; - } - for (auto vector_id : vectors_to_scan_last.vectors) { - if (tasks.count(vector_id) == 0) - continue; - auto & buckets = tasks[vector_id]; - doVector(out, vector_id, buckets); - if (buckets.empty()) { - DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", - ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - tasks.size() - 1); - tasks.erase(vector_id); - } - } - DEBUG(cycle,out).print("cycle done; %zu registered building(s) left\n", - planned_buildings.size()); -} - ///////////////////////////////////////////////////// // Lua API // core will already be suspended when coming in through here @@ -617,7 +320,7 @@ static void printStatus(color_ostream &out) { int32_t total = 0; for (auto &buckets : tasks) { for (auto &bucket_queue : buckets.second) { - deque> &tqueue = bucket_queue.second; + Bucket &tqueue = bucket_queue.second; for (auto it = tqueue.begin(); it != tqueue.end();) { auto & task = *it; auto id = task.first; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h new file mode 100644 index 000000000..ac6d3a9a6 --- /dev/null +++ b/plugins/buildingplan/buildingplan.h @@ -0,0 +1,28 @@ +#pragma once + +#include "modules/Persistence.h" + +#include "df/job_item_vector_id.h" + +#include + +typedef std::deque> Bucket; +typedef std::map> Tasks; + +extern const std::string BLD_CONFIG_KEY; + +enum ConfigValues { + CONFIG_BLOCKS = 1, + CONFIG_BOULDERS = 2, + CONFIG_LOGS = 3, + CONFIG_BARS = 4, +}; + +enum BuildingConfigValues { + BLD_CONFIG_ID = 0, +}; + +int get_config_val(DFHack::PersistentDataItem &c, int index); +bool get_config_bool(DFHack::PersistentDataItem &c, int index); +void set_config_val(DFHack::PersistentDataItem &c, int index, int value); +void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp new file mode 100644 index 000000000..875cd432f --- /dev/null +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -0,0 +1,275 @@ +#include "plannedbuilding.h" +#include "buildingplan.h" + +#include "Debug.h" + +#include "modules/Items.h" +#include "modules/Job.h" +#include "modules/Materials.h" + +#include "df/building_design.h" +#include "df/item.h" +#include "df/job.h" +#include "df/job_item.h" +#include "df/world.h" + +#include + +using std::map; +using std::string; +using std::unordered_map; + +namespace DFHack { + DBG_EXTERN(buildingplan, cycle); +} + +using namespace DFHack; + +struct BadFlags { + uint32_t whole; + + BadFlags() { + df::item_flags flags; + #define F(x) flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(in_job); + F(owned); F(in_chest); F(removed); F(encased); + F(spider_web); + #undef F + whole = flags.whole; + } +}; + +static bool itemPassesScreen(df::item * item) { + static const BadFlags bad_flags; + return !(item->flags.whole & bad_flags.whole) + && !item->isAssignedToStockpile(); +} + +static bool matchesFilters(df::item * item, df::job_item * job_item) { + // check the properties that are not checked by Job::isSuitableItem() + if (job_item->item_type > -1 && job_item->item_type != item->getType()) + return false; + + if (job_item->item_subtype > -1 && + job_item->item_subtype != item->getSubtype()) + return false; + + if (job_item->flags2.bits.building_material && !item->isBuildMat()) + return false; + + if (job_item->metal_ore > -1 && !item->isMetalOre(job_item->metal_ore)) + return false; + + if (job_item->has_tool_use > df::tool_uses::NONE + && !item->hasToolUse(job_item->has_tool_use)) + return false; + + return Job::isSuitableItem( + job_item, item->getType(), item->getSubtype()) + && Job::isSuitableMaterial( + job_item, item->getMaterial(), item->getMaterialIndex(), + item->getType()); +} + +static bool isJobReady(color_ostream &out, df::job * job) { + int needed_items = 0; + for (auto job_item : job->job_items) { needed_items += job_item->quantity; } + if (needed_items) { + DEBUG(cycle,out).print("building needs %d more item(s)\n", needed_items); + return false; + } + return true; +} + +static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b) { + // we want the items in the opposite order of the filters + return a->job_item_idx > b->job_item_idx; +} + +// this function does not remove the job_items since their quantity fields are +// now all at 0, so there is no risk of having extra items attached. we don't +// remove them to keep the "finalize with buildingplan active" path as similar +// as possible to the "finalize with buildingplan disabled" path. +static void finalizeBuilding(color_ostream &out, df::building * bld) { + DEBUG(cycle,out).print("finalizing building %d\n", bld->id); + auto job = bld->jobs[0]; + + // sort the items so they get added to the structure in the correct order + std::sort(job->items.begin(), job->items.end(), job_item_idx_lt); + + // derive the material properties of the building and job from the first + // applicable item. if any boulders are involved, it makes the whole + // structure "rough". + bool rough = false; + for (auto attached_item : job->items) { + df::item *item = attached_item->item; + rough = rough || item->getType() == df::item_type::BOULDER; + if (bld->mat_type == -1) { + bld->mat_type = item->getMaterial(); + job->mat_type = bld->mat_type; + } + if (bld->mat_index == -1) { + bld->mat_index = item->getMaterialIndex(); + job->mat_index = bld->mat_index; + } + } + + if (bld->needsDesign()) { + auto act = (df::building_actual *)bld; + if (!act->design) + act->design = new df::building_design(); + act->design->flags.bits.rough = rough; + } + + // we're good to go! + job->flags.bits.suspend = false; + Job::checkBuildingsNow(); +} + +static df::building * popInvalidTasks(color_ostream &out, Bucket &task_queue, + unordered_map &planned_buildings) { + while (!task_queue.empty()) { + auto & task = task_queue.front(); + auto id = task.first; + if (planned_buildings.count(id) > 0) { + auto bld = planned_buildings.at(id).getBuildingIfValidOrRemoveIfNot(out); + if (bld && bld->jobs[0]->job_items[task.second]->quantity) + return bld; + } + DEBUG(cycle,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n", id, task.second); + task_queue.pop_front(); + } + return NULL; +} + +static void doVector(color_ostream &out, df::job_item_vector_id vector_id, + map &buckets, + unordered_map &planned_buildings) { + auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); + auto item_vector = df::global::world->items.other[other_id]; + DEBUG(cycle,out).print("matching %zu item(s) in vector %s against %zu filter bucket(s)\n", + item_vector.size(), + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + buckets.size()); + for (auto item_it = item_vector.rbegin(); + item_it != item_vector.rend(); + ++item_it) { + auto item = *item_it; + if (!itemPassesScreen(item)) + continue; + for (auto bucket_it = buckets.begin(); bucket_it != buckets.end(); ) { + auto & task_queue = bucket_it->second; + auto bld = popInvalidTasks(out, task_queue, planned_buildings); + if (!bld) { + DEBUG(cycle,out).print("removing empty bucket: %s/%s; %zu bucket(s) left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str(), + buckets.size() - 1); + bucket_it = buckets.erase(bucket_it); + continue; + } + auto & task = task_queue.front(); + auto id = task.first; + auto job = bld->jobs[0]; + auto filter_idx = task.second; + if (matchesFilters(item, job->job_items[filter_idx]) + && Job::attachJobItem(job, item, + df::job_item_ref::Hauled, filter_idx)) + { + MaterialInfo material; + material.decode(item); + ItemTypeInfo item_type; + item_type.decode(item); + DEBUG(cycle,out).print("attached %s %s to filter %d for %s(%d): %s/%s\n", + material.toString().c_str(), + item_type.toString().c_str(), + filter_idx, + ENUM_KEY_STR(building_type, bld->getType()).c_str(), + id, + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str()); + // keep quantity aligned with the actual number of remaining + // items so if buildingplan is turned off, the building will + // be completed with the correct number of items. + --job->job_items[filter_idx]->quantity; + task_queue.pop_front(); + if (isJobReady(out, job)) { + finalizeBuilding(out, bld); + planned_buildings.at(id).remove(out); + } + if (task_queue.empty()) { + DEBUG(cycle,out).print( + "removing empty item bucket: %s/%s; %zu left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + bucket_it->first.c_str(), + buckets.size() - 1); + buckets.erase(bucket_it); + } + // we found a home for this item; no need to look further + break; + } + ++bucket_it; + } + if (buckets.empty()) + break; + } +} + +struct VectorsToScanLast { + std::vector vectors; + VectorsToScanLast() { + // order is important here. we want to match boulders before wood and + // everything before bars. blocks are not listed here since we'll have + // already scanned them when we did the first pass through the buckets. + vectors.push_back(df::job_item_vector_id::BOULDER); + vectors.push_back(df::job_item_vector_id::WOOD); + vectors.push_back(df::job_item_vector_id::BAR); + } +}; + +void buildingplan_cycle(color_ostream &out, Tasks &tasks, + unordered_map &planned_buildings) { + static const VectorsToScanLast vectors_to_scan_last; + + DEBUG(cycle,out).print( + "running buildingplan cycle for %zu registered buildings\n", + planned_buildings.size()); + + for (auto it = tasks.begin(); it != tasks.end(); ) { + auto vector_id = it->first; + // we could make this a set, but it's only three elements + if (std::find(vectors_to_scan_last.vectors.begin(), + vectors_to_scan_last.vectors.end(), + vector_id) != vectors_to_scan_last.vectors.end()) { + ++it; + continue; + } + + auto & buckets = it->second; + doVector(out, vector_id, buckets, planned_buildings); + if (buckets.empty()) { + DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + tasks.size() - 1); + it = tasks.erase(it); + } + else + ++it; + } + for (auto vector_id : vectors_to_scan_last.vectors) { + if (tasks.count(vector_id) == 0) + continue; + auto & buckets = tasks[vector_id]; + doVector(out, vector_id, buckets, planned_buildings); + if (buckets.empty()) { + DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), + tasks.size() - 1); + tasks.erase(vector_id); + } + } + DEBUG(cycle,out).print("cycle done; %zu registered building(s) left\n", + planned_buildings.size()); +} diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/plugins/buildingplan/itemfilter.h @@ -0,0 +1 @@ +#pragma once diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp new file mode 100644 index 000000000..c03f56161 --- /dev/null +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -0,0 +1,36 @@ +#include "plannedbuilding.h" +#include "buildingplan.h" + +#include "Debug.h" + +#include "modules/World.h" + +namespace DFHack { + DBG_EXTERN(buildingplan, status); + DBG_EXTERN(buildingplan, cycle); +} + +using namespace DFHack; + +PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building) + : id(building->id) { + DEBUG(status,out).print("creating persistent data for building %d\n", id); + bld_config = World::AddPersistentData(BLD_CONFIG_KEY); + set_config_val(bld_config, BLD_CONFIG_ID, id); +} + +PlannedBuilding::PlannedBuilding(PersistentDataItem &bld_config) + : id(get_config_val(bld_config, BLD_CONFIG_ID)), bld_config(bld_config) { } + +// Ensure the building still exists and is in a valid state. It can disappear +// for lots of reasons, such as running the game with the buildingplan plugin +// disabled, manually removing the building, modifying it via the API, etc. +df::building * PlannedBuilding::getBuildingIfValidOrRemoveIfNot(color_ostream &out) { + auto bld = df::building::find(id); + bool valid = bld && bld->getBuildStage() == 0; + if (!valid) { + remove(out); + return NULL; + } + return bld; +} diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h new file mode 100644 index 000000000..9f0273b82 --- /dev/null +++ b/plugins/buildingplan/plannedbuilding.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Core.h" + +#include "modules/Persistence.h" + +#include "df/building.h" + +class PlannedBuilding { +public: + const df::building::key_field_type id; + + PlannedBuilding(DFHack::color_ostream &out, df::building *building); + PlannedBuilding(DFHack::PersistentDataItem &bld_config); + + void remove(DFHack::color_ostream &out); + + // Ensure the building still exists and is in a valid state. It can disappear + // for lots of reasons, such as running the game with the buildingplan plugin + // disabled, manually removing the building, modifying it via the API, etc. + df::building * getBuildingIfValidOrRemoveIfNot(DFHack::color_ostream &out); + +private: + DFHack::PersistentDataItem bld_config; +}; From e5c3a2b519bda50f76c1d99f27f8bfed55af79c0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Feb 2023 16:54:38 -0800 Subject: [PATCH 0673/2222] dynamically count available materials when placing --- plugins/buildingplan/buildingplan.cpp | 157 ++++++++++++++++---- plugins/buildingplan/buildingplan.h | 4 + plugins/buildingplan/buildingplan_cycle.cpp | 5 +- plugins/lua/buildingplan.lua | 112 ++++++++++---- 4 files changed, 214 insertions(+), 64 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 3d26a7c24..2c5d96b94 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -47,11 +47,37 @@ void set_config_bool(PersistentDataItem &c, int index, bool value) { set_config_val(c, index, value ? 1 : 0); } +// building type, subtype, custom +typedef std::tuple BuildingTypeKey; + +// rotates a size_t value left by count bits +// assumes count is not 0 or >= size_t_bits +// replace this with std::rotl when we move to C++20 +static std::size_t rotl_size_t(size_t val, uint32_t count) +{ + static const int size_t_bits = CHAR_BIT * sizeof(std::size_t); + return val << count | val >> (size_t_bits - count); +} + +struct BuildingTypeKeyHash { + std::size_t operator() (const BuildingTypeKey & key) const { + // cast first param to appease gcc-4.8, which is missing the enum + // specializations for std::hash + std::size_t h1 = std::hash()(static_cast(std::get<0>(key))); + std::size_t h2 = std::hash()(std::get<1>(key)); + std::size_t h3 = std::hash()(std::get<2>(key)); + + return h1 ^ rotl_size_t(h2, 8) ^ rotl_size_t(h3, 16); + } +}; + static PersistentDataItem config; +// for use in counting available materials for the UI +static unordered_map, BuildingTypeKeyHash> job_item_repo; // building id -> PlannedBuilding -unordered_map planned_buildings; +static unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) -Tasks tasks; +static Tasks tasks; // note that this just removes the PlannedBuilding. the tasks will get dropped // as we discover them in the tasks queues and they fail to be found in planned_buildings. @@ -61,7 +87,7 @@ Tasks tasks; // no chance of duplicate tasks getting added to the tasks queues. void PlannedBuilding::remove(color_ostream &out) { DEBUG(status,out).print("removing persistent data for building %d\n", id); - World::DeletePersistentData(config); + World::DeletePersistentData(bld_config); if (planned_buildings.count(id) > 0) planned_buildings.erase(id); } @@ -106,6 +132,31 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { return CR_OK; } +static void validate_config(color_ostream &out, bool verbose = false) { + if (get_config_bool(config, CONFIG_BLOCKS) + || get_config_bool(config, CONFIG_BOULDERS) + || get_config_bool(config, CONFIG_LOGS) + || get_config_bool(config, CONFIG_BARS)) + return; + + if (verbose) + out.printerr("all contruction materials disabled; resetting config\n"); + + set_config_bool(config, CONFIG_BLOCKS, true); + set_config_bool(config, CONFIG_BOULDERS, true); + set_config_bool(config, CONFIG_LOGS, true); + set_config_bool(config, CONFIG_BARS, false); +} + +static void clear_job_item_repo() { + for (auto &entry : job_item_repo) { + for (auto &jitem : entry.second) { + delete jitem; + } + } + job_item_repo.clear(); +} + DFhackCExport command_result plugin_load_data (color_ostream &out) { cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); @@ -113,15 +164,13 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { if (!config.isValid()) { DEBUG(status,out).print("no config found in this save; initializing\n"); config = World::AddPersistentData(CONFIG_KEY); - set_config_bool(config, CONFIG_BLOCKS, true); - set_config_bool(config, CONFIG_BOULDERS, true); - set_config_bool(config, CONFIG_LOGS, true); - set_config_bool(config, CONFIG_BARS, false); } + validate_config(out); DEBUG(status,out).print("loading persisted state\n"); planned_buildings.clear(); tasks.clear(); + clear_job_item_repo(); vector building_configs; World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); @@ -138,30 +187,11 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DEBUG(status,out).print("world unloaded; clearing state for %s\n", plugin_name); planned_buildings.clear(); tasks.clear(); + clear_job_item_repo(); } return CR_OK; } -static bool cycle_requested = false; - -static void do_cycle(color_ostream &out) { - // mark that we have recently run - cycle_timestamp = world->frame_counter; - cycle_requested = false; - - buildingplan_cycle(out, tasks, planned_buildings); -} - -DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if (!Core::getInstance().isWorldLoaded()) - return CR_OK; - - if (is_enabled && - (cycle_requested || world->frame_counter - cycle_timestamp >= CYCLE_TICKS)) - do_cycle(out); - return CR_OK; -} - static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, int nargs = 0, int nres = 0, Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, @@ -182,6 +212,27 @@ static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, std::forward(res_lambda)); } +static bool cycle_requested = false; + +static void do_cycle(color_ostream &out) { + // mark that we have recently run + cycle_timestamp = world->frame_counter; + cycle_requested = false; + + buildingplan_cycle(out, tasks, planned_buildings); + call_buildingplan_lua(&out, "reset_counts"); +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (!Core::getInstance().isWorldLoaded()) + return CR_OK; + + if (is_enabled && + (cycle_requested || world->frame_counter - cycle_timestamp >= CYCLE_TICKS)) + do_cycle(out); + return CR_OK; +} + static command_result do_command(color_ostream &out, vector ¶meters) { CoreSuspender suspend; @@ -228,8 +279,7 @@ static string getBucket(const df::job_item & ji) { } // get a list of item vectors that we should search for matches -static vector getVectorIds(color_ostream &out, df::job_item *job_item) -{ +static vector getVectorIds(color_ostream &out, df::job_item *job_item) { std::vector ret; // if the filter already has the vector_id set to something specific, use it @@ -344,7 +394,7 @@ static void printStatus(color_ostream &out) { } } - out.print("Waiting for %d item(s) to be produced or %zd building(s):\n", + out.print("Waiting for %d item(s) to be produced for %zd building(s):\n", total, planned_buildings.size()); for (auto &count : counts) out.print(" %3d %s\n", count.second, count.first.c_str()); @@ -365,6 +415,9 @@ static bool setSetting(color_ostream &out, string name, bool value) { out.printerr("unrecognized setting: '%s'\n", name.c_str()); return false; } + + validate_config(out, true); + call_buildingplan_lua(&out, "reset_counts"); return true; } @@ -412,7 +465,49 @@ static void scheduleCycle(color_ostream &out) { static int countAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { DEBUG(status,out).print("entering countAvailableItems\n"); - return 10; + DEBUG(status,out).print( + "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + BuildingTypeKey key(type, subtype, custom); + auto &job_items = job_item_repo[key]; + if (index >= job_items.size()) { + for (int i = job_items.size(); i <= index; ++i) { + bool failed = false; + if (!call_buildingplan_lua(&out, "get_job_item", 4, 1, + [&](lua_State *L) { + Lua::Push(L, type); + Lua::Push(L, subtype); + Lua::Push(L, custom); + Lua::Push(L, index+1); + }, + [&](lua_State *L) { + df::job_item *jitem = Lua::GetDFObject(L, -1); + DEBUG(status,out).print("retrieving job_item for index=%d: %p\n", + index, jitem); + if (!jitem) + failed = true; + else + job_items.emplace_back(jitem); + }) || failed) { + return 0; + } + } + } + + auto &jitem = job_items[index]; + auto vector_ids = getVectorIds(out, jitem); + + int count = 0; + for (auto vector_id : vector_ids) { + auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); + for (auto &item : df::global::world->items.other[other_id]) { + if (itemPassesScreen(item) && matchesFilters(item, jitem)) + ++count; + } + } + + DEBUG(status,out).print("found matches %d\n", count); + return count; } DFHACK_PLUGIN_LUA_FUNCTIONS { diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index ac6d3a9a6..0e7e288ac 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -2,6 +2,7 @@ #include "modules/Persistence.h" +#include "df/job_item.h" #include "df/job_item_vector_id.h" #include @@ -26,3 +27,6 @@ int get_config_val(DFHack::PersistentDataItem &c, int index); bool get_config_bool(DFHack::PersistentDataItem &c, int index); void set_config_val(DFHack::PersistentDataItem &c, int index, int value); void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); + +bool itemPassesScreen(df::item * item); +bool matchesFilters(df::item * item, df::job_item * job_item); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 875cd432f..6d5e4a405 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -10,7 +10,6 @@ #include "df/building_design.h" #include "df/item.h" #include "df/job.h" -#include "df/job_item.h" #include "df/world.h" #include @@ -41,13 +40,13 @@ struct BadFlags { } }; -static bool itemPassesScreen(df::item * item) { +bool itemPassesScreen(df::item * item) { static const BadFlags bad_flags; return !(item->flags.whole & bad_flags.whole) && !item->isAssignedToStockpile(); } -static bool matchesFilters(df::item * item, df::job_item * job_item) { +bool matchesFilters(df::item * item, df::job_item * job_item) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) return false; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 5da03e38f..24ef90903 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -56,6 +56,19 @@ function get_num_filters(btype, subtype, custom) return filters and #filters or 0 end +function get_job_item(btype, subtype, custom, index) + local filters = dfhack.buildings.getFiltersByType({}, btype, subtype, custom) + if not filters or not filters[index] then return nil end + local obj = df.job_item:new() + obj:assign(filters[index]) + return obj +end + +local reset_counts_flag = false +function reset_counts() + reset_counts_flag = true +end + -------------------------------- -- Planner Overlay -- @@ -143,8 +156,32 @@ local function to_title_case(str) return str end -function get_item_line_text(idx) - local filter = get_cur_filters()[idx] +ItemLine = defclass(ItemLine, widgets.Panel) +ItemLine.ATTRS{ + idx=DEFAULT_NIL, +} + +function ItemLine:init() + self.frame.h = 1 + self.visible = function() return #get_cur_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text={{text=function() return self:get_item_line_text() end}}, + }, + widgets.Label{ + frame={t=0, l=22}, + text='[filter][x]', + }, + } +end + +function ItemLine:reset() + self.desc = nil + self.available = nil +end + +local function get_desc(filter) local desc = 'Unknown' if filter.has_tool_use then desc = to_title_case(df.tool_uses[filter.has_tool_use]) @@ -170,7 +207,10 @@ function get_item_line_text(idx) if desc == 'Trappart' then desc = 'Mechanism' end + return desc +end +local function get_quantity(filter) local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims() if quantity < 1 then @@ -178,34 +218,29 @@ function get_item_line_text(idx) else quantity = quantity * dimx * dimy * dimz end - desc = ('%d %s%s'):format(quantity, desc, quantity == 1 and '' or 's') + return quantity +end - local available = countAvailableItems(uibs.building_type, +function ItemLine:get_item_line_text() + local idx = self.idx + local filter = get_cur_filters()[idx] + local quantity = get_quantity(filter) + + self.desc = self.desc or get_desc(filter) + local line = ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') + + self.available = self.available or countAvailableItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) - local note = available >= quantity and + local note = self.available >= quantity and 'Can build now' or 'Will wait for item' - return ('%-21s%s%s'):format(desc:sub(1,21), (' '):rep(13), note) + return ('%-21s%s%s'):format(line:sub(1,21), (' '):rep(13), note) end -ItemLine = defclass(ItemLine, widgets.Panel) -ItemLine.ATTRS{ - idx=DEFAULT_NIL, -} - -function ItemLine:init() - self.frame.h = 1 - self.visible = function() return #get_cur_filters() >= self.idx end - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text={{text=function() return get_item_line_text(self.idx) end}}, - }, - widgets.Label{ - frame={t=0, l=22}, - text='[filter][x]', - }, - } +function ItemLine:reduce_quantity() + if not self.available then return end + local filter = get_cur_filters()[self.idx] + self.available = math.max(0, self.available - get_quantity(filter)) end PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) @@ -226,10 +261,10 @@ function PlannerOverlay:init() text='No items required.', visible=function() return #get_cur_filters() == 0 end, }, - ItemLine{frame={t=0, l=0}, idx=1}, - ItemLine{frame={t=2, l=0}, idx=2}, - ItemLine{frame={t=4, l=0}, idx=3}, - ItemLine{frame={t=6, l=0}, idx=4}, + ItemLine{view_id='item1', frame={t=0, l=0}, idx=1}, + ItemLine{view_id='item2', frame={t=2, l=0}, idx=2}, + ItemLine{view_id='item3', frame={t=4, l=0}, idx=3}, + ItemLine{view_id='item4', frame={t=6, l=0}, idx=4}, widgets.CycleHotkeyLabel{ view_id="stairs_top_subtype", frame={t=3, l=0}, @@ -268,13 +303,22 @@ function PlannerOverlay:init() } end -function PlannerOverlay:do_config() - dfhack.run_script('gui/buildingplan') +function PlannerOverlay:reset() + self.subviews.item1:reset() + self.subviews.item2:reset() + self.subviews.item3:reset() + self.subviews.item4:reset() + reset_counts_flag = false end function PlannerOverlay:onInput(keys) if not is_plannable() then return false end if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + if uibs.selection_pos:isValid() then + uibs.selection_pos:clear() + return true + end + self:reset() return false end if PlannerOverlay.super.onInput(self, keys) then @@ -290,6 +334,10 @@ function PlannerOverlay:onInput(keys) if #get_cur_filters() == 0 then return false -- we don't add value; let the game place it end + self.subviews.item1:reduce_quantity() + self.subviews.item2:reduce_quantity() + self.subviews.item3:reduce_quantity() + self.subviews.item4:reduce_quantity() self:place_building() uibs.selection_pos:clear() return true @@ -315,6 +363,10 @@ local BAD_PEN = to_pen{ch='X', fg=COLOR_RED, function PlannerOverlay:onRenderFrame(dc, rect) PlannerOverlay.super.onRenderFrame(self, dc, rect) + if reset_counts_flag then + self:reset() + end + if not is_choosing_area() then return end local bounds = { From 18ad29dde4b09335730550b1f21d3b19f3504cc0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Feb 2023 19:10:42 -0800 Subject: [PATCH 0674/2222] show queue position --- plugins/buildingplan/buildingplan.cpp | 47 ++++++++- plugins/buildingplan/buildingplan.h | 1 + plugins/buildingplan/plannedbuilding.cpp | 62 +++++++++++- plugins/buildingplan/plannedbuilding.h | 6 +- plugins/lua/buildingplan.lua | 120 +++++++++++++++-------- 5 files changed, 189 insertions(+), 47 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 2c5d96b94..17db21cd2 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -175,7 +175,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); for (size_t idx = 0; idx < num_building_configs; ++idx) { - PlannedBuilding pb(building_configs[idx]); + PlannedBuilding pb(out, building_configs[idx]); registerPlannedBuilding(out, pb); } @@ -279,7 +279,7 @@ static string getBucket(const df::job_item & ji) { } // get a list of item vectors that we should search for matches -static vector getVectorIds(color_ostream &out, df::job_item *job_item) { +vector getVectorIds(color_ostream &out, df::job_item *job_item) { std::vector ret; // if the filter already has the vector_id set to something specific, use it @@ -310,6 +310,7 @@ static vector getVectorIds(color_ostream &out, df::job_i ret.push_back(df::job_item_vector_id::IN_PLAY); return ret; } + static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { df::building * bld = pb.getBuildingIfValidOrRemoveIfNot(out); if (!bld) @@ -385,7 +386,10 @@ static void printStatus(color_ostream &out) { auto *jitem = bld->jobs[0]->job_items[task.second]; int32_t quantity = jitem->quantity; if (quantity) { - string desc = toLower(ENUM_KEY_STR(item_type, jitem->item_type)); + string desc = "none"; + call_buildingplan_lua(&out, "get_desc", 1, 1, + [&](lua_State *L) { Lua::Push(L, jitem); }, + [&](lua_State *L) { desc = lua_tostring(L, -1); }); counts[desc] += quantity; total += quantity; } @@ -510,6 +514,42 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return count; } +static int getQueuePosition(color_ostream &out, df::building *bld, int index) { + DEBUG(status,out).print("entering getQueuePosition\n"); + if (!isPlannedBuilding(out, bld) || bld->jobs.size() != 1) + return 0; + + auto &job_items = bld->jobs[0]->job_items; + if (job_items.size() <= index) + return 0; + + PlannedBuilding &pb = planned_buildings.at(bld->id); + if (pb.vector_ids.size() <= index) + return 0; + + auto &job_item = job_items[index]; + + int min_pos = -1; + for (auto &vec_id : pb.vector_ids[index]) { + if (!tasks.count(vec_id)) + continue; + auto &buckets = tasks.at(vec_id); + string bucket_id = getBucket(*job_item); + if (!buckets.count(bucket_id)) + continue; + int bucket_pos = -1; + for (auto &task : buckets.at(bucket_id)) { + ++bucket_pos; + if (bld->id == task.first && index == task.second) + break; + } + if (bucket_pos++ >= 0) + min_pos = min_pos < 0 ? bucket_pos : std::min(min_pos, bucket_pos); + } + + return min_pos < 0 ? 0 : min_pos; +} + DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(printStatus), DFHACK_LUA_FUNCTION(setSetting), @@ -519,5 +559,6 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), + DFHACK_LUA_FUNCTION(getQueuePosition), DFHACK_LUA_END }; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 0e7e288ac..7fe2478aa 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -28,5 +28,6 @@ bool get_config_bool(DFHack::PersistentDataItem &c, int index); void set_config_val(DFHack::PersistentDataItem &c, int index, int value); void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); +std::vector getVectorIds(DFHack::color_ostream &out, df::job_item *job_item); bool itemPassesScreen(df::item * item); bool matchesFilters(df::item * item, df::job_item * job_item); diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index c03f56161..f4f3564b7 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -2,25 +2,79 @@ #include "buildingplan.h" #include "Debug.h" +#include "MiscUtils.h" #include "modules/World.h" +#include "df/job.h" + namespace DFHack { DBG_EXTERN(buildingplan, status); - DBG_EXTERN(buildingplan, cycle); } +using std::string; +using std::vector; using namespace DFHack; +static vector> get_vector_ids(color_ostream &out, int bld_id) { + vector> ret; + + df::building *bld = df::building::find(bld_id); + + if (!bld || bld->jobs.size() != 1) + return ret; + + auto &job = bld->jobs[0]; + for (auto &jitem : job->job_items) { + ret.emplace_back(getVectorIds(out, jitem)); + } + return ret; +} + +static vector> deserialize(color_ostream &out, PersistentDataItem &bld_config) { + vector> ret; + + DEBUG(status,out).print("deserializing state for building %d: %s\n", + get_config_val(bld_config, BLD_CONFIG_ID), bld_config.val().c_str()); + + vector joined; + split_string(&joined, bld_config.val(), "|"); + for (auto &str : joined) { + vector lst; + split_string(&lst, str, ","); + vector ids; + for (auto &s : lst) + ids.emplace_back(df::job_item_vector_id(string_to_int(s))); + ret.emplace_back(ids); + } + + if (!ret.size()) + ret = get_vector_ids(out, get_config_val(bld_config, BLD_CONFIG_ID)); + + return ret; +} + +static string serialize(const vector> &vector_ids) { + vector joined; + for (auto &vec_list : vector_ids) { + joined.emplace_back(join_strings(",", vec_list)); + } + return join_strings("|", joined); +} + PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building) - : id(building->id) { + : id(building->id), vector_ids(get_vector_ids(out, id)) { DEBUG(status,out).print("creating persistent data for building %d\n", id); bld_config = World::AddPersistentData(BLD_CONFIG_KEY); set_config_val(bld_config, BLD_CONFIG_ID, id); + bld_config.val() = serialize(vector_ids); + DEBUG(status,out).print("serialized state for building %d: %s\n", id, bld_config.val().c_str()); } -PlannedBuilding::PlannedBuilding(PersistentDataItem &bld_config) - : id(get_config_val(bld_config, BLD_CONFIG_ID)), bld_config(bld_config) { } +PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_config) + : id(get_config_val(bld_config, BLD_CONFIG_ID)), + vector_ids(deserialize(out, bld_config)), + bld_config(bld_config) { } // Ensure the building still exists and is in a valid state. It can disappear // for lots of reasons, such as running the game with the buildingplan plugin diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 9f0273b82..592f0e4b3 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -5,13 +5,17 @@ #include "modules/Persistence.h" #include "df/building.h" +#include "df/job_item_vector_id.h" class PlannedBuilding { public: const df::building::key_field_type id; + // job_item idx -> list of vectors the task is linked to + const std::vector> vector_ids; + PlannedBuilding(DFHack::color_ostream &out, df::building *building); - PlannedBuilding(DFHack::PersistentDataItem &bld_config); + PlannedBuilding(DFHack::color_ostream &out, DFHack::PersistentDataItem &bld_config); void remove(DFHack::color_ostream &out); diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 24ef90903..376787952 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -70,7 +70,7 @@ function reset_counts() end -------------------------------- --- Planner Overlay +-- PlannerOverlay -- local uibs = df.global.buildreq @@ -181,7 +181,7 @@ function ItemLine:reset() self.available = nil end -local function get_desc(filter) +function get_desc(filter) local desc = 'Unknown' if filter.has_tool_use then desc = to_title_case(df.tool_uses[filter.has_tool_use]) @@ -190,13 +190,15 @@ local function get_desc(filter) desc = to_title_case(df.item_type[filter.item_type]) end if filter.flags2 and filter.flags2.building_material then - desc = "Generic material"; + desc = 'Generic material'; if filter.flags2.fire_safe then - desc = "Fire-safe material"; + desc = 'Fire-safe material'; end if filter.flags2.magma_safe then - desc = "Magma-safe material"; + desc = 'Magma-safe material'; end + elseif filter.flags2 and filter.flags2.screw then + desc = 'Screw' elseif filter.vector_id then desc = to_title_case(df.job_item_vector_id[filter.vector_id]) end @@ -249,27 +251,30 @@ PlannerOverlay.ATTRS{ default_enabled=true, viewscreens='dwarfmode/Building/Placement', frame={w=54, h=9}, - frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } function PlannerOverlay:init() self:addviews{ + widgets.Panel{ + frame={}, + frame_style=gui.MEDIUM_FRAME, + }, widgets.Label{ frame={}, auto_width=true, text='No items required.', visible=function() return #get_cur_filters() == 0 end, }, - ItemLine{view_id='item1', frame={t=0, l=0}, idx=1}, - ItemLine{view_id='item2', frame={t=2, l=0}, idx=2}, - ItemLine{view_id='item3', frame={t=4, l=0}, idx=3}, - ItemLine{view_id='item4', frame={t=6, l=0}, idx=4}, + ItemLine{view_id='item1', frame={t=1, l=1, r=1}, idx=1}, + ItemLine{view_id='item2', frame={t=3, l=1, r=1}, idx=2}, + ItemLine{view_id='item3', frame={t=5, l=1, r=1}, idx=3}, + ItemLine{view_id='item4', frame={t=7, l=1, r=1}, idx=4}, widgets.CycleHotkeyLabel{ - view_id="stairs_top_subtype", - frame={t=3, l=0}, - key="CUSTOM_R", - label="Top Stair Type: ", + view_id='stairs_top_subtype', + frame={t=4, l=1}, + key='CUSTOM_R', + label='Top Stair Type: ', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -278,10 +283,10 @@ function PlannerOverlay:init() }, }, widgets.CycleHotkeyLabel { - view_id="stairs_bottom_subtype", - frame={t=4, l=0}, - key="CUSTOM_B", - label="Bottom Stair Type: ", + view_id='stairs_bottom_subtype', + frame={t=5, l=1}, + key='CUSTOM_B', + label='Bottom Stair Type: ', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -290,7 +295,7 @@ function PlannerOverlay:init() }, }, widgets.Label{ - frame={b=0, l=17}, + frame={b=1, l=17}, text={ 'Selected area: ', {text=function() @@ -300,6 +305,17 @@ function PlannerOverlay:init() }, visible=is_choosing_area, }, + widgets.CycleHotkeyLabel{ + view_id='safety', + frame={b=0, l=1}, + key='CUSTOM_F', + label='Extra safety: ', + options={ + {label='None', value='none'}, + {label='Magma', value='magma'}, + {label='Fire', value='fire'}, + }, + }, } end @@ -473,12 +489,50 @@ function PlannerOverlay:place_building() scheduleCycle() end +-------------------------------- +-- InspectorOverlay +-- + +local function get_building_filters() + local bld = dfhack.gui.getSelectedBuilding() + return dfhack.buildings.getFiltersByType({}, + bld:getType(), bld:getSubtype(), bld:getCustomType()) +end + +InspectorLine = defclass(InspectorLine, widgets.Panel) +InspectorLine.ATTRS{ + idx=DEFAULT_NIL, +} + +function InspectorLine:init() + self.frame.h = 2 + self.visible = function() return #get_building_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text={{text=function() return get_desc(get_building_filters()[self.idx]) end}}, + }, + widgets.Label{ + frame={t=1, l=2}, + text={{text=self:callback('get_status_line')}}, + }, + } +end + +function InspectorLine:get_status_line() + local queue_pos = getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1) + if queue_pos <= 0 then + return 'Item attached' + end + return ('Position in line: %d'):format(queue_pos) +end + InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) InspectorOverlay.ATTRS{ default_pos={x=-41,y=14}, default_enabled=true, viewscreens='dwarfmode/ViewSheets/BUILDING', - frame={w=30, h=9}, + frame={w=30, h=14}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } @@ -489,29 +543,17 @@ function InspectorOverlay:init() frame={t=0, l=0}, text='Waiting for items:', }, - widgets.Label{ - frame={t=1, l=0}, - text='item1', - }, - widgets.Label{ - frame={t=2, l=0}, - text='item2', - }, - widgets.Label{ - frame={t=3, l=0}, - text='item3', - }, - widgets.Label{ - frame={t=4, l=0}, - text='item4', - }, + InspectorLine{view_id='item1', frame={t=2, l=0}, idx=1}, + InspectorLine{view_id='item2', frame={t=4, l=0}, idx=2}, + InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3}, + InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4}, widgets.HotkeyLabel{ - frame={t=5, l=0}, + frame={t=10, l=0}, label='adjust filters', key='CUSTOM_CTRL_F', }, widgets.HotkeyLabel{ - frame={t=6, l=0}, + frame={t=11, l=0}, label='make top priority', key='CUSTOM_CTRL_T', }, @@ -578,7 +620,7 @@ end -- does not need the core suspended. function show_global_settings_dialog(settings) GlobalSettings{ - frame_title="Buildingplan Global Settings", + frame_title='Buildingplan Global Settings', settings=settings, }:show() end From 56c8927316947ddbd494b36a25566b9e10cd9bd3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 03:08:55 -0800 Subject: [PATCH 0675/2222] better description string for inspection overlay --- plugins/buildingplan/buildingplan.cpp | 86 +++++++++++++++++---------- plugins/lua/buildingplan.lua | 20 +++---- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 17db21cd2..579c1e83f 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -357,6 +357,20 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { return true; } +static string get_desc_string(color_ostream &out, df::job_item *jitem, + const vector &vec_ids) { + vector descs; + for (auto &vec_id : vec_ids) { + df::job_item jitem_copy = *jitem; + jitem_copy.vector_id = vec_id; + call_buildingplan_lua(&out, "get_desc", 1, 1, + [&](lua_State *L) { Lua::Push(L, &jitem_copy); }, + [&](lua_State *L) { + descs.emplace_back(lua_tostring(L, -1)); }); + } + return join_strings(" or ", descs); +} + static void printStatus(color_ostream &out) { DEBUG(status,out).print("entering buildingplan_printStatus\n"); out.print("buildingplan is %s\n\n", is_enabled ? "enabled" : "disabled"); @@ -369,31 +383,21 @@ static void printStatus(color_ostream &out) { map counts; int32_t total = 0; - for (auto &buckets : tasks) { - for (auto &bucket_queue : buckets.second) { - Bucket &tqueue = bucket_queue.second; - for (auto it = tqueue.begin(); it != tqueue.end();) { - auto & task = *it; - auto id = task.first; - df::building *bld = NULL; - if (!planned_buildings.count(id) || - !(bld = planned_buildings.at(id).getBuildingIfValidOrRemoveIfNot(out))) { - DEBUG(status,out).print("discarding invalid task: bld=%d, job_item_idx=%d\n", - id, task.second); - it = tqueue.erase(it); - continue; - } - auto *jitem = bld->jobs[0]->job_items[task.second]; - int32_t quantity = jitem->quantity; - if (quantity) { - string desc = "none"; - call_buildingplan_lua(&out, "get_desc", 1, 1, - [&](lua_State *L) { Lua::Push(L, jitem); }, - [&](lua_State *L) { desc = lua_tostring(L, -1); }); - counts[desc] += quantity; - total += quantity; - } - ++it; + for (auto &entry : planned_buildings) { + auto &pb = entry.second; + auto bld = pb.getBuildingIfValidOrRemoveIfNot(out); + if (!bld || bld->jobs.size() != 1) + continue; + auto &job_items = bld->jobs[0]->job_items; + if (job_items.size() != pb.vector_ids.size()) + continue; + int job_item_idx = 0; + for (auto &vec_ids : pb.vector_ids) { + auto &jitem = job_items[job_item_idx++]; + int32_t quantity = jitem->quantity; + if (quantity) { + counts[get_desc_string(out, jitem, vec_ids)] += quantity; + total += quantity; } } } @@ -514,20 +518,41 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return count; } -static int getQueuePosition(color_ostream &out, df::building *bld, int index) { - DEBUG(status,out).print("entering getQueuePosition\n"); +static bool validate_pb(color_ostream &out, df::building *bld, int index) { if (!isPlannedBuilding(out, bld) || bld->jobs.size() != 1) - return 0; + return false; auto &job_items = bld->jobs[0]->job_items; if (job_items.size() <= index) - return 0; + return false; PlannedBuilding &pb = planned_buildings.at(bld->id); if (pb.vector_ids.size() <= index) + return false; + + return true; +} + +static string getDescString(color_ostream &out, df::building *bld, int index) { + DEBUG(status,out).print("entering getDescString\n"); + if (!validate_pb(out, bld, index)) return 0; - auto &job_item = job_items[index]; + PlannedBuilding &pb = planned_buildings.at(bld->id); + auto &jitem = bld->jobs[0]->job_items[index]; + return get_desc_string(out, jitem, pb.vector_ids[index]); +} + +static int getQueuePosition(color_ostream &out, df::building *bld, int index) { + DEBUG(status,out).print("entering getQueuePosition\n"); + if (!validate_pb(out, bld, index)) + return 0; + + PlannedBuilding &pb = planned_buildings.at(bld->id); + auto &job_item = bld->jobs[0]->job_items[index]; + + if (job_item->quantity <= 0) + return 0; int min_pos = -1; for (auto &vec_id : pb.vector_ids[index]) { @@ -559,6 +584,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), + DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), DFHACK_LUA_END }; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 376787952..d0b3417fb 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -183,13 +183,15 @@ end function get_desc(filter) local desc = 'Unknown' - if filter.has_tool_use then + if filter.has_tool_use and filter.has_tool_use > -1 then desc = to_title_case(df.tool_uses[filter.has_tool_use]) - end - if filter.item_type then + elseif filter.flags2 and filter.flags2.screw then + desc = 'Screw' + elseif filter.item_type and filter.item_type > -1 then desc = to_title_case(df.item_type[filter.item_type]) - end - if filter.flags2 and filter.flags2.building_material then + elseif filter.vector_id and filter.vector_id > -1 then + desc = to_title_case(df.job_item_vector_id[filter.vector_id]) + elseif filter.flags2 and filter.flags2.building_material then desc = 'Generic material'; if filter.flags2.fire_safe then desc = 'Fire-safe material'; @@ -197,10 +199,6 @@ function get_desc(filter) if filter.flags2.magma_safe then desc = 'Magma-safe material'; end - elseif filter.flags2 and filter.flags2.screw then - desc = 'Screw' - elseif filter.vector_id then - desc = to_title_case(df.job_item_vector_id[filter.vector_id]) end if desc:endswith('s') then @@ -208,6 +206,8 @@ function get_desc(filter) end if desc == 'Trappart' then desc = 'Mechanism' + elseif desc == 'Wood' then + desc = 'Log' end return desc end @@ -510,7 +510,7 @@ function InspectorLine:init() self:addviews{ widgets.Label{ frame={t=0, l=0}, - text={{text=function() return get_desc(get_building_filters()[self.idx]) end}}, + text={{text=function() return getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) end}}, }, widgets.Label{ frame={t=1, l=2}, From 0d3285678c3a0e3e058ad4fc742f0a0b4ed16b91 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 17:25:15 -0800 Subject: [PATCH 0676/2222] separate errors panel, fix pb vectors on load --- plugins/buildingplan/buildingplan.cpp | 15 +++-- plugins/lua/buildingplan.lua | 90 +++++++++++++++++++++------ 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 579c1e83f..e2db9c367 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -330,12 +330,11 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) { auto job_item = job_items[job_item_idx]; auto bucket = getBucket(*job_item); - auto vector_ids = getVectorIds(out, job_item); // if there are multiple vector_ids, schedule duplicate tasks. after // the correct number of items are matched, the extras will get popped // as invalid - for (auto vector_id : vector_ids) { + for (auto vector_id : pb.vector_ids[job_item_idx]) { for (int item_num = 0; item_num < job_item->quantity; ++item_num) { tasks[vector_id][bucket].push_back(std::make_pair(id, job_item_idx)); DEBUG(status,out).print("added task: %s/%s/%d,%d; " @@ -402,10 +401,14 @@ static void printStatus(color_ostream &out) { } } - out.print("Waiting for %d item(s) to be produced for %zd building(s):\n", - total, planned_buildings.size()); - for (auto &count : counts) - out.print(" %3d %s\n", count.second, count.first.c_str()); + if (planned_buildings.size()) { + out.print("Waiting for %d item(s) to be produced for %zd building(s):\n", + total, planned_buildings.size()); + for (auto &count : counts) + out.print(" %3d %s\n", count.second, count.first.c_str()); + } else { + out.print("Currently no planned buildings\n"); + } out.print("\n"); } diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index d0b3417fb..5a12292ea 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -192,7 +192,7 @@ function get_desc(filter) elseif filter.vector_id and filter.vector_id > -1 then desc = to_title_case(df.job_item_vector_id[filter.vector_id]) elseif filter.flags2 and filter.flags2.building_material then - desc = 'Generic material'; + desc = 'Building material'; if filter.flags2.fire_safe then desc = 'Fire-safe material'; end @@ -236,7 +236,7 @@ function ItemLine:get_item_line_text() local note = self.available >= quantity and 'Can build now' or 'Will wait for item' - return ('%-21s%s%s'):format(line:sub(1,21), (' '):rep(13), note) + return ('%-21s%s%s'):format(line:sub(1,21), (' '):rep(14), note) end function ItemLine:reduce_quantity() @@ -245,34 +245,44 @@ function ItemLine:reduce_quantity() self.available = math.max(0, self.available - get_quantity(filter)) end +local function get_placement_errors() + local out = '' + for _,str in ipairs(uibs.errors) do + if #out > 0 then out = out .. NEWLINE end + out = out .. str.value + end + return out +end + PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) PlannerOverlay.ATTRS{ - default_pos={x=6,y=9}, + default_pos={x=5,y=9}, default_enabled=true, viewscreens='dwarfmode/Building/Placement', - frame={w=54, h=9}, - frame_background=gui.CLEAR_PEN, + frame={w=56, h=18}, } function PlannerOverlay:init() - self:addviews{ - widgets.Panel{ - frame={}, - frame_style=gui.MEDIUM_FRAME, - }, + local main_panel = widgets.Panel{ + frame={t=0, l=0, r=0, h=14}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + } + + main_panel:addviews{ widgets.Label{ frame={}, auto_width=true, text='No items required.', visible=function() return #get_cur_filters() == 0 end, }, - ItemLine{view_id='item1', frame={t=1, l=1, r=1}, idx=1}, - ItemLine{view_id='item2', frame={t=3, l=1, r=1}, idx=2}, - ItemLine{view_id='item3', frame={t=5, l=1, r=1}, idx=3}, - ItemLine{view_id='item4', frame={t=7, l=1, r=1}, idx=4}, + ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1}, + ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2}, + ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3}, + ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4}, widgets.CycleHotkeyLabel{ view_id='stairs_top_subtype', - frame={t=4, l=1}, + frame={t=4, l=4}, key='CUSTOM_R', label='Top Stair Type: ', visible=is_stairs, @@ -284,7 +294,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel { view_id='stairs_bottom_subtype', - frame={t=5, l=1}, + frame={t=5, l=4}, key='CUSTOM_B', label='Bottom Stair Type: ', visible=is_stairs, @@ -295,7 +305,7 @@ function PlannerOverlay:init() }, }, widgets.Label{ - frame={b=1, l=17}, + frame={b=3, l=17}, text={ 'Selected area: ', {text=function() @@ -307,15 +317,54 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel{ view_id='safety', - frame={b=0, l=1}, - key='CUSTOM_F', - label='Extra safety: ', + frame={b=0, l=2}, + key='CUSTOM_G', + label='Safety: ', options={ {label='None', value='none'}, {label='Magma', value='magma'}, {label='Fire', value='fire'}, }, }, + widgets.HotkeyLabel{ + frame={b=1, l=0}, + key='SELECT', + label='Choose item', + }, + widgets.HotkeyLabel{ + frame={b=1, l=21}, + key='CUSTOM_F', + label='Filter', + }, + widgets.HotkeyLabel{ + frame={b=1, l=33}, + key='CUSTOM_X', + label='Clear filter', + }, + } + + local error_panel = widgets.ResizingPanel{ + view_id='errors', + frame={t=14, l=0, r=0, h=3}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + } + + error_panel:addviews{ + widgets.WrappedLabel{ + text_pen=COLOR_LIGHTRED, + text_to_wrap=get_placement_errors, + }, + widgets.Label{ + text_pen=COLOR_GREEN, + text='OK to build', + visible=function() return #uibs.errors == 0 end, + }, + } + + self:addviews{ + main_panel, + error_panel, } end @@ -367,6 +416,7 @@ end function PlannerOverlay:render(dc) if not is_plannable() then return end + self.subviews.errors:updateLayout() PlannerOverlay.super.render(self, dc) end From 3f8be2cd9e579ecf912ddb12eff9e68ba13cd30b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 18:02:15 -0800 Subject: [PATCH 0677/2222] implement make_top_priority, cache inspector data --- plugins/buildingplan/buildingplan.cpp | 33 ++++++++++++++++++++++- plugins/lua/buildingplan.lua | 38 +++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index e2db9c367..dff04f0c2 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -336,7 +336,7 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { // as invalid for (auto vector_id : pb.vector_ids[job_item_idx]) { for (int item_num = 0; item_num < job_item->quantity; ++item_num) { - tasks[vector_id][bucket].push_back(std::make_pair(id, job_item_idx)); + tasks[vector_id][bucket].emplace_back(id, job_item_idx); DEBUG(status,out).print("added task: %s/%s/%d,%d; " "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), @@ -578,6 +578,36 @@ static int getQueuePosition(color_ostream &out, df::building *bld, int index) { return min_pos < 0 ? 0 : min_pos; } +static void makeTopPriority(color_ostream &out, df::building *bld) { + DEBUG(status,out).print("entering makeTopPriority\n"); + if (!validate_pb(out, bld, 0)) + return; + + PlannedBuilding &pb = planned_buildings.at(bld->id); + auto &job_items = bld->jobs[0]->job_items; + + for (int index = 0; index < job_items.size(); ++index) { + for (auto &vec_id : pb.vector_ids[index]) { + if (!tasks.count(vec_id)) + continue; + auto &buckets = tasks.at(vec_id); + string bucket_id = getBucket(*job_items[index]); + if (!buckets.count(bucket_id)) + continue; + auto &bucket = buckets.at(bucket_id); + for (auto taskit = bucket.begin(); taskit != bucket.end(); ++taskit) { + if (bld->id == taskit->first && index == taskit->second) { + auto task_bld_id = taskit->first; + auto task_job_item_idx = taskit->second; + bucket.erase(taskit); + bucket.emplace_front(task_bld_id, task_job_item_idx); + break; + } + } + } + } +} + DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(printStatus), DFHACK_LUA_FUNCTION(setSetting), @@ -589,5 +619,6 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), + DFHACK_LUA_FUNCTION(makeTopPriority), DFHACK_LUA_END }; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 5a12292ea..6bf35bc2a 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -65,8 +65,10 @@ function get_job_item(btype, subtype, custom, index) end local reset_counts_flag = false +local reset_inspector_flag = false function reset_counts() reset_counts_flag = true + reset_inspector_flag = true end -------------------------------- @@ -560,7 +562,7 @@ function InspectorLine:init() self:addviews{ widgets.Label{ frame={t=0, l=0}, - text={{text=function() return getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) end}}, + text={{text=self:callback('get_desc_string')}}, }, widgets.Label{ frame={t=1, l=2}, @@ -569,12 +571,24 @@ function InspectorLine:init() } end +function InspectorLine:get_desc_string() + if self.desc then return self.desc end + self.desc = getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) + return self.desc +end + function InspectorLine:get_status_line() + if self.status then return self.status end local queue_pos = getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1) if queue_pos <= 0 then return 'Item attached' end - return ('Position in line: %d'):format(queue_pos) + self.status = ('Position in line: %d'):format(queue_pos) + return self.status +end + +function InspectorLine:reset() + self.status = nil end InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) @@ -606,14 +620,31 @@ function InspectorOverlay:init() frame={t=11, l=0}, label='make top priority', key='CUSTOM_CTRL_T', + on_activate=self:callback('make_top_priority'), }, } end +function InspectorOverlay:reset() + self.subviews.item1:reset() + self.subviews.item2:reset() + self.subviews.item3:reset() + self.subviews.item4:reset() + reset_inspector_flag = false +end + +function InspectorOverlay:make_top_priority() + makeTopPriority(dfhack.gui.getSelectedBuilding()) + self:reset() +end + function InspectorOverlay:onInput(keys) if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then return false end + if keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then + self:reset() + end return InspectorOverlay.super.onInput(self, keys) end @@ -621,6 +652,9 @@ function InspectorOverlay:render(dc) if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then return end + if reset_inspector_flag then + self:reset() + end InspectorOverlay.super.render(self, dc) end From 96fa7fa1e2951bb7c96b7de3bca6715aaf5a50ad Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 18:23:14 -0800 Subject: [PATCH 0678/2222] fix position of errors panel --- plugins/lua/buildingplan.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 6bf35bc2a..82172613d 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -261,7 +261,7 @@ PlannerOverlay.ATTRS{ default_pos={x=5,y=9}, default_enabled=true, viewscreens='dwarfmode/Building/Placement', - frame={w=56, h=18}, + frame={w=56, h=20}, } function PlannerOverlay:init() @@ -347,17 +347,20 @@ function PlannerOverlay:init() local error_panel = widgets.ResizingPanel{ view_id='errors', - frame={t=14, l=0, r=0, h=3}, + frame={t=14, l=0, r=0}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } error_panel:addviews{ widgets.WrappedLabel{ + frame={t=0, l=0, r=0}, text_pen=COLOR_LIGHTRED, text_to_wrap=get_placement_errors, + visible=function() return #uibs.errors > 0 end, }, widgets.Label{ + frame={t=0, l=0, r=0}, text_pen=COLOR_GREEN, text='OK to build', visible=function() return #uibs.errors == 0 end, From b3198c88a0aa6885c6ca09f8fea5d63b4e0de615 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 18:43:06 -0800 Subject: [PATCH 0679/2222] only block mouse clicks over exactly the panel area --- plugins/lua/buildingplan.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 82172613d..b7ed789e4 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -266,6 +266,7 @@ PlannerOverlay.ATTRS{ function PlannerOverlay:init() local main_panel = widgets.Panel{ + view_id='main', frame={t=0, l=0, r=0, h=14}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, @@ -396,7 +397,14 @@ function PlannerOverlay:onInput(keys) end if keys._MOUSE_L_DOWN then if is_over_options_panel() then return false end - if self:getMouseFramePos() then return true end + local detect_rect = copyall(self.frame_rect) + detect_rect.height = self.subviews.main.frame_rect.height + + self.subviews.errors.frame_rect.height + detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 + if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) + or self.subviews.errors:getMousePos() then + return true + end if #uibs.errors > 0 then return true end local pos = dfhack.gui.getMousePos() if pos then From e92a54deaa0ec4a48850b9c204839183f5864406 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 21:17:55 -0800 Subject: [PATCH 0680/2222] beginning of textures --- plugins/buildingplan/buildingplan.cpp | 4 ++-- plugins/lua/buildingplan.lua | 29 ++++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index dff04f0c2..080941823 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -220,7 +220,7 @@ static void do_cycle(color_ostream &out) { cycle_requested = false; buildingplan_cycle(out, tasks, planned_buildings); - call_buildingplan_lua(&out, "reset_counts"); + call_buildingplan_lua(&out, "signal_reset"); } DFhackCExport command_result plugin_onupdate(color_ostream &out) { @@ -428,7 +428,7 @@ static bool setSetting(color_ostream &out, string name, bool value) { } validate_config(out, true); - call_buildingplan_lua(&out, "reset_counts"); + call_buildingplan_lua(&out, "signal_reset"); return true; } diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index b7ed789e4..2123f636e 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -64,9 +64,11 @@ function get_job_item(btype, subtype, custom, index) return obj end +local texpos_base = -1 local reset_counts_flag = false local reset_inspector_flag = false -function reset_counts() +function signal_reset() + texpos_base = dfhack.textures.getControlPanelTexposStart() reset_counts_flag = true reset_inspector_flag = true end @@ -168,12 +170,22 @@ function ItemLine:init() self.visible = function() return #get_cur_filters() >= self.idx end self:addviews{ widgets.Label{ - frame={t=0, l=0}, - text={{text=function() return self:get_item_line_text() end}}, + frame={t=0, l=23}, + text={ + {tile=2600}, + {gap=6, tile=2602}, + {tile=2600}, + {gap=1, tile=2602}, + }, }, widgets.Label{ - frame={t=0, l=22}, - text='[filter][x]', + frame={t=0, l=0}, + text={ + {width=21, text=function() return self:get_item_line_text() end}, + {gap=3, text='filter'}, + {gap=2, text='x'}, + {gap=3, text=function() return self.note end}, + }, }, } end @@ -231,14 +243,13 @@ function ItemLine:get_item_line_text() local quantity = get_quantity(filter) self.desc = self.desc or get_desc(filter) - local line = ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') self.available = self.available or countAvailableItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) - local note = self.available >= quantity and - 'Can build now' or 'Will wait for item' + self.note = self.available >= quantity and + 'Can build now' or 'Will build later' - return ('%-21s%s%s'):format(line:sub(1,21), (' '):rep(14), note) + return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') end function ItemLine:reduce_quantity() From aa4ebe6398ed199bf9b8f3516cedfb9aa0570abd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 03:16:34 -0800 Subject: [PATCH 0681/2222] remove some cruft --- plugins/lua/buildingplan.lua | 130 ----------------------------------- 1 file changed, 130 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 2123f636e..7f8d996c8 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -685,10 +685,6 @@ OVERLAY_WIDGETS = { inspector=InspectorOverlay, } - -local dialogs = require('gui.dialogs') -local guidm = require('gui.dwarfmode') - -- returns whether the items matched by the specified filter can have a quality -- rating. This also conveniently indicates whether an item can be decorated. -- does not need the core suspended @@ -704,130 +700,4 @@ function item_can_be_improved(btype, subtype, custom, reverse_idx) filter.item_type ~= df.item_type.BOULDER end - --- --- GlobalSettings dialog --- - -local GlobalSettings = defclass(GlobalSettings, dialogs.MessageBox) -GlobalSettings.focus_path = 'buildingplan_globalsettings' - -GlobalSettings.ATTRS{ - settings = {} -} - -function GlobalSettings:onDismiss() - for k,v in pairs(self.settings) do - -- call back into C++ to save changes - setSetting(k, v) - end -end - --- does not need the core suspended. -function show_global_settings_dialog(settings) - GlobalSettings{ - frame_title='Buildingplan Global Settings', - settings=settings, - }:show() -end - -function GlobalSettings:toggle_setting(name) - self.settings[name] = not self.settings[name] -end - -function GlobalSettings:get_setting_string(name) - if self.settings[name] then return 'On' end - return 'Off' -end - -function GlobalSettings:get_setting_pen(name) - if self.settings[name] then return COLOR_LIGHTGREEN end - return COLOR_LIGHTRED -end - -function GlobalSettings:is_setting_enabled(name) - return self.settings[name] -end - -function GlobalSettings:make_setting_label_token(text, key, name, width) - return {text=text, key=key, key_sep=': ', key_pen=COLOR_LIGHTGREEN, - on_activate=self:callback('toggle_setting', name), width=width} -end - -function GlobalSettings:make_setting_value_token(name) - return {text=self:callback('get_setting_string', name), - enabled=self:callback('is_setting_enabled', name), - pen=self:callback('get_setting_pen', name), - dpen=COLOR_GRAY} -end - --- mockup: ---[[ - Buildingplan Global Settings - - e: Enable all: Off - Enables buildingplan for all building types. Use this to avoid having to - manually enable buildingplan for each building type that you want to plan. - Note that DFHack quickfort will use buildingplan to manage buildings - regardless of whether buildingplan is "enabled" for the building type. - - Allowed types for generic, fire-safe, and magma-safe building material: - b: Blocks: On - s: Boulders: On - w: Wood: On - r: Bars: Off - Changes to these settings will be applied to newly-planned buildings. - - A: Apply building material filter settings to existing planned buildings - Use this if your planned buildings can't be completed because the settings - above were too restrictive when the buildings were originally planned. - - M: Edit list of materials to avoid - potash - pearlash - ash - coal - Buildingplan will avoid using these material types when a planned building's - material filter is set to 'any'. They can stil be matched when they are - explicitly allowed by a planned building's material filter. Changes to this - list take effect for existing buildings immediately. - - g: Allow bags: Off - This allows bags to be placed where a 'coffer' is planned. - - f: Legacy Quickfort Mode: Off - Compatibility mode for the legacy Python-based Quickfort application. This - setting is not needed for DFHack quickfort. ---]] -function GlobalSettings:init() - - self.subviews.label:setText{ - self:make_setting_label_token('Enable all', 'CUSTOM_E', 'all_enabled', 12), - self:make_setting_value_token('all_enabled'), '\n', - ' Enables buildingplan for all building types. Use this to avoid having\n', - ' to manually enable buildingplan for each building type that you want\n', - ' to plan. Note that DFHack quickfort will use buildingplan to manage\n', - ' buildings regardless of whether buildingplan is "enabled" for the\n', - ' building type.\n', - '\n', - 'Allowed types for generic, fire-safe, and magma-safe building material:\n', - self:make_setting_label_token('Blocks', 'CUSTOM_B', 'blocks', 10), - self:make_setting_value_token('blocks'), '\n', - self:make_setting_label_token('Boulders', 'CUSTOM_S', 'boulders', 10), - self:make_setting_value_token('boulders'), '\n', - self:make_setting_label_token('Wood', 'CUSTOM_W', 'logs', 10), - self:make_setting_value_token('logs'), '\n', - self:make_setting_label_token('Bars', 'CUSTOM_R', 'bars', 10), - self:make_setting_value_token('bars'), '\n', - ' Changes to these settings will be applied to newly-planned buildings.\n', - ' If no types are enabled above, then any building material is allowed.\n', - '\n', - self:make_setting_label_token('Legacy Quickfort Mode', 'CUSTOM_F', - 'quickfort_mode', 23), - self:make_setting_value_token('quickfort_mode'), '\n', - ' Compatibility mode for the legacy Python-based Quickfort application.\n', - ' This setting is not needed for DFHack quickfort.' - } -end - return _ENV From c0cdd58b5080a43f94ec175e552c8c8c982e25ca Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 21:42:58 -0800 Subject: [PATCH 0682/2222] fix signed-unsigned compare --- plugins/buildingplan/buildingplan.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 080941823..e31914372 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -481,7 +481,7 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 type, subtype, custom, index); BuildingTypeKey key(type, subtype, custom); auto &job_items = job_item_repo[key]; - if (index >= job_items.size()) { + if (index >= (int)job_items.size()) { for (int i = job_items.size(); i <= index; ++i) { bool failed = false; if (!call_buildingplan_lua(&out, "get_job_item", 4, 1, @@ -526,11 +526,11 @@ static bool validate_pb(color_ostream &out, df::building *bld, int index) { return false; auto &job_items = bld->jobs[0]->job_items; - if (job_items.size() <= index) + if ((int)job_items.size() <= index) return false; PlannedBuilding &pb = planned_buildings.at(bld->id); - if (pb.vector_ids.size() <= index) + if ((int)pb.vector_ids.size() <= index) return false; return true; @@ -586,7 +586,7 @@ static void makeTopPriority(color_ostream &out, df::building *bld) { PlannedBuilding &pb = planned_buildings.at(bld->id); auto &job_items = bld->jobs[0]->job_items; - for (int index = 0; index < job_items.size(); ++index) { + for (int index = 0; index < (int)job_items.size(); ++index) { for (auto &vec_id : pb.vector_ids[index]) { if (!tasks.count(vec_id)) continue; From c59ad78f40ab72dedd699c10cd8e0cc2e34d4906 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Feb 2023 23:02:34 -0800 Subject: [PATCH 0683/2222] more tokens, textures, and colors --- plugins/buildingplan/buildingplan.cpp | 53 +++++++++++++-------------- plugins/lua/buildingplan.lua | 52 +++++++++++++++++++------- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index e31914372..687f46705 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -148,7 +148,30 @@ static void validate_config(color_ostream &out, bool verbose = false) { set_config_bool(config, CONFIG_BARS, false); } -static void clear_job_item_repo() { +static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(status).print("calling buildingplan lua function: '%s'\n", fn_name); + + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.buildingplan", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} + +static void clear_state(color_ostream &out) { + call_buildingplan_lua(&out, "signal_reset"); + planned_buildings.clear(); + tasks.clear(); for (auto &entry : job_item_repo) { for (auto &jitem : entry.second) { delete jitem; @@ -168,9 +191,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { validate_config(out); DEBUG(status,out).print("loading persisted state\n"); - planned_buildings.clear(); - tasks.clear(); - clear_job_item_repo(); + clear_state(out); vector building_configs; World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); @@ -185,33 +206,11 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { if (event == SC_WORLD_UNLOADED) { DEBUG(status,out).print("world unloaded; clearing state for %s\n", plugin_name); - planned_buildings.clear(); - tasks.clear(); - clear_job_item_repo(); + clear_state(out); } return CR_OK; } -static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, - int nargs = 0, int nres = 0, - Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, - Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { - DEBUG(status).print("calling buildingplan lua function: '%s'\n", fn_name); - - CoreSuspender guard; - - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - - if (!out) - out = &Core::getInstance().getConsole(); - - return Lua::CallLuaModuleFunction(*out, L, "plugins.buildingplan", fn_name, - nargs, nres, - std::forward(args_lambda), - std::forward(res_lambda)); -} - static bool cycle_requested = false; static void do_cycle(color_ostream &out) { diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 7f8d996c8..6b1abb84e 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -64,15 +64,34 @@ function get_job_item(btype, subtype, custom, index) return obj end -local texpos_base = -1 +local BUTTON_START_PEN, BUTTON_END_PEN = nil, nil local reset_counts_flag = false local reset_inspector_flag = false function signal_reset() - texpos_base = dfhack.textures.getControlPanelTexposStart() + BUTTON_START_PEN = nil + BUTTON_END_PEN = nil reset_counts_flag = true reset_inspector_flag = true end +local to_pen = dfhack.pen.parse +local function get_button_start_pen() + if not BUTTON_START_PEN then + local texpos_base = dfhack.textures.getControlPanelTexposStart() + BUTTON_START_PEN = to_pen{ch='[', fg=COLOR_YELLOW, + tile=texpos_base > 0 and texpos_base + 13 or nil} + end + return BUTTON_START_PEN +end +local function get_button_end_pen() + if not BUTTON_END_PEN then + local texpos_base = dfhack.textures.getControlPanelTexposStart() + BUTTON_END_PEN = to_pen{ch=']', fg=COLOR_YELLOW, + tile=texpos_base > 0 and texpos_base + 15 or nil} + end + return BUTTON_END_PEN +end + -------------------------------- -- PlannerOverlay -- @@ -172,19 +191,20 @@ function ItemLine:init() widgets.Label{ frame={t=0, l=23}, text={ - {tile=2600}, - {gap=6, tile=2602}, - {tile=2600}, - {gap=1, tile=2602}, + {tile=get_button_start_pen}, + {gap=6, tile=get_button_end_pen}, + {tile=get_button_start_pen}, + {gap=1, tile=get_button_end_pen}, }, }, widgets.Label{ frame={t=0, l=0}, text={ - {width=21, text=function() return self:get_item_line_text() end}, - {gap=3, text='filter'}, - {gap=2, text='x'}, - {gap=3, text=function() return self.note end}, + {width=21, text=self:callback('get_item_line_text')}, + {gap=3, text='filter', pen=COLOR_GREEN}, + {gap=2, text='x', pen=COLOR_GREEN}, + {gap=3, text=function() return self.note end, + pen=function() return self.note_pen end}, }, }, } @@ -246,8 +266,13 @@ function ItemLine:get_item_line_text() self.available = self.available or countAvailableItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) - self.note = self.available >= quantity and - 'Can build now' or 'Will build later' + if self.available >= quantity then + self.note_pen = COLOR_GREEN + self.note = 'Available now' + else + self.note_pen = COLOR_YELLOW + self.note = 'Will link later' + end return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') end @@ -298,7 +323,7 @@ function PlannerOverlay:init() view_id='stairs_top_subtype', frame={t=4, l=4}, key='CUSTOM_R', - label='Top Stair Type: ', + label='Top Stair Type: ', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -444,7 +469,6 @@ function PlannerOverlay:render(dc) PlannerOverlay.super.render(self, dc) end -local to_pen = dfhack.pen.parse local GOOD_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} local BAD_PEN = to_pen{ch='X', fg=COLOR_RED, From daf691839fd73f4c40f3cc3cc4ae149d74b10e04 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Feb 2023 14:24:21 -0800 Subject: [PATCH 0684/2222] item selection, callback skeleton --- plugins/lua/buildingplan.lua | 139 +++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 30 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 6b1abb84e..b9be7e800 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -182,6 +182,10 @@ end ItemLine = defclass(ItemLine, widgets.Panel) ItemLine.ATTRS{ idx=DEFAULT_NIL, + is_selected_fn=DEFAULT_NIL, + on_select=DEFAULT_NIL, + on_filter=DEFAULT_NIL, + on_clear_filter=DEFAULT_NIL, } function ItemLine:init() @@ -189,16 +193,38 @@ function ItemLine:init() self.visible = function() return #get_cur_filters() >= self.idx end self:addviews{ widgets.Label{ - frame={t=0, l=23}, + frame={t=0, l=0}, + text='*', + auto_width=true, + visible=self.is_selected_fn, + }, + widgets.Label{ + frame={t=0, r=0}, + text='*', + auto_width=true, + visible=self.is_selected_fn, + on_click=self.on_filter, + }, + widgets.Label{ + frame={t=0, l=25}, text={ {tile=get_button_start_pen}, {gap=6, tile=get_button_end_pen}, + }, + auto_width=true, + on_click=function() self.on_filter(self.idx) end, + }, + widgets.Label{ + frame={t=0, l=33}, + text={ {tile=get_button_start_pen}, {gap=1, tile=get_button_end_pen}, }, + auto_width=true, + on_click=function() self.on_clear_filter(self.idx) end, }, widgets.Label{ - frame={t=0, l=0}, + frame={t=0, l=2}, text={ {width=21, text=self:callback('get_item_line_text')}, {gap=3, text='filter', pen=COLOR_GREEN}, @@ -215,6 +241,13 @@ function ItemLine:reset() self.available = nil end +function ItemLine:onInput(keys) + if keys._MOUSE_L_DOWN and self:getMousePos() then + self.on_select(self.idx) + end + return ItemLine.super.onInput(self, keys) +end + function get_desc(filter) local desc = 'Unknown' if filter.has_tool_use and filter.has_tool_use > -1 then @@ -301,6 +334,8 @@ PlannerOverlay.ATTRS{ } function PlannerOverlay:init() + self.selected = 1 + local main_panel = widgets.Panel{ view_id='main', frame={t=0, l=0, r=0, h=14}, @@ -308,6 +343,14 @@ function PlannerOverlay:init() frame_background=gui.CLEAR_PEN, } + local function make_is_selected_fn(idx) + return function() return self.selected == idx end + end + + local function on_select_fn(idx) + self.selected = idx + end + main_panel:addviews{ widgets.Label{ frame={}, @@ -315,10 +358,22 @@ function PlannerOverlay:init() text='No items required.', visible=function() return #get_cur_filters() == 0 end, }, - ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1}, - ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2}, - ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3}, - ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4}, + ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, + is_selected_fn=make_is_selected_fn(1), on_select=on_select_fn, + on_filter=self:callback('filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, + is_selected_fn=make_is_selected_fn(2), on_select=on_select_fn, + on_filter=self:callback('filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, + is_selected_fn=make_is_selected_fn(3), on_select=on_select_fn, + on_filter=self:callback('filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, + is_selected_fn=make_is_selected_fn(4), on_select=on_select_fn, + on_filter=self:callback('filter'), + on_clear_filter=self:callback('clear_filter')}, widgets.CycleHotkeyLabel{ view_id='stairs_top_subtype', frame={t=4, l=4}, @@ -354,32 +409,43 @@ function PlannerOverlay:init() }, visible=is_choosing_area, }, - widgets.CycleHotkeyLabel{ - view_id='safety', - frame={b=0, l=2}, - key='CUSTOM_G', - label='Safety: ', - options={ - {label='None', value='none'}, - {label='Magma', value='magma'}, - {label='Fire', value='fire'}, + widgets.Panel{ + visible=function() return #get_cur_filters() > 0 end, + subviews={ + widgets.HotkeyLabel{ + frame={b=1, l=0}, + key='SELECT', + label='Choose item', + on_activate=function() self:choose(self.selected) end, + enabled=function() + return (self.subviews['item'..self.selected].available or 0) > 0 + end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=21}, + key='CUSTOM_F', + label='Filter', + on_activate=function() self:filter(self.selected) end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=33}, + key='CUSTOM_X', + label='Clear filter', + on_activate=function() self:clear_filter(self.selected) end, + }, + widgets.CycleHotkeyLabel{ + view_id='safety', + frame={b=0, l=2}, + key='CUSTOM_G', + label='Safety: ', + options={ + {label='None', value='none'}, + {label='Magma', value='magma'}, + {label='Fire', value='fire'}, + }, + }, }, }, - widgets.HotkeyLabel{ - frame={b=1, l=0}, - key='SELECT', - label='Choose item', - }, - widgets.HotkeyLabel{ - frame={b=1, l=21}, - key='CUSTOM_F', - label='Filter', - }, - widgets.HotkeyLabel{ - frame={b=1, l=33}, - key='CUSTOM_X', - label='Clear filter', - }, } local error_panel = widgets.ResizingPanel{ @@ -418,6 +484,18 @@ function PlannerOverlay:reset() reset_counts_flag = false end +function PlannerOverlay:choose(idx) + print('choose', idx) +end + +function PlannerOverlay:filter(idx) + print('filter', idx) +end + +function PlannerOverlay:clear_filter(idx) + print('clear_filter', idx) +end + function PlannerOverlay:onInput(keys) if not is_plannable() then return false end if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then @@ -425,6 +503,7 @@ function PlannerOverlay:onInput(keys) uibs.selection_pos:clear() return true end + self.selected = 1 self:reset() return false end From 66a14ecc7471e147a98c07b4466233e997021166 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Feb 2023 19:16:45 -0800 Subject: [PATCH 0685/2222] get UI semi-finalized, prep for item choosing --- plugins/buildingplan/buildingplan.cpp | 27 ++++++-- plugins/buildingplan/buildingplan.h | 2 + plugins/buildingplan/buildingplan_cycle.cpp | 8 +-- plugins/lua/buildingplan.lua | 75 +++++++++++++-------- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 687f46705..2dbceba1f 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -319,12 +319,15 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { DEBUG(status,out).print("unexpected number of jobs: want 1, got %zu\n", bld->jobs.size()); return false; } + auto job_items = bld->jobs[0]->job_items; - int num_job_items = job_items.size(); - if (num_job_items < 1) { - DEBUG(status,out).print("unexpected number of job items: want >0, got %d\n", num_job_items); - return false; + if (isJobReady(out, job_items)) { + // all items are already attached + finalizeBuilding(out, bld); + return true; } + + int num_job_items = job_items.size(); int32_t id = bld->id; for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) { auto job_item = job_items[job_item_idx]; @@ -520,6 +523,19 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return count; } +static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering hasFilter\n"); + return false; +} + +static void setFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering setFilter\n"); +} + +static void clearFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering clearFilter\n"); +} + static bool validate_pb(color_ostream &out, df::building *bld, int index) { if (!isPlannedBuilding(out, bld) || bld->jobs.size() != 1) return false; @@ -616,6 +632,9 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), + DFHACK_LUA_FUNCTION(hasFilter), + DFHACK_LUA_FUNCTION(setFilter), + DFHACK_LUA_FUNCTION(clearFilter), DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), DFHACK_LUA_FUNCTION(makeTopPriority), diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 7fe2478aa..01c72e370 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -31,3 +31,5 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, df::job_item *job_item); bool itemPassesScreen(df::item * item); bool matchesFilters(df::item * item, df::job_item * job_item); +bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); +void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 6d5e4a405..069787f39 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -72,9 +72,9 @@ bool matchesFilters(df::item * item, df::job_item * job_item) { item->getType()); } -static bool isJobReady(color_ostream &out, df::job * job) { +bool isJobReady(color_ostream &out, const std::vector &jitems) { int needed_items = 0; - for (auto job_item : job->job_items) { needed_items += job_item->quantity; } + for (auto job_item : jitems) { needed_items += job_item->quantity; } if (needed_items) { DEBUG(cycle,out).print("building needs %d more item(s)\n", needed_items); return false; @@ -91,7 +91,7 @@ static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b) { // now all at 0, so there is no risk of having extra items attached. we don't // remove them to keep the "finalize with buildingplan active" path as similar // as possible to the "finalize with buildingplan disabled" path. -static void finalizeBuilding(color_ostream &out, df::building * bld) { +void finalizeBuilding(color_ostream &out, df::building *bld) { DEBUG(cycle,out).print("finalizing building %d\n", bld->id); auto job = bld->jobs[0]; @@ -194,7 +194,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, // be completed with the correct number of items. --job->job_items[filter_idx]->quantity; task_queue.pop_front(); - if (isJobReady(out, job)) { + if (isJobReady(out, job->job_items)) { finalizeBuilding(out, bld); planned_buildings.at(id).remove(out); } diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index b9be7e800..b6c13d5a7 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -198,13 +198,6 @@ function ItemLine:init() auto_width=true, visible=self.is_selected_fn, }, - widgets.Label{ - frame={t=0, r=0}, - text='*', - auto_width=true, - visible=self.is_selected_fn, - on_click=self.on_filter, - }, widgets.Label{ frame={t=0, l=25}, text={ @@ -228,7 +221,7 @@ function ItemLine:init() text={ {width=21, text=self:callback('get_item_line_text')}, {gap=3, text='filter', pen=COLOR_GREEN}, - {gap=2, text='x', pen=COLOR_GREEN}, + {gap=2, text='x', pen=self:callback('get_x_pen')}, {gap=3, text=function() return self.note end, pen=function() return self.note_pen end}, }, @@ -248,6 +241,10 @@ function ItemLine:onInput(keys) return ItemLine.super.onInput(self, keys) end +function ItemLine:get_x_pen() + return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx) and COLOR_GREEN or COLOR_GREY +end + function get_desc(filter) local desc = 'Unknown' if filter.has_tool_use and filter.has_tool_use > -1 then @@ -414,32 +411,52 @@ function PlannerOverlay:init() subviews={ widgets.HotkeyLabel{ frame={b=1, l=0}, - key='SELECT', - label='Choose item', - on_activate=function() self:choose(self.selected) end, - enabled=function() - return (self.subviews['item'..self.selected].available or 0) > 0 - end, + key='STRING_A042', + enabled=function() return #get_cur_filters() > 1 end, + on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=1}, + key='STRING_A047', + label='Prev/next item', + enabled=function() return #get_cur_filters() > 1 end, + on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, }, widgets.HotkeyLabel{ frame={b=1, l=21}, key='CUSTOM_F', - label='Filter', + label='Set filter', on_activate=function() self:filter(self.selected) end, }, widgets.HotkeyLabel{ - frame={b=1, l=33}, + frame={b=1, l=37}, key='CUSTOM_X', label='Clear filter', on_activate=function() self:clear_filter(self.selected) end, }, + widgets.CycleHotkeyLabel{ + view_id='choose', + frame={b=0, l=0}, + key='CUSTOM_I', + label='Choose exact items:', + options={{label='Yes', value=true}, + {label='No', value=false}}, + initial_option=false, + enabled=function() + for idx = 1,4 do + if (self.subviews['item'..idx].available or 0) > 0 then + return true + end + end + end, + }, widgets.CycleHotkeyLabel{ view_id='safety', - frame={b=0, l=2}, + frame={b=0, l=29}, key='CUSTOM_G', - label='Safety: ', + label='Building safety:', options={ - {label='None', value='none'}, + {label='Any', value='none'}, {label='Magma', value='magma'}, {label='Fire', value='fire'}, }, @@ -484,10 +501,6 @@ function PlannerOverlay:reset() reset_counts_flag = false end -function PlannerOverlay:choose(idx) - print('choose', idx) -end - function PlannerOverlay:filter(idx) print('filter', idx) end @@ -504,6 +517,8 @@ function PlannerOverlay:onInput(keys) return true end self.selected = 1 + self.subviews.choose:setOption(false) + self.subviews.safety:setOption('none') self:reset() return false end @@ -527,10 +542,6 @@ function PlannerOverlay:onInput(keys) if #get_cur_filters() == 0 then return false -- we don't add value; let the game place it end - self.subviews.item1:reduce_quantity() - self.subviews.item2:reduce_quantity() - self.subviews.item3:reduce_quantity() - self.subviews.item4:reduce_quantity() self:place_building() uibs.selection_pos:clear() return true @@ -607,6 +618,11 @@ function PlannerOverlay:place_building() max_y = min_y + height - 1 max_z = math.max(uibs.selection_pos.z, uibs.pos.z) end + if self.subviews.choose:getOptionValue() then + -- TODO + -- open dialog, showing all items (restricted to current filters) + -- select items (doesn't have to be all required items) + end local blds = {} local subtype = uibs.building_subtype for z=min_z,max_z do for y=min_y,max_y do for x=min_x,max_x do @@ -660,7 +676,12 @@ function PlannerOverlay:place_building() end table.insert(blds, bld) end end end + self.subviews.item1:reduce_quantity() + self.subviews.item2:reduce_quantity() + self.subviews.item3:reduce_quantity() + self.subviews.item4:reduce_quantity() for _,bld in ipairs(blds) do + -- TODO: attach chosen items and reduce job_item quantity addPlannedBuilding(bld) end scheduleCycle() From 4001ef381508eb1a04ae9ecb82b73a3d2d36a05c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 18 Feb 2023 01:09:54 -0800 Subject: [PATCH 0686/2222] implement selecting specific items --- plugins/buildingplan/buildingplan.cpp | 38 +++- plugins/lua/buildingplan.lua | 294 ++++++++++++++++++++++---- 2 files changed, 282 insertions(+), 50 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 2dbceba1f..d1e78b675 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -476,8 +476,8 @@ static void scheduleCycle(color_ostream &out) { cycle_requested = true; } -static int countAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering countAvailableItems\n"); +static int scanAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, + int32_t custom, int index, vector *item_ids = NULL) { DEBUG(status,out).print( "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); @@ -514,8 +514,11 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 for (auto vector_id : vector_ids) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { - if (itemPassesScreen(item) && matchesFilters(item, jitem)) + if (itemPassesScreen(item) && matchesFilters(item, jitem)) { + if (item_ids) + item_ids->emplace_back(item->id); ++count; + } } } @@ -523,6 +526,30 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return count; } +static int getAvailableItems(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering getAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + vector item_ids; + scanAvailableItems(*out, type, subtype, custom, index, &item_ids); + Lua::PushVector(L, item_ids); + return 1; +} + +static int countAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print( + "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + return scanAvailableItems(out, type, subtype, custom, index); +} + static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { DEBUG(status,out).print("entering hasFilter\n"); return false; @@ -640,3 +667,8 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(makeTopPriority), DFHACK_LUA_END }; + +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(getAvailableItems), + DFHACK_LUA_END +}; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index b6c13d5a7..46d27d626 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -20,6 +20,8 @@ local utils = require('utils') local widgets = require('gui.widgets') require('dfhack.buildings') +local uibs = df.global.buildreq + local function process_args(opts, args) if args[1] == 'help' then opts.help = true @@ -64,12 +66,40 @@ function get_job_item(btype, subtype, custom, index) return obj end -local BUTTON_START_PEN, BUTTON_END_PEN = nil, nil +local function get_cur_filters() + return dfhack.buildings.getFiltersByType({}, uibs.building_type, + uibs.building_subtype, uibs.custom_type) +end + +local function is_choosing_area() + return uibs.selection_pos.x >= 0 +end + +local function get_cur_area_dims() + if not is_choosing_area() then return 1, 1, 1 end + return math.abs(uibs.selection_pos.x - uibs.pos.x) + 1, + math.abs(uibs.selection_pos.y - uibs.pos.y) + 1, + math.abs(uibs.selection_pos.z - uibs.pos.z) + 1 +end + +local function get_quantity(filter) + local quantity = filter.quantity or 1 + local dimx, dimy, dimz = get_cur_area_dims() + if quantity < 1 then + quantity = (((dimx * dimy) // 4) + 1) * dimz + else + quantity = quantity * dimx * dimy * dimz + end + return quantity +end + +local BUTTON_START_PEN, BUTTON_END_PEN, SELECTED_ITEM_PEN = nil, nil, nil local reset_counts_flag = false local reset_inspector_flag = false function signal_reset() BUTTON_START_PEN = nil BUTTON_END_PEN = nil + SELECTED_ITEM_PEN = nil reset_counts_flag = true reset_inspector_flag = true end @@ -91,12 +121,169 @@ local function get_button_end_pen() end return BUTTON_END_PEN end +local function get_selected_item_pen() + if not SELECTED_ITEM_PEN then + local texpos_base = dfhack.textures.getControlPanelTexposStart() + SELECTED_ITEM_PEN = to_pen{ch='x', fg=COLOR_GREEN, + tile=texpos_base > 0 and texpos_base + 9 or nil} + end + return SELECTED_ITEM_PEN +end -------------------------------- --- PlannerOverlay +-- ItemSelection -- -local uibs = df.global.buildreq +ItemSelection = defclass(ItemSelection, widgets.Window) +ItemSelection.ATTRS{ + frame_title='Choose items', + frame={w=60, h=30, l=4, t=8}, + resizable=true, + resize_min={w=56, h=20}, + index=DEFAULT_NIL, + selected_set=DEFAULT_NIL, +} + +function ItemSelection:init() + local filter = get_cur_filters()[self.index] + self.quantity = get_quantity(filter) + self.num_selected = 0 + + self:addviews{ + widgets.Label{ + frame={t=0}, + text={ + get_desc(filter), + self.quantity == 1 and '' or 's', + NEWLINE, + ('Select up to %d items ('):format(self.quantity), + {text=function() return self.num_selected end}, + ' selected)', + }, + }, + widgets.FilteredList{ + frame={t=3, l=0, r=0, b=0}, + case_sensitive=false, + choices=self:get_choices(), + icon_width=2, + on_submit=self:callback('toggle_item'), + }, + } +end + +local function make_search_key(str) + local out = '' + for c in str:gmatch("[%w%s]") do + out = out .. c + end + return out +end + +function ItemSelection:get_choices() + local item_ids = getAvailableItems(uibs.building_type, + uibs.building_subtype, uibs.custom_type, self.index - 1) + local buckets, selected_buckets = {}, {} + for _,item_id in ipairs(item_ids) do + local item = df.item.find(item_id) + if not item then goto continue end + local desc = dfhack.items.getDescription(item, 0, true) + if buckets[desc] then + local bucket = buckets[desc] + table.insert(bucket.item_ids, item_id) + bucket.quantity = bucket.quantity + 1 + else + local entry = { + text=desc, + search_key=make_search_key(desc), + icon=self:callback('get_entry_icon', item_id), + item_ids={item_id}, + item_type=item:getType(), + item_subtype=item:getSubtype(), + quantity=1, + selected=false, + } + buckets[desc] = entry + end + ::continue:: + end + local selected_qty = 0 + for bucket in pairs(selected_buckets) do + for _,item_id in ipairs(bucket.item_ids) do + self.selected_set[item_id] = true + end + selected_qty = selected_qty + bucket.quantity + bucket.selected = true + if selected_qty >= self.quantity then break end + end + self.num_selected = selected_qty + local choices = {} + for _,choice in pairs(buckets) do + choice.text = ('(%d) %s'):format(choice.quantity, choice.text) + table.insert(choices, choice) + end + local function choice_sort(a, b) + return a.item_type < b.item_type or + (a.item_type == b.item_type and a.item_subtype < b.item_subtype) or + (a.item_type == b.item_type and a.item_subtype == b.item_subtype and a.search_key < b.search_key) + end + table.sort(choices, choice_sort) + return choices +end + +function ItemSelection:toggle_item(_, choice) + if choice.selected then + for _,item_id in ipairs(choice.item_ids) do + self.selected_set[item_id] = nil + end + self.num_selected = self.num_selected - choice.quantity + choice.selected = false + elseif self.quantity > self.num_selected then + for _,item_id in ipairs(choice.item_ids) do + self.selected_set[item_id] = true + end + self.num_selected = self.num_selected + choice.quantity + choice.selected = true + end +end + +function ItemSelection:get_entry_icon(item_id) + return self.selected_set[item_id] and get_selected_item_pen() or nil +end + +ItemSelectionScreen = defclass(ItemSelectionScreen, gui.ZScreen) +ItemSelectionScreen.ATTRS { + focus_path='buildingplan/itemselection', + force_pause=true, + pass_pause=false, + pass_movement_keys=true, + pass_mouse_clicks=false, + defocusable=false, + index=DEFAULT_NIL, + on_submit=DEFAULT_NIL, +} + +function ItemSelectionScreen:init() + self.selected_set = {} + + self:addviews{ + ItemSelection{ + index=self.index, + selected_set=self.selected_set, + } + } +end + +function ItemSelectionScreen:onDismiss() + local selected_items = {} + for item_id in pairs(self.selected_set) do + table.insert(selected_items, item_id) + end + self.on_submit(selected_items) +end + +-------------------------------- +-- PlannerOverlay +-- local function cur_building_has_no_area() if uibs.building_type == df.building_type.Construction then return false end @@ -107,22 +294,6 @@ local function cur_building_has_no_area() return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) end -local function is_choosing_area() - return uibs.selection_pos.x >= 0 -end - -local function get_cur_area_dims() - if not is_choosing_area() then return 1, 1, 1 end - return math.abs(uibs.selection_pos.x - uibs.pos.x) + 1, - math.abs(uibs.selection_pos.y - uibs.pos.y) + 1, - math.abs(uibs.selection_pos.z - uibs.pos.z) + 1 -end - -local function get_cur_filters() - return dfhack.buildings.getFiltersByType({}, uibs.building_type, - uibs.building_subtype, uibs.custom_type) -end - local function is_plannable() return get_cur_filters() and not (uibs.building_type == df.building_type.Construction @@ -276,17 +447,6 @@ function get_desc(filter) return desc end -local function get_quantity(filter) - local quantity = filter.quantity or 1 - local dimx, dimy, dimz = get_cur_area_dims() - if quantity < 1 then - quantity = (((dimx * dimy) // 4) + 1) * dimz - else - quantity = quantity * dimx * dimy * dimz - end - return quantity -end - function ItemLine:get_item_line_text() local idx = self.idx local filter = get_cur_filters()[idx] @@ -357,19 +517,19 @@ function PlannerOverlay:init() }, ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, is_selected_fn=make_is_selected_fn(1), on_select=on_select_fn, - on_filter=self:callback('filter'), + on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, is_selected_fn=make_is_selected_fn(2), on_select=on_select_fn, - on_filter=self:callback('filter'), + on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, is_selected_fn=make_is_selected_fn(3), on_select=on_select_fn, - on_filter=self:callback('filter'), + on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, is_selected_fn=make_is_selected_fn(4), on_select=on_select_fn, - on_filter=self:callback('filter'), + on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, widgets.CycleHotkeyLabel{ view_id='stairs_top_subtype', @@ -426,7 +586,7 @@ function PlannerOverlay:init() frame={b=1, l=21}, key='CUSTOM_F', label='Set filter', - on_activate=function() self:filter(self.selected) end, + on_activate=function() self:set_filter(self.selected) end, }, widgets.HotkeyLabel{ frame={b=1, l=37}, @@ -501,8 +661,8 @@ function PlannerOverlay:reset() reset_counts_flag = false end -function PlannerOverlay:filter(idx) - print('filter', idx) +function PlannerOverlay:set_filter(idx) + print('set_filter', idx) end function PlannerOverlay:clear_filter(idx) @@ -539,11 +699,34 @@ function PlannerOverlay:onInput(keys) local pos = dfhack.gui.getMousePos() if pos then if is_choosing_area() or cur_building_has_no_area() then - if #get_cur_filters() == 0 then + local num_filters = #get_cur_filters() + if num_filters == 0 then return false -- we don't add value; let the game place it end - self:place_building() - uibs.selection_pos:clear() + local choose = self.subviews.choose + if choose.enabled() and choose:getOptionValue() then + local chosen_items = {} + local pending = num_filters + for idx = num_filters,1,-1 do + chosen_items[idx] = {} + if (self.subviews['item'..idx].available or 0) > 0 then + ItemSelectionScreen{ + index=self.selected, + on_submit=function(items) + chosen_items[idx] = items + pending = pending - 1 + if pending == 0 then + self:place_building(chosen_items) + end + end, + }:show() + else + pending = pending - 1 + end + end + else + self:place_building() + end return true elseif not is_choosing_area() then return false @@ -589,7 +772,7 @@ function PlannerOverlay:onRenderFrame(dc, rect) guidm.renderMapOverlay(get_overlay_pen, bounds) end -function PlannerOverlay:place_building() +function PlannerOverlay:place_building(chosen_items) local direction = uibs.direction local width, height, depth = get_cur_area_dims() local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( @@ -618,11 +801,6 @@ function PlannerOverlay:place_building() max_y = min_y + height - 1 max_z = math.max(uibs.selection_pos.z, uibs.pos.z) end - if self.subviews.choose:getOptionValue() then - -- TODO - -- open dialog, showing all items (restricted to current filters) - -- select items (doesn't have to be all required items) - end local blds = {} local subtype = uibs.building_subtype for z=min_z,max_z do for y=min_y,max_y do for x=min_x,max_x do @@ -681,10 +859,32 @@ function PlannerOverlay:place_building() self.subviews.item3:reduce_quantity() self.subviews.item4:reduce_quantity() for _,bld in ipairs(blds) do - -- TODO: attach chosen items and reduce job_item quantity + -- attach chosen items and reduce job_item quantity + if chosen_items then + local job = bld.jobs[0] + local jitems = job.job_items + for idx=1,#get_cur_filters() do + local item_ids = chosen_items[idx] + while jitems[idx-1].quantity > 0 and #item_ids > 0 do + local item_id = item_ids[#item_ids] + local item = df.item.find(item_id) + if not item then + dfhack.printerr(('item no longer available: %d'):format(item_id)) + break + end + if not dfhack.job.attachJobItem(job, item, df.job_item_ref.T_role.Hauled, idx-1, -1) then + dfhack.printerr(('cannot attach item: %d'):format(item_id)) + break + end + jitems[idx-1].quantity = jitems[idx-1].quantity - 1 + item_ids[#item_ids] = nil + end + end + end addPlannedBuilding(bld) end scheduleCycle() + uibs.selection_pos:clear() end -------------------------------- From ee827f5ca19cc4c90860fe7de1c42f11d308373e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 18 Feb 2023 01:18:15 -0800 Subject: [PATCH 0687/2222] remember mouse pos from before item choosing --- plugins/lua/buildingplan.lua | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 46d27d626..65bf918ce 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -75,11 +75,12 @@ local function is_choosing_area() return uibs.selection_pos.x >= 0 end -local function get_cur_area_dims() +local function get_cur_area_dims(pos) if not is_choosing_area() then return 1, 1, 1 end - return math.abs(uibs.selection_pos.x - uibs.pos.x) + 1, - math.abs(uibs.selection_pos.y - uibs.pos.y) + 1, - math.abs(uibs.selection_pos.z - uibs.pos.z) + 1 + pos = pos or uibs.pos + return math.abs(uibs.selection_pos.x - pos.x) + 1, + math.abs(uibs.selection_pos.y - pos.y) + 1, + math.abs(uibs.selection_pos.z - pos.z) + 1 end local function get_quantity(filter) @@ -716,7 +717,7 @@ function PlannerOverlay:onInput(keys) chosen_items[idx] = items pending = pending - 1 if pending == 0 then - self:place_building(chosen_items) + self:place_building(pos, chosen_items) end end, }:show() @@ -725,7 +726,7 @@ function PlannerOverlay:onInput(keys) end end else - self:place_building() + self:place_building(pos) end return true elseif not is_choosing_area() then @@ -756,11 +757,12 @@ function PlannerOverlay:onRenderFrame(dc, rect) if not is_choosing_area() then return end + local pos = uibs.pos local bounds = { - x1 = math.min(uibs.selection_pos.x, uibs.pos.x), - x2 = math.max(uibs.selection_pos.x, uibs.pos.x), - y1 = math.min(uibs.selection_pos.y, uibs.pos.y), - y2 = math.max(uibs.selection_pos.y, uibs.pos.y), + x1 = math.min(uibs.selection_pos.x, pos.x), + x2 = math.max(uibs.selection_pos.x, pos.x), + y1 = math.min(uibs.selection_pos.y, pos.y), + y2 = math.max(uibs.selection_pos.y, pos.y), } local pen = #uibs.errors > 0 and BAD_PEN or GOOD_PEN @@ -772,18 +774,18 @@ function PlannerOverlay:onRenderFrame(dc, rect) guidm.renderMapOverlay(get_overlay_pen, bounds) end -function PlannerOverlay:place_building(chosen_items) +function PlannerOverlay:place_building(pos, chosen_items) local direction = uibs.direction - local width, height, depth = get_cur_area_dims() + local width, height, depth = get_cur_area_dims(pos) local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( width, height, uibs.building_type, uibs.building_subtype, uibs.custom_type, direction) -- get the upper-left corner of the building/area at min z-level local has_selection = is_choosing_area() local start_pos = xyz2pos( - has_selection and math.min(uibs.selection_pos.x, uibs.pos.x) or uibs.pos.x - adjusted_width//2, - has_selection and math.min(uibs.selection_pos.y, uibs.pos.y) or uibs.pos.y - adjusted_height//2, - has_selection and math.min(uibs.selection_pos.z, uibs.pos.z) or uibs.pos.z + has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, + has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, + has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z ) if uibs.building_type == df.building_type.ScrewPump then if direction == df.screw_pump_direction.FromSouth then @@ -799,7 +801,7 @@ function PlannerOverlay:place_building(chosen_items) and (width > 1 or height > 1 or depth > 1) then max_x = min_x + width - 1 max_y = min_y + height - 1 - max_z = math.max(uibs.selection_pos.z, uibs.pos.z) + max_z = math.max(uibs.selection_pos.z, pos.z) end local blds = {} local subtype = uibs.building_subtype From 2477a239724258d632cb41dab54ce6cac0cc6843 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 18 Feb 2023 01:25:07 -0800 Subject: [PATCH 0688/2222] pass correct job_item index for item selection --- plugins/lua/buildingplan.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 65bf918ce..327f9f685 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -712,7 +712,7 @@ function PlannerOverlay:onInput(keys) chosen_items[idx] = {} if (self.subviews['item'..idx].available or 0) > 0 then ItemSelectionScreen{ - index=self.selected, + index=idx, on_submit=function(items) chosen_items[idx] = items pending = pending - 1 From daa812b21eab7fa86e9d74c919f6c9e27d304e50 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 18 Feb 2023 01:25:24 -0800 Subject: [PATCH 0689/2222] pluralize plural plurals --- plugins/buildingplan/buildingplan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index d1e78b675..ad21ada94 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -407,7 +407,7 @@ static void printStatus(color_ostream &out) { out.print("Waiting for %d item(s) to be produced for %zd building(s):\n", total, planned_buildings.size()); for (auto &count : counts) - out.print(" %3d %s\n", count.second, count.first.c_str()); + out.print(" %3d %s%s\n", count.second, count.first.c_str(), count.second == 1 ? "" : "s"); } else { out.print("Currently no planned buildings\n"); } From a0785bded456708b49d6a174d7d01c3034c4bf8e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 00:57:30 -0800 Subject: [PATCH 0690/2222] implement heat safety --- plugins/buildingplan/buildingplan.cpp | 72 +++++++++++++++++---- plugins/buildingplan/buildingplan.h | 10 ++- plugins/buildingplan/buildingplan_cycle.cpp | 15 +++-- plugins/buildingplan/plannedbuilding.cpp | 6 +- plugins/buildingplan/plannedbuilding.h | 6 +- plugins/lua/buildingplan.lua | 32 +++++---- 6 files changed, 111 insertions(+), 30 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index ad21ada94..0a05c4ed5 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -74,6 +74,7 @@ struct BuildingTypeKeyHash { static PersistentDataItem config; // for use in counting available materials for the UI static unordered_map, BuildingTypeKeyHash> job_item_repo; +static unordered_map cur_heat_safety; // building id -> PlannedBuilding static unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) @@ -456,13 +457,20 @@ static bool isPlannedBuilding(color_ostream &out, df::building *bld) { return bld && planned_buildings.count(bld->id) > 0; } +static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { + if (cur_heat_safety.count(key)) + return cur_heat_safety.at(key); + return HEAT_SAFETY_ANY; +} + static bool addPlannedBuilding(color_ostream &out, df::building *bld) { DEBUG(status,out).print("entering addPlannedBuilding\n"); if (!bld || planned_buildings.count(bld->id) || !isPlannableBuilding(out, bld->getType(), bld->getSubtype(), bld->getCustomType())) return false; - PlannedBuilding pb(out, bld); + BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); + PlannedBuilding pb(out, bld, get_heat_safety_filter(key)); return registerPlannedBuilding(out, pb); } @@ -482,6 +490,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); BuildingTypeKey key(type, subtype, custom); + HeatSafety heat = get_heat_safety_filter(key); auto &job_items = job_item_repo[key]; if (index >= (int)job_items.size()) { for (int i = job_items.size(); i <= index; ++i) { @@ -514,7 +523,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ for (auto vector_id : vector_ids) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { - if (itemPassesScreen(item) && matchesFilters(item, jitem)) { + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat)) { if (item_ids) item_ids->emplace_back(item->id); ++count; @@ -550,17 +559,56 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return scanAvailableItems(out, type, subtype, custom, index); } -static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering hasFilter\n"); +static bool hasMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering hasMaterialFilter\n"); return false; } -static void setFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering setFilter\n"); +static void setMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index, string filter) { + DEBUG(status,out).print("entering setMaterialFilter\n"); + call_buildingplan_lua(&out, "signal_reset"); +} + +static int getMaterialFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering getMaterialFilter building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + vector filter; + Lua::PushVector(L, filter); + return 1; +} + +static void setHeatSafetyFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int heat) { + DEBUG(status,out).print("entering setHeatSafetyFilter\n"); + BuildingTypeKey key(type, subtype, custom); + if (heat == HEAT_SAFETY_FIRE || heat == HEAT_SAFETY_MAGMA) + cur_heat_safety[key] = (HeatSafety)heat; + else + cur_heat_safety.erase(key); + call_buildingplan_lua(&out, "signal_reset"); } -static void clearFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering clearFilter\n"); +static int getHeatSafetyFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + DEBUG(status,*out).print( + "entering getHeatSafetyFilter building_type=%d subtype=%d custom=%d\n", + type, subtype, custom); + BuildingTypeKey key(type, subtype, custom); + HeatSafety heat = get_heat_safety_filter(key); + Lua::Push(L, heat); + return 1; } static bool validate_pb(color_ostream &out, df::building *bld, int index) { @@ -659,9 +707,9 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), - DFHACK_LUA_FUNCTION(hasFilter), - DFHACK_LUA_FUNCTION(setFilter), - DFHACK_LUA_FUNCTION(clearFilter), + DFHACK_LUA_FUNCTION(hasMaterialFilter), + DFHACK_LUA_FUNCTION(setMaterialFilter), + DFHACK_LUA_FUNCTION(setHeatSafetyFilter), DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), DFHACK_LUA_FUNCTION(makeTopPriority), @@ -670,5 +718,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getAvailableItems), + DFHACK_LUA_COMMAND(getMaterialFilter), + DFHACK_LUA_COMMAND(getHeatSafetyFilter), DFHACK_LUA_END }; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 01c72e370..787987586 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -2,6 +2,7 @@ #include "modules/Persistence.h" +#include "df/building.h" #include "df/job_item.h" #include "df/job_item_vector_id.h" @@ -21,6 +22,13 @@ enum ConfigValues { enum BuildingConfigValues { BLD_CONFIG_ID = 0, + BLD_CONFIG_HEAT = 1, +}; + +enum HeatSafety { + HEAT_SAFETY_ANY = 0, + HEAT_SAFETY_FIRE = 1, + HEAT_SAFETY_MAGMA = 2, }; int get_config_val(DFHack::PersistentDataItem &c, int index); @@ -30,6 +38,6 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, df::job_item *job_item); bool itemPassesScreen(df::item * item); -bool matchesFilters(df::item * item, df::job_item * job_item); +bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 069787f39..703bab9b0 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -46,7 +46,7 @@ bool itemPassesScreen(df::item * item) { && !item->isAssignedToStockpile(); } -bool matchesFilters(df::item * item, df::job_item * job_item) { +bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) return false; @@ -65,10 +65,17 @@ bool matchesFilters(df::item * item, df::job_item * job_item) { && !item->hasToolUse(job_item->has_tool_use)) return false; + df::job_item jitem = *job_item; + if (heat == HEAT_SAFETY_MAGMA) { + jitem.flags2.bits.magma_safe = true; + jitem.flags2.bits.fire_safe = false; + } else if (heat == HEAT_SAFETY_FIRE && !jitem.flags2.bits.magma_safe) + jitem.flags2.bits.fire_safe = true; + return Job::isSuitableItem( - job_item, item->getType(), item->getSubtype()) + &jitem, item->getType(), item->getSubtype()) && Job::isSuitableMaterial( - job_item, item->getMaterial(), item->getMaterialIndex(), + &jitem, item->getMaterial(), item->getMaterialIndex(), item->getType()); } @@ -173,7 +180,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto id = task.first; auto job = bld->jobs[0]; auto filter_idx = task.second; - if (matchesFilters(item, job->job_items[filter_idx]) + if (matchesFilters(item, job->job_items[filter_idx], planned_buildings.at(id).heat_safety) && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) { diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index f4f3564b7..eb55a95b4 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -62,11 +62,12 @@ static string serialize(const vector> &vector_ids return join_strings("|", joined); } -PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building) - : id(building->id), vector_ids(get_vector_ids(out, id)) { +PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafety heat) + : id(bld->id), vector_ids(get_vector_ids(out, id)), heat_safety(heat) { DEBUG(status,out).print("creating persistent data for building %d\n", id); bld_config = World::AddPersistentData(BLD_CONFIG_KEY); set_config_val(bld_config, BLD_CONFIG_ID, id); + set_config_val(bld_config, BLD_CONFIG_HEAT, heat_safety); bld_config.val() = serialize(vector_ids); DEBUG(status,out).print("serialized state for building %d: %s\n", id, bld_config.val().c_str()); } @@ -74,6 +75,7 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *building) PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_config) : id(get_config_val(bld_config, BLD_CONFIG_ID)), vector_ids(deserialize(out, bld_config)), + heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), bld_config(bld_config) { } // Ensure the building still exists and is in a valid state. It can disappear diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 592f0e4b3..0a67e0edc 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -1,5 +1,7 @@ #pragma once +#include "buildingplan.h" + #include "Core.h" #include "modules/Persistence.h" @@ -14,7 +16,9 @@ public: // job_item idx -> list of vectors the task is linked to const std::vector> vector_ids; - PlannedBuilding(DFHack::color_ostream &out, df::building *building); + const HeatSafety heat_safety; + + PlannedBuilding(DFHack::color_ostream &out, df::building *bld, HeatSafety heat); PlannedBuilding(DFHack::color_ostream &out, DFHack::PersistentDataItem &bld_config); void remove(DFHack::color_ostream &out); diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 327f9f685..e4aee907b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -251,14 +251,18 @@ function ItemSelection:get_entry_icon(item_id) return self.selected_set[item_id] and get_selected_item_pen() or nil end -ItemSelectionScreen = defclass(ItemSelectionScreen, gui.ZScreen) -ItemSelectionScreen.ATTRS { - focus_path='buildingplan/itemselection', +BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) +BuildingplanScreen.ATTRS { force_pause=true, pass_pause=false, pass_movement_keys=true, pass_mouse_clicks=false, defocusable=false, +} + +ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) +ItemSelectionScreen.ATTRS { + focus_path='buildingplan/itemselection', index=DEFAULT_NIL, on_submit=DEFAULT_NIL, } @@ -414,7 +418,8 @@ function ItemLine:onInput(keys) end function ItemLine:get_x_pen() - return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx) and COLOR_GREEN or COLOR_GREY + return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx) and + COLOR_GREEN or COLOR_GREY end function get_desc(filter) @@ -599,7 +604,7 @@ function PlannerOverlay:init() view_id='choose', frame={b=0, l=0}, key='CUSTOM_I', - label='Choose exact items:', + label='Choose from items:', options={{label='Yes', value=true}, {label='No', value=false}}, initial_option=false, @@ -617,10 +622,13 @@ function PlannerOverlay:init() key='CUSTOM_G', label='Building safety:', options={ - {label='Any', value='none'}, - {label='Magma', value='magma'}, - {label='Fire', value='fire'}, + {label='Any', value=0}, + {label='Magma', value=2, pen=COLOR_RED}, + {label='Fire', value=1, pen=COLOR_LIGHTRED}, }, + on_change=function(heat) + setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) + end, }, }, }, @@ -663,11 +671,11 @@ function PlannerOverlay:reset() end function PlannerOverlay:set_filter(idx) - print('set_filter', idx) + print('TODO: set_filter', idx) end function PlannerOverlay:clear_filter(idx) - print('clear_filter', idx) + setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx, "") end function PlannerOverlay:onInput(keys) @@ -679,8 +687,8 @@ function PlannerOverlay:onInput(keys) end self.selected = 1 self.subviews.choose:setOption(false) - self.subviews.safety:setOption('none') self:reset() + reset_counts_flag = true return false end if PlannerOverlay.super.onInput(self, keys) then @@ -753,6 +761,8 @@ function PlannerOverlay:onRenderFrame(dc, rect) if reset_counts_flag then self:reset() + self.subviews.safety:setOption(getHeatSafetyFilter( + uibs.building_type, uibs.building_subtype, uibs.custom_type)) end if not is_choosing_area() then return end From 273183e864ac2b19bb2741fddc5cc21b8bf03b15 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 01:58:17 -0800 Subject: [PATCH 0691/2222] allow cancel when choosing items --- plugins/lua/buildingplan.lua | 65 ++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index e4aee907b..7283d805f 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -135,24 +135,29 @@ end -- ItemSelection -- +local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} +local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} + ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection.ATTRS{ frame_title='Choose items', - frame={w=60, h=30, l=4, t=8}, + frame={w=56, h=20, l=4, t=8}, + draggable=false, resizable=true, - resize_min={w=56, h=20}, index=DEFAULT_NIL, - selected_set=DEFAULT_NIL, + on_submit=DEFAULT_NIL, + on_cancel=DEFAULT_NIL, } function ItemSelection:init() local filter = get_cur_filters()[self.index] self.quantity = get_quantity(filter) self.num_selected = 0 + self.selected_set = {} self:addviews{ widgets.Label{ - frame={t=0}, + frame={t=0, l=0, r=10}, text={ get_desc(filter), self.quantity == 1 and '' or 's', @@ -162,6 +167,17 @@ function ItemSelection:init() ' selected)', }, }, + widgets.Label{ + frame={r=0, w=9, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' ', NEWLINE, + ' Build ', NEWLINE, + ' ', + }, + on_click=self:callback('submit'), + }, widgets.FilteredList{ frame={t=3, l=0, r=0, b=0}, case_sensitive=false, @@ -251,6 +267,22 @@ function ItemSelection:get_entry_icon(item_id) return self.selected_set[item_id] and get_selected_item_pen() or nil end +function ItemSelection:submit() + local selected_items = {} + for item_id in pairs(self.selected_set) do + table.insert(selected_items, item_id) + end + self.on_submit(selected_items) +end + +function ItemSelection:onInput(keys) + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + self.on_cancel() + return true + end + return ItemSelection.super.onInput(self, keys) +end + BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) BuildingplanScreen.ATTRS { force_pause=true, @@ -265,27 +297,19 @@ ItemSelectionScreen.ATTRS { focus_path='buildingplan/itemselection', index=DEFAULT_NIL, on_submit=DEFAULT_NIL, + on_cancel=DEFAULT_NIL, } function ItemSelectionScreen:init() - self.selected_set = {} - self:addviews{ ItemSelection{ index=self.index, - selected_set=self.selected_set, + on_submit=self.on_submit, + on_cancel=self.on_cancel, } } end -function ItemSelectionScreen:onDismiss() - local selected_items = {} - for item_id in pairs(self.selected_set) do - table.insert(selected_items, item_id) - end - self.on_submit(selected_items) -end - -------------------------------- -- PlannerOverlay -- @@ -714,20 +738,27 @@ function PlannerOverlay:onInput(keys) end local choose = self.subviews.choose if choose.enabled() and choose:getOptionValue() then - local chosen_items = {} + local chosen_items, active_screens = {}, {} local pending = num_filters for idx = num_filters,1,-1 do chosen_items[idx] = {} if (self.subviews['item'..idx].available or 0) > 0 then - ItemSelectionScreen{ + active_screens[idx] = ItemSelectionScreen{ index=idx, on_submit=function(items) chosen_items[idx] = items + active_screens[idx]:dismiss() + active_screens[idx] = nil pending = pending - 1 if pending == 0 then self:place_building(pos, chosen_items) end end, + on_cancel=function() + for i,scr in pairs(active_screens) do + scr:dismiss() + end + end, }:show() else pending = pending - 1 From e9555c29be83c3b21278aea082dffdb3f9fb2eeb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 02:03:39 -0800 Subject: [PATCH 0692/2222] initialize heat safety option to 'Any' --- plugins/lua/buildingplan.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 7283d805f..fcd7b83fd 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -650,6 +650,7 @@ function PlannerOverlay:init() {label='Magma', value=2, pen=COLOR_RED}, {label='Fire', value=1, pen=COLOR_LIGHTRED}, }, + initial_option=0, on_change=function(heat) setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) end, From 348ac55f4cb5160122f073814de88dfb579e28d6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 21:17:03 -0800 Subject: [PATCH 0693/2222] allow singleton selection for items --- plugins/lua/buildingplan.lua | 238 +++++++++++++++++++++++++---------- 1 file changed, 171 insertions(+), 67 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index fcd7b83fd..cb5926723 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -131,6 +131,15 @@ local function get_selected_item_pen() return SELECTED_ITEM_PEN end +BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) +BuildingplanScreen.ATTRS { + force_pause=true, + pass_pause=false, + pass_movement_keys=true, + pass_mouse_clicks=false, + defocusable=false, +} + -------------------------------- -- ItemSelection -- @@ -154,15 +163,16 @@ function ItemSelection:init() self.quantity = get_quantity(filter) self.num_selected = 0 self.selected_set = {} + local plural = self.quantity == 1 and '' or 's' self:addviews{ widgets.Label{ frame={t=0, l=0, r=10}, text={ get_desc(filter), - self.quantity == 1 and '' or 's', + plural, NEWLINE, - ('Select up to %d items ('):format(self.quantity), + ('Select up to %d item%s ('):format(self.quantity, plural), {text=function() return self.num_selected end}, ' selected)', }, @@ -179,11 +189,52 @@ function ItemSelection:init() on_click=self:callback('submit'), }, widgets.FilteredList{ - frame={t=3, l=0, r=0, b=0}, + view_id='flist', + frame={t=3, l=0, r=0, b=4}, case_sensitive=false, choices=self:get_choices(), icon_width=2, - on_submit=self:callback('toggle_item'), + on_submit=self:callback('toggle_group'), + }, + widgets.HotkeyLabel{ + frame={l=0, b=2}, + key='SELECT', + label='Use all/none selected', + auto_width=true, + on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.HotkeyLabel{ + frame={l=32, b=2}, + key='LEAVESCREEN', + label='Cancel build', + auto_width=true, + on_activate=function() self.on_cancel() end, + }, + widgets.HotkeyLabel{ + frame={l=0, b=1}, + key='KEYBOARD_CURSOR_RIGHT_FAST', + key_sep=' : ', + label='Use one selected', + auto_width=true, + on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=6, b=1, w=5}, + text_pen=COLOR_LIGHTGREEN, + text='Right', + }, + widgets.HotkeyLabel{ + frame={l=0, b=0}, + key='KEYBOARD_CURSOR_LEFT_FAST', + key_sep=' : ', + label='Use one fewer selected', + auto_width=true, + on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=6, b=0, w=4}, + text_pen=COLOR_LIGHTGREEN, + text='Left', }, } end @@ -199,67 +250,79 @@ end function ItemSelection:get_choices() local item_ids = getAvailableItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index - 1) - local buckets, selected_buckets = {}, {} + local buckets = {} for _,item_id in ipairs(item_ids) do local item = df.item.find(item_id) if not item then goto continue end local desc = dfhack.items.getDescription(item, 0, true) if buckets[desc] then local bucket = buckets[desc] - table.insert(bucket.item_ids, item_id) - bucket.quantity = bucket.quantity + 1 + table.insert(bucket.data.item_ids, item_id) + bucket.data.quantity = bucket.data.quantity + 1 else local entry = { - text=desc, search_key=make_search_key(desc), icon=self:callback('get_entry_icon', item_id), - item_ids={item_id}, - item_type=item:getType(), - item_subtype=item:getSubtype(), - quantity=1, - selected=false, + data={ + item_ids={item_id}, + item_type=item:getType(), + item_subtype=item:getSubtype(), + quantity=1, + quality=item:getQuality(), + selected=0, + }, } buckets[desc] = entry end ::continue:: end - local selected_qty = 0 - for bucket in pairs(selected_buckets) do - for _,item_id in ipairs(bucket.item_ids) do - self.selected_set[item_id] = true - end - selected_qty = selected_qty + bucket.quantity - bucket.selected = true - if selected_qty >= self.quantity then break end - end - self.num_selected = selected_qty local choices = {} - for _,choice in pairs(buckets) do - choice.text = ('(%d) %s'):format(choice.quantity, choice.text) + for desc,choice in pairs(buckets) do + local data = choice.data + choice.text = { + {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, + {gap=2, text=desc}, + } table.insert(choices, choice) end local function choice_sort(a, b) - return a.item_type < b.item_type or - (a.item_type == b.item_type and a.item_subtype < b.item_subtype) or - (a.item_type == b.item_type and a.item_subtype == b.item_subtype and a.search_key < b.search_key) + local ad, bd = a.data, b.data + return ad.item_type < bd.item_type or + (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) end table.sort(choices, choice_sort) return choices end -function ItemSelection:toggle_item(_, choice) - if choice.selected then - for _,item_id in ipairs(choice.item_ids) do - self.selected_set[item_id] = nil - end - self.num_selected = self.num_selected - choice.quantity - choice.selected = false - elseif self.quantity > self.num_selected then - for _,item_id in ipairs(choice.item_ids) do - self.selected_set[item_id] = true - end - self.num_selected = self.num_selected + choice.quantity - choice.selected = true +function ItemSelection:increment_group(idx, choice) + local data = choice.data + if self.quantity <= self.num_selected then return false end + if data.selected >= data.quantity then return false end + data.selected = data.selected + 1 + self.num_selected = self.num_selected + 1 + local item_id = data.item_ids[data.selected] + self.selected_set[item_id] = true + return true +end + +function ItemSelection:decrement_group(idx, choice) + local data = choice.data + if data.selected <= 0 then return false end + local item_id = data.item_ids[data.selected] + self.selected_set[item_id] = nil + self.num_selected = self.num_selected - 1 + data.selected = data.selected - 1 + return true +end + +function ItemSelection:toggle_group(idx, choice) + local data = choice.data + if data.selected > 0 then + while self:decrement_group(idx, choice) do end + else + while self:increment_group(idx, choice) do end end end @@ -279,19 +342,26 @@ function ItemSelection:onInput(keys) if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then self.on_cancel() return true + elseif keys._MOUSE_L_DOWN then + local list = self.subviews.flist.list + local idx = list:getIdxUnderMouse() + if idx then + list:setSelected(idx) + local modstate = dfhack.internal.getModstate() + if modstate & 2 > 0 then -- ctrl + local choice = list:getChoices()[idx] + if modstate & 1 > 0 then -- shift + self:decrement_group(idx, choice) + else + self:increment_group(idx, choice) + end + return true + end + end end return ItemSelection.super.onInput(self, keys) end -BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) -BuildingplanScreen.ATTRS { - force_pause=true, - pass_pause=false, - pass_movement_keys=true, - pass_mouse_clicks=false, - defocusable=false, -} - ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) ItemSelectionScreen.ATTRS { focus_path='buildingplan/itemselection', @@ -311,7 +381,48 @@ function ItemSelectionScreen:init() end -------------------------------- --- PlannerOverlay +-- FilterSelection +-- + +-- returns whether the items matched by the specified filter can have a quality +-- rating. This also conveniently indicates whether an item can be decorated. +local function can_be_improved(idx) + local filter = get_cur_filters()[idx] + if filter.flags2 and filter.flags2.building_material then + return false; + end + return filter.item_type ~= df.item_type.WOOD and + filter.item_type ~= df.item_type.BLOCKS and + filter.item_type ~= df.item_type.BAR and + filter.item_type ~= df.item_type.BOULDER +end + +FilterSelection = defclass(FilterSelection, widgets.Window) +FilterSelection.ATTRS{ + frame_title='Choose filters', + frame={w=60, h=40, l=4, t=8}, + draggable=false, + resizable=true, + index=DEFAULT_NIL, +} + +function FilterSelection:init() +end + +FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) +FilterSelectionScreen.ATTRS { + focus_path='buildingplan/filterselection', + index=DEFAULT_NIL, +} + +function FilterSelectionScreen:init() + self:addviews{ + FilterSelection{index=self.index} + } +end + +-------------------------------- +-- ItemLine -- local function cur_building_has_no_area() @@ -512,6 +623,10 @@ local function get_placement_errors() return out end +-------------------------------- +-- PlannerOverlay +-- + PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) PlannerOverlay.ATTRS{ default_pos={x=5,y=9}, @@ -932,7 +1047,7 @@ function PlannerOverlay:place_building(pos, chosen_items) end -------------------------------- --- InspectorOverlay +-- InspectorLine -- local function get_building_filters() @@ -981,6 +1096,10 @@ function InspectorLine:reset() self.status = nil end +-------------------------------- +-- InspectorOverlay +-- + InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) InspectorOverlay.ATTRS{ default_pos={x=-41,y=14}, @@ -1053,19 +1172,4 @@ OVERLAY_WIDGETS = { inspector=InspectorOverlay, } --- returns whether the items matched by the specified filter can have a quality --- rating. This also conveniently indicates whether an item can be decorated. --- does not need the core suspended --- reverse_idx is 0-based and is expected to be counted from the *last* filter -function item_can_be_improved(btype, subtype, custom, reverse_idx) - local filter = get_filter(btype, subtype, custom, reverse_idx) - if filter.flags2 and filter.flags2.building_material then - return false; - end - return filter.item_type ~= df.item_type.WOOD and - filter.item_type ~= df.item_type.BLOCKS and - filter.item_type ~= df.item_type.BAR and - filter.item_type ~= df.item_type.BOULDER -end - return _ENV From 69e9da2e79c3305ca97a6c5aa7f721e8a3ba5856 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Feb 2023 23:28:57 -0800 Subject: [PATCH 0694/2222] keep target area higlighted while choosing items --- plugins/buildingplan/buildingplan.cpp | 1 + plugins/lua/buildingplan.lua | 216 ++++++++++++++++---------- 2 files changed, 135 insertions(+), 82 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 0a05c4ed5..6c16b043e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -171,6 +171,7 @@ static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "signal_reset"); + call_buildingplan_lua(&out, "reload_cursors"); planned_buildings.clear(); tasks.clear(); for (auto &entry : job_item_repo) { diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index cb5926723..b24f5893a 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -75,17 +75,18 @@ local function is_choosing_area() return uibs.selection_pos.x >= 0 end -local function get_cur_area_dims(pos) - if not is_choosing_area() then return 1, 1, 1 end - pos = pos or uibs.pos - return math.abs(uibs.selection_pos.x - pos.x) + 1, - math.abs(uibs.selection_pos.y - pos.y) + 1, - math.abs(uibs.selection_pos.z - pos.z) + 1 +local function get_cur_area_dims(placement_data) + if not placement_data and not is_choosing_area() then return 1, 1, 1 end + local selection_pos = placement_data and placement_data.p1 or uibs.selection_pos + local pos = placement_data and placement_data.p2 or uibs.pos + return math.abs(selection_pos.x - pos.x) + 1, + math.abs(selection_pos.y - pos.y) + 1, + math.abs(selection_pos.z - pos.z) + 1 end -local function get_quantity(filter) +local function get_quantity(filter, placement_data) local quantity = filter.quantity or 1 - local dimx, dimy, dimz = get_cur_area_dims() + local dimx, dimy, dimz = get_cur_area_dims(placement_data) if quantity < 1 then quantity = (((dimx * dimy) // 4) + 1) * dimz else @@ -151,16 +152,16 @@ ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection.ATTRS{ frame_title='Choose items', frame={w=56, h=20, l=4, t=8}, - draggable=false, resizable=true, index=DEFAULT_NIL, + placement_data=DEFAULT_NIL, on_submit=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } function ItemSelection:init() local filter = get_cur_filters()[self.index] - self.quantity = get_quantity(filter) + self.quantity = get_quantity(filter, self.placement_data) self.num_selected = 0 self.selected_set = {} local plural = self.quantity == 1 and '' or 's' @@ -364,8 +365,9 @@ end ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) ItemSelectionScreen.ATTRS { - focus_path='buildingplan/itemselection', + focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/itemselection', index=DEFAULT_NIL, + placement_data=DEFAULT_NIL, on_submit=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } @@ -374,6 +376,7 @@ function ItemSelectionScreen:init() self:addviews{ ItemSelection{ index=self.index, + placement_data=self.placement_data, on_submit=self.on_submit, on_cancel=self.on_cancel, } @@ -401,7 +404,6 @@ FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ frame_title='Choose filters', frame={w=60, h=40, l=4, t=8}, - draggable=false, resizable=true, index=DEFAULT_NIL, } @@ -411,7 +413,7 @@ end FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) FilterSelectionScreen.ATTRS { - focus_path='buildingplan/filterselection', + focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', index=DEFAULT_NIL, } @@ -818,6 +820,73 @@ function PlannerOverlay:clear_filter(idx) setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx, "") end +local function get_placement_data() + local pos = uibs.pos + local direction = uibs.direction + local width, height, depth = get_cur_area_dims() + local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( + width, height, uibs.building_type, uibs.building_subtype, + uibs.custom_type, direction) + -- get the upper-left corner of the building/area at min z-level + local has_selection = is_choosing_area() + local start_pos = xyz2pos( + has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, + has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, + has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z + ) + if uibs.building_type == df.building_type.ScrewPump then + if direction == df.screw_pump_direction.FromSouth then + start_pos.y = start_pos.y + 1 + elseif direction == df.screw_pump_direction.FromEast then + start_pos.x = start_pos.x + 1 + end + end + local min_x, max_x = start_pos.x, start_pos.x + local min_y, max_y = start_pos.y, start_pos.y + local min_z, max_z = start_pos.z, start_pos.z + if adjusted_width == 1 and adjusted_height == 1 + and (width > 1 or height > 1 or depth > 1) then + max_x = min_x + width - 1 + max_y = min_y + height - 1 + max_z = math.max(uibs.selection_pos.z, pos.z) + end + return { + p1=xyz2pos(min_x, min_y, min_z), + p2=xyz2pos(max_x, max_y, max_z), + width=adjusted_width, + height=adjusted_height + } +end + +function PlannerOverlay:save_placement() + self.saved_placement = get_placement_data() + if (uibs.selection_pos:isValid()) then + self.saved_selection_pos_valid = true + self.saved_selection_pos = copyall(uibs.selection_pos) + self.saved_pos = copyall(uibs.pos) + uibs.selection_pos:clear() + else + self.saved_selection_pos = copyall(self.saved_placement.p1) + self.saved_pos = copyall(self.saved_placement.p2) + self.saved_pos.x = self.saved_pos.x + self.saved_placement.width - 1 + self.saved_pos.y = self.saved_pos.y + self.saved_placement.height - 1 + end +end + +function PlannerOverlay:restore_placement() + if self.saved_selection_pos_valid then + uibs.selection_pos = self.saved_selection_pos + self.saved_selection_pos_valid = nil + else + uibs.selection_pos:clear() + end + self.saved_selection_pos = nil + self.saved_pos = nil + local placement_data = self.saved_placement + self.saved_placement = nil + return placement_data +end + function PlannerOverlay:onInput(keys) if not is_plannable() then return false end if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then @@ -845,8 +914,7 @@ function PlannerOverlay:onInput(keys) return true end if #uibs.errors > 0 then return true end - local pos = dfhack.gui.getMousePos() - if pos then + if dfhack.gui.getMousePos() then if is_choosing_area() or cur_building_has_no_area() then local num_filters = #get_cur_filters() if num_filters == 0 then @@ -854,6 +922,7 @@ function PlannerOverlay:onInput(keys) end local choose = self.subviews.choose if choose.enabled() and choose:getOptionValue() then + self:save_placement() local chosen_items, active_screens = {}, {} local pending = num_filters for idx = num_filters,1,-1 do @@ -861,19 +930,21 @@ function PlannerOverlay:onInput(keys) if (self.subviews['item'..idx].available or 0) > 0 then active_screens[idx] = ItemSelectionScreen{ index=idx, + placement_data=self.saved_placement, on_submit=function(items) chosen_items[idx] = items active_screens[idx]:dismiss() active_screens[idx] = nil pending = pending - 1 if pending == 0 then - self:place_building(pos, chosen_items) + self:place_building(self:restore_placement(), chosen_items) end end, on_cancel=function() for i,scr in pairs(active_screens) do scr:dismiss() end + self:restore_placement() end, }:show() else @@ -881,7 +952,7 @@ function PlannerOverlay:onInput(keys) end end else - self:place_building(pos) + self:place_building(get_placement_data()) end return true elseif not is_choosing_area() then @@ -898,10 +969,12 @@ function PlannerOverlay:render(dc) PlannerOverlay.super.render(self, dc) end -local GOOD_PEN = to_pen{ch='o', fg=COLOR_GREEN, - tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} -local BAD_PEN = to_pen{ch='X', fg=COLOR_RED, - tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} +local GOOD_PEN, BAD_PEN +function reload_cursors() + GOOD_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} + BAD_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} +end +reload_cursors() function PlannerOverlay:onRenderFrame(dc, rect) PlannerOverlay.super.onRenderFrame(self, dc, rect) @@ -912,17 +985,18 @@ function PlannerOverlay:onRenderFrame(dc, rect) uibs.building_type, uibs.building_subtype, uibs.custom_type)) end - if not is_choosing_area() then return end + local selection_pos = self.saved_selection_pos or uibs.selection_pos + if not selection_pos or selection_pos.x < 0 then return end - local pos = uibs.pos + local pos = self.saved_pos or uibs.pos local bounds = { - x1 = math.min(uibs.selection_pos.x, pos.x), - x2 = math.max(uibs.selection_pos.x, pos.x), - y1 = math.min(uibs.selection_pos.y, pos.y), - y2 = math.max(uibs.selection_pos.y, pos.y), + x1 = math.min(selection_pos.x, pos.x), + x2 = math.max(selection_pos.x, pos.x), + y1 = math.min(selection_pos.y, pos.y), + y2 = math.max(selection_pos.y, pos.y), } - local pen = #uibs.errors > 0 and BAD_PEN or GOOD_PEN + local pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN local function get_overlay_pen(pos) return pen @@ -931,69 +1005,47 @@ function PlannerOverlay:onRenderFrame(dc, rect) guidm.renderMapOverlay(get_overlay_pen, bounds) end -function PlannerOverlay:place_building(pos, chosen_items) - local direction = uibs.direction - local width, height, depth = get_cur_area_dims(pos) - local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( - width, height, uibs.building_type, uibs.building_subtype, - uibs.custom_type, direction) - -- get the upper-left corner of the building/area at min z-level - local has_selection = is_choosing_area() - local start_pos = xyz2pos( - has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, - has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, - has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z - ) - if uibs.building_type == df.building_type.ScrewPump then - if direction == df.screw_pump_direction.FromSouth then - start_pos.y = start_pos.y + 1 - elseif direction == df.screw_pump_direction.FromEast then - start_pos.x = start_pos.x + 1 +function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) + local subtype = uibs.building_subtype + if pos.z == corner1.z then + local opt = self.subviews.stairs_bottom_subtype:getOptionValue() + if opt == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape ~= df.tiletype_shape.STAIR_DOWN then + subtype = df.construction_type.UpStair + end + else + subtype = opt + end + elseif pos.z == corner2.z then + local opt = self.subviews.stairs_top_subtype:getOptionValue() + if opt == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape ~= df.tiletype_shape.STAIR_UP then + subtype = df.construction_type.DownStair + end + else + subtype = opt end end - local min_x, max_x = start_pos.x, start_pos.x - local min_y, max_y = start_pos.y, start_pos.y - local min_z, max_z = start_pos.z, start_pos.z - if adjusted_width == 1 and adjusted_height == 1 - and (width > 1 or height > 1 or depth > 1) then - max_x = min_x + width - 1 - max_y = min_y + height - 1 - max_z = math.max(uibs.selection_pos.z, pos.z) - end + return subtype +end + +function PlannerOverlay:place_building(placement_data, chosen_items) + local p1, p2 = placement_data.p1, placement_data.p2 local blds = {} local subtype = uibs.building_subtype - for z=min_z,max_z do for y=min_y,max_y do for x=min_x,max_x do + for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do local pos = xyz2pos(x, y, z) if is_stairs() then - if z == min_z then - subtype = self.subviews.stairs_bottom_subtype:getOptionValue() - if subtype == 'auto' then - local tt = dfhack.maps.getTileType(pos) - local shape = df.tiletype.attrs[tt].shape - if shape == df.tiletype_shape.STAIR_DOWN then - subtype = uibs.building_subtype - else - subtype = df.construction_type.UpStair - end - end - elseif z == max_z then - subtype = self.subviews.stairs_top_subtype:getOptionValue() - if subtype == 'auto' then - local tt = dfhack.maps.getTileType(pos) - local shape = df.tiletype.attrs[tt].shape - if shape == df.tiletype_shape.STAIR_UP then - subtype = uibs.building_subtype - else - subtype = df.construction_type.DownStair - end - end - else - subtype = uibs.building_subtype - end + subtype = self:get_stairs_subtype(pos, p1, p2) end local bld, err = dfhack.buildings.constructBuilding{pos=pos, type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, - width=adjusted_width, height=adjusted_height, direction=direction} + width=placement_data.width, height=placement_data.height, + direction=uibs.direction} if err then for _,b in ipairs(blds) do dfhack.buildings.deconstruct(b) From f09eeee864bfa8234a5fd95cdabd4c51733d5ed5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Feb 2023 00:11:05 -0800 Subject: [PATCH 0695/2222] only enable clear filter hotkey when a filter is set --- plugins/lua/buildingplan.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index b24f5893a..308afd797 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -555,7 +555,7 @@ function ItemLine:onInput(keys) end function ItemLine:get_x_pen() - return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx) and + return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx - 1) and COLOR_GREEN or COLOR_GREY end @@ -707,11 +707,13 @@ function PlannerOverlay:init() text={ 'Selected area: ', {text=function() - return ('%dx%dx%d'):format(get_cur_area_dims()) + return ('%dx%dx%d'):format(get_cur_area_dims(self.saved_placement)) end }, }, - visible=is_choosing_area, + visible=function() + return not cur_building_has_no_area() and (self.saved_placement or is_choosing_area()) + end, }, widgets.Panel{ visible=function() return #get_cur_filters() > 0 end, @@ -719,6 +721,7 @@ function PlannerOverlay:init() widgets.HotkeyLabel{ frame={b=1, l=0}, key='STRING_A042', + auto_width=true, enabled=function() return #get_cur_filters() > 1 end, on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, }, @@ -726,6 +729,7 @@ function PlannerOverlay:init() frame={b=1, l=1}, key='STRING_A047', label='Prev/next item', + auto_width=true, enabled=function() return #get_cur_filters() > 1 end, on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, }, @@ -733,17 +737,22 @@ function PlannerOverlay:init() frame={b=1, l=21}, key='CUSTOM_F', label='Set filter', + auto_width=true, on_activate=function() self:set_filter(self.selected) end, }, widgets.HotkeyLabel{ frame={b=1, l=37}, key='CUSTOM_X', label='Clear filter', + auto_width=true, on_activate=function() self:clear_filter(self.selected) end, + enabled=function() + return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) + end }, widgets.CycleHotkeyLabel{ view_id='choose', - frame={b=0, l=0}, + frame={b=0, l=0, w=25}, key='CUSTOM_I', label='Choose from items:', options={{label='Yes', value=true}, @@ -759,7 +768,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel{ view_id='safety', - frame={b=0, l=29}, + frame={b=0, l=29, w=25}, key='CUSTOM_G', label='Building safety:', options={ @@ -817,7 +826,7 @@ function PlannerOverlay:set_filter(idx) end function PlannerOverlay:clear_filter(idx) - setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx, "") + setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1, "") end local function get_placement_data() From 1957ad4cdfeb353a4c69780e591c966578c8951e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Feb 2023 15:25:36 -0800 Subject: [PATCH 0696/2222] move the filter window a bit to the side, can pause --- plugins/lua/buildingplan.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 308afd797..5b048ff7c 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -134,8 +134,6 @@ end BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) BuildingplanScreen.ATTRS { - force_pause=true, - pass_pause=false, pass_movement_keys=true, pass_mouse_clicks=false, defocusable=false, @@ -366,6 +364,8 @@ end ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) ItemSelectionScreen.ATTRS { focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/itemselection', + force_pause=true, + pass_pause=false, index=DEFAULT_NIL, placement_data=DEFAULT_NIL, on_submit=DEFAULT_NIL, @@ -403,7 +403,7 @@ end FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ frame_title='Choose filters', - frame={w=60, h=40, l=4, t=8}, + frame={w=60, h=40, l=30, t=8}, resizable=true, index=DEFAULT_NIL, } @@ -822,7 +822,7 @@ function PlannerOverlay:reset() end function PlannerOverlay:set_filter(idx) - print('TODO: set_filter', idx) + FilterSelectionScreen{index=idx}:show() end function PlannerOverlay:clear_filter(idx) From 4f2d86f50af4a85f6c1062f5f55d5e6920e47b6c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Feb 2023 13:04:53 -0800 Subject: [PATCH 0697/2222] implement hollow area placement for constructions --- plugins/buildingplan/buildingplan.cpp | 6 ++--- plugins/lua/buildingplan.lua | 37 ++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 6c16b043e..828ae4954 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -560,8 +560,8 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 return scanAvailableItems(out, type, subtype, custom, index); } -static bool hasMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering hasMaterialFilter\n"); +static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + DEBUG(status,out).print("entering hasFilter\n"); return false; } @@ -708,7 +708,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), - DFHACK_LUA_FUNCTION(hasMaterialFilter), + DFHACK_LUA_FUNCTION(hasFilter), DFHACK_LUA_FUNCTION(setMaterialFilter), DFHACK_LUA_FUNCTION(setHeatSafetyFilter), DFHACK_LUA_FUNCTION(getDescString), diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 5b048ff7c..fe74ca7c0 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -146,6 +146,8 @@ BuildingplanScreen.ATTRS { local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} +local recently_selected = {} + ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection.ATTRS{ frame_title='Choose items', @@ -442,8 +444,12 @@ local function is_plannable() and uibs.building_subtype == df.construction_type.TrackNSEW) end -local function is_stairs() +local function is_construction() return uibs.building_type == df.building_type.Construction +end + +local function is_stairs() + return is_construction and uibs.building_subtype == df.construction_type.UpDownStair end @@ -555,7 +561,7 @@ function ItemLine:onInput(keys) end function ItemLine:get_x_pen() - return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx - 1) and + return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx - 1) and COLOR_GREEN or COLOR_GREY end @@ -678,6 +684,17 @@ function PlannerOverlay:init() is_selected_fn=make_is_selected_fn(4), on_select=on_select_fn, on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, + widgets.CycleHotkeyLabel{ + view_id='hollow', + frame={t=3, l=4}, + key='CUSTOM_H', + label='Hollow area:', + visible=is_construction, + options={ + {label='No', value=false}, + {label='Yes', value=true}, + }, + }, widgets.CycleHotkeyLabel{ view_id='stairs_top_subtype', frame={t=4, l=4}, @@ -747,7 +764,7 @@ function PlannerOverlay:init() auto_width=true, on_activate=function() self:clear_filter(self.selected) end, enabled=function() - return hasMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) + return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) end }, widgets.CycleHotkeyLabel{ @@ -904,6 +921,7 @@ function PlannerOverlay:onInput(keys) return true end self.selected = 1 + self.subviews.hollow:setOption(false) self.subviews.choose:setOption(false) self:reset() reset_counts_flag = true @@ -1005,10 +1023,16 @@ function PlannerOverlay:onRenderFrame(dc, rect) y2 = math.max(selection_pos.y, pos.y), } + local hollow = self.subviews.hollow:getOptionValue() local pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN local function get_overlay_pen(pos) - return pen + if not hollow then return pen end + if pos.x == bounds.x1 or pos.x == bounds.x2 or + pos.y == bounds.y1 or pos.y == bounds.y2 then + return pen + end + return gui.TRANSPARENT_PEN end guidm.renderMapOverlay(get_overlay_pen, bounds) @@ -1045,8 +1069,12 @@ end function PlannerOverlay:place_building(placement_data, chosen_items) local p1, p2 = placement_data.p1, placement_data.p2 local blds = {} + local hollow = self.subviews.hollow:getOptionValue() local subtype = uibs.building_subtype for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do + if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then + goto continue + end local pos = xyz2pos(x, y, z) if is_stairs() then subtype = self:get_stairs_subtype(pos, p1, p2) @@ -1073,6 +1101,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) if k == 'speed' then bld.speed = uibs.speed end end table.insert(blds, bld) + ::continue:: end end end self.subviews.item1:reduce_quantity() self.subviews.item2:reduce_quantity() From c52b2c27c8c5b67b5d8c5ebf68bc5c03cd9f2e1e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Feb 2023 15:05:06 -0800 Subject: [PATCH 0698/2222] implement automaterial in buildingplan --- plugins/lua/buildingplan.lua | 133 +++++++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index fe74ca7c0..370ab5159 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -146,7 +146,40 @@ BuildingplanScreen.ATTRS { local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} -local recently_selected = {} +-- map of building type -> {set=set of recently used, list=list of recently used} +-- most recent entries are at the *end* of the list +local recently_used = {} + +local function sort_by_type(a, b) + local ad, bd = a.data, b.data + return ad.item_type < bd.item_type or + (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) +end + +local function sort_by_recency(a, b) + local tracker = recently_used[uibs.building_type] + if not tracker then return sort_by_type(a, b) end + local recent_a, recent_b = tracker.set[a.search_key], tracker.set[b.search_key] + -- if they're both in the set, return the one with the greater index, + -- indicating more recent + if recent_a and recent_b then return recent_a > recent_b end + if recent_a and not recent_b then return true end + if not recent_a and recent_b then return false end + return sort_by_type(a, b) +end + +local function sort_by_name(a, b) + return a.search_key < b.search_key or + (a.search_key == b.search_key and sort_by_type(a, b)) +end + +local function sort_by_quantity(a, b) + local ad, bd = a.data, b.data + return ad.quantity > bd.quantity or + (ad.quantity == bd.quantity and sort_by_type(a, b)) +end ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection.ATTRS{ @@ -193,53 +226,80 @@ function ItemSelection:init() view_id='flist', frame={t=3, l=0, r=0, b=4}, case_sensitive=false, - choices=self:get_choices(), + choices=self:get_choices(sort_by_recency), icon_width=2, on_submit=self:callback('toggle_group'), }, - widgets.HotkeyLabel{ + widgets.CycleHotkeyLabel{ frame={l=0, b=2}, + key='CUSTOM_CTRL_X', + label='Sort by:', + options={ + {label='Recently used', value=sort_by_recency}, + {label='Name', value=sort_by_name}, + {label='Amount', value=sort_by_quantity}, + }, + on_change=self:callback('on_sort'), + }, + widgets.HotkeyLabel{ + frame={l=0, b=1}, key='SELECT', - label='Use all/none selected', + label='Use all/none', auto_width=true, on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, }, widgets.HotkeyLabel{ - frame={l=32, b=2}, + frame={l=22, b=1}, + key='CUSTOM_CTRL_D', + label='Build', + auto_width=true, + on_activate=self:callback('submit'), + }, + widgets.HotkeyLabel{ + frame={l=38, b=1}, key='LEAVESCREEN', - label='Cancel build', + label='Go back', auto_width=true, - on_activate=function() self.on_cancel() end, + on_activate=self:callback('on_cancel'), }, widgets.HotkeyLabel{ - frame={l=0, b=1}, + frame={l=0, b=0}, key='KEYBOARD_CURSOR_RIGHT_FAST', key_sep=' : ', - label='Use one selected', + label='Use one', auto_width=true, on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, }, widgets.Label{ - frame={l=6, b=1, w=5}, + frame={l=6, b=0, w=5}, text_pen=COLOR_LIGHTGREEN, text='Right', }, widgets.HotkeyLabel{ - frame={l=0, b=0}, + frame={l=23, b=0}, key='KEYBOARD_CURSOR_LEFT_FAST', key_sep=' : ', - label='Use one fewer selected', + label='Use one fewer', auto_width=true, on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, }, widgets.Label{ - frame={l=6, b=0, w=4}, + frame={l=29, b=0, w=4}, text_pen=COLOR_LIGHTGREEN, text='Left', }, } end +-- resort and restore selection +function ItemSelection:on_sort(sort_fn) + local flist = self.subviews.flist + local saved_filter = flist:getFilter() + flist:setFilter('') + flist:setChoices(self:get_choices(sort_fn), flist:getSelected()) + flist:setFilter(saved_filter) +end + local function make_search_key(str) local out = '' for c in str:gmatch("[%w%s]") do @@ -248,7 +308,7 @@ local function make_search_key(str) return out end -function ItemSelection:get_choices() +function ItemSelection:get_choices(sort_fn) local item_ids = getAvailableItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index - 1) local buckets = {} @@ -286,14 +346,7 @@ function ItemSelection:get_choices() } table.insert(choices, choice) end - local function choice_sort(a, b) - local ad, bd = a.data, b.data - return ad.item_type < bd.item_type or - (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or - (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or - (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) - end - table.sort(choices, choice_sort) + table.sort(choices, sort_fn) return choices end @@ -331,11 +384,47 @@ function ItemSelection:get_entry_icon(item_id) return self.selected_set[item_id] and get_selected_item_pen() or nil end +local function track_recently_used(choices) + -- use same set for all subtypes + local tracker = ensure_key(recently_used, uibs.building_type) + for _,choice in ipairs(choices) do + local data = choice.data + if data.selected <= 0 then goto continue end + local key = choice.search_key + local recent_set = ensure_key(tracker, 'set') + local recent_list = ensure_key(tracker, 'list') + if recent_set[key] then + if recent_list[#recent_list] ~= key then + for i,v in ipairs(recent_list) do + if v == key then + table.remove(recent_list, i) + table.insert(recent_list, key) + break + end + end + tracker.set = utils.invert(recent_list) + end + else + -- only keep most recent 10 + if #recent_list >= 10 then + -- remove least recently used from list and set + recent_set[table.remove(recent_list, 1)] = nil + end + table.insert(recent_list, key) + recent_set[key] = #recent_list + end + ::continue:: + end +end + function ItemSelection:submit() local selected_items = {} for item_id in pairs(self.selected_set) do table.insert(selected_items, item_id) end + if #selected_items > 0 then + track_recently_used(self.subviews.flist:getChoices()) + end self.on_submit(selected_items) end From a0798178a6380422bc1b6ee12678f0140d6a633f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Feb 2023 15:42:30 -0800 Subject: [PATCH 0699/2222] ensure item quantity is correct when hollow --- plugins/lua/buildingplan.lua | 50 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 370ab5159..9d8eb463b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -84,15 +84,16 @@ local function get_cur_area_dims(placement_data) math.abs(selection_pos.z - pos.z) + 1 end -local function get_quantity(filter, placement_data) +local function get_quantity(filter, hollow, placement_data) local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims(placement_data) if quantity < 1 then - quantity = (((dimx * dimy) // 4) + 1) * dimz - else - quantity = quantity * dimx * dimy * dimz + return (((dimx * dimy) // 4) + 1) * dimz + end + if hollow and dimx > 2 and dimy > 2 then + return quantity * (2*dimx + 2*dimy - 4) * dimz end - return quantity + return quantity * dimx * dimy * dimz end local BUTTON_START_PEN, BUTTON_END_PEN, SELECTED_ITEM_PEN = nil, nil, nil @@ -187,14 +188,13 @@ ItemSelection.ATTRS{ frame={w=56, h=20, l=4, t=8}, resizable=true, index=DEFAULT_NIL, - placement_data=DEFAULT_NIL, + quantity=DEFAULT_NIL, on_submit=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } function ItemSelection:init() local filter = get_cur_filters()[self.index] - self.quantity = get_quantity(filter, self.placement_data) self.num_selected = 0 self.selected_set = {} local plural = self.quantity == 1 and '' or 's' @@ -458,7 +458,7 @@ ItemSelectionScreen.ATTRS { force_pause=true, pass_pause=false, index=DEFAULT_NIL, - placement_data=DEFAULT_NIL, + quantity=DEFAULT_NIL, on_submit=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } @@ -467,7 +467,7 @@ function ItemSelectionScreen:init() self:addviews{ ItemSelection{ index=self.index, - placement_data=self.placement_data, + quantity=self.quantity, on_submit=self.on_submit, on_cancel=self.on_cancel, } @@ -591,6 +591,7 @@ ItemLine = defclass(ItemLine, widgets.Panel) ItemLine.ATTRS{ idx=DEFAULT_NIL, is_selected_fn=DEFAULT_NIL, + is_hollow_fn=DEFAULT_NIL, on_select=DEFAULT_NIL, on_filter=DEFAULT_NIL, on_clear_filter=DEFAULT_NIL, @@ -688,7 +689,7 @@ end function ItemLine:get_item_line_text() local idx = self.idx local filter = get_cur_filters()[idx] - local quantity = get_quantity(filter) + local quantity = get_quantity(filter, self.is_hollow_fn()) self.desc = self.desc or get_desc(filter) @@ -708,7 +709,7 @@ end function ItemLine:reduce_quantity() if not self.available then return end local filter = get_cur_filters()[self.idx] - self.available = math.max(0, self.available - get_quantity(filter)) + self.available = math.max(0, self.available - get_quantity(filter, self.is_hollow_fn())) end local function get_placement_errors() @@ -750,6 +751,10 @@ function PlannerOverlay:init() self.selected = idx end + local function is_hollow_fn() + return self.subviews.hollow:getOptionValue() + end + main_panel:addviews{ widgets.Label{ frame={}, @@ -758,20 +763,20 @@ function PlannerOverlay:init() visible=function() return #get_cur_filters() == 0 end, }, ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, - is_selected_fn=make_is_selected_fn(1), on_select=on_select_fn, - on_filter=self:callback('set_filter'), + is_selected_fn=make_is_selected_fn(1), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, - is_selected_fn=make_is_selected_fn(2), on_select=on_select_fn, - on_filter=self:callback('set_filter'), + is_selected_fn=make_is_selected_fn(2), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, - is_selected_fn=make_is_selected_fn(3), on_select=on_select_fn, - on_filter=self:callback('set_filter'), + is_selected_fn=make_is_selected_fn(3), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, - is_selected_fn=make_is_selected_fn(4), on_select=on_select_fn, - on_filter=self:callback('set_filter'), + is_selected_fn=make_is_selected_fn(4), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), on_clear_filter=self:callback('clear_filter')}, widgets.CycleHotkeyLabel{ view_id='hollow', @@ -1032,13 +1037,15 @@ function PlannerOverlay:onInput(keys) if #uibs.errors > 0 then return true end if dfhack.gui.getMousePos() then if is_choosing_area() or cur_building_has_no_area() then - local num_filters = #get_cur_filters() + local filters = get_cur_filters() + local num_filters = #filters if num_filters == 0 then return false -- we don't add value; let the game place it end local choose = self.subviews.choose if choose.enabled() and choose:getOptionValue() then self:save_placement() + local is_hollow = self.subviews.hollow:getOptionValue() local chosen_items, active_screens = {}, {} local pending = num_filters for idx = num_filters,1,-1 do @@ -1046,7 +1053,8 @@ function PlannerOverlay:onInput(keys) if (self.subviews['item'..idx].available or 0) > 0 then active_screens[idx] = ItemSelectionScreen{ index=idx, - placement_data=self.saved_placement, + quantity=get_quantity(filters[idx], is_hollow, + self.saved_placement), on_submit=function(items) chosen_items[idx] = items active_screens[idx]:dismiss() From 097e955796269e07b17e24edfb81875d1857cb59 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Feb 2023 18:05:15 -0800 Subject: [PATCH 0700/2222] infrastructure for item filtering --- plugins/buildingplan/buildingplan.cpp | 174 ++++++++++-------- plugins/buildingplan/buildingplan.h | 4 +- plugins/buildingplan/buildingplan_cycle.cpp | 9 +- plugins/buildingplan/itemfilter.cpp | 189 ++++++++++++++++++++ plugins/buildingplan/itemfilter.h | 38 ++++ plugins/buildingplan/plannedbuilding.cpp | 56 ++++-- plugins/buildingplan/plannedbuilding.h | 5 +- plugins/lua/buildingplan.lua | 6 +- 8 files changed, 393 insertions(+), 88 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 828ae4954..7359d842c 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -73,8 +73,9 @@ struct BuildingTypeKeyHash { static PersistentDataItem config; // for use in counting available materials for the UI -static unordered_map, BuildingTypeKeyHash> job_item_repo; +static unordered_map, BuildingTypeKeyHash> job_item_cache; static unordered_map cur_heat_safety; +static unordered_map, BuildingTypeKeyHash> cur_item_filters; // building id -> PlannedBuilding static unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) @@ -96,6 +97,87 @@ void PlannedBuilding::remove(color_ostream &out) { static const int32_t CYCLE_TICKS = 600; // twice per game day static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle +static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(status).print("calling buildingplan lua function: '%s'\n", fn_name); + + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.buildingplan", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); +} + +static int get_num_filters(color_ostream &out, BuildingTypeKey key) { + int num_filters = 0; + if (!call_buildingplan_lua(&out, "get_num_filters", 3, 1, + [&](lua_State *L) { + Lua::Push(L, std::get<0>(key)); + Lua::Push(L, std::get<1>(key)); + Lua::Push(L, std::get<2>(key)); + }, + [&](lua_State *L) { + num_filters = lua_tonumber(L, -1); + })) { + return 0; + } + return num_filters; +} + +static vector & get_job_items(color_ostream &out, BuildingTypeKey key) { + if (job_item_cache.count(key)) + return job_item_cache[key]; + const int num_filters = get_num_filters(out, key); + auto &jitems = job_item_cache[key]; + for (int index = 0; index < num_filters; ++index) { + bool failed = false; + if (!call_buildingplan_lua(&out, "get_job_item", 4, 1, + [&](lua_State *L) { + Lua::Push(L, std::get<0>(key)); + Lua::Push(L, std::get<1>(key)); + Lua::Push(L, std::get<2>(key)); + Lua::Push(L, index+1); + }, + [&](lua_State *L) { + df::job_item *jitem = Lua::GetDFObject(L, -1); + DEBUG(status,out).print("retrieving job_item for (%d, %d, %d) index=%d: %p\n", + std::get<0>(key), std::get<1>(key), std::get<2>(key), index, jitem); + if (!jitem) + failed = true; + else + jitems.emplace_back(jitem); + }) || failed) { + jitems.clear(); + break; + } + } + return jitems; +} + +static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { + if (cur_heat_safety.count(key)) + return cur_heat_safety.at(key); + return HEAT_SAFETY_ANY; +} + +static vector & get_item_filters(color_ostream &out, const BuildingTypeKey &key) { + if (cur_item_filters.count(key)) + return cur_item_filters[key]; + + vector &filters = cur_item_filters[key]; + filters.resize(get_job_items(out, key).size()); + return filters; +} + static command_result do_command(color_ostream &out, vector ¶meters); void buildingplan_cycle(color_ostream &out, Tasks &tasks, unordered_map &planned_buildings); @@ -149,37 +231,19 @@ static void validate_config(color_ostream &out, bool verbose = false) { set_config_bool(config, CONFIG_BARS, false); } -static bool call_buildingplan_lua(color_ostream *out, const char *fn_name, - int nargs = 0, int nres = 0, - Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, - Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { - DEBUG(status).print("calling buildingplan lua function: '%s'\n", fn_name); - - CoreSuspender guard; - - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - - if (!out) - out = &Core::getInstance().getConsole(); - - return Lua::CallLuaModuleFunction(*out, L, "plugins.buildingplan", fn_name, - nargs, nres, - std::forward(args_lambda), - std::forward(res_lambda)); -} - static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "signal_reset"); call_buildingplan_lua(&out, "reload_cursors"); planned_buildings.clear(); tasks.clear(); - for (auto &entry : job_item_repo) { + cur_heat_safety.clear(); + cur_item_filters.clear(); + for (auto &entry : job_item_cache ) { for (auto &jitem : entry.second) { delete jitem; } } - job_item_repo.clear(); + job_item_cache.clear(); } DFhackCExport command_result plugin_load_data (color_ostream &out) { @@ -199,7 +263,15 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { const size_t num_building_configs = building_configs.size(); for (size_t idx = 0; idx < num_building_configs; ++idx) { PlannedBuilding pb(out, building_configs[idx]); - registerPlannedBuilding(out, pb); + df::building *bld = df::building::find(pb.id); + if (!bld) { + WARN(status).print("cannot find building %d; halting load\n", pb.id); + } + BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); + if (pb.item_filters.size() != get_item_filters(out, key).size()) + WARN(status).print("loaded state for building %d doesn't match world\n", pb.id); + else + registerPlannedBuilding(out, pb); } return CR_OK; @@ -438,30 +510,12 @@ static bool setSetting(color_ostream &out, string name, bool value) { static bool isPlannableBuilding(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom) { DEBUG(status,out).print("entering isPlannableBuilding\n"); - int num_filters = 0; - if (!call_buildingplan_lua(&out, "get_num_filters", 3, 1, - [&](lua_State *L) { - Lua::Push(L, type); - Lua::Push(L, subtype); - Lua::Push(L, custom); - }, - [&](lua_State *L) { - num_filters = lua_tonumber(L, -1); - })) { - return false; - } - return num_filters >= 1; + return get_num_filters(out, BuildingTypeKey(type, subtype, custom)) >= 1; } static bool isPlannedBuilding(color_ostream &out, df::building *bld) { TRACE(status,out).print("entering isPlannedBuilding\n"); - return bld && planned_buildings.count(bld->id) > 0; -} - -static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { - if (cur_heat_safety.count(key)) - return cur_heat_safety.at(key); - return HEAT_SAFETY_ANY; + return bld && planned_buildings.count(bld->id); } static bool addPlannedBuilding(color_ostream &out, df::building *bld) { @@ -471,7 +525,7 @@ static bool addPlannedBuilding(color_ostream &out, df::building *bld) { bld->getCustomType())) return false; BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); - PlannedBuilding pb(out, bld, get_heat_safety_filter(key)); + PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key)); return registerPlannedBuilding(out, pb); } @@ -492,30 +546,10 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ type, subtype, custom, index); BuildingTypeKey key(type, subtype, custom); HeatSafety heat = get_heat_safety_filter(key); - auto &job_items = job_item_repo[key]; - if (index >= (int)job_items.size()) { - for (int i = job_items.size(); i <= index; ++i) { - bool failed = false; - if (!call_buildingplan_lua(&out, "get_job_item", 4, 1, - [&](lua_State *L) { - Lua::Push(L, type); - Lua::Push(L, subtype); - Lua::Push(L, custom); - Lua::Push(L, index+1); - }, - [&](lua_State *L) { - df::job_item *jitem = Lua::GetDFObject(L, -1); - DEBUG(status,out).print("retrieving job_item for index=%d: %p\n", - index, jitem); - if (!jitem) - failed = true; - else - job_items.emplace_back(jitem); - }) || failed) { - return 0; - } - } - } + auto &job_items = get_job_items(out, key); + if (job_items.size() <= index) + return 0; + auto &item_filters = get_item_filters(out, key); auto &jitem = job_items[index]; auto vector_ids = getVectorIds(out, jitem); @@ -524,7 +558,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ for (auto vector_id : vector_ids) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { - if (itemPassesScreen(item) && matchesFilters(item, jitem, heat)) { + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, item_filters[index])) { if (item_ids) item_ids->emplace_back(item->id); ++count; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 787987586..4f0d374e7 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -1,5 +1,7 @@ #pragma once +#include "itemfilter.h" + #include "modules/Persistence.h" #include "df/building.h" @@ -38,6 +40,6 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, df::job_item *job_item); bool itemPassesScreen(df::item * item); -bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat); +bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 703bab9b0..a904bc5a8 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -46,7 +46,7 @@ bool itemPassesScreen(df::item * item) { && !item->isAssignedToStockpile(); } -bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat) { +bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) return false; @@ -76,7 +76,8 @@ bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat) { &jitem, item->getType(), item->getSubtype()) && Job::isSuitableMaterial( &jitem, item->getMaterial(), item->getMaterialIndex(), - item->getType()); + item->getType()) + && item_filter.matches(item); } bool isJobReady(color_ostream &out, const std::vector &jitems) { @@ -180,7 +181,9 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto id = task.first; auto job = bld->jobs[0]; auto filter_idx = task.second; - if (matchesFilters(item, job->job_items[filter_idx], planned_buildings.at(id).heat_safety) + auto &pb = planned_buildings.at(id); + if (matchesFilters(item, job->job_items[filter_idx], pb.heat_safety, + pb.item_filters[filter_idx]) && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) { diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index e69de29bb..bd35c848a 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -0,0 +1,189 @@ +#include "itemfilter.h" + +#include "Debug.h" + +#include "df/item.h" + +using namespace DFHack; + +namespace DFHack { + DBG_EXTERN(buildingplan, status); +} + +ItemFilter::ItemFilter() { + clear(); +} + +void ItemFilter::clear() { + min_quality = df::item_quality::Ordinary; + max_quality = df::item_quality::Masterful; + decorated_only = false; + mat_mask.whole = 0; + materials.clear(); +} + +bool ItemFilter::isEmpty() { + return min_quality == df::item_quality::Ordinary + && max_quality == df::item_quality::Masterful + && !decorated_only + && !mat_mask.whole + && materials.empty(); +} + +static bool deserializeMaterialMask(std::string ser, df::dfhack_material_category mat_mask) { + if (ser.empty()) + return true; + + if (!parseJobMaterialCategory(&mat_mask, ser)) { + DEBUG(status).print("invalid job material category serialization: '%s'", ser.c_str()); + return false; + } + return true; +} + +static bool deserializeMaterials(std::string ser, std::vector &materials) { + if (ser.empty()) + return true; + + std::vector mat_names; + split_string(&mat_names, ser, ","); + for (auto m = mat_names.begin(); m != mat_names.end(); m++) { + DFHack::MaterialInfo material; + if (!material.find(*m) || !material.isValid()) { + DEBUG(status).print("invalid material name serialization: '%s'", ser.c_str()); + return false; + } + materials.push_back(material); + } + return true; +} + +ItemFilter::ItemFilter(std::string serialized) { + clear(); + + std::vector tokens; + split_string(&tokens, serialized, "/"); + if (tokens.size() != 5) { + DEBUG(status).print("invalid ItemFilter serialization: '%s'", serialized.c_str()); + return; + } + + if (!deserializeMaterialMask(tokens[0], mat_mask) || !deserializeMaterials(tokens[1], materials)) + return; + + setMinQuality(atoi(tokens[2].c_str())); + setMaxQuality(atoi(tokens[3].c_str())); + decorated_only = static_cast(atoi(tokens[4].c_str())); +} + +// format: mat,mask,elements/materials,list/minq/maxq/decorated +std::string ItemFilter::serialize() const { + std::ostringstream ser; + ser << bitfield_to_string(mat_mask, ",") << "/"; + if (!materials.empty()) { + ser << materials[0].getToken(); + for (size_t i = 1; i < materials.size(); ++i) + ser << "," << materials[i].getToken(); + } + ser << "/" << static_cast(min_quality); + ser << "/" << static_cast(max_quality); + ser << "/" << static_cast(decorated_only); + return ser.str(); +} + +static void clampItemQuality(df::item_quality *quality) { + if (*quality > df::item_quality::Artifact) { + DEBUG(status).print("clamping quality to Artifact"); + *quality = df::item_quality::Artifact; + } + if (*quality < df::item_quality::Ordinary) { + DEBUG(status).print("clamping quality to Ordinary"); + *quality = df::item_quality::Ordinary; + } +} + +void ItemFilter::setMinQuality(int quality) { + min_quality = static_cast(quality); + clampItemQuality(&min_quality); + if (max_quality < min_quality) + max_quality = min_quality; +} + +void ItemFilter::setMaxQuality(int quality) { + max_quality = static_cast(quality); + clampItemQuality(&max_quality); + if (max_quality < min_quality) + min_quality = max_quality; +} + +void ItemFilter::setDecoratedOnly(bool decorated) { + decorated_only = decorated; +} + +void ItemFilter::setMaterialMask(uint32_t mask) { + mat_mask.whole = mask; +} + +void ItemFilter::setMaterials(const std::vector &materials) { + this->materials = materials; +} + +std::string ItemFilter::getMinQuality() const { + return ENUM_KEY_STR(item_quality, min_quality); +} + +std::string ItemFilter::getMaxQuality() const { + return ENUM_KEY_STR(item_quality, max_quality); +} + +bool ItemFilter::getDecoratedOnly() const { + return decorated_only; +} + +uint32_t ItemFilter::getMaterialMask() const { + return mat_mask.whole; +} + +static std::string material_to_string_fn(const MaterialInfo &m) { return m.toString(); } + +std::vector ItemFilter::getMaterials() const { + std::vector descriptions; + transform_(materials, descriptions, material_to_string_fn); + + if (descriptions.size() == 0) + bitfield_to_string(&descriptions, mat_mask); + + if (descriptions.size() == 0) + descriptions.push_back("any"); + + return descriptions; +} + +static bool matchesMask(DFHack::MaterialInfo &mat, df::dfhack_material_category mat_mask) { + return mat_mask.whole ? mat.matches(mat_mask) : true; +} + +bool ItemFilter::matches(df::dfhack_material_category mask) const { + return mask.whole & mat_mask.whole; +} + +bool ItemFilter::matches(DFHack::MaterialInfo &material) const { + for (auto it = materials.begin(); it != materials.end(); ++it) + if (material.matches(*it)) + return true; + return false; +} + +bool ItemFilter::matches(df::item *item) const { + if (item->getQuality() < min_quality || item->getQuality() > max_quality) + return false; + + if (decorated_only && !item->hasImprovements()) + return false; + + auto imattype = item->getActualMaterial(); + auto imatindex = item->getActualMaterialIndex(); + auto item_mat = DFHack::MaterialInfo(imattype, imatindex); + + return (materials.size() == 0) ? matchesMask(item_mat, mat_mask) : matches(item_mat); +} diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h index 6f70f09be..134d3b249 100644 --- a/plugins/buildingplan/itemfilter.h +++ b/plugins/buildingplan/itemfilter.h @@ -1 +1,39 @@ #pragma once + +#include "modules/Materials.h" + +#include "df/dfhack_material_category.h" +#include "df/item_quality.h" + +class ItemFilter { +public: + ItemFilter(); + ItemFilter(std::string serialized); + + void clear(); + bool isEmpty(); + std::string serialize() const; + + void setMinQuality(int quality); + void setMaxQuality(int quality); + void setDecoratedOnly(bool decorated); + void setMaterialMask(uint32_t mask); + void setMaterials(const std::vector &materials); + + std::string getMinQuality() const; + std::string getMaxQuality() const; + bool getDecoratedOnly() const; + uint32_t getMaterialMask() const; + std::vector getMaterials() const; + + bool matches(df::dfhack_material_category mask) const; + bool matches(DFHack::MaterialInfo &material) const; + bool matches(df::item *item) const; + +private: + df::item_quality min_quality; + df::item_quality max_quality; + bool decorated_only; + df::dfhack_material_category mat_mask; + std::vector materials; +}; diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index eb55a95b4..c68d668bf 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -31,14 +31,18 @@ static vector> get_vector_ids(color_ostream &out, return ret; } -static vector> deserialize(color_ostream &out, PersistentDataItem &bld_config) { +static vector> deserialize_vector_ids(color_ostream &out, PersistentDataItem &bld_config) { vector> ret; - DEBUG(status,out).print("deserializing state for building %d: %s\n", - get_config_val(bld_config, BLD_CONFIG_ID), bld_config.val().c_str()); + vector rawstrs; + split_string(&rawstrs, bld_config.val(), "|"); + const string &serialized = rawstrs[0]; + + DEBUG(status,out).print("deserializing vector ids for building %d: %s\n", + get_config_val(bld_config, BLD_CONFIG_ID), serialized.c_str()); vector joined; - split_string(&joined, bld_config.val(), "|"); + split_string(&joined, serialized, ";"); for (auto &str : joined) { vector lst; split_string(&lst, str, ","); @@ -54,28 +58,60 @@ static vector> deserialize(color_ostream &out, Pe return ret; } -static string serialize(const vector> &vector_ids) { +static std::vector deserialize_item_filters(color_ostream &out, PersistentDataItem &bld_config) { + std::vector ret; + + vector rawstrs; + split_string(&rawstrs, bld_config.val(), "|"); + if (rawstrs.size() < 2) + return ret; + const string &serialized = rawstrs[1]; + + DEBUG(status,out).print("deserializing item filters for building %d: %s\n", + get_config_val(bld_config, BLD_CONFIG_ID), serialized.c_str()); + + vector filterstrs; + split_string(&filterstrs, serialized, ";"); + for (auto &str : filterstrs) { + ret.emplace_back(str); + } + + return ret; +} + +static string serialize(const vector> &vector_ids, const vector &item_filters) { vector joined; for (auto &vec_list : vector_ids) { joined.emplace_back(join_strings(",", vec_list)); } - return join_strings("|", joined); + std::ostringstream out; + out << join_strings(";", joined) << "|"; + + joined.clear(); + for (auto &filter : item_filters) { + joined.emplace_back(filter.serialize()); + } + out << join_strings(";", joined); + + return out.str(); } -PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafety heat) - : id(bld->id), vector_ids(get_vector_ids(out, id)), heat_safety(heat) { +PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafety heat, const vector &item_filters) + : id(bld->id), vector_ids(get_vector_ids(out, id)), heat_safety(heat), + item_filters(item_filters) { DEBUG(status,out).print("creating persistent data for building %d\n", id); bld_config = World::AddPersistentData(BLD_CONFIG_KEY); set_config_val(bld_config, BLD_CONFIG_ID, id); set_config_val(bld_config, BLD_CONFIG_HEAT, heat_safety); - bld_config.val() = serialize(vector_ids); + bld_config.val() = serialize(vector_ids, item_filters); DEBUG(status,out).print("serialized state for building %d: %s\n", id, bld_config.val().c_str()); } PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_config) : id(get_config_val(bld_config, BLD_CONFIG_ID)), - vector_ids(deserialize(out, bld_config)), + vector_ids(deserialize_vector_ids(out, bld_config)), heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), + item_filters(deserialize_item_filters(out, bld_config)), bld_config(bld_config) { } // Ensure the building still exists and is in a valid state. It can disappear diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 0a67e0edc..5bd09ba5a 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -1,6 +1,7 @@ #pragma once #include "buildingplan.h" +#include "itemfilter.h" #include "Core.h" @@ -18,7 +19,9 @@ public: const HeatSafety heat_safety; - PlannedBuilding(DFHack::color_ostream &out, df::building *bld, HeatSafety heat); + const std::vector item_filters; + + PlannedBuilding(DFHack::color_ostream &out, df::building *bld, HeatSafety heat, const std::vector &item_filters); PlannedBuilding(DFHack::color_ostream &out, DFHack::PersistentDataItem &bld_config); void remove(DFHack::color_ostream &out); diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 9d8eb463b..b6cbb383b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1292,7 +1292,7 @@ InspectorOverlay.ATTRS{ default_pos={x=-41,y=14}, default_enabled=true, viewscreens='dwarfmode/ViewSheets/BUILDING', - frame={w=30, h=14}, + frame={w=30, h=15}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } @@ -1308,12 +1308,12 @@ function InspectorOverlay:init() InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3}, InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4}, widgets.HotkeyLabel{ - frame={t=10, l=0}, + frame={t=11, l=0}, label='adjust filters', key='CUSTOM_CTRL_F', }, widgets.HotkeyLabel{ - frame={t=11, l=0}, + frame={t=12, l=0}, label='make top priority', key='CUSTOM_CTRL_T', on_activate=self:callback('make_top_priority'), From 60de4619a294ab84cf82c61d8839f540ef326534 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 08:34:55 -0800 Subject: [PATCH 0701/2222] fix signed unsigned compare --- plugins/buildingplan/buildingplan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 7359d842c..3a73372c3 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -547,7 +547,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ BuildingTypeKey key(type, subtype, custom); HeatSafety heat = get_heat_safety_filter(key); auto &job_items = get_job_items(out, key); - if (job_items.size() <= index) + if (index < 0 || job_items.size() <= (size_t)index) return 0; auto &item_filters = get_item_filters(out, key); From 4cc262c796f58126a964cfe12fdee17f19280bc4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 15:08:11 -0800 Subject: [PATCH 0702/2222] overhaul serialization; persist item filters --- plugins/buildingplan/CMakeLists.txt | 5 +- plugins/buildingplan/buildingplan.cpp | 79 ++++++++++----------- plugins/buildingplan/buildingplan.h | 11 ++- plugins/buildingplan/buildingplan_cycle.cpp | 2 +- plugins/buildingplan/buildingtypekey.cpp | 59 +++++++++++++++ plugins/buildingplan/buildingtypekey.h | 22 ++++++ plugins/buildingplan/defaultitemfilters.cpp | 60 ++++++++++++++++ plugins/buildingplan/defaultitemfilters.h | 24 +++++++ plugins/buildingplan/itemfilter.cpp | 55 +++++++++----- plugins/buildingplan/itemfilter.h | 7 +- plugins/buildingplan/plannedbuilding.cpp | 26 ++----- 11 files changed, 264 insertions(+), 86 deletions(-) create mode 100644 plugins/buildingplan/buildingtypekey.cpp create mode 100644 plugins/buildingplan/buildingtypekey.h create mode 100644 plugins/buildingplan/defaultitemfilters.cpp create mode 100644 plugins/buildingplan/defaultitemfilters.h diff --git a/plugins/buildingplan/CMakeLists.txt b/plugins/buildingplan/CMakeLists.txt index 85475edaa..118b2a1d1 100644 --- a/plugins/buildingplan/CMakeLists.txt +++ b/plugins/buildingplan/CMakeLists.txt @@ -2,12 +2,15 @@ project(buildingplan) set(COMMON_HDRS buildingplan.h + buildingtypekey.h + defaultitemfilters.h itemfilter.h plannedbuilding.h ) set_source_files_properties(${COMMON_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) dfhack_plugin(buildingplan - buildingplan.cpp buildingplan_cycle.cpp itemfilter.cpp plannedbuilding.cpp + buildingplan.cpp buildingplan_cycle.cpp buildingtypekey.cpp + defaultitemfilters.cpp itemfilter.cpp plannedbuilding.cpp ${COMMON_HDRS} LINK_LIBRARIES lua) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 3a73372c3..4fa119ebc 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -1,5 +1,7 @@ -#include "plannedbuilding.h" #include "buildingplan.h" +#include "buildingtypekey.h" +#include "defaultitemfilters.h" +#include "plannedbuilding.h" #include "Debug.h" #include "LuaTools.h" @@ -29,6 +31,7 @@ namespace DFHack { } static const string CONFIG_KEY = string(plugin_name) + "/config"; +const string FILTER_CONFIG_KEY = string(plugin_name) + "/filter"; const string BLD_CONFIG_KEY = string(plugin_name) + "/building"; int get_config_val(PersistentDataItem &c, int index) { @@ -47,35 +50,11 @@ void set_config_bool(PersistentDataItem &c, int index, bool value) { set_config_val(c, index, value ? 1 : 0); } -// building type, subtype, custom -typedef std::tuple BuildingTypeKey; - -// rotates a size_t value left by count bits -// assumes count is not 0 or >= size_t_bits -// replace this with std::rotl when we move to C++20 -static std::size_t rotl_size_t(size_t val, uint32_t count) -{ - static const int size_t_bits = CHAR_BIT * sizeof(std::size_t); - return val << count | val >> (size_t_bits - count); -} - -struct BuildingTypeKeyHash { - std::size_t operator() (const BuildingTypeKey & key) const { - // cast first param to appease gcc-4.8, which is missing the enum - // specializations for std::hash - std::size_t h1 = std::hash()(static_cast(std::get<0>(key))); - std::size_t h2 = std::hash()(std::get<1>(key)); - std::size_t h3 = std::hash()(std::get<2>(key)); - - return h1 ^ rotl_size_t(h2, 8) ^ rotl_size_t(h3, 16); - } -}; - static PersistentDataItem config; // for use in counting available materials for the UI -static unordered_map, BuildingTypeKeyHash> job_item_cache; +static unordered_map, BuildingTypeKeyHash> job_item_cache; static unordered_map cur_heat_safety; -static unordered_map, BuildingTypeKeyHash> cur_item_filters; +static unordered_map cur_item_filters; // building id -> PlannedBuilding static unordered_map planned_buildings; // vector id -> filter bucket -> queue of (building id, job_item index) @@ -133,7 +112,7 @@ static int get_num_filters(color_ostream &out, BuildingTypeKey key) { return num_filters; } -static vector & get_job_items(color_ostream &out, BuildingTypeKey key) { +static const vector & get_job_items(color_ostream &out, BuildingTypeKey key) { if (job_item_cache.count(key)) return job_item_cache[key]; const int num_filters = get_num_filters(out, key); @@ -169,13 +148,11 @@ static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { return HEAT_SAFETY_ANY; } -static vector & get_item_filters(color_ostream &out, const BuildingTypeKey &key) { +static DefaultItemFilters & get_item_filters(color_ostream &out, const BuildingTypeKey &key) { if (cur_item_filters.count(key)) - return cur_item_filters[key]; - - vector &filters = cur_item_filters[key]; - filters.resize(get_job_items(out, key).size()); - return filters; + return cur_item_filters.at(key); + cur_item_filters.emplace(key, DefaultItemFilters(out, key, get_job_items(out, key))); + return cur_item_filters.at(key); } static command_result do_command(color_ostream &out, vector ¶meters); @@ -258,6 +235,14 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DEBUG(status,out).print("loading persisted state\n"); clear_state(out); + + vector filter_configs; + World::GetPersistentData(&filter_configs, FILTER_CONFIG_KEY); + for (auto &cfg : filter_configs) { + BuildingTypeKey key = DefaultItemFilters::getKey(cfg); + cur_item_filters.emplace(key, DefaultItemFilters(out, cfg, get_job_items(out, key))); + } + vector building_configs; World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); @@ -265,13 +250,17 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { PlannedBuilding pb(out, building_configs[idx]); df::building *bld = df::building::find(pb.id); if (!bld) { - WARN(status).print("cannot find building %d; halting load\n", pb.id); + INFO(status).print("building %d no longer exists; skipping\n", pb.id); + pb.remove(out); + continue; } BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); - if (pb.item_filters.size() != get_item_filters(out, key).size()) + if (pb.item_filters.size() != get_item_filters(out, key).getItemFilters().size()) { WARN(status).print("loaded state for building %d doesn't match world\n", pb.id); - else - registerPlannedBuilding(out, pb); + pb.remove(out); + continue; + } + registerPlannedBuilding(out, pb); } return CR_OK; @@ -352,7 +341,7 @@ static string getBucket(const df::job_item & ji) { } // get a list of item vectors that we should search for matches -vector getVectorIds(color_ostream &out, df::job_item *job_item) { +vector getVectorIds(color_ostream &out, const df::job_item *job_item) { std::vector ret; // if the filter already has the vector_id set to something specific, use it @@ -525,7 +514,7 @@ static bool addPlannedBuilding(color_ostream &out, df::building *bld) { bld->getCustomType())) return false; BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); - PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key)); + PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key).getItemFilters()); return registerPlannedBuilding(out, pb); } @@ -549,7 +538,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ auto &job_items = get_job_items(out, key); if (index < 0 || job_items.size() <= (size_t)index) return 0; - auto &item_filters = get_item_filters(out, key); + auto &item_filters = get_item_filters(out, key).getItemFilters(); auto &jitem = job_items[index]; auto vector_ids = getVectorIds(out, jitem); @@ -595,7 +584,13 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 } static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { - DEBUG(status,out).print("entering hasFilter\n"); + TRACE(status,out).print("entering hasFilter\n"); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(out, key); + for (auto &filter : filters.getItemFilters()) { + if (filter.isEmpty()) + return true; + } return false; } diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 4f0d374e7..eef9808e6 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -13,6 +13,7 @@ typedef std::deque> Bucket; typedef std::map> Tasks; +extern const std::string FILTER_CONFIG_KEY; extern const std::string BLD_CONFIG_KEY; enum ConfigValues { @@ -22,6 +23,12 @@ enum ConfigValues { CONFIG_BARS = 4, }; +enum FilterConfigValues { + FILTER_CONFIG_TYPE = 0, + FILTER_CONFIG_SUBTYPE = 1, + FILTER_CONFIG_CUSTOM = 2, +}; + enum BuildingConfigValues { BLD_CONFIG_ID = 0, BLD_CONFIG_HEAT = 1, @@ -38,8 +45,8 @@ bool get_config_bool(DFHack::PersistentDataItem &c, int index); void set_config_val(DFHack::PersistentDataItem &c, int index, int value); void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); -std::vector getVectorIds(DFHack::color_ostream &out, df::job_item *job_item); +std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item); bool itemPassesScreen(df::item * item); -bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter); +bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index a904bc5a8..655dc8c1a 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -46,7 +46,7 @@ bool itemPassesScreen(df::item * item) { && !item->isAssignedToStockpile(); } -bool matchesFilters(df::item * item, df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter) { +bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) return false; diff --git a/plugins/buildingplan/buildingtypekey.cpp b/plugins/buildingplan/buildingtypekey.cpp new file mode 100644 index 000000000..664fdf27d --- /dev/null +++ b/plugins/buildingplan/buildingtypekey.cpp @@ -0,0 +1,59 @@ +#include "buildingplan.h" +#include "buildingtypekey.h" + +#include "Debug.h" +#include "MiscUtils.h" + +using std::string; +using std::vector; + +namespace DFHack { + DBG_EXTERN(buildingplan, status); +} + +using namespace DFHack; + +// building type, subtype, custom +BuildingTypeKey::BuildingTypeKey(df::building_type type, int16_t subtype, int32_t custom) + : tuple(type, subtype, custom) { } + +static BuildingTypeKey deserialize(color_ostream &out, const std::string &serialized) { + vector key_parts; + split_string(&key_parts, serialized, ","); + if (key_parts.size() != 3) { + WARN(status,out).print("invalid key_str: '%s'\n", serialized.c_str()); + return BuildingTypeKey(df::building_type::NONE, -1, -1); + } + return BuildingTypeKey((df::building_type)string_to_int(key_parts[0]), + string_to_int(key_parts[1]), string_to_int(key_parts[2])); +} + +BuildingTypeKey::BuildingTypeKey(color_ostream &out, const std::string &serialized) + :tuple(deserialize(out, serialized)) { } + +string BuildingTypeKey::serialize() const { + std::ostringstream ser; + ser << std::get<0>(*this) << ","; + ser << std::get<1>(*this) << ","; + ser << std::get<2>(*this); + return ser.str(); +} + +// rotates a size_t value left by count bits +// assumes count is not 0 or >= size_t_bits +// replace this with std::rotl when we move to C++20 +static std::size_t rotl_size_t(size_t val, uint32_t count) +{ + static const int size_t_bits = CHAR_BIT * sizeof(std::size_t); + return val << count | val >> (size_t_bits - count); +} + +std::size_t BuildingTypeKeyHash::operator() (const BuildingTypeKey & key) const { + // cast first param to appease gcc-4.8, which is missing the enum + // specializations for std::hash + std::size_t h1 = std::hash()(static_cast(std::get<0>(key))); + std::size_t h2 = std::hash()(std::get<1>(key)); + std::size_t h3 = std::hash()(std::get<2>(key)); + + return h1 ^ rotl_size_t(h2, 8) ^ rotl_size_t(h3, 16); +} diff --git a/plugins/buildingplan/buildingtypekey.h b/plugins/buildingplan/buildingtypekey.h new file mode 100644 index 000000000..81bb043c5 --- /dev/null +++ b/plugins/buildingplan/buildingtypekey.h @@ -0,0 +1,22 @@ +#pragma once + +#include "df/building_type.h" + +#include +#include + +namespace DFHack { + class color_ostream; +} + +// building type, subtype, custom +struct BuildingTypeKey : public std::tuple { + BuildingTypeKey(df::building_type type, int16_t subtype, int32_t custom); + BuildingTypeKey(DFHack::color_ostream &out, const std::string & serialized); + + std::string serialize() const; +}; + +struct BuildingTypeKeyHash { + std::size_t operator() (const BuildingTypeKey & key) const; +}; diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp new file mode 100644 index 000000000..4cc6f11cf --- /dev/null +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -0,0 +1,60 @@ +#include "defaultitemfilters.h" + +#include "Debug.h" +#include "MiscUtils.h" + +#include "modules/World.h" + +namespace DFHack { + DBG_EXTERN(buildingplan, status); +} + +using std::string; +using std::vector; +using namespace DFHack; + +BuildingTypeKey DefaultItemFilters::getKey(PersistentDataItem &filter_config) { + return BuildingTypeKey( + (df::building_type)get_config_val(filter_config, FILTER_CONFIG_TYPE), + get_config_val(filter_config, FILTER_CONFIG_SUBTYPE), + get_config_val(filter_config, FILTER_CONFIG_CUSTOM)); +} + +DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, const std::vector &jitems) + : key(key) { + DEBUG(status,out).print("creating persistent data for filter key %d,%d,%d\n", + std::get<0>(key), std::get<1>(key), std::get<2>(key)); + filter_config = World::AddPersistentData(FILTER_CONFIG_KEY); + set_config_val(filter_config, FILTER_CONFIG_TYPE, std::get<0>(key)); + set_config_val(filter_config, FILTER_CONFIG_SUBTYPE, std::get<1>(key)); + set_config_val(filter_config, FILTER_CONFIG_CUSTOM, std::get<2>(key)); + item_filters.resize(jitems.size()); + filter_config.val() = serialize_item_filters(item_filters); +} + +DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &filter_config, const std::vector &jitems) + : key(getKey(filter_config)), filter_config(filter_config) { + auto &serialized = filter_config.val(); + DEBUG(status,out).print("deserializing item filters for key %d,%d,%d: %s\n", + std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); + std::vector filters = deserialize_item_filters(out, serialized); + if (filters.size() != jitems.size()) { + WARN(status,out).print("ignoring invalid filters_str for key %d,%d,%d: '%s'\n", + std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); + item_filters.resize(jitems.size()); + } else + item_filters = filters; +} + +void DefaultItemFilters::setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index) { + if (item_filters.size() <= index) { + WARN(status,out).print("invalid index for filter key %d,%d,%d: %d\n", + std::get<0>(key), std::get<1>(key), std::get<2>(key), index); + return; + } + + item_filters[index] = filter; + filter_config.val() = serialize_item_filters(item_filters); + DEBUG(status,out).print("updated item filter and persisted for key %d,%d,%d: %s\n", + std::get<0>(key), std::get<1>(key), std::get<2>(key), filter_config.val().c_str()); +} diff --git a/plugins/buildingplan/defaultitemfilters.h b/plugins/buildingplan/defaultitemfilters.h new file mode 100644 index 000000000..4d1d5cbd2 --- /dev/null +++ b/plugins/buildingplan/defaultitemfilters.h @@ -0,0 +1,24 @@ +#pragma once + +#include "buildingplan.h" +#include "buildingtypekey.h" + +#include "modules/Persistence.h" + +class DefaultItemFilters { +public: + static BuildingTypeKey getKey(DFHack::PersistentDataItem &filter_config); + + const BuildingTypeKey key; + + DefaultItemFilters(DFHack::color_ostream &out, BuildingTypeKey key, const std::vector &jitems); + DefaultItemFilters(DFHack::color_ostream &out, DFHack::PersistentDataItem &filter_config, const std::vector &jitems); + + void setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index); + + const std::vector & getItemFilters() const { return item_filters; } + +private: + DFHack::PersistentDataItem filter_config; + std::vector item_filters; +}; diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index bd35c848a..a714b62d4 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -4,12 +4,15 @@ #include "df/item.h" -using namespace DFHack; - namespace DFHack { DBG_EXTERN(buildingplan, status); } +using std::string; +using std::vector; + +using namespace DFHack; + ItemFilter::ItemFilter() { clear(); } @@ -22,7 +25,7 @@ void ItemFilter::clear() { materials.clear(); } -bool ItemFilter::isEmpty() { +bool ItemFilter::isEmpty() const { return min_quality == df::item_quality::Ordinary && max_quality == df::item_quality::Masterful && !decorated_only @@ -30,7 +33,7 @@ bool ItemFilter::isEmpty() { && materials.empty(); } -static bool deserializeMaterialMask(std::string ser, df::dfhack_material_category mat_mask) { +static bool deserializeMaterialMask(string ser, df::dfhack_material_category mat_mask) { if (ser.empty()) return true; @@ -41,11 +44,11 @@ static bool deserializeMaterialMask(std::string ser, df::dfhack_material_categor return true; } -static bool deserializeMaterials(std::string ser, std::vector &materials) { +static bool deserializeMaterials(string ser, vector &materials) { if (ser.empty()) return true; - std::vector mat_names; + vector mat_names; split_string(&mat_names, ser, ","); for (auto m = mat_names.begin(); m != mat_names.end(); m++) { DFHack::MaterialInfo material; @@ -58,13 +61,13 @@ static bool deserializeMaterials(std::string ser, std::vector tokens; + vector tokens; split_string(&tokens, serialized, "/"); if (tokens.size() != 5) { - DEBUG(status).print("invalid ItemFilter serialization: '%s'", serialized.c_str()); + DEBUG(status,out).print("invalid ItemFilter serialization: '%s'", serialized.c_str()); return; } @@ -77,7 +80,7 @@ ItemFilter::ItemFilter(std::string serialized) { } // format: mat,mask,elements/materials,list/minq/maxq/decorated -std::string ItemFilter::serialize() const { +string ItemFilter::serialize() const { std::ostringstream ser; ser << bitfield_to_string(mat_mask, ",") << "/"; if (!materials.empty()) { @@ -124,15 +127,15 @@ void ItemFilter::setMaterialMask(uint32_t mask) { mat_mask.whole = mask; } -void ItemFilter::setMaterials(const std::vector &materials) { +void ItemFilter::setMaterials(const vector &materials) { this->materials = materials; } -std::string ItemFilter::getMinQuality() const { +string ItemFilter::getMinQuality() const { return ENUM_KEY_STR(item_quality, min_quality); } -std::string ItemFilter::getMaxQuality() const { +string ItemFilter::getMaxQuality() const { return ENUM_KEY_STR(item_quality, max_quality); } @@ -144,10 +147,10 @@ uint32_t ItemFilter::getMaterialMask() const { return mat_mask.whole; } -static std::string material_to_string_fn(const MaterialInfo &m) { return m.toString(); } +static string material_to_string_fn(const MaterialInfo &m) { return m.toString(); } -std::vector ItemFilter::getMaterials() const { - std::vector descriptions; +vector ItemFilter::getMaterials() const { + vector descriptions; transform_(materials, descriptions, material_to_string_fn); if (descriptions.size() == 0) @@ -187,3 +190,23 @@ bool ItemFilter::matches(df::item *item) const { return (materials.size() == 0) ? matchesMask(item_mat, mat_mask) : matches(item_mat); } + +vector deserialize_item_filters(color_ostream &out, const string &serialized) { + std::vector filters; + + vector filter_strs; + split_string(&filter_strs, serialized, ";"); + for (auto &str : filter_strs) { + filters.emplace_back(out, str); + } + + return filters; +} + +string serialize_item_filters(const vector &filters) { + vector strs; + for (auto &filter : filters) { + strs.emplace_back(filter.serialize()); + } + return join_strings(";", strs); +} diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h index 134d3b249..6eb7551b4 100644 --- a/plugins/buildingplan/itemfilter.h +++ b/plugins/buildingplan/itemfilter.h @@ -8,10 +8,10 @@ class ItemFilter { public: ItemFilter(); - ItemFilter(std::string serialized); + ItemFilter(DFHack::color_ostream &out, std::string serialized); void clear(); - bool isEmpty(); + bool isEmpty() const; std::string serialize() const; void setMinQuality(int quality); @@ -37,3 +37,6 @@ private: df::dfhack_material_category mat_mask; std::vector materials; }; + +std::vector deserialize_item_filters(DFHack::color_ostream &out, const std::string &serialized); +std::string serialize_item_filters(const std::vector &filters); diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index c68d668bf..27be36a5b 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -58,25 +58,14 @@ static vector> deserialize_vector_ids(color_ostre return ret; } -static std::vector deserialize_item_filters(color_ostream &out, PersistentDataItem &bld_config) { +static std::vector get_item_filters(color_ostream &out, PersistentDataItem &bld_config) { std::vector ret; vector rawstrs; split_string(&rawstrs, bld_config.val(), "|"); if (rawstrs.size() < 2) return ret; - const string &serialized = rawstrs[1]; - - DEBUG(status,out).print("deserializing item filters for building %d: %s\n", - get_config_val(bld_config, BLD_CONFIG_ID), serialized.c_str()); - - vector filterstrs; - split_string(&filterstrs, serialized, ";"); - for (auto &str : filterstrs) { - ret.emplace_back(str); - } - - return ret; + return deserialize_item_filters(out, rawstrs[1]); } static string serialize(const vector> &vector_ids, const vector &item_filters) { @@ -85,14 +74,7 @@ static string serialize(const vector> &vector_ids joined.emplace_back(join_strings(",", vec_list)); } std::ostringstream out; - out << join_strings(";", joined) << "|"; - - joined.clear(); - for (auto &filter : item_filters) { - joined.emplace_back(filter.serialize()); - } - out << join_strings(";", joined); - + out << join_strings(";", joined) << "|" << serialize_item_filters(item_filters); return out.str(); } @@ -111,7 +93,7 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_con : id(get_config_val(bld_config, BLD_CONFIG_ID)), vector_ids(deserialize_vector_ids(out, bld_config)), heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), - item_filters(deserialize_item_filters(out, bld_config)), + item_filters(get_item_filters(out, bld_config)), bld_config(bld_config) { } // Ensure the building still exists and is in a valid state. It can disappear From 20a0390c50c3386ba8b16a824814bd057241acfd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 18:06:30 -0800 Subject: [PATCH 0703/2222] no building shadow when other windows are up --- plugins/lua/buildingplan.lua | 75 ++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index b6cbb383b..f3a6ef6ce 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -500,6 +500,70 @@ FilterSelection.ATTRS{ } function FilterSelection:init() + self:addviews{ + widgets.Panel{ + view_id='options_panel', + frame={l=0, t=0, b=5, w=10}, + autoarrange_subviews=true, + subviews={ + widgets.Panel{ + view_id='quality_panel', + frame={}, + frame_style=gui.MEDIUM_FRAME, + frame_title='Item quality', + subviews={ + }, + }, + widgets.Panel{ + view_id='building_panel', + frame={}, + frame_style=gui.MEDIUM_FRAME, + frame_title='Building options', + subviews={ + }, + }, + widgets.Panel{ + view_id='global_panel', + frame={}, + frame_style=gui.MEDIUM_FRAME, + frame_title='Global options', + subviews={ + }, + }, + }, + }, + widgets.Panel{ + view_id='materials_panel', + frame={l=10, t=0, b=5, r=0}, + subviews={ + widgets.Panel{ + view_id='materials_top', + frame={l=0, t=0, r=0, h=5}, + subviews={ + }, + }, + widgets.Panel{ + view_id='materials_lists', + frame={l=0, t=5, r=0, b=0}, + frame_style=gui.MEDIUM_FRAME, + subviews={ + widgets.Panel{ + view_id='materials_categories', + frame={l=0, t=0, b=0, w=20}, + subviews={ + }, + }, + widgets.Panel{ + view_id='materials_mats', + frame={l=21, t=0, r=0, b=0}, + subviews={ + }, + }, + }, + }, + }, + }, + } end FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) @@ -514,6 +578,14 @@ function FilterSelectionScreen:init() } end +function FilterSelectionScreen:onShow() + df.global.game.main_interface.bottom_mode_selected = -1 +end + +function FilterSelectionScreen:onDismiss() + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT +end + -------------------------------- -- ItemLine -- @@ -1048,6 +1120,7 @@ function PlannerOverlay:onInput(keys) local is_hollow = self.subviews.hollow:getOptionValue() local chosen_items, active_screens = {}, {} local pending = num_filters + df.global.game.main_interface.bottom_mode_selected = -1 for idx = num_filters,1,-1 do chosen_items[idx] = {} if (self.subviews['item'..idx].available or 0) > 0 then @@ -1061,6 +1134,7 @@ function PlannerOverlay:onInput(keys) active_screens[idx] = nil pending = pending - 1 if pending == 0 then + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT self:place_building(self:restore_placement(), chosen_items) end end, @@ -1068,6 +1142,7 @@ function PlannerOverlay:onInput(keys) for i,scr in pairs(active_screens) do scr:dismiss() end + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT self:restore_placement() end, }:show() From dadecdcf45f9a2aa914b8a5caa13ba57013fb73f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 18:14:11 -0800 Subject: [PATCH 0704/2222] fix inspector screen not resetting the description --- plugins/lua/buildingplan.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index f3a6ef6ce..ee2aff21b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1355,6 +1355,7 @@ function InspectorLine:get_status_line() end function InspectorLine:reset() + self.desc = nil self.status = nil end From 4b2645469686e678846811a0c66769b8ec9f8a9d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 18:59:15 -0800 Subject: [PATCH 0705/2222] start of filters dialog --- plugins/lua/buildingplan.lua | 106 +++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 11 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index ee2aff21b..aef04e020 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -494,7 +494,7 @@ end FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ frame_title='Choose filters', - frame={w=60, h=40, l=30, t=8}, + frame={w=80, h=53, l=30, t=8}, resizable=true, index=DEFAULT_NIL, } @@ -503,38 +503,122 @@ function FilterSelection:init() self:addviews{ widgets.Panel{ view_id='options_panel', - frame={l=0, t=0, b=5, w=10}, + frame={l=0, t=0, b=5, w=30}, autoarrange_subviews=true, subviews={ widgets.Panel{ view_id='quality_panel', - frame={}, - frame_style=gui.MEDIUM_FRAME, + frame={l=0, r=0, h=23}, + frame_style=gui.INTERIOR_FRAME, frame_title='Item quality', subviews={ + widgets.Label{ + frame={l=0, t=0}, + text='updown hotkeys', + }, + widgets.Panel{ + view_id='quality_slider', + frame={l=0, t=2, w=3, h=15}, + frame_background=to_pen{fg=COLOR_GREEN, bg=COLOR_GREEN, ch=' '}, + }, + widgets.Label{ + frame={l=3, t=3}, + text='- Artifact (num)', + }, + widgets.Label{ + frame={l=3, t=5}, + text='- Masterful (num)', + }, + widgets.Label{ + frame={l=3, t=7}, + text='- Exceptional (num)', + }, + widgets.Label{ + frame={l=3, t=9}, + text='- Superior (num)', + }, + widgets.Label{ + frame={l=3, t=11}, + text='- FinelyCrafted (num)', + }, + widgets.Label{ + frame={l=3, t=13}, + text='- WellCrafted (num)', + }, + widgets.Label{ + frame={l=3, t=15}, + text='- Ordinary (num)', + }, + widgets.Label{ + frame={l=0, t=18}, + text='updown hotkeys', + }, + widgets.CycleHotkeyLabel{ + frame={l=0, t=20}, + label='Decorated only:', + options={'No', 'Yes'}, + }, }, }, - widgets.Panel{ + widgets.ResizingPanel{ view_id='building_panel', - frame={}, - frame_style=gui.MEDIUM_FRAME, + frame={l=0, r=0}, + frame_style=gui.INTERIOR_FRAME, frame_title='Building options', + autoarrange_subviews=true, + autoarrange_gap=1, subviews={ + widgets.WrappedLabel{ + frame={l=0}, + text_to_wrap='These options will affect all items for the current building type.', + }, + widgets.CycleHotkeyLabel{ + frame={l=0}, + key='CUSTOM_G', + label='Building safety:', + options={ + {label='Any', value=0}, + {label='Magma', value=2, pen=COLOR_RED}, + {label='Fire', value=1, pen=COLOR_LIGHTRED}, + }, + }, }, }, widgets.Panel{ view_id='global_panel', - frame={}, - frame_style=gui.MEDIUM_FRAME, + frame={l=0, r=0, b=0}, + frame_style=gui.INTERIOR_FRAME, frame_title='Global options', + autoarrange_subviews=true, + autoarrange_gap=1, subviews={ + widgets.WrappedLabel{ + frame={l=0}, + text_to_wrap='These options will affect the selection of "Generic Materials" for future buildings.', + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + label='Blocks', + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + label='Logs', + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + label='Boulders', + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + label='Bars', + }, }, }, }, }, widgets.Panel{ view_id='materials_panel', - frame={l=10, t=0, b=5, r=0}, + frame={l=30, t=0, b=5, r=0}, subviews={ widgets.Panel{ view_id='materials_top', @@ -545,7 +629,7 @@ function FilterSelection:init() widgets.Panel{ view_id='materials_lists', frame={l=0, t=5, r=0, b=0}, - frame_style=gui.MEDIUM_FRAME, + frame_style=gui.INTERIOR_FRAME, subviews={ widgets.Panel{ view_id='materials_categories', From f0ca7ad4257b2ab7b69486831ed133908a9437b9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 20:34:54 -0800 Subject: [PATCH 0706/2222] fix all buildings being identified as constructions --- plugins/lua/buildingplan.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index aef04e020..905a5558f 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -694,7 +694,7 @@ local function is_construction() end local function is_stairs() - return is_construction + return is_construction() and uibs.building_subtype == df.construction_type.UpDownStair end From d8e440806c8ad617f29ff2b462d2c0ce407735d2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Feb 2023 23:19:04 -0800 Subject: [PATCH 0707/2222] fix signed/unsigned compare --- plugins/buildingplan/defaultitemfilters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index 4cc6f11cf..36d074363 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -47,7 +47,7 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &f } void DefaultItemFilters::setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index) { - if (item_filters.size() <= index) { + if (index < 0 || item_filters.size() <= (size_t)index) { WARN(status,out).print("invalid index for filter key %d,%d,%d: %d\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), index); return; From fbd3cd44d60b3f57d27d1b694fc720bb061ecd4f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Feb 2023 01:15:22 -0800 Subject: [PATCH 0708/2222] initial mock of filter dialog --- plugins/lua/buildingplan.lua | 183 ++++++++++++++++++++++++++++++----- 1 file changed, 157 insertions(+), 26 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 905a5558f..54e8c9d8d 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -491,6 +491,11 @@ local function can_be_improved(idx) filter.item_type ~= df.item_type.BOULDER end +local OPTIONS_COL_WIDTH = 28 +local TYPE_COL_WIDTH = 20 +local HEADER_HEIGHT = 5 +local FOOTER_HEIGHT = 4 + FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ frame_title='Choose filters', @@ -499,62 +504,76 @@ FilterSelection.ATTRS{ index=DEFAULT_NIL, } +local STANDIN_PEN = to_pen{fg=COLOR_GREEN, bg=COLOR_GREEN, ch=' '} + function FilterSelection:init() self:addviews{ widgets.Panel{ view_id='options_panel', - frame={l=0, t=0, b=5, w=30}, + frame={l=0, t=0, b=FOOTER_HEIGHT, w=OPTIONS_COL_WIDTH}, autoarrange_subviews=true, subviews={ widgets.Panel{ view_id='quality_panel', - frame={l=0, r=0, h=23}, + frame={l=0, r=0, h=24}, + frame_inset={t=1}, frame_style=gui.INTERIOR_FRAME, frame_title='Item quality', subviews={ - widgets.Label{ + widgets.HotkeyLabel{ frame={l=0, t=0}, - text='updown hotkeys', + key='CUSTOM_SHIFT_Q', + }, + widgets.HotkeyLabel{ + frame={l=1, t=0}, + key='CUSTOM_SHIFT_W', + label='Set max quality', }, widgets.Panel{ view_id='quality_slider', frame={l=0, t=2, w=3, h=15}, - frame_background=to_pen{fg=COLOR_GREEN, bg=COLOR_GREEN, ch=' '}, + frame_background=STANDIN_PEN, }, widgets.Label{ frame={l=3, t=3}, - text='- Artifact (num)', + text='- Artifact (1)', }, widgets.Label{ frame={l=3, t=5}, - text='- Masterful (num)', + text='- Masterful (3)', }, widgets.Label{ frame={l=3, t=7}, - text='- Exceptional (num)', + text='- Exceptional (34)', }, widgets.Label{ frame={l=3, t=9}, - text='- Superior (num)', + text='- Superior (50)', }, widgets.Label{ frame={l=3, t=11}, - text='- FinelyCrafted (num)', + text='- FinelyCrafted (67)', }, widgets.Label{ frame={l=3, t=13}, - text='- WellCrafted (num)', + text='- WellCrafted (79)', }, widgets.Label{ frame={l=3, t=15}, - text='- Ordinary (num)', + text='- Ordinary (206)', }, - widgets.Label{ + widgets.HotkeyLabel{ frame={l=0, t=18}, - text='updown hotkeys', + key='CUSTOM_SHIFT_Z', + }, + widgets.HotkeyLabel{ + frame={l=1, t=18}, + key='CUSTOM_SHIFT_X', + label='Set min quality', }, widgets.CycleHotkeyLabel{ frame={l=0, t=20}, + key='CUSTOM_SHIFT_D', label='Decorated only:', options={'No', 'Yes'}, }, @@ -563,6 +582,7 @@ function FilterSelection:init() widgets.ResizingPanel{ view_id='building_panel', frame={l=0, r=0}, + frame_inset={t=1}, frame_style=gui.INTERIOR_FRAME, frame_title='Building options', autoarrange_subviews=true, @@ -574,7 +594,7 @@ function FilterSelection:init() }, widgets.CycleHotkeyLabel{ frame={l=0}, - key='CUSTOM_G', + key='CUSTOM_SHIFT_G', label='Building safety:', options={ {label='Any', value=0}, @@ -587,30 +607,41 @@ function FilterSelection:init() widgets.Panel{ view_id='global_panel', frame={l=0, r=0, b=0}, + frame_inset={t=1}, frame_style=gui.INTERIOR_FRAME, frame_title='Global options', autoarrange_subviews=true, - autoarrange_gap=1, subviews={ widgets.WrappedLabel{ frame={l=0}, text_to_wrap='These options will affect the selection of "Generic Materials" for future buildings.', }, + widgets.Panel{ + frame={h=1}, + }, widgets.ToggleHotkeyLabel{ frame={l=0}, + key='CUSTOM_SHIFT_B', label='Blocks', + label_width=8, }, widgets.ToggleHotkeyLabel{ frame={l=0}, + key='CUSTOM_SHIFT_L', label='Logs', + label_width=8, }, widgets.ToggleHotkeyLabel{ frame={l=0}, + key='CUSTOM_SHIFT_O', label='Boulders', + label_width=8, }, widgets.ToggleHotkeyLabel{ frame={l=0}, + key='CUSTOM_SHIFT_P', label='Bars', + label_width=8, }, }, }, @@ -618,38 +649,138 @@ function FilterSelection:init() }, widgets.Panel{ view_id='materials_panel', - frame={l=30, t=0, b=5, r=0}, + frame={l=OPTIONS_COL_WIDTH, t=0, b=FOOTER_HEIGHT, r=0}, subviews={ widgets.Panel{ - view_id='materials_top', - frame={l=0, t=0, r=0, h=5}, + view_id='header', + frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, subviews={ + widgets.EditField{ + frame={l=1, t=0}, + label_text='Search: ', + on_char=function(ch) return ch:match('%l') end, + }, + widgets.CycleHotkeyLabel{ + frame={l=1, t=2, w=21}, + label='Sort by:', + key='CUSTOM_SHIFT_R', + options={'name', 'available'}, + }, + widgets.ToggleHotkeyLabel{ + frame={l=24, t=2, w=24}, + label='Hide unavailable:', + key='CUSTOM_SHIFT_H', + initial_option=false, + }, + widgets.Label{ + frame={l=1, b=0}, + text='Type', + text_pen=COLOR_LIGHTRED, + }, + widgets.Label{ + frame={l=TYPE_COL_WIDTH, b=0}, + text='Material', + text_pen=COLOR_LIGHTRED, + }, }, }, widgets.Panel{ view_id='materials_lists', - frame={l=0, t=5, r=0, b=0}, + frame={l=0, t=HEADER_HEIGHT, r=0, b=0}, frame_style=gui.INTERIOR_FRAME, subviews={ - widgets.Panel{ + widgets.List{ view_id='materials_categories', - frame={l=0, t=0, b=0, w=20}, - subviews={ + frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, + scroll_keys={}, + choices={ + {text='Stone', key='CUSTOM_SHIFT_S'}, + {text='Wood', key='CUSTOM_SHIFT_W'}, + {text='Metal', key='CUSTOM_SHIFT_M'}, + {text='Other', key='CUSTOM_SHIFT_O'}, }, }, - widgets.Panel{ + widgets.List{ view_id='materials_mats', - frame={l=21, t=0, r=0, b=0}, - subviews={ + frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, + choices={ + {text='9 - granite'}, + {text='0 - graphite'}, }, }, }, }, + widgets.Panel{ + view_id='divider', + frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=0, w=1}, + on_render=self:callback('draw_divider'), + } }, }, + widgets.Panel{ + view_id='footer', + frame={l=0, r=0, b=0, h=FOOTER_HEIGHT}, + frame_inset={l=20, t=1}, + subviews={ + widgets.HotkeyLabel{ + frame={l=0, t=0}, + label='Toggle', + auto_width=true, + key='SELECT', + }, + widgets.HotkeyLabel{ + frame={l=0, t=2}, + label='Done', + auto_width=true, + key='LEAVESCREEN', + }, + widgets.HotkeyLabel{ + frame={l=30, t=0}, + label='Select all', + auto_width=true, + key='CUSTOM_SHIFT_A', + }, + widgets.HotkeyLabel{ + frame={l=30, t=1}, + label='Invert selection', + auto_width=true, + key='CUSTOM_SHIFT_I', + }, + widgets.HotkeyLabel{ + frame={l=30, t=2}, + label='Clear selection', + auto_width=true, + key='CUSTOM_SHIFT_C', + }, + }, + } } end +local texpos = dfhack.textures.getThinBordersTexposStart() +local tp = function(offset) + if texpos == -1 then return nil end + return texpos + offset +end + +local TOP_PEN = to_pen{tile=tp(10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} +local MID_PEN = to_pen{tile=tp(4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} +local BOT_PEN = to_pen{tile=tp(11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} + +function FilterSelection:draw_divider(dc) + local y2 = dc.height - 1 + for y=0,y2 do + dc:seek(0, y) + if y == 0 then + dc:char(nil, TOP_PEN) + elseif y == y2 then + dc:char(nil, BOT_PEN) + else + dc:char(nil, MID_PEN) + end + end +end + FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) FilterSelectionScreen.ATTRS { focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', From ce3ee386fdc48e39d624014e7d87b9cca71b2b83 Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 20:07:12 +0000 Subject: [PATCH 0709/2222] makeSquad, updateRoomAssignments --- docs/dev/Lua API.rst | 13 ++ library/LuaApi.cpp | 2 + library/include/modules/Units.h | 3 + library/modules/Units.cpp | 278 ++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index c2cc7f5cd..b089fb8ea 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1589,6 +1589,19 @@ Units module Returns a table of the cutoffs used by the above stress level functions. +* ``dfhack.units.makeSquad(assignment_id)`` + + Creates a new squad associated with the assignment. Fails if one already exists + Note: This function does not name the squad, but they are otherwise complete + +* ``dfhack.units.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` + + Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks + +* ``dfhack.units.getSquadName(squad)`` + + Returns the name of a squad + Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 426c87566..af56bb4c3 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1812,6 +1812,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getGoalType), WRAPM(Units, getGoalName), WRAPM(Units, isGoalAchieved), + WRAPM(Units, makeSquad), + WRAPM(Units, updateRoomAssignments), WRAPM(Units, getSquadName), WRAPM(Units, getPhysicalDescription), WRAPM(Units, getRaceName), diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 0edebacdc..f3c7baf6a 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -37,6 +37,7 @@ distribution. #include "df/mental_attribute_type.h" #include "df/misc_trait_type.h" #include "df/physical_attribute_type.h" +#include "df/squad.h" #include "df/unit.h" #include "df/unit_action.h" #include "df/unit_action_type_group.h" @@ -223,6 +224,8 @@ DFHACK_EXPORT std::string getGoalName(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT bool isGoalAchieved(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT std::string getSquadName(df::unit *unit); +DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); +DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit); DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 9a415fb34..0ab507517 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -33,6 +33,7 @@ distribution. #include #include #include +#include using namespace std; #include "VersionInfo.h" @@ -51,6 +52,7 @@ using namespace std; #include "MiscUtils.h" #include "df/activity_entry.h" +#include "df/building_civzonest.h" #include "df/burrow.h" #include "df/caste_raw.h" #include "df/creature_raw.h" @@ -61,6 +63,7 @@ using namespace std; #include "df/entity_raw_flags.h" #include "df/identity_type.h" #include "df/game_mode.h" +#include "df/global_objects.h" #include "df/histfig_entity_link_positionst.h" #include "df/histfig_relationship_type.h" #include "df/historical_entity.h" @@ -72,6 +75,10 @@ using namespace std; #include "df/job.h" #include "df/nemesis_record.h" #include "df/squad.h" +#include "df/squad_position.h" +#include "df/squad_schedule_order.h" +#include "df/squad_order.h" +#include "df/squad_order_trainst.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" #include "df/unit_inventory_item.h" @@ -1972,6 +1979,277 @@ std::string Units::getSquadName(df::unit *unit) return Translation::TranslateName(&squad->name, true); } +//only works for making squads for fort mode player controlled dwarf squads +//could be extended straightforwardly by passing in entity +df::squad* Units::makeSquad(int32_t assignment_id) +{ + if (df::global::squad_next_id == nullptr || df::global::plotinfo == nullptr) + return nullptr; + + df::language_name name; + name.type = df::language_name_type::Squad; + + for (int i=0; i < 7; i++) + { + name.words[i] = -1; + name.parts_of_speech[i] = df::part_of_speech::Noun; + } + + df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); + + df::entity_position_assignment* found_assignment = nullptr; + + for (auto* assignment : fort->positions.assignments) + { + if (assignment->id == assignment_id) + { + found_assignment = assignment; + break; + } + } + + if (found_assignment == nullptr) + return nullptr; + + //this function does not attempt to delete or replace squads for assignments + if (found_assignment->squad_id != -1) + return nullptr; + + df::entity_position* corresponding_position = nullptr; + + for (auto* position : fort->positions.own) + { + if (position->id == found_assignment->position_id) + { + corresponding_position = position; + break; + } + } + + if (corresponding_position == nullptr) + return nullptr; + + df::squad* result = new df::squad(); + result->id = *df::global::squad_next_id; + result->cur_routine_idx = 0; + result->uniform_priority = result->id + 1; //no idea why, but seems to hold + result->activity = -1; //?? + result->carry_food = 2; + result->carry_water = 1; + result->entity_id = df::global::plotinfo->group_id; + result->leader_position = corresponding_position->id; + result->leader_assignment = found_assignment->id; + result->unk_1 = -1; + result->name = name; + result->ammo.unk_v50_1 = 0; + + int16_t squad_size = corresponding_position->squad_size; + + for (int i=0; i < squad_size; i++) + { + //construct for squad_position seems to set all the attributes correctly + //except I've observed unk_2 is -1 generally + df::squad_position* pos = new df::squad_position(); + pos->unk_2 = -1; + pos->flags.whole = 0; + + result->positions.push_back(pos); + } + + const auto& routines = df::global::plotinfo->alerts.routines; + + for (const auto& routine : routines) + { + df::squad_schedule_entry* asched = (df::squad_schedule_entry*)malloc(sizeof(df::squad_schedule_entry) * 12); + + for(int kk=0; kk < 12; kk++) + { + new (&asched[kk]) df::squad_schedule_entry; + + for(int jj=0; jj < squad_size; jj++) + { + int32_t* order_assignments = new int32_t(); + *order_assignments = -1; + + asched[kk].order_assignments.push_back(order_assignments); + } + } + + auto insert_training_order = [asched, squad_size](int month) + { + df::squad_schedule_order* order = new df::squad_schedule_order(); + order->min_count = squad_size; + //assumed + order->positions.resize(squad_size); + + df::squad_order* s_order = df::allocate(); + + s_order->unk_v40_1 = -1; + s_order->unk_v40_2 = -1; + s_order->year = *df::global::cur_year; + s_order->year_tick = *df::global::cur_year_tick; + s_order->unk_v40_3 = -1; + s_order->unk_1 = 0; + + order->order = s_order; + + asched[month].orders.push_back(order); + //wear uniform while training + asched[month].uniform_mode = 0; + }; + + //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 + //Off duty: No orders, Sleep/room at will. Equip/orders only + if (routine->name == "Off duty") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 1; + } + } + //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always + //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters + else if (routine->name == "Staggered training") + { + //this is semi randomised for different squads + //appears to be something like squad.id & 1, it isn't smart + //if you alternate squad creation, its 'correctly' staggered + //but it'll also happily not stagger them if you eg delete a squad and make another + std::array indices; + + if ((*df::global::squad_next_id) & 1) + { + indices = {3, 4, 5, 9, 10, 11}; + } + else + { + indices = {0, 1, 2, 6, 7, 8}; + } + + for (int index : indices) + { + insert_training_order(index); + //still sleep in room at will even when training + asched[index].sleep_mode = 0; + } + } + //see above, but with all indices + else if (routine->name == "Constant training") + { + for (int i=0; i < 12; i++) + { + insert_training_order(i); + //still sleep in room at will even when training + asched[i].sleep_mode = 0; + } + } + else if (routine->name == "Ready") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 2; + asched[i].uniform_mode = 0; + } + } + else + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 0; + } + } + + result->schedule.push_back(reinterpret_cast(asched)); + } + + //all we've done so far is leak memory if anything goes wrong + //modify state + (*df::global::squad_next_id)++; + fort->squads.push_back(result->id); + df::global::world->squads.all.push_back(result); + found_assignment->squad_id = result->id; + + //todo: find and modify old squad + + return result; +} + +void Units::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags) +{ + df::squad* squad = df::squad::find(squad_id); + df::building* bzone = df::building::find(civzone_id); + + df::building_civzonest* zone = strict_virtual_cast(bzone); + + if (squad == nullptr || zone == nullptr) + return; + + df::squad::T_rooms* room_from_squad = nullptr; + df::building_civzonest::T_squad_room_info* room_from_building = nullptr; + + for (auto room : squad->rooms) + { + if (room->building_id == civzone_id) + { + room_from_squad = room; + break; + } + } + + for (auto room : zone->squad_room_info) + { + if (room->squad_id == squad_id) + { + room_from_building = room; + break; + } + } + + if (flags.whole == 0 && room_from_squad == nullptr && room_from_building == nullptr) + return; + + //if we're setting 0 flags, and there's no room already, don't set a room + bool avoiding_squad_roundtrip = flags.whole == 0 && room_from_squad == nullptr; + + if (!avoiding_squad_roundtrip && room_from_squad == nullptr) + { + room_from_squad = new df::squad::T_rooms(); + room_from_squad->building_id = civzone_id; + squad->rooms.push_back(room_from_squad); + + std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); + } + + if (room_from_building == nullptr) + { + room_from_building = new df::building_civzonest::T_squad_room_info(); + room_from_building->squad_id = squad_id; + zone->squad_room_info.push_back(room_from_building); + + std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); + } + + if (room_from_squad) + room_from_squad->mode = flags; + + room_from_building->mode = flags; + + if (flags.whole == 0 && !avoiding_squad_roundtrip) + { + for (int i=0; i < (int)squad->rooms.size(); i++) + { + if (squad->rooms[i]->building_id == civzone_id) + { + delete squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + i--; + } + } + } +} + df::activity_entry *Units::getMainSocialActivity(df::unit *unit) { CHECK_NULL_POINTER(unit); From 3912c6290f632d3cb1f41620aeeae1a0e050eaf3 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 30 Jan 2023 06:28:23 +0000 Subject: [PATCH 0710/2222] Military module start --- docs/dev/Lua API.rst | 9 +- library/CMakeLists.txt | 2 + library/LuaApi.cpp | 14 +- library/include/modules/Military.h | 19 ++ library/include/modules/Units.h | 5 - library/modules/Military.cpp | 307 +++++++++++++++++++++++++++++ library/modules/Units.cpp | 284 -------------------------- 7 files changed, 345 insertions(+), 295 deletions(-) create mode 100644 library/include/modules/Military.h create mode 100644 library/modules/Military.cpp diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index b089fb8ea..9125ca902 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1589,16 +1589,19 @@ Units module Returns a table of the cutoffs used by the above stress level functions. -* ``dfhack.units.makeSquad(assignment_id)`` +Military Module API +~~~~~~~~~~~~~~~~~~~ + +* ``dfhack.military.makeSquad(assignment_id)`` Creates a new squad associated with the assignment. Fails if one already exists Note: This function does not name the squad, but they are otherwise complete -* ``dfhack.units.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` +* ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks -* ``dfhack.units.getSquadName(squad)`` +* ``dfhack.military.getSquadName(squad)`` Returns the name of a squad diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 87f869493..92db43563 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -137,6 +137,7 @@ set(MODULE_HEADERS include/modules/MapCache.h include/modules/Maps.h include/modules/Materials.h + include/modules/Military.h include/modules/Once.h include/modules/Persistence.h include/modules/Random.h @@ -164,6 +165,7 @@ set(MODULE_SOURCES modules/MapCache.cpp modules/Maps.cpp modules/Materials.cpp + modules/Military.cpp modules/Once.cpp modules/Persistence.cpp modules/Random.cpp diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index af56bb4c3..13ea1a7fe 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -55,6 +55,7 @@ distribution. #include "modules/MapCache.h" #include "modules/Maps.h" #include "modules/Materials.h" +#include "modules/Military.h" #include "modules/Random.h" #include "modules/Screen.h" #include "modules/Textures.h" @@ -1812,9 +1813,6 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getGoalType), WRAPM(Units, getGoalName), WRAPM(Units, isGoalAchieved), - WRAPM(Units, makeSquad), - WRAPM(Units, updateRoomAssignments), - WRAPM(Units, getSquadName), WRAPM(Units, getPhysicalDescription), WRAPM(Units, getRaceName), WRAPM(Units, getRaceNamePlural), @@ -1939,6 +1937,15 @@ static const luaL_Reg dfhack_units_funcs[] = { { NULL, NULL } }; +/***** Military Module *****/ + +static const LuaWrapper::FunctionReg dfhack_military_module[] = { + WRAPM(Military, makeSquad), + WRAPM(Military, updateRoomAssignments), + WRAPM(Military, getSquadName), + { NULL, NULL } +}; + /***** Items module *****/ static bool items_moveToGround(df::item *item, df::coord pos) @@ -3449,6 +3456,7 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs); OpenModule(state, "textures", dfhack_textures_module); OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs); + OpenModule(state, "military", dfhack_military_module); OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); OpenModule(state, "world", dfhack_world_module, dfhack_world_funcs); diff --git a/library/include/modules/Military.h b/library/include/modules/Military.h new file mode 100644 index 000000000..19ed47ee2 --- /dev/null +++ b/library/include/modules/Military.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Export.h" +#include "DataDefs.h" + +#include "df/squad.h" +#include "df/unit.h" + +namespace DFHack +{ +namespace Military +{ + +DFHACK_EXPORT std::string getSquadName(df::unit *unit); +DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); +DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); + +} +} \ No newline at end of file diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index f3c7baf6a..4fd9246aa 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -37,7 +37,6 @@ distribution. #include "df/mental_attribute_type.h" #include "df/misc_trait_type.h" #include "df/physical_attribute_type.h" -#include "df/squad.h" #include "df/unit.h" #include "df/unit_action.h" #include "df/unit_action_type_group.h" @@ -223,10 +222,6 @@ DFHACK_EXPORT df::goal_type getGoalType(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT std::string getGoalName(df::unit *unit, size_t goalIndex = 0); DFHACK_EXPORT bool isGoalAchieved(df::unit *unit, size_t goalIndex = 0); -DFHACK_EXPORT std::string getSquadName(df::unit *unit); -DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); -DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); - DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit); DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit); diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp new file mode 100644 index 000000000..b67b3a129 --- /dev/null +++ b/library/modules/Military.cpp @@ -0,0 +1,307 @@ +#include +#include +#include +#include "modules/Military.h" +#include "modules/Translation.h" +#include "df/building.h" +#include "df/building_civzonest.h" +#include "df/historical_figure.h" +#include "df/historical_entity.h" +#include "df/entity_position.h" +#include "df/entity_position_assignment.h" +#include "df/plotinfost.h" +#include "df/squad.h" +#include "df/squad_position.h" +#include "df/squad_schedule_order.h" +#include "df/squad_order.h" +#include "df/squad_order_trainst.h" +#include "df/world.h" + +using namespace DFHack; +using namespace df::enums; +using df::global::world; +using df::global::plotinfo; + +std::string Military::getSquadName(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + if (unit->military.squad_id == -1) + return ""; + df::squad *squad = df::squad::find(unit->military.squad_id); + if (!squad) + return ""; + if (squad->alias.size() > 0) + return squad->alias; + return Translation::TranslateName(&squad->name, true); +} + +//only works for making squads for fort mode player controlled dwarf squads +//could be extended straightforwardly by passing in entity +df::squad* Military::makeSquad(int32_t assignment_id) +{ + if (df::global::squad_next_id == nullptr || df::global::plotinfo == nullptr) + return nullptr; + + df::language_name name; + name.type = df::language_name_type::Squad; + + for (int i=0; i < 7; i++) + { + name.words[i] = -1; + name.parts_of_speech[i] = df::part_of_speech::Noun; + } + + df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); + + df::entity_position_assignment* found_assignment = nullptr; + + for (auto* assignment : fort->positions.assignments) + { + if (assignment->id == assignment_id) + { + found_assignment = assignment; + break; + } + } + + if (found_assignment == nullptr) + return nullptr; + + //this function does not attempt to delete or replace squads for assignments + if (found_assignment->squad_id != -1) + return nullptr; + + df::entity_position* corresponding_position = nullptr; + + for (auto* position : fort->positions.own) + { + if (position->id == found_assignment->position_id) + { + corresponding_position = position; + break; + } + } + + if (corresponding_position == nullptr) + return nullptr; + + df::squad* result = new df::squad(); + result->id = *df::global::squad_next_id; + result->cur_routine_idx = 0; + result->uniform_priority = result->id + 1; //no idea why, but seems to hold + result->activity = -1; //?? + result->carry_food = 2; + result->carry_water = 1; + result->entity_id = df::global::plotinfo->group_id; + result->leader_position = corresponding_position->id; + result->leader_assignment = found_assignment->id; + result->unk_1 = -1; + result->name = name; + result->ammo.unk_v50_1 = 0; + + int16_t squad_size = corresponding_position->squad_size; + + for (int i=0; i < squad_size; i++) + { + //construct for squad_position seems to set all the attributes correctly + //except I've observed unk_2 is -1 generally + df::squad_position* pos = new df::squad_position(); + pos->unk_2 = -1; + pos->flags.whole = 0; + + result->positions.push_back(pos); + } + + const auto& routines = df::global::plotinfo->alerts.routines; + + for (const auto& routine : routines) + { + df::squad_schedule_entry* asched = (df::squad_schedule_entry*)malloc(sizeof(df::squad_schedule_entry) * 12); + + for(int kk=0; kk < 12; kk++) + { + new (&asched[kk]) df::squad_schedule_entry; + + for(int jj=0; jj < squad_size; jj++) + { + int32_t* order_assignments = new int32_t(); + *order_assignments = -1; + + asched[kk].order_assignments.push_back(order_assignments); + } + } + + auto insert_training_order = [asched, squad_size](int month) + { + df::squad_schedule_order* order = new df::squad_schedule_order(); + order->min_count = squad_size; + //assumed + order->positions.resize(squad_size); + + df::squad_order* s_order = df::allocate(); + + s_order->unk_v40_1 = -1; + s_order->unk_v40_2 = -1; + s_order->year = *df::global::cur_year; + s_order->year_tick = *df::global::cur_year_tick; + s_order->unk_v40_3 = -1; + s_order->unk_1 = 0; + + order->order = s_order; + + asched[month].orders.push_back(order); + //wear uniform while training + asched[month].uniform_mode = 0; + }; + + //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 + //Off duty: No orders, Sleep/room at will. Equip/orders only + if (routine->name == "Off duty") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 1; + } + } + //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always + //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters + else if (routine->name == "Staggered training") + { + //this is semi randomised for different squads + //appears to be something like squad.id & 1, it isn't smart + //if you alternate squad creation, its 'correctly' staggered + //but it'll also happily not stagger them if you eg delete a squad and make another + std::array indices; + + if ((*df::global::squad_next_id) & 1) + { + indices = {3, 4, 5, 9, 10, 11}; + } + else + { + indices = {0, 1, 2, 6, 7, 8}; + } + + for (int index : indices) + { + insert_training_order(index); + //still sleep in room at will even when training + asched[index].sleep_mode = 0; + } + } + //see above, but with all indices + else if (routine->name == "Constant training") + { + for (int i=0; i < 12; i++) + { + insert_training_order(i); + //still sleep in room at will even when training + asched[i].sleep_mode = 0; + } + } + else if (routine->name == "Ready") + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 2; + asched[i].uniform_mode = 0; + } + } + else + { + for (int i=0; i < 12; i++) + { + asched[i].sleep_mode = 0; + asched[i].uniform_mode = 0; + } + } + + result->schedule.push_back(reinterpret_cast(asched)); + } + + //all we've done so far is leak memory if anything goes wrong + //modify state + (*df::global::squad_next_id)++; + fort->squads.push_back(result->id); + df::global::world->squads.all.push_back(result); + found_assignment->squad_id = result->id; + + //todo: find and modify old squad + + return result; +} + +void Military::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags) +{ + df::squad* squad = df::squad::find(squad_id); + df::building* bzone = df::building::find(civzone_id); + + df::building_civzonest* zone = strict_virtual_cast(bzone); + + if (squad == nullptr || zone == nullptr) + return; + + df::squad::T_rooms* room_from_squad = nullptr; + df::building_civzonest::T_squad_room_info* room_from_building = nullptr; + + for (auto room : squad->rooms) + { + if (room->building_id == civzone_id) + { + room_from_squad = room; + break; + } + } + + for (auto room : zone->squad_room_info) + { + if (room->squad_id == squad_id) + { + room_from_building = room; + break; + } + } + + if (flags.whole == 0 && room_from_squad == nullptr && room_from_building == nullptr) + return; + + //if we're setting 0 flags, and there's no room already, don't set a room + bool avoiding_squad_roundtrip = flags.whole == 0 && room_from_squad == nullptr; + + if (!avoiding_squad_roundtrip && room_from_squad == nullptr) + { + room_from_squad = new df::squad::T_rooms(); + room_from_squad->building_id = civzone_id; + squad->rooms.push_back(room_from_squad); + + std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); + } + + if (room_from_building == nullptr) + { + room_from_building = new df::building_civzonest::T_squad_room_info(); + room_from_building->squad_id = squad_id; + zone->squad_room_info.push_back(room_from_building); + + std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); + } + + if (room_from_squad) + room_from_squad->mode = flags; + + room_from_building->mode = flags; + + if (flags.whole == 0 && !avoiding_squad_roundtrip) + { + for (int i=0; i < (int)squad->rooms.size(); i++) + { + if (squad->rooms[i]->building_id == civzone_id) + { + delete squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + i--; + } + } + } +} \ No newline at end of file diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 0ab507517..137a07da6 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1966,290 +1966,6 @@ bool Units::isGoalAchieved(df::unit *unit, size_t goalIndex) && unit->status.current_soul->personality.dreams[goalIndex]->flags.whole != 0; } -std::string Units::getSquadName(df::unit *unit) -{ - CHECK_NULL_POINTER(unit); - if (unit->military.squad_id == -1) - return ""; - df::squad *squad = df::squad::find(unit->military.squad_id); - if (!squad) - return ""; - if (squad->alias.size() > 0) - return squad->alias; - return Translation::TranslateName(&squad->name, true); -} - -//only works for making squads for fort mode player controlled dwarf squads -//could be extended straightforwardly by passing in entity -df::squad* Units::makeSquad(int32_t assignment_id) -{ - if (df::global::squad_next_id == nullptr || df::global::plotinfo == nullptr) - return nullptr; - - df::language_name name; - name.type = df::language_name_type::Squad; - - for (int i=0; i < 7; i++) - { - name.words[i] = -1; - name.parts_of_speech[i] = df::part_of_speech::Noun; - } - - df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); - - df::entity_position_assignment* found_assignment = nullptr; - - for (auto* assignment : fort->positions.assignments) - { - if (assignment->id == assignment_id) - { - found_assignment = assignment; - break; - } - } - - if (found_assignment == nullptr) - return nullptr; - - //this function does not attempt to delete or replace squads for assignments - if (found_assignment->squad_id != -1) - return nullptr; - - df::entity_position* corresponding_position = nullptr; - - for (auto* position : fort->positions.own) - { - if (position->id == found_assignment->position_id) - { - corresponding_position = position; - break; - } - } - - if (corresponding_position == nullptr) - return nullptr; - - df::squad* result = new df::squad(); - result->id = *df::global::squad_next_id; - result->cur_routine_idx = 0; - result->uniform_priority = result->id + 1; //no idea why, but seems to hold - result->activity = -1; //?? - result->carry_food = 2; - result->carry_water = 1; - result->entity_id = df::global::plotinfo->group_id; - result->leader_position = corresponding_position->id; - result->leader_assignment = found_assignment->id; - result->unk_1 = -1; - result->name = name; - result->ammo.unk_v50_1 = 0; - - int16_t squad_size = corresponding_position->squad_size; - - for (int i=0; i < squad_size; i++) - { - //construct for squad_position seems to set all the attributes correctly - //except I've observed unk_2 is -1 generally - df::squad_position* pos = new df::squad_position(); - pos->unk_2 = -1; - pos->flags.whole = 0; - - result->positions.push_back(pos); - } - - const auto& routines = df::global::plotinfo->alerts.routines; - - for (const auto& routine : routines) - { - df::squad_schedule_entry* asched = (df::squad_schedule_entry*)malloc(sizeof(df::squad_schedule_entry) * 12); - - for(int kk=0; kk < 12; kk++) - { - new (&asched[kk]) df::squad_schedule_entry; - - for(int jj=0; jj < squad_size; jj++) - { - int32_t* order_assignments = new int32_t(); - *order_assignments = -1; - - asched[kk].order_assignments.push_back(order_assignments); - } - } - - auto insert_training_order = [asched, squad_size](int month) - { - df::squad_schedule_order* order = new df::squad_schedule_order(); - order->min_count = squad_size; - //assumed - order->positions.resize(squad_size); - - df::squad_order* s_order = df::allocate(); - - s_order->unk_v40_1 = -1; - s_order->unk_v40_2 = -1; - s_order->year = *df::global::cur_year; - s_order->year_tick = *df::global::cur_year_tick; - s_order->unk_v40_3 = -1; - s_order->unk_1 = 0; - - order->order = s_order; - - asched[month].orders.push_back(order); - //wear uniform while training - asched[month].uniform_mode = 0; - }; - - //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 - //Off duty: No orders, Sleep/room at will. Equip/orders only - if (routine->name == "Off duty") - { - for (int i=0; i < 12; i++) - { - asched[i].sleep_mode = 0; - asched[i].uniform_mode = 1; - } - } - //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always - //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters - else if (routine->name == "Staggered training") - { - //this is semi randomised for different squads - //appears to be something like squad.id & 1, it isn't smart - //if you alternate squad creation, its 'correctly' staggered - //but it'll also happily not stagger them if you eg delete a squad and make another - std::array indices; - - if ((*df::global::squad_next_id) & 1) - { - indices = {3, 4, 5, 9, 10, 11}; - } - else - { - indices = {0, 1, 2, 6, 7, 8}; - } - - for (int index : indices) - { - insert_training_order(index); - //still sleep in room at will even when training - asched[index].sleep_mode = 0; - } - } - //see above, but with all indices - else if (routine->name == "Constant training") - { - for (int i=0; i < 12; i++) - { - insert_training_order(i); - //still sleep in room at will even when training - asched[i].sleep_mode = 0; - } - } - else if (routine->name == "Ready") - { - for (int i=0; i < 12; i++) - { - asched[i].sleep_mode = 2; - asched[i].uniform_mode = 0; - } - } - else - { - for (int i=0; i < 12; i++) - { - asched[i].sleep_mode = 0; - asched[i].uniform_mode = 0; - } - } - - result->schedule.push_back(reinterpret_cast(asched)); - } - - //all we've done so far is leak memory if anything goes wrong - //modify state - (*df::global::squad_next_id)++; - fort->squads.push_back(result->id); - df::global::world->squads.all.push_back(result); - found_assignment->squad_id = result->id; - - //todo: find and modify old squad - - return result; -} - -void Units::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags) -{ - df::squad* squad = df::squad::find(squad_id); - df::building* bzone = df::building::find(civzone_id); - - df::building_civzonest* zone = strict_virtual_cast(bzone); - - if (squad == nullptr || zone == nullptr) - return; - - df::squad::T_rooms* room_from_squad = nullptr; - df::building_civzonest::T_squad_room_info* room_from_building = nullptr; - - for (auto room : squad->rooms) - { - if (room->building_id == civzone_id) - { - room_from_squad = room; - break; - } - } - - for (auto room : zone->squad_room_info) - { - if (room->squad_id == squad_id) - { - room_from_building = room; - break; - } - } - - if (flags.whole == 0 && room_from_squad == nullptr && room_from_building == nullptr) - return; - - //if we're setting 0 flags, and there's no room already, don't set a room - bool avoiding_squad_roundtrip = flags.whole == 0 && room_from_squad == nullptr; - - if (!avoiding_squad_roundtrip && room_from_squad == nullptr) - { - room_from_squad = new df::squad::T_rooms(); - room_from_squad->building_id = civzone_id; - squad->rooms.push_back(room_from_squad); - - std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); - } - - if (room_from_building == nullptr) - { - room_from_building = new df::building_civzonest::T_squad_room_info(); - room_from_building->squad_id = squad_id; - zone->squad_room_info.push_back(room_from_building); - - std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); - } - - if (room_from_squad) - room_from_squad->mode = flags; - - room_from_building->mode = flags; - - if (flags.whole == 0 && !avoiding_squad_roundtrip) - { - for (int i=0; i < (int)squad->rooms.size(); i++) - { - if (squad->rooms[i]->building_id == civzone_id) - { - delete squad->rooms[i]; - squad->rooms.erase(squad->rooms.begin() + i); - i--; - } - } - } -} - df::activity_entry *Units::getMainSocialActivity(df::unit *unit) { CHECK_NULL_POINTER(unit); From d84b1187678ce65a1106a3cec54a73fa3c5d9fbe Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 30 Jan 2023 07:11:42 +0000 Subject: [PATCH 0711/2222] docs, rework, rename --- docs/changelog.txt | 7 +++++++ docs/dev/Lua API.rst | 10 +++++----- library/LuaApi.cpp | 2 +- library/include/modules/Military.h | 6 +++--- library/modules/Military.cpp | 9 +++------ plugins/manipulator.cpp | 3 ++- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index dd109a218..2da870ca7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -171,6 +171,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Screen::Pen``: now accepts ``top_of_text`` and ``bottom_of_text`` properties to support offset text in graphics mode - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - ``Lua::Push``: now supports ``std::unordered_map`` +- `Military`: New module for military functionality +- `Military`: new ``makeSquad`` to create a squad +- `Military`: changed ``getSquadName`` to take a squad identifier +- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range +- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play @@ -181,6 +187,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked - ``widgets.Label``: token ``tile`` properties can now be either pens or numeric texture ids - `tiletypes`: now has a Lua API! ``tiletypes_setTile`` +- ``maps.getBiomeType``: exposed preexisting function to Lua ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9125ca902..304ce6651 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1594,16 +1594,16 @@ Military Module API * ``dfhack.military.makeSquad(assignment_id)`` - Creates a new squad associated with the assignment. Fails if one already exists - Note: This function does not name the squad, but they are otherwise complete + Creates a new squad associated with the assignment. Fails if one already exists. + Note: This function does not name the squad, but they are otherwise complete. * ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` - Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks + Sets the sleep, train, indiv_eq, and squad_eq flags when training at a barracks. -* ``dfhack.military.getSquadName(squad)`` +* ``dfhack.military.getSquadName(squad_id)`` - Returns the name of a squad + Returns the name of a squad/ Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 13ea1a7fe..c9bdc3021 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1940,7 +1940,7 @@ static const luaL_Reg dfhack_units_funcs[] = { /***** Military Module *****/ static const LuaWrapper::FunctionReg dfhack_military_module[] = { - WRAPM(Military, makeSquad), + WRAPM(Military, makeSquad), WRAPM(Military, updateRoomAssignments), WRAPM(Military, getSquadName), { NULL, NULL } diff --git a/library/include/modules/Military.h b/library/include/modules/Military.h index 19ed47ee2..3a9710687 100644 --- a/library/include/modules/Military.h +++ b/library/include/modules/Military.h @@ -10,10 +10,10 @@ namespace DFHack { namespace Military { - -DFHACK_EXPORT std::string getSquadName(df::unit *unit); + +DFHACK_EXPORT std::string getSquadName(int32_t squad_id); DFHACK_EXPORT df::squad* makeSquad(int32_t assignment_id); DFHACK_EXPORT void updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::squad_use_flags flags); } -} \ No newline at end of file +} diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index b67b3a129..1dd71f11d 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -22,12 +22,9 @@ using namespace df::enums; using df::global::world; using df::global::plotinfo; -std::string Military::getSquadName(df::unit *unit) +std::string Military::getSquadName(int32_t squad_id) { - CHECK_NULL_POINTER(unit); - if (unit->military.squad_id == -1) - return ""; - df::squad *squad = df::squad::find(unit->military.squad_id); + df::squad *squad = df::squad::find(squad_id); if (!squad) return ""; if (squad->alias.size() > 0) @@ -304,4 +301,4 @@ void Military::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::s } } } -} \ No newline at end of file +} diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 6731aa513..b8f6ce706 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1305,7 +1306,7 @@ void viewscreen_unitlaborsst::refreshNames() cur->job_mode = UnitInfo::JOB; } if (unit->military.squad_id > -1) { - cur->squad_effective_name = Units::getSquadName(unit); + cur->squad_effective_name = Military::getSquadName(unit->military.squad_id); cur->squad_info = stl_sprintf("%i", unit->military.squad_position + 1) + "." + cur->squad_effective_name; } else { cur->squad_effective_name = ""; From 2bd48f1f90ec2ac5d8b07dd7fd3c216041c11f10 Mon Sep 17 00:00:00 2001 From: 20k Date: Tue, 31 Jan 2023 05:31:58 +0000 Subject: [PATCH 0712/2222] address some review comments --- docs/dev/Lua API.rst | 4 ++-- library/include/modules/Military.h | 1 - library/modules/Units.cpp | 8 -------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 304ce6651..ec7ff57df 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1589,7 +1589,7 @@ Units module Returns a table of the cutoffs used by the above stress level functions. -Military Module API +Military module ~~~~~~~~~~~~~~~~~~~ * ``dfhack.military.makeSquad(assignment_id)`` @@ -1603,7 +1603,7 @@ Military Module API * ``dfhack.military.getSquadName(squad_id)`` - Returns the name of a squad/ + Returns the name of a squad. Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/include/modules/Military.h b/library/include/modules/Military.h index 3a9710687..8ceb987b5 100644 --- a/library/include/modules/Military.h +++ b/library/include/modules/Military.h @@ -4,7 +4,6 @@ #include "DataDefs.h" #include "df/squad.h" -#include "df/unit.h" namespace DFHack { diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 137a07da6..a636310e1 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -33,7 +33,6 @@ distribution. #include #include #include -#include using namespace std; #include "VersionInfo.h" @@ -52,7 +51,6 @@ using namespace std; #include "MiscUtils.h" #include "df/activity_entry.h" -#include "df/building_civzonest.h" #include "df/burrow.h" #include "df/caste_raw.h" #include "df/creature_raw.h" @@ -63,7 +61,6 @@ using namespace std; #include "df/entity_raw_flags.h" #include "df/identity_type.h" #include "df/game_mode.h" -#include "df/global_objects.h" #include "df/histfig_entity_link_positionst.h" #include "df/histfig_relationship_type.h" #include "df/historical_entity.h" @@ -74,11 +71,6 @@ using namespace std; #include "df/identity.h" #include "df/job.h" #include "df/nemesis_record.h" -#include "df/squad.h" -#include "df/squad_position.h" -#include "df/squad_schedule_order.h" -#include "df/squad_order.h" -#include "df/squad_order_trainst.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" #include "df/unit_inventory_item.h" From 1eeefdd598007d24940ba95fd2b3febdb33710c4 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 07:45:08 +0000 Subject: [PATCH 0713/2222] clean up a variety of unks --- library/modules/Military.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 1dd71f11d..a0d3cea2a 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -92,18 +92,15 @@ df::squad* Military::makeSquad(int32_t assignment_id) result->entity_id = df::global::plotinfo->group_id; result->leader_position = corresponding_position->id; result->leader_assignment = found_assignment->id; - result->unk_1 = -1; result->name = name; - result->ammo.unk_v50_1 = 0; + result->ammo.update = 0; int16_t squad_size = corresponding_position->squad_size; for (int i=0; i < squad_size; i++) { //construct for squad_position seems to set all the attributes correctly - //except I've observed unk_2 is -1 generally df::squad_position* pos = new df::squad_position(); - pos->unk_2 = -1; pos->flags.whole = 0; result->positions.push_back(pos); @@ -137,12 +134,9 @@ df::squad* Military::makeSquad(int32_t assignment_id) df::squad_order* s_order = df::allocate(); - s_order->unk_v40_1 = -1; - s_order->unk_v40_2 = -1; s_order->year = *df::global::cur_year; s_order->year_tick = *df::global::cur_year_tick; s_order->unk_v40_3 = -1; - s_order->unk_1 = 0; order->order = s_order; From 19616f7e32fe6329c5ea363d07cd6faac401bb01 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 07:48:41 +0000 Subject: [PATCH 0714/2222] fix changelog issues # Conflicts: # docs/changelog.txt --- docs/changelog.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2da870ca7..6f24ee642 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -79,6 +79,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` +- `Military`: New module for military functionality +- `Military`: new ``makeSquad`` to create a squad +- `Military`: changed ``getSquadName`` to take a squad identifier +- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently @@ -171,12 +177,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Screen::Pen``: now accepts ``top_of_text`` and ``bottom_of_text`` properties to support offset text in graphics mode - `overlay`: overlay widgets can now specify a default enabled state if they are not already set in the player's overlay config file - ``Lua::Push``: now supports ``std::unordered_map`` -- `Military`: New module for military functionality -- `Military`: new ``makeSquad`` to create a squad -- `Military`: changed ``getSquadName`` to take a squad identifier -- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range -- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency -- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play From 837f32fdee04bb50fef7b2d2b338b7bba4c855ad Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 07:51:16 +0000 Subject: [PATCH 0715/2222] more changelog fixes --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 6f24ee642..4753371e0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -90,6 +90,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. - ``widgets.TabBar``: new library widget (migrated from control-panel.lua) +- ``maps.getBiomeType``: exposed preexisting function to Lua # 50.07-alpha1 @@ -187,7 +188,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ ``gui.ZScreen``: new attribute: ``defocusable`` for controlling whether a window loses keyboard focus when the map is clicked - ``widgets.Label``: token ``tile`` properties can now be either pens or numeric texture ids - `tiletypes`: now has a Lua API! ``tiletypes_setTile`` -- ``maps.getBiomeType``: exposed preexisting function to Lua ## Removed - `autohauler`: no plans to port to v50, as it just doesn't make sense with the new work detail system From e50f3dbb64cb6c9b18611b7566e937159fbc2f86 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 20 Feb 2023 18:02:47 +0000 Subject: [PATCH 0716/2222] remove unnecessary init --- library/modules/Military.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index a0d3cea2a..e30ea0ae4 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -101,7 +101,6 @@ df::squad* Military::makeSquad(int32_t assignment_id) { //construct for squad_position seems to set all the attributes correctly df::squad_position* pos = new df::squad_position(); - pos->flags.whole = 0; result->positions.push_back(pos); } From 0c9a9c8b9e4fa2bae92fafdb97964ab687d278f7 Mon Sep 17 00:00:00 2001 From: 20k Date: Tue, 21 Feb 2023 20:27:32 +0000 Subject: [PATCH 0717/2222] cleanup remaining unk --- library/modules/Military.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index e30ea0ae4..9a1fed614 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -135,7 +135,6 @@ df::squad* Military::makeSquad(int32_t assignment_id) s_order->year = *df::global::cur_year; s_order->year_tick = *df::global::cur_year_tick; - s_order->unk_v40_3 = -1; order->order = s_order; From 63d752b3f836d0dd4d72344fade8b60756404e85 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 10:53:30 -0800 Subject: [PATCH 0718/2222] update docs --- docs/guides/quickfort-user-guide.rst | 2 +- docs/plugins/buildingplan.rst | 97 +++++++++++++++++----------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/docs/guides/quickfort-user-guide.rst b/docs/guides/quickfort-user-guide.rst index 6f606c43a..ecee46918 100644 --- a/docs/guides/quickfort-user-guide.rst +++ b/docs/guides/quickfort-user-guide.rst @@ -1308,7 +1308,7 @@ legacy Python Quickfort. This setting has no effect on DFHack Quickfort, which will use buildingplan to manage everything designated in a ``#build`` blueprint regardless of the buildingplan UI settings. -However, quickfort *does* use `buildingplan's filters ` +However, quickfort *does* use `buildingplan's filters ` for each building type. For example, you can use the buildingplan UI to set the type of stone you want your walls made out of. Or you can specify that all buildingplan-managed chairs and tables must be of Masterful quality. The current diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index a331e9eb8..f77e5b590 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -2,32 +2,73 @@ buildingplan ============ .. dfhack-tool:: - :summary: Plan building construction before you have materials. + :summary: Plan building layouts with or without materials. :tags: fort design buildings -This plugin adds a planning mode for building placement. You can then place -furniture, constructions, and other buildings before the required materials are -available, and they will be created in a suspended state. Buildingplan will -periodically scan for appropriate items, and the jobs will be unsuspended when -the items are available. - -This is very powerful when used with tools like `quickfort`, which allow you to -set a building plan according to a blueprint, and the buildings will simply be -built when you can build them. - -You can use manager work orders or `workflow` to ensure you always have one or -two doors/beds/tables/chairs/etc. available, and place as many as you like. -Materials are used to build the planned buildings as they are produced, with -minimal space dedicated to stockpiles. +Buildingplan allows you to place furniture, constructions, and other buildings, +regardless of whether the required materials are available. This allows you to +focus purely on design elements when you are laying out your fort, and defers +item production concerns to a more convenient time. + +Buildingplan is as an alternative to the vanilla building placement UI. It +appears after you have selected the type of building, furniture, or construction +that you want to place in the vanilla build menu. Buildingplan then takes over +for the actual placement step. If any building materials are not available yet +for the placed building, it will be created in a suspended state. Buildingplan +will periodically scan for appropriate items and attach them. Once all items are +attached, the construction job will be unsuspended and a dwarf will come and +build the building. If you have the `unsuspend` overlay enabled (it is enabled +by default), then buildingplan-suspended buildings will appear with a ``P`` marker +on the main map, as opposed to the usual ``x`` marker for "regular" suspended +buildings. + +If you want to impose restrictions on which items are chosen for the buildings, +buildingplan has full support for quality and material filters. Before you place +a building, you can select a component item in the list and hit ``f`` or click on +the ``filter`` button next to the item description. This will let you choose your +desired item quality range, whether the item must be decorated, and even which +specific materials the item must be made out of. This lets you create layouts +with a consistent color, if that is part of your design. + +If you just care about the heat sensitivity of the building, you can set the +building to be fire- or magma-proof in the placement UI screen or in any item +filter screen, and the restriction will apply to all building items. This makes it +very easy to create magma-safe pump stacks, for example. + +Buildingplan works very well in conjuction with other design tools like +`gui/quickfort`, which allow you to apply a building layout from a blueprint. You +can apply very large, complicated layouts, and the buildings will simply be built +when your dwarves get around to producing the needed materials. If you set filters +in the buildingplan UI before applying the blueprint, the filters will be applied +to the blueprint buildings, just as if you had planned them from the buildingplan +placement UI. + +One way to integrate buildingplan into your gameplay is to create manager +workorders to ensure you always have a few blocks/doors/beds/etc. available. You +can then place as many of each building as you like. Produced items will be used +to build the planned buildings as they are produced, with minimal space dedicated +to stockpiles. The DFHack `orders` library can help with setting up these manager +workorders for you. + +If you do not wish to use the ``buildingplan`` interface, you can turn off the +``buildingplan.planner`` overlay in `gui/overlay`. You should not disable the +``buildingplan`` service entirely in `gui/control-panel` since then existing +planned buildings in loaded forts will stop functioning. Usage ----- :: - enable buildingplan buildingplan [status] - buildingplan set true|false + buildingplan set (true|false) + +Examples +-------- + +``buildingplan`` + Print a report of current settings, which kinds of buildings are planned, + and what kinds of materials the buildings are waiting for. .. _buildingplan-settings: @@ -49,23 +90,5 @@ want to add this line to your ``dfhack-config/init/onMapLoad.init`` file to always configure `buildingplan` to just use blocks for buildings and constructions:: - on-new-fortress buildingplan set boulders false; buildingplan set logs false - -.. _buildingplan-filters: - -Item filtering --------------- - -While placing a building, you can set filters for what materials you want the -building made out of, what quality you want the component items to be, and -whether you want the items to be decorated. - -If a building type takes more than one item to construct, use -:kbd:`Ctrl`:kbd:`Left` and :kbd:`Ctrl`:kbd:`Right` to select the item that you -want to set filters for. Any filters that you set will be used for all buildings -of the selected type placed from that point onward (until you set a new filter -or clear the current one). Buildings placed before the filters were changed will -keep the filter values that were set when the building was placed. - -For example, you can be sure that all your constructed walls are the same color -by setting a filter to accept only certain types of stone. + on-new-fortress buildingplan set boulders false + on-new-fortress buildingplan set logs false From 97ee1022c7200b1c47b7c5e36c3260dcba50582c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 10:55:50 -0800 Subject: [PATCH 0719/2222] note that filter page is a mock --- plugins/lua/buildingplan.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 54e8c9d8d..0e1341a3b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -498,7 +498,7 @@ local FOOTER_HEIGHT = 4 FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ - frame_title='Choose filters', + frame_title='Choose filters [MOCK -- NOT FUNCTIONAL]', frame={w=80, h=53, l=30, t=8}, resizable=true, index=DEFAULT_NIL, From 3c1d3ce21c57f8fa3bbd06f90fb6ee09f30b86e1 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 27 Feb 2023 01:45:10 +0000 Subject: [PATCH 0720/2222] rework docs, comments, clean up unnecessary init --- docs/changelog.txt | 2 +- docs/dev/Lua API.rst | 8 +++++--- library/modules/Military.cpp | 13 +++---------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4753371e0..412ee07a0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -82,7 +82,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `Military`: New module for military functionality - `Military`: new ``makeSquad`` to create a squad - `Military`: changed ``getSquadName`` to take a squad identifier -- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range - ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency - ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ec7ff57df..54ac4d48e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1594,8 +1594,10 @@ Military module * ``dfhack.military.makeSquad(assignment_id)`` - Creates a new squad associated with the assignment. Fails if one already exists. - Note: This function does not name the squad, but they are otherwise complete. + Creates a new squad associated with the assignment (ie df::entity_position_assignment, via `id``) and returns it. + Fails if a squad already exists that is associated with that assignment, or if the assignment is not a fort mode player controlled squad. + Note: This function does not name the squad: consider setting a nickname (under result.name.nickname), and/or filling out the language_name object at result.name. + The returned squad is otherwise complete and requires no more setup to work correctly. * ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` @@ -1603,7 +1605,7 @@ Military module * ``dfhack.military.getSquadName(squad_id)`` - Returns the name of a squad. + Returns the name of a squad as a string. Action Timer API ~~~~~~~~~~~~~~~~ diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 9a1fed614..07925e8e4 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -84,16 +84,13 @@ df::squad* Military::makeSquad(int32_t assignment_id) df::squad* result = new df::squad(); result->id = *df::global::squad_next_id; - result->cur_routine_idx = 0; result->uniform_priority = result->id + 1; //no idea why, but seems to hold - result->activity = -1; //?? result->carry_food = 2; result->carry_water = 1; result->entity_id = df::global::plotinfo->group_id; result->leader_position = corresponding_position->id; result->leader_assignment = found_assignment->id; result->name = name; - result->ammo.update = 0; int16_t squad_size = corresponding_position->squad_size; @@ -143,7 +140,7 @@ df::squad* Military::makeSquad(int32_t assignment_id) asched[month].uniform_mode = 0; }; - //I thought this was a terrible hack, but its literally how dwarf fortress does it 1:1 + //Dwarf fortress does do this via a series of string comparisons //Off duty: No orders, Sleep/room at will. Equip/orders only if (routine->name == "Off duty") { @@ -153,8 +150,7 @@ df::squad* Military::makeSquad(int32_t assignment_id) asched[i].uniform_mode = 1; } } - //Staggered Training: Training orders at 3 4 5, 9 10 11, sleep/room at will. Equip/orders only, except train months which are equip/always - //always seen the training indices 0 1 2 6 7 8, so its unclear. Check if squad id matters + //Staggered Training: Training orders at months 3 4 5 9 10 11, *or* 0 1 2 6 7 8, sleep/room at will. Equip/orders only, except train months which are equip/always else if (routine->name == "Staggered training") { //this is semi randomised for different squads @@ -209,15 +205,12 @@ df::squad* Military::makeSquad(int32_t assignment_id) result->schedule.push_back(reinterpret_cast(asched)); } - //all we've done so far is leak memory if anything goes wrong - //modify state + //Modify necessary world state (*df::global::squad_next_id)++; fort->squads.push_back(result->id); df::global::world->squads.all.push_back(result); found_assignment->squad_id = result->id; - //todo: find and modify old squad - return result; } From c0bd452c86577aa49d159dba8858078d2179f23a Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 27 Feb 2023 02:06:36 +0000 Subject: [PATCH 0721/2222] add a failure case check just in case --- library/modules/Military.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 07925e8e4..3b7ce7bec 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -50,6 +50,9 @@ df::squad* Military::makeSquad(int32_t assignment_id) df::historical_entity* fort = df::historical_entity::find(df::global::plotinfo->group_id); + if (fort == nullptr) + return nullptr; + df::entity_position_assignment* found_assignment = nullptr; for (auto* assignment : fort->positions.assignments) From c38a288eee9775706decba2b0f69b43c4550d4c3 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 27 Feb 2023 02:15:26 +0000 Subject: [PATCH 0722/2222] use insert_into_vector, tweak docs again --- docs/dev/Lua API.rst | 4 ++-- library/modules/Military.cpp | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 54ac4d48e..63803a66a 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1594,9 +1594,9 @@ Military module * ``dfhack.military.makeSquad(assignment_id)`` - Creates a new squad associated with the assignment (ie df::entity_position_assignment, via `id``) and returns it. + Creates a new squad associated with the assignment (ie ``df::entity_position_assignment``, via ``id``) and returns it. Fails if a squad already exists that is associated with that assignment, or if the assignment is not a fort mode player controlled squad. - Note: This function does not name the squad: consider setting a nickname (under result.name.nickname), and/or filling out the language_name object at result.name. + Note: This function does not name the squad: consider setting a nickname (under ``squad.name.nickname``), and/or filling out the ``language_name`` object at ``squad.name``. The returned squad is otherwise complete and requires no more setup to work correctly. * ``dfhack.military.updateRoomAssignments(squad_id, assignment_id, squad_use_flags)`` diff --git a/library/modules/Military.cpp b/library/modules/Military.cpp index 3b7ce7bec..b402cc0fa 100644 --- a/library/modules/Military.cpp +++ b/library/modules/Military.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "MiscUtils.h" #include "modules/Military.h" #include "modules/Translation.h" #include "df/building.h" @@ -258,18 +259,16 @@ void Military::updateRoomAssignments(int32_t squad_id, int32_t civzone_id, df::s { room_from_squad = new df::squad::T_rooms(); room_from_squad->building_id = civzone_id; - squad->rooms.push_back(room_from_squad); - std::sort(squad->rooms.begin(), squad->rooms.end(), [](df::squad::T_rooms* a, df::squad::T_rooms* b){return a->building_id < b->building_id;}); + insert_into_vector(squad->rooms, &df::squad::T_rooms::building_id, room_from_squad); } if (room_from_building == nullptr) { room_from_building = new df::building_civzonest::T_squad_room_info(); room_from_building->squad_id = squad_id; - zone->squad_room_info.push_back(room_from_building); - std::sort(zone->squad_room_info.begin(), zone->squad_room_info.end(), [](df::building_civzonest::T_squad_room_info* a, df::building_civzonest::T_squad_room_info* b){return a->squad_id < b->squad_id;}); + insert_into_vector(zone->squad_room_info, &df::building_civzonest::T_squad_room_info::squad_id, room_from_building); } if (room_from_squad) From 851bb50dc8e58f05f2ad4f231a1dbd8755204c61 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 15:37:54 -0800 Subject: [PATCH 0723/2222] add SDL_PushEvent shim for RemoteFortressReader --- library/include/modules/DFSDL.h | 2 ++ library/modules/DFSDL.cpp | 6 ++++++ plugins/remotefortressreader/remotefortressreader.cpp | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h index b5fd119f7..226b9f881 100644 --- a/library/include/modules/DFSDL.h +++ b/library/include/modules/DFSDL.h @@ -8,6 +8,7 @@ namespace DFHack // SDL stand-in type definitions typedef signed short SINT16; typedef void DFSDL_sem; + typedef void DFSDL_Event; typedef struct { @@ -86,6 +87,7 @@ DFHACK_EXPORT DFSDL_Surface * DFSDL_ConvertSurface(DFSDL_Surface *src, const DFS DFHACK_EXPORT void DFSDL_FreeSurface(DFSDL_Surface *surface); DFHACK_EXPORT int DFSDL_SemWait(DFSDL_sem *sem); DFHACK_EXPORT int DFSDL_SemPost(DFSDL_sem *sem); +DFHACK_EXPORT int DFSDL_PushEvent(DFSDL_Event *event); } diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index a7847fdb5..08e213b94 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -34,6 +34,7 @@ DFSDL_Surface * (*g_SDL_ConvertSurface)(DFSDL_Surface *, const DFSDL_PixelFormat void (*g_SDL_FreeSurface)(DFSDL_Surface *); int (*g_SDL_SemWait)(DFSDL_sem *); int (*g_SDL_SemPost)(DFSDL_sem *); +int (*g_SDL_PushEvent)(DFSDL_Event *); bool DFSDL::init(color_ostream &out) { for (auto &lib_str : SDL_LIBS) { @@ -69,6 +70,7 @@ bool DFSDL::init(color_ostream &out) { bind(g_sdl_handle, SDL_FreeSurface); bind(g_sdl_handle, SDL_SemWait); bind(g_sdl_handle, SDL_SemPost); + bind(g_sdl_handle, SDL_PushEvent); #undef bind DEBUG(dfsdl,out).print("sdl successfully loaded\n"); @@ -118,3 +120,7 @@ int DFSDL::DFSDL_SemWait(DFSDL_sem *sem) { int DFSDL::DFSDL_SemPost(DFSDL_sem *sem) { return g_SDL_SemPost(sem); } + +int DFSDL::DFSDL_PushEvent(DFSDL_Event *event) { + return g_SDL_PushEvent(event); +} diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index d1a269d52..f33044877 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -27,6 +27,7 @@ #include "modules/MapCache.h" #include "modules/Maps.h" #include "modules/Materials.h" +#include "modules/DFSDL.h" #include "modules/Translation.h" #include "modules/Units.h" #include "modules/World.h" @@ -2897,7 +2898,7 @@ static command_result PassKeyboardEvent(color_ostream &stream, const KeyboardEve e.key.ksym.scancode = in->scancode(); e.key.ksym.sym = (SDL::Key)in->sym(); e.key.ksym.unicode = in->unicode(); - SDL_PushEvent(&e); + DFHack::DFSDL::DFSDL_PushEvent(&e); #endif return CR_OK; } From c513c246a5be456b9c452c3dd080ec206e21dc0d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 21:17:25 -0800 Subject: [PATCH 0724/2222] more SDL wrapping for stonesense --- library/include/modules/DFSDL.h | 2 ++ library/modules/DFSDL.cpp | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/library/include/modules/DFSDL.h b/library/include/modules/DFSDL.h index 226b9f881..9f07ea3db 100644 --- a/library/include/modules/DFSDL.h +++ b/library/include/modules/DFSDL.h @@ -81,7 +81,9 @@ void cleanup(); DFHACK_EXPORT DFSDL_Surface * DFIMG_Load(const char *file); DFHACK_EXPORT int DFSDL_SetAlpha(DFSDL_Surface *surface, uint32_t flag, uint8_t alpha); +DFHACK_EXPORT DFSDL_Surface * DFSDL_GetVideoSurface(void); DFHACK_EXPORT DFSDL_Surface * DFSDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask); +DFHACK_EXPORT DFSDL_Surface * DFSDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask); DFHACK_EXPORT int DFSDL_UpperBlit(DFSDL_Surface *src, const DFSDL_Rect *srcrect, DFSDL_Surface *dst, DFSDL_Rect *dstrect); DFHACK_EXPORT DFSDL_Surface * DFSDL_ConvertSurface(DFSDL_Surface *src, const DFSDL_PixelFormat *fmt, uint32_t flags); DFHACK_EXPORT void DFSDL_FreeSurface(DFSDL_Surface *surface); diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index 08e213b94..6a3e6af2f 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -28,13 +28,15 @@ static const std::vector SDL_IMAGE_LIBS { DFSDL_Surface * (*g_IMG_Load)(const char *) = nullptr; int (*g_SDL_SetAlpha)(DFSDL_Surface *, uint32_t, uint8_t) = nullptr; -DFSDL_Surface * (*g_SDL_CreateRGBSurface)(uint32_t, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t); -int (*g_SDL_UpperBlit)(DFSDL_Surface *, const DFSDL_Rect *, DFSDL_Surface *, DFSDL_Rect *); -DFSDL_Surface * (*g_SDL_ConvertSurface)(DFSDL_Surface *, const DFSDL_PixelFormat *, uint32_t); -void (*g_SDL_FreeSurface)(DFSDL_Surface *); -int (*g_SDL_SemWait)(DFSDL_sem *); -int (*g_SDL_SemPost)(DFSDL_sem *); -int (*g_SDL_PushEvent)(DFSDL_Event *); +DFSDL_Surface * (*g_SDL_GetVideoSurface)(void) = nullptr; +DFSDL_Surface * (*g_SDL_CreateRGBSurface)(uint32_t, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t) = nullptr; +DFSDL_Surface * (*g_SDL_CreateRGBSurfaceFrom)(void *pixels, int width, int height, int depth, int pitch, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = nullptr; +int (*g_SDL_UpperBlit)(DFSDL_Surface *, const DFSDL_Rect *, DFSDL_Surface *, DFSDL_Rect *) = nullptr; +DFSDL_Surface * (*g_SDL_ConvertSurface)(DFSDL_Surface *, const DFSDL_PixelFormat *, uint32_t) = nullptr; +void (*g_SDL_FreeSurface)(DFSDL_Surface *) = nullptr; +int (*g_SDL_SemWait)(DFSDL_sem *) = nullptr; +int (*g_SDL_SemPost)(DFSDL_sem *) = nullptr; +int (*g_SDL_PushEvent)(DFSDL_Event *) = nullptr; bool DFSDL::init(color_ostream &out) { for (auto &lib_str : SDL_LIBS) { @@ -64,7 +66,9 @@ bool DFSDL::init(color_ostream &out) { bind(g_sdl_image_handle, IMG_Load); bind(g_sdl_handle, SDL_SetAlpha); + bind(g_sdl_handle, SDL_GetVideoSurface); bind(g_sdl_handle, SDL_CreateRGBSurface); + bind(g_sdl_handle, SDL_CreateRGBSurfaceFrom); bind(g_sdl_handle, SDL_UpperBlit); bind(g_sdl_handle, SDL_ConvertSurface); bind(g_sdl_handle, SDL_FreeSurface); @@ -97,10 +101,18 @@ int DFSDL::DFSDL_SetAlpha(DFSDL_Surface *surface, uint32_t flag, uint8_t alpha) return g_SDL_SetAlpha(surface, flag, alpha); } +DFSDL_Surface * DFSDL::DFSDL_GetVideoSurface(void) { + return g_SDL_GetVideoSurface(); +} + DFSDL_Surface * DFSDL::DFSDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) { return g_SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask); } +DFSDL_Surface * DFSDL::DFSDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) { + return g_SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask); +} + int DFSDL::DFSDL_UpperBlit(DFSDL_Surface *src, const DFSDL_Rect *srcrect, DFSDL_Surface *dst, DFSDL_Rect *dstrect) { return g_SDL_UpperBlit(src, srcrect, dst, dstrect); } From 8f99b03a534dbcc8c56221aec445b8c99263cd26 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 21:25:05 -0800 Subject: [PATCH 0725/2222] update stonesense ref --- plugins/stonesense | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stonesense b/plugins/stonesense index 6570fe010..3e494d9d9 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 6570fe01081f7e402495bc5339b4ff7a1aabf305 +Subproject commit 3e494d9d968add443ebd63cc167933cc813f0eee From d2da06acc637bc95c3031efaaa0d1d9c250f74c7 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 26 Feb 2023 21:37:02 -0800 Subject: [PATCH 0726/2222] Update docs/changelog.txt --- docs/changelog.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 412ee07a0..0d29f5f3c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -79,10 +79,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` -- `Military`: New module for military functionality -- `Military`: new ``makeSquad`` to create a squad -- `Military`: changed ``getSquadName`` to take a squad identifier -- `Military`: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range +- ``Military``: New module for military functionality +- ``Military``: new ``makeSquad`` to create a squad +- ``Military``: changed ``getSquadName`` to take a squad identifier +- ``Military``: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range - ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency - ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency From 87ba0d270c9b0036236666ca451b889ef714b0c1 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 27 Feb 2023 05:49:22 +0000 Subject: [PATCH 0727/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index d4170eacf..8ae81f8d8 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d4170eacfc0f82fcb7364e558d0e782dc497f7d5 +Subproject commit 8ae81f8d8f1f96d82b9074b205073bb8e8d29f96 diff --git a/scripts b/scripts index c7345f6fe..1bf1cec84 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit c7345f6fe096bc6ce1700b70b4f7d4c65b2a3e57 +Subproject commit 1bf1cec84ce34cdf56eabe5140d8f6e8cd028169 From df0c7c27cb8020722b6ffd31fa6ca6269c0f4841 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 22:04:22 -0800 Subject: [PATCH 0728/2222] adjust to structures change --- library/modules/Units.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index a636310e1..533b40ca8 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -588,19 +588,7 @@ bool Units::isMischievous(df::unit *unit) bool Units::isAvailableForAdoption(df::unit* unit) { CHECK_NULL_POINTER(unit); - auto refs = unit->specific_refs; - for(size_t i=0; itype; - if( reftype == df::specific_ref_type::PETINFO_PET ) - { - //df::pet_info* pet = ref->pet; - return true; - } - } - - return false; + return unit->flags3.bits.available_for_adoption; } bool Units::isPet(df::unit* unit) From 18189510b533eb9d19120edd85093b18e3319439 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 27 Feb 2023 07:15:47 +0000 Subject: [PATCH 0729/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 1bf1cec84..81183a380 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 1bf1cec84ce34cdf56eabe5140d8f6e8cd028169 +Subproject commit 81183a380b11f4c3045a7888c35afe215d2185ad From 9b8400ab40df4006d869f7dc6c9ba00bcf55933c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 22:54:15 -0800 Subject: [PATCH 0730/2222] prevent planned buildings from being resumed note this only prevents unsuspending from the building sheet panel, not the tasks screen --- plugins/lua/buildingplan.lua | 18 +++++++++++++++++- plugins/lua/overlay.lua | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 0e1341a3b..25e9bc46e 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1625,11 +1625,27 @@ function InspectorOverlay:make_top_priority() self:reset() end +local RESUME_BUTTON_FRAME = {t=15, h=3, r=73, w=25} + +local function mouse_is_over_resume_button(rect) + local x,y = dfhack.screen.getMousePos() + if not x then return false end + if y < RESUME_BUTTON_FRAME.t or y > RESUME_BUTTON_FRAME.t + RESUME_BUTTON_FRAME.h - 1 then + return false + end + if x > rect.x2 - RESUME_BUTTON_FRAME.r + 1 or x < rect.x2 - RESUME_BUTTON_FRAME.r - RESUME_BUTTON_FRAME.w + 2 then + return false + end + return true +end + function InspectorOverlay:onInput(keys) if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then return false end - if keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then + if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then + return true + elseif keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then self:reset() end return InspectorOverlay.super.onInput(self, keys) diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 56ad72372..3d476bf0d 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -493,6 +493,9 @@ function feed_viewscreen_widgets(vs_name, keys) return false end gui.markMouseClicksHandled(keys) + if keys._MOUSE_L_DOWN then + df.global.enabler.mouse_lbut = 0 + end return true end From 4f933e0a368a14743408331b3a02d26e2517ec02 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 23:06:25 -0800 Subject: [PATCH 0731/2222] ensure reachability for selected items --- plugins/buildingplan/buildingplan_cycle.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 655dc8c1a..329ee3b99 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -5,6 +5,7 @@ #include "modules/Items.h" #include "modules/Job.h" +#include "modules/Maps.h" #include "modules/Materials.h" #include "df/building_design.h" @@ -151,6 +152,10 @@ static df::building * popInvalidTasks(color_ostream &out, Bucket &task_queue, return NULL; } +static bool isAccessibleFrom(df::item *item, df::job *job) { + return Maps::canWalkBetween(Items::getPosition(item), job->pos); +} + static void doVector(color_ostream &out, df::job_item_vector_id vector_id, map &buckets, unordered_map &planned_buildings) { @@ -182,9 +187,10 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto job = bld->jobs[0]; auto filter_idx = task.second; auto &pb = planned_buildings.at(id); - if (matchesFilters(item, job->job_items[filter_idx], pb.heat_safety, + if (isAccessibleFrom(item, job) + && matchesFilters(item, job->job_items[filter_idx], pb.heat_safety, pb.item_filters[filter_idx]) - && Job::attachJobItem(job, item, + && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) { MaterialInfo material; From c1ea43244b6cb9649db4179f273ef276b971718b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 23:38:00 -0800 Subject: [PATCH 0732/2222] order buckets by pickiness --- plugins/buildingplan/buildingplan.cpp | 35 ++++++++++++++++++++++++--- plugins/buildingplan/itemfilter.cpp | 31 ------------------------ plugins/buildingplan/itemfilter.h | 10 ++++---- 3 files changed, 36 insertions(+), 40 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 4fa119ebc..53ab4cdff 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -323,9 +323,34 @@ static command_result do_command(color_ostream &out, vector ¶meters) // core will already be suspended when coming in through here // -static string getBucket(const df::job_item & ji) { +static string getBucket(const df::job_item & ji, const PlannedBuilding & pb, int idx) { + if (idx < 0 || (size_t)idx < pb.item_filters.size()) + return "INVALID"; + std::ostringstream ser; + // put elements in front that significantly affect the difficulty of matching + // the filter. ensure the lexicographically "less" value is the pickier value. + const ItemFilter & item_filter = pb.item_filters[idx]; + + if (item_filter.getDecoratedOnly()) + ser << "Da"; + else + ser << "Db"; + + if (ji.flags2.bits.magma_safe || pb.heat_safety == HEAT_SAFETY_MAGMA) + ser << "Ha"; + else if (ji.flags2.bits.fire_safe || pb.heat_safety == HEAT_SAFETY_FIRE) + ser << "Hb"; + else + ser << "Hc"; + + size_t num_materials = item_filter.getMaterials().size(); + if (num_materials == 0 || num_materials >= 9 || item_filter.getMaterialMask().whole) + ser << "M9"; + else + ser << "M" << num_materials; + // pull out and serialize only known relevant fields. if we miss a few, then // the filter bucket will be slighly less specific than it could be, but // that's probably ok. we'll just end up bucketing slightly different items @@ -337,6 +362,8 @@ static string getBucket(const df::job_item & ji) { << ':' << ji.flags3.whole << ':' << ji.flags4 << ':' << ji.flags5 << ':' << ji.metal_ore << ':' << ji.has_tool_use; + ser << ':' << item_filter.serialize(); + return ser.str(); } @@ -394,7 +421,7 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { int32_t id = bld->id; for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) { auto job_item = job_items[job_item_idx]; - auto bucket = getBucket(*job_item); + auto bucket = getBucket(*job_item, pb, job_item_idx); // if there are multiple vector_ids, schedule duplicate tasks. after // the correct number of items are matched, the extras will get popped @@ -682,7 +709,7 @@ static int getQueuePosition(color_ostream &out, df::building *bld, int index) { if (!tasks.count(vec_id)) continue; auto &buckets = tasks.at(vec_id); - string bucket_id = getBucket(*job_item); + string bucket_id = getBucket(*job_item, pb, index); if (!buckets.count(bucket_id)) continue; int bucket_pos = -1; @@ -711,7 +738,7 @@ static void makeTopPriority(color_ostream &out, df::building *bld) { if (!tasks.count(vec_id)) continue; auto &buckets = tasks.at(vec_id); - string bucket_id = getBucket(*job_items[index]); + string bucket_id = getBucket(*job_items[index], pb, index); if (!buckets.count(bucket_id)) continue; auto &bucket = buckets.at(bucket_id); diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index a714b62d4..86c9c1378 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -131,37 +131,6 @@ void ItemFilter::setMaterials(const vector &materials) { this->materials = materials; } -string ItemFilter::getMinQuality() const { - return ENUM_KEY_STR(item_quality, min_quality); -} - -string ItemFilter::getMaxQuality() const { - return ENUM_KEY_STR(item_quality, max_quality); -} - -bool ItemFilter::getDecoratedOnly() const { - return decorated_only; -} - -uint32_t ItemFilter::getMaterialMask() const { - return mat_mask.whole; -} - -static string material_to_string_fn(const MaterialInfo &m) { return m.toString(); } - -vector ItemFilter::getMaterials() const { - vector descriptions; - transform_(materials, descriptions, material_to_string_fn); - - if (descriptions.size() == 0) - bitfield_to_string(&descriptions, mat_mask); - - if (descriptions.size() == 0) - descriptions.push_back("any"); - - return descriptions; -} - static bool matchesMask(DFHack::MaterialInfo &mat, df::dfhack_material_category mat_mask) { return mat_mask.whole ? mat.matches(mat_mask) : true; } diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h index 6eb7551b4..29eb7226c 100644 --- a/plugins/buildingplan/itemfilter.h +++ b/plugins/buildingplan/itemfilter.h @@ -20,11 +20,11 @@ public: void setMaterialMask(uint32_t mask); void setMaterials(const std::vector &materials); - std::string getMinQuality() const; - std::string getMaxQuality() const; - bool getDecoratedOnly() const; - uint32_t getMaterialMask() const; - std::vector getMaterials() const; + df::item_quality getMinQuality() const { return min_quality; } + df::item_quality getMaxQuality() const {return max_quality; } + bool getDecoratedOnly() const { return decorated_only; } + df::dfhack_material_category getMaterialMask() const { return mat_mask; } + std::vector getMaterials() const { return materials; } bool matches(df::dfhack_material_category mask) const; bool matches(DFHack::MaterialInfo &material) const; From 0f2c88265e0f1f2a8f55572afd31916fb48b0e11 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 26 Feb 2023 23:41:28 -0800 Subject: [PATCH 0733/2222] scan IN_PLAY last so more specific vectors are scanned first --- plugins/buildingplan/buildingplan_cycle.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 329ee3b99..375e26212 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -241,6 +241,7 @@ struct VectorsToScanLast { vectors.push_back(df::job_item_vector_id::BOULDER); vectors.push_back(df::job_item_vector_id::WOOD); vectors.push_back(df::job_item_vector_id::BAR); + vectors.push_back(df::job_item_vector_id::IN_PLAY); } }; @@ -254,7 +255,7 @@ void buildingplan_cycle(color_ostream &out, Tasks &tasks, for (auto it = tasks.begin(); it != tasks.end(); ) { auto vector_id = it->first; - // we could make this a set, but it's only three elements + // we could make this a set, but it's only a few elements if (std::find(vectors_to_scan_last.vectors.begin(), vectors_to_scan_last.vectors.end(), vector_id) != vectors_to_scan_last.vectors.end()) { From a5d22705e862d0a0e9d34c3b37c2b40b19c21ade Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Feb 2023 04:13:05 -0800 Subject: [PATCH 0734/2222] add label_below attribute --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 2 ++ library/lua/gui/widgets.lua | 12 +++++++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7010bdd31..4a3428031 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -50,6 +50,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``dfhack.job.attachJobItem()``: allows you to attach specific items to a job - ``dfhack.screen.paintTile()``: you can now explicitly clear the interface cursor from a map tile by passing ``0`` as the tile value - ``widgets.Label``: token ``tile`` properties can now be functions that return a value +- ``widgets.CycleHotkeyLabel``: add ``label_below`` attribute for compact 2-line output -@ ``widgets.FilteredList``: search key matching is now case insensitive by default -@ ``gui.INTERIOR_FRAME``: a panel frame style for use in highlighting off interior areas of a UI diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index f9aafe4e0..9f2386660 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4896,6 +4896,8 @@ It has the following attributes: hotkey. :label_width: The number of spaces to allocate to the ``label`` (for use in aligning a column of ``CycleHotkeyLabel`` labels). +:label_below: If ``true``, then the option value will apear below the label + instead of to the right of it. Defaults to ``false``. :options: A list of strings or tables of ``{label=string, value=string[, pen=pen]}``. String options use the same string for the label and value and the default pen. The optional ``pen`` diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index ab018a50a..b86e31710 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1490,6 +1490,7 @@ CycleHotkeyLabel.ATTRS{ key_back=DEFAULT_NIL, label=DEFAULT_NIL, label_width=DEFAULT_NIL, + label_below=false, options=DEFAULT_NIL, initial_option=1, on_change=DEFAULT_NIL, @@ -1498,12 +1499,17 @@ CycleHotkeyLabel.ATTRS{ function CycleHotkeyLabel:init() self:setOption(self.initial_option) + local val_gap = 1 + if self.label_below then + val_gap = 0 + (self.key_back and 1 or 0) + (self.key and 3 or 0) + end + self:setText{ self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self:callback('cycle', true)} or {}, {key=self.key, key_sep=': ', text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, - ' ', - {text=self:callback('getOptionLabel'), + self.label_below and NEWLINE or '', + {gap=val_gap, text=self:callback('getOptionLabel'), pen=self:callback('getOptionPen')}, } end @@ -1580,7 +1586,7 @@ end function CycleHotkeyLabel:onInput(keys) if CycleHotkeyLabel.super.onInput(self, keys) then return true - elseif keys._MOUSE_L_DOWN and self:getMousePos() then + elseif keys._MOUSE_L_DOWN and self:getMousePos() and not is_disabled(self) then self:cycle() return true end From 9f794a0710bedaaf0f5c9313990999127506f00b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Feb 2023 04:13:29 -0800 Subject: [PATCH 0735/2222] filter dialog mock, draft 2; implement Slider --- plugins/lua/buildingplan.lua | 614 +++++++++++++++++++++++------------ 1 file changed, 405 insertions(+), 209 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 25e9bc46e..58bb257b9 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -475,9 +475,157 @@ function ItemSelectionScreen:init() end -------------------------------- --- FilterSelection +-- Slider -- +Slider = defclass(Slider, widgets.Widget) +Slider.ATTRS{ + num_stops=DEFAULT_NIL, + get_left_idx_fn=DEFAULT_NIL, + get_right_idx_fn=DEFAULT_NIL, + on_left_change=DEFAULT_NIL, + on_right_change=DEFAULT_NIL, +} + +function Slider:preinit(init_table) + init_table.frame = init_table.frame or {} + init_table.frame.h = init_table.frame.h or 1 +end + +function Slider:init() + if self.num_stops < 2 then error('too few Slider stops') end + self.is_dragging_target = nil -- 'left', 'right', or 'both' + self.is_dragging_idx = nil -- offset from leftmost dragged tile +end + +local function slider_get_width_per_idx(self) + return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) +end + +function Slider:onInput(keys) + if not keys._MOUSE_L_DOWN then return false end + local x = self:getMousePos() + if not x then return false end + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + local left_pos = width_per_idx*(left_idx-1) + local right_pos = width_per_idx*(right_idx-1) + 4 + if x < left_pos then + self.on_left_change(self.get_left_idx_fn() - 1) + elseif x < left_pos+3 then + self.is_dragging_target = 'left' + self.is_dragging_idx = x - left_pos + elseif x < right_pos then + self.is_dragging_target = 'both' + self.is_dragging_idx = x - left_pos + elseif x < right_pos+3 then + self.is_dragging_target = 'right' + self.is_dragging_idx = x - right_pos + else + self.on_right_change(self.get_right_idx_fn() + 1) + end + return true +end + +local function slider_do_drag(self, width_per_idx) + local x = self.frame_body:localXY(dfhack.screen.getMousePos()) + local cur_pos = x - self.is_dragging_idx + cur_pos = math.max(0, cur_pos) + cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) + local offset = self.is_dragging_target == 'right' and -2 or 1 + local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 + local new_left_idx, new_right_idx + if self.is_dragging_target == 'right' then + new_right_idx = new_idx + else + new_left_idx = new_idx + if self.is_dragging_target == 'both' then + new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() + if new_right_idx > self.num_stops then + return + end + end + end + if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then + self.on_left_change(new_left_idx) + end + if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then + self.on_right_change(new_right_idx) + end +end + +local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} + +function Slider:onRenderBody(dc, rect) + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + -- draw track + dc:seek(1,0) + dc:char(nil, SLIDER_LEFT_END) + dc:char(nil, SLIDER_TRACK) + for stop_idx=1,self.num_stops-1 do + local track_stop_pen = SLIDER_TRACK_STOP_SELECTED + local track_pen = SLIDER_TRACK_SELECTED + if left_idx > stop_idx or right_idx < stop_idx then + track_stop_pen = SLIDER_TRACK_STOP + track_pen = SLIDER_TRACK + elseif right_idx == stop_idx then + track_pen = SLIDER_TRACK + end + dc:char(nil, track_stop_pen) + for i=2,width_per_idx do + dc:char(nil, track_pen) + end + end + if right_idx >= self.num_stops then + dc:char(nil, SLIDER_TRACK_STOP_SELECTED) + else + dc:char(nil, SLIDER_TRACK_STOP) + end + dc:char(nil, SLIDER_TRACK) + dc:char(nil, SLIDER_RIGHT_END) + -- draw tabs + dc:seek(width_per_idx*(left_idx-1)) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + dc:seek(width_per_idx*(right_idx-1)+4) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + -- manage dragging + if self.is_dragging_target then + slider_do_drag(self, width_per_idx) + end + if df.global.enabler.mouse_lbut == 0 then + self.is_dragging_target = nil + self.is_dragging_idx = nil + end +end + +-------------------------------- +-- QualityAndMaterialsPage +-- + +QualityAndMaterialsPage = defclass(QualityAndMaterialsPage, widgets.Panel) +QualityAndMaterialsPage.ATTRS{ + frame={t=0, l=0}, + index=DEFAULT_NIL, +} + +local TYPE_COL_WIDTH = 20 +local HEADER_HEIGHT = 8 +local QUALITY_HEIGHT = 9 +local FOOTER_HEIGHT = 4 + -- returns whether the items matched by the specified filter can have a quality -- rating. This also conveniently indicates whether an item can be decorated. local function can_be_improved(idx) @@ -491,236 +639,172 @@ local function can_be_improved(idx) filter.item_type ~= df.item_type.BOULDER end -local OPTIONS_COL_WIDTH = 28 -local TYPE_COL_WIDTH = 20 -local HEADER_HEIGHT = 5 -local FOOTER_HEIGHT = 4 - -FilterSelection = defclass(FilterSelection, widgets.Window) -FilterSelection.ATTRS{ - frame_title='Choose filters [MOCK -- NOT FUNCTIONAL]', - frame={w=80, h=53, l=30, t=8}, - resizable=true, - index=DEFAULT_NIL, -} - -local STANDIN_PEN = to_pen{fg=COLOR_GREEN, bg=COLOR_GREEN, ch=' '} +function QualityAndMaterialsPage:init() + self.lowest_other_item_heat_safety = 2 -function FilterSelection:init() self:addviews{ widgets.Panel{ - view_id='options_panel', - frame={l=0, t=0, b=FOOTER_HEIGHT, w=OPTIONS_COL_WIDTH}, - autoarrange_subviews=true, + view_id='header', + frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, + frame_inset={l=1}, subviews={ - widgets.Panel{ - view_id='quality_panel', - frame={l=0, r=0, h=24}, - frame_inset={t=1}, - frame_style=gui.INTERIOR_FRAME, - frame_title='Item quality', - subviews={ - widgets.HotkeyLabel{ - frame={l=0, t=0}, - key='CUSTOM_SHIFT_Q', - }, - widgets.HotkeyLabel{ - frame={l=1, t=0}, - key='CUSTOM_SHIFT_W', - label='Set max quality', - }, - widgets.Panel{ - view_id='quality_slider', - frame={l=0, t=2, w=3, h=15}, - frame_background=STANDIN_PEN, - }, - widgets.Label{ - frame={l=3, t=3}, - text='- Artifact (1)', - }, - widgets.Label{ - frame={l=3, t=5}, - text='- Masterful (3)', - }, - widgets.Label{ - frame={l=3, t=7}, - text='- Exceptional (34)', - }, - widgets.Label{ - frame={l=3, t=9}, - text='- Superior (50)', - }, - widgets.Label{ - frame={l=3, t=11}, - text='- FinelyCrafted (67)', - }, - widgets.Label{ - frame={l=3, t=13}, - text='- WellCrafted (79)', - }, - widgets.Label{ - frame={l=3, t=15}, - text='- Ordinary (206)', - }, - widgets.HotkeyLabel{ - frame={l=0, t=18}, - key='CUSTOM_SHIFT_Z', - }, - widgets.HotkeyLabel{ - frame={l=1, t=18}, - key='CUSTOM_SHIFT_X', - label='Set min quality', - }, - widgets.CycleHotkeyLabel{ - frame={l=0, t=20}, - key='CUSTOM_SHIFT_D', - label='Decorated only:', - options={'No', 'Yes'}, - }, + widgets.Label{ + frame={l=0, t=0, h=1, r=0}, + text={ + 'Current filter:', + {gap=1, pen=COLOR_LIGHTCYAN, text=self:callback('get_summary')} + }, + }, + widgets.CycleHotkeyLabel{ + view_id='safety', + frame={t=2, l=0, w=35}, + key='CUSTOM_SHIFT_G', + label='Building heat safety:', + options={ + {label='Fire Magma', value=0, pen=COLOR_GREY}, + {label='Fire Magma', value=2, pen=COLOR_RED}, + {label='Fire', value=1, pen=COLOR_LIGHTRED}, }, }, - widgets.ResizingPanel{ - view_id='building_panel', - frame={l=0, r=0}, - frame_inset={t=1}, - frame_style=gui.INTERIOR_FRAME, - frame_title='Building options', - autoarrange_subviews=true, - autoarrange_gap=1, - subviews={ - widgets.WrappedLabel{ - frame={l=0}, - text_to_wrap='These options will affect all items for the current building type.', - }, - widgets.CycleHotkeyLabel{ - frame={l=0}, - key='CUSTOM_SHIFT_G', - label='Building safety:', - options={ - {label='Any', value=0}, - {label='Magma', value=2, pen=COLOR_RED}, - {label='Fire', value=1, pen=COLOR_LIGHTRED}, - }, - }, + widgets.Label{ + frame={t=2, l=30}, + text='Magma', + auto_width=true, + text_pen=COLOR_GREY, + visible=function() return self.subviews.safety:getOptionValue() == 1 end, + }, + widgets.Label{ + frame={t=3, l=3}, + text='Other items for this building may not be able to use all of their selected materials.', + visible=function() return self.subviews.safety:getOptionValue() > self.lowest_other_item_heat_safety end, + }, + widgets.EditField{ + frame={l=0, t=4, w=23}, + label_text='Search: ', + on_char=function(ch) return ch:match('%l') end, + }, + widgets.CycleHotkeyLabel{ + frame={l=24, t=4, w=21}, + label='Sort by:', + key='CUSTOM_SHIFT_R', + options={'name', 'available'}, + }, + widgets.ToggleHotkeyLabel{ + frame={l=24, t=5, w=24}, + label='Hide unavailable:', + key='CUSTOM_SHIFT_H', + initial_option=false, + }, + widgets.Label{ + frame={l=1, b=0}, + text='Type', + text_pen=COLOR_LIGHTRED, + }, + widgets.Label{ + frame={l=TYPE_COL_WIDTH, b=0}, + text='Material', + text_pen=COLOR_LIGHTRED, + }, + }, + }, + widgets.Panel{ + view_id='materials_lists', + frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, + frame_style=gui.INTERIOR_FRAME, + subviews={ + widgets.List{ + view_id='materials_categories', + frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, + scroll_keys={}, + choices={ + {text='Stone', key='CUSTOM_SHIFT_S'}, + {text='Wood', key='CUSTOM_SHIFT_O'}, + {text='Metal', key='CUSTOM_SHIFT_M'}, + {text='Other', key='CUSTOM_SHIFT_T'}, }, }, - widgets.Panel{ - view_id='global_panel', - frame={l=0, r=0, b=0}, - frame_inset={t=1}, - frame_style=gui.INTERIOR_FRAME, - frame_title='Global options', - autoarrange_subviews=true, - subviews={ - widgets.WrappedLabel{ - frame={l=0}, - text_to_wrap='These options will affect the selection of "Generic Materials" for future buildings.', - }, - widgets.Panel{ - frame={h=1}, - }, - widgets.ToggleHotkeyLabel{ - frame={l=0}, - key='CUSTOM_SHIFT_B', - label='Blocks', - label_width=8, - }, - widgets.ToggleHotkeyLabel{ - frame={l=0}, - key='CUSTOM_SHIFT_L', - label='Logs', - label_width=8, - }, - widgets.ToggleHotkeyLabel{ - frame={l=0}, - key='CUSTOM_SHIFT_O', - label='Boulders', - label_width=8, - }, - widgets.ToggleHotkeyLabel{ - frame={l=0}, - key='CUSTOM_SHIFT_P', - label='Bars', - label_width=8, - }, + widgets.List{ + view_id='materials_mats', + frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, + choices={ + {text='9 - granite'}, + {text='0 - graphite'}, }, }, }, }, widgets.Panel{ - view_id='materials_panel', - frame={l=OPTIONS_COL_WIDTH, t=0, b=FOOTER_HEIGHT, r=0}, + view_id='divider', + frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=FOOTER_HEIGHT+QUALITY_HEIGHT, w=1}, + on_render=self:callback('draw_divider'), + }, + widgets.Panel{ + view_id='quality_panel', + frame={l=0, r=0, h=QUALITY_HEIGHT, b=FOOTER_HEIGHT}, + frame_style=gui.INTERIOR_FRAME, + frame_title='Item quality', subviews={ - widgets.Panel{ - view_id='header', - frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, - subviews={ - widgets.EditField{ - frame={l=1, t=0}, - label_text='Search: ', - on_char=function(ch) return ch:match('%l') end, - }, - widgets.CycleHotkeyLabel{ - frame={l=1, t=2, w=21}, - label='Sort by:', - key='CUSTOM_SHIFT_R', - options={'name', 'available'}, - }, - widgets.ToggleHotkeyLabel{ - frame={l=24, t=2, w=24}, - label='Hide unavailable:', - key='CUSTOM_SHIFT_H', - initial_option=false, - }, - widgets.Label{ - frame={l=1, b=0}, - text='Type', - text_pen=COLOR_LIGHTRED, - }, - widgets.Label{ - frame={l=TYPE_COL_WIDTH, b=0}, - text='Material', - text_pen=COLOR_LIGHTRED, - }, + widgets.CycleHotkeyLabel{ + frame={l=0, t=1, w=23}, + key='CUSTOM_SHIFT_D', + label='Decorated only:', + options={'No', 'Yes'}, + enabled=function() return can_be_improved(self.index) end, + }, + widgets.CycleHotkeyLabel{ + view_id='min_quality', + frame={l=0, t=3, w=18}, + label='Min quality:', + label_below=true, + key_back='CUSTOM_SHIFT_Z', + key='CUSTOM_SHIFT_X', + options={ + {label='Ordinary', value=0}, + {label='Well Crafted', value=1}, + {label='Finely Crafted', value=2}, + {label='Superior', value=3}, + {label='Exceptional', value=4}, + {label='Masterful', value=5}, + {label='Artifact', value=6}, }, + on_change=function(val) self:set_min_quality(val+1) end, }, - widgets.Panel{ - view_id='materials_lists', - frame={l=0, t=HEADER_HEIGHT, r=0, b=0}, - frame_style=gui.INTERIOR_FRAME, - subviews={ - widgets.List{ - view_id='materials_categories', - frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, - scroll_keys={}, - choices={ - {text='Stone', key='CUSTOM_SHIFT_S'}, - {text='Wood', key='CUSTOM_SHIFT_W'}, - {text='Metal', key='CUSTOM_SHIFT_M'}, - {text='Other', key='CUSTOM_SHIFT_O'}, - }, - }, - widgets.List{ - view_id='materials_mats', - frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, - choices={ - {text='9 - granite'}, - {text='0 - graphite'}, - }, - }, + widgets.CycleHotkeyLabel{ + view_id='max_quality', + frame={r=1, t=3, w=18}, + label='Max quality:', + label_below=true, + key_back='CUSTOM_SHIFT_Q', + key='CUSTOM_SHIFT_W', + options={ + {label='Ordinary', value=0}, + {label='Well Crafted', value=1}, + {label='Finely Crafted', value=2}, + {label='Superior', value=3}, + {label='Exceptional', value=4}, + {label='Masterful', value=5}, + {label='Artifact', value=6}, }, + on_change=function(val) self:set_max_quality(val+1) end, + }, + Slider{ + frame={l=0, t=6}, + num_stops=7, + get_left_idx_fn=function() + return self.subviews.min_quality:getOptionValue() + 1 + end, + get_right_idx_fn=function() + return self.subviews.max_quality:getOptionValue() + 1 + end, + on_left_change=self:callback('set_min_quality'), + on_right_change=self:callback('set_max_quality'), }, - widgets.Panel{ - view_id='divider', - frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=0, w=1}, - on_render=self:callback('draw_divider'), - } }, }, widgets.Panel{ view_id='footer', frame={l=0, r=0, b=0, h=FOOTER_HEIGHT}, - frame_inset={l=20, t=1}, + frame_inset={t=1, l=1}, subviews={ widgets.HotkeyLabel{ frame={l=0, t=0}, @@ -757,6 +841,22 @@ function FilterSelection:init() } end +function QualityAndMaterialsPage:set_min_quality(idx) + idx = math.min(6, math.max(0, idx-1)) + self.subviews.min_quality:setOption(idx) + if self.subviews.max_quality:getOptionValue() < idx then + self.subviews.max_quality:setOption(idx) + end +end + +function QualityAndMaterialsPage:set_max_quality(idx) + idx = math.min(6, math.max(0, idx-1)) + self.subviews.max_quality:setOption(idx) + if self.subviews.min_quality:getOptionValue() > idx then + self.subviews.min_quality:setOption(idx) + end +end + local texpos = dfhack.textures.getThinBordersTexposStart() local tp = function(offset) if texpos == -1 then return nil end @@ -767,7 +867,7 @@ local TOP_PEN = to_pen{tile=tp(10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} local MID_PEN = to_pen{tile=tp(4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} local BOT_PEN = to_pen{tile=tp(11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} -function FilterSelection:draw_divider(dc) +function QualityAndMaterialsPage:draw_divider(dc) local y2 = dc.height - 1 for y=0,y2 do dc:seek(0, y) @@ -781,6 +881,100 @@ function FilterSelection:draw_divider(dc) end end +function QualityAndMaterialsPage:get_summary() + return 'filter summary' +end + +-------------------------------- +-- GlobalSettingsPage +-- + +GlobalSettingsPage = defclass(GlobalSettingsPage, widgets.ResizingPanel) +GlobalSettingsPage.ATTRS{ + autoarrange_subviews=true, + frame={t=0, l=0}, + frame_inset={l=1, r=1}, +} + +function GlobalSettingsPage:init() + self:addviews{ + widgets.WrappedLabel{ + frame={l=0}, + text_to_wrap='These options will affect the selection of "Generic Materials" for all future buildings.', + }, + widgets.Panel{ + frame={h=1}, + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + key='CUSTOM_B', + label='Blocks', + label_width=8, + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + key='CUSTOM_L', + label='Logs', + label_width=8, + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + key='CUSTOM_O', + label='Boulders', + label_width=8, + }, + widgets.ToggleHotkeyLabel{ + frame={l=0}, + key='CUSTOM_R', + label='Bars', + label_width=8, + }, + } +end + +-------------------------------- +-- FilterSelection +-- + +FilterSelection = defclass(FilterSelection, widgets.Window) +FilterSelection.ATTRS{ + frame_title='Choose filters [MOCK -- NOT FUNCTIONAL]', + frame={w=53, h=53, l=30, t=8}, + frame_inset={t=1}, + resizable=true, + index=DEFAULT_NIL, + autoarrange_subviews=true, +} + +function FilterSelection:init() + self:addviews{ + widgets.TabBar{ + frame={t=0}, + labels={ + 'Quality and materials', + 'Global settings', + }, + on_select=function(idx) + self.subviews.pages:setSelected(idx) + self:updateLayout() + end, + get_cur_page=function() return self.subviews.pages:getSelected() end, + key='CUSTOM_CTRL_T', + }, + widgets.Widget{ + frame={h=1}, + }, + widgets.Pages{ + view_id='pages', + frame={t=5, l=0, b=0, r=0}, + subviews={ + QualityAndMaterialsPage{index=self.index}, + GlobalSettingsPage{}, + }, + }, + } +end + FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) FilterSelectionScreen.ATTRS { focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', @@ -794,10 +988,12 @@ function FilterSelectionScreen:init() end function FilterSelectionScreen:onShow() + -- don't let the building "shadow" follow the mouse cursor while this screen is open df.global.game.main_interface.bottom_mode_selected = -1 end function FilterSelectionScreen:onDismiss() + -- re-enable building shadow df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT end From 1d855014c26d1247cfdafafea94db791a01639e4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Feb 2023 10:16:58 -0800 Subject: [PATCH 0736/2222] implement global settings page --- plugins/buildingplan/buildingplan.cpp | 18 ++++++++++++++++-- plugins/lua/buildingplan.lua | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 53ab4cdff..01eeda120 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -603,6 +603,20 @@ static int getAvailableItems(lua_State *L) { return 1; } +static int getGlobalSettings(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + DEBUG(status,*out).print("entering getGlobalSettings\n"); + map settings; + settings.emplace("blocks", get_config_bool(config, CONFIG_BLOCKS)); + settings.emplace("logs", get_config_bool(config, CONFIG_LOGS)); + settings.emplace("boulders", get_config_bool(config, CONFIG_BOULDERS)); + settings.emplace("bars", get_config_bool(config, CONFIG_BARS)); + Lua::Push(L, settings); + return 1; +} + static int countAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { DEBUG(status,out).print( "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", @@ -762,8 +776,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(isPlannedBuilding), DFHACK_LUA_FUNCTION(addPlannedBuilding), DFHACK_LUA_FUNCTION(doCycle), - DFHACK_LUA_FUNCTION(scheduleCycle), - DFHACK_LUA_FUNCTION(countAvailableItems), + DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_FUNCTION(hasFilter), DFHACK_LUA_FUNCTION(setMaterialFilter), DFHACK_LUA_FUNCTION(setHeatSafetyFilter), @@ -774,6 +787,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { }; DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(getGlobalSettings), DFHACK_LUA_COMMAND(getAvailableItems), DFHACK_LUA_COMMAND(getMaterialFilter), DFHACK_LUA_COMMAND(getHeatSafetyFilter), diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 58bb257b9..95ba9b09c 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -906,30 +906,54 @@ function GlobalSettingsPage:init() frame={h=1}, }, widgets.ToggleHotkeyLabel{ + view_id='blocks', frame={l=0}, key='CUSTOM_B', label='Blocks', label_width=8, + on_change=self:callback('update_setting', 'blocks'), }, widgets.ToggleHotkeyLabel{ + view_id='logs', frame={l=0}, key='CUSTOM_L', label='Logs', label_width=8, + on_change=self:callback('update_setting', 'logs'), }, widgets.ToggleHotkeyLabel{ + view_id='boulders', frame={l=0}, key='CUSTOM_O', label='Boulders', label_width=8, + on_change=self:callback('update_setting', 'boulders'), }, widgets.ToggleHotkeyLabel{ + view_id='bars', frame={l=0}, key='CUSTOM_R', label='Bars', label_width=8, + on_change=self:callback('update_setting', 'bars'), }, } + + self:init_settings() +end + +function GlobalSettingsPage:init_settings() + local settings = getGlobalSettings() + local subviews = self.subviews + subviews.blocks:setOption(settings.blocks) + subviews.logs:setOption(settings.logs) + subviews.boulders:setOption(settings.boulders) + subviews.bars:setOption(settings.bars) +end + +function GlobalSettingsPage:update_setting(setting, val) + dfhack.run_command('buildingplan', 'set', setting, tostring(val)) + self:init_settings() end -------------------------------- From 97e5fdb78eb76fbd83165a6529d924b86e5ddc93 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Feb 2023 12:27:21 -0800 Subject: [PATCH 0737/2222] implement saving and retrieving item quality filters --- plugins/buildingplan/buildingplan.cpp | 54 ++++++++++++++++- plugins/lua/buildingplan.lua | 83 +++++++++++++++++++++++---- 2 files changed, 124 insertions(+), 13 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 01eeda120..43f15ff5d 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -629,12 +629,22 @@ static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtyp BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(out, key); for (auto &filter : filters.getItemFilters()) { - if (filter.isEmpty()) + if (!filter.isEmpty()) return true; } return false; } +static void clearFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { + TRACE(status,out).print("entering clearFilter\n"); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(out, key); + if (filters.getItemFilters().size() <= index) + return; + filters.setItemFilter(out, ItemFilter(), index); + call_buildingplan_lua(&out, "signal_reset"); +} + static void setMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index, string filter) { DEBUG(status,out).print("entering setMaterialFilter\n"); call_buildingplan_lua(&out, "signal_reset"); @@ -682,6 +692,45 @@ static int getHeatSafetyFilter(lua_State *L) { return 1; } +static void setQualityFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index, + int decorated, int min_quality, int max_quality) { + DEBUG(status,out).print("entering setQualityFilter\n"); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(out, key).getItemFilters(); + if (filters.size() <= index) + return; + ItemFilter filter = filters[index]; + filter.setDecoratedOnly(decorated != 0); + filter.setMinQuality(min_quality); + filter.setMaxQuality(max_quality); + get_item_filters(out, key).setItemFilter(out, filter, index); + call_buildingplan_lua(&out, "signal_reset"); +} + +static int getQualityFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering getQualityFilter building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(*out, key).getItemFilters(); + if (filters.size() <= index) + return 0; + auto &filter = filters[index]; + map ret; + ret.emplace("decorated", filter.getDecoratedOnly()); + ret.emplace("min_quality", filter.getMinQuality()); + ret.emplace("max_quality", filter.getMaxQuality()); + Lua::Push(L, ret); + return 1; +} + static bool validate_pb(color_ostream &out, df::building *bld, int index) { if (!isPlannedBuilding(out, bld) || bld->jobs.size() != 1) return false; @@ -778,8 +827,10 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(doCycle), DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_FUNCTION(hasFilter), + DFHACK_LUA_FUNCTION(clearFilter), DFHACK_LUA_FUNCTION(setMaterialFilter), DFHACK_LUA_FUNCTION(setHeatSafetyFilter), + DFHACK_LUA_FUNCTION(setQualityFilter), DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), DFHACK_LUA_FUNCTION(makeTopPriority), @@ -791,5 +842,6 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getAvailableItems), DFHACK_LUA_COMMAND(getMaterialFilter), DFHACK_LUA_COMMAND(getHeatSafetyFilter), + DFHACK_LUA_COMMAND(getQualityFilter), DFHACK_LUA_END }; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 95ba9b09c..2aaa34bbb 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -310,7 +310,7 @@ end function ItemSelection:get_choices(sort_fn) local item_ids = getAvailableItems(uibs.building_type, - uibs.building_subtype, uibs.custom_type, self.index - 1) + uibs.building_subtype, uibs.custom_type, self.index-1) local buckets = {} for _,item_id in ipairs(item_ids) do local item = df.item.find(item_id) @@ -641,6 +641,9 @@ end function QualityAndMaterialsPage:init() self.lowest_other_item_heat_safety = 2 + self.dirty = true + + local enable_item_quality = can_be_improved(self.index) self:addviews{ widgets.Panel{ @@ -665,6 +668,7 @@ function QualityAndMaterialsPage:init() {label='Fire Magma', value=2, pen=COLOR_RED}, {label='Fire', value=1, pen=COLOR_LIGHTRED}, }, + on_change=self:callback('set_heat_safety'), }, widgets.Label{ frame={t=2, l=30}, @@ -745,11 +749,16 @@ function QualityAndMaterialsPage:init() frame_title='Item quality', subviews={ widgets.CycleHotkeyLabel{ + view_id='decorated', frame={l=0, t=1, w=23}, key='CUSTOM_SHIFT_D', label='Decorated only:', - options={'No', 'Yes'}, - enabled=function() return can_be_improved(self.index) end, + options={ + {label='No', value=false}, + {label='Yes', value=true}, + }, + enabled=enable_item_quality, + on_change=self:callback('set_decorated'), }, widgets.CycleHotkeyLabel{ view_id='min_quality', @@ -767,6 +776,7 @@ function QualityAndMaterialsPage:init() {label='Masterful', value=5}, {label='Artifact', value=6}, }, + enabled=enable_item_quality, on_change=function(val) self:set_min_quality(val+1) end, }, widgets.CycleHotkeyLabel{ @@ -785,6 +795,7 @@ function QualityAndMaterialsPage:init() {label='Masterful', value=5}, {label='Artifact', value=6}, }, + enabled=enable_item_quality, on_change=function(val) self:set_max_quality(val+1) end, }, Slider{ @@ -798,6 +809,7 @@ function QualityAndMaterialsPage:init() end, on_left_change=self:callback('set_min_quality'), on_right_change=self:callback('set_max_quality'), + active=enable_item_quality, }, }, }, @@ -841,20 +853,64 @@ function QualityAndMaterialsPage:init() } end +function QualityAndMaterialsPage:refresh() + local summary = '' + local subviews = self.subviews + + local heat = getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) + subviews.safety:setOption(heat) + if heat >= 2 then summary = summary .. 'Magma safe ' + elseif heat == 1 then summary = summary .. 'Fire safe ' + end + + local quality = getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + subviews.decorated:setOption(quality.decorated ~= 0) + subviews.min_quality:setOption(quality.min_quality) + subviews.max_quality:setOption(quality.max_quality) + + self.summary = summary + self.dirty = false +end + +function QualityAndMaterialsPage:get_summary() + -- TODO: summarize materials + return self.summary +end + +function QualityAndMaterialsPage:set_heat_safety(heat) + setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) + self.dirty = true +end + +function QualityAndMaterialsPage:set_decorated(decorated) + local subviews = self.subviews + setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + decorated and 1 or 0, subviews.min_quality:getOptionValue(), subviews.max_quality:getOptionValue()) + self.dirty = true +end + function QualityAndMaterialsPage:set_min_quality(idx) idx = math.min(6, math.max(0, idx-1)) - self.subviews.min_quality:setOption(idx) - if self.subviews.max_quality:getOptionValue() < idx then - self.subviews.max_quality:setOption(idx) + local subviews = self.subviews + subviews.min_quality:setOption(idx) + if subviews.max_quality:getOptionValue() < idx then + subviews.max_quality:setOption(idx) end + setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + subviews.decorated:getOptionValue() and 1 or 0, idx, subviews.max_quality:getOptionValue()) + self.dirty = true end function QualityAndMaterialsPage:set_max_quality(idx) idx = math.min(6, math.max(0, idx-1)) - self.subviews.max_quality:setOption(idx) - if self.subviews.min_quality:getOptionValue() > idx then - self.subviews.min_quality:setOption(idx) + local subviews = self.subviews + subviews.max_quality:setOption(idx) + if subviews.min_quality:getOptionValue() > idx then + subviews.min_quality:setOption(idx) end + setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + subviews.decorated:getOptionValue() and 1 or 0, subviews.min_quality:getOptionValue(), idx) + self.dirty = true end local texpos = dfhack.textures.getThinBordersTexposStart() @@ -881,8 +937,11 @@ function QualityAndMaterialsPage:draw_divider(dc) end end -function QualityAndMaterialsPage:get_summary() - return 'filter summary' +function QualityAndMaterialsPage:onRenderFrame(dc, rect) + QualityAndMaterialsPage.super.onRenderFrame(self, dc, rect) + if self.dirty then + self:refresh() + end end -------------------------------- @@ -1444,7 +1503,7 @@ function PlannerOverlay:set_filter(idx) end function PlannerOverlay:clear_filter(idx) - setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1, "") + clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) end local function get_placement_data() From 926bc8b7d4060a034058e82986faf8929e91ce7e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 04:38:40 -0800 Subject: [PATCH 0738/2222] cache valid materials on world load --- plugins/buildingplan/buildingplan.cpp | 58 ++++++++++++++++++++++++--- plugins/lua/buildingplan.lua | 2 + 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 43f15ff5d..476d1863d 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -52,6 +52,7 @@ void set_config_bool(PersistentDataItem &c, int index, bool value) { static PersistentDataItem config; // for use in counting available materials for the UI +static vector mat_cache; static unordered_map, BuildingTypeKeyHash> job_item_cache; static unordered_map cur_heat_safety; static unordered_map cur_item_filters; @@ -142,6 +143,47 @@ static const vector & get_job_items(color_ostream &out, Bu return jitems; } +static void cache_matched(int16_t type, int32_t index) { + static const df::dfhack_material_category building_material_categories( + df::dfhack_material_category::mask_glass | + df::dfhack_material_category::mask_metal | + df::dfhack_material_category::mask_soap | + df::dfhack_material_category::mask_stone | + df::dfhack_material_category::mask_wood + ); + + MaterialInfo mi; + mi.decode(type, index); + if (mi.matches(building_material_categories)) { + DEBUG(status).print("cached material: %s\n", mi.toString().c_str()); + mat_cache.emplace_back(mi); + } + else + TRACE(status).print("not matched: %s\n", mi.toString().c_str()); +} + +static void load_material_cache() { + df::world_raws &raws = world->raws; + for (int i = 1; i < DFHack::MaterialInfo::NUM_BUILTIN; ++i) + if (raws.mat_table.builtin[i]) + cache_matched(i, -1); + + for (size_t i = 0; i < raws.inorganics.size(); i++) + cache_matched(0, i); + + for (size_t i = 0; i < raws.plants.all.size(); i++) { + df::plant_raw *p = raws.plants.all[i]; + if (p->material.size() <= 1) + continue; + for (size_t j = 0; j < p->material.size(); j++) { + if (p->material[j]->id == "WOOD") { + cache_matched(DFHack::MaterialInfo::PLANT_BASE+j, i); + break; + } + } + } +} + static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { if (cur_heat_safety.count(key)) return cur_heat_safety.at(key); @@ -221,6 +263,7 @@ static void clear_state(color_ostream &out) { } } job_item_cache.clear(); + mat_cache.clear(); } DFhackCExport command_result plugin_load_data (color_ostream &out) { @@ -236,6 +279,8 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { DEBUG(status,out).print("loading persisted state\n"); clear_state(out); + load_material_cache(); + vector filter_configs; World::GetPersistentData(&filter_configs, FILTER_CONFIG_KEY); for (auto &cfg : filter_configs) { @@ -250,7 +295,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { PlannedBuilding pb(out, building_configs[idx]); df::building *bld = df::building::find(pb.id); if (!bld) { - INFO(status).print("building %d no longer exists; skipping\n", pb.id); + INFO(status,out).print("building %d no longer exists; skipping\n", pb.id); pb.remove(out); continue; } @@ -639,7 +684,7 @@ static void clearFilter(color_ostream &out, df::building_type type, int16_t subt TRACE(status,out).print("entering clearFilter\n"); BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(out, key); - if (filters.getItemFilters().size() <= index) + if (index < 0 || filters.getItemFilters().size() <= (size_t)index) return; filters.setItemFilter(out, ItemFilter(), index); call_buildingplan_lua(&out, "signal_reset"); @@ -661,8 +706,8 @@ static int getMaterialFilter(lua_State *L) { DEBUG(status,*out).print( "entering getMaterialFilter building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); - vector filter; - Lua::PushVector(L, filter); + map counts_per_material; + Lua::Push(L, counts_per_material); return 1; } @@ -697,7 +742,7 @@ static void setQualityFilter(color_ostream &out, df::building_type type, int16_t DEBUG(status,out).print("entering setQualityFilter\n"); BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(out, key).getItemFilters(); - if (filters.size() <= index) + if (index < 0 || filters.size() <= (size_t)index) return; ItemFilter filter = filters[index]; filter.setDecoratedOnly(decorated != 0); @@ -825,7 +870,8 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(isPlannedBuilding), DFHACK_LUA_FUNCTION(addPlannedBuilding), DFHACK_LUA_FUNCTION(doCycle), - DFHACK_LUA_FUNCTION(scheduleCycle), DFHACK_LUA_FUNCTION(countAvailableItems), + DFHACK_LUA_FUNCTION(scheduleCycle), + DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_FUNCTION(hasFilter), DFHACK_LUA_FUNCTION(clearFilter), DFHACK_LUA_FUNCTION(setMaterialFilter), diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 2aaa34bbb..413dd545a 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -868,6 +868,8 @@ function QualityAndMaterialsPage:refresh() subviews.min_quality:setOption(quality.min_quality) subviews.max_quality:setOption(quality.max_quality) + local materials = getMaterialFilter(ibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + self.summary = summary self.dirty = false end From 4f3cdeaf054c2ccbd89f0da3edd3674184e6a9d2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 04:59:41 -0800 Subject: [PATCH 0739/2222] stub out reachability check for now it's more complicated than we thought --- plugins/buildingplan/buildingplan_cycle.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 375e26212..c8cd018e4 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -152,8 +152,19 @@ static df::building * popInvalidTasks(color_ostream &out, Bucket &task_queue, return NULL; } +// This is tricky. we want to choose an item that can be brought to the job site, but that's not +// necessarily the same as job->pos. it could be many tiles off in any direction (e.g. for bridges), or +// up or down (e.g. for stairs). static bool isAccessibleFrom(df::item *item, df::job *job) { - return Maps::canWalkBetween(Items::getPosition(item), job->pos); + // stub this out for now until we have a good algorithm. + // df::coord item_pos = Items::getPosition(item); + // const df::coord &job_pos = job->pos; + // return Maps::canWalkBetween(item_pos, job_pos) || + // Maps::canWalkBetween(item_pos, df::coord{job_pos.x-1, job_pos.y, job_pos.z}) || + // Maps::canWalkBetween(item_pos, df::coord{job_pos.x+1, job_pos.y, job_pos.z}) || + // Maps::canWalkBetween(item_pos, df::coord{job_pos.x, job_pos.y-1, job_pos.z}) || + // Maps::canWalkBetween(item_pos, df::coord{job_pos.x, job_pos.y+1, job_pos.z}); + return true; } static void doVector(color_ostream &out, df::job_item_vector_id vector_id, From 28599eb2bb37547052d60ed0718b8317e0fd3ae2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 05:28:12 -0800 Subject: [PATCH 0740/2222] ensure item is on walkable tile --- plugins/buildingplan/buildingplan_cycle.cpp | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index c8cd018e4..f401c90a8 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -11,6 +11,7 @@ #include "df/building_design.h" #include "df/item.h" #include "df/job.h" +#include "df/map_block.h" #include "df/world.h" #include @@ -154,17 +155,18 @@ static df::building * popInvalidTasks(color_ostream &out, Bucket &task_queue, // This is tricky. we want to choose an item that can be brought to the job site, but that's not // necessarily the same as job->pos. it could be many tiles off in any direction (e.g. for bridges), or -// up or down (e.g. for stairs). -static bool isAccessibleFrom(df::item *item, df::job *job) { - // stub this out for now until we have a good algorithm. - // df::coord item_pos = Items::getPosition(item); - // const df::coord &job_pos = job->pos; - // return Maps::canWalkBetween(item_pos, job_pos) || - // Maps::canWalkBetween(item_pos, df::coord{job_pos.x-1, job_pos.y, job_pos.z}) || - // Maps::canWalkBetween(item_pos, df::coord{job_pos.x+1, job_pos.y, job_pos.z}) || - // Maps::canWalkBetween(item_pos, df::coord{job_pos.x, job_pos.y-1, job_pos.z}) || - // Maps::canWalkBetween(item_pos, df::coord{job_pos.x, job_pos.y+1, job_pos.z}); - return true; +// up or down (e.g. for stairs). For now, just return if the item is on a walkable tile. +static bool isAccessibleFrom(color_ostream &out, df::item *item, df::job *job) { + df::coord item_pos = Items::getPosition(item); + df::map_block *block = Maps::getTileBlock(item_pos); + bool is_walkable = false; + if (block) { + uint16_t walkability_group = index_tile(block->walkable, item_pos); + is_walkable = walkability_group != 0; + TRACE(cycle,out).print("item %d in walkability_group %u at (%d,%d,%d) is %saccessible from job site\n", + item->id, walkability_group, item_pos.x, item_pos.y, item_pos.z, is_walkable ? "" : "not "); + } + return is_walkable; } static void doVector(color_ostream &out, df::job_item_vector_id vector_id, @@ -198,7 +200,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto job = bld->jobs[0]; auto filter_idx = task.second; auto &pb = planned_buildings.at(id); - if (isAccessibleFrom(item, job) + if (isAccessibleFrom(out, item, job) && matchesFilters(item, job->job_items[filter_idx], pb.heat_safety, pb.item_filters[filter_idx]) && Job::attachJobItem(job, item, From 982d6a995a827b3b2c1729eb697b592f446fcf87 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 05:29:33 -0800 Subject: [PATCH 0741/2222] fix signed/unsigned error --- plugins/buildingplan/buildingplan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 476d1863d..7479d348d 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -765,7 +765,7 @@ static int getQualityFilter(lua_State *L) { type, subtype, custom, index); BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(*out, key).getItemFilters(); - if (filters.size() <= index) + if (index < 0 || filters.size() <= (size_t)index) return 0; auto &filter = filters[index]; map ret; From 80da035186ffb779e098087420f05179abf31616 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 06:00:21 -0800 Subject: [PATCH 0742/2222] always allow constructions to be placed even if some tiles are invalid. the first selected tile must still be valid --- plugins/lua/buildingplan.lua | 45 +++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 413dd545a..e82b03cb9 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1274,10 +1274,11 @@ function ItemLine:get_item_line_text() return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') end -function ItemLine:reduce_quantity() +function ItemLine:reduce_quantity(used_quantity) if not self.available then return end local filter = get_cur_filters()[self.idx] - self.available = math.max(0, self.available - get_quantity(filter, self.is_hollow_fn())) + used_quantity = used_quantity or get_quantity(filter, self.is_hollow_fn()) + self.available = math.max(0, self.available - used_quantity) end local function get_placement_errors() @@ -1602,7 +1603,7 @@ function PlannerOverlay:onInput(keys) or self.subviews.errors:getMousePos() then return true end - if #uibs.errors > 0 then return true end + if not is_construction() and #uibs.errors > 0 then return true end if dfhack.gui.getMousePos() then if is_choosing_area() or cur_building_has_no_area() then local filters = get_cur_filters() @@ -1671,6 +1672,8 @@ function reload_cursors() end reload_cursors() +local ONE_BY_ONE = xy2pos(1, 1) + function PlannerOverlay:onRenderFrame(dc, rect) PlannerOverlay.super.onRenderFrame(self, dc, rect) @@ -1685,20 +1688,26 @@ function PlannerOverlay:onRenderFrame(dc, rect) local pos = self.saved_pos or uibs.pos local bounds = { - x1 = math.min(selection_pos.x, pos.x), - x2 = math.max(selection_pos.x, pos.x), - y1 = math.min(selection_pos.y, pos.y), - y2 = math.max(selection_pos.y, pos.y), + x1 = math.max(0, math.min(selection_pos.x, pos.x)), + x2 = math.min(df.global.world.map.x_count-1, math.max(selection_pos.x, pos.x)), + y1 = math.max(0, math.min(selection_pos.y, pos.y)), + y2 = math.min(df.global.world.map.y_count-1, math.max(selection_pos.y, pos.y)), } local hollow = self.subviews.hollow:getOptionValue() - local pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN + local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN + + local get_pen_fn = is_construction() and function(pos) + return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN + end or function() + return default_pen + end local function get_overlay_pen(pos) - if not hollow then return pen end + if not hollow then return get_pen_fn(pos) end if pos.x == bounds.x1 or pos.x == bounds.x2 or pos.y == bounds.y1 or pos.y == bounds.y2 then - return pen + return get_pen_fn(pos) end return gui.TRANSPARENT_PEN end @@ -1752,11 +1761,8 @@ function PlannerOverlay:place_building(placement_data, chosen_items) width=placement_data.width, height=placement_data.height, direction=uibs.direction} if err then - for _,b in ipairs(blds) do - dfhack.buildings.deconstruct(b) - end - dfhack.printerr(err .. (' (%d, %d, %d)'):format(pos.x, pos.y, pos.z)) - return + -- it's ok if some buildings fail to build + goto continue end -- assign fields for the types that need them. we can't pass them all in -- to the call to constructBuilding since attempting to assign unrelated @@ -1771,10 +1777,11 @@ function PlannerOverlay:place_building(placement_data, chosen_items) table.insert(blds, bld) ::continue:: end end end - self.subviews.item1:reduce_quantity() - self.subviews.item2:reduce_quantity() - self.subviews.item3:reduce_quantity() - self.subviews.item4:reduce_quantity() + local used_quantity = is_construction() and #blds or false + self.subviews.item1:reduce_quantity(used_quantity) + self.subviews.item2:reduce_quantity(used_quantity) + self.subviews.item3:reduce_quantity(used_quantity) + self.subviews.item4:reduce_quantity(used_quantity) for _,bld in ipairs(blds) do -- attach chosen items and reduce job_item quantity if chosen_items then From c8c1572bc4d80b28d2287023fbdd5a715f733520 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 06:08:51 -0800 Subject: [PATCH 0743/2222] fix typo --- plugins/lua/buildingplan.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index e82b03cb9..a0da8d838 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -868,8 +868,6 @@ function QualityAndMaterialsPage:refresh() subviews.min_quality:setOption(quality.min_quality) subviews.max_quality:setOption(quality.max_quality) - local materials = getMaterialFilter(ibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - self.summary = summary self.dirty = false end From dca19e9dadbc2adcc7c47cc148ae74e95668a06b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 3 Mar 2023 11:13:25 -0800 Subject: [PATCH 0744/2222] Adds more logging for dig-now --- plugins/dig-now.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index e94cb41da..037e7ae84 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -81,6 +81,7 @@ private: public: void load(MapExtras::MapCache &map) { designations.clear(); + DEBUG(general).print("DesignationJobs: reading jobs list\n"); df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; @@ -134,6 +135,7 @@ public: jobs.emplace(job->pos, job); } } + DEBUG(general).print("DesignationJobs: DONE reading jobs list\n"); } void remove(const df::coord &pos) { if(jobs.count(pos)) { @@ -731,9 +733,11 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, Random::MersenneRNG rng; DesignationJobs jobs; + DEBUG(general).print("do_dig(): starting..\n"); jobs.load(map); rng.init(); + DEBUG(general).print("do_dig(): reading map..\n"); std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { @@ -765,6 +769,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, } } + DEBUG(general).print("do_dig(): processing designations..\n"); // process designations for(auto &d : buffer) { auto pos = d.pos; @@ -812,6 +817,7 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, } } + DEBUG(general).print("do_dig(): write changes to map..\n"); map.WriteAll(); } From 9cc7c22306cecb913b79153673949ffc4ed6ff80 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 5 Mar 2023 07:14:23 +0000 Subject: [PATCH 0745/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 81183a380..288b38c9e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 81183a380b11f4c3045a7888c35afe215d2185ad +Subproject commit 288b38c9e9d8fabf1a0934ffbe23104274c39883 From 8f07a037722137cefbcfe7487b8f7a15887d0023 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 16:16:32 -0800 Subject: [PATCH 0746/2222] get savestock and loadstock minimally functional --- plugins/CMakeLists.txt | 2 +- plugins/stockpiles/StockpileSerializer.cpp | 3 - plugins/stockpiles/proto/stockpiles.proto | 2 +- plugins/stockpiles/stockpiles.cpp | 403 +-------------------- 4 files changed, 16 insertions(+), 394 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 82722f16a..d87b41978 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -156,7 +156,7 @@ dfhack_plugin(showmood showmood.cpp) #dfhack_plugin(steam-engine steam-engine.cpp) #add_subdirectory(spectate) #dfhack_plugin(stockflow stockflow.cpp LINK_LIBRARIES lua) -#add_subdirectory(stockpiles) +add_subdirectory(stockpiles) #dfhack_plugin(stocks stocks.cpp) dfhack_plugin(strangemood strangemood.cpp) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index cf3bfa349..c413613f6 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -474,7 +474,6 @@ void StockpileSerializer::write_general() mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows ); mBuffer.set_max_barrels ( mPile->max_barrels ); mBuffer.set_use_links_only ( mPile->use_links_only ); - mBuffer.set_unknown1 ( mPile->settings.unk1 ); mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic ); mBuffer.set_allow_organic ( mPile->settings.allow_organic ); mBuffer.set_corpses ( mPile->settings.flags.bits.corpses ); @@ -490,8 +489,6 @@ void StockpileSerializer::read_general() mPile->max_barrels = mBuffer.max_barrels(); if ( mBuffer.has_use_links_only() ) mPile->use_links_only = mBuffer.use_links_only(); - if ( mBuffer.has_unknown1() ) - mPile->settings.unk1 = mBuffer.unknown1(); if ( mBuffer.has_allow_inorganic() ) mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); if ( mBuffer.has_allow_organic() ) diff --git a/plugins/stockpiles/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto index 992989efd..90c95b93f 100644 --- a/plugins/stockpiles/proto/stockpiles.proto +++ b/plugins/stockpiles/proto/stockpiles.proto @@ -136,7 +136,7 @@ message StockpileSettings { optional AnimalsSet animals = 1; optional FoodSet food = 2; optional FurnitureSet furniture = 3; - optional int32 unknown1 = 4; + optional int32 unknown1 = 4 [deprecated=true]; optional RefuseSet refuse = 5; optional StoneSet stone = 6; optional OreSet ore = 7; diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 4b61ff175..4a2bdc33c 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -1,82 +1,32 @@ -#include "Core.h" -#include "Console.h" -#include "Export.h" #include "PluginManager.h" - -#include "DataFuncs.h" -#include "LuaTools.h" -#include "modules/Filesystem.h" - -#include "../uicommon.h" - #include "StockpileUtils.h" #include "StockpileSerializer.h" - #include "modules/Filesystem.h" #include "modules/Gui.h" -#include "modules/Filesystem.h" - -#include "df/world.h" -#include "df/world_data.h" - -#include "DataDefs.h" -#include "df/plotinfost.h" -#include "df/building_stockpilest.h" -#include "df/stockpile_settings.h" -#include "df/global_objects.h" -#include "df/viewscreen_dwarfmodest.h" - -// stl -#include -#include using std::vector; using std::string; -using std::endl; -using namespace DFHack; -using namespace df::enums; -using namespace google::protobuf; -using namespace dfstockpiles; - -DFHACK_PLUGIN ( "stockpiles" ); -REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(plotinfo); -REQUIRE_GLOBAL(selection_rect); -using df::building_stockpilest; -using std::placeholders::_1; +using namespace DFHack; -static command_result copystock ( color_ostream &out, vector & parameters ); -static bool copystock_guard ( df::viewscreen *top ); +DFHACK_PLUGIN("stockpiles"); static command_result savestock ( color_ostream &out, vector & parameters ); -static bool savestock_guard ( df::viewscreen *top ); - static command_result loadstock ( color_ostream &out, vector & parameters ); -static bool loadstock_guard ( df::viewscreen *top ); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands ) { - if ( world && plotinfo ) - { - commands.push_back(PluginCommand( - "copystock", - "Copy stockpile under cursor.", - copystock, - copystock_guard)); - commands.push_back(PluginCommand( - "savestock", - "Save the active stockpile's settings to a file.", - savestock, - savestock_guard)); - commands.push_back(PluginCommand( - "loadstock", - "Load and apply stockpile settings from a file.", - loadstock, - loadstock_guard)); - } + commands.push_back(PluginCommand( + "savestock", + "Save the active stockpile's settings to a file.", + savestock, + Gui::any_stockpile_hotkey)); + commands.push_back(PluginCommand( + "loadstock", + "Load and apply stockpile settings from a file.", + loadstock, + Gui::any_stockpile_hotkey)); return CR_OK; } @@ -86,111 +36,10 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event ) -{ - switch ( event ) - { - case SC_MAP_LOADED: - break; - default: - break; - } - - return CR_OK; -} - -static bool copystock_guard ( df::viewscreen *top ) -{ - using namespace ui_sidebar_mode; - - if ( !Gui::dwarfmode_hotkey ( top ) ) - return false; - - switch ( plotinfo->main.mode ) - { - case Stockpiles: - return true; - case BuildingItems: - case QueryBuilding: - return !!virtual_cast ( world->selected_building ); - default: - return false; - } -} - -static command_result copystock ( color_ostream &out, vector & parameters ) -{ - // HOTKEY COMMAND: CORE ALREADY SUSPENDED - - // For convenience: when used in the stockpiles mode, switch to 'q' - if ( plotinfo->main.mode == ui_sidebar_mode::Stockpiles ) - { - world->selected_building = NULL; // just in case it contains some kind of garbage - plotinfo->main.mode = ui_sidebar_mode::QueryBuilding; - selection_rect->start_x = -30000; - - out << "Switched back to query building." << endl; - return CR_OK; - } - - building_stockpilest *sp = virtual_cast ( world->selected_building ); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); - return CR_WRONG_USAGE; - } - - plotinfo->stockpile.custom_settings = sp->settings; - plotinfo->main.mode = ui_sidebar_mode::Stockpiles; - world->selected_stockpile_type = stockpile_category::Custom; - - out << "Stockpile options copied." << endl; - return CR_OK; -} - - -static bool savestock_guard ( df::viewscreen *top ) -{ - using namespace ui_sidebar_mode; - - if ( !Gui::dwarfmode_hotkey ( top ) ) - return false; - - switch ( plotinfo->main.mode ) - { - case Stockpiles: - return true; - case BuildingItems: - case QueryBuilding: - return !!virtual_cast ( world->selected_building ); - default: - return false; - } -} - -static bool loadstock_guard ( df::viewscreen *top ) -{ - using namespace ui_sidebar_mode; - - if ( !Gui::dwarfmode_hotkey ( top ) ) - return false; - - switch ( plotinfo->main.mode ) - { - case Stockpiles: - return true; - case BuildingItems: - case QueryBuilding: - return !!virtual_cast ( world->selected_building ); - default: - return false; - } -} - // exporting static command_result savestock ( color_ostream &out, vector & parameters ) { - building_stockpilest *sp = virtual_cast ( world->selected_building ); + df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true); if ( !sp ) { out.printerr ( "Selected building isn't a stockpile.\n" ); @@ -247,7 +96,7 @@ static command_result savestock ( color_ostream &out, vector & paramete // importing static command_result loadstock ( color_ostream &out, vector & parameters ) { - building_stockpilest *sp = virtual_cast ( world->selected_building ); + df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true); if ( !sp ) { out.printerr ( "Selected building isn't a stockpile.\n" ); @@ -302,227 +151,3 @@ static command_result loadstock ( color_ostream &out, vector & paramete } return CR_OK; } - -/** - * calls the lua function manage_settings() to kickoff the GUI - */ -bool manage_settings ( building_stockpilest *sp ) -{ - auto L = Lua::Core::State; - color_ostream_proxy out ( Core::getInstance().getConsole() ); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top ( L ); - - if ( !lua_checkstack ( L, 2 ) ) - return false; - - if ( !Lua::PushModulePublic ( out, L, "plugins.stockpiles", "manage_settings" ) ) - return false; - - Lua::Push ( L, sp ); - - if ( !Lua::SafeCall ( out, L, 1, 2 ) ) - return false; - - return true; -} - -bool show_message_box ( const std::string & title, const std::string & msg, bool is_error = false ) -{ - auto L = Lua::Core::State; - color_ostream_proxy out ( Core::getInstance().getConsole() ); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top ( L ); - - if ( !lua_checkstack ( L, 4 ) ) - return false; - - if ( !Lua::PushModulePublic ( out, L, "plugins.stockpiles", "show_message_box" ) ) - return false; - - Lua::Push ( L, title ); - Lua::Push ( L, msg ); - Lua::Push ( L, is_error ); - - if ( !Lua::SafeCall ( out, L, 3, 0 ) ) - return false; - - return true; -} - -struct stockpiles_import_hook : public df::viewscreen_dwarfmodest -{ - typedef df::viewscreen_dwarfmodest interpose_base; - - bool handleInput ( set *input ) - { - if ( Gui::inRenameBuilding() ) - return false; - - df::building_stockpilest *sp = get_selected_stockpile(); - if ( !sp ) - return false; - - if ( input->count ( interface_key::CUSTOM_L ) ) - { - manage_settings ( sp ); - return true; - } - - return false; - } - - DEFINE_VMETHOD_INTERPOSE ( void, feed, ( set *input ) ) - { - if ( !handleInput ( input ) ) - INTERPOSE_NEXT ( feed ) ( input ); - } - - DEFINE_VMETHOD_INTERPOSE ( void, render, () ) - { - INTERPOSE_NEXT ( render ) (); - - df::building_stockpilest *sp = get_selected_stockpile(); - if ( !sp ) - return; - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = dims.y2 - 3; // below autodump, automelt, autotrade, stocks; above stockflow - OutputHotkeyString ( x, y, "Load/Save Settings", "l", true, left_margin, COLOR_WHITE, COLOR_LIGHTRED ); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE ( stockpiles_import_hook, feed ); -IMPLEMENT_VMETHOD_INTERPOSE ( stockpiles_import_hook, render ); - -DFHACK_PLUGIN_IS_ENABLED ( is_enabled ); - -DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) -{ - if ( !gps ) - return CR_FAILURE; - - if ( enable != is_enabled ) - { - if ( - !INTERPOSE_HOOK ( stockpiles_import_hook, feed ).apply ( enable ) || - !INTERPOSE_HOOK ( stockpiles_import_hook, render ).apply ( enable ) - ) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -static std::vector list_dir ( const std::string &path, bool recursive = false ) -{ -// color_ostream_proxy out ( Core::getInstance().getConsole() ); - std::vector files; - std::stack dirs; - dirs.push(path); -// out << "list_dir start" << endl; - while (!dirs.empty() ) { - const std::string current = dirs.top(); -// out << "\t walking " << current << endl; - dirs.pop(); - std::vector entries; - const int res = DFHack::getdir(current, entries); - if ( res != 0 ) - continue; - for ( std::vector::iterator it = entries.begin() ; it != entries.end(); ++it ) - { - if ( (*it).empty() || (*it)[0] == '.' ) continue; - // shitty cross platform c++ we've got to construct the actual path manually - std::ostringstream child_path_s; - child_path_s << current << "/" << *it; - const std::string child = child_path_s.str(); - if ( recursive && Filesystem::isdir ( child ) ) - { -// out << "\t\tgot child dir: " << child << endl; - dirs.push ( child ); - } - else if ( Filesystem::isfile ( child ) ) - { - const std::string rel_path ( child.substr ( std::string ( "./"+path).length()-1 ) ); -// out << "\t\t adding file: " << child << " as " << rel_path << endl; - files.push_back ( rel_path ); - } - } - } -// out << "listdir_stop" << endl; - return files; -} - -static std::vector clean_dfstock_list ( const std::string &path ) -{ - if ( !Filesystem::exists ( path ) ) - { - return std::vector(); - } - std::vector files ( list_dir ( path, true) ); - files.erase ( std::remove_if ( files.begin(), files.end(), [] ( const std::string &f ) - { - return !is_dfstockfile ( f ); - } ), files.end() ); - std::transform ( files.begin(), files.end(), files.begin(), [] ( const std::string &f ) - { - return f.substr ( 0, f.find_last_of ( "." ) ); - } ); - std::sort ( files.begin(),files.end(), CompareNoCase ); - return files; -} - -static int stockpiles_list_settings ( lua_State *L ) -{ - auto path = luaL_checkstring ( L, 1 ); - if ( Filesystem::exists ( path ) && !Filesystem::isdir ( path ) ) - { - lua_pushfstring ( L, "stocksettings path invalid: %s", path ); - lua_error ( L ); - return 0; - } - std::vector files = clean_dfstock_list ( path ); - Lua::PushVector ( L, files, true ); - return 1; -} - -const std::string err_title = "Stockpile Settings Error"; -const std::string err_help = "Does the folder exist?\nCheck the console for more information."; - -static void stockpiles_load ( color_ostream &out, std::string filename ) -{ - std::vector params; - params.push_back ( filename ); - command_result r = loadstock ( out, params ); - if ( r != CR_OK ) - show_message_box ( err_title, "Couldn't load. " + err_help, true ); -} - - -static void stockpiles_save ( color_ostream &out, std::string filename ) -{ - std::vector params; - params.push_back ( filename ); - command_result r = savestock ( out, params ); - if ( r != CR_OK ) - show_message_box ( err_title, "Couldn't save. " + err_help, true ); -} - -DFHACK_PLUGIN_LUA_FUNCTIONS -{ - DFHACK_LUA_FUNCTION ( stockpiles_load ), - DFHACK_LUA_FUNCTION ( stockpiles_save ), - DFHACK_LUA_END -}; - -DFHACK_PLUGIN_LUA_COMMANDS -{ - DFHACK_LUA_COMMAND ( stockpiles_list_settings ), - DFHACK_LUA_END -}; From 23d5607835f570ad3b74e613d34d4f8ae34b5adb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 16:21:28 -0800 Subject: [PATCH 0747/2222] update docs --- docs/plugins/stockpiles.rst | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index bfc378ed0..01cd3159b 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -5,32 +5,20 @@ stockpiles .. dfhack-tool:: :summary: Import and export stockpile settings. - :tags: untested fort design productivity stockpiles + :tags: fort design productivity stockpiles :no-command: -.. dfhack-command:: copystock - :summary: Copies the configuration of the selected stockpile. - .. dfhack-command:: savestock :summary: Exports the configuration of the selected stockpile. .. dfhack-command:: loadstock - :summary: Imports the configuration of the selected stockpile. + :summary: Imports configuration for the selected stockpile. -When the plugin is enabled, the :kbd:`q` menu of each stockpile will have an -option for saving or loading the stockpile settings. See `gui/stockpiles` for -an in-game interface. +Select a stockpile in the UI first to use these commands. Usage ----- -``enable stockpiles`` - Add a hotkey that you can hit to easily save and load settings from - stockpiles selected in :kbd:`q` mode. -``copystock`` - Copies the parameters of the currently highlighted stockpile to the custom - stockpile settings and switches to custom stockpile placement mode, - effectively allowing you to copy/paste stockpiles easily. ``savestock `` Saves the currently highlighted stockpile's settings to a file in your Dwarf Fortress folder. This file can be used to copy settings between game @@ -45,9 +33,9 @@ etc. are not saved as they are different in every world. Examples -------- -``savestock food_settings.dfstock`` - Export the stockpile settings for the stockpile currently selected in - :kbd:`q` mode to a file named ``food_settings.dfstock``. -``loadstock food_settings.dfstock`` - Set the selected stockpile settings to those saved in the - ``food_settings.dfstock`` file. +``savestock food`` + Export the stockpile settings for the currently selected stockpile to a + file named ``food.dfstock``. +``loadstock food`` + Set the selected stockpile settings to those saved in the ``food.dfstock`` + file. From 090f298a47a3ea54b39ba4f51a1d57368b5faaca Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 06:24:19 -0800 Subject: [PATCH 0748/2222] init building materials to ordinary max quality --- plugins/buildingplan/defaultitemfilters.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index 36d074363..851565370 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -20,6 +20,17 @@ BuildingTypeKey DefaultItemFilters::getKey(PersistentDataItem &filter_config) { get_config_val(filter_config, FILTER_CONFIG_CUSTOM)); } +static int get_max_quality(const df::job_item *jitem) { + if (jitem->flags2.bits.building_material || + jitem->item_type == df::item_type::WOOD || + jitem->item_type == df::item_type::BLOCKS || + jitem->item_type == df::item_type::BAR || + jitem->item_type == df::item_type::BOULDER) + return df::item_quality::Ordinary; + + return df::item_quality::Masterful; +} + DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, const std::vector &jitems) : key(key) { DEBUG(status,out).print("creating persistent data for filter key %d,%d,%d\n", @@ -29,6 +40,9 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, set_config_val(filter_config, FILTER_CONFIG_SUBTYPE, std::get<1>(key)); set_config_val(filter_config, FILTER_CONFIG_CUSTOM, std::get<2>(key)); item_filters.resize(jitems.size()); + for (size_t idx = 0; idx < jitems.size(); ++idx) { + item_filters[idx].setMaxQuality(get_max_quality(jitems[idx])); + } filter_config.val() = serialize_item_filters(item_filters); } From 3b116c80616e808a3a176e58870261bf0e4275f8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 06:31:54 -0800 Subject: [PATCH 0749/2222] give global settings page an interior frame --- plugins/lua/buildingplan.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index a0da8d838..d472a1e28 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -952,7 +952,7 @@ GlobalSettingsPage = defclass(GlobalSettingsPage, widgets.ResizingPanel) GlobalSettingsPage.ATTRS{ autoarrange_subviews=true, frame={t=0, l=0}, - frame_inset={l=1, r=1}, + frame_style=gui.INTERIOR_FRAME, } function GlobalSettingsPage:init() From d3ef96cc052bd83c33cee5d8344bd6a528b9da71 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 17:49:28 -0800 Subject: [PATCH 0750/2222] allow MaterialInfo structs to be sorted --- library/include/modules/Materials.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 74acf5a8d..4018fcb2a 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -169,6 +169,9 @@ namespace DFHack inline bool operator!= (const MaterialInfo &a, const MaterialInfo &b) { return a.type != b.type || a.index != b.index; } + inline bool operator< (const MaterialInfo &a, const MaterialInfo &b) { + return a.type < b.type || (a.type == b.type && a.index < b.index); + } DFHACK_EXPORT bool isSoilInorganic(int material); DFHACK_EXPORT bool isStoneInorganic(int material); From f9924d90903969e0672ef80134fc0cb72bedbd25 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 17:50:12 -0800 Subject: [PATCH 0751/2222] implement material filter setting and retrieving --- plugins/buildingplan/buildingplan.cpp | 174 ++++++++++++++++++++--- plugins/buildingplan/itemfilter.cpp | 20 +-- plugins/buildingplan/itemfilter.h | 6 +- plugins/buildingplan/plannedbuilding.cpp | 8 +- 4 files changed, 173 insertions(+), 35 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 7479d348d..55e8f0e64 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -14,6 +14,7 @@ #include "df/world.h" using std::map; +using std::set; using std::string; using std::unordered_map; using std::vector; @@ -52,7 +53,7 @@ void set_config_bool(PersistentDataItem &c, int index, bool value) { static PersistentDataItem config; // for use in counting available materials for the UI -static vector mat_cache; +static map> mat_cache; static unordered_map, BuildingTypeKeyHash> job_item_cache; static unordered_map cur_heat_safety; static unordered_map cur_item_filters; @@ -144,19 +145,25 @@ static const vector & get_job_items(color_ostream &out, Bu } static void cache_matched(int16_t type, int32_t index) { - static const df::dfhack_material_category building_material_categories( - df::dfhack_material_category::mask_glass | - df::dfhack_material_category::mask_metal | - df::dfhack_material_category::mask_soap | - df::dfhack_material_category::mask_stone | - df::dfhack_material_category::mask_wood - ); + static const df::dfhack_material_category stone_cat(df::dfhack_material_category::mask_stone); + static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); + static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); + static const df::dfhack_material_category other_cat(df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap); MaterialInfo mi; mi.decode(type, index); - if (mi.matches(building_material_categories)) { - DEBUG(status).print("cached material: %s\n", mi.toString().c_str()); - mat_cache.emplace_back(mi); + if (mi.matches(stone_cat)) { + DEBUG(status).print("cached stone material: %s\n", mi.toString().c_str()); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "stone")); + } else if (mi.matches(wood_cat)) { + DEBUG(status).print("cached wood material: %s\n", mi.toString().c_str()); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "wood")); + } else if (mi.matches(metal_cat)) { + DEBUG(status).print("cached metal material: %s\n", mi.toString().c_str()); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "metal")); + } else if (mi.matches(other_cat)) { + DEBUG(status).print("cached other material: %s\n", mi.toString().c_str()); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "other")); } else TRACE(status).print("not matched: %s\n", mi.toString().c_str()); @@ -601,7 +608,8 @@ static void scheduleCycle(color_ostream &out) { } static int scanAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, - int32_t custom, int index, vector *item_ids = NULL) { + int32_t custom, int index, vector *item_ids = NULL, + map *counts = NULL) { DEBUG(status,out).print( "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); @@ -619,9 +627,20 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ for (auto vector_id : vector_ids) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { - if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, item_filters[index])) { + ItemFilter filter = item_filters[index]; + if (counts) { + // don't filter by material; we want counts for all materials + filter.setMaterialMask(0); + filter.setMaterials(set()); + } + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter)) { if (item_ids) item_ids->emplace_back(item->id); + if (counts) { + MaterialInfo mi; + mi.decode(item); + (*counts)[mi]++; + } ++count; } } @@ -690,9 +709,103 @@ static void clearFilter(color_ostream &out, df::building_type type, int16_t subt call_buildingplan_lua(&out, "signal_reset"); } -static void setMaterialFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index, string filter) { - DEBUG(status,out).print("entering setMaterialFilter\n"); - call_buildingplan_lua(&out, "signal_reset"); +static int setMaterialMaskFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering setMaterialMaskFilter building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(*out, key).getItemFilters(); + if (index < 0 || filters.size() <= (size_t)index) + return 0; + uint32_t mask = 0; + vector cats; + Lua::GetVector(L, cats); + for (auto &cat : cats) { + if (cat == "stone") + mask |= df::dfhack_material_category::mask_stone; + else if (cat == "wood") + mask |= df::dfhack_material_category::mask_wood; + else if (cat == "metal") + mask |= df::dfhack_material_category::mask_metal; + else if (cat == "other") + mask |= df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap; + } + DEBUG(status,*out).print( + "setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n", + type, subtype, custom, index, mask); + ItemFilter filter = filters[index]; + filter.setMaterialMask(mask); + get_item_filters(*out, key).setItemFilter(*out, filter, index); + call_buildingplan_lua(out, "signal_reset"); + return 0; +} + +static int getMaterialMaskFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering getMaterialFilter building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(*out, key); + if (index < 0 || filters.getItemFilters().size() <= (size_t)index) + return 0; + vector cat_names; + uint32_t bits = filters.getItemFilters()[index].getMaterialMask().whole; + if (!bits || bits & df::dfhack_material_category::mask_stone) + cat_names.emplace_back("stone"); + if (!bits || bits & df::dfhack_material_category::mask_wood) + cat_names.emplace_back("wood"); + if (!bits || bits & df::dfhack_material_category::mask_metal) + cat_names.emplace_back("metal"); + if (!bits || bits & (df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap)) + cat_names.emplace_back("other"); + Lua::PushVector(L, cat_names); + return 1; +} + +static int setMaterialFilter(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + int index = luaL_checkint(L, 4); + DEBUG(status,*out).print( + "entering setMaterialFilter building_type=%d subtype=%d custom=%d index=%d\n", + type, subtype, custom, index); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(*out, key).getItemFilters(); + if (index < 0 || filters.size() <= (size_t)index) + return 0; + set mats; + vector matstrs; + Lua::GetVector(L, matstrs); + for (auto &mat : matstrs) { + if (mat_cache.count(mat)) + mats.emplace(mat_cache.at(mat).first); + } + DEBUG(status,*out).print( + "setting material filter for building_type=%d subtype=%d custom=%d index=%d to %zd materials\n", + type, subtype, custom, index, mats.size()); + ItemFilter filter = filters[index]; + filter.setMaterials(mats); + get_item_filters(*out, key).setItemFilter(*out, filter, index); + call_buildingplan_lua(out, "signal_reset"); + return 0; } static int getMaterialFilter(lua_State *L) { @@ -706,8 +819,29 @@ static int getMaterialFilter(lua_State *L) { DEBUG(status,*out).print( "entering getMaterialFilter building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); - map counts_per_material; - Lua::Push(L, counts_per_material); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(*out, key).getItemFilters(); + if (index < 0 || filters.size() <= (size_t)index) + return 0; + const auto &mat_filter = filters[index].getMaterials(); + map counts; + scanAvailableItems(*out, type, subtype, custom, index, NULL, &counts); + // name -> {count=int, enabled=bool, category=string} + map> ret; + for (auto & entry : mat_cache) { + auto &name = entry.first; + auto &mat = entry.second.first; + auto &cat = entry.second.second; + map props; + string count = "0"; + if (counts.count(mat)) + count = int_to_string(counts.at(mat)); + props.emplace("count", count); + props.emplace("enabled", (!mat_filter.size() || mat_filter.count(mat)) ? "true" : "false"); + props.emplace("category", cat); + ret.emplace(name, props); + } + Lua::Push(L, ret); return 1; } @@ -874,7 +1008,6 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_FUNCTION(hasFilter), DFHACK_LUA_FUNCTION(clearFilter), - DFHACK_LUA_FUNCTION(setMaterialFilter), DFHACK_LUA_FUNCTION(setHeatSafetyFilter), DFHACK_LUA_FUNCTION(setQualityFilter), DFHACK_LUA_FUNCTION(getDescString), @@ -886,6 +1019,9 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getGlobalSettings), DFHACK_LUA_COMMAND(getAvailableItems), + DFHACK_LUA_COMMAND(setMaterialMaskFilter), + DFHACK_LUA_COMMAND(getMaterialMaskFilter), + DFHACK_LUA_COMMAND(setMaterialFilter), DFHACK_LUA_COMMAND(getMaterialFilter), DFHACK_LUA_COMMAND(getHeatSafetyFilter), DFHACK_LUA_COMMAND(getQualityFilter), diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index 86c9c1378..c7794e109 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -8,6 +8,7 @@ namespace DFHack { DBG_EXTERN(buildingplan, status); } +using std::set; using std::string; using std::vector; @@ -44,7 +45,7 @@ static bool deserializeMaterialMask(string ser, df::dfhack_material_category mat return true; } -static bool deserializeMaterials(string ser, vector &materials) { +static bool deserializeMaterials(string ser, set &materials) { if (ser.empty()) return true; @@ -56,7 +57,7 @@ static bool deserializeMaterials(string ser, vector &mater DEBUG(status).print("invalid material name serialization: '%s'", ser.c_str()); return false; } - materials.push_back(material); + materials.emplace(material); } return true; } @@ -83,10 +84,11 @@ ItemFilter::ItemFilter(color_ostream &out, string serialized) { string ItemFilter::serialize() const { std::ostringstream ser; ser << bitfield_to_string(mat_mask, ",") << "/"; + vector matstrs; if (!materials.empty()) { - ser << materials[0].getToken(); - for (size_t i = 1; i < materials.size(); ++i) - ser << "," << materials[i].getToken(); + for (auto &mat : materials) + matstrs.emplace_back(mat.getToken()); + ser << join_strings(",", matstrs); } ser << "/" << static_cast(min_quality); ser << "/" << static_cast(max_quality); @@ -127,7 +129,7 @@ void ItemFilter::setMaterialMask(uint32_t mask) { mat_mask.whole = mask; } -void ItemFilter::setMaterials(const vector &materials) { +void ItemFilter::setMaterials(const set &materials) { this->materials = materials; } @@ -140,8 +142,8 @@ bool ItemFilter::matches(df::dfhack_material_category mask) const { } bool ItemFilter::matches(DFHack::MaterialInfo &material) const { - for (auto it = materials.begin(); it != materials.end(); ++it) - if (material.matches(*it)) + for (auto &mat : materials) + if (material.matches(mat)) return true; return false; } @@ -161,7 +163,7 @@ bool ItemFilter::matches(df::item *item) const { } vector deserialize_item_filters(color_ostream &out, const string &serialized) { - std::vector filters; + vector filters; vector filter_strs; split_string(&filter_strs, serialized, ";"); diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h index 29eb7226c..1bed7b7f6 100644 --- a/plugins/buildingplan/itemfilter.h +++ b/plugins/buildingplan/itemfilter.h @@ -18,13 +18,13 @@ public: void setMaxQuality(int quality); void setDecoratedOnly(bool decorated); void setMaterialMask(uint32_t mask); - void setMaterials(const std::vector &materials); + void setMaterials(const std::set &materials); df::item_quality getMinQuality() const { return min_quality; } df::item_quality getMaxQuality() const {return max_quality; } bool getDecoratedOnly() const { return decorated_only; } df::dfhack_material_category getMaterialMask() const { return mat_mask; } - std::vector getMaterials() const { return materials; } + std::set getMaterials() const { return materials; } bool matches(df::dfhack_material_category mask) const; bool matches(DFHack::MaterialInfo &material) const; @@ -35,7 +35,7 @@ private: df::item_quality max_quality; bool decorated_only; df::dfhack_material_category mat_mask; - std::vector materials; + std::set materials; }; std::vector deserialize_item_filters(DFHack::color_ostream &out, const std::string &serialized); diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index 27be36a5b..a693f6fcf 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -12,8 +12,10 @@ namespace DFHack { DBG_EXTERN(buildingplan, status); } +using std::set; using std::string; using std::vector; + using namespace DFHack; static vector> get_vector_ids(color_ostream &out, int bld_id) { @@ -58,13 +60,11 @@ static vector> deserialize_vector_ids(color_ostre return ret; } -static std::vector get_item_filters(color_ostream &out, PersistentDataItem &bld_config) { - std::vector ret; - +static vector get_item_filters(color_ostream &out, PersistentDataItem &bld_config) { vector rawstrs; split_string(&rawstrs, bld_config.val(), "|"); if (rawstrs.size() < 2) - return ret; + return vector(); return deserialize_item_filters(out, rawstrs[1]); } From 028bbca07b813837b34db3799a20b206c8b0291e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 19:32:10 -0800 Subject: [PATCH 0752/2222] allow vectors to be read from indices other than 1 --- library/LuaTools.cpp | 4 ++-- library/include/LuaTools.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index ed0b0699d..a1bf855d6 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -121,10 +121,10 @@ void DFHack::Lua::Push(lua_State *state, const df::coord2d &pos) lua_setfield(state, -2, "y"); } -void DFHack::Lua::GetVector(lua_State *state, std::vector &pvec) +void DFHack::Lua::GetVector(lua_State *state, std::vector &pvec, int idx) { lua_pushnil(state); // first key - while (lua_next(state, 1) != 0) + while (lua_next(state, idx) != 0) { pvec.push_back(lua_tostring(state, -1)); lua_pop(state, 1); // remove value, leave key diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 86d3d0c7e..689d82260 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -359,7 +359,7 @@ namespace DFHack {namespace Lua { } } - DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec); + DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec, int idx = 1); DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos); DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos); From d07864e5bb83568cadbd3915bdc301c29cb1629b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 2 Mar 2023 19:32:25 -0800 Subject: [PATCH 0753/2222] allow material categories to be set from the ui --- plugins/buildingplan/buildingplan.cpp | 2 +- plugins/lua/buildingplan.lua | 92 +++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 13 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 55e8f0e64..aa23ddd0b 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -726,7 +726,7 @@ static int setMaterialMaskFilter(lua_State *L) { return 0; uint32_t mask = 0; vector cats; - Lua::GetVector(L, cats); + Lua::GetVector(L, cats, 5); for (auto &cat : cats) { if (cat == "stone") mask |= df::dfhack_material_category::mask_stone; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index d472a1e28..5137ee700 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -639,6 +639,15 @@ local function can_be_improved(idx) filter.item_type ~= df.item_type.BOULDER end +local function mat_sort_by_name(a, b) + return a.name < b.name +end + +local function mat_sort_by_quantity(a, b) + return a.quantity > b.quantity or + (a.quantity == b.quantity and mat_sort_by_name(a, b)) +end + function QualityAndMaterialsPage:init() self.lowest_other_item_heat_safety = 2 self.dirty = true @@ -688,16 +697,23 @@ function QualityAndMaterialsPage:init() on_char=function(ch) return ch:match('%l') end, }, widgets.CycleHotkeyLabel{ + view_id='mat_sort', frame={l=24, t=4, w=21}, label='Sort by:', key='CUSTOM_SHIFT_R', - options={'name', 'available'}, + options={ + {label='name', value=mat_sort_by_name}, + {label='available', value=mat_sort_by_quantity} + }, + on_change=function() self.dirty = true end, }, widgets.ToggleHotkeyLabel{ + view_id='hide_zero', frame={l=24, t=5, w=24}, label='Hide unavailable:', key='CUSTOM_SHIFT_H', initial_option=false, + on_change=function() self.dirty = true end, }, widgets.Label{ frame={l=1, b=0}, @@ -720,20 +736,14 @@ function QualityAndMaterialsPage:init() view_id='materials_categories', frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, scroll_keys={}, - choices={ - {text='Stone', key='CUSTOM_SHIFT_S'}, - {text='Wood', key='CUSTOM_SHIFT_O'}, - {text='Metal', key='CUSTOM_SHIFT_M'}, - {text='Other', key='CUSTOM_SHIFT_T'}, - }, + icon_width=2, + cursor_pen=COLOR_CYAN, + on_double_click=self:callback('toggle_category'), }, widgets.List{ view_id='materials_mats', frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, - choices={ - {text='9 - granite'}, - {text='0 - graphite'}, - }, + icon_width=2, }, }, }, @@ -853,6 +863,32 @@ function QualityAndMaterialsPage:init() } end +local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} +local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} + +local function make_cat_choice(label, cat, key, enabled_cats) + local enabled = enabled_cats[cat] + return { + text=label, + key=key, + enabled=enabled, + cat=cat, + icon=enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + } +end + +local function make_mat_choice(name, props) + local quantity = tonumber(props.count) + local text = ('%5d - %s'):format(quantity, name) + local enabled = props.enabled == 'true' + return { + text=text, + icon=enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN, + name=name, + quantity=quantity, + } +end + function QualityAndMaterialsPage:refresh() local summary = '' local subviews = self.subviews @@ -868,6 +904,26 @@ function QualityAndMaterialsPage:refresh() subviews.min_quality:setOption(quality.min_quality) subviews.max_quality:setOption(quality.max_quality) + local categories = utils.invert(getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1)) + local category_choices={ + make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', categories), + make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', categories), + make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', categories), + make_cat_choice('Other', 'other', 'CUSTOM_SHIFT_T', categories), + } + self.subviews.materials_categories:setChoices(category_choices) + + local mat_filter = getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + local mat_choices = {} + local hide_zero = self.subviews.hide_zero:getOptionValue() + for name,props in pairs(mat_filter) do + if not hide_zero or tonumber(props.count) > 0 then + table.insert(mat_choices, make_mat_choice(name, props)) + end + end + table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) + self.subviews.materials_mats:setChoices(mat_choices) + self.summary = summary self.dirty = false end @@ -877,6 +933,18 @@ function QualityAndMaterialsPage:get_summary() return self.summary end +function QualityAndMaterialsPage:toggle_category(idx, choice) + choice.enabled = not choice.enabled + local cats = {} + for _,c in ipairs(self.subviews.materials_categories:getChoices()) do + if c.enabled then + table.insert(cats, c.cat) + end + end + setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) + self.dirty = true +end + function QualityAndMaterialsPage:set_heat_safety(heat) setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) self.dirty = true @@ -1022,7 +1090,7 @@ end FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ frame_title='Choose filters [MOCK -- NOT FUNCTIONAL]', - frame={w=53, h=53, l=30, t=8}, + frame={w=55, h=53, l=30, t=8}, frame_inset={t=1}, resizable=true, index=DEFAULT_NIL, From 0562dc52343ee22d6388932d5ffdcc995940a6d7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 18:16:49 -0800 Subject: [PATCH 0754/2222] constify some MaterialInfo methods --- library/include/modules/Materials.h | 16 ++++++++-------- library/modules/Materials.cpp | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 4018fcb2a..3cdf146c9 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -139,25 +139,25 @@ namespace DFHack std::string getToken() const; std::string toString(uint16_t temp = 10015, bool named = true) const; - bool isAnyCloth(); + bool isAnyCloth() const; - void getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask); - void getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &mask); - void getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &mask); + void getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask) const; + void getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &mask) const; + void getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &mask) const; df::craft_material_class getCraftClass(); - bool matches(const MaterialInfo &mat) + bool matches(const MaterialInfo &mat) const { if (!mat.isValid()) return true; return (type == mat.type) && (mat.index == -1 || index == mat.index); } - bool matches(const df::job_material_category &cat); - bool matches(const df::dfhack_material_category &cat); + bool matches(const df::job_material_category &cat) const; + bool matches(const df::dfhack_material_category &cat) const; bool matches(const df::job_item &item, - df::item_type itype = df::item_type::NONE); + df::item_type itype = df::item_type::NONE) const; }; DFHACK_EXPORT bool parseJobMaterialCategory(df::job_material_category *cat, const std::string &token); diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index a6141f1d8..d73b59922 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -376,7 +376,7 @@ df::craft_material_class MaterialInfo::getCraftClass() return craft_material_class::None; } -bool MaterialInfo::isAnyCloth() +bool MaterialInfo::isAnyCloth() const { using namespace df::enums::material_flags; @@ -387,7 +387,7 @@ bool MaterialInfo::isAnyCloth() ); } -bool MaterialInfo::matches(const df::job_material_category &cat) +bool MaterialInfo::matches(const df::job_material_category &cat) const { if (!material) return false; @@ -416,7 +416,7 @@ bool MaterialInfo::matches(const df::job_material_category &cat) return false; } -bool MaterialInfo::matches(const df::dfhack_material_category &cat) +bool MaterialInfo::matches(const df::dfhack_material_category &cat) const { if (!material) return false; @@ -444,7 +444,7 @@ bool MaterialInfo::matches(const df::dfhack_material_category &cat) #undef TEST -bool MaterialInfo::matches(const df::job_item &item, df::item_type itype) +bool MaterialInfo::matches(const df::job_item &item, df::item_type itype) const { if (!isValid()) return false; @@ -465,7 +465,7 @@ bool MaterialInfo::matches(const df::job_item &item, df::item_type itype) bits_match(item.flags3.whole, ok3.whole, mask3.whole); } -void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask) +void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &mask) const { ok.whole = mask.whole = 0; if (!isValid()) return; @@ -500,7 +500,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags1 &ok, df::job_item_flags1 &ma //04000000 - "milkable" - vtable[107],1,1 } -void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &mask) +void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &mask) const { ok.whole = mask.whole = 0; if (!isValid()) return; @@ -538,7 +538,7 @@ void MaterialInfo::getMatchBits(df::job_item_flags2 &ok, df::job_item_flags2 &ma TEST(yarn, MAT_FLAG(YARN)); } -void MaterialInfo::getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &mask) +void MaterialInfo::getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &mask) const { ok.whole = mask.whole = 0; if (!isValid()) return; From 2a2141931f59fcaeea12cb5fb4f9a5af3a8b12fd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 18:17:10 -0800 Subject: [PATCH 0755/2222] allow material filter to be read and set --- plugins/buildingplan/buildingplan.cpp | 69 ++++++++++------ plugins/lua/buildingplan.lua | 114 +++++++++++++------------- 2 files changed, 103 insertions(+), 80 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index aa23ddd0b..de133e668 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -144,12 +144,12 @@ static const vector & get_job_items(color_ostream &out, Bu return jitems; } -static void cache_matched(int16_t type, int32_t index) { - static const df::dfhack_material_category stone_cat(df::dfhack_material_category::mask_stone); - static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); - static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); - static const df::dfhack_material_category other_cat(df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap); +static const df::dfhack_material_category stone_cat(df::dfhack_material_category::mask_stone); +static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); +static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); +static const df::dfhack_material_category glass_cat(df::dfhack_material_category::mask_glass); +static void cache_matched(int16_t type, int32_t index) { MaterialInfo mi; mi.decode(type, index); if (mi.matches(stone_cat)) { @@ -161,9 +161,9 @@ static void cache_matched(int16_t type, int32_t index) { } else if (mi.matches(metal_cat)) { DEBUG(status).print("cached metal material: %s\n", mi.toString().c_str()); mat_cache.emplace(mi.toString(), std::make_pair(mi, "metal")); - } else if (mi.matches(other_cat)) { - DEBUG(status).print("cached other material: %s\n", mi.toString().c_str()); - mat_cache.emplace(mi.toString(), std::make_pair(mi, "other")); + } else if (mi.matches(glass_cat)) { + DEBUG(status).print("cached glass material: %s\n", mi.toString().c_str()); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "glass")); } else TRACE(status).print("not matched: %s\n", mi.toString().c_str()); @@ -729,19 +729,30 @@ static int setMaterialMaskFilter(lua_State *L) { Lua::GetVector(L, cats, 5); for (auto &cat : cats) { if (cat == "stone") - mask |= df::dfhack_material_category::mask_stone; + mask |= stone_cat.whole; else if (cat == "wood") - mask |= df::dfhack_material_category::mask_wood; + mask |= wood_cat.whole; else if (cat == "metal") - mask |= df::dfhack_material_category::mask_metal; - else if (cat == "other") - mask |= df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap; + mask |= metal_cat.whole; + else if (cat == "glass") + mask |= glass_cat.whole; } DEBUG(status,*out).print( "setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n", type, subtype, custom, index, mask); ItemFilter filter = filters[index]; filter.setMaterialMask(mask); + if (mask) { + // remove materials from the list that don't match the mask + const auto &mats = filter.getMaterials(); + set new_mats; + const df::dfhack_material_category mat_mask(mask); + for (auto & mat : mats) { + if (mat.matches(mat_mask)) + new_mats.emplace(mat); + } + filter.setMaterials(new_mats); + } get_item_filters(*out, key).setItemFilter(*out, filter, index); call_buildingplan_lua(out, "signal_reset"); return 0; @@ -762,17 +773,14 @@ static int getMaterialMaskFilter(lua_State *L) { auto &filters = get_item_filters(*out, key); if (index < 0 || filters.getItemFilters().size() <= (size_t)index) return 0; - vector cat_names; + map ret; uint32_t bits = filters.getItemFilters()[index].getMaterialMask().whole; - if (!bits || bits & df::dfhack_material_category::mask_stone) - cat_names.emplace_back("stone"); - if (!bits || bits & df::dfhack_material_category::mask_wood) - cat_names.emplace_back("wood"); - if (!bits || bits & df::dfhack_material_category::mask_metal) - cat_names.emplace_back("metal"); - if (!bits || bits & (df::dfhack_material_category::mask_glass | df::dfhack_material_category::mask_soap)) - cat_names.emplace_back("other"); - Lua::PushVector(L, cat_names); + ret.emplace("unset", !bits); + ret.emplace("stone", !bits || bits & stone_cat.whole); + ret.emplace("wood", !bits || bits & wood_cat.whole); + ret.emplace("metal", !bits || bits & metal_cat.whole); + ret.emplace("glass", !bits || bits & glass_cat.whole); + Lua::Push(L, ret); return 1; } @@ -793,7 +801,7 @@ static int setMaterialFilter(lua_State *L) { return 0; set mats; vector matstrs; - Lua::GetVector(L, matstrs); + Lua::GetVector(L, matstrs, 5); for (auto &mat : matstrs) { if (mat_cache.count(mat)) mats.emplace(mat_cache.at(mat).first); @@ -803,6 +811,19 @@ static int setMaterialFilter(lua_State *L) { type, subtype, custom, index, mats.size()); ItemFilter filter = filters[index]; filter.setMaterials(mats); + // ensure relevant masks are explicitly enabled + df::dfhack_material_category mask = filter.getMaterialMask(); + for (auto & mat : mats) { + if (mat.matches(stone_cat)) + mask.whole |= stone_cat.whole; + else if (mat.matches(wood_cat)) + mask.whole |= wood_cat.whole; + else if (mat.matches(metal_cat)) + mask.whole |= metal_cat.whole; + else if (mat.matches(glass_cat)) + mask.whole |= glass_cat.whole; + } + filter.setMaterialMask(mask.whole); get_item_filters(*out, key).setItemFilter(*out, filter, index); call_buildingplan_lua(out, "signal_reset"); return 0; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 5137ee700..fae27c3e5 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -622,7 +622,7 @@ QualityAndMaterialsPage.ATTRS{ } local TYPE_COL_WIDTH = 20 -local HEADER_HEIGHT = 8 +local HEADER_HEIGHT = 6 local QUALITY_HEIGHT = 9 local FOOTER_HEIGHT = 4 @@ -649,10 +649,9 @@ local function mat_sort_by_quantity(a, b) end function QualityAndMaterialsPage:init() - self.lowest_other_item_heat_safety = 2 self.dirty = true - local enable_item_quality = can_be_improved(self.index) + local enable_item_quality = can_be_improved(self.index) self:addviews{ widgets.Panel{ @@ -667,38 +666,9 @@ function QualityAndMaterialsPage:init() {gap=1, pen=COLOR_LIGHTCYAN, text=self:callback('get_summary')} }, }, - widgets.CycleHotkeyLabel{ - view_id='safety', - frame={t=2, l=0, w=35}, - key='CUSTOM_SHIFT_G', - label='Building heat safety:', - options={ - {label='Fire Magma', value=0, pen=COLOR_GREY}, - {label='Fire Magma', value=2, pen=COLOR_RED}, - {label='Fire', value=1, pen=COLOR_LIGHTRED}, - }, - on_change=self:callback('set_heat_safety'), - }, - widgets.Label{ - frame={t=2, l=30}, - text='Magma', - auto_width=true, - text_pen=COLOR_GREY, - visible=function() return self.subviews.safety:getOptionValue() == 1 end, - }, - widgets.Label{ - frame={t=3, l=3}, - text='Other items for this building may not be able to use all of their selected materials.', - visible=function() return self.subviews.safety:getOptionValue() > self.lowest_other_item_heat_safety end, - }, - widgets.EditField{ - frame={l=0, t=4, w=23}, - label_text='Search: ', - on_char=function(ch) return ch:match('%l') end, - }, widgets.CycleHotkeyLabel{ view_id='mat_sort', - frame={l=24, t=4, w=21}, + frame={l=0, t=2, w=21}, label='Sort by:', key='CUSTOM_SHIFT_R', options={ @@ -709,12 +679,17 @@ function QualityAndMaterialsPage:init() }, widgets.ToggleHotkeyLabel{ view_id='hide_zero', - frame={l=24, t=5, w=24}, + frame={l=0, t=3, w=24}, label='Hide unavailable:', key='CUSTOM_SHIFT_H', initial_option=false, on_change=function() self.dirty = true end, }, + widgets.EditField{ + frame={l=26, t=2}, + label_text='Search: ', + on_char=function(ch) return ch:match('[%l -]') end, + }, widgets.Label{ frame={l=1, b=0}, text='Type', @@ -727,8 +702,7 @@ function QualityAndMaterialsPage:init() }, }, }, - widgets.Panel{ - view_id='materials_lists', + widgets.Panel{view_id='materials_lists', frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, frame_style=gui.INTERIOR_FRAME, subviews={ @@ -739,11 +713,13 @@ function QualityAndMaterialsPage:init() icon_width=2, cursor_pen=COLOR_CYAN, on_double_click=self:callback('toggle_category'), + on_submit=self:callback('toggle_category'), }, widgets.List{ view_id='materials_mats', frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, icon_width=2, + on_submit=self:callback('toggle_material'), }, }, }, @@ -866,25 +842,35 @@ end local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} -local function make_cat_choice(label, cat, key, enabled_cats) - local enabled = enabled_cats[cat] +local function make_cat_choice(label, cat, key, cats) + local enabled = cats[cat] + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end return { text=label, key=key, enabled=enabled, cat=cat, - icon=enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + icon=icon, } end -local function make_mat_choice(name, props) +local function make_mat_choice(name, props, cats) local quantity = tonumber(props.count) local text = ('%5d - %s'):format(quantity, name) - local enabled = props.enabled == 'true' + local enabled = props.enabled == 'true' and cats[props.category] + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end return { text=text, - icon=enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN, + enabled=enabled, + icon=icon, name=name, + cat=props.category, quantity=quantity, } end @@ -894,7 +880,6 @@ function QualityAndMaterialsPage:refresh() local subviews = self.subviews local heat = getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) - subviews.safety:setOption(heat) if heat >= 2 then summary = summary .. 'Magma safe ' elseif heat == 1 then summary = summary .. 'Fire safe ' end @@ -904,12 +889,12 @@ function QualityAndMaterialsPage:refresh() subviews.min_quality:setOption(quality.min_quality) subviews.max_quality:setOption(quality.max_quality) - local categories = utils.invert(getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1)) + local cats = getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) local category_choices={ - make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', categories), - make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', categories), - make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', categories), - make_cat_choice('Other', 'other', 'CUSTOM_SHIFT_T', categories), + make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', cats), + make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), + make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), + make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), } self.subviews.materials_categories:setChoices(category_choices) @@ -918,7 +903,7 @@ function QualityAndMaterialsPage:refresh() local hide_zero = self.subviews.hide_zero:getOptionValue() for name,props in pairs(mat_filter) do if not hide_zero or tonumber(props.count) > 0 then - table.insert(mat_choices, make_mat_choice(name, props)) + table.insert(mat_choices, make_mat_choice(name, props, cats)) end end table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) @@ -933,20 +918,37 @@ function QualityAndMaterialsPage:get_summary() return self.summary end -function QualityAndMaterialsPage:toggle_category(idx, choice) - choice.enabled = not choice.enabled +function QualityAndMaterialsPage:toggle_category(_, choice) local cats = {} - for _,c in ipairs(self.subviews.materials_categories:getChoices()) do - if c.enabled then - table.insert(cats, c.cat) + if not choice.icon then + -- toggling from unset to something is set + table.insert(cats, choice.cat) + else + choice.enabled = not choice.enabled + for _,c in ipairs(self.subviews.materials_categories:getChoices()) do + if c.enabled then + table.insert(cats, c.cat) + end end end setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) self.dirty = true end -function QualityAndMaterialsPage:set_heat_safety(heat) - setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) +function QualityAndMaterialsPage:toggle_material(_, choice) + local mats = {} + if not choice.icon then + -- toggling from unset to something is set + table.insert(mats, choice.name) + else + choice.enabled = not choice.enabled + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + if c.enabled then + table.insert(mats, c.name) + end + end + end + setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) self.dirty = true end From c752223fbcb41e21f9ef832a97bc285d966dd84b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 18:31:03 -0800 Subject: [PATCH 0756/2222] implement invert and reset --- plugins/lua/buildingplan.lua | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index fae27c3e5..a6c98cf2e 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -712,7 +712,6 @@ function QualityAndMaterialsPage:init() scroll_keys={}, icon_width=2, cursor_pen=COLOR_CYAN, - on_double_click=self:callback('toggle_category'), on_submit=self:callback('toggle_category'), }, widgets.List{ @@ -818,21 +817,17 @@ function QualityAndMaterialsPage:init() }, widgets.HotkeyLabel{ frame={l=30, t=0}, - label='Select all', - auto_width=true, - key='CUSTOM_SHIFT_A', - }, - widgets.HotkeyLabel{ - frame={l=30, t=1}, label='Invert selection', auto_width=true, key='CUSTOM_SHIFT_I', + on_activate=self:callback('invert_materials'), }, widgets.HotkeyLabel{ frame={l=30, t=2}, - label='Clear selection', + label='Reset filter', auto_width=true, - key='CUSTOM_SHIFT_C', + key='CUSTOM_SHIFT_X', + on_activate=self:callback('clear_filter'), }, }, } @@ -952,6 +947,23 @@ function QualityAndMaterialsPage:toggle_material(_, choice) self.dirty = true end +function QualityAndMaterialsPage:invert_materials() + local mats = {} + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + if not c.icon then return end + if not c.enabled then + table.insert(mats, c.name) + end + end + setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) + self.dirty = true +end + +function QualityAndMaterialsPage:clear_filter() + clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + self.dirty = true +end + function QualityAndMaterialsPage:set_decorated(decorated) local subviews = self.subviews setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, From ea549f6572d79eeff89576147931db23e2c813f9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 19:15:04 -0800 Subject: [PATCH 0757/2222] generate text summary as the filter changes --- plugins/buildingplan/buildingplan.cpp | 6 +- plugins/lua/buildingplan.lua | 130 ++++++++++++++------------ 2 files changed, 74 insertions(+), 62 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index de133e668..99e9a9af2 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -742,17 +742,17 @@ static int setMaterialMaskFilter(lua_State *L) { type, subtype, custom, index, mask); ItemFilter filter = filters[index]; filter.setMaterialMask(mask); + set new_mats; if (mask) { // remove materials from the list that don't match the mask const auto &mats = filter.getMaterials(); - set new_mats; const df::dfhack_material_category mat_mask(mask); for (auto & mat : mats) { if (mat.matches(mat_mask)) new_mats.emplace(mat); } - filter.setMaterials(new_mats); } + filter.setMaterials(new_mats); get_item_filters(*out, key).setItemFilter(*out, filter, index); call_buildingplan_lua(out, "signal_reset"); return 0; @@ -813,6 +813,8 @@ static int setMaterialFilter(lua_State *L) { filter.setMaterials(mats); // ensure relevant masks are explicitly enabled df::dfhack_material_category mask = filter.getMaterialMask(); + if (!mats.size()) + mask.whole = 0; // if all materials are disabled, reset the mask for (auto & mat : mats) { if (mat.matches(stone_cat)) mask.whole |= stone_cat.whole; diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index a6c98cf2e..898b520b8 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -96,6 +96,44 @@ local function get_quantity(filter, hollow, placement_data) return quantity * dimx * dimy * dimz end +local function to_title_case(str) + str = str:gsub('(%a)([%w_]*)', + function (first, rest) return first:upper()..rest:lower() end) + str = str:gsub('_', ' ') + return str +end + +function get_desc(filter) + local desc = 'Unknown' + if filter.has_tool_use and filter.has_tool_use > -1 then + desc = to_title_case(df.tool_uses[filter.has_tool_use]) + elseif filter.flags2 and filter.flags2.screw then + desc = 'Screw' + elseif filter.item_type and filter.item_type > -1 then + desc = to_title_case(df.item_type[filter.item_type]) + elseif filter.vector_id and filter.vector_id > -1 then + desc = to_title_case(df.job_item_vector_id[filter.vector_id]) + elseif filter.flags2 and filter.flags2.building_material then + desc = 'Building material'; + if filter.flags2.fire_safe then + desc = 'Fire-safe material'; + end + if filter.flags2.magma_safe then + desc = 'Magma-safe material'; + end + end + + if desc:endswith('s') then + desc = desc:sub(1,-2) + end + if desc == 'Trappart' then + desc = 'Mechanism' + elseif desc == 'Wood' then + desc = 'Log' + end + return desc +end + local BUTTON_START_PEN, BUTTON_END_PEN, SELECTED_ITEM_PEN = nil, nil, nil local reset_counts_flag = false local reset_inspector_flag = false @@ -622,7 +660,7 @@ QualityAndMaterialsPage.ATTRS{ } local TYPE_COL_WIDTH = 20 -local HEADER_HEIGHT = 6 +local HEADER_HEIGHT = 7 local QUALITY_HEIGHT = 9 local FOOTER_HEIGHT = 4 @@ -650,6 +688,7 @@ end function QualityAndMaterialsPage:init() self.dirty = true + self.summary = '' local enable_item_quality = can_be_improved(self.index) @@ -660,15 +699,18 @@ function QualityAndMaterialsPage:init() frame_inset={l=1}, subviews={ widgets.Label{ - frame={l=0, t=0, h=1, r=0}, - text={ - 'Current filter:', - {gap=1, pen=COLOR_LIGHTCYAN, text=self:callback('get_summary')} - }, + frame={l=0, t=0}, + text='Current filter:', + }, + widgets.WrappedLabel{ + frame={l=16, t=0, h=2, r=0}, + text_pen=COLOR_LIGHTCYAN, + text_to_wrap=function() return self.summary end, + auto_height=false, }, widgets.CycleHotkeyLabel{ view_id='mat_sort', - frame={l=0, t=2, w=21}, + frame={l=0, t=3, w=21}, label='Sort by:', key='CUSTOM_SHIFT_R', options={ @@ -679,14 +721,14 @@ function QualityAndMaterialsPage:init() }, widgets.ToggleHotkeyLabel{ view_id='hide_zero', - frame={l=0, t=3, w=24}, + frame={l=0, t=4, w=24}, label='Hide unavailable:', key='CUSTOM_SHIFT_H', initial_option=false, on_change=function() self.dirty = true end, }, widgets.EditField{ - frame={l=26, t=2}, + frame={l=26, t=3}, label_text='Search: ', on_char=function(ch) return ch:match('[%l -]') end, }, @@ -852,10 +894,9 @@ local function make_cat_choice(label, cat, key, cats) } end -local function make_mat_choice(name, props, cats) +local function make_mat_choice(name, props, enabled, cats) local quantity = tonumber(props.count) local text = ('%5d - %s'):format(quantity, name) - local enabled = props.enabled == 'true' and cats[props.category] local icon = nil if not cats.unset then icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN @@ -871,12 +912,13 @@ local function make_mat_choice(name, props, cats) end function QualityAndMaterialsPage:refresh() - local summary = '' + local summary = get_desc(get_cur_filters()[self.index]) local subviews = self.subviews local heat = getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) - if heat >= 2 then summary = summary .. 'Magma safe ' - elseif heat == 1 then summary = summary .. 'Fire safe ' + if heat >= 2 then summary = 'Magma safe ' .. summary + elseif heat == 1 then summary = 'Fire safe ' .. summary + else summary = 'Any ' .. summary end local quality = getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) @@ -893,24 +935,30 @@ function QualityAndMaterialsPage:refresh() } self.subviews.materials_categories:setChoices(category_choices) - local mat_filter = getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + local mats = getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) local mat_choices = {} local hide_zero = self.subviews.hide_zero:getOptionValue() - for name,props in pairs(mat_filter) do + local enabled_mat_names = {} + for name,props in pairs(mats) do + local enabled = props.enabled == 'true' and cats[props.category] + if not cats.unset and enabled then + table.insert(enabled_mat_names, name) + end if not hide_zero or tonumber(props.count) > 0 then - table.insert(mat_choices, make_mat_choice(name, props, cats)) + table.insert(mat_choices, make_mat_choice(name, props, enabled, cats)) end end table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) self.subviews.materials_mats:setChoices(mat_choices) + if #enabled_mat_names > 0 then + table.sort(enabled_mat_names) + summary = summary .. (' of %s'):format(table.concat(enabled_mat_names, ', ')) + end + self.summary = summary self.dirty = false -end - -function QualityAndMaterialsPage:get_summary() - -- TODO: summarize materials - return self.summary + self:updateLayout() end function QualityAndMaterialsPage:toggle_category(_, choice) @@ -1228,13 +1276,6 @@ local function is_over_options_panel() return v:getMousePos() end -local function to_title_case(str) - str = str:gsub('(%a)([%w_]*)', - function (first, rest) return first:upper()..rest:lower() end) - str = str:gsub('_', ' ') - return str -end - ItemLine = defclass(ItemLine, widgets.Panel) ItemLine.ATTRS{ idx=DEFAULT_NIL, @@ -1303,37 +1344,6 @@ function ItemLine:get_x_pen() COLOR_GREEN or COLOR_GREY end -function get_desc(filter) - local desc = 'Unknown' - if filter.has_tool_use and filter.has_tool_use > -1 then - desc = to_title_case(df.tool_uses[filter.has_tool_use]) - elseif filter.flags2 and filter.flags2.screw then - desc = 'Screw' - elseif filter.item_type and filter.item_type > -1 then - desc = to_title_case(df.item_type[filter.item_type]) - elseif filter.vector_id and filter.vector_id > -1 then - desc = to_title_case(df.job_item_vector_id[filter.vector_id]) - elseif filter.flags2 and filter.flags2.building_material then - desc = 'Building material'; - if filter.flags2.fire_safe then - desc = 'Fire-safe material'; - end - if filter.flags2.magma_safe then - desc = 'Magma-safe material'; - end - end - - if desc:endswith('s') then - desc = desc:sub(1,-2) - end - if desc == 'Trappart' then - desc = 'Mechanism' - elseif desc == 'Wood' then - desc = 'Log' - end - return desc -end - function ItemLine:get_item_line_text() local idx = self.idx local filter = get_cur_filters()[idx] From d009668339ac37cbac00d07b05855b473345147d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 22:09:21 -0800 Subject: [PATCH 0758/2222] implement materials search --- plugins/lua/buildingplan.lua | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 898b520b8..eae8a9b43 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -728,6 +728,7 @@ function QualityAndMaterialsPage:init() on_change=function() self.dirty = true end, }, widgets.EditField{ + view_id='search', frame={l=26, t=3}, label_text='Search: ', on_char=function(ch) return ch:match('[%l -]') end, @@ -744,7 +745,8 @@ function QualityAndMaterialsPage:init() }, }, }, - widgets.Panel{view_id='materials_lists', + widgets.Panel{ + view_id='materials_lists', frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, frame_style=gui.INTERIOR_FRAME, subviews={ @@ -756,7 +758,7 @@ function QualityAndMaterialsPage:init() cursor_pen=COLOR_CYAN, on_submit=self:callback('toggle_category'), }, - widgets.List{ + widgets.FilteredList{ view_id='materials_mats', frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, icon_width=2, @@ -874,6 +876,12 @@ function QualityAndMaterialsPage:init() }, } } + + -- replace the FilteredList's built-in EditField with our own + self.subviews.materials_mats.list.frame.t = 0 + self.subviews.materials_mats.edit.visible = false + self.subviews.materials_mats.edit = self.subviews.search + self.subviews.search.on_change = self.subviews.materials_mats:callback('onFilterChange') end local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} @@ -949,7 +957,10 @@ function QualityAndMaterialsPage:refresh() end end table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) + + local prev_filter = self.subviews.search.text self.subviews.materials_mats:setChoices(mat_choices) + self.subviews.materials_mats:setFilter(prev_filter) if #enabled_mat_names > 0 then table.sort(enabled_mat_names) @@ -984,9 +995,12 @@ function QualityAndMaterialsPage:toggle_material(_, choice) -- toggling from unset to something is set table.insert(mats, choice.name) else - choice.enabled = not choice.enabled for _,c in ipairs(self.subviews.materials_mats:getChoices()) do - if c.enabled then + local enabled = c.enabled + if choice.name == c.name then + enabled = not c.enabled + end + if enabled then table.insert(mats, c.name) end end From efb307ed25cf8600d3c55e71bcf508f6e65f51cd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 22:09:45 -0800 Subject: [PATCH 0759/2222] remove mock warning --- plugins/lua/buildingplan.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index eae8a9b43..ca193d38b 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1165,7 +1165,7 @@ end FilterSelection = defclass(FilterSelection, widgets.Window) FilterSelection.ATTRS{ - frame_title='Choose filters [MOCK -- NOT FUNCTIONAL]', + frame_title='Choose filters', frame={w=55, h=53, l=30, t=8}, frame_inset={t=1}, resizable=true, From 80addc92d128ea93272c7d15b53e9e1c3c406195 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 23:04:03 -0800 Subject: [PATCH 0760/2222] remember default max quality for each item --- plugins/buildingplan/buildingplan.cpp | 4 +++- plugins/buildingplan/defaultitemfilters.cpp | 2 +- plugins/buildingplan/itemfilter.cpp | 20 ++++++++++++-------- plugins/buildingplan/itemfilter.h | 3 ++- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 99e9a9af2..ec85d7952 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -705,7 +705,9 @@ static void clearFilter(color_ostream &out, df::building_type type, int16_t subt auto &filters = get_item_filters(out, key); if (index < 0 || filters.getItemFilters().size() <= (size_t)index) return; - filters.setItemFilter(out, ItemFilter(), index); + ItemFilter filter = filters.getItemFilters()[index]; + filter.clear(); + filters.setItemFilter(out, filter, index); call_buildingplan_lua(&out, "signal_reset"); } diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index 851565370..3c3b2f3a9 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -41,7 +41,7 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, set_config_val(filter_config, FILTER_CONFIG_CUSTOM, std::get<2>(key)); item_filters.resize(jitems.size()); for (size_t idx = 0; idx < jitems.size(); ++idx) { - item_filters[idx].setMaxQuality(get_max_quality(jitems[idx])); + item_filters[idx].setMaxQuality(get_max_quality(jitems[idx]), true); } filter_config.val() = serialize_item_filters(item_filters); } diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index c7794e109..e9639f281 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -14,13 +14,13 @@ using std::vector; using namespace DFHack; -ItemFilter::ItemFilter() { +ItemFilter::ItemFilter() : default_max_quality(df::item_quality::Masterful) { clear(); } void ItemFilter::clear() { min_quality = df::item_quality::Ordinary; - max_quality = df::item_quality::Masterful; + max_quality = default_max_quality; decorated_only = false; mat_mask.whole = 0; materials.clear(); @@ -28,7 +28,7 @@ void ItemFilter::clear() { bool ItemFilter::isEmpty() const { return min_quality == df::item_quality::Ordinary - && max_quality == df::item_quality::Masterful + && max_quality == default_max_quality && !decorated_only && !mat_mask.whole && materials.empty(); @@ -62,12 +62,10 @@ static bool deserializeMaterials(string ser, set &material return true; } -ItemFilter::ItemFilter(color_ostream &out, string serialized) { - clear(); - +ItemFilter::ItemFilter(color_ostream &out, string serialized) : ItemFilter() { vector tokens; split_string(&tokens, serialized, "/"); - if (tokens.size() != 5) { + if (tokens.size() < 5) { DEBUG(status,out).print("invalid ItemFilter serialization: '%s'", serialized.c_str()); return; } @@ -78,6 +76,9 @@ ItemFilter::ItemFilter(color_ostream &out, string serialized) { setMinQuality(atoi(tokens[2].c_str())); setMaxQuality(atoi(tokens[3].c_str())); decorated_only = static_cast(atoi(tokens[4].c_str())); + + if (tokens.size() >= 6) + default_max_quality = static_cast(atoi(tokens[5].c_str())); } // format: mat,mask,elements/materials,list/minq/maxq/decorated @@ -93,6 +94,7 @@ string ItemFilter::serialize() const { ser << "/" << static_cast(min_quality); ser << "/" << static_cast(max_quality); ser << "/" << static_cast(decorated_only); + ser << "/" << static_cast(default_max_quality); return ser.str(); } @@ -114,11 +116,13 @@ void ItemFilter::setMinQuality(int quality) { max_quality = min_quality; } -void ItemFilter::setMaxQuality(int quality) { +void ItemFilter::setMaxQuality(int quality, bool is_default) { max_quality = static_cast(quality); clampItemQuality(&max_quality); if (max_quality < min_quality) min_quality = max_quality; + if (is_default) + default_max_quality = max_quality; } void ItemFilter::setDecoratedOnly(bool decorated) { diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h index 1bed7b7f6..5ae59dd4a 100644 --- a/plugins/buildingplan/itemfilter.h +++ b/plugins/buildingplan/itemfilter.h @@ -15,7 +15,7 @@ public: std::string serialize() const; void setMinQuality(int quality); - void setMaxQuality(int quality); + void setMaxQuality(int quality, bool is_default = false); void setDecoratedOnly(bool decorated); void setMaterialMask(uint32_t mask); void setMaterials(const std::set &materials); @@ -33,6 +33,7 @@ public: private: df::item_quality min_quality; df::item_quality max_quality; + df::item_quality default_max_quality; bool decorated_only; df::dfhack_material_category mat_mask; std::set materials; From e9060624af9e892fa9a1bbe3d3bc6541b5a021ec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 5 Mar 2023 23:04:35 -0800 Subject: [PATCH 0761/2222] use same hotkey for sorting, hide unused elements --- plugins/lua/buildingplan.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index ca193d38b..a666103e5 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -267,10 +267,11 @@ function ItemSelection:init() choices=self:get_choices(sort_by_recency), icon_width=2, on_submit=self:callback('toggle_group'), + edit_on_char=function(ch) return ch:match('[%l -]') end, }, widgets.CycleHotkeyLabel{ frame={l=0, b=2}, - key='CUSTOM_CTRL_X', + key='CUSTOM_SHIFT_R', label='Sort by:', options={ {label='Recently used', value=sort_by_recency}, @@ -288,7 +289,7 @@ function ItemSelection:init() }, widgets.HotkeyLabel{ frame={l=22, b=1}, - key='CUSTOM_CTRL_D', + key='CUSTOM_SHIFT_B', label='Build', auto_width=true, on_activate=self:callback('submit'), @@ -1760,7 +1761,7 @@ function PlannerOverlay:onInput(keys) end end end - return keys._MOUSE_L + return keys._MOUSE_L or keys.SELECT end function PlannerOverlay:render(dc) @@ -1994,6 +1995,7 @@ function InspectorOverlay:init() frame={t=11, l=0}, label='adjust filters', key='CUSTOM_CTRL_F', + visible=false, -- until implemented }, widgets.HotkeyLabel{ frame={t=12, l=0}, From bb0d4c410dae7194271ac8df04ce31d9d388ba6d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 00:00:25 -0800 Subject: [PATCH 0762/2222] update docs --- docs/plugins/buildingplan.rst | 126 ++++++++++++++++++++++++++++------ docs/plugins/overlay.rst | 4 +- 2 files changed, 107 insertions(+), 23 deletions(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index f77e5b590..d6fce19b8 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -18,41 +18,41 @@ for the placed building, it will be created in a suspended state. Buildingplan will periodically scan for appropriate items and attach them. Once all items are attached, the construction job will be unsuspended and a dwarf will come and build the building. If you have the `unsuspend` overlay enabled (it is enabled -by default), then buildingplan-suspended buildings will appear with a ``P`` marker -on the main map, as opposed to the usual ``x`` marker for "regular" suspended -buildings. +by default), then buildingplan-suspended buildings will appear with a ``P`` +marker on the main map, as opposed to the usual ``x`` marker for "regular" +suspended buildings. If you want to impose restrictions on which items are chosen for the buildings, buildingplan has full support for quality and material filters. Before you place -a building, you can select a component item in the list and hit ``f`` or click on -the ``filter`` button next to the item description. This will let you choose your -desired item quality range, whether the item must be decorated, and even which -specific materials the item must be made out of. This lets you create layouts -with a consistent color, if that is part of your design. +a building, you can select a component item in the list and hit ``f`` or click +on the ``filter`` button next to the item description. This will let you choose +your desired item quality range, whether the item must be decorated, and even +which specific materials the item must be made out of. This lets you create +layouts with a consistent color, if that is part of your design. If you just care about the heat sensitivity of the building, you can set the building to be fire- or magma-proof in the placement UI screen or in any item -filter screen, and the restriction will apply to all building items. This makes it -very easy to create magma-safe pump stacks, for example. +filter screen, and the restriction will apply to all building items. This makes +it very easy to create magma-safe pump stacks, for example. Buildingplan works very well in conjuction with other design tools like -`gui/quickfort`, which allow you to apply a building layout from a blueprint. You -can apply very large, complicated layouts, and the buildings will simply be built -when your dwarves get around to producing the needed materials. If you set filters -in the buildingplan UI before applying the blueprint, the filters will be applied -to the blueprint buildings, just as if you had planned them from the buildingplan -placement UI. +`gui/quickfort`, which allow you to apply a building layout from a blueprint. +You can apply very large, complicated layouts, and the buildings will simply be +built when your dwarves get around to producing the needed materials. If you +set filters in the buildingplan UI before applying the blueprint, the filters +will be applied to the blueprint buildings, just as if you had planned them +from the buildingplan placement UI. One way to integrate buildingplan into your gameplay is to create manager workorders to ensure you always have a few blocks/doors/beds/etc. available. You can then place as many of each building as you like. Produced items will be used -to build the planned buildings as they are produced, with minimal space dedicated -to stockpiles. The DFHack `orders` library can help with setting up these manager -workorders for you. +to build the planned buildings as they are produced, with minimal space +dedicated to stockpiles. The DFHack `orders` library can help with setting up +these manager workorders for you. If you do not wish to use the ``buildingplan`` interface, you can turn off the -``buildingplan.planner`` overlay in `gui/overlay`. You should not disable the -``buildingplan`` service entirely in `gui/control-panel` since then existing +``buildingplan.planner`` overlay in `gui/control-panel`. You should not disable +the ``buildingplan`` service entirely in `gui/control-panel` since existing planned buildings in loaded forts will stop functioning. Usage @@ -92,3 +92,87 @@ constructions:: on-new-fortress buildingplan set boulders false on-new-fortress buildingplan set logs false + +Building placement +------------------ + +Once you have selected a building type to build in the vanilla build menu, the +`buildingplan` placement UI appears as an `overlay` widget, covering the +vanilla building placement panel. + +For basic usage, you don't need to change any settings. Just click to place +buildings of the selected type and right click to exit building mode. Any +buildings that require materials that you don't have on hand will be suspended +and built when the items are available. + +When building constructions, you'll get a few extra options, like whether the +construction area should be hollow or what types of stairs you'd like at the +top and bottom of a stairwell. Also, unlike other buildings, it is ok if some +tiles selected in the construction area are not appropriate for building. For +example, if you want to fill an area with flooring, you can select the entire +area, and any tiles with existing buildings or walls will simply be skipped. + +Setting heat safety filters ++++++++++++++++++++++++++++ + +If you specifically need the building to be magma- or fire-safe, click on the +"Building safety" button or hit :kbd:`g` until the desired heat safety is +displayed. This filter applies to all items used to construct the building. + +Setting quality and material filters +++++++++++++++++++++++++++++++++++++ + +If you want to set restrictions on the items chosen to complete the planned +building, you can click on the "filter" button next to the item name or select +the item with the :kbd:`*` and :kbd:`/` keys and hit :kbd:`f` to bring up the +filter dialog. + +You can select whether the item must be decorated, and you can drag the ends of +the "Item quality" slider to set your desired quality range. Note that blocks, +boulders, logs, and bars don't have a quality and the quality options are +disabled for those types. As you change the quality settings, the number of +currently available matched items of each material appears in the materials +list. + +You can click on specific materials to allow only items of those materials when +building the current type of building. You can also allow or disallow entire +categories of materials by clicking on the "Type" options on the left. Note +that it is perfectly fine to choose materials that currently show 0 quantity. +`buildingplan` will patiently watch for items made of materials you have +selected. + +Choosing specific items ++++++++++++++++++++++++ + +If you want to choose specific items, click on the "Choose from items" toggle +or hit :kbd:`i` before placing the building. When you click to place the +building, a dialog will come up that allows you choose which items to use. The +list is sorted by most recently used materials for that building type by +default, but you can change to sort by name or by available quantity by +clicking on the "Sort by" selector or hitting :kbd:`R`. + +You can select the maximum quantity of a specified item by clicking on the item +name or selecting it with the arrow keys and hitting :kbd:`Enter`. You can +instead select items one at a time by Ctrl-clicking (:kbd:`Shift`:kbd:`Right`) +to increment or Ctrl-Shift-clicking (:kbd:`Shift`:kbd:`Left`) to decrement. + +Once you are satisfied with your choices, click on the "Build" button or hit +:kbd:`B` to continue building. Note that you don't have to select all the items +that the building needs. Any remaining items will be automatically chosen from +other available items (or future items if not all items are available yet). If +there are multiple item types to choose for the current building, one dialog +will appear per item type. + +Building status +--------------- + +When viewing a planned building, a separate `overlay` widget appears on the +building info sheet, showing you which items have been attached and which items +are still pending. For the pending items, you can see its position in the +fulfillment queue. If there is a particular building that you need built ASAP, +you can click on the "make top priority" button (or hit :kbd:`Ctrl`:kbd:`T`) to +bump the items for this building to the front of their respective queues. + +Note that each item type and filter configuration has its own queue, so even if +an item is in queue position 1, there may be other queues that snag the needed +item first. diff --git a/docs/plugins/overlay.rst b/docs/plugins/overlay.rst index ab63de67e..313220fd4 100644 --- a/docs/plugins/overlay.rst +++ b/docs/plugins/overlay.rst @@ -7,8 +7,8 @@ overlay The overlay framework manages the on-screen widgets that other tools (including 3rd party plugins and scripts) can register for display. For a graphical -configuration interface, please see `gui/overlay`. If you are a developer who -wants to write an overlay widget, please see the `overlay-dev-guide`. +configuration interface, please see `gui/control-panel`. If you are a developer +who wants to write an overlay widget, please see the `overlay-dev-guide`. Usage ----- From 2220c5c07e5fce1520b67d4092c20ac9c04d61ab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 00:20:02 -0800 Subject: [PATCH 0763/2222] editing pass --- docs/plugins/buildingplan.rst | 62 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index d6fce19b8..fe3683f77 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -10,32 +10,29 @@ regardless of whether the required materials are available. This allows you to focus purely on design elements when you are laying out your fort, and defers item production concerns to a more convenient time. -Buildingplan is as an alternative to the vanilla building placement UI. It -appears after you have selected the type of building, furniture, or construction -that you want to place in the vanilla build menu. Buildingplan then takes over -for the actual placement step. If any building materials are not available yet -for the placed building, it will be created in a suspended state. Buildingplan -will periodically scan for appropriate items and attach them. Once all items are -attached, the construction job will be unsuspended and a dwarf will come and -build the building. If you have the `unsuspend` overlay enabled (it is enabled -by default), then buildingplan-suspended buildings will appear with a ``P`` -marker on the main map, as opposed to the usual ``x`` marker for "regular" -suspended buildings. +Buildingplan is an alternative to the vanilla building placement UI. It appears +after you have selected the type of building, furniture, or construction that +you want to place in the vanilla build menu. Buildingplan then takes over for +the actual placement step. If the placed building requires materials that +aren't available yet, it will be created in a suspended state. Buildingplan will +periodically scan for appropriate items and attach them to the planned +building. Once all items are attached, the construction job will be unsuspended +and a dwarf will come and build the building. If you have the `unsuspend` +overlay enabled (it is enabled by default), then buildingplan-suspended +buildings will appear with a ``P`` marker on the main map, as opposed to the +usual ``x`` marker for "regular" suspended buildings. If you want to impose restrictions on which items are chosen for the buildings, -buildingplan has full support for quality and material filters. Before you place -a building, you can select a component item in the list and hit ``f`` or click -on the ``filter`` button next to the item description. This will let you choose -your desired item quality range, whether the item must be decorated, and even -which specific materials the item must be made out of. This lets you create -layouts with a consistent color, if that is part of your design. +buildingplan has full support for quality and material filters (see `below +`_). This lets you create layouts with a +consistent color, if that is part of your design. If you just care about the heat sensitivity of the building, you can set the -building to be fire- or magma-proof in the placement UI screen or in any item -filter screen, and the restriction will apply to all building items. This makes -it very easy to create magma-safe pump stacks, for example. +building to be fire- or magma-proof in the placement UI screen. This makes it +very easy to ensure that your pump stacks and floodgates, for example, are +magma-safe. -Buildingplan works very well in conjuction with other design tools like +Buildingplan works well in conjuction with other design tools like `gui/quickfort`, which allow you to apply a building layout from a blueprint. You can apply very large, complicated layouts, and the buildings will simply be built when your dwarves get around to producing the needed materials. If you @@ -47,13 +44,14 @@ One way to integrate buildingplan into your gameplay is to create manager workorders to ensure you always have a few blocks/doors/beds/etc. available. You can then place as many of each building as you like. Produced items will be used to build the planned buildings as they are produced, with minimal space -dedicated to stockpiles. The DFHack `orders` library can help with setting up -these manager workorders for you. +dedicated to stockpiles. The DFHack `orders` library can help with setting +these manager workorders up for you. If you do not wish to use the ``buildingplan`` interface, you can turn off the -``buildingplan.planner`` overlay in `gui/control-panel`. You should not disable -the ``buildingplan`` service entirely in `gui/control-panel` since existing -planned buildings in loaded forts will stop functioning. +``buildingplan.planner`` overlay in `gui/control-panel` (on the "Overlays" +tab). You should not disable the ``buildingplan`` "System service" in +`gui/control-panel` since existing planned buildings in loaded forts will stop +functioning. Usage ----- @@ -70,6 +68,10 @@ Examples Print a report of current settings, which kinds of buildings are planned, and what kinds of materials the buildings are waiting for. +``buildingplan set boulders false`` + When finding items to satisfy "building materials" requirements, don't + select boulders. Use blocks or logs (if enabled) instead. + .. _buildingplan-settings: Global settings @@ -129,15 +131,15 @@ filter dialog. You can select whether the item must be decorated, and you can drag the ends of the "Item quality" slider to set your desired quality range. Note that blocks, -boulders, logs, and bars don't have a quality and the quality options are +boulders, logs, and bars don't have a quality, and the quality options are disabled for those types. As you change the quality settings, the number of -currently available matched items of each material appears in the materials +currently available matched items of each material is adjusted in the materials list. You can click on specific materials to allow only items of those materials when building the current type of building. You can also allow or disallow entire categories of materials by clicking on the "Type" options on the left. Note -that it is perfectly fine to choose materials that currently show 0 quantity. +that it is perfectly fine to choose materials that currently show zero quantity. `buildingplan` will patiently watch for items made of materials you have selected. @@ -168,7 +170,7 @@ Building status When viewing a planned building, a separate `overlay` widget appears on the building info sheet, showing you which items have been attached and which items -are still pending. For the pending items, you can see its position in the +are still pending. For a pending item, you can see its position in the fulfillment queue. If there is a particular building that you need built ASAP, you can click on the "make top priority" button (or hit :kbd:`Ctrl`:kbd:`T`) to bump the items for this building to the front of their respective queues. From 161f84e7b7b350e5a0f6475f0203dd7cc44fa9c3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 01:27:27 -0800 Subject: [PATCH 0764/2222] turn down automaterial (superseded by buildingplan) --- docs/about/Removed.rst | 8 + docs/changelog.txt | 1 + docs/plugins/automaterial.rst | 53 -- plugins/CMakeLists.txt | 1 - plugins/automaterial.cpp | 1340 --------------------------------- plugins/lua/automaterial.lua | 23 - 6 files changed, 9 insertions(+), 1417 deletions(-) delete mode 100644 docs/plugins/automaterial.rst delete mode 100644 plugins/automaterial.cpp delete mode 100644 plugins/lua/automaterial.lua diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 954aa2ce2..eea11b088 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -18,6 +18,14 @@ An automated labor management tool that only addressed hauling labors, leaving t of skilled labors entirely up to the player. Fundamentally incompatible with the work detail system of labor management in v50 of Dwarf Fortress. +.. _automaterial: + +automaterial +============ +Moved frequently used materials to the top of the materials list when building +buildings. Also offered extended options when building constructions. All +functionality has been merged into `buildingplan`. + .. _combine-drinks: combine-drinks diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a3428031..3eb93b1c0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -56,6 +56,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Removed -@ ``gui.THIN_FRAME``: replaced by ``gui.INTERIOR_FRAME`` +- `automaterial`: all functionality has been merged into `buildingplan` # 50.07-alpha2 diff --git a/docs/plugins/automaterial.rst b/docs/plugins/automaterial.rst deleted file mode 100644 index 54653a685..000000000 --- a/docs/plugins/automaterial.rst +++ /dev/null @@ -1,53 +0,0 @@ -automaterial -============ - -.. dfhack-tool:: - :summary: Sorts building materials by recent usage. - :tags: untested fort design productivity buildings map - :no-command: - -This plugin makes building constructions (walls, floors, fortifications, etc) -much easier by saving you from having to trawl through long lists of materials -each time you place one. - -It moves the last used material for a given construction type to the top of the -list, if there are any left. So if you build a wall with chalk blocks, the next -time you place a wall the chalk blocks will be at the top of the list, -regardless of distance (it only does this in "grouped" mode, as individual item -lists could be huge). This means you can place most constructions without having -to search for your preferred material type. - -Usage ------ - -:: - - enable automaterial - -.. image:: ../images/automaterial-mat.png - -Pressing :kbd:`a` while highlighting any material will enable that material for -"auto select" for this construction type. You can enable multiple materials. Now -the next time you place this type of construction, the plugin will automatically -choose materials for you from the kinds you enabled. If there is enough to -satisfy the whole placement, you won't be prompted with the material screen at -all -- the construction will be placed and you will be back in the construction -menu. - -When choosing the construction placement, you will see a couple of options: - -.. image:: ../images/automaterial-pos.png - -Use :kbd:`a` here to temporarily disable the material autoselection, e.g. if you -need to go to the material selection screen so you can toggle some materials on -or off. - -The other option (auto type selection, off by default) can be toggled on with -:kbd:`t`. If you toggle this option on, instead of returning you to the main -construction menu after selecting materials, it returns you back to this screen. -If you use this along with several autoselect enabled materials, you should be -able to place complex constructions more conveniently. - -The ``automaterial`` plugin also enables extra construction placement modes, -such as designating areas larger than 10x10 and allowing you to designate hollow -rectangles instead of the default filled ones. diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index d87b41978..477e83436 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -82,7 +82,6 @@ dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) add_subdirectory(autolabor) -#dfhack_plugin(automaterial automaterial.cpp LINK_LIBRARIES lua) dfhack_plugin(automelt automelt.cpp LINK_LIBRARIES lua) dfhack_plugin(autonestbox autonestbox.cpp LINK_LIBRARIES lua) #dfhack_plugin(autotrade autotrade.cpp) diff --git a/plugins/automaterial.cpp b/plugins/automaterial.cpp deleted file mode 100644 index 9783d28dc..000000000 --- a/plugins/automaterial.cpp +++ /dev/null @@ -1,1340 +0,0 @@ -// Auto Material Select - -#include -#include -#include - -#include "Core.h" -#include "LuaTools.h" -#include -#include -#include -#include - - -// DF data structure definition headers -#include "DataDefs.h" -#include "Debug.h" -#include "MiscUtils.h" -#include "TileTypes.h" -#include "df/build_req_choice_genst.h" -#include "df/build_req_choice_specst.h" -#include "df/construction_type.h" -#include "df/item.h" -#include "df/plotinfost.h" -#include "df/buildreq.h" -#include "df/viewscreen_dwarfmodest.h" -#include "df/items_other_id.h" -#include "df/job.h" -#include "df/world.h" -#include "df/building_constructionst.h" -#include "df/job_item.h" - -#include "modules/Gui.h" -#include "modules/Screen.h" -#include "modules/Items.h" -#include "modules/Constructions.h" -#include "modules/Buildings.h" -#include "modules/Maps.h" -#include "modules/MapCache.h" - -#include "uicommon.h" - -using namespace std; -using std::map; -using std::string; -using std::vector; - -using namespace DFHack; -using namespace df::enums; - -DFHACK_PLUGIN("automaterial"); -REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(plotinfo); -REQUIRE_GLOBAL(ui_build_selector); - -namespace DFHack { - DBG_DECLARE(automaterial,log,DebugCategory::LINFO); -} - -struct MaterialDescriptor -{ - df::item_type item_type; - int16_t item_subtype; - int16_t type; - int32_t index; - bool valid; - - bool matches(const MaterialDescriptor &a) const - { - return a.valid && valid && - a.type == type && - a.index == index && - a.item_type == item_type && - a.item_subtype == item_subtype; - } -}; - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} - -void AMOutputToggleString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = true, int left_margin = 0, int8_t color = COLOR_WHITE) -{ - OutputHotkeyString(x, y, text, hotkey); - OutputString(COLOR_WHITE, x, y, ": "); - if (state) - OutputString(COLOR_GREEN, x, y, "Enabled", newline, left_margin); - else - OutputString(COLOR_GREY, x, y, "Disabled", newline, left_margin); -} - -//START UI Functions - -static enum t_box_select_mode {SELECT_FIRST, SELECT_SECOND, SELECT_MATERIALS, AUTOSELECT_MATERIALS} box_select_mode = SELECT_FIRST; -static coord32_t box_first, box_second; -static bool box_select_enabled = false; -static bool show_box_selection = true; -static bool hollow_selection = false; -static deque box_select_materials; - -#define SELECTION_IGNORE_TICKS 1 -static int ignore_selection = SELECTION_IGNORE_TICKS; - -static map last_used_material; -static map last_moved_material; -static map< int16_t, vector > preferred_materials; -static map< int16_t, df::interface_key > hotkeys; -static bool last_used_moved = false; -static bool auto_choose_materials = true; -static bool revert_to_last_used_type = false; -static bool allow_future_placement = false; - -static inline bool in_material_choice_stage() -{ - return Gui::build_selector_hotkey(Core::getTopViewscreen()) && - ui_build_selector->building_type == df::building_type::Construction && - plotinfo->main.mode == ui_sidebar_mode::Build && - ui_build_selector->stage == 2; -} - -static inline bool in_placement_stage() -{ - return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - plotinfo->main.mode == ui_sidebar_mode::Build && - ui_build_selector && - ui_build_selector->building_type == df::building_type::Construction && - ui_build_selector->stage == 1; -} - -static inline bool in_type_choice_stage() -{ - return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && - plotinfo->main.mode == ui_sidebar_mode::Build && - ui_build_selector && - ui_build_selector->building_type < 0; -} - -static inline vector &get_curr_constr_prefs() -{ - if (preferred_materials.find(ui_build_selector->building_subtype) == preferred_materials.end()) - preferred_materials[ui_build_selector->building_subtype] = vector(); - - return preferred_materials[ui_build_selector->building_subtype]; -} - -static inline MaterialDescriptor &get_last_used_material() -{ - if (last_used_material.find(ui_build_selector->building_subtype) == last_used_material.end()) - last_used_material[ui_build_selector->building_subtype] = MaterialDescriptor(); - - return last_used_material[ui_build_selector->building_subtype]; -} - -static void set_last_used_material(const MaterialDescriptor &matetial) -{ - last_used_material[ui_build_selector->building_subtype] = matetial; -} - -static MaterialDescriptor &get_last_moved_material() -{ - if (last_moved_material.find(ui_build_selector->building_subtype) == last_moved_material.end()) - last_moved_material[ui_build_selector->building_subtype] = MaterialDescriptor(); - - return last_moved_material[ui_build_selector->building_subtype]; -} - -static void set_last_moved_material(const MaterialDescriptor &matetial) -{ - last_moved_material[ui_build_selector->building_subtype] = matetial; -} - -static MaterialDescriptor get_material_in_list(size_t i) -{ - MaterialDescriptor result; - result.valid = false; - - if (VIRTUAL_CAST_VAR(gen, df::build_req_choice_genst, ui_build_selector->choices[i])) - { - result.item_type = gen->item_type; - result.item_subtype = gen->item_subtype; - result.type = gen->mat_type; - result.index = gen->mat_index; - result.valid = true; - } - else if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, ui_build_selector->choices[i])) - { - result.item_type = spec->candidate->getType(); - result.item_subtype = spec->candidate->getSubtype(); - result.type = spec->candidate->getActualMaterial(); - result.index = spec->candidate->getActualMaterialIndex(); - result.valid = true; - } - - return result; -} - -static bool is_material_in_autoselect(size_t &i, MaterialDescriptor &material) -{ - for (i = 0; i < get_curr_constr_prefs().size(); i++) - { - if (get_curr_constr_prefs()[i].matches(material)) - return true; - } - - return false; -} - -static bool is_material_in_list(size_t &i, MaterialDescriptor &material) -{ - const size_t size = ui_build_selector->choices.size(); //Just because material list could be very big - for (i = 0; i < size; i++) - { - if (get_material_in_list(i).matches(material)) - return true; - } - - return false; -} - -static bool move_material_to_top(MaterialDescriptor &material) -{ - size_t i; - if (is_material_in_list(i, material)) - { - auto sel_item = ui_build_selector->choices[i]; - ui_build_selector->choices.erase(ui_build_selector->choices.begin() + i); - ui_build_selector->choices.insert(ui_build_selector->choices.begin(), sel_item); - - ui_build_selector->sel_index = 0; - set_last_moved_material(material); - return true; - } - - set_last_moved_material(MaterialDescriptor()); - return false; -} - -static bool check_autoselect(MaterialDescriptor &material, bool toggle) -{ - size_t idx; - if (is_material_in_autoselect(idx, material)) - { - if (toggle) - vector_erase_at(get_curr_constr_prefs(), idx); - - return true; - } - else - { - if (toggle) - get_curr_constr_prefs().push_back(material); - - return false; - } -} - -static void cancel_box_selection() -{ - if (box_select_mode == SELECT_FIRST) - return; - - box_select_mode = SELECT_FIRST; - box_select_materials.clear(); - if (!show_box_selection) - Gui::setDesignationCoords(-1, -1, -1); -} -//END UI Functions - - -//START Building and Verification -struct building_site -{ - df::coord pos; - bool in_open_air; - - building_site(df::coord pos, bool in_open_air) - { - this->pos = pos; - this->in_open_air = in_open_air; - } - - building_site() {} -}; - -static deque valid_building_sites; -static deque open_air_sites; -static building_site anchor; - -static bool is_orthogonal_to_pending_construction(building_site &site) -{ - for (deque::iterator it = valid_building_sites.begin(); it != valid_building_sites.end(); it++) - { - if ((it->pos.x == site.pos.x && abs(it->pos.y - site.pos.y) == 1) || (it->pos.y == site.pos.y && abs(it->pos.x - site.pos.x) == 1)) - { - site.in_open_air = true; - return true; - } - } - - return false; -} - -static df::building_constructionst *get_construction_on_tile(const df::coord &pos) -{ - auto current = Buildings::findAtTile(pos); - if (current) - return strict_virtual_cast(current); - - return NULL; -} - -static df::tiletype *read_tile_shapes(const df::coord &pos, df::tiletype_shape &shape, df::tiletype_shape_basic &shape_basic) -{ - if (!Maps::isValidTilePos(pos)) - return NULL; - - auto ttype = Maps::getTileType(pos); - - if (!ttype) - return NULL; - - shape = tileShape(*ttype); - shape_basic = tileShapeBasic(shape); - - return ttype; -} - -static bool is_valid_building_site(building_site &site, bool orthogonal_check, bool check_placed_constructions, bool in_future_placement_mode) -{ - df::tiletype_shape shape; - df::tiletype_shape_basic shape_basic; - - auto ttype = read_tile_shapes(site.pos, shape, shape_basic); - if (!ttype) - return false; - - if (shape_basic == tiletype_shape_basic::Open) - { - if (orthogonal_check) - { - // Check if this is a valid tile to have a construction placed orthogonally to it - if (!in_future_placement_mode) - return false; - - df::building_constructionst *cons = get_construction_on_tile(site.pos); - if (cons && cons->type == construction_type::Floor) - { - site.in_open_air = true; - return true; - } - - return false; - } - - // Stairs can be placed in open space, if they can connect to other stairs - df::tiletype_shape shape_s; - df::tiletype_shape_basic shape_basic_s; - - if (ui_build_selector->building_subtype == construction_type::DownStair || - ui_build_selector->building_subtype == construction_type::UpDownStair) - { - df::coord below(site.pos.x, site.pos.y, site.pos.z - 1); - auto ttype_s = read_tile_shapes(below, shape_s, shape_basic_s); - if (ttype_s) - { - if (shape_s == tiletype_shape::STAIR_UP || shape_s == tiletype_shape::STAIR_UPDOWN) - return true; - } - } - - if (ui_build_selector->building_subtype == construction_type::UpStair || - ui_build_selector->building_subtype == construction_type::UpDownStair) - { - df::coord above(site.pos.x, site.pos.y, site.pos.z + 1); - auto ttype_s = read_tile_shapes(above, shape_s, shape_basic_s); - if (ttype_s) - { - if (shape_s == tiletype_shape::STAIR_DOWN || shape_s == tiletype_shape::STAIR_UPDOWN) - return true; - } - } - - // Check if there is a valid tile orthogonally adjacent - bool valid_orthogonal_tile_found = false; - df::coord orthagonal_pos; - orthagonal_pos.z = site.pos.z; - for (orthagonal_pos.x = site.pos.x-1; orthagonal_pos.x <= site.pos.x+1 && !valid_orthogonal_tile_found; orthagonal_pos.x++) - { - for (orthagonal_pos.y = site.pos.y-1; orthagonal_pos.y <= site.pos.y+1; orthagonal_pos.y++) - { - if ((site.pos.x == orthagonal_pos.x) == (site.pos.y == orthagonal_pos.y)) - continue; - - building_site orthogonal_site(orthagonal_pos, false); - if (is_valid_building_site(orthogonal_site, true, check_placed_constructions, in_future_placement_mode)) - { - valid_orthogonal_tile_found = true; - if (orthogonal_site.in_open_air) - site.in_open_air = true; - break; - } - - } - } - - if (!(valid_orthogonal_tile_found || (check_placed_constructions && is_orthogonal_to_pending_construction(site)))) - { - site.in_open_air = true; - return false; - } - } - else if (orthogonal_check) - { - if (shape != tiletype_shape::RAMP && - shape_basic != tiletype_shape_basic::Floor && - shape_basic != tiletype_shape_basic::Stair) - return false; - } - else - { - auto material = tileMaterial(*ttype); - if (shape == tiletype_shape::RAMP) - { - if (material == tiletype_material::CONSTRUCTION) - return false; - } - else - { - if (shape != tiletype_shape::STAIR_DOWN && shape_basic != tiletype_shape_basic::Floor) - return false; - - // Can build on top of a wall, but not on other construction - auto construction = Constructions::findAtTile(site.pos); - if (construction) - { - if (construction->flags.bits.top_of_wall==0) - return false; - } - - if (material == tiletype_material::FIRE || - material == tiletype_material::POOL || - material == tiletype_material::BROOK || - material == tiletype_material::RIVER || - material == tiletype_material::MAGMA || - material == tiletype_material::DRIFTWOOD || - material == tiletype_material::CAMPFIRE - ) - - return false; - } - } - - if (orthogonal_check) - return true; - - auto designation = Maps::getTileDesignation(site.pos); - if (designation->bits.flow_size > 2) - return false; - - auto current = Buildings::findAtTile(site.pos); - if (current) - return false; - - df::coord2d size(1,1); - return Buildings::checkFreeTiles(site.pos, size, NULL, false, false); -} - - -static bool find_anchor_in_spiral(const df::coord &start) -{ - bool found = false; - - for (anchor.pos.z = start.z; anchor.pos.z > start.z - 4; anchor.pos.z--) - { - int x, y, dx, dy; - x = y = dx = 0; - dy = -1; - const int side = 11; - const int maxI = side*side; - for (int i = 0; i < maxI; i++) - { - if (-side/2 < x && x <= side/2 && -side/2 < y && y <= side/2) - { - anchor.pos.x = start.x + x; - anchor.pos.y = start.y + y; - if (is_valid_building_site(anchor, false, false, false)) - { - found = true; - break; - } - } - - if ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y))) - { - int tmp = dx; - dx = -dy; - dy = tmp; - } - - x += dx; - y += dy; - } - - if (found) - break; - } - - return found; -} - -static bool find_valid_building_sites(bool in_future_placement_mode, bool use_buildingplan) -{ - valid_building_sites.clear(); - open_air_sites.clear(); - - int xD = (box_second.x > box_first.x) ? 1 : -1; - int yD = (box_second.y > box_first.y) ? 1 : -1; - for (int32_t xB = box_first.x; (xD > 0) ? (xB <= box_second.x) : (xB >= box_second.x); xB += xD) - { - for (int32_t yB = box_first.y; (yD > 0) ? (yB <= box_second.y) : (yB >= box_second.y); yB += yD) - { - if (hollow_selection && !(xB == box_first.x || xB == box_second.x || yB == box_first.y || yB == box_second.y)) - continue; - - building_site site(df::coord(xB, yB, box_second.z), false); - // if we're using buildingplan, it will take care of filtering out bad tiles - if (use_buildingplan || is_valid_building_site(site, false, true, in_future_placement_mode)) - valid_building_sites.push_back(site); - else if (site.in_open_air) - { - if (in_future_placement_mode) - valid_building_sites.push_back(site); - else - open_air_sites.push_back(site); - } - } - } - - if (!use_buildingplan) - { - size_t last_open_air_count = 0; - while (valid_building_sites.size() > 0 && open_air_sites.size() != last_open_air_count) - { - last_open_air_count = open_air_sites.size(); - deque current_open_air_list = open_air_sites; - open_air_sites.clear(); - for (deque::iterator it = current_open_air_list.begin(); it != current_open_air_list.end(); it++) - { - if (is_orthogonal_to_pending_construction(*it)) - valid_building_sites.push_back(*it); - else - open_air_sites.push_back(*it); - } - - } - } - - return valid_building_sites.size() > 0; -} - -static bool is_buildingplan_enabled() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!(lua_checkstack(L, 1) && - Lua::PushModulePublic(out, L, "plugins.buildingplan", "isEnabled") && - Lua::SafeCall(out, L, 0, 1))) - { - return false; - } - - return lua_toboolean(L, -1); -} - -static bool is_buildingplan_planmode_enabled( - df::building_type type, int16_t subtype, int32_t custom) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 4) || - !Lua::PushModulePublic( - out, L, "plugins.buildingplan", "isPlanModeEnabled")) - return false; - - Lua::Push(L, type); - Lua::Push(L, subtype); - Lua::Push(L, custom); - - if (!Lua::SafeCall(out, L, 3, 1)) - return false; - - return lua_toboolean(L, -1); -} - -static bool is_buildingplan_managed() -{ - return is_buildingplan_enabled() && - is_buildingplan_planmode_enabled(ui_build_selector->building_type, - ui_build_selector->building_subtype, - ui_build_selector->custom_type); -} - -static bool build_with_buildingplan_box_select(const df::coord &pos) -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top(L); - - if (!lua_checkstack(L, 5) || - !Lua::PushModulePublic( - out, L, "plugins.automaterial", - "build_with_buildingplan_box_select")) - { - return false; - } - - Lua::Push(L, ui_build_selector->building_subtype); - Lua::Push(L, pos.x); - Lua::Push(L, pos.y); - Lua::Push(L, pos.z); - - if (!Lua::SafeCall(out, L, 4, 1)) - return false; - - return lua_toboolean(L, -1); -} - -static bool build_with_buildingplan_ui() -{ - auto L = Lua::Core::State; - color_ostream_proxy out(Core::getInstance().getConsole()); - - CoreSuspendClaimer suspend; - Lua::StackUnwinder top(L); - - return lua_checkstack(L, 1) && - Lua::PushModulePublic(out, L, "plugins.automaterial", - "build_with_buildingplan_ui") && - Lua::SafeCall(out, L, 0, 1); -} - -static bool designate_new_construction(df::coord &pos, df::construction_type &type, df::item *item) -{ - auto newinst = Buildings::allocInstance(pos, building_type::Construction, type); - if (!newinst) - return false; - - vector items; - items.push_back(item); - Maps::ensureTileBlock(pos); - - if (!Buildings::constructWithItems(newinst, items)) - { - delete newinst; - return false; - } - - return true; -} -//END Building and Verification - - -//START Viewscreen Hook -struct jobutils_hook : public df::viewscreen_dwarfmodest -{ - //START UI Methods - typedef df::viewscreen_dwarfmodest interpose_base; - - void send_key(const df::interface_key &key) - { - set< df::interface_key > keys; - keys.insert(key); - this->feed(&keys); - } - - bool select_material_at_index(size_t i) - { - ui_build_selector->sel_index = i; - std::set< df::interface_key > keys; - keys.insert(df::interface_key::SELECT_ALL); - this->feed(&keys); - return !in_material_choice_stage(); - } - - bool choose_materials() - { - if (!auto_choose_materials || get_curr_constr_prefs().size() == 0) - return false; - - size_t size = ui_build_selector->choices.size(); - for (size_t i = 0; i < size; i++) - { - MaterialDescriptor material = get_material_in_list(i); - size_t j; - if (is_material_in_autoselect(j, material)) - { - return select_material_at_index(i); - } - } - - return false; - } - - void draw_box_selection() - { - if (!box_select_enabled) - return; - - if (plotinfo->main.mode != df::ui_sidebar_mode::Build || - ui_build_selector->building_type != df::building_type::Construction) - return; - - df::coord vport = Gui::getViewportPos(); - - //Even if selection drawing is disabled, paint a green cursor as we can place box selection anywhere - - if (box_select_mode == SELECT_FIRST || (!show_box_selection && box_select_mode == SELECT_SECOND)) - { - int32_t x, y, z; - if (!Gui::getCursorCoords(x, y, z)) - return; - - x = x - vport.x + 1; - y = y - vport.y + 1; - OutputString(COLOR_GREEN, x, y, "X", false, 0, 0, true /* map */); - } - else if (show_box_selection && box_select_mode == SELECT_SECOND) - { - if (!Gui::getCursorCoords(box_second.x, box_second.y, box_second.z)) - return; - - Gui::DwarfmodeDims dims = Gui::getDwarfmodeViewDims(); - int32_t startx = std::max((int32_t)vport.x, std::min(box_first.x, box_second.x)); - int32_t endx = std::min(vport.x + dims.map_x2 - dims.map_x1, std::max(box_first.x, box_second.x)); - int32_t starty = std::max((int32_t)vport.y, std::min(box_first.y, box_second.y)); - int32_t endy = std::min(vport.y + dims.map_y2 - dims.map_y1, std::max(box_first.y, box_second.y)); - for (int32_t yB = starty; yB <= endy; ++yB) { - for (int32_t xB = startx; xB <= endx; ++xB) { - if (hollow_selection && !(xB == box_first.x || xB == box_second.x || yB == box_first.y || yB == box_second.y)) - continue; - - int8_t color = (xB == box_second.x && yB == box_second.y) ? COLOR_GREEN : COLOR_BROWN; - - int32_t x = xB - vport.x + 1; - int32_t y = yB - vport.y + 1; - OutputString(color, x, y, "X", false, 0, 0, true /* map */); - } - } - } - else if (show_box_selection && box_select_mode == SELECT_MATERIALS) - { - for (deque::iterator it = valid_building_sites.begin(); it != valid_building_sites.end(); it++) - { - int32_t x = it->pos.x - vport.x + 1; - int32_t y = it->pos.y - vport.y + 1; - OutputString(COLOR_GREEN, x, y, "X", false, 0, 0, true /* map */); - } - } - } - - void reset_existing_selection() - { - for (int i = 0; i < 10; i++) - { - send_key(df::interface_key::BUILDING_DIM_Y_DOWN); - send_key(df::interface_key::BUILDING_DIM_X_DOWN); - } - } - - void handle_input(set *input) - { - if (ui_build_selector->building_subtype >= 7) - return; - - if (in_material_choice_stage()) - { - if (input->count(interface_key::LEAVESCREEN)) - { - box_select_mode = SELECT_FIRST; - } - - MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index); - if (material.valid) - { - if (input->count(interface_key::SELECT) || input->count(interface_key::SELECT_ALL)) - { - if (get_last_moved_material().matches(material)) - last_used_moved = false; //Keep selected material on top - - set_last_used_material(material); - - if (box_select_enabled) - { - auto curr_index = ui_build_selector->sel_index; - vector gen_material; - gen_material.push_back(get_material_in_list(curr_index)); - box_select_materials.clear(); - // Populate material list with selected material - populate_box_materials(gen_material, ((input->count(interface_key::SELECT_ALL) && ui_build_selector->is_grouped) ? -1 : 1)); - - input->clear(); // Let the apply_box_selection routine allocate the construction - input->insert(interface_key::LEAVESCREEN); - } - } - else if (input->count(interface_key::CUSTOM_A)) - { - check_autoselect(material, true); - input->clear(); - } - } - } - else if (in_placement_stage()) - { - bool use_buildingplan = is_buildingplan_managed(); - - if (!use_buildingplan && input->count(interface_key::CUSTOM_A)) - { - auto_choose_materials = !auto_choose_materials; - } - else if (!use_buildingplan && input->count(interface_key::CUSTOM_T)) - { - revert_to_last_used_type = !revert_to_last_used_type; - } - else if (input->count(interface_key::CUSTOM_B)) - { - reset_existing_selection(); - box_select_enabled = !box_select_enabled; - if (!box_select_enabled) - cancel_box_selection(); - - return; - } - else if (!use_buildingplan && input->count(interface_key::CUSTOM_O)) - { - allow_future_placement = !allow_future_placement; - } - else if (input->count(interface_key::LEAVESCREEN)) - { - switch (box_select_mode) - { - case SELECT_FIRST: - case SELECT_SECOND: - cancel_box_selection(); - - default: - break; - } - } - else if (box_select_enabled) - { - if (input->count(interface_key::SELECT)) - { - switch (box_select_mode) - { - case SELECT_FIRST: - if (!Gui::getCursorCoords(box_first.x, box_first.y, box_first.z)) - { - cancel_box_selection(); - return; - } - box_select_mode = SELECT_SECOND; - if (!show_box_selection) - Gui::setDesignationCoords(box_first.x, box_first.y, box_first.z); - input->clear(); - return; - - case SELECT_SECOND: - if (!Gui::getCursorCoords(box_second.x, box_second.y, box_second.z)) - { - cancel_box_selection(); - return; - } - cancel_box_selection(); - input->clear(); - apply_box_selection(true); - return; - - default: - break; - } - } - else if (input->count(interface_key::CUSTOM_X)) - { - show_box_selection = !show_box_selection; - if (box_select_mode == SELECT_SECOND) - { - if (show_box_selection) - { - Gui::setDesignationCoords(-1, -1, -1); - } - else - { - Gui::setDesignationCoords(box_first.x, box_first.y, box_first.z); - } - } - } - else if (input->count(interface_key::CUSTOM_H)) - { - hollow_selection = !hollow_selection; - } - else if (input->count(interface_key::BUILDING_DIM_Y_UP) || - input->count(interface_key::BUILDING_DIM_Y_DOWN) || - input->count(interface_key::BUILDING_DIM_X_UP) || - input->count(interface_key::BUILDING_DIM_X_DOWN)) - { - input->clear(); - return; - } - } - else if (use_buildingplan - && ui_build_selector->errors.size() == 0 - && input->count(interface_key::SELECT)) - { - build_with_buildingplan_ui(); - Gui::refreshSidebar(); - input->clear(); - return; - } - } - } - //END UI Methods - - //START Building Application - bool populate_box_materials(vector &gen_materials, int32_t count = -1) - { - bool result = false; - - if (gen_materials.size() == 0) - return result; - - if (ui_build_selector->is_grouped) - send_key(interface_key::BUILDING_EXPAND_CONTRACT); - - size_t size = ui_build_selector->choices.size(); - vector::iterator gen_material; - for (size_t i = 0; i < size; i++) - { - if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, ui_build_selector->choices[i])) - { - for (gen_material = gen_materials.begin(); gen_material != gen_materials.end(); gen_material++) - { - if (gen_material->item_type == spec->candidate->getType() && - gen_material->item_subtype == spec->candidate->getSubtype() && - gen_material->type == spec->candidate->getActualMaterial() && - gen_material->index == spec->candidate->getActualMaterialIndex()) - { - box_select_materials.push_back(spec->candidate); - if (count > -1) - return true; // Right now we only support 1 or all materials - - result = true; - break; - } - } - } - } - send_key(interface_key::BUILDING_EXPAND_CONTRACT); - - return result; - } - - void move_cursor(df::coord &pos) - { - int32_t x, y, z; - Gui::getCursorCoords(x, y, z); - DEBUG(log).print("moving cursor from %d, %d, %d to %d, %d, %d\n", - x, y, z, pos.x, pos.y, pos.z); - - Gui::setCursorCoords(pos.x, pos.y, pos.z); - Gui::refreshSidebar(); - } - - void move_cursor(coord32_t &pos) - { - df::coord c((int16_t) pos.x, (int16_t) pos.y, (int16_t) pos.z); - move_cursor(c); - } - - void apply_box_selection(bool new_start) - { - static bool saved_revert_setting = false; - static bool auto_select_applied = false; - - bool use_buildingplan = is_buildingplan_managed(); - box_select_mode = SELECT_MATERIALS; - if (new_start) - { - bool ok_to_continue = false; - bool in_future_placement_mode = false; - if (!find_valid_building_sites(false, use_buildingplan)) - { - if (allow_future_placement) - { - in_future_placement_mode = find_valid_building_sites(true, use_buildingplan); - } - } - else - { - ok_to_continue = true; - } - - // if using buildingplan, we don't need an anchor - if (!use_buildingplan) - { - if (in_future_placement_mode) - { - ok_to_continue = - find_anchor_in_spiral(valid_building_sites[0].pos); - } - else if (ok_to_continue) - { - // First valid site is guaranteed to be anchored, either on - // a tile or against a valid orthogonal tile - // Use it as an anchor point to generate materials list - anchor = valid_building_sites.front(); - valid_building_sites.pop_front(); - valid_building_sites.push_back(anchor); - } - } - - if (!ok_to_continue) - { - cancel_box_selection(); - hollow_selection = false; - return; - } - - saved_revert_setting = revert_to_last_used_type; - revert_to_last_used_type = true; - auto_select_applied = false; - box_select_materials.clear(); - - } - - while (valid_building_sites.size() > 0) - { - building_site site = valid_building_sites.front(); - valid_building_sites.pop_front(); - - if (use_buildingplan) - { - // we don't actually care if this fails. buildingplan will return - // false when it filters out bad tiles, and that's ok. - build_with_buildingplan_box_select(site.pos); - continue; - } - - if (box_select_materials.size() > 0) - { - df::construction_type type = (df::construction_type) ui_build_selector->building_subtype; - df::item *item = NULL; - while (box_select_materials.size() > 0) - { - item = box_select_materials.front(); - if (!item->flags.bits.in_job) - break; - box_select_materials.pop_front(); - item = NULL; - } - - if (item != NULL) - { - if (designate_new_construction(site.pos, type, item)) - { - box_select_materials.pop_front(); - box_select_mode = AUTOSELECT_MATERIALS; - send_key(interface_key::LEAVESCREEN); //Must do this to register items in use - send_key(hotkeys[type]); - box_select_mode = SELECT_MATERIALS; - } - continue; - } - } - - // Generate material list using regular construction placement routine - - if (site.in_open_air) - { - // Cannot invoke material selection on an unconnected tile, use anchor instead - move_cursor(anchor.pos); - send_key(df::interface_key::SELECT); - } - - move_cursor(site.pos); - - if (!site.in_open_air) - send_key(df::interface_key::SELECT); - - if (in_material_choice_stage()) - { - valid_building_sites.push_front(site); //Redo current tile with whatever gets selected - if (!auto_select_applied) - { - // See if any auto select materials are available - auto_select_applied = true; - if (auto_choose_materials && populate_box_materials(preferred_materials[ui_build_selector->building_subtype])) - { - continue; - } - } - - last_used_moved = false; - return; // No auto select materials left, ask user - } - } - - // Allocation done, reset - move_cursor(box_second); - - // if we're using buildingplan, we never actually leave the placement - // screen, so there's no need to re-enter the screen - revert_to_last_used_type = saved_revert_setting; - if (!use_buildingplan && !revert_to_last_used_type) - { - send_key(df::interface_key::LEAVESCREEN); - } - - cancel_box_selection(); - hollow_selection = false; - ignore_selection = 0; - } - //END Building Application - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (ignore_selection < SELECTION_IGNORE_TICKS) - { - //FIXME: Sometimes there's an extra ENTER key left over after box selection - ignore_selection = SELECTION_IGNORE_TICKS; - return; - } - - if (box_select_mode != AUTOSELECT_MATERIALS) - handle_input(input); - - int16_t last_used_constr_subtype = (in_material_choice_stage()) ? ui_build_selector->building_subtype : -1; - INTERPOSE_NEXT(feed)(input); - - if (revert_to_last_used_type && - last_used_constr_subtype >= 0 && - in_type_choice_stage() && - hotkeys.find(last_used_constr_subtype) != hotkeys.end()) - { - input->clear(); - input->insert(hotkeys[last_used_constr_subtype]); - INTERPOSE_NEXT(feed)(input); - - if (box_select_mode == SELECT_MATERIALS) - { - apply_box_selection(false); - } - } - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - if (ignore_selection < SELECTION_IGNORE_TICKS) - { - ++ignore_selection; - } - - if (in_material_choice_stage()) - { - if (!last_used_moved && ui_build_selector->is_grouped) - { - last_used_moved = true; - if (!box_select_enabled && choose_materials()) - { - return; - } - else - { - move_material_to_top(get_last_used_material()); - } - } - else if (!ui_build_selector->is_grouped) - { - last_used_moved = false; - } - } - else - { - last_used_moved = false; - } - - INTERPOSE_NEXT(render)(); - - draw_box_selection(); - - if (in_type_choice_stage()) - { - cancel_box_selection(); - return; - } - - auto dims = Gui::getDwarfmodeViewDims(); - int left_margin = dims.menu_x1 + 1; - int x = left_margin; - int y = 25; - if (in_material_choice_stage()) - { - MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index); - if (material.valid) - { - AMOutputToggleString(x, y, "Autoselect", "a", check_autoselect(material, false), true, left_margin); - - if (box_select_mode == SELECT_MATERIALS) - { - ++y; - OutputString(COLOR_BROWN, x, y, "Construction:", true, left_margin); - OutputString(COLOR_WHITE, x, y, int_to_string(valid_building_sites.size()) + " tiles to fill", true, left_margin); - } - } - } - else if (in_placement_stage() && ui_build_selector->building_subtype < 7) - { - bool use_buildingplan = is_buildingplan_managed(); - - OutputString(COLOR_BROWN, x, y, "DFHack Automaterial Options", true, left_margin); - if (use_buildingplan) - { - y += 2; - } - else - { - AMOutputToggleString(x, y, "Auto Mat-select", "a", auto_choose_materials, true, left_margin); - AMOutputToggleString(x, y, "Reselect Type", "t", revert_to_last_used_type, true, left_margin); - } - - ++y; - AMOutputToggleString(x, y, "Box Select", "b", box_select_enabled, true, left_margin); - if (box_select_enabled) - { - AMOutputToggleString(x, y, "Show Box Mask", "x", show_box_selection, true, left_margin); - OutputHotkeyString(x, y, (hollow_selection) ? "Make Solid" : "Make Hollow", "h", true, left_margin); - - if (use_buildingplan) - ++y; - else - AMOutputToggleString(x, y, "Open Placement", "o", allow_future_placement, true, left_margin); - } - else - { - y += 3; - } - y += 2; - if (is_buildingplan_enabled()) - OutputString(COLOR_BROWN, x, y, "DFHack Buildingplan Options", true, left_margin); - - if (box_select_enabled) - { - Screen::Pen pen(' ',COLOR_BLACK); - y = dims.y1 + 2; - Screen::fillRect(pen, x, y, dims.menu_x2, y + 17); - - y += 2; - switch (box_select_mode) - { - case SELECT_FIRST: - OutputString(COLOR_BROWN, x, y, "Choose first corner", true, left_margin); - break; - - case SELECT_SECOND: - { - OutputString(COLOR_GREEN, x, y, "Choose second corner", true, left_margin); - - int32_t curr_x, curr_y, curr_z; - Gui::getCursorCoords(curr_x, curr_y, curr_z); - int dX = abs(box_first.x - curr_x) + 1; - int dY = abs(box_first.y - curr_y) + 1; - stringstream label; - label << "Selection: " << dX << "x" << dY; - OutputString(COLOR_WHITE, x, ++y, label.str(), true, left_margin); - - df::coord vport = Gui::getViewportPos(); - int cx = box_first.x - vport.x + 1; - int cy = box_first.y - vport.y + 1; - - Gui::DwarfmodeDims dims = Gui::getDwarfmodeViewDims(); - if (cx >= 1 && cx <= dims.map_x2 && cy >= 1 && cy <= dims.map_y2) - OutputString(COLOR_BROWN, cx, cy, "X", false, 0, 0, true /* map */); - break; - } - - default: - break; - } - - OutputString(COLOR_BROWN, x, ++y, "Ignore Building Restrictions", true, left_margin); - } - } - } -}; -//END Viewscreen Hook - -color_ostream_proxy console_out(Core::getInstance().getConsole()); - - -IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, render); - -DFHACK_PLUGIN_IS_ENABLED(is_enabled); - -DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) -{ - if (!gps) - return CR_FAILURE; - - if (enable != is_enabled) - { - if (!INTERPOSE_HOOK(jobutils_hook, feed).apply(enable) || - !INTERPOSE_HOOK(jobutils_hook, render).apply(enable)) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - hotkeys[construction_type::Wall] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_WALL; - hotkeys[construction_type::Floor] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FLOOR; - hotkeys[construction_type::Ramp] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_RAMP; - hotkeys[construction_type::UpStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_UP; - hotkeys[construction_type::DownStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_DOWN; - hotkeys[construction_type::UpDownStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_UPDOWN; - hotkeys[construction_type::Fortification] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FORTIFICATION; - //Ignore tracks, DF already returns to track menu - - return CR_OK; -} diff --git a/plugins/lua/automaterial.lua b/plugins/lua/automaterial.lua deleted file mode 100644 index 1cd7e9faf..000000000 --- a/plugins/lua/automaterial.lua +++ /dev/null @@ -1,23 +0,0 @@ -local _ENV = mkmodule('plugins.automaterial') - -local buildingplan = require('plugins.buildingplan') - --- construct the building and register it with buildingplan for item selection -function build_with_buildingplan_box_select(subtype, x, y, z) - local pos = xyz2pos(x, y, z) - local bld, err = dfhack.buildings.constructBuilding{ - type=df.building_type.Construction, subtype=subtype, pos=pos} - -- it's not a user error if we can't place a building here; just indicate - -- that no building was placed by returning false. - if err then return false end - buildingplan.addPlannedBuilding(bld) - return true -end - -function build_with_buildingplan_ui() - for _,bld in ipairs(buildingplan.construct_buildings_from_ui_state()) do - buildingplan.addPlannedBuilding(bld) - end -end - -return _ENV From da1df122b1228b276366fd2e845646c161ab813a Mon Sep 17 00:00:00 2001 From: Will H Date: Mon, 6 Mar 2023 23:21:28 +1100 Subject: [PATCH 0765/2222] [quickfort] new alias structure Files created with `savestock` for each existing alias. Some aliases missing (search) currently. --- data/stockpiles/adamantinecloth.dfstock | Bin 0 -> 45 bytes data/stockpiles/adamantinethread.dfstock | Bin 0 -> 45 bytes data/stockpiles/ammo.dfstock | Bin 0 -> 1015 bytes data/stockpiles/animalsprefix.dfstock | 71 + data/stockpiles/armorprefix.dfstock | Bin 0 -> 2040 bytes data/stockpiles/artifactammo.dfstock | Bin 0 -> 33 bytes data/stockpiles/artifactarmor.dfstock | Bin 0 -> 38 bytes data/stockpiles/artifactfinishedgoods.dfstock | Bin 0 -> 33 bytes data/stockpiles/artifactfurniture.dfstock | Bin 0 -> 33 bytes data/stockpiles/artifactweapons.dfstock | Bin 0 -> 38 bytes data/stockpiles/ash.dfstock | Bin 0 -> 28 bytes data/stockpiles/bags.dfstock | Bin 0 -> 34 bytes data/stockpiles/bars.dfstock | Bin 0 -> 803 bytes data/stockpiles/barsprefix.dfstock | Bin 0 -> 3450 bytes data/stockpiles/bauxite.dfstock | Bin 0 -> 42 bytes data/stockpiles/blocks.dfstock | Bin 0 -> 2671 bytes data/stockpiles/bolts.dfstock | Bin 0 -> 45 bytes data/stockpiles/boneammo.dfstock | Bin 0 -> 29 bytes data/stockpiles/bones.dfstock | 0 data/stockpiles/bronzearmor.dfstock | Bin 0 -> 46 bytes data/stockpiles/bronzeweapons.dfstock | Bin 0 -> 46 bytes data/stockpiles/buckets.dfstock | Bin 0 -> 31 bytes data/stockpiles/clay.dfstock | Bin 0 -> 125 bytes data/stockpiles/cloth.dfstock | Bin 0 -> 4919 bytes data/stockpiles/clothprefix.dfstock | 141 + data/stockpiles/coal.dfstock | Bin 0 -> 31 bytes data/stockpiles/coalproducing.dfstock | Bin 0 -> 69 bytes data/stockpiles/coinsprefix.dfstock | Bin 0 -> 7467 bytes data/stockpiles/copperarmor.dfstock | Bin 0 -> 46 bytes data/stockpiles/copperweapons.dfstock | Bin 0 -> 46 bytes data/stockpiles/corpses.dfstock | Bin 0 -> 21 bytes data/stockpiles/corpsesprefix.dfstock | Bin 0 -> 21 bytes data/stockpiles/crafts.dfstock | Bin 0 -> 82 bytes data/stockpiles/cutgems.dfstock | Bin 0 -> 2907 bytes data/stockpiles/cutglass.dfstock | Bin 0 -> 64 bytes data/stockpiles/cutstone.dfstock | Bin 0 -> 1880 bytes data/stockpiles/drinkanimal.dfstock | Bin 0 -> 76 bytes data/stockpiles/drinkplant.dfstock | Bin 0 -> 1780 bytes data/stockpiles/dye.dfstock | Bin 0 -> 127 bytes data/stockpiles/economic.dfstock | Bin 0 -> 266 bytes data/stockpiles/emptyanimaltraps.dfstock | Bin 0 -> 27 bytes data/stockpiles/emptycages.dfstock | Bin 0 -> 27 bytes data/stockpiles/finishedgoodsprefix.dfstock | Bin 0 -> 5995 bytes data/stockpiles/flux.dfstock | Bin 0 -> 118 bytes data/stockpiles/foodprefix.dfstock | 18619 ++++++++++++++++ data/stockpiles/furnitureprefix.dfstock | Bin 0 -> 3259 bytes data/stockpiles/gemsprefix.dfstock | Bin 0 -> 7728 bytes data/stockpiles/goblets.dfstock | Bin 0 -> 31 bytes data/stockpiles/hair.dfstock | 0 data/stockpiles/horns.dfstock | 0 data/stockpiles/iron.dfstock | Bin 0 -> 84 bytes data/stockpiles/ironarmor.dfstock | Bin 0 -> 44 bytes data/stockpiles/ironbars.dfstock | Bin 0 -> 39 bytes data/stockpiles/ironweapons.dfstock | Bin 0 -> 44 bytes data/stockpiles/leatherprefix.dfstock | 1363 ++ data/stockpiles/masterworkammo.dfstock | Bin 0 -> 34 bytes data/stockpiles/masterworkarmor.dfstock | Bin 0 -> 39 bytes .../masterworkfinishedgoods.dfstock | Bin 0 -> 34 bytes data/stockpiles/masterworkfurniture.dfstock | Bin 0 -> 34 bytes data/stockpiles/masterworkweapons.dfstock | Bin 0 -> 39 bytes data/stockpiles/metal.dfstock | Bin 0 -> 389 bytes data/stockpiles/metalammo.dfstock | Bin 0 -> 768 bytes data/stockpiles/metalarmor.dfstock | Bin 0 -> 773 bytes data/stockpiles/metalbars.dfstock | Bin 0 -> 768 bytes data/stockpiles/metalweapons.dfstock | Bin 0 -> 773 bytes data/stockpiles/miscliquid.dfstock | Bin 0 -> 57 bytes data/stockpiles/otherarmor.dfstock | Bin 0 -> 122 bytes data/stockpiles/otherbars.dfstock | Bin 0 -> 715 bytes data/stockpiles/otherstone.dfstock | Bin 0 -> 1123 bytes data/stockpiles/otherweapons.dfstock | Bin 0 -> 122 bytes data/stockpiles/pearlash.dfstock | Bin 0 -> 33 bytes data/stockpiles/pigironbars.dfstock | Bin 0 -> 43 bytes data/stockpiles/plants.dfstock | Bin 0 -> 3177 bytes data/stockpiles/plaster.dfstock | Bin 0 -> 103 bytes data/stockpiles/potash.dfstock | Bin 0 -> 31 bytes data/stockpiles/pots.dfstock | Bin 0 -> 33 bytes data/stockpiles/preparedmeals.dfstock | Bin 0 -> 26 bytes data/stockpiles/rawhides.dfstock | 0 data/stockpiles/refuse.dfstock | 0 data/stockpiles/roughgems.dfstock | Bin 0 -> 2907 bytes data/stockpiles/roughglass.dfstock | Bin 0 -> 64 bytes data/stockpiles/sand.dfstock | Bin 0 -> 29 bytes data/stockpiles/seeds.dfstock | Bin 0 -> 3395 bytes data/stockpiles/sheetprefix.dfstock | Bin 0 -> 21 bytes data/stockpiles/shells.dfstock | 0 data/stockpiles/skulls.dfstock | 0 data/stockpiles/soap.dfstock | Bin 0 -> 29 bytes data/stockpiles/steelarmor.dfstock | Bin 0 -> 45 bytes data/stockpiles/steelbars.dfstock | Bin 0 -> 40 bytes data/stockpiles/steelweapons.dfstock | Bin 0 -> 45 bytes data/stockpiles/stone.dfstock | Bin 0 -> 1832 bytes data/stockpiles/stonetools.dfstock | Bin 0 -> 1886 bytes data/stockpiles/stoneweapons.dfstock | Bin 0 -> 1885 bytes data/stockpiles/tallow.dfstock | 1 + data/stockpiles/tannedhides.dfstock | 0 data/stockpiles/teeth.dfstock | 0 data/stockpiles/thread.dfstock | Bin 0 -> 4919 bytes data/stockpiles/tools.dfstock | Bin 0 -> 29 bytes data/stockpiles/trapcomponents.dfstock | Bin 0 -> 224 bytes data/stockpiles/unpreparedfish.dfstock | Bin 0 -> 1720 bytes data/stockpiles/wax.dfstock | Bin 0 -> 98 bytes data/stockpiles/weaponsprefix.dfstock | Bin 0 -> 3838 bytes data/stockpiles/woodammo.dfstock | Bin 0 -> 29 bytes data/stockpiles/woodentools.dfstock | Bin 0 -> 35 bytes data/stockpiles/woodprefix.dfstock | Bin 0 -> 674 bytes 105 files changed, 20195 insertions(+) create mode 100644 data/stockpiles/adamantinecloth.dfstock create mode 100644 data/stockpiles/adamantinethread.dfstock create mode 100644 data/stockpiles/ammo.dfstock create mode 100644 data/stockpiles/animalsprefix.dfstock create mode 100644 data/stockpiles/armorprefix.dfstock create mode 100644 data/stockpiles/artifactammo.dfstock create mode 100644 data/stockpiles/artifactarmor.dfstock create mode 100644 data/stockpiles/artifactfinishedgoods.dfstock create mode 100644 data/stockpiles/artifactfurniture.dfstock create mode 100644 data/stockpiles/artifactweapons.dfstock create mode 100644 data/stockpiles/ash.dfstock create mode 100644 data/stockpiles/bags.dfstock create mode 100644 data/stockpiles/bars.dfstock create mode 100644 data/stockpiles/barsprefix.dfstock create mode 100644 data/stockpiles/bauxite.dfstock create mode 100644 data/stockpiles/blocks.dfstock create mode 100644 data/stockpiles/bolts.dfstock create mode 100644 data/stockpiles/boneammo.dfstock create mode 100644 data/stockpiles/bones.dfstock create mode 100644 data/stockpiles/bronzearmor.dfstock create mode 100644 data/stockpiles/bronzeweapons.dfstock create mode 100644 data/stockpiles/buckets.dfstock create mode 100644 data/stockpiles/clay.dfstock create mode 100644 data/stockpiles/cloth.dfstock create mode 100644 data/stockpiles/clothprefix.dfstock create mode 100644 data/stockpiles/coal.dfstock create mode 100644 data/stockpiles/coalproducing.dfstock create mode 100644 data/stockpiles/coinsprefix.dfstock create mode 100644 data/stockpiles/copperarmor.dfstock create mode 100644 data/stockpiles/copperweapons.dfstock create mode 100644 data/stockpiles/corpses.dfstock create mode 100644 data/stockpiles/corpsesprefix.dfstock create mode 100644 data/stockpiles/crafts.dfstock create mode 100644 data/stockpiles/cutgems.dfstock create mode 100644 data/stockpiles/cutglass.dfstock create mode 100644 data/stockpiles/cutstone.dfstock create mode 100644 data/stockpiles/drinkanimal.dfstock create mode 100644 data/stockpiles/drinkplant.dfstock create mode 100644 data/stockpiles/dye.dfstock create mode 100644 data/stockpiles/economic.dfstock create mode 100644 data/stockpiles/emptyanimaltraps.dfstock create mode 100644 data/stockpiles/emptycages.dfstock create mode 100644 data/stockpiles/finishedgoodsprefix.dfstock create mode 100644 data/stockpiles/flux.dfstock create mode 100644 data/stockpiles/foodprefix.dfstock create mode 100644 data/stockpiles/furnitureprefix.dfstock create mode 100644 data/stockpiles/gemsprefix.dfstock create mode 100644 data/stockpiles/goblets.dfstock create mode 100644 data/stockpiles/hair.dfstock create mode 100644 data/stockpiles/horns.dfstock create mode 100644 data/stockpiles/iron.dfstock create mode 100644 data/stockpiles/ironarmor.dfstock create mode 100644 data/stockpiles/ironbars.dfstock create mode 100644 data/stockpiles/ironweapons.dfstock create mode 100644 data/stockpiles/leatherprefix.dfstock create mode 100644 data/stockpiles/masterworkammo.dfstock create mode 100644 data/stockpiles/masterworkarmor.dfstock create mode 100644 data/stockpiles/masterworkfinishedgoods.dfstock create mode 100644 data/stockpiles/masterworkfurniture.dfstock create mode 100644 data/stockpiles/masterworkweapons.dfstock create mode 100644 data/stockpiles/metal.dfstock create mode 100644 data/stockpiles/metalammo.dfstock create mode 100644 data/stockpiles/metalarmor.dfstock create mode 100644 data/stockpiles/metalbars.dfstock create mode 100644 data/stockpiles/metalweapons.dfstock create mode 100644 data/stockpiles/miscliquid.dfstock create mode 100644 data/stockpiles/otherarmor.dfstock create mode 100644 data/stockpiles/otherbars.dfstock create mode 100644 data/stockpiles/otherstone.dfstock create mode 100644 data/stockpiles/otherweapons.dfstock create mode 100644 data/stockpiles/pearlash.dfstock create mode 100644 data/stockpiles/pigironbars.dfstock create mode 100644 data/stockpiles/plants.dfstock create mode 100644 data/stockpiles/plaster.dfstock create mode 100644 data/stockpiles/potash.dfstock create mode 100644 data/stockpiles/pots.dfstock create mode 100644 data/stockpiles/preparedmeals.dfstock create mode 100644 data/stockpiles/rawhides.dfstock create mode 100644 data/stockpiles/refuse.dfstock create mode 100644 data/stockpiles/roughgems.dfstock create mode 100644 data/stockpiles/roughglass.dfstock create mode 100644 data/stockpiles/sand.dfstock create mode 100644 data/stockpiles/seeds.dfstock create mode 100644 data/stockpiles/sheetprefix.dfstock create mode 100644 data/stockpiles/shells.dfstock create mode 100644 data/stockpiles/skulls.dfstock create mode 100644 data/stockpiles/soap.dfstock create mode 100644 data/stockpiles/steelarmor.dfstock create mode 100644 data/stockpiles/steelbars.dfstock create mode 100644 data/stockpiles/steelweapons.dfstock create mode 100644 data/stockpiles/stone.dfstock create mode 100644 data/stockpiles/stonetools.dfstock create mode 100644 data/stockpiles/stoneweapons.dfstock create mode 100644 data/stockpiles/tallow.dfstock create mode 100644 data/stockpiles/tannedhides.dfstock create mode 100644 data/stockpiles/teeth.dfstock create mode 100644 data/stockpiles/thread.dfstock create mode 100644 data/stockpiles/tools.dfstock create mode 100644 data/stockpiles/trapcomponents.dfstock create mode 100644 data/stockpiles/unpreparedfish.dfstock create mode 100644 data/stockpiles/wax.dfstock create mode 100644 data/stockpiles/weaponsprefix.dfstock create mode 100644 data/stockpiles/woodammo.dfstock create mode 100644 data/stockpiles/woodentools.dfstock create mode 100644 data/stockpiles/woodprefix.dfstock diff --git a/data/stockpiles/adamantinecloth.dfstock b/data/stockpiles/adamantinecloth.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..e63bd31d021f0d0dce2373a5c6e31b96906b9962 GIT binary patch literal 45 zcmXRCa}x3N^AB=&^z(GKa&&R@b@U7I^mCoS$T)+MaRDR43Py!(qrTnWACxx=IKKB|H5O6br#)*%^Gm1b7@^ zp=5V2<~-=`5gt+JGmE$wY|9OceESa3 zU2drJ#dwLpQrbW&svTWZF=gXq{W_ZgRxFx@b{z%kE1}t;xvPtuzS&M`tf)vvOlLo- zVI|lIsMD(*0KT?E0mXsc7{e@FKgLqNjUuT}jW7T`>F=hA4lIC0<4y6gIU<1ltK@xX z-wYRA(q#QZM+Oom!7vT++{016%!|$d{W2}wPht9_%F4y6aND@7@5gU(KAMGYy5@KZ-kyu P>lqDRQ0otB{Y9<+l)4>` literal 0 HcmV?d00001 diff --git a/data/stockpiles/animalsprefix.dfstock b/data/stockpiles/animalsprefix.dfstock new file mode 100644 index 000000000..e09193a2f --- /dev/null +++ b/data/stockpiles/animalsprefix.dfstock @@ -0,0 +1,71 @@ + +™÷TOADTOAD_MAN +GIANT_TOADWORMWORM_MAN BIRD_BLUEJAY BLUEJAY_MAN GIANT_BLUEJAY BIRD_CARDINAL CARDINAL_MANGIANT_CARDINAL BIRD_GRACKLE GRACKLE_MAN GIANT_GRACKLE BIRD_ORIOLE +ORIOLE_MAN GIANT_ORIOLEBIRD_RW_BLACKBIRDRW_BLACKBIRD_MANGIANT_RW_BLACKBIRD BIRD_PENGUINBIRD_PENGUIN_LITTLEBIRD_PENGUIN_EMPEROR PENGUIN MANBIRD_PENGUIN_GIANTBIRD_FALCON_PEREGRINEPEREGRINE FALCON MANGIANT PEREGRINE FALCON BIRD_KIWIKIWI MANBIRD_KIWI_GIANT BIRD_OSTRICH OSTRICH MANBIRD_OSTRICH_GIANT BIRD_CROWCROW_MAN +GIANT_CROW +BIRD_RAVEN RAVEN_MAN GIANT_RAVENBIRD_CASSOWARY CASSOWARY_MANGIANT_CASSOWARYBIRD_KEAKEA_MAN GIANT_KEABIRD_OWL_SNOWY SNOWY_OWL_MANGIANT_SNOWY_OWLSPARROW SPARROW_MAN GIANT_SPARROWBIRD_STORK_WHITEWHITE_STORK_MANGIANT_WHITE_STORK BIRD_LOONLOON_MAN +GIANT_LOON BIRD_OWL_BARN BARN_OWL_MANGIANT_BARN_OWL BIRD_PARAKEET PARAKEET_MANGIANT_PARAKEET BIRD_KAKAPO +KAKAPO_MAN GIANT_KAKAPOBIRD_PARROT_GREYGREY_PARROT_MANGIANT_GREY_PARROT BIRD_PUFFIN +PUFFIN_MAN GIANT_PUFFIN BIRD_SWANSWAN_MAN +GIANT_SWAN BIRD_LORIKEET LORIKEET_MANGIANT_LORIKEET BIRD_WRENWREN_MAN +GIANT_WREN BIRD_OSPREY +OSPREY_MAN GIANT_OSPREYBIRD_EMUEMU_MAN GIANT_EMUBIRD_COCKATIEL COCKATIEL_MANGIANT_COCKATIELBIRD_LOVEBIRD_PEACH-FACEDPEACH-FACED_LOVEBIRD_MANGIANT_PEACH-FACED_LOVEBIRD BIRD_MAGPIE +MAGPIE_MAN GIANT_MAGPIE BIRD_KESTREL KESTREL_MAN GIANT_KESTRELBIRD_ALBATROSS ALBATROSS_MANGIANT_ALBATROSSBIRD_OWL_GREAT_HORNEDGREAT_HORNED_OWL_MANGIANT_GREAT_HORNED_OWL +BIRD_EAGLE EAGLE_MAN GIANT_EAGLE BIRD_HORNBILL HORNBILL_MANGIANT_HORNBILLBIRD_LOVEBIRD_MASKEDMASKED_LOVEBIRD_MANGIANT_MASKED_LOVEBIRD BIRD_BUSHTIT BUSHTIT_MAN GIANT_BUSHTIT DAMSELFLY DAMSELFLY_MANGIANT_DAMSELFLYMOTHMOTH_MAN +GIANT_MOTH GRASSHOPPERGRASSHOPPER_MANGIANT_GRASSHOPPER BARK_SCORPIONBARK_SCORPION_MANGIANT_BARK_SCORPIONMANTIS +MANTIS_MAN GIANT_MANTISTICKTICK_MAN +GIANT_TICKLOUSE LOUSE_MAN GIANT_LOUSETHRIPS +THRIPS_MAN GIANT_THRIPSSLUGSLUG_MAN +GIANT_SLUGMOSQUITO MOSQUITO_MANGIANT_MOSQUITOSPIDER_JUMPINGJUMPING_SPIDER_MANGIANT_JUMPING_SPIDERTERMITE +MOON_SNAILMOON_SNAIL_MANGIANT_MOON_SNAILSPIDER_BROWN_RECLUSEBROWN_RECLUSE_SPIDER_MANGIANT_BROWN_RECLUSE_SPIDERSNAIL SNAIL_MAN GIANT_SNAIL GECKO_LEOPARDLEOPARD_GECKO_MANGIANT_LEOPARD_GECKODESERT TORTOISEDESERT_TORTOISE_MANGIANT_DESERT_TORTOISE GILA_MONSTERGILA_MONSTER_MANGIANT_GILA_MONSTERDOGCATMULEDONKEYHORSECOWSHEEPPIGGOAT BIRD_CHICKENCAVY BIRD_DUCK WATER_BUFFALOREINDEER +BIRD_GOOSEYAKLLAMAALPACABIRD_GUINEAFOWLBIRD_PEAFOWL_BLUE BIRD_TURKEYRABBITEQUIPMENT_WAGONCHIMERACENTAURGRIFFONFLYFLY_MAN GIANT_FLY ROACH_LARGE ROACH_MAN GIANT_ROACHBEETLE +BEETLE_MAN GIANT_BEETLEANTBUTTERFLY_MONARCHBUTTERFLY_MONARCH_MANGIANT_BUTTERFLY_MONARCHFIREFLY FIREFLY_MAN GIANT_FIREFLY DRAGONFLY DRAGONFLY_MANGIANT_DRAGONFLY HONEY_BEE BUMBLEBEE GOAT_MOUNTAINGOAT_MOUNTAIN_MANGIANT_GOAT_MOUNTAIN MARMOT_HOARYMARMOT_HOARY_MANGIANT_MARMOT_HOARYGNOME_MOUNTAIN +GNOME_DARKWALRUS +WALRUS_MAN GIANT_WALRUSFISH_LAMPREY_SEASHARK_GREAT_WHITE SHARK_FRILLSHARK_SPINY_DOGFISHSHARK_WOBBEGONG_SPOTTED SHARK_WHALE SHARK_BASKING SHARK_NURSESHARK_MAKO_SHORTFINSHARK_MAKO_LONGFIN SHARK_TIGER +SHARK_BULLSHARK_REEF_BLACKTIPSHARK_REEF_WHITETIP +SHARK_BLUESHARK_HAMMERHEAD SHARK_ANGELFISH_SKATE_COMMONFISH_RAY_MANTA FISH_STINGRAYFISH_COELACANTH FISH_STURGEONFISH_CONGER_EEL FISH_MILKFISHFISH_COD FISH_OPAHFISH_GROUPER_GIANT FISH_BLUEFISHFISH_SUNFISH_OCEANFISH_SWORDFISH FISH_MARLIN FISH_HALIBUTFISH_BARRACUDA_GREATFISH_TUNA_BLUEFINNARWHAL NARWHAL MANNARWHAL, GIANTHIPPO HIPPO_MAN GIANT_HIPPOFISH_GAR_LONGNOSE FISH_CARPFISH_TIGERFISH FISH_PIKEPLATYPUS PLATYPUS MANPLATYPUS, GIANT BEAR_GRIZZLYBEAR_GRIZZLY_MANGIANT_BEAR_GRIZZLY +BEAR_BLACKBEAR_BLACK_MANGIANT_BEAR_BLACKDEERDEER_MAN +GIANT_DEERFOXFOX_MAN GIANT_FOXRACCOON RACCOON_MAN GIANT_RACCOONMACAQUE_RHESUSMACAQUE_RHESUS_MANGIANT_MACAQUE_RHESUSCOUGAR +COUGAR_MAN GIANT_COUGARWOLFWOLF_MAN +GIANT_WOLF GROUNDHOG GROUNDHOG_MANGIANT_GROUNDHOG ALLIGATOR ALLIGATOR_MANGIANT_ALLIGATOR BIRD_BUZZARD BUZZARD_MAN GIANT_BUZZARDPANDAPANDA, GIGANTIC PANDA MANCAPYBARACAPYBARA, GIANT CAPYBARA MANBADGER +BADGER MAN BADGER, GIANTMOOSE MOOSE MAN MOOSE, GIANT RED PANDA RED PANDA MANRED PANDA, GIANTELEPHANT ELEPHANT_MANGIANT_ELEPHANTWARTHOG WARTHOG_MAN GIANT_WARTHOGLIONLION_MAN +GIANT_LIONLEOPARD LEOPARD_MAN GIANT_LEOPARDJAGUAR +JAGUAR_MAN GIANT_JAGUARTIGER TIGER_MAN GIANT_TIGERCHEETAH CHEETAH_MAN GIANT_CHEETAHGAZELLE GAZELLE_MAN GIANT_GAZELLEMANDRILL MANDRILL_MANGIANT_MANDRILL +CHIMPANZEEBONOBOGORILLA ORANGUTANGIBBON_SIAMANGGIBBON_WHITE_HANDEDGIBBON_BLACK_HANDED GIBBON_GRAYGIBBON_SILVERYGIBBON_PILEATED GIBBON_BILOUGIBBON_WHITE_BROWEDGIBBON_BLACK_CRESTED CAMEL_1_HUMPCAMEL_1_HUMP_MANGIANT_CAMEL_1_HUMP CAMEL_2_HUMPCAMEL_2_HUMP_MANGIANT_CAMEL_2_HUMPCROCODILE_SALTWATERCROCODILE_SALTWATER_MANGIANT_CROCODILE_SALTWATER BIRD_VULTURE VULTURE_MAN GIANT_VULTURE +RHINOCEROSRHINOCEROS_MANGIANT_RHINOCEROSGIRAFFE GIRAFFE_MAN GIANT_GIRAFFE HONEY BADGERHONEY BADGER MANHONEY BADGER, GIANTGIANT TORTOISEGIANT TORTOISE MANGIGANTIC TORTOISE ARMADILLO ARMADILLO MANARMADILLO, GIANTMUSKOX +MUSKOX_MAN GIANT_MUSKOXELKELK_MAN GIANT_ELK +BEAR_POLARBEAR_POLAR_MANGIANT_BEAR_POLAR WOLVERINE WOLVERINE_MANGIANT_WOLVERINE +CHINCHILLACHINCHILLA_MANGIANT_CHINCHILLA FLOATING_GUTSDRUNIAN CREEPING_EYEVORACIOUS_CAVE_CRAWLERBLIND_CAVE_OGRE +CAP_HOPPER +MAGMA_CRABCRUNDLE HUNGRY_HEAD +FLESH_BALLELK_BIRD HELMET_SNAKEGREEN_DEVOURERRUTHERERCREEPY_CRAWLERDRALTHAGIANT_EARTHWORM BLOOD_MANBUGBATMANERA +MOLEMARIANJABBERER POND_GRABBERBLIND_CAVE_BEAR CAVE_DRAGONREACHERELEMENTMAN_GABBROGORLAK CAVE_FLOATERPLUMP_HELMET_MAN CAVE_BLOBELEMENTMAN_AMETHYSTOCTOPUS OCTOPUS_MAN GIANT_OCTOPUSCRABCRAB_MAN +GIANT_CRAB LEOPARD_SEALLEOPARD_SEAL_MANGIANT_LEOPARD_SEAL +CUTTLEFISHCUTTLEFISH_MANGIANT_CUTTLEFISHORCAORCA_MAN +GIANT_ORCASPONGE +SPONGE_MAN GIANT_SPONGEHORSESHOE_CRABHORSESHOE_CRAB_MANGIANT_HORSESHOE_CRAB SPERM_WHALESPERM_WHALE_MANGIANT_SPERM_WHALE ELEPHANT_SEALELEPHANT_SEAL_MANGIANT_ELEPHANT_SEAL HARP_SEAL HARP_SEAL_MANGIANT_HARP_SEALNAUTILUS NAUTILUS_MANGIANT_NAUTILUS FOXSQUIRREL MOGHOPPER RAT_DEMONWAMBLER_FLUFFYLIZARD_RHINO_TWO_LEGGED WORM_KNUCKLESPIDER_PHANTOM FLY_ACORN +GNAT_BLOODLIZARD +LIZARD_MAN GIANT_LIZARDSKINK SKINK_MAN GIANT_SKINK CHAMELEON CHAMELEON_MANGIANT_CHAMELEONANOLE ANOLE_MAN GIANT_ANOLEIGUANA +IGUANA_MAN GIANT_IGUANA RIVER OTTER SEA OTTER OTTER_MAN GIANT_OTTERSNAPPING TURTLEALLIGATOR SNAPPING TURTLESNAPPING_TURTLE_MANGIANT_SNAPPING_TURTLEBEAVER +BEAVER_MAN GIANT_BEAVERLEECH LEECH_MAN GIANT_LEECHAXOLOTL AXOLOTL_MAN GIANT_AXOLOTLMINKMINK_MAN +GIANT_MINK POND_TURTLEPOND_TURTLE_MANGIANT_POND_TURTLERATRAT_MAN SQUIRREL_GRAYSQUIRREL_GRAY_MANGIANT_SQUIRREL_GRAY SQUIRREL_REDSQUIRREL_RED_MANGIANT_SQUIRREL_REDCHIPMUNK CHIPMUNK_MANGIANT_CHIPMUNKHAMSTER HAMSTER_MAN GIANT_HAMSTERHEDGEHOG HEDGEHOG_MANGIANT_HEDGEHOGSQUIRREL_FLYINGFLYING_SQUIRREL_MANGIANT_FLYING_SQUIRRELMUSSELOYSTER FISH_SALMONFISH_CLOWNFISH FISH_HAGFISHFISH_LAMPREY_BROOK FISH_RAY_BATFISH_RAY_THORNBACKFISH_RATFISH_SPOTTED FISH_HERRING FISH_SHAD FISH_ANCHOVYFISH_TROUT_STEELHEAD FISH_HAKE FISH_SEAHORSE FISH_GLASSEYEFISH_PUFFER_WHITE_SPOTTED FISH_SOLE FISH_FLOUNDER FISH_MACKERELJELLYFISH_SEA_NETTLESQUID SQUID MANGIGANTIC SQUID FISH_LUNGFISHFISH_LOACH_CLOWNFISH_BULLHEAD_BROWNFISH_BULLHEAD_YELLOWFISH_BULLHEAD_BLACKFISH_KNIFEFISH_BANDED FISH_CHARFISH_TROUT_RAINBOWFISH_MOLLY_SAILFIN +FISH_GUPPY +FISH_PERCHDWARFHUMANELFGOBLINKOBOLDGREMLINTROLLOGREUNICORNDRAGONSATYRCOLOSSUS_BRONZEGIANTCYCLOPSETTINMINOTAURYETI SASQUATCH BLIZZARD_MANWOLF_ICEFAIRYPIXIEBEAK_DOG GRIMELING BLENDEC_FOUL STRANGLER NIGHTWINGHARPYHYDRA MERPERSON SEA_SERPENT SEA_MONSTERBIRD_ROCCROCODILE_CAVETOAD_GIANT_CAVE OLM_GIANT BAT_GIANT RAT_GIANT RAT_LARGEMOLE_DOG_NAKED +TROGLODYTE +MOLE_GIANTIMP_FIRESPIDER_CAVE_GIANT SPIDER_CAVE FISH_CAVE CAVE_FISH_MAN LOBSTER_CAVE +SNAKE_FIREOLMOLM_MANBATBAT_MANMAGGOT_PURRINGELEMENTMAN_FIREELEMENTMAN_MAGMAELEMENTMAN_IRONELEMENTMAN_MUDBIRD_SWALLOW_CAVECAVE_SWALLOW_MANBIRD_SWALLOW_CAVE_GIANT AMPHIBIAN_MAN REPTILE_MAN SERPENT_MANANT_MAN +RODENT MAN WILD_BOAR WILD_BOAR_MANGIANT_WILD_BOARCOYOTE +COYOTE_MAN GIANT_COYOTEKANGAROO KANGAROO_MANGIANT_KANGAROOKOALA KOALA_MAN GIANT_KOALAADDER ADDER_MAN GIANT_ADDERECHIDNA ECHIDNA_MAN GIANT_ECHIDNA PORCUPINE PORCUPINE_MANGIANT_PORCUPINE KINGSNAKE KINGSNAKE_MANGIANT_KINGSNAKE GRAY_LANGURGRAY_LANGUR_MANGIANT_GRAY_LANGURBOBCAT +BOBCAT_MAN GIANT_BOBCATSKUNK SKUNK_MAN GIANT_SKUNKGREEN_TREE_FROGGREEN_TREE_FROG_MANGIANT_GREEN_TREE_FROGHAREHARE_MAN +GIANT_HARE RATTLESNAKERATTLESNAKE_MANGIANT_RATTLESNAKEWEASEL +WEASEL_MAN GIANT_WEASELCOPPERHEAD_SNAKECOPPERHEAD_SNAKE_MANGIANT_COPPERHEAD_SNAKEIBEXIBEX_MAN +GIANT_IBEXWOMBAT +WOMBAT_MAN GIANT_WOMBATDINGO DINGO_MAN GIANT_DINGOCOATI COATI_MAN GIANT_COATIOPOSSUM OPOSSUM_MAN GIANT_OPOSSUMMONGOOSE MONGOOSE_MANGIANT_MONGOOSEHYENA HYENA_MAN GIANT_HYENAANACONDA ANACONDA_MANGIANT_ANACONDAMONITOR_LIZARDMONITOR_LIZARD_MANGIANT_MONITOR_LIZARD +KING_COBRAKING_COBRA_MANGIANT_KING_COBRAOCELOT +OCELOT_MAN GIANT_OCELOTJACKAL +JACKAL_MAN GIANT_JACKALCAPUCHIN CAPUCHIN_MANGIANT_CAPUCHINSLOTH SLOTH_MAN GIANT_SLOTH SPIDER_MONKEYSPIDER_MONKEY_MANGIANT_SPIDER_MONKEYPANGOLIN PANGOLIN_MANGIANT_PANGOLIN BLACK_MAMBABLACK_MAMBA_MANGIANT_BLACK_MAMBA +BEAR_SLOTHSLOTH_BEAR_MANGIANT_SLOTH_BEARAYE-AYE AYE-AYE_MAN GIANT_AYE-AYE +BUSHMASTERBUSHMASTER_MANGIANT_BUSHMASTERPYTHON +PYTHON_MAN GIANT_PYTHONTAPIR TAPIR_MAN GIANT_TAPIRIMPALA +IMPALA_MAN GIANT_IMPALAAARDVARK AARDVARK_MANGIANT_AARDVARK LION_TAMARINLION_TAMARIN_MANGIANT_LION_TAMARINSTOAT STOAT_MAN GIANT_STOATLYNXLYNX_MAN +GIANT_LYNXGNOLLNAGAFORGOTTEN_BEAST_1FORGOTTEN_BEAST_2FORGOTTEN_BEAST_3FORGOTTEN_BEAST_4FORGOTTEN_BEAST_5FORGOTTEN_BEAST_6FORGOTTEN_BEAST_7FORGOTTEN_BEAST_8FORGOTTEN_BEAST_9FORGOTTEN_BEAST_10FORGOTTEN_BEAST_11FORGOTTEN_BEAST_12FORGOTTEN_BEAST_13FORGOTTEN_BEAST_14FORGOTTEN_BEAST_15FORGOTTEN_BEAST_16FORGOTTEN_BEAST_17FORGOTTEN_BEAST_18FORGOTTEN_BEAST_19FORGOTTEN_BEAST_20FORGOTTEN_BEAST_21FORGOTTEN_BEAST_22FORGOTTEN_BEAST_23FORGOTTEN_BEAST_24FORGOTTEN_BEAST_25FORGOTTEN_BEAST_26FORGOTTEN_BEAST_27FORGOTTEN_BEAST_28FORGOTTEN_BEAST_29FORGOTTEN_BEAST_30FORGOTTEN_BEAST_31FORGOTTEN_BEAST_32FORGOTTEN_BEAST_33FORGOTTEN_BEAST_34FORGOTTEN_BEAST_35FORGOTTEN_BEAST_36FORGOTTEN_BEAST_37FORGOTTEN_BEAST_38FORGOTTEN_BEAST_39FORGOTTEN_BEAST_40FORGOTTEN_BEAST_41FORGOTTEN_BEAST_42FORGOTTEN_BEAST_43FORGOTTEN_BEAST_44FORGOTTEN_BEAST_45FORGOTTEN_BEAST_46FORGOTTEN_BEAST_47FORGOTTEN_BEAST_48FORGOTTEN_BEAST_49FORGOTTEN_BEAST_50FORGOTTEN_BEAST_51FORGOTTEN_BEAST_52FORGOTTEN_BEAST_53FORGOTTEN_BEAST_54FORGOTTEN_BEAST_55FORGOTTEN_BEAST_56FORGOTTEN_BEAST_57FORGOTTEN_BEAST_58FORGOTTEN_BEAST_59FORGOTTEN_BEAST_60FORGOTTEN_BEAST_61FORGOTTEN_BEAST_62FORGOTTEN_BEAST_63FORGOTTEN_BEAST_64FORGOTTEN_BEAST_65FORGOTTEN_BEAST_66FORGOTTEN_BEAST_67FORGOTTEN_BEAST_68FORGOTTEN_BEAST_69FORGOTTEN_BEAST_70FORGOTTEN_BEAST_71FORGOTTEN_BEAST_72FORGOTTEN_BEAST_73FORGOTTEN_BEAST_74FORGOTTEN_BEAST_75FORGOTTEN_BEAST_76FORGOTTEN_BEAST_77FORGOTTEN_BEAST_78FORGOTTEN_BEAST_79FORGOTTEN_BEAST_80FORGOTTEN_BEAST_81FORGOTTEN_BEAST_82FORGOTTEN_BEAST_83FORGOTTEN_BEAST_84FORGOTTEN_BEAST_85FORGOTTEN_BEAST_86FORGOTTEN_BEAST_87FORGOTTEN_BEAST_88FORGOTTEN_BEAST_89FORGOTTEN_BEAST_90FORGOTTEN_BEAST_91FORGOTTEN_BEAST_92FORGOTTEN_BEAST_93FORGOTTEN_BEAST_94FORGOTTEN_BEAST_95FORGOTTEN_BEAST_96FORGOTTEN_BEAST_97FORGOTTEN_BEAST_98FORGOTTEN_BEAST_99FORGOTTEN_BEAST_100FORGOTTEN_BEAST_101FORGOTTEN_BEAST_102FORGOTTEN_BEAST_103FORGOTTEN_BEAST_104FORGOTTEN_BEAST_105FORGOTTEN_BEAST_106FORGOTTEN_BEAST_107FORGOTTEN_BEAST_108FORGOTTEN_BEAST_109FORGOTTEN_BEAST_110FORGOTTEN_BEAST_111FORGOTTEN_BEAST_112FORGOTTEN_BEAST_113FORGOTTEN_BEAST_114FORGOTTEN_BEAST_115FORGOTTEN_BEAST_116FORGOTTEN_BEAST_117FORGOTTEN_BEAST_118FORGOTTEN_BEAST_119FORGOTTEN_BEAST_120FORGOTTEN_BEAST_121FORGOTTEN_BEAST_122FORGOTTEN_BEAST_123FORGOTTEN_BEAST_124FORGOTTEN_BEAST_125FORGOTTEN_BEAST_126FORGOTTEN_BEAST_127FORGOTTEN_BEAST_128FORGOTTEN_BEAST_129FORGOTTEN_BEAST_130FORGOTTEN_BEAST_131FORGOTTEN_BEAST_132FORGOTTEN_BEAST_133FORGOTTEN_BEAST_134FORGOTTEN_BEAST_135FORGOTTEN_BEAST_136FORGOTTEN_BEAST_137FORGOTTEN_BEAST_138FORGOTTEN_BEAST_139FORGOTTEN_BEAST_140FORGOTTEN_BEAST_141FORGOTTEN_BEAST_142FORGOTTEN_BEAST_143FORGOTTEN_BEAST_144FORGOTTEN_BEAST_145FORGOTTEN_BEAST_146FORGOTTEN_BEAST_147FORGOTTEN_BEAST_148FORGOTTEN_BEAST_149FORGOTTEN_BEAST_150FORGOTTEN_BEAST_151FORGOTTEN_BEAST_152FORGOTTEN_BEAST_153FORGOTTEN_BEAST_154FORGOTTEN_BEAST_155FORGOTTEN_BEAST_156FORGOTTEN_BEAST_157FORGOTTEN_BEAST_158FORGOTTEN_BEAST_159FORGOTTEN_BEAST_160FORGOTTEN_BEAST_161FORGOTTEN_BEAST_162FORGOTTEN_BEAST_163FORGOTTEN_BEAST_164FORGOTTEN_BEAST_165FORGOTTEN_BEAST_166FORGOTTEN_BEAST_167FORGOTTEN_BEAST_168FORGOTTEN_BEAST_169FORGOTTEN_BEAST_170FORGOTTEN_BEAST_171FORGOTTEN_BEAST_172FORGOTTEN_BEAST_173FORGOTTEN_BEAST_174FORGOTTEN_BEAST_175FORGOTTEN_BEAST_176FORGOTTEN_BEAST_177FORGOTTEN_BEAST_178FORGOTTEN_BEAST_179FORGOTTEN_BEAST_180FORGOTTEN_BEAST_181FORGOTTEN_BEAST_182FORGOTTEN_BEAST_183FORGOTTEN_BEAST_184FORGOTTEN_BEAST_185FORGOTTEN_BEAST_186FORGOTTEN_BEAST_187FORGOTTEN_BEAST_188FORGOTTEN_BEAST_189FORGOTTEN_BEAST_190FORGOTTEN_BEAST_191FORGOTTEN_BEAST_192FORGOTTEN_BEAST_193FORGOTTEN_BEAST_194FORGOTTEN_BEAST_195FORGOTTEN_BEAST_196FORGOTTEN_BEAST_197FORGOTTEN_BEAST_198FORGOTTEN_BEAST_199FORGOTTEN_BEAST_200FORGOTTEN_BEAST_201FORGOTTEN_BEAST_202FORGOTTEN_BEAST_203FORGOTTEN_BEAST_204FORGOTTEN_BEAST_205FORGOTTEN_BEAST_206FORGOTTEN_BEAST_207FORGOTTEN_BEAST_208FORGOTTEN_BEAST_209FORGOTTEN_BEAST_210FORGOTTEN_BEAST_211FORGOTTEN_BEAST_212FORGOTTEN_BEAST_213FORGOTTEN_BEAST_214FORGOTTEN_BEAST_215FORGOTTEN_BEAST_216FORGOTTEN_BEAST_217FORGOTTEN_BEAST_218FORGOTTEN_BEAST_219FORGOTTEN_BEAST_220FORGOTTEN_BEAST_221FORGOTTEN_BEAST_222FORGOTTEN_BEAST_223FORGOTTEN_BEAST_224FORGOTTEN_BEAST_225FORGOTTEN_BEAST_226FORGOTTEN_BEAST_227FORGOTTEN_BEAST_228FORGOTTEN_BEAST_229FORGOTTEN_BEAST_230FORGOTTEN_BEAST_231FORGOTTEN_BEAST_232FORGOTTEN_BEAST_233FORGOTTEN_BEAST_234FORGOTTEN_BEAST_235FORGOTTEN_BEAST_236FORGOTTEN_BEAST_237FORGOTTEN_BEAST_238FORGOTTEN_BEAST_239FORGOTTEN_BEAST_240FORGOTTEN_BEAST_241FORGOTTEN_BEAST_242FORGOTTEN_BEAST_243FORGOTTEN_BEAST_244FORGOTTEN_BEAST_245FORGOTTEN_BEAST_246FORGOTTEN_BEAST_247FORGOTTEN_BEAST_248FORGOTTEN_BEAST_249FORGOTTEN_BEAST_250FORGOTTEN_BEAST_251FORGOTTEN_BEAST_252FORGOTTEN_BEAST_253FORGOTTEN_BEAST_254FORGOTTEN_BEAST_255FORGOTTEN_BEAST_256FORGOTTEN_BEAST_257FORGOTTEN_BEAST_258FORGOTTEN_BEAST_259FORGOTTEN_BEAST_260FORGOTTEN_BEAST_261FORGOTTEN_BEAST_262FORGOTTEN_BEAST_263FORGOTTEN_BEAST_264FORGOTTEN_BEAST_265FORGOTTEN_BEAST_266FORGOTTEN_BEAST_267FORGOTTEN_BEAST_268FORGOTTEN_BEAST_269FORGOTTEN_BEAST_270FORGOTTEN_BEAST_271FORGOTTEN_BEAST_272FORGOTTEN_BEAST_273FORGOTTEN_BEAST_274FORGOTTEN_BEAST_275FORGOTTEN_BEAST_276FORGOTTEN_BEAST_277FORGOTTEN_BEAST_278FORGOTTEN_BEAST_279FORGOTTEN_BEAST_280FORGOTTEN_BEAST_281FORGOTTEN_BEAST_282FORGOTTEN_BEAST_283FORGOTTEN_BEAST_284FORGOTTEN_BEAST_285FORGOTTEN_BEAST_286FORGOTTEN_BEAST_287FORGOTTEN_BEAST_288FORGOTTEN_BEAST_289FORGOTTEN_BEAST_290FORGOTTEN_BEAST_291FORGOTTEN_BEAST_292FORGOTTEN_BEAST_293FORGOTTEN_BEAST_294FORGOTTEN_BEAST_295FORGOTTEN_BEAST_296FORGOTTEN_BEAST_297FORGOTTEN_BEAST_298FORGOTTEN_BEAST_299FORGOTTEN_BEAST_300FORGOTTEN_BEAST_301FORGOTTEN_BEAST_302FORGOTTEN_BEAST_303FORGOTTEN_BEAST_304FORGOTTEN_BEAST_305FORGOTTEN_BEAST_306FORGOTTEN_BEAST_307FORGOTTEN_BEAST_308FORGOTTEN_BEAST_309FORGOTTEN_BEAST_310FORGOTTEN_BEAST_311FORGOTTEN_BEAST_312FORGOTTEN_BEAST_313FORGOTTEN_BEAST_314FORGOTTEN_BEAST_315FORGOTTEN_BEAST_316FORGOTTEN_BEAST_317FORGOTTEN_BEAST_318FORGOTTEN_BEAST_319FORGOTTEN_BEAST_320FORGOTTEN_BEAST_321FORGOTTEN_BEAST_322FORGOTTEN_BEAST_323FORGOTTEN_BEAST_324FORGOTTEN_BEAST_325FORGOTTEN_BEAST_326FORGOTTEN_BEAST_327FORGOTTEN_BEAST_328FORGOTTEN_BEAST_329FORGOTTEN_BEAST_330FORGOTTEN_BEAST_331FORGOTTEN_BEAST_332FORGOTTEN_BEAST_333FORGOTTEN_BEAST_334FORGOTTEN_BEAST_335FORGOTTEN_BEAST_336FORGOTTEN_BEAST_337FORGOTTEN_BEAST_338FORGOTTEN_BEAST_339FORGOTTEN_BEAST_340FORGOTTEN_BEAST_341FORGOTTEN_BEAST_342FORGOTTEN_BEAST_343FORGOTTEN_BEAST_344FORGOTTEN_BEAST_345FORGOTTEN_BEAST_346FORGOTTEN_BEAST_347FORGOTTEN_BEAST_348FORGOTTEN_BEAST_349FORGOTTEN_BEAST_350FORGOTTEN_BEAST_351FORGOTTEN_BEAST_352FORGOTTEN_BEAST_353FORGOTTEN_BEAST_354FORGOTTEN_BEAST_355FORGOTTEN_BEAST_356FORGOTTEN_BEAST_357FORGOTTEN_BEAST_358FORGOTTEN_BEAST_359FORGOTTEN_BEAST_360FORGOTTEN_BEAST_361FORGOTTEN_BEAST_362FORGOTTEN_BEAST_363FORGOTTEN_BEAST_364FORGOTTEN_BEAST_365FORGOTTEN_BEAST_366FORGOTTEN_BEAST_367FORGOTTEN_BEAST_368FORGOTTEN_BEAST_369FORGOTTEN_BEAST_370FORGOTTEN_BEAST_371FORGOTTEN_BEAST_372FORGOTTEN_BEAST_373FORGOTTEN_BEAST_374FORGOTTEN_BEAST_375FORGOTTEN_BEAST_376FORGOTTEN_BEAST_377FORGOTTEN_BEAST_378FORGOTTEN_BEAST_379FORGOTTEN_BEAST_380FORGOTTEN_BEAST_381FORGOTTEN_BEAST_382FORGOTTEN_BEAST_383FORGOTTEN_BEAST_384FORGOTTEN_BEAST_385FORGOTTEN_BEAST_386FORGOTTEN_BEAST_387FORGOTTEN_BEAST_388FORGOTTEN_BEAST_389FORGOTTEN_BEAST_390FORGOTTEN_BEAST_391FORGOTTEN_BEAST_392FORGOTTEN_BEAST_393FORGOTTEN_BEAST_394FORGOTTEN_BEAST_395FORGOTTEN_BEAST_396FORGOTTEN_BEAST_397FORGOTTEN_BEAST_398FORGOTTEN_BEAST_399FORGOTTEN_BEAST_400FORGOTTEN_BEAST_401FORGOTTEN_BEAST_402FORGOTTEN_BEAST_403FORGOTTEN_BEAST_404FORGOTTEN_BEAST_405FORGOTTEN_BEAST_406FORGOTTEN_BEAST_407FORGOTTEN_BEAST_408FORGOTTEN_BEAST_409FORGOTTEN_BEAST_410FORGOTTEN_BEAST_411FORGOTTEN_BEAST_412FORGOTTEN_BEAST_413FORGOTTEN_BEAST_414FORGOTTEN_BEAST_415FORGOTTEN_BEAST_416FORGOTTEN_BEAST_417FORGOTTEN_BEAST_418FORGOTTEN_BEAST_419FORGOTTEN_BEAST_420FORGOTTEN_BEAST_421FORGOTTEN_BEAST_422FORGOTTEN_BEAST_423FORGOTTEN_BEAST_424FORGOTTEN_BEAST_425FORGOTTEN_BEAST_426FORGOTTEN_BEAST_427FORGOTTEN_BEAST_428FORGOTTEN_BEAST_429FORGOTTEN_BEAST_430FORGOTTEN_BEAST_431FORGOTTEN_BEAST_432FORGOTTEN_BEAST_433FORGOTTEN_BEAST_434FORGOTTEN_BEAST_435FORGOTTEN_BEAST_436FORGOTTEN_BEAST_437FORGOTTEN_BEAST_438FORGOTTEN_BEAST_439FORGOTTEN_BEAST_440FORGOTTEN_BEAST_441FORGOTTEN_BEAST_442FORGOTTEN_BEAST_443FORGOTTEN_BEAST_444FORGOTTEN_BEAST_445FORGOTTEN_BEAST_446FORGOTTEN_BEAST_447FORGOTTEN_BEAST_448FORGOTTEN_BEAST_449FORGOTTEN_BEAST_450FORGOTTEN_BEAST_451FORGOTTEN_BEAST_452FORGOTTEN_BEAST_453FORGOTTEN_BEAST_454FORGOTTEN_BEAST_455FORGOTTEN_BEAST_456FORGOTTEN_BEAST_457FORGOTTEN_BEAST_458FORGOTTEN_BEAST_459FORGOTTEN_BEAST_460FORGOTTEN_BEAST_461FORGOTTEN_BEAST_462FORGOTTEN_BEAST_463FORGOTTEN_BEAST_464FORGOTTEN_BEAST_465FORGOTTEN_BEAST_466FORGOTTEN_BEAST_467FORGOTTEN_BEAST_468FORGOTTEN_BEAST_469FORGOTTEN_BEAST_470FORGOTTEN_BEAST_471FORGOTTEN_BEAST_472FORGOTTEN_BEAST_473FORGOTTEN_BEAST_474FORGOTTEN_BEAST_475FORGOTTEN_BEAST_476FORGOTTEN_BEAST_477FORGOTTEN_BEAST_478FORGOTTEN_BEAST_479FORGOTTEN_BEAST_480FORGOTTEN_BEAST_481FORGOTTEN_BEAST_482FORGOTTEN_BEAST_483FORGOTTEN_BEAST_484FORGOTTEN_BEAST_485FORGOTTEN_BEAST_486FORGOTTEN_BEAST_487FORGOTTEN_BEAST_488FORGOTTEN_BEAST_489FORGOTTEN_BEAST_490FORGOTTEN_BEAST_491FORGOTTEN_BEAST_492FORGOTTEN_BEAST_493FORGOTTEN_BEAST_494FORGOTTEN_BEAST_495FORGOTTEN_BEAST_496FORGOTTEN_BEAST_497FORGOTTEN_BEAST_498FORGOTTEN_BEAST_499FORGOTTEN_BEAST_500FORGOTTEN_BEAST_501FORGOTTEN_BEAST_502FORGOTTEN_BEAST_503FORGOTTEN_BEAST_504FORGOTTEN_BEAST_505FORGOTTEN_BEAST_506FORGOTTEN_BEAST_507FORGOTTEN_BEAST_508FORGOTTEN_BEAST_509FORGOTTEN_BEAST_510FORGOTTEN_BEAST_511FORGOTTEN_BEAST_512FORGOTTEN_BEAST_513FORGOTTEN_BEAST_514FORGOTTEN_BEAST_515FORGOTTEN_BEAST_516FORGOTTEN_BEAST_517FORGOTTEN_BEAST_518FORGOTTEN_BEAST_519FORGOTTEN_BEAST_520FORGOTTEN_BEAST_521FORGOTTEN_BEAST_522FORGOTTEN_BEAST_523FORGOTTEN_BEAST_524FORGOTTEN_BEAST_525FORGOTTEN_BEAST_526FORGOTTEN_BEAST_527FORGOTTEN_BEAST_528FORGOTTEN_BEAST_529FORGOTTEN_BEAST_530FORGOTTEN_BEAST_531FORGOTTEN_BEAST_532FORGOTTEN_BEAST_533FORGOTTEN_BEAST_534FORGOTTEN_BEAST_535FORGOTTEN_BEAST_536FORGOTTEN_BEAST_537FORGOTTEN_BEAST_538FORGOTTEN_BEAST_539FORGOTTEN_BEAST_540FORGOTTEN_BEAST_541FORGOTTEN_BEAST_542FORGOTTEN_BEAST_543FORGOTTEN_BEAST_544FORGOTTEN_BEAST_545FORGOTTEN_BEAST_546FORGOTTEN_BEAST_547FORGOTTEN_BEAST_548FORGOTTEN_BEAST_549FORGOTTEN_BEAST_550FORGOTTEN_BEAST_551FORGOTTEN_BEAST_552FORGOTTEN_BEAST_553FORGOTTEN_BEAST_554FORGOTTEN_BEAST_555FORGOTTEN_BEAST_556FORGOTTEN_BEAST_557FORGOTTEN_BEAST_558FORGOTTEN_BEAST_559FORGOTTEN_BEAST_560FORGOTTEN_BEAST_561FORGOTTEN_BEAST_562FORGOTTEN_BEAST_563FORGOTTEN_BEAST_564FORGOTTEN_BEAST_565FORGOTTEN_BEAST_566FORGOTTEN_BEAST_567FORGOTTEN_BEAST_568FORGOTTEN_BEAST_569FORGOTTEN_BEAST_570FORGOTTEN_BEAST_571FORGOTTEN_BEAST_572FORGOTTEN_BEAST_573FORGOTTEN_BEAST_574FORGOTTEN_BEAST_575FORGOTTEN_BEAST_576FORGOTTEN_BEAST_577FORGOTTEN_BEAST_578FORGOTTEN_BEAST_579FORGOTTEN_BEAST_580FORGOTTEN_BEAST_581FORGOTTEN_BEAST_582FORGOTTEN_BEAST_583FORGOTTEN_BEAST_584FORGOTTEN_BEAST_585FORGOTTEN_BEAST_586FORGOTTEN_BEAST_587FORGOTTEN_BEAST_588FORGOTTEN_BEAST_589FORGOTTEN_BEAST_590FORGOTTEN_BEAST_591FORGOTTEN_BEAST_592FORGOTTEN_BEAST_593FORGOTTEN_BEAST_594FORGOTTEN_BEAST_595FORGOTTEN_BEAST_596FORGOTTEN_BEAST_597FORGOTTEN_BEAST_598FORGOTTEN_BEAST_599FORGOTTEN_BEAST_600FORGOTTEN_BEAST_601FORGOTTEN_BEAST_602FORGOTTEN_BEAST_603FORGOTTEN_BEAST_604FORGOTTEN_BEAST_605FORGOTTEN_BEAST_606FORGOTTEN_BEAST_607FORGOTTEN_BEAST_608FORGOTTEN_BEAST_609FORGOTTEN_BEAST_610FORGOTTEN_BEAST_611FORGOTTEN_BEAST_612FORGOTTEN_BEAST_613FORGOTTEN_BEAST_614FORGOTTEN_BEAST_615FORGOTTEN_BEAST_616FORGOTTEN_BEAST_617FORGOTTEN_BEAST_618FORGOTTEN_BEAST_619FORGOTTEN_BEAST_620FORGOTTEN_BEAST_621FORGOTTEN_BEAST_622FORGOTTEN_BEAST_623FORGOTTEN_BEAST_624FORGOTTEN_BEAST_625FORGOTTEN_BEAST_626FORGOTTEN_BEAST_627FORGOTTEN_BEAST_628FORGOTTEN_BEAST_629FORGOTTEN_BEAST_630FORGOTTEN_BEAST_631FORGOTTEN_BEAST_632FORGOTTEN_BEAST_633FORGOTTEN_BEAST_634FORGOTTEN_BEAST_635FORGOTTEN_BEAST_636FORGOTTEN_BEAST_637FORGOTTEN_BEAST_638FORGOTTEN_BEAST_639FORGOTTEN_BEAST_640FORGOTTEN_BEAST_641FORGOTTEN_BEAST_642FORGOTTEN_BEAST_643FORGOTTEN_BEAST_644FORGOTTEN_BEAST_645FORGOTTEN_BEAST_646FORGOTTEN_BEAST_647FORGOTTEN_BEAST_648FORGOTTEN_BEAST_649FORGOTTEN_BEAST_650FORGOTTEN_BEAST_651FORGOTTEN_BEAST_652FORGOTTEN_BEAST_653FORGOTTEN_BEAST_654FORGOTTEN_BEAST_655FORGOTTEN_BEAST_656FORGOTTEN_BEAST_657FORGOTTEN_BEAST_658FORGOTTEN_BEAST_659FORGOTTEN_BEAST_660FORGOTTEN_BEAST_661FORGOTTEN_BEAST_662FORGOTTEN_BEAST_663FORGOTTEN_BEAST_664FORGOTTEN_BEAST_665FORGOTTEN_BEAST_666FORGOTTEN_BEAST_667FORGOTTEN_BEAST_668FORGOTTEN_BEAST_669FORGOTTEN_BEAST_670FORGOTTEN_BEAST_671FORGOTTEN_BEAST_672FORGOTTEN_BEAST_673FORGOTTEN_BEAST_674FORGOTTEN_BEAST_675FORGOTTEN_BEAST_676FORGOTTEN_BEAST_677FORGOTTEN_BEAST_678FORGOTTEN_BEAST_679FORGOTTEN_BEAST_680FORGOTTEN_BEAST_681FORGOTTEN_BEAST_682FORGOTTEN_BEAST_683FORGOTTEN_BEAST_684FORGOTTEN_BEAST_685FORGOTTEN_BEAST_686FORGOTTEN_BEAST_687FORGOTTEN_BEAST_688FORGOTTEN_BEAST_689FORGOTTEN_BEAST_690FORGOTTEN_BEAST_691FORGOTTEN_BEAST_692FORGOTTEN_BEAST_693FORGOTTEN_BEAST_694FORGOTTEN_BEAST_695FORGOTTEN_BEAST_696FORGOTTEN_BEAST_697FORGOTTEN_BEAST_698FORGOTTEN_BEAST_699FORGOTTEN_BEAST_700FORGOTTEN_BEAST_701FORGOTTEN_BEAST_702FORGOTTEN_BEAST_703FORGOTTEN_BEAST_704FORGOTTEN_BEAST_705FORGOTTEN_BEAST_706FORGOTTEN_BEAST_707FORGOTTEN_BEAST_708FORGOTTEN_BEAST_709FORGOTTEN_BEAST_710FORGOTTEN_BEAST_711FORGOTTEN_BEAST_712FORGOTTEN_BEAST_713FORGOTTEN_BEAST_714FORGOTTEN_BEAST_715FORGOTTEN_BEAST_716FORGOTTEN_BEAST_717FORGOTTEN_BEAST_718FORGOTTEN_BEAST_719FORGOTTEN_BEAST_720FORGOTTEN_BEAST_721FORGOTTEN_BEAST_722FORGOTTEN_BEAST_723FORGOTTEN_BEAST_724FORGOTTEN_BEAST_725FORGOTTEN_BEAST_726FORGOTTEN_BEAST_727FORGOTTEN_BEAST_728FORGOTTEN_BEAST_729FORGOTTEN_BEAST_730FORGOTTEN_BEAST_731FORGOTTEN_BEAST_732FORGOTTEN_BEAST_733FORGOTTEN_BEAST_734FORGOTTEN_BEAST_735FORGOTTEN_BEAST_736FORGOTTEN_BEAST_737FORGOTTEN_BEAST_738FORGOTTEN_BEAST_739FORGOTTEN_BEAST_740FORGOTTEN_BEAST_741FORGOTTEN_BEAST_742FORGOTTEN_BEAST_743FORGOTTEN_BEAST_744FORGOTTEN_BEAST_745FORGOTTEN_BEAST_746FORGOTTEN_BEAST_747FORGOTTEN_BEAST_748FORGOTTEN_BEAST_749FORGOTTEN_BEAST_750FORGOTTEN_BEAST_751FORGOTTEN_BEAST_752FORGOTTEN_BEAST_753FORGOTTEN_BEAST_754FORGOTTEN_BEAST_755FORGOTTEN_BEAST_756FORGOTTEN_BEAST_757FORGOTTEN_BEAST_758FORGOTTEN_BEAST_759FORGOTTEN_BEAST_760FORGOTTEN_BEAST_761FORGOTTEN_BEAST_762FORGOTTEN_BEAST_763FORGOTTEN_BEAST_764FORGOTTEN_BEAST_765FORGOTTEN_BEAST_766FORGOTTEN_BEAST_767FORGOTTEN_BEAST_768FORGOTTEN_BEAST_769FORGOTTEN_BEAST_770FORGOTTEN_BEAST_771FORGOTTEN_BEAST_772FORGOTTEN_BEAST_773FORGOTTEN_BEAST_774FORGOTTEN_BEAST_775FORGOTTEN_BEAST_776FORGOTTEN_BEAST_777FORGOTTEN_BEAST_778FORGOTTEN_BEAST_779FORGOTTEN_BEAST_780FORGOTTEN_BEAST_781FORGOTTEN_BEAST_782FORGOTTEN_BEAST_783FORGOTTEN_BEAST_784FORGOTTEN_BEAST_785FORGOTTEN_BEAST_786FORGOTTEN_BEAST_787FORGOTTEN_BEAST_788FORGOTTEN_BEAST_789FORGOTTEN_BEAST_790FORGOTTEN_BEAST_791FORGOTTEN_BEAST_792FORGOTTEN_BEAST_793FORGOTTEN_BEAST_794FORGOTTEN_BEAST_795FORGOTTEN_BEAST_796FORGOTTEN_BEAST_797FORGOTTEN_BEAST_798FORGOTTEN_BEAST_799FORGOTTEN_BEAST_800FORGOTTEN_BEAST_801FORGOTTEN_BEAST_802FORGOTTEN_BEAST_803FORGOTTEN_BEAST_804FORGOTTEN_BEAST_805FORGOTTEN_BEAST_806FORGOTTEN_BEAST_807FORGOTTEN_BEAST_808FORGOTTEN_BEAST_809FORGOTTEN_BEAST_810FORGOTTEN_BEAST_811FORGOTTEN_BEAST_812FORGOTTEN_BEAST_813FORGOTTEN_BEAST_814FORGOTTEN_BEAST_815FORGOTTEN_BEAST_816FORGOTTEN_BEAST_817FORGOTTEN_BEAST_818FORGOTTEN_BEAST_819FORGOTTEN_BEAST_820FORGOTTEN_BEAST_821FORGOTTEN_BEAST_822FORGOTTEN_BEAST_823FORGOTTEN_BEAST_824FORGOTTEN_BEAST_825FORGOTTEN_BEAST_826FORGOTTEN_BEAST_827FORGOTTEN_BEAST_828FORGOTTEN_BEAST_829FORGOTTEN_BEAST_830FORGOTTEN_BEAST_831FORGOTTEN_BEAST_832FORGOTTEN_BEAST_833FORGOTTEN_BEAST_834FORGOTTEN_BEAST_835FORGOTTEN_BEAST_836FORGOTTEN_BEAST_837FORGOTTEN_BEAST_838FORGOTTEN_BEAST_839FORGOTTEN_BEAST_840FORGOTTEN_BEAST_841FORGOTTEN_BEAST_842FORGOTTEN_BEAST_843FORGOTTEN_BEAST_844FORGOTTEN_BEAST_845FORGOTTEN_BEAST_846FORGOTTEN_BEAST_847FORGOTTEN_BEAST_848FORGOTTEN_BEAST_849FORGOTTEN_BEAST_850FORGOTTEN_BEAST_851FORGOTTEN_BEAST_852FORGOTTEN_BEAST_853FORGOTTEN_BEAST_854FORGOTTEN_BEAST_855FORGOTTEN_BEAST_856FORGOTTEN_BEAST_857FORGOTTEN_BEAST_858FORGOTTEN_BEAST_859FORGOTTEN_BEAST_860FORGOTTEN_BEAST_861FORGOTTEN_BEAST_862FORGOTTEN_BEAST_863FORGOTTEN_BEAST_864FORGOTTEN_BEAST_865FORGOTTEN_BEAST_866FORGOTTEN_BEAST_867TITAN_1TITAN_2TITAN_3TITAN_4TITAN_5TITAN_6TITAN_7TITAN_8TITAN_9TITAN_10TITAN_11TITAN_12TITAN_13TITAN_14TITAN_15TITAN_16TITAN_17TITAN_18TITAN_19TITAN_20TITAN_21TITAN_22TITAN_23TITAN_24TITAN_25TITAN_26TITAN_27TITAN_28TITAN_29TITAN_30TITAN_31TITAN_32TITAN_33DEMON_1DEMON_2DEMON_3DEMON_4DEMON_5DEMON_6DEMON_7DEMON_8DEMON_9DEMON_10DEMON_11DEMON_12DEMON_13DEMON_14DEMON_15DEMON_16DEMON_17DEMON_18DEMON_19DEMON_20DEMON_21DEMON_22DEMON_23DEMON_24DEMON_25DEMON_26DEMON_27DEMON_28DEMON_29DEMON_30DEMON_31DEMON_32DEMON_33DEMON_34DEMON_35DEMON_36DEMON_37DEMON_38DEMON_39DEMON_40DEMON_41DEMON_42DEMON_43DEMON_44DEMON_45DEMON_46DEMON_47DEMON_48DEMON_49DEMON_50DEMON_51DEMON_52NIGHT_CREATURE_1NIGHT_CREATURE_2NIGHT_CREATURE_3NIGHT_CREATURE_4NIGHT_CREATURE_5NIGHT_CREATURE_6NIGHT_CREATURE_7NIGHT_CREATURE_8NIGHT_CREATURE_9NIGHT_CREATURE_10NIGHT_CREATURE_11NIGHT_CREATURE_12NIGHT_CREATURE_13NIGHT_CREATURE_14NIGHT_CREATURE_15NIGHT_CREATURE_16NIGHT_CREATURE_17NIGHT_CREATURE_18NIGHT_CREATURE_19NIGHT_CREATURE_20NIGHT_CREATURE_21NIGHT_CREATURE_22NIGHT_CREATURE_23NIGHT_CREATURE_24NIGHT_CREATURE_25NIGHT_CREATURE_26NIGHT_CREATURE_27NIGHT_CREATURE_28NIGHT_CREATURE_29NIGHT_CREATURE_30NIGHT_CREATURE_31NIGHT_CREATURE_32NIGHT_CREATURE_33NIGHT_CREATURE_34NIGHT_CREATURE_35NIGHT_CREATURE_36NIGHT_CREATURE_37NIGHT_CREATURE_38NIGHT_CREATURE_39NIGHT_CREATURE_40NIGHT_CREATURE_41NIGHT_CREATURE_42NIGHT_CREATURE_43NIGHT_CREATURE_44NIGHT_CREATURE_45NIGHT_CREATURE_46NIGHT_CREATURE_47NIGHT_CREATURE_48NIGHT_CREATURE_49NIGHT_CREATURE_50NIGHT_CREATURE_51NIGHT_CREATURE_52NIGHT_CREATURE_53NIGHT_CREATURE_54NIGHT_CREATURE_55NIGHT_CREATURE_56NIGHT_CREATURE_57NIGHT_CREATURE_58NIGHT_CREATURE_59NIGHT_CREATURE_60NIGHT_CREATURE_61NIGHT_CREATURE_62NIGHT_CREATURE_63NIGHT_CREATURE_64NIGHT_CREATURE_65NIGHT_CREATURE_66NIGHT_CREATURE_67NIGHT_CREATURE_68NIGHT_CREATURE_69NIGHT_CREATURE_70NIGHT_CREATURE_71NIGHT_CREATURE_72NIGHT_CREATURE_73NIGHT_CREATURE_74NIGHT_CREATURE_75NIGHT_CREATURE_76NIGHT_CREATURE_77NIGHT_CREATURE_78NIGHT_CREATURE_79NIGHT_CREATURE_80NIGHT_CREATURE_81NIGHT_CREATURE_82NIGHT_CREATURE_83NIGHT_CREATURE_84NIGHT_CREATURE_85NIGHT_CREATURE_86NIGHT_CREATURE_87NIGHT_CREATURE_88NIGHT_CREATURE_89NIGHT_CREATURE_90NIGHT_CREATURE_91NIGHT_CREATURE_92NIGHT_CREATURE_93NIGHT_CREATURE_94NIGHT_CREATURE_95NIGHT_CREATURE_96NIGHT_CREATURE_97NIGHT_CREATURE_98NIGHT_CREATURE_99NIGHT_CREATURE_100NIGHT_CREATURE_101NIGHT_CREATURE_102NIGHT_CREATURE_103NIGHT_CREATURE_104HF1248 DIVINE_1HF1248 DIVINE_2HF1248 DIVINE_3HF1108 DIVINE_1HF1108 DIVINE_2HF1108 DIVINE_3HF1249 DIVINE_1HF1249 DIVINE_2HF1249 DIVINE_3HF1345 DIVINE_1HF1345 DIVINE_2HF1345 DIVINE_3˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/armorprefix.dfstock b/data/stockpiles/armorprefix.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..8d69cec678552608e310a1d01ab3fd05581ddda6 GIT binary patch literal 2040 zcmZ{kL2ueH6vv6gv<}pDqbmj4Q;*!*s;Zisojei~Cq}k`wU-RFR6;^+#i+D?g`IZX zamO8Z+;QJzUuBN~O^Uq>*uVet|Jm=^`Re@LsT>j+3+XZqaheTNDuF27<3vN{z))kt zc&b7sb){j59Ec7?kk0th`X;>kJv3M#v`|im4YM< zI8n;2dML`(WZRRMN)x`CmM@7+=v$Tu84!G>E|cQLJP_Qc?{XdO2Ox{Y6TRL`f<6)O zqU#m5pty!&mMfq;xi6MSsUlDs(4&?|^jMH)yDKvM-m#<~7vq3~rzNuOd@Tmiy?ok( zp$3_DSORq4z*@2n2l~MfIJqny3ajcD6BFK#v&|GNm!(L}s!_)AqR?iZ1P?4Z-r;p{ zJ(nmaUWA|ctZ2JAmqJ0Bb?AK#DN}J@hqPg!wL{@##3wOcsYM|QG`mwq1AI2Xxb(Pq z<`p}&urmugx3CLZIMF)Cu??Qu;F%4c+u%iaOU!O2cca-;w>lh8r`~LIJ0IV4w>y)& z@$_ju-BOPa+!>Ua(_3PcHhLK&-3x@_Ho+XBD4AAc62?L&yKR_C|^3t lXGd9dl)0mPcKqmka=tmvcgOkRlzuvmUry<_Q~KkS{sDA}NDlx2 literal 0 HcmV?d00001 diff --git a/data/stockpiles/artifactammo.dfstock b/data/stockpiles/artifactammo.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..874ec21064edbf5f45e96dcb2a57a5a51462099c GIT binary patch literal 33 pcmZ?bQsQtdD#=VsOfH$g$T)+MaRDR43Py@q*wF6SD-&~`Vl3N*hIQkfd0C|`t8GCtReDt;a6R?r$v zOiHEoDe*olq}XL6WEtNne3uRKtKeH0ur>zPq4&5A3N7kx_qv!7I3e36)*)f(jAs|& zt{3r#aMbwf&~ytSkw5jYGh#u=?|TJ;w>T6bRB>aVw%I)rbr^M1IoGp@|P{ zNXYsqehj68pj>r_q1^>;l*MlMLpK2y!Zxj2@=*^R9in7VK?Bl{=OnLPB2n)HDzWyu xZX9Pmut|bV6Ks}Xa~>r+_86z&Bn77_I7`9#2V2tWQMANdieyR3&hTo#EGc)x6=NIR~lD_!S*+`+e_#yqgaFV=E|IULYclomRQkuSIC|tf2 znop;XSHd4dBt3neKU!PmKPh8{UALe!aBD^SkFOf)bgZ^ zz&j;dd6N0hv)3v`P0eAD1Cpg zjLhF};ON!8mie2_fu0I@!3|1F!eMn?3M$54A&IbtWC7fYTO`#djzloS0&o)^G|>415TK_#A};Sus@*E?Zd5$U^&qwaM;*Ysig!I%Z(euQNjWmzsW%2v=I7DmYbBE4) zAvGz3G4OCsGxtd%$c?uJ1DR^jkCHq=;s^p&+HMjAf`x|k!iL~-Zq#|p10|4-XhW-3 zj*?IlVNI=V%V~)cSx{L<6=PsqG}6ZC0guRU)Wz65owl=TUM=BugX&OvLwIaY<3Q&2?mYT5^t7fm9d%$ zWC*(_xJ0V87lJo7UmalR@ANw9OBK^Hv|YebYgG>cg7g#$cOv&f*J6%@GKNa>NKs)+ zCT(&UC>mw-)W~Y>U}L?RwkcMlKDxk`U>WdxNZ=U#C?o23T~+a79$N#(jbpp&UdgIY zuPAh#Y3_!!Las}%+EE46_n_J`t^H*{(+XGXR`HTFGXgDH@3?DCL@Y#A4p+fCQ;_SG z>_F>jbB4Ht@G@DJAp>KT`)pGc>R2#V_x@i13Iv^45=&kMs z2Z88LMbU8Ua9^X}C3T;Y=`D}G?*D_!j4G-S>7Z(X<3Ivx-m-Vk6`+xaKLh!Y)miSO#pJU^2|#h6fE#NU|sBxnkl$t z>yT-VV~YR-IA6nMV6~^m9@Zr!lIJ1U0m7T#iRjVk4Yz|`?_f7O*zFE>hp9N(xY`4+ h_rRMy@OBTp`?Cihn>>H`1p^N|k literal 0 HcmV?d00001 diff --git a/data/stockpiles/bauxite.dfstock b/data/stockpiles/bauxite.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..25524dcb9032e269b96a0f3e161a091056a1c451 GIT binary patch literal 42 ycmXpK<`VSu^AB=&^z(GKa&iof@CWA7%O1IC9Q;EyRa}zHqi(5Xwc?h8@zO_EYTD&zM zZ492j>)@*5#d@BsyVuBw30=2E;ODH4y*yfP_)du^k6L`U`Yj_;WgumRkK5Ivv-L1# z$C#6Bec!U3IyE^0d%oUpINVvU#m@&gM|Q77mH)gY-EwFos3 zh=h({5?@XJ*%Hj=86;V>{bEQ0OO2qZ6vLpM2X`@m=p-()rL(QhmedmyeC$6Dk|x2+{D>QbGhAEjE|nc2=cK1rQ$~PSeh+5;G!X z70P5QE1T~8)}!B#qal@(Fm;=oU2}2!ERtz#%A4%HTFf}r(@HGS6w0B|78AYo4*J7H z4(Ya*=&ert&|G`myEWdE+G;<26!h7uhCDM*?-qp6&BV!8nkXY#I<);t6_RwsJ)PRH_=L5m(jMf3+nIL zw&l1Ew*@V$&F)7fsyHZug%o0ksr8w$lx;a(rRb0#`ZdO((MuP1KSGo_p3QmC=q{c; zi!G8ihSnkMc$t`ueYV@>(@O1iFLcwyd36vG-6aOsPYIIHJDW{|(ZO7E-lg=P$IwR| z{r@)67?&Z+XTFXC@5h&b!jDo};!xpdf$3BvnI?@L2;VsfcrLuLg+-#5+-X;Putk`)YNDWK)57;+5jl-0>?dXVaF7t?x0>IaFnN%I+VW8)d zh{QfbAE><4sB?!Om}+8oc*9vP*xM`EyDQlHE7%97;wi?PYv9{!;Ja(!`)lBbFQ?P* Wr_&#&n?Fyl{yN?KeY*MQbn_n;ywVl` literal 0 HcmV?d00001 diff --git a/data/stockpiles/bolts.dfstock b/data/stockpiles/bolts.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..8f525cf2b4a31813c73d98135cdcfdfe17cee2f2 GIT binary patch literal 45 ycmZ<~;}UW7_4T*%3~}|12eIOv{Cz@#ConS3U}Rjt$gqM@Vgn^FQzJ$?*7|mdMwLGzx#B(5<1H~LjbKu-TEMC1E1HCy$Rd=1O`fF?UU7y55ya5J- z422jBp2AXOgC~d~pr=wI8-y7e*-+3^C6JBO*?1qQ+!H7+s{tzsrZ9XEb7>14#9ZD2 z6z&Qbp!gbxC^RHDKq2gl;?RnQ2eH@{`n)eJfd?^$t#DWX#<9B`HUkqdbxdJjl#`4= zUNr1Frm?%JQ=Y&N86<9n3$3bIh77~_G5bRyyXY~1g5rp%i zZ~<`F2|NR23{xis>|Kc?l6a~~EX`8G0!R(pTWTCMR&R=-FBHRJLow_+!ncvIG*bfg zg|IXezOaPnG-357iL)zVrHSAqAv^;JUp2ywNm#uVZU*IFE9dn^F_;&HE3NSOR0LZ} zVL4P-8I*sL1cJtI5{b=HHaX;p#xEyqVJ!{J2KbY*M zG+(lAd;BZ3_Ryz8XD86s?ivp*hmHC1_e5QJ@L~X4vn$5E4!gCLx$(T$18>us{l4!^ zQT9!-@r#$vh_&m!ts8GBTaG2zZ>?>ztts07z_K(uGl0)WrYRefJM>|H*Vbh@-M^_k Y_$`XQN72t{@hf`yJ6imS7Js9~J!7g}d;kCd literal 0 HcmV?d00001 diff --git a/data/stockpiles/clothprefix.dfstock b/data/stockpiles/clothprefix.dfstock new file mode 100644 index 000000000..62d84c402 --- /dev/null +++ b/data/stockpiles/clothprefix.dfstock @@ -0,0 +1,141 @@ +rêL +"CREATURE:SPIDER_BROWN_RECLUSE:SILK +&CREATURE:BROWN_RECLUSE_SPIDER_MAN:SILK +(CREATURE:GIANT_BROWN_RECLUSE_SPIDER:SILK +CREATURE:SPIDER_PHANTOM:SILK +CREATURE:SPIDER_CAVE_GIANT:SILK +CREATURE:SPIDER_CAVE:SILK +INORGANIC:DIVINE_2 +INORGANIC:DIVINE_4 +INORGANIC:DIVINE_6 +INORGANIC:DIVINE_8 +INORGANIC:DIVINE_10 +INORGANIC:DIVINE_12 +INORGANIC:DIVINE_14 +INORGANIC:DIVINE_16 +INORGANIC:DIVINE_18 +INORGANIC:DIVINE_20 + CREATURE:FORGOTTEN_BEAST_10:SILK + CREATURE:FORGOTTEN_BEAST_12:SILK + CREATURE:FORGOTTEN_BEAST_17:SILK + CREATURE:FORGOTTEN_BEAST_20:SILK + CREATURE:FORGOTTEN_BEAST_29:SILK + CREATURE:FORGOTTEN_BEAST_31:SILK + CREATURE:FORGOTTEN_BEAST_42:SILK + CREATURE:FORGOTTEN_BEAST_55:SILK + CREATURE:FORGOTTEN_BEAST_65:SILK + CREATURE:FORGOTTEN_BEAST_68:SILK + CREATURE:FORGOTTEN_BEAST_71:SILK + CREATURE:FORGOTTEN_BEAST_74:SILK + CREATURE:FORGOTTEN_BEAST_91:SILK +!CREATURE:FORGOTTEN_BEAST_101:SILK +!CREATURE:FORGOTTEN_BEAST_109:SILK +!CREATURE:FORGOTTEN_BEAST_119:SILK +!CREATURE:FORGOTTEN_BEAST_121:SILK +!CREATURE:FORGOTTEN_BEAST_125:SILK +!CREATURE:FORGOTTEN_BEAST_128:SILK +!CREATURE:FORGOTTEN_BEAST_132:SILK +!CREATURE:FORGOTTEN_BEAST_142:SILK +!CREATURE:FORGOTTEN_BEAST_146:SILK +!CREATURE:FORGOTTEN_BEAST_168:SILK +!CREATURE:FORGOTTEN_BEAST_173:SILK +!CREATURE:FORGOTTEN_BEAST_186:SILK +!CREATURE:FORGOTTEN_BEAST_204:SILK +!CREATURE:FORGOTTEN_BEAST_213:SILK +!CREATURE:FORGOTTEN_BEAST_224:SILK +!CREATURE:FORGOTTEN_BEAST_235:SILK +!CREATURE:FORGOTTEN_BEAST_236:SILK +!CREATURE:FORGOTTEN_BEAST_239:SILK +!CREATURE:FORGOTTEN_BEAST_248:SILK +!CREATURE:FORGOTTEN_BEAST_275:SILK +!CREATURE:FORGOTTEN_BEAST_277:SILK +!CREATURE:FORGOTTEN_BEAST_281:SILK +!CREATURE:FORGOTTEN_BEAST_289:SILK +!CREATURE:FORGOTTEN_BEAST_290:SILK +!CREATURE:FORGOTTEN_BEAST_293:SILK +!CREATURE:FORGOTTEN_BEAST_297:SILK +!CREATURE:FORGOTTEN_BEAST_309:SILK +!CREATURE:FORGOTTEN_BEAST_311:SILK +!CREATURE:FORGOTTEN_BEAST_313:SILK +!CREATURE:FORGOTTEN_BEAST_326:SILK +!CREATURE:FORGOTTEN_BEAST_330:SILK +!CREATURE:FORGOTTEN_BEAST_341:SILK +!CREATURE:FORGOTTEN_BEAST_343:SILK +!CREATURE:FORGOTTEN_BEAST_357:SILK +!CREATURE:FORGOTTEN_BEAST_362:SILK +!CREATURE:FORGOTTEN_BEAST_371:SILK +!CREATURE:FORGOTTEN_BEAST_376:SILK +!CREATURE:FORGOTTEN_BEAST_379:SILK +!CREATURE:FORGOTTEN_BEAST_384:SILK +!CREATURE:FORGOTTEN_BEAST_397:SILK +!CREATURE:FORGOTTEN_BEAST_398:SILK +!CREATURE:FORGOTTEN_BEAST_403:SILK +!CREATURE:FORGOTTEN_BEAST_412:SILK +!CREATURE:FORGOTTEN_BEAST_426:SILK +!CREATURE:FORGOTTEN_BEAST_427:SILK +!CREATURE:FORGOTTEN_BEAST_432:SILK +!CREATURE:FORGOTTEN_BEAST_435:SILK +!CREATURE:FORGOTTEN_BEAST_438:SILK +!CREATURE:FORGOTTEN_BEAST_442:SILK +!CREATURE:FORGOTTEN_BEAST_456:SILK +!CREATURE:FORGOTTEN_BEAST_460:SILK +!CREATURE:FORGOTTEN_BEAST_465:SILK +!CREATURE:FORGOTTEN_BEAST_467:SILK +!CREATURE:FORGOTTEN_BEAST_472:SILK +!CREATURE:FORGOTTEN_BEAST_476:SILK +!CREATURE:FORGOTTEN_BEAST_486:SILK +!CREATURE:FORGOTTEN_BEAST_508:SILK +!CREATURE:FORGOTTEN_BEAST_549:SILK +!CREATURE:FORGOTTEN_BEAST_567:SILK +!CREATURE:FORGOTTEN_BEAST_568:SILK +!CREATURE:FORGOTTEN_BEAST_575:SILK +!CREATURE:FORGOTTEN_BEAST_580:SILK +!CREATURE:FORGOTTEN_BEAST_583:SILK +!CREATURE:FORGOTTEN_BEAST_588:SILK +!CREATURE:FORGOTTEN_BEAST_589:SILK +!CREATURE:FORGOTTEN_BEAST_600:SILK +!CREATURE:FORGOTTEN_BEAST_605:SILK +!CREATURE:FORGOTTEN_BEAST_606:SILK +!CREATURE:FORGOTTEN_BEAST_612:SILK +!CREATURE:FORGOTTEN_BEAST_613:SILK +!CREATURE:FORGOTTEN_BEAST_614:SILK +!CREATURE:FORGOTTEN_BEAST_630:SILK +!CREATURE:FORGOTTEN_BEAST_632:SILK +!CREATURE:FORGOTTEN_BEAST_639:SILK +!CREATURE:FORGOTTEN_BEAST_643:SILK +!CREATURE:FORGOTTEN_BEAST_650:SILK +!CREATURE:FORGOTTEN_BEAST_653:SILK +!CREATURE:FORGOTTEN_BEAST_661:SILK +!CREATURE:FORGOTTEN_BEAST_662:SILK +!CREATURE:FORGOTTEN_BEAST_676:SILK +!CREATURE:FORGOTTEN_BEAST_680:SILK +!CREATURE:FORGOTTEN_BEAST_697:SILK +!CREATURE:FORGOTTEN_BEAST_706:SILK +!CREATURE:FORGOTTEN_BEAST_720:SILK +!CREATURE:FORGOTTEN_BEAST_723:SILK +!CREATURE:FORGOTTEN_BEAST_730:SILK +!CREATURE:FORGOTTEN_BEAST_757:SILK +!CREATURE:FORGOTTEN_BEAST_764:SILK +!CREATURE:FORGOTTEN_BEAST_765:SILK +!CREATURE:FORGOTTEN_BEAST_768:SILK +!CREATURE:FORGOTTEN_BEAST_774:SILK +!CREATURE:FORGOTTEN_BEAST_791:SILK +!CREATURE:FORGOTTEN_BEAST_809:SILK +!CREATURE:FORGOTTEN_BEAST_814:SILK +!CREATURE:FORGOTTEN_BEAST_821:SILK +!CREATURE:FORGOTTEN_BEAST_827:SILK +!CREATURE:FORGOTTEN_BEAST_829:SILK +!CREATURE:FORGOTTEN_BEAST_843:SILK +!CREATURE:FORGOTTEN_BEAST_855:SILK +!CREATURE:FORGOTTEN_BEAST_863:SILK +CREATURE:TITAN_4:SILK +CREATURE:TITAN_9:SILK +CREATURE:TITAN_14:SILK +CREATURE:TITAN_15:SILK +CREATURE:TITAN_16:SILK +CREATURE:TITAN_21:SILK +CREATURE:TITAN_22:SILK +CREATURE:TITAN_23:SILK +CREATURE:DEMON_39:SILK +CREATURE:DEMON_42:SILK +CREATURE:DEMON_51:SILKPLANT:FLAX:THREADPLANT:JUTE:THREADPLANT:HEMP:THREADPLANT:COTTON:THREADPLANT:RAMIE:THREADPLANT:KENAF:THREADPLANT:GRASS_TAIL_PIG:THREADPLANT:REED_ROPE:THREADCREATURE:SHEEP:HAIRCREATURE:LLAMA:HAIRCREATURE:ALPACA:HAIRCREATURE:TROLL:HAIRCREATURE:GNOLL:HAIR"INORGANIC:ADAMANTINE*"CREATURE:SPIDER_BROWN_RECLUSE:SILK*&CREATURE:BROWN_RECLUSE_SPIDER_MAN:SILK*(CREATURE:GIANT_BROWN_RECLUSE_SPIDER:SILK*CREATURE:SPIDER_PHANTOM:SILK*CREATURE:SPIDER_CAVE_GIANT:SILK*CREATURE:SPIDER_CAVE:SILK*INORGANIC:DIVINE_2*INORGANIC:DIVINE_4*INORGANIC:DIVINE_6*INORGANIC:DIVINE_8*INORGANIC:DIVINE_10*INORGANIC:DIVINE_12*INORGANIC:DIVINE_14*INORGANIC:DIVINE_16*INORGANIC:DIVINE_18*INORGANIC:DIVINE_20* CREATURE:FORGOTTEN_BEAST_10:SILK* CREATURE:FORGOTTEN_BEAST_12:SILK* CREATURE:FORGOTTEN_BEAST_17:SILK* CREATURE:FORGOTTEN_BEAST_20:SILK* CREATURE:FORGOTTEN_BEAST_29:SILK* CREATURE:FORGOTTEN_BEAST_31:SILK* CREATURE:FORGOTTEN_BEAST_42:SILK* CREATURE:FORGOTTEN_BEAST_55:SILK* CREATURE:FORGOTTEN_BEAST_65:SILK* CREATURE:FORGOTTEN_BEAST_68:SILK* CREATURE:FORGOTTEN_BEAST_71:SILK* CREATURE:FORGOTTEN_BEAST_74:SILK* CREATURE:FORGOTTEN_BEAST_91:SILK*!CREATURE:FORGOTTEN_BEAST_101:SILK*!CREATURE:FORGOTTEN_BEAST_109:SILK*!CREATURE:FORGOTTEN_BEAST_119:SILK*!CREATURE:FORGOTTEN_BEAST_121:SILK*!CREATURE:FORGOTTEN_BEAST_125:SILK*!CREATURE:FORGOTTEN_BEAST_128:SILK*!CREATURE:FORGOTTEN_BEAST_132:SILK*!CREATURE:FORGOTTEN_BEAST_142:SILK*!CREATURE:FORGOTTEN_BEAST_146:SILK*!CREATURE:FORGOTTEN_BEAST_168:SILK*!CREATURE:FORGOTTEN_BEAST_173:SILK*!CREATURE:FORGOTTEN_BEAST_186:SILK*!CREATURE:FORGOTTEN_BEAST_204:SILK*!CREATURE:FORGOTTEN_BEAST_213:SILK*!CREATURE:FORGOTTEN_BEAST_224:SILK*!CREATURE:FORGOTTEN_BEAST_235:SILK*!CREATURE:FORGOTTEN_BEAST_236:SILK*!CREATURE:FORGOTTEN_BEAST_239:SILK*!CREATURE:FORGOTTEN_BEAST_248:SILK*!CREATURE:FORGOTTEN_BEAST_275:SILK*!CREATURE:FORGOTTEN_BEAST_277:SILK*!CREATURE:FORGOTTEN_BEAST_281:SILK*!CREATURE:FORGOTTEN_BEAST_289:SILK*!CREATURE:FORGOTTEN_BEAST_290:SILK*!CREATURE:FORGOTTEN_BEAST_293:SILK*!CREATURE:FORGOTTEN_BEAST_297:SILK*!CREATURE:FORGOTTEN_BEAST_309:SILK*!CREATURE:FORGOTTEN_BEAST_311:SILK*!CREATURE:FORGOTTEN_BEAST_313:SILK*!CREATURE:FORGOTTEN_BEAST_326:SILK*!CREATURE:FORGOTTEN_BEAST_330:SILK*!CREATURE:FORGOTTEN_BEAST_341:SILK*!CREATURE:FORGOTTEN_BEAST_343:SILK*!CREATURE:FORGOTTEN_BEAST_357:SILK*!CREATURE:FORGOTTEN_BEAST_362:SILK*!CREATURE:FORGOTTEN_BEAST_371:SILK*!CREATURE:FORGOTTEN_BEAST_376:SILK*!CREATURE:FORGOTTEN_BEAST_379:SILK*!CREATURE:FORGOTTEN_BEAST_384:SILK*!CREATURE:FORGOTTEN_BEAST_397:SILK*!CREATURE:FORGOTTEN_BEAST_398:SILK*!CREATURE:FORGOTTEN_BEAST_403:SILK*!CREATURE:FORGOTTEN_BEAST_412:SILK*!CREATURE:FORGOTTEN_BEAST_426:SILK*!CREATURE:FORGOTTEN_BEAST_427:SILK*!CREATURE:FORGOTTEN_BEAST_432:SILK*!CREATURE:FORGOTTEN_BEAST_435:SILK*!CREATURE:FORGOTTEN_BEAST_438:SILK*!CREATURE:FORGOTTEN_BEAST_442:SILK*!CREATURE:FORGOTTEN_BEAST_456:SILK*!CREATURE:FORGOTTEN_BEAST_460:SILK*!CREATURE:FORGOTTEN_BEAST_465:SILK*!CREATURE:FORGOTTEN_BEAST_467:SILK*!CREATURE:FORGOTTEN_BEAST_472:SILK*!CREATURE:FORGOTTEN_BEAST_476:SILK*!CREATURE:FORGOTTEN_BEAST_486:SILK*!CREATURE:FORGOTTEN_BEAST_508:SILK*!CREATURE:FORGOTTEN_BEAST_549:SILK*!CREATURE:FORGOTTEN_BEAST_567:SILK*!CREATURE:FORGOTTEN_BEAST_568:SILK*!CREATURE:FORGOTTEN_BEAST_575:SILK*!CREATURE:FORGOTTEN_BEAST_580:SILK*!CREATURE:FORGOTTEN_BEAST_583:SILK*!CREATURE:FORGOTTEN_BEAST_588:SILK*!CREATURE:FORGOTTEN_BEAST_589:SILK*!CREATURE:FORGOTTEN_BEAST_600:SILK*!CREATURE:FORGOTTEN_BEAST_605:SILK*!CREATURE:FORGOTTEN_BEAST_606:SILK*!CREATURE:FORGOTTEN_BEAST_612:SILK*!CREATURE:FORGOTTEN_BEAST_613:SILK*!CREATURE:FORGOTTEN_BEAST_614:SILK*!CREATURE:FORGOTTEN_BEAST_630:SILK*!CREATURE:FORGOTTEN_BEAST_632:SILK*!CREATURE:FORGOTTEN_BEAST_639:SILK*!CREATURE:FORGOTTEN_BEAST_643:SILK*!CREATURE:FORGOTTEN_BEAST_650:SILK*!CREATURE:FORGOTTEN_BEAST_653:SILK*!CREATURE:FORGOTTEN_BEAST_661:SILK*!CREATURE:FORGOTTEN_BEAST_662:SILK*!CREATURE:FORGOTTEN_BEAST_676:SILK*!CREATURE:FORGOTTEN_BEAST_680:SILK*!CREATURE:FORGOTTEN_BEAST_697:SILK*!CREATURE:FORGOTTEN_BEAST_706:SILK*!CREATURE:FORGOTTEN_BEAST_720:SILK*!CREATURE:FORGOTTEN_BEAST_723:SILK*!CREATURE:FORGOTTEN_BEAST_730:SILK*!CREATURE:FORGOTTEN_BEAST_757:SILK*!CREATURE:FORGOTTEN_BEAST_764:SILK*!CREATURE:FORGOTTEN_BEAST_765:SILK*!CREATURE:FORGOTTEN_BEAST_768:SILK*!CREATURE:FORGOTTEN_BEAST_774:SILK*!CREATURE:FORGOTTEN_BEAST_791:SILK*!CREATURE:FORGOTTEN_BEAST_809:SILK*!CREATURE:FORGOTTEN_BEAST_814:SILK*!CREATURE:FORGOTTEN_BEAST_821:SILK*!CREATURE:FORGOTTEN_BEAST_827:SILK*!CREATURE:FORGOTTEN_BEAST_829:SILK*!CREATURE:FORGOTTEN_BEAST_843:SILK*!CREATURE:FORGOTTEN_BEAST_855:SILK*!CREATURE:FORGOTTEN_BEAST_863:SILK*CREATURE:TITAN_4:SILK*CREATURE:TITAN_9:SILK*CREATURE:TITAN_14:SILK*CREATURE:TITAN_15:SILK*CREATURE:TITAN_16:SILK*CREATURE:TITAN_21:SILK*CREATURE:TITAN_22:SILK*CREATURE:TITAN_23:SILK*CREATURE:DEMON_39:SILK*CREATURE:DEMON_42:SILK*CREATURE:DEMON_51:SILK2PLANT:FLAX:THREAD2PLANT:JUTE:THREAD2PLANT:HEMP:THREAD2PLANT:COTTON:THREAD2PLANT:RAMIE:THREAD2PLANT:KENAF:THREAD2PLANT:GRASS_TAIL_PIG:THREAD2PLANT:REED_ROPE:THREAD:CREATURE:SHEEP:HAIR:CREATURE:LLAMA:HAIR:CREATURE:ALPACA:HAIR:CREATURE:TROLL:HAIR:CREATURE:GNOLL:HAIRBINORGANIC:ADAMANTINE˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/coal.dfstock b/data/stockpiles/coal.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..0db45dd7f02fc03b156fcdeec39002e5355f8711 GIT binary patch literal 31 ncmWII;9?8#4{;3kn83(5gOPCoBf|SwY1Z=fx z%FE`zU6(Q~^3yma?T0IFjgw=j(Dzp`Q?Bm2YX32TE*2;ig_ZlC(q-3~Ms6*(#^zEB)PiCVt{O`%zy<>0PzrwS!7Ht5xu?|B zN}opaJO-S2Y?|SX8CM&tkZ)#kUKy*o%*e7lh*ESjUozdx!;2_p$7<0kcYxcyR?ONK zG@et(0|(QJ*-4o;rU<#>^K0W=dVWBI0ZPzWSmkmBPl?ufWd~=X3kjSXtp(H930rW^ zb732)m1sD`GOioeb`Hw@2XXi+3hZrlhfEH&7BR1xcVrW%!iXaiP>$6J@2z^%e zg3^I48twuUCD13Y4GcRkOVkCe%DWzRrWN|a)#fxDzV_huJ9qS}c#GkX*RQBtQcqE5 z2yj^>-mTY^AaVm#ashj(Rjh#WCH;;6Z4B{RE* z4mX|$q~WS5RB4z9*_t z7eSJ50&a3BAV(>Dt79d_l2EYHI1mzsMpNUDHWQ3I7~_x^FX#STI!dhVGlkIxl_B@@ zpsQ)mlm_}3sz4SXa{qJ(ul0$ADNgDDQOmA^5U|q%icw=KK=~-K6Ky~~E@~VUk}7y2 zj;-e;GcTYp@Qk6bV%uRM2uy+ox#dX0-buWY-8s6*B&e2WOp4UH!A2#lmY4)*Di>D3 z)K%C02on}4Ers$-Ek-@?u^04tU}=#xMqfOFXt+eV2Ub&u+oD>e?P4sjfRle&@HHV) zU*Mp5oVv?`S71)Z1&_R3GZkw?XI%UoafeveAsOB71=Ez;_*y`|=xj)(sl61@*?A>xHwT11aZxeX zgbpN0D^$G5zPn0eDKZR`e44F>C=9eK6CnWd35mn(1)peFA;P#$yG-U3ia2`>#cO{N zj>9W=7IRw-I$RKB-{qBpA`ntGQsGTa2=pOCQOJi1*Ky~MBMj*`Avx4AUd0sdSnQq+DlK)zJji_oVy_ zCUA-loPJ7B%T}>cXfT3LGPXt6HXUQGQ(5?wP6q{1FYgkyo*VSpLqtN{6|vFiTfFBK zR0I<-ZB!b^`j08Iw(rzjd}@JiSP~uAV3ZC@gr5L)9~`6#wyMi3v<~{3{mw-H=8sN! z^zFAGQl-pT@y4Gc!1Ln1_Eee9p}#sRVN6iS@`O?_e%GjtsJV*%>Wf>`rEj+561fqqP}ZZBfYO2f z292N6R=){1FplN8{MK2Hu|8t5XASn>jMEH21_K<&uq7JD0xq35;l~k*rIli?J$Nvd z`28;=mH6&CgWm!nu%j{f5swN8?=cjATcH&AaV#y5|Ir}%)wc*OGTv|+ChWL^-L7DF zE7<)C_OOCIu3%3q*k|sKi_f3dz?(JjxCY*?fp=@*{TleN20pHVPix>>M8{8j9brD# zsG{i$l!tB&!jBvH?FN3gf!}Z74;%R72L7~xf9CO-{J;6MwQ#ewaNJtB-CDTYTDad@ zc-UHa+*)|rTG)ylrKZ7+QgYmi{J0hQaVzrUR^-R6$d3;!eZeb>|0jTdbXi~F<2ro1 z4&SZA_v`S6Cy(p9pVr~e2_IuPX`e+r?X!rdeHQVw&mx}oS;W&mi+I{+5l{Op;%T2n zd?Vf!T?zZawWY2^d?Q{kZGHbnyx`gT{)d0O4|aA92yqSKVs#Gk5BK9@0m`~_vAa43ff*c5 gL5|L@K!p<+8D}suE?{I>!6>nTkzofT!vRJH02vz-1ONa4 literal 0 HcmV?d00001 diff --git a/data/stockpiles/cutgems.dfstock b/data/stockpiles/cutgems.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..42949b8dcc2b9070e7c83af389a31a7f3ec5e305 GIT binary patch literal 2907 zcmZuzJF??85PZCV*WdNpYge(D>>Zp9L5Rc(1o#G|^r%Eva_G>ZLx&Ds<%@anVX-A{ zs0KK{?rG@u-}~k(<2?7|jQY*H^{x4O|2=v(-XRjy;Mq3c?{_RIT~kl9Zob>m)-JmK zOr37t@1Uj0gxb<=v8H*q;}cWR7&8I2pXeb|YrXHn?A^oN(T8x%pU@NlinV`)F_XFbzetAVW2k#9WegjES; zR8a@e6$a_(FpgH_oamEl-kk`c<@eL>~i}B80U{I=( z4WG#huFi|?X;C{x1b3bX+%c!l`r0C$|K5pYW(|Z&G)RSDWj}|dM)E?*} z&iW{*p0#!u2b%5lNN)C~!z%L!rFxiG#uICxbrF~Go#zI&N><%;en#hI@vRaPxQ!@C z9g@Y9GAO8*tkl~*Eo{8n3pHV}#JCg5(jYtpirE z)54-l6bf1WCKVMrJO5|U5+AqdiJ=fxvL0o-~tpe|J|ZMR_?;yvfK5hcJs0<*4>(xT3rq9+H<2utj*Y zXCdH*%1J*}H?m^3ob5Jv{De^3~(c^j`ciu>cXo;! zJctos(Ik#B%D)1k4+dLgBjQ6+^B;u+3sH#oDQXgxmrx0CvJ?i4oeq-09MEL9*h#uu z%;erfT%Y(c^)>m9YhDlacp?j<(EQbZXm~2=Uv_q fMjwg|CGhvE`lG7;tX}@AKKxz1{8PRBTfO`bI|&d} literal 0 HcmV?d00001 diff --git a/data/stockpiles/cutglass.dfstock b/data/stockpiles/cutglass.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..7523381b419e564539214d990c8119cf6b4ab40a GIT binary patch literal 64 zcma#j6ykRGaSRTQcMo!P^@DMoeOw)bgm@t`&OwpEA&x#17#U|UGA>|bSivZX*Q%A7b&X0lj{a6FVIN9!Ij%Kub7Fiq0Wy9VhRa^C3Klt5 zQ|(!3$W`na{86Hs*BCQ*x?3q1u)2M*{{D_d){jHik?)t&xL?B}_oZ)y-E{ukE$+vGSW&L9wumEYBgE>E*aR9q?vc uPDD6>^_r7~s}o!&=)l1;qY#qc!w!Eo&9A2U-JJe3_kWwyzvlGRoc;q%bNt@` literal 0 HcmV?d00001 diff --git a/data/stockpiles/drinkanimal.dfstock b/data/stockpiles/drinkanimal.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..f3cfabebf17ec146a46a1e43b5808916d6d4c525 GIT binary patch literal 76 zcmWeKH4=9Ya&-&|4RW>e@b`0#jCXQ%weod!bU~7E3iWmJafM0DU}TuU$T)+MaRH;m R3Py$vj0`&%84fTq008$;6Da@y literal 0 HcmV?d00001 diff --git a/data/stockpiles/drinkplant.dfstock b/data/stockpiles/drinkplant.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..c03acc8bcbe6006b47659f0c9271ce1e85dbd204 GIT binary patch literal 1780 zcmZuyIgZpo6m1^>0mC*dLLwb$E-=}*U2d1UHfrgnN$N_36A}^<5^@j@!|(R{Zo4&d zVCnt+{iSaHJ-hoH47vP}wA&hWzm23_J&cO-Lm#!<-F=+o`QV=rzJ;+L^2!MKNU>kx zFQFfl5&t(6)KWC+h?sAHm2S68<@p51qk`Tmxbty477OqNv~(wGh-yiH7YI?m7VSL2 z5ZI2KJ_z<{au$^9nWajWBL7ENe3+!I@xH&7fwxd%FzQ-(D{i^!YPB&+=z!!f>a5n1 z_mj=QjZ$$fLLP(vmPubZMDc@n+OHYPY8ZfkaEe)Xqu&Wjpt4nSdhezr4%+m!qZqy9 zjaGD&677XOfPrFiI;?39K2w&AKz*cMCm|P1qytB{h88&+h}54;dQ4yV=(`zZG&15ozFMR##ldvxT463nP(?-k%bu*dhYTKliz&BE12; n=iT6!Wj-oswOVG{A5HVvG(Vf$U(M!sv-#6({x+Nc>Qm}N literal 0 HcmV?d00001 diff --git a/data/stockpiles/dye.dfstock b/data/stockpiles/dye.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..e8ef137d85db9a0f916a1bc5c210afb5a2acafaa GIT binary patch literal 127 zcmWg2h>{ENar6tZ@(m642=e#$jduT+{ literal 0 HcmV?d00001 diff --git a/data/stockpiles/economic.dfstock b/data/stockpiles/economic.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..b69a8034c0f694c53da4e9d61fbdc15695d09241 GIT binary patch literal 266 zcmYk0EfNAT3`QGU!=K_BNL*SnC9`R}({u%jDvdQ*NlP=wNmMum4p_z8!A>#I%2s2+yNe7o?u=8Z!jM)Ki^$N-2eap literal 0 HcmV?d00001 diff --git a/data/stockpiles/emptyanimaltraps.dfstock b/data/stockpiles/emptyanimaltraps.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..d9121dc8aa9a79a2205e2a71192d620506ede5bb GIT binary patch literal 27 jcmd;L;b0J8oWRI9gOPCoBf|j literal 0 HcmV?d00001 diff --git a/data/stockpiles/finishedgoodsprefix.dfstock b/data/stockpiles/finishedgoodsprefix.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..556f7d3059b572b2125e96cfb61905a7fce2dd67 GIT binary patch literal 5995 zcmZ`-O>gA38D3*;uRF%J>9kGLX$uT|Dd0nZAP9o=Mp3lH9ZJ-a)QsiJz)ie>0b>)m z4$%CI9&^kw#~yp^f9N0TLrSyrXyhIi-g!m7pU?ZeB>(o`PiH5x6uO?BTp8h4v(v(6 zMupi-*FLzmQgxWU47Qt{2v=D*JMpDees)|cQ(>{OYy3L9(nafZt!Ag9YUB1-UaBT2 zte4Ji>e(^A7PD7Mxb&a1%n7OD!k2}rW+%ah_@@zdfVND?RfB)C)3>c&L&>RcjD{np z(zQXBv*Tc`nH^`=uI9%ZYxDVeW3YECjSb}-A8Iv+()6RWuAZMXFm2}WUKulgWt0e| za`R)a&1!xksti9)q;V_Y$rR#hUX83vB321(~5!Z`f>e{+O z)LQ;kJ6rSb!kT>X(f&IuA|#eb+caYd^sJO&EjPNByeET|8@2e=UN180JqvtL&@tby zXmqhn-m%PxpzF5cb*PJsrC+?>@5Q3U@Af|%wLx*WS19@7_Y-k&`pT%qZzms&=r|~( zHfO`av{iD3RQ9RZPP3M2W02`T?2RB`2bdJQ#UJlKWd?BC4oUmXUhb?{+f<=n?}eH6 zRU691Clk?514SlsQ2}`|5u^fWjNUB%yjLNW6P1=*zzNW+H^QmKAE%#v0CxZ1$HqEz z4Z3wfF$iBqKmrXUghPXnplR#wnn8qq)<_|PQ=(f8R}P6&OHqh0QlFVtE#HV-Ehx!G zG}ms2Xo!NPZN$h@X3)qfe70X%@2B>YG%28%*TQKpH9RGx$`U#~$_J;Ym*VD>;*lNYT-V1+s5WA)pv3h#q!=1quX-V2 zEVS9K1|+@GZYL#!4SHxNrrH4dn;MFJWEui?qh)BJXZS)pd}UYN@@)&a-f)wG?)q@Z z>qxnB2m2}`~yV?0gSq{2TPIV(dCE48l zeiB#m7!|6-Bb_g9;B03XuN-XN3TA6LrI^dFlB8<9*hp7f+E{OW3uP_NQ z1mnRCn*X$K|FyO_oG!tZ9;_Z>s8sh9(NK3{u~l2&9O#j{u0@4L-DifZlxY5LIWS$H z2g>~$FyJsb7`%zd2Ut)p1}#&xN5WP0kY+MIY~T>-G#OBU8%9nN^c5hc8kFp#qr|#4 z(>Sf5GUR?bPA{iDQ&{k0ssd>M$^FGIBiBzf3~^Eih)RVL$8&aCfH5kZ3dS>FfDORM zMePp?lPW|YZkvcnMqZLaLA*1SY%zUE1T&E)y5&gH+JU?#t0lU~PNYVyarmRwO*%?k zZ4|CbBbG~FfYfJQk4KpFf$)~=h}3**IySqLUUf_@s={d&_Zm7}qTC&`smHV^57M?% zmQcXS-+hQRAyDtciSsyhyAP3pIUPHBkIUy&^|c`zJ3qI$**WH+7|=xMk+}X)K{&xqF@Wg-PYJ|S^9d%-7Km5X#- zr&Tud8buskaQd--5RS*ibrf?`b|wu7((h%d!3ZQNYNc^aCJFcf4;~y(iE$jFIr0nX zw<$T)FpYE6vLoXyA z1F$uT>542Z*euf_-m`7%x%x&IDL`61_>9XeCgAvuAPu%`+%VE+0Bv-bAR1I~qE!7X zrZmM@ncYR5s!?=}hb7__ythbxiFkv<7U^%=94C>YZw(^G26k~yR{1twPPmkSIfw~f zQm!+nt6l}wcctUAP{I@$99{?XVXHVL3=qMVtZUGe! z`1y0fFP;;A`JC{pi!=N9&Hek^#~&`vH+K&Y^6~cF)7>{0udnXk-#z@W`+4TS|L*Sb z{@cfkbM=p}@4kDw|Mvau!^JPE+rK~EJ-++?;o?j@KHa~&{rc(hgT_nNcsbU1`9b3) kYrNcRy!@c?pGQYO9v%I3^y25EPyTiE;@?Lv{&V!=f7onI82|tP literal 0 HcmV?d00001 diff --git a/data/stockpiles/flux.dfstock b/data/stockpiles/flux.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..0b074cca483267b074385f1543378ecde4da6ff7 GIT binary patch literal 118 zcmXqt=Mwhx^AB=&^z(GK^6~U_4G!`5bLA332)g+D`1^W>xN`9$#GE}GeZ08@5CXoA oK~6rdT!IJ@XGb4rpsopwj58P+7cercU}V_9$hd=%;Q%8803BT&_W%F@ literal 0 HcmV?d00001 diff --git a/data/stockpiles/foodprefix.dfstock b/data/stockpiles/foodprefix.dfstock new file mode 100644 index 000000000..9333d0c61 --- /dev/null +++ b/data/stockpiles/foodprefix.dfstock @@ -0,0 +1,18619 @@ +©À- +PLANT:WORMY TENDRILS:STRUCTURAL +PLANT:EYEBALL:STRUCTURAL +CREATURE:TOAD:MUSCLE +CREATURE:TOAD:EYE +CREATURE:TOAD:BRAIN +CREATURE:TOAD:LUNG +CREATURE:TOAD:HEART +CREATURE:TOAD:LIVER +CREATURE:TOAD:GUT +CREATURE:TOAD:STOMACH +CREATURE:TOAD:GIZZARD +CREATURE:TOAD:PANCREAS +CREATURE:TOAD:SPLEEN +CREATURE:TOAD:KIDNEY +CREATURE:TOAD_MAN:MUSCLE +CREATURE:TOAD_MAN:EYE +CREATURE:TOAD_MAN:BRAIN +CREATURE:TOAD_MAN:LUNG +CREATURE:TOAD_MAN:HEART +CREATURE:TOAD_MAN:LIVER +CREATURE:TOAD_MAN:GUT +CREATURE:TOAD_MAN:STOMACH +CREATURE:TOAD_MAN:GIZZARD +CREATURE:TOAD_MAN:PANCREAS +CREATURE:TOAD_MAN:SPLEEN +CREATURE:TOAD_MAN:KIDNEY +CREATURE:GIANT_TOAD:MUSCLE +CREATURE:GIANT_TOAD:EYE +CREATURE:GIANT_TOAD:BRAIN +CREATURE:GIANT_TOAD:LUNG +CREATURE:GIANT_TOAD:HEART +CREATURE:GIANT_TOAD:LIVER +CREATURE:GIANT_TOAD:GUT +CREATURE:GIANT_TOAD:STOMACH +CREATURE:GIANT_TOAD:GIZZARD +CREATURE:GIANT_TOAD:PANCREAS +CREATURE:GIANT_TOAD:SPLEEN +CREATURE:GIANT_TOAD:KIDNEY +CREATURE:WORM:MUSCLE +CREATURE:WORM:EYE +CREATURE:WORM:BRAIN +CREATURE:WORM:LUNG +CREATURE:WORM:HEART +CREATURE:WORM:LIVER +CREATURE:WORM:GUT +CREATURE:WORM:STOMACH +CREATURE:WORM:GIZZARD +CREATURE:WORM:PANCREAS +CREATURE:WORM:SPLEEN +CREATURE:WORM:KIDNEY +CREATURE:WORM_MAN:MUSCLE +CREATURE:WORM_MAN:EYE +CREATURE:WORM_MAN:BRAIN +CREATURE:WORM_MAN:LUNG +CREATURE:WORM_MAN:HEART +CREATURE:WORM_MAN:LIVER +CREATURE:WORM_MAN:GUT +CREATURE:WORM_MAN:STOMACH +CREATURE:WORM_MAN:GIZZARD +CREATURE:WORM_MAN:PANCREAS +CREATURE:WORM_MAN:SPLEEN +CREATURE:WORM_MAN:KIDNEY +CREATURE:BIRD_BLUEJAY:MUSCLE +CREATURE:BIRD_BLUEJAY:EYE +CREATURE:BIRD_BLUEJAY:BRAIN +CREATURE:BIRD_BLUEJAY:LUNG +CREATURE:BIRD_BLUEJAY:HEART +CREATURE:BIRD_BLUEJAY:LIVER +CREATURE:BIRD_BLUEJAY:GUT +CREATURE:BIRD_BLUEJAY:STOMACH +CREATURE:BIRD_BLUEJAY:GIZZARD +CREATURE:BIRD_BLUEJAY:PANCREAS +CREATURE:BIRD_BLUEJAY:SPLEEN +CREATURE:BIRD_BLUEJAY:KIDNEY +CREATURE:BLUEJAY_MAN:MUSCLE +CREATURE:BLUEJAY_MAN:EYE +CREATURE:BLUEJAY_MAN:BRAIN +CREATURE:BLUEJAY_MAN:LUNG +CREATURE:BLUEJAY_MAN:HEART +CREATURE:BLUEJAY_MAN:LIVER +CREATURE:BLUEJAY_MAN:GUT +CREATURE:BLUEJAY_MAN:STOMACH +CREATURE:BLUEJAY_MAN:GIZZARD +CREATURE:BLUEJAY_MAN:PANCREAS +CREATURE:BLUEJAY_MAN:SPLEEN +CREATURE:BLUEJAY_MAN:KIDNEY +CREATURE:GIANT_BLUEJAY:MUSCLE +CREATURE:GIANT_BLUEJAY:EYE +CREATURE:GIANT_BLUEJAY:BRAIN +CREATURE:GIANT_BLUEJAY:LUNG +CREATURE:GIANT_BLUEJAY:HEART +CREATURE:GIANT_BLUEJAY:LIVER +CREATURE:GIANT_BLUEJAY:GUT +CREATURE:GIANT_BLUEJAY:STOMACH +CREATURE:GIANT_BLUEJAY:GIZZARD +CREATURE:GIANT_BLUEJAY:PANCREAS +CREATURE:GIANT_BLUEJAY:SPLEEN +CREATURE:GIANT_BLUEJAY:KIDNEY +CREATURE:BIRD_CARDINAL:MUSCLE +CREATURE:BIRD_CARDINAL:EYE +CREATURE:BIRD_CARDINAL:BRAIN +CREATURE:BIRD_CARDINAL:LUNG +CREATURE:BIRD_CARDINAL:HEART +CREATURE:BIRD_CARDINAL:LIVER +CREATURE:BIRD_CARDINAL:GUT +CREATURE:BIRD_CARDINAL:STOMACH +CREATURE:BIRD_CARDINAL:GIZZARD +CREATURE:BIRD_CARDINAL:PANCREAS +CREATURE:BIRD_CARDINAL:SPLEEN +CREATURE:BIRD_CARDINAL:KIDNEY +CREATURE:CARDINAL_MAN:MUSCLE +CREATURE:CARDINAL_MAN:EYE +CREATURE:CARDINAL_MAN:BRAIN +CREATURE:CARDINAL_MAN:LUNG +CREATURE:CARDINAL_MAN:HEART +CREATURE:CARDINAL_MAN:LIVER +CREATURE:CARDINAL_MAN:GUT +CREATURE:CARDINAL_MAN:STOMACH +CREATURE:CARDINAL_MAN:GIZZARD +CREATURE:CARDINAL_MAN:PANCREAS +CREATURE:CARDINAL_MAN:SPLEEN +CREATURE:CARDINAL_MAN:KIDNEY +CREATURE:GIANT_CARDINAL:MUSCLE +CREATURE:GIANT_CARDINAL:EYE +CREATURE:GIANT_CARDINAL:BRAIN +CREATURE:GIANT_CARDINAL:LUNG +CREATURE:GIANT_CARDINAL:HEART +CREATURE:GIANT_CARDINAL:LIVER +CREATURE:GIANT_CARDINAL:GUT +CREATURE:GIANT_CARDINAL:STOMACH +CREATURE:GIANT_CARDINAL:GIZZARD + CREATURE:GIANT_CARDINAL:PANCREAS +CREATURE:GIANT_CARDINAL:SPLEEN +CREATURE:GIANT_CARDINAL:KIDNEY +CREATURE:BIRD_GRACKLE:MUSCLE +CREATURE:BIRD_GRACKLE:EYE +CREATURE:BIRD_GRACKLE:BRAIN +CREATURE:BIRD_GRACKLE:LUNG +CREATURE:BIRD_GRACKLE:HEART +CREATURE:BIRD_GRACKLE:LIVER +CREATURE:BIRD_GRACKLE:GUT +CREATURE:BIRD_GRACKLE:STOMACH +CREATURE:BIRD_GRACKLE:GIZZARD +CREATURE:BIRD_GRACKLE:PANCREAS +CREATURE:BIRD_GRACKLE:SPLEEN +CREATURE:BIRD_GRACKLE:KIDNEY +CREATURE:GRACKLE_MAN:MUSCLE +CREATURE:GRACKLE_MAN:EYE +CREATURE:GRACKLE_MAN:BRAIN +CREATURE:GRACKLE_MAN:LUNG +CREATURE:GRACKLE_MAN:HEART +CREATURE:GRACKLE_MAN:LIVER +CREATURE:GRACKLE_MAN:GUT +CREATURE:GRACKLE_MAN:STOMACH +CREATURE:GRACKLE_MAN:GIZZARD +CREATURE:GRACKLE_MAN:PANCREAS +CREATURE:GRACKLE_MAN:SPLEEN +CREATURE:GRACKLE_MAN:KIDNEY +CREATURE:GIANT_GRACKLE:MUSCLE +CREATURE:GIANT_GRACKLE:EYE +CREATURE:GIANT_GRACKLE:BRAIN +CREATURE:GIANT_GRACKLE:LUNG +CREATURE:GIANT_GRACKLE:HEART +CREATURE:GIANT_GRACKLE:LIVER +CREATURE:GIANT_GRACKLE:GUT +CREATURE:GIANT_GRACKLE:STOMACH +CREATURE:GIANT_GRACKLE:GIZZARD +CREATURE:GIANT_GRACKLE:PANCREAS +CREATURE:GIANT_GRACKLE:SPLEEN +CREATURE:GIANT_GRACKLE:KIDNEY +CREATURE:BIRD_ORIOLE:MUSCLE +CREATURE:BIRD_ORIOLE:EYE +CREATURE:BIRD_ORIOLE:BRAIN +CREATURE:BIRD_ORIOLE:LUNG +CREATURE:BIRD_ORIOLE:HEART +CREATURE:BIRD_ORIOLE:LIVER +CREATURE:BIRD_ORIOLE:GUT +CREATURE:BIRD_ORIOLE:STOMACH +CREATURE:BIRD_ORIOLE:GIZZARD +CREATURE:BIRD_ORIOLE:PANCREAS +CREATURE:BIRD_ORIOLE:SPLEEN +CREATURE:BIRD_ORIOLE:KIDNEY +CREATURE:ORIOLE_MAN:MUSCLE +CREATURE:ORIOLE_MAN:EYE +CREATURE:ORIOLE_MAN:BRAIN +CREATURE:ORIOLE_MAN:LUNG +CREATURE:ORIOLE_MAN:HEART +CREATURE:ORIOLE_MAN:LIVER +CREATURE:ORIOLE_MAN:GUT +CREATURE:ORIOLE_MAN:STOMACH +CREATURE:ORIOLE_MAN:GIZZARD +CREATURE:ORIOLE_MAN:PANCREAS +CREATURE:ORIOLE_MAN:SPLEEN +CREATURE:ORIOLE_MAN:KIDNEY +CREATURE:GIANT_ORIOLE:MUSCLE +CREATURE:GIANT_ORIOLE:EYE +CREATURE:GIANT_ORIOLE:BRAIN +CREATURE:GIANT_ORIOLE:LUNG +CREATURE:GIANT_ORIOLE:HEART +CREATURE:GIANT_ORIOLE:LIVER +CREATURE:GIANT_ORIOLE:GUT +CREATURE:GIANT_ORIOLE:STOMACH +CREATURE:GIANT_ORIOLE:GIZZARD +CREATURE:GIANT_ORIOLE:PANCREAS +CREATURE:GIANT_ORIOLE:SPLEEN +CREATURE:GIANT_ORIOLE:KIDNEY +!CREATURE:BIRD_RW_BLACKBIRD:MUSCLE +CREATURE:BIRD_RW_BLACKBIRD:EYE + CREATURE:BIRD_RW_BLACKBIRD:BRAIN +CREATURE:BIRD_RW_BLACKBIRD:LUNG + CREATURE:BIRD_RW_BLACKBIRD:HEART + CREATURE:BIRD_RW_BLACKBIRD:LIVER +CREATURE:BIRD_RW_BLACKBIRD:GUT +"CREATURE:BIRD_RW_BLACKBIRD:STOMACH +"CREATURE:BIRD_RW_BLACKBIRD:GIZZARD +#CREATURE:BIRD_RW_BLACKBIRD:PANCREAS +!CREATURE:BIRD_RW_BLACKBIRD:SPLEEN +!CREATURE:BIRD_RW_BLACKBIRD:KIDNEY + CREATURE:RW_BLACKBIRD_MAN:MUSCLE +CREATURE:RW_BLACKBIRD_MAN:EYE +CREATURE:RW_BLACKBIRD_MAN:BRAIN +CREATURE:RW_BLACKBIRD_MAN:LUNG +CREATURE:RW_BLACKBIRD_MAN:HEART +CREATURE:RW_BLACKBIRD_MAN:LIVER +CREATURE:RW_BLACKBIRD_MAN:GUT +!CREATURE:RW_BLACKBIRD_MAN:STOMACH +!CREATURE:RW_BLACKBIRD_MAN:GIZZARD +"CREATURE:RW_BLACKBIRD_MAN:PANCREAS + CREATURE:RW_BLACKBIRD_MAN:SPLEEN + CREATURE:RW_BLACKBIRD_MAN:KIDNEY +"CREATURE:GIANT_RW_BLACKBIRD:MUSCLE +CREATURE:GIANT_RW_BLACKBIRD:EYE +!CREATURE:GIANT_RW_BLACKBIRD:BRAIN + CREATURE:GIANT_RW_BLACKBIRD:LUNG +!CREATURE:GIANT_RW_BLACKBIRD:HEART +!CREATURE:GIANT_RW_BLACKBIRD:LIVER +CREATURE:GIANT_RW_BLACKBIRD:GUT +#CREATURE:GIANT_RW_BLACKBIRD:STOMACH +#CREATURE:GIANT_RW_BLACKBIRD:GIZZARD +$CREATURE:GIANT_RW_BLACKBIRD:PANCREAS +"CREATURE:GIANT_RW_BLACKBIRD:SPLEEN +"CREATURE:GIANT_RW_BLACKBIRD:KIDNEY +CREATURE:BIRD_PENGUIN:MUSCLE +CREATURE:BIRD_PENGUIN:EYE +CREATURE:BIRD_PENGUIN:BRAIN +CREATURE:BIRD_PENGUIN:LUNG +CREATURE:BIRD_PENGUIN:HEART +CREATURE:BIRD_PENGUIN:LIVER +CREATURE:BIRD_PENGUIN:GUT +CREATURE:BIRD_PENGUIN:STOMACH +CREATURE:BIRD_PENGUIN:GIZZARD +CREATURE:BIRD_PENGUIN:PANCREAS +CREATURE:BIRD_PENGUIN:SPLEEN +CREATURE:BIRD_PENGUIN:KIDNEY +#CREATURE:BIRD_PENGUIN_LITTLE:MUSCLE + CREATURE:BIRD_PENGUIN_LITTLE:EYE +"CREATURE:BIRD_PENGUIN_LITTLE:BRAIN +!CREATURE:BIRD_PENGUIN_LITTLE:LUNG +"CREATURE:BIRD_PENGUIN_LITTLE:HEART +"CREATURE:BIRD_PENGUIN_LITTLE:LIVER + CREATURE:BIRD_PENGUIN_LITTLE:GUT +$CREATURE:BIRD_PENGUIN_LITTLE:STOMACH +$CREATURE:BIRD_PENGUIN_LITTLE:GIZZARD +%CREATURE:BIRD_PENGUIN_LITTLE:PANCREAS +#CREATURE:BIRD_PENGUIN_LITTLE:SPLEEN +#CREATURE:BIRD_PENGUIN_LITTLE:KIDNEY +$CREATURE:BIRD_PENGUIN_EMPEROR:MUSCLE +!CREATURE:BIRD_PENGUIN_EMPEROR:EYE +#CREATURE:BIRD_PENGUIN_EMPEROR:BRAIN +"CREATURE:BIRD_PENGUIN_EMPEROR:LUNG +#CREATURE:BIRD_PENGUIN_EMPEROR:HEART +#CREATURE:BIRD_PENGUIN_EMPEROR:LIVER +!CREATURE:BIRD_PENGUIN_EMPEROR:GUT +%CREATURE:BIRD_PENGUIN_EMPEROR:STOMACH +%CREATURE:BIRD_PENGUIN_EMPEROR:GIZZARD +&CREATURE:BIRD_PENGUIN_EMPEROR:PANCREAS +$CREATURE:BIRD_PENGUIN_EMPEROR:SPLEEN +$CREATURE:BIRD_PENGUIN_EMPEROR:KIDNEY +CREATURE:PENGUIN MAN:MUSCLE +CREATURE:PENGUIN MAN:EYE +CREATURE:PENGUIN MAN:BRAIN +CREATURE:PENGUIN MAN:LUNG +CREATURE:PENGUIN MAN:HEART +CREATURE:PENGUIN MAN:LIVER +CREATURE:PENGUIN MAN:GUT +CREATURE:PENGUIN MAN:STOMACH +CREATURE:PENGUIN MAN:GIZZARD +CREATURE:PENGUIN MAN:PANCREAS +CREATURE:PENGUIN MAN:SPLEEN +CREATURE:PENGUIN MAN:KIDNEY +"CREATURE:BIRD_PENGUIN_GIANT:MUSCLE +CREATURE:BIRD_PENGUIN_GIANT:EYE +!CREATURE:BIRD_PENGUIN_GIANT:BRAIN + CREATURE:BIRD_PENGUIN_GIANT:LUNG +!CREATURE:BIRD_PENGUIN_GIANT:HEART +!CREATURE:BIRD_PENGUIN_GIANT:LIVER +CREATURE:BIRD_PENGUIN_GIANT:GUT +#CREATURE:BIRD_PENGUIN_GIANT:STOMACH +#CREATURE:BIRD_PENGUIN_GIANT:GIZZARD +$CREATURE:BIRD_PENGUIN_GIANT:PANCREAS +"CREATURE:BIRD_PENGUIN_GIANT:SPLEEN +"CREATURE:BIRD_PENGUIN_GIANT:KIDNEY +%CREATURE:BIRD_FALCON_PEREGRINE:MUSCLE +"CREATURE:BIRD_FALCON_PEREGRINE:EYE +$CREATURE:BIRD_FALCON_PEREGRINE:BRAIN +#CREATURE:BIRD_FALCON_PEREGRINE:LUNG +$CREATURE:BIRD_FALCON_PEREGRINE:HEART +$CREATURE:BIRD_FALCON_PEREGRINE:LIVER +"CREATURE:BIRD_FALCON_PEREGRINE:GUT +&CREATURE:BIRD_FALCON_PEREGRINE:STOMACH +&CREATURE:BIRD_FALCON_PEREGRINE:GIZZARD +'CREATURE:BIRD_FALCON_PEREGRINE:PANCREAS +%CREATURE:BIRD_FALCON_PEREGRINE:SPLEEN +%CREATURE:BIRD_FALCON_PEREGRINE:KIDNEY +$CREATURE:PEREGRINE FALCON MAN:MUSCLE +!CREATURE:PEREGRINE FALCON MAN:EYE +#CREATURE:PEREGRINE FALCON MAN:BRAIN +"CREATURE:PEREGRINE FALCON MAN:LUNG +#CREATURE:PEREGRINE FALCON MAN:HEART +#CREATURE:PEREGRINE FALCON MAN:LIVER +!CREATURE:PEREGRINE FALCON MAN:GUT +%CREATURE:PEREGRINE FALCON MAN:STOMACH +%CREATURE:PEREGRINE FALCON MAN:GIZZARD +&CREATURE:PEREGRINE FALCON MAN:PANCREAS +$CREATURE:PEREGRINE FALCON MAN:SPLEEN +$CREATURE:PEREGRINE FALCON MAN:KIDNEY +&CREATURE:GIANT PEREGRINE FALCON:MUSCLE +#CREATURE:GIANT PEREGRINE FALCON:EYE +%CREATURE:GIANT PEREGRINE FALCON:BRAIN +$CREATURE:GIANT PEREGRINE FALCON:LUNG +%CREATURE:GIANT PEREGRINE FALCON:HEART +%CREATURE:GIANT PEREGRINE FALCON:LIVER +#CREATURE:GIANT PEREGRINE FALCON:GUT +'CREATURE:GIANT PEREGRINE FALCON:STOMACH +'CREATURE:GIANT PEREGRINE FALCON:GIZZARD +(CREATURE:GIANT PEREGRINE FALCON:PANCREAS +&CREATURE:GIANT PEREGRINE FALCON:SPLEEN +&CREATURE:GIANT PEREGRINE FALCON:KIDNEY +CREATURE:BIRD_KIWI:MUSCLE +CREATURE:BIRD_KIWI:EYE +CREATURE:BIRD_KIWI:BRAIN +CREATURE:BIRD_KIWI:LUNG +CREATURE:BIRD_KIWI:HEART +CREATURE:BIRD_KIWI:LIVER +CREATURE:BIRD_KIWI:GUT +CREATURE:BIRD_KIWI:STOMACH +CREATURE:BIRD_KIWI:GIZZARD +CREATURE:BIRD_KIWI:PANCREAS +CREATURE:BIRD_KIWI:SPLEEN +CREATURE:BIRD_KIWI:KIDNEY +CREATURE:KIWI MAN:MUSCLE +CREATURE:KIWI MAN:EYE +CREATURE:KIWI MAN:BRAIN +CREATURE:KIWI MAN:LUNG +CREATURE:KIWI MAN:HEART +CREATURE:KIWI MAN:LIVER +CREATURE:KIWI MAN:GUT +CREATURE:KIWI MAN:STOMACH +CREATURE:KIWI MAN:GIZZARD +CREATURE:KIWI MAN:PANCREAS +CREATURE:KIWI MAN:SPLEEN +CREATURE:KIWI MAN:KIDNEY +CREATURE:BIRD_KIWI_GIANT:MUSCLE +CREATURE:BIRD_KIWI_GIANT:EYE +CREATURE:BIRD_KIWI_GIANT:BRAIN +CREATURE:BIRD_KIWI_GIANT:LUNG +CREATURE:BIRD_KIWI_GIANT:HEART +CREATURE:BIRD_KIWI_GIANT:LIVER +CREATURE:BIRD_KIWI_GIANT:GUT + CREATURE:BIRD_KIWI_GIANT:STOMACH + CREATURE:BIRD_KIWI_GIANT:GIZZARD +!CREATURE:BIRD_KIWI_GIANT:PANCREAS +CREATURE:BIRD_KIWI_GIANT:SPLEEN +CREATURE:BIRD_KIWI_GIANT:KIDNEY +CREATURE:BIRD_OSTRICH:MUSCLE +CREATURE:BIRD_OSTRICH:EYE +CREATURE:BIRD_OSTRICH:BRAIN +CREATURE:BIRD_OSTRICH:LUNG +CREATURE:BIRD_OSTRICH:HEART +CREATURE:BIRD_OSTRICH:LIVER +CREATURE:BIRD_OSTRICH:GUT +CREATURE:BIRD_OSTRICH:STOMACH +CREATURE:BIRD_OSTRICH:GIZZARD +CREATURE:BIRD_OSTRICH:PANCREAS +CREATURE:BIRD_OSTRICH:SPLEEN +CREATURE:BIRD_OSTRICH:KIDNEY +CREATURE:OSTRICH MAN:MUSCLE +CREATURE:OSTRICH MAN:EYE +CREATURE:OSTRICH MAN:BRAIN +CREATURE:OSTRICH MAN:LUNG +CREATURE:OSTRICH MAN:HEART +CREATURE:OSTRICH MAN:LIVER +CREATURE:OSTRICH MAN:GUT +CREATURE:OSTRICH MAN:STOMACH +CREATURE:OSTRICH MAN:GIZZARD +CREATURE:OSTRICH MAN:PANCREAS +CREATURE:OSTRICH MAN:SPLEEN +CREATURE:OSTRICH MAN:KIDNEY +"CREATURE:BIRD_OSTRICH_GIANT:MUSCLE +CREATURE:BIRD_OSTRICH_GIANT:EYE +!CREATURE:BIRD_OSTRICH_GIANT:BRAIN + CREATURE:BIRD_OSTRICH_GIANT:LUNG +!CREATURE:BIRD_OSTRICH_GIANT:HEART +!CREATURE:BIRD_OSTRICH_GIANT:LIVER +CREATURE:BIRD_OSTRICH_GIANT:GUT +#CREATURE:BIRD_OSTRICH_GIANT:STOMACH +#CREATURE:BIRD_OSTRICH_GIANT:GIZZARD +$CREATURE:BIRD_OSTRICH_GIANT:PANCREAS +"CREATURE:BIRD_OSTRICH_GIANT:SPLEEN +"CREATURE:BIRD_OSTRICH_GIANT:KIDNEY +CREATURE:BIRD_CROW:MUSCLE +CREATURE:BIRD_CROW:EYE +CREATURE:BIRD_CROW:BRAIN +CREATURE:BIRD_CROW:LUNG +CREATURE:BIRD_CROW:HEART +CREATURE:BIRD_CROW:LIVER +CREATURE:BIRD_CROW:GUT +CREATURE:BIRD_CROW:STOMACH +CREATURE:BIRD_CROW:GIZZARD +CREATURE:BIRD_CROW:PANCREAS +CREATURE:BIRD_CROW:SPLEEN +CREATURE:BIRD_CROW:KIDNEY +CREATURE:CROW_MAN:MUSCLE +CREATURE:CROW_MAN:EYE +CREATURE:CROW_MAN:BRAIN +CREATURE:CROW_MAN:LUNG +CREATURE:CROW_MAN:HEART +CREATURE:CROW_MAN:LIVER +CREATURE:CROW_MAN:GUT +CREATURE:CROW_MAN:STOMACH +CREATURE:CROW_MAN:GIZZARD +CREATURE:CROW_MAN:PANCREAS +CREATURE:CROW_MAN:SPLEEN +CREATURE:CROW_MAN:KIDNEY +CREATURE:GIANT_CROW:MUSCLE +CREATURE:GIANT_CROW:EYE +CREATURE:GIANT_CROW:BRAIN +CREATURE:GIANT_CROW:LUNG +CREATURE:GIANT_CROW:HEART +CREATURE:GIANT_CROW:LIVER +CREATURE:GIANT_CROW:GUT +CREATURE:GIANT_CROW:STOMACH +CREATURE:GIANT_CROW:GIZZARD +CREATURE:GIANT_CROW:PANCREAS +CREATURE:GIANT_CROW:SPLEEN +CREATURE:GIANT_CROW:KIDNEY +CREATURE:BIRD_RAVEN:MUSCLE +CREATURE:BIRD_RAVEN:EYE +CREATURE:BIRD_RAVEN:BRAIN +CREATURE:BIRD_RAVEN:LUNG +CREATURE:BIRD_RAVEN:HEART +CREATURE:BIRD_RAVEN:LIVER +CREATURE:BIRD_RAVEN:GUT +CREATURE:BIRD_RAVEN:STOMACH +CREATURE:BIRD_RAVEN:GIZZARD +CREATURE:BIRD_RAVEN:PANCREAS +CREATURE:BIRD_RAVEN:SPLEEN +CREATURE:BIRD_RAVEN:KIDNEY +CREATURE:RAVEN_MAN:MUSCLE +CREATURE:RAVEN_MAN:EYE +CREATURE:RAVEN_MAN:BRAIN +CREATURE:RAVEN_MAN:LUNG +CREATURE:RAVEN_MAN:HEART +CREATURE:RAVEN_MAN:LIVER +CREATURE:RAVEN_MAN:GUT +CREATURE:RAVEN_MAN:STOMACH +CREATURE:RAVEN_MAN:GIZZARD +CREATURE:RAVEN_MAN:PANCREAS +CREATURE:RAVEN_MAN:SPLEEN +CREATURE:RAVEN_MAN:KIDNEY +CREATURE:GIANT_RAVEN:MUSCLE +CREATURE:GIANT_RAVEN:EYE +CREATURE:GIANT_RAVEN:BRAIN +CREATURE:GIANT_RAVEN:LUNG +CREATURE:GIANT_RAVEN:HEART +CREATURE:GIANT_RAVEN:LIVER +CREATURE:GIANT_RAVEN:GUT +CREATURE:GIANT_RAVEN:STOMACH +CREATURE:GIANT_RAVEN:GIZZARD +CREATURE:GIANT_RAVEN:PANCREAS +CREATURE:GIANT_RAVEN:SPLEEN +CREATURE:GIANT_RAVEN:KIDNEY +CREATURE:BIRD_CASSOWARY:MUSCLE +CREATURE:BIRD_CASSOWARY:EYE +CREATURE:BIRD_CASSOWARY:BRAIN +CREATURE:BIRD_CASSOWARY:LUNG +CREATURE:BIRD_CASSOWARY:HEART +CREATURE:BIRD_CASSOWARY:LIVER +CREATURE:BIRD_CASSOWARY:GUT +CREATURE:BIRD_CASSOWARY:STOMACH +CREATURE:BIRD_CASSOWARY:GIZZARD + CREATURE:BIRD_CASSOWARY:PANCREAS +CREATURE:BIRD_CASSOWARY:SPLEEN +CREATURE:BIRD_CASSOWARY:KIDNEY +CREATURE:CASSOWARY_MAN:MUSCLE +CREATURE:CASSOWARY_MAN:EYE +CREATURE:CASSOWARY_MAN:BRAIN +CREATURE:CASSOWARY_MAN:LUNG +CREATURE:CASSOWARY_MAN:HEART +CREATURE:CASSOWARY_MAN:LIVER +CREATURE:CASSOWARY_MAN:GUT +CREATURE:CASSOWARY_MAN:STOMACH +CREATURE:CASSOWARY_MAN:GIZZARD +CREATURE:CASSOWARY_MAN:PANCREAS +CREATURE:CASSOWARY_MAN:SPLEEN +CREATURE:CASSOWARY_MAN:KIDNEY +CREATURE:GIANT_CASSOWARY:MUSCLE +CREATURE:GIANT_CASSOWARY:EYE +CREATURE:GIANT_CASSOWARY:BRAIN +CREATURE:GIANT_CASSOWARY:LUNG +CREATURE:GIANT_CASSOWARY:HEART +CREATURE:GIANT_CASSOWARY:LIVER +CREATURE:GIANT_CASSOWARY:GUT + CREATURE:GIANT_CASSOWARY:STOMACH + CREATURE:GIANT_CASSOWARY:GIZZARD +!CREATURE:GIANT_CASSOWARY:PANCREAS +CREATURE:GIANT_CASSOWARY:SPLEEN +CREATURE:GIANT_CASSOWARY:KIDNEY +CREATURE:BIRD_KEA:MUSCLE +CREATURE:BIRD_KEA:EYE +CREATURE:BIRD_KEA:BRAIN +CREATURE:BIRD_KEA:LUNG +CREATURE:BIRD_KEA:HEART +CREATURE:BIRD_KEA:LIVER +CREATURE:BIRD_KEA:GUT +CREATURE:BIRD_KEA:STOMACH +CREATURE:BIRD_KEA:GIZZARD +CREATURE:BIRD_KEA:PANCREAS +CREATURE:BIRD_KEA:SPLEEN +CREATURE:BIRD_KEA:KIDNEY +CREATURE:KEA_MAN:MUSCLE +CREATURE:KEA_MAN:EYE +CREATURE:KEA_MAN:BRAIN +CREATURE:KEA_MAN:LUNG +CREATURE:KEA_MAN:HEART +CREATURE:KEA_MAN:LIVER +CREATURE:KEA_MAN:GUT +CREATURE:KEA_MAN:STOMACH +CREATURE:KEA_MAN:GIZZARD +CREATURE:KEA_MAN:PANCREAS +CREATURE:KEA_MAN:SPLEEN +CREATURE:KEA_MAN:KIDNEY +CREATURE:GIANT_KEA:MUSCLE +CREATURE:GIANT_KEA:EYE +CREATURE:GIANT_KEA:BRAIN +CREATURE:GIANT_KEA:LUNG +CREATURE:GIANT_KEA:HEART +CREATURE:GIANT_KEA:LIVER +CREATURE:GIANT_KEA:GUT +CREATURE:GIANT_KEA:STOMACH +CREATURE:GIANT_KEA:GIZZARD +CREATURE:GIANT_KEA:PANCREAS +CREATURE:GIANT_KEA:SPLEEN +CREATURE:GIANT_KEA:KIDNEY +CREATURE:BIRD_OWL_SNOWY:MUSCLE +CREATURE:BIRD_OWL_SNOWY:EYE +CREATURE:BIRD_OWL_SNOWY:BRAIN +CREATURE:BIRD_OWL_SNOWY:LUNG +CREATURE:BIRD_OWL_SNOWY:HEART +CREATURE:BIRD_OWL_SNOWY:LIVER +CREATURE:BIRD_OWL_SNOWY:GUT +CREATURE:BIRD_OWL_SNOWY:STOMACH +CREATURE:BIRD_OWL_SNOWY:GIZZARD + CREATURE:BIRD_OWL_SNOWY:PANCREAS +CREATURE:BIRD_OWL_SNOWY:SPLEEN +CREATURE:BIRD_OWL_SNOWY:KIDNEY +CREATURE:SNOWY_OWL_MAN:MUSCLE +CREATURE:SNOWY_OWL_MAN:EYE +CREATURE:SNOWY_OWL_MAN:BRAIN +CREATURE:SNOWY_OWL_MAN:LUNG +CREATURE:SNOWY_OWL_MAN:HEART +CREATURE:SNOWY_OWL_MAN:LIVER +CREATURE:SNOWY_OWL_MAN:GUT +CREATURE:SNOWY_OWL_MAN:STOMACH +CREATURE:SNOWY_OWL_MAN:GIZZARD +CREATURE:SNOWY_OWL_MAN:PANCREAS +CREATURE:SNOWY_OWL_MAN:SPLEEN +CREATURE:SNOWY_OWL_MAN:KIDNEY +CREATURE:GIANT_SNOWY_OWL:MUSCLE +CREATURE:GIANT_SNOWY_OWL:EYE +CREATURE:GIANT_SNOWY_OWL:BRAIN +CREATURE:GIANT_SNOWY_OWL:LUNG +CREATURE:GIANT_SNOWY_OWL:HEART +CREATURE:GIANT_SNOWY_OWL:LIVER +CREATURE:GIANT_SNOWY_OWL:GUT + CREATURE:GIANT_SNOWY_OWL:STOMACH + CREATURE:GIANT_SNOWY_OWL:GIZZARD +!CREATURE:GIANT_SNOWY_OWL:PANCREAS +CREATURE:GIANT_SNOWY_OWL:SPLEEN +CREATURE:GIANT_SNOWY_OWL:KIDNEY +CREATURE:SPARROW:MUSCLE +CREATURE:SPARROW:EYE +CREATURE:SPARROW:BRAIN +CREATURE:SPARROW:LUNG +CREATURE:SPARROW:HEART +CREATURE:SPARROW:LIVER +CREATURE:SPARROW:GUT +CREATURE:SPARROW:STOMACH +CREATURE:SPARROW:GIZZARD +CREATURE:SPARROW:PANCREAS +CREATURE:SPARROW:SPLEEN +CREATURE:SPARROW:KIDNEY +CREATURE:SPARROW_MAN:MUSCLE +CREATURE:SPARROW_MAN:EYE +CREATURE:SPARROW_MAN:BRAIN +CREATURE:SPARROW_MAN:LUNG +CREATURE:SPARROW_MAN:HEART +CREATURE:SPARROW_MAN:LIVER +CREATURE:SPARROW_MAN:GUT +CREATURE:SPARROW_MAN:STOMACH +CREATURE:SPARROW_MAN:GIZZARD +CREATURE:SPARROW_MAN:PANCREAS +CREATURE:SPARROW_MAN:SPLEEN +CREATURE:SPARROW_MAN:KIDNEY +CREATURE:GIANT_SPARROW:MUSCLE +CREATURE:GIANT_SPARROW:EYE +CREATURE:GIANT_SPARROW:BRAIN +CREATURE:GIANT_SPARROW:LUNG +CREATURE:GIANT_SPARROW:HEART +CREATURE:GIANT_SPARROW:LIVER +CREATURE:GIANT_SPARROW:GUT +CREATURE:GIANT_SPARROW:STOMACH +CREATURE:GIANT_SPARROW:GIZZARD +CREATURE:GIANT_SPARROW:PANCREAS +CREATURE:GIANT_SPARROW:SPLEEN +CREATURE:GIANT_SPARROW:KIDNEY + CREATURE:BIRD_STORK_WHITE:MUSCLE +CREATURE:BIRD_STORK_WHITE:EYE +CREATURE:BIRD_STORK_WHITE:BRAIN +CREATURE:BIRD_STORK_WHITE:LUNG +CREATURE:BIRD_STORK_WHITE:HEART +CREATURE:BIRD_STORK_WHITE:LIVER +CREATURE:BIRD_STORK_WHITE:GUT +!CREATURE:BIRD_STORK_WHITE:STOMACH +!CREATURE:BIRD_STORK_WHITE:GIZZARD +"CREATURE:BIRD_STORK_WHITE:PANCREAS + CREATURE:BIRD_STORK_WHITE:SPLEEN + CREATURE:BIRD_STORK_WHITE:KIDNEY +CREATURE:WHITE_STORK_MAN:MUSCLE +CREATURE:WHITE_STORK_MAN:EYE +CREATURE:WHITE_STORK_MAN:BRAIN +CREATURE:WHITE_STORK_MAN:LUNG +CREATURE:WHITE_STORK_MAN:HEART +CREATURE:WHITE_STORK_MAN:LIVER +CREATURE:WHITE_STORK_MAN:GUT + CREATURE:WHITE_STORK_MAN:STOMACH + CREATURE:WHITE_STORK_MAN:GIZZARD +!CREATURE:WHITE_STORK_MAN:PANCREAS +CREATURE:WHITE_STORK_MAN:SPLEEN +CREATURE:WHITE_STORK_MAN:KIDNEY +!CREATURE:GIANT_WHITE_STORK:MUSCLE +CREATURE:GIANT_WHITE_STORK:EYE + CREATURE:GIANT_WHITE_STORK:BRAIN +CREATURE:GIANT_WHITE_STORK:LUNG + CREATURE:GIANT_WHITE_STORK:HEART + CREATURE:GIANT_WHITE_STORK:LIVER +CREATURE:GIANT_WHITE_STORK:GUT +"CREATURE:GIANT_WHITE_STORK:STOMACH +"CREATURE:GIANT_WHITE_STORK:GIZZARD +#CREATURE:GIANT_WHITE_STORK:PANCREAS +!CREATURE:GIANT_WHITE_STORK:SPLEEN +!CREATURE:GIANT_WHITE_STORK:KIDNEY +CREATURE:BIRD_LOON:MUSCLE +CREATURE:BIRD_LOON:EYE +CREATURE:BIRD_LOON:BRAIN +CREATURE:BIRD_LOON:LUNG +CREATURE:BIRD_LOON:HEART +CREATURE:BIRD_LOON:LIVER +CREATURE:BIRD_LOON:GUT +CREATURE:BIRD_LOON:STOMACH +CREATURE:BIRD_LOON:GIZZARD +CREATURE:BIRD_LOON:PANCREAS +CREATURE:BIRD_LOON:SPLEEN +CREATURE:BIRD_LOON:KIDNEY +CREATURE:LOON_MAN:MUSCLE +CREATURE:LOON_MAN:EYE +CREATURE:LOON_MAN:BRAIN +CREATURE:LOON_MAN:LUNG +CREATURE:LOON_MAN:HEART +CREATURE:LOON_MAN:LIVER +CREATURE:LOON_MAN:GUT +CREATURE:LOON_MAN:STOMACH +CREATURE:LOON_MAN:GIZZARD +CREATURE:LOON_MAN:PANCREAS +CREATURE:LOON_MAN:SPLEEN +CREATURE:LOON_MAN:KIDNEY +CREATURE:GIANT_LOON:MUSCLE +CREATURE:GIANT_LOON:EYE +CREATURE:GIANT_LOON:BRAIN +CREATURE:GIANT_LOON:LUNG +CREATURE:GIANT_LOON:HEART +CREATURE:GIANT_LOON:LIVER +CREATURE:GIANT_LOON:GUT +CREATURE:GIANT_LOON:STOMACH +CREATURE:GIANT_LOON:GIZZARD +CREATURE:GIANT_LOON:PANCREAS +CREATURE:GIANT_LOON:SPLEEN +CREATURE:GIANT_LOON:KIDNEY +CREATURE:BIRD_OWL_BARN:MUSCLE +CREATURE:BIRD_OWL_BARN:EYE +CREATURE:BIRD_OWL_BARN:BRAIN +CREATURE:BIRD_OWL_BARN:LUNG +CREATURE:BIRD_OWL_BARN:HEART +CREATURE:BIRD_OWL_BARN:LIVER +CREATURE:BIRD_OWL_BARN:GUT +CREATURE:BIRD_OWL_BARN:STOMACH +CREATURE:BIRD_OWL_BARN:GIZZARD +CREATURE:BIRD_OWL_BARN:PANCREAS +CREATURE:BIRD_OWL_BARN:SPLEEN +CREATURE:BIRD_OWL_BARN:KIDNEY +CREATURE:BARN_OWL_MAN:MUSCLE +CREATURE:BARN_OWL_MAN:EYE +CREATURE:BARN_OWL_MAN:BRAIN +CREATURE:BARN_OWL_MAN:LUNG +CREATURE:BARN_OWL_MAN:HEART +CREATURE:BARN_OWL_MAN:LIVER +CREATURE:BARN_OWL_MAN:GUT +CREATURE:BARN_OWL_MAN:STOMACH +CREATURE:BARN_OWL_MAN:GIZZARD +CREATURE:BARN_OWL_MAN:PANCREAS +CREATURE:BARN_OWL_MAN:SPLEEN +CREATURE:BARN_OWL_MAN:KIDNEY +CREATURE:GIANT_BARN_OWL:MUSCLE +CREATURE:GIANT_BARN_OWL:EYE +CREATURE:GIANT_BARN_OWL:BRAIN +CREATURE:GIANT_BARN_OWL:LUNG +CREATURE:GIANT_BARN_OWL:HEART +CREATURE:GIANT_BARN_OWL:LIVER +CREATURE:GIANT_BARN_OWL:GUT +CREATURE:GIANT_BARN_OWL:STOMACH +CREATURE:GIANT_BARN_OWL:GIZZARD + CREATURE:GIANT_BARN_OWL:PANCREAS +CREATURE:GIANT_BARN_OWL:SPLEEN +CREATURE:GIANT_BARN_OWL:KIDNEY +CREATURE:BIRD_PARAKEET:MUSCLE +CREATURE:BIRD_PARAKEET:EYE +CREATURE:BIRD_PARAKEET:BRAIN +CREATURE:BIRD_PARAKEET:LUNG +CREATURE:BIRD_PARAKEET:HEART +CREATURE:BIRD_PARAKEET:LIVER +CREATURE:BIRD_PARAKEET:GUT +CREATURE:BIRD_PARAKEET:STOMACH +CREATURE:BIRD_PARAKEET:GIZZARD +CREATURE:BIRD_PARAKEET:PANCREAS +CREATURE:BIRD_PARAKEET:SPLEEN +CREATURE:BIRD_PARAKEET:KIDNEY +CREATURE:PARAKEET_MAN:MUSCLE +CREATURE:PARAKEET_MAN:EYE +CREATURE:PARAKEET_MAN:BRAIN +CREATURE:PARAKEET_MAN:LUNG +CREATURE:PARAKEET_MAN:HEART +CREATURE:PARAKEET_MAN:LIVER +CREATURE:PARAKEET_MAN:GUT +CREATURE:PARAKEET_MAN:STOMACH +CREATURE:PARAKEET_MAN:GIZZARD +CREATURE:PARAKEET_MAN:PANCREAS +CREATURE:PARAKEET_MAN:SPLEEN +CREATURE:PARAKEET_MAN:KIDNEY +CREATURE:GIANT_PARAKEET:MUSCLE +CREATURE:GIANT_PARAKEET:EYE +CREATURE:GIANT_PARAKEET:BRAIN +CREATURE:GIANT_PARAKEET:LUNG +CREATURE:GIANT_PARAKEET:HEART +CREATURE:GIANT_PARAKEET:LIVER +CREATURE:GIANT_PARAKEET:GUT +CREATURE:GIANT_PARAKEET:STOMACH +CREATURE:GIANT_PARAKEET:GIZZARD + CREATURE:GIANT_PARAKEET:PANCREAS +CREATURE:GIANT_PARAKEET:SPLEEN +CREATURE:GIANT_PARAKEET:KIDNEY +CREATURE:BIRD_KAKAPO:MUSCLE +CREATURE:BIRD_KAKAPO:EYE +CREATURE:BIRD_KAKAPO:BRAIN +CREATURE:BIRD_KAKAPO:LUNG +CREATURE:BIRD_KAKAPO:HEART +CREATURE:BIRD_KAKAPO:LIVER +CREATURE:BIRD_KAKAPO:GUT +CREATURE:BIRD_KAKAPO:STOMACH +CREATURE:BIRD_KAKAPO:GIZZARD +CREATURE:BIRD_KAKAPO:PANCREAS +CREATURE:BIRD_KAKAPO:SPLEEN +CREATURE:BIRD_KAKAPO:KIDNEY +CREATURE:KAKAPO_MAN:MUSCLE +CREATURE:KAKAPO_MAN:EYE +CREATURE:KAKAPO_MAN:BRAIN +CREATURE:KAKAPO_MAN:LUNG +CREATURE:KAKAPO_MAN:HEART +CREATURE:KAKAPO_MAN:LIVER +CREATURE:KAKAPO_MAN:GUT +CREATURE:KAKAPO_MAN:STOMACH +CREATURE:KAKAPO_MAN:GIZZARD +CREATURE:KAKAPO_MAN:PANCREAS +CREATURE:KAKAPO_MAN:SPLEEN +CREATURE:KAKAPO_MAN:KIDNEY +CREATURE:GIANT_KAKAPO:MUSCLE +CREATURE:GIANT_KAKAPO:EYE +CREATURE:GIANT_KAKAPO:BRAIN +CREATURE:GIANT_KAKAPO:LUNG +CREATURE:GIANT_KAKAPO:HEART +CREATURE:GIANT_KAKAPO:LIVER +CREATURE:GIANT_KAKAPO:GUT +CREATURE:GIANT_KAKAPO:STOMACH +CREATURE:GIANT_KAKAPO:GIZZARD +CREATURE:GIANT_KAKAPO:PANCREAS +CREATURE:GIANT_KAKAPO:SPLEEN +CREATURE:GIANT_KAKAPO:KIDNEY + CREATURE:BIRD_PARROT_GREY:MUSCLE +CREATURE:BIRD_PARROT_GREY:EYE +CREATURE:BIRD_PARROT_GREY:BRAIN +CREATURE:BIRD_PARROT_GREY:LUNG +CREATURE:BIRD_PARROT_GREY:HEART +CREATURE:BIRD_PARROT_GREY:LIVER +CREATURE:BIRD_PARROT_GREY:GUT +!CREATURE:BIRD_PARROT_GREY:STOMACH +!CREATURE:BIRD_PARROT_GREY:GIZZARD +"CREATURE:BIRD_PARROT_GREY:PANCREAS + CREATURE:BIRD_PARROT_GREY:SPLEEN + CREATURE:BIRD_PARROT_GREY:KIDNEY +CREATURE:GREY_PARROT_MAN:MUSCLE +CREATURE:GREY_PARROT_MAN:EYE +CREATURE:GREY_PARROT_MAN:BRAIN +CREATURE:GREY_PARROT_MAN:LUNG +CREATURE:GREY_PARROT_MAN:HEART +CREATURE:GREY_PARROT_MAN:LIVER +CREATURE:GREY_PARROT_MAN:GUT + CREATURE:GREY_PARROT_MAN:STOMACH + CREATURE:GREY_PARROT_MAN:GIZZARD +!CREATURE:GREY_PARROT_MAN:PANCREAS +CREATURE:GREY_PARROT_MAN:SPLEEN +CREATURE:GREY_PARROT_MAN:KIDNEY +!CREATURE:GIANT_GREY_PARROT:MUSCLE +CREATURE:GIANT_GREY_PARROT:EYE + CREATURE:GIANT_GREY_PARROT:BRAIN +CREATURE:GIANT_GREY_PARROT:LUNG + CREATURE:GIANT_GREY_PARROT:HEART + CREATURE:GIANT_GREY_PARROT:LIVER +CREATURE:GIANT_GREY_PARROT:GUT +"CREATURE:GIANT_GREY_PARROT:STOMACH +"CREATURE:GIANT_GREY_PARROT:GIZZARD +#CREATURE:GIANT_GREY_PARROT:PANCREAS +!CREATURE:GIANT_GREY_PARROT:SPLEEN +!CREATURE:GIANT_GREY_PARROT:KIDNEY +CREATURE:BIRD_PUFFIN:MUSCLE +CREATURE:BIRD_PUFFIN:EYE +CREATURE:BIRD_PUFFIN:BRAIN +CREATURE:BIRD_PUFFIN:LUNG +CREATURE:BIRD_PUFFIN:HEART +CREATURE:BIRD_PUFFIN:LIVER +CREATURE:BIRD_PUFFIN:GUT +CREATURE:BIRD_PUFFIN:STOMACH +CREATURE:BIRD_PUFFIN:GIZZARD +CREATURE:BIRD_PUFFIN:PANCREAS +CREATURE:BIRD_PUFFIN:SPLEEN +CREATURE:BIRD_PUFFIN:KIDNEY +CREATURE:PUFFIN_MAN:MUSCLE +CREATURE:PUFFIN_MAN:EYE +CREATURE:PUFFIN_MAN:BRAIN +CREATURE:PUFFIN_MAN:LUNG +CREATURE:PUFFIN_MAN:HEART +CREATURE:PUFFIN_MAN:LIVER +CREATURE:PUFFIN_MAN:GUT +CREATURE:PUFFIN_MAN:STOMACH +CREATURE:PUFFIN_MAN:GIZZARD +CREATURE:PUFFIN_MAN:PANCREAS +CREATURE:PUFFIN_MAN:SPLEEN +CREATURE:PUFFIN_MAN:KIDNEY +CREATURE:GIANT_PUFFIN:MUSCLE +CREATURE:GIANT_PUFFIN:EYE +CREATURE:GIANT_PUFFIN:BRAIN +CREATURE:GIANT_PUFFIN:LUNG +CREATURE:GIANT_PUFFIN:HEART +CREATURE:GIANT_PUFFIN:LIVER +CREATURE:GIANT_PUFFIN:GUT +CREATURE:GIANT_PUFFIN:STOMACH +CREATURE:GIANT_PUFFIN:GIZZARD +CREATURE:GIANT_PUFFIN:PANCREAS +CREATURE:GIANT_PUFFIN:SPLEEN +CREATURE:GIANT_PUFFIN:KIDNEY +CREATURE:BIRD_SWAN:MUSCLE +CREATURE:BIRD_SWAN:EYE +CREATURE:BIRD_SWAN:BRAIN +CREATURE:BIRD_SWAN:LUNG +CREATURE:BIRD_SWAN:HEART +CREATURE:BIRD_SWAN:LIVER +CREATURE:BIRD_SWAN:GUT +CREATURE:BIRD_SWAN:STOMACH +CREATURE:BIRD_SWAN:GIZZARD +CREATURE:BIRD_SWAN:PANCREAS +CREATURE:BIRD_SWAN:SPLEEN +CREATURE:BIRD_SWAN:KIDNEY +CREATURE:SWAN_MAN:MUSCLE +CREATURE:SWAN_MAN:EYE +CREATURE:SWAN_MAN:BRAIN +CREATURE:SWAN_MAN:LUNG +CREATURE:SWAN_MAN:HEART +CREATURE:SWAN_MAN:LIVER +CREATURE:SWAN_MAN:GUT +CREATURE:SWAN_MAN:STOMACH +CREATURE:SWAN_MAN:GIZZARD +CREATURE:SWAN_MAN:PANCREAS +CREATURE:SWAN_MAN:SPLEEN +CREATURE:SWAN_MAN:KIDNEY +CREATURE:GIANT_SWAN:MUSCLE +CREATURE:GIANT_SWAN:EYE +CREATURE:GIANT_SWAN:BRAIN +CREATURE:GIANT_SWAN:LUNG +CREATURE:GIANT_SWAN:HEART +CREATURE:GIANT_SWAN:LIVER +CREATURE:GIANT_SWAN:GUT +CREATURE:GIANT_SWAN:STOMACH +CREATURE:GIANT_SWAN:GIZZARD +CREATURE:GIANT_SWAN:PANCREAS +CREATURE:GIANT_SWAN:SPLEEN +CREATURE:GIANT_SWAN:KIDNEY +CREATURE:BIRD_LORIKEET:MUSCLE +CREATURE:BIRD_LORIKEET:EYE +CREATURE:BIRD_LORIKEET:BRAIN +CREATURE:BIRD_LORIKEET:LUNG +CREATURE:BIRD_LORIKEET:HEART +CREATURE:BIRD_LORIKEET:LIVER +CREATURE:BIRD_LORIKEET:GUT +CREATURE:BIRD_LORIKEET:STOMACH +CREATURE:BIRD_LORIKEET:GIZZARD +CREATURE:BIRD_LORIKEET:PANCREAS +CREATURE:BIRD_LORIKEET:SPLEEN +CREATURE:BIRD_LORIKEET:KIDNEY +CREATURE:LORIKEET_MAN:MUSCLE +CREATURE:LORIKEET_MAN:EYE +CREATURE:LORIKEET_MAN:BRAIN +CREATURE:LORIKEET_MAN:LUNG +CREATURE:LORIKEET_MAN:HEART +CREATURE:LORIKEET_MAN:LIVER +CREATURE:LORIKEET_MAN:GUT +CREATURE:LORIKEET_MAN:STOMACH +CREATURE:LORIKEET_MAN:GIZZARD +CREATURE:LORIKEET_MAN:PANCREAS +CREATURE:LORIKEET_MAN:SPLEEN +CREATURE:LORIKEET_MAN:KIDNEY +CREATURE:GIANT_LORIKEET:MUSCLE +CREATURE:GIANT_LORIKEET:EYE +CREATURE:GIANT_LORIKEET:BRAIN +CREATURE:GIANT_LORIKEET:LUNG +CREATURE:GIANT_LORIKEET:HEART +CREATURE:GIANT_LORIKEET:LIVER +CREATURE:GIANT_LORIKEET:GUT +CREATURE:GIANT_LORIKEET:STOMACH +CREATURE:GIANT_LORIKEET:GIZZARD + CREATURE:GIANT_LORIKEET:PANCREAS +CREATURE:GIANT_LORIKEET:SPLEEN +CREATURE:GIANT_LORIKEET:KIDNEY +CREATURE:BIRD_WREN:MUSCLE +CREATURE:BIRD_WREN:EYE +CREATURE:BIRD_WREN:BRAIN +CREATURE:BIRD_WREN:LUNG +CREATURE:BIRD_WREN:HEART +CREATURE:BIRD_WREN:LIVER +CREATURE:BIRD_WREN:GUT +CREATURE:BIRD_WREN:STOMACH +CREATURE:BIRD_WREN:GIZZARD +CREATURE:BIRD_WREN:PANCREAS +CREATURE:BIRD_WREN:SPLEEN +CREATURE:BIRD_WREN:KIDNEY +CREATURE:WREN_MAN:MUSCLE +CREATURE:WREN_MAN:EYE +CREATURE:WREN_MAN:BRAIN +CREATURE:WREN_MAN:LUNG +CREATURE:WREN_MAN:HEART +CREATURE:WREN_MAN:LIVER +CREATURE:WREN_MAN:GUT +CREATURE:WREN_MAN:STOMACH +CREATURE:WREN_MAN:GIZZARD +CREATURE:WREN_MAN:PANCREAS +CREATURE:WREN_MAN:SPLEEN +CREATURE:WREN_MAN:KIDNEY +CREATURE:GIANT_WREN:MUSCLE +CREATURE:GIANT_WREN:EYE +CREATURE:GIANT_WREN:BRAIN +CREATURE:GIANT_WREN:LUNG +CREATURE:GIANT_WREN:HEART +CREATURE:GIANT_WREN:LIVER +CREATURE:GIANT_WREN:GUT +CREATURE:GIANT_WREN:STOMACH +CREATURE:GIANT_WREN:GIZZARD +CREATURE:GIANT_WREN:PANCREAS +CREATURE:GIANT_WREN:SPLEEN +CREATURE:GIANT_WREN:KIDNEY +CREATURE:BIRD_OSPREY:MUSCLE +CREATURE:BIRD_OSPREY:EYE +CREATURE:BIRD_OSPREY:BRAIN +CREATURE:BIRD_OSPREY:LUNG +CREATURE:BIRD_OSPREY:HEART +CREATURE:BIRD_OSPREY:LIVER +CREATURE:BIRD_OSPREY:GUT +CREATURE:BIRD_OSPREY:STOMACH +CREATURE:BIRD_OSPREY:GIZZARD +CREATURE:BIRD_OSPREY:PANCREAS +CREATURE:BIRD_OSPREY:SPLEEN +CREATURE:BIRD_OSPREY:KIDNEY +CREATURE:OSPREY_MAN:MUSCLE +CREATURE:OSPREY_MAN:EYE +CREATURE:OSPREY_MAN:BRAIN +CREATURE:OSPREY_MAN:LUNG +CREATURE:OSPREY_MAN:HEART +CREATURE:OSPREY_MAN:LIVER +CREATURE:OSPREY_MAN:GUT +CREATURE:OSPREY_MAN:STOMACH +CREATURE:OSPREY_MAN:GIZZARD +CREATURE:OSPREY_MAN:PANCREAS +CREATURE:OSPREY_MAN:SPLEEN +CREATURE:OSPREY_MAN:KIDNEY +CREATURE:GIANT_OSPREY:MUSCLE +CREATURE:GIANT_OSPREY:EYE +CREATURE:GIANT_OSPREY:BRAIN +CREATURE:GIANT_OSPREY:LUNG +CREATURE:GIANT_OSPREY:HEART +CREATURE:GIANT_OSPREY:LIVER +CREATURE:GIANT_OSPREY:GUT +CREATURE:GIANT_OSPREY:STOMACH +CREATURE:GIANT_OSPREY:GIZZARD +CREATURE:GIANT_OSPREY:PANCREAS +CREATURE:GIANT_OSPREY:SPLEEN +CREATURE:GIANT_OSPREY:KIDNEY +CREATURE:BIRD_EMU:MUSCLE +CREATURE:BIRD_EMU:EYE +CREATURE:BIRD_EMU:BRAIN +CREATURE:BIRD_EMU:LUNG +CREATURE:BIRD_EMU:HEART +CREATURE:BIRD_EMU:LIVER +CREATURE:BIRD_EMU:GUT +CREATURE:BIRD_EMU:STOMACH +CREATURE:BIRD_EMU:GIZZARD +CREATURE:BIRD_EMU:PANCREAS +CREATURE:BIRD_EMU:SPLEEN +CREATURE:BIRD_EMU:KIDNEY +CREATURE:EMU_MAN:MUSCLE +CREATURE:EMU_MAN:EYE +CREATURE:EMU_MAN:BRAIN +CREATURE:EMU_MAN:LUNG +CREATURE:EMU_MAN:HEART +CREATURE:EMU_MAN:LIVER +CREATURE:EMU_MAN:GUT +CREATURE:EMU_MAN:STOMACH +CREATURE:EMU_MAN:GIZZARD +CREATURE:EMU_MAN:PANCREAS +CREATURE:EMU_MAN:SPLEEN +CREATURE:EMU_MAN:KIDNEY +CREATURE:GIANT_EMU:MUSCLE +CREATURE:GIANT_EMU:EYE +CREATURE:GIANT_EMU:BRAIN +CREATURE:GIANT_EMU:LUNG +CREATURE:GIANT_EMU:HEART +CREATURE:GIANT_EMU:LIVER +CREATURE:GIANT_EMU:GUT +CREATURE:GIANT_EMU:STOMACH +CREATURE:GIANT_EMU:GIZZARD +CREATURE:GIANT_EMU:PANCREAS +CREATURE:GIANT_EMU:SPLEEN +CREATURE:GIANT_EMU:KIDNEY +CREATURE:BIRD_COCKATIEL:MUSCLE +CREATURE:BIRD_COCKATIEL:EYE +CREATURE:BIRD_COCKATIEL:BRAIN +CREATURE:BIRD_COCKATIEL:LUNG +CREATURE:BIRD_COCKATIEL:HEART +CREATURE:BIRD_COCKATIEL:LIVER +CREATURE:BIRD_COCKATIEL:GUT +CREATURE:BIRD_COCKATIEL:STOMACH +CREATURE:BIRD_COCKATIEL:GIZZARD + CREATURE:BIRD_COCKATIEL:PANCREAS +CREATURE:BIRD_COCKATIEL:SPLEEN +CREATURE:BIRD_COCKATIEL:KIDNEY +CREATURE:COCKATIEL_MAN:MUSCLE +CREATURE:COCKATIEL_MAN:EYE +CREATURE:COCKATIEL_MAN:BRAIN +CREATURE:COCKATIEL_MAN:LUNG +CREATURE:COCKATIEL_MAN:HEART +CREATURE:COCKATIEL_MAN:LIVER +CREATURE:COCKATIEL_MAN:GUT +CREATURE:COCKATIEL_MAN:STOMACH +CREATURE:COCKATIEL_MAN:GIZZARD +CREATURE:COCKATIEL_MAN:PANCREAS +CREATURE:COCKATIEL_MAN:SPLEEN +CREATURE:COCKATIEL_MAN:KIDNEY +CREATURE:GIANT_COCKATIEL:MUSCLE +CREATURE:GIANT_COCKATIEL:EYE +CREATURE:GIANT_COCKATIEL:BRAIN +CREATURE:GIANT_COCKATIEL:LUNG +CREATURE:GIANT_COCKATIEL:HEART +CREATURE:GIANT_COCKATIEL:LIVER +CREATURE:GIANT_COCKATIEL:GUT + CREATURE:GIANT_COCKATIEL:STOMACH + CREATURE:GIANT_COCKATIEL:GIZZARD +!CREATURE:GIANT_COCKATIEL:PANCREAS +CREATURE:GIANT_COCKATIEL:SPLEEN +CREATURE:GIANT_COCKATIEL:KIDNEY +)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:MUSCLE +&CREATURE:BIRD_LOVEBIRD_PEACH-FACED:EYE +(CREATURE:BIRD_LOVEBIRD_PEACH-FACED:BRAIN +'CREATURE:BIRD_LOVEBIRD_PEACH-FACED:LUNG +(CREATURE:BIRD_LOVEBIRD_PEACH-FACED:HEART +(CREATURE:BIRD_LOVEBIRD_PEACH-FACED:LIVER +&CREATURE:BIRD_LOVEBIRD_PEACH-FACED:GUT +*CREATURE:BIRD_LOVEBIRD_PEACH-FACED:STOMACH +*CREATURE:BIRD_LOVEBIRD_PEACH-FACED:GIZZARD ++CREATURE:BIRD_LOVEBIRD_PEACH-FACED:PANCREAS +)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:SPLEEN +)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:KIDNEY +(CREATURE:PEACH-FACED_LOVEBIRD_MAN:MUSCLE +%CREATURE:PEACH-FACED_LOVEBIRD_MAN:EYE +'CREATURE:PEACH-FACED_LOVEBIRD_MAN:BRAIN +&CREATURE:PEACH-FACED_LOVEBIRD_MAN:LUNG +'CREATURE:PEACH-FACED_LOVEBIRD_MAN:HEART +'CREATURE:PEACH-FACED_LOVEBIRD_MAN:LIVER +%CREATURE:PEACH-FACED_LOVEBIRD_MAN:GUT +)CREATURE:PEACH-FACED_LOVEBIRD_MAN:STOMACH +)CREATURE:PEACH-FACED_LOVEBIRD_MAN:GIZZARD +*CREATURE:PEACH-FACED_LOVEBIRD_MAN:PANCREAS +(CREATURE:PEACH-FACED_LOVEBIRD_MAN:SPLEEN +(CREATURE:PEACH-FACED_LOVEBIRD_MAN:KIDNEY +*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:MUSCLE +'CREATURE:GIANT_PEACH-FACED_LOVEBIRD:EYE +)CREATURE:GIANT_PEACH-FACED_LOVEBIRD:BRAIN +(CREATURE:GIANT_PEACH-FACED_LOVEBIRD:LUNG +)CREATURE:GIANT_PEACH-FACED_LOVEBIRD:HEART +)CREATURE:GIANT_PEACH-FACED_LOVEBIRD:LIVER +'CREATURE:GIANT_PEACH-FACED_LOVEBIRD:GUT ++CREATURE:GIANT_PEACH-FACED_LOVEBIRD:STOMACH ++CREATURE:GIANT_PEACH-FACED_LOVEBIRD:GIZZARD +,CREATURE:GIANT_PEACH-FACED_LOVEBIRD:PANCREAS +*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:SPLEEN +*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:KIDNEY +CREATURE:BIRD_MAGPIE:MUSCLE +CREATURE:BIRD_MAGPIE:EYE +CREATURE:BIRD_MAGPIE:BRAIN +CREATURE:BIRD_MAGPIE:LUNG +CREATURE:BIRD_MAGPIE:HEART +CREATURE:BIRD_MAGPIE:LIVER +CREATURE:BIRD_MAGPIE:GUT +CREATURE:BIRD_MAGPIE:STOMACH +CREATURE:BIRD_MAGPIE:GIZZARD +CREATURE:BIRD_MAGPIE:PANCREAS +CREATURE:BIRD_MAGPIE:SPLEEN +CREATURE:BIRD_MAGPIE:KIDNEY +CREATURE:MAGPIE_MAN:MUSCLE +CREATURE:MAGPIE_MAN:EYE +CREATURE:MAGPIE_MAN:BRAIN +CREATURE:MAGPIE_MAN:LUNG +CREATURE:MAGPIE_MAN:HEART +CREATURE:MAGPIE_MAN:LIVER +CREATURE:MAGPIE_MAN:GUT +CREATURE:MAGPIE_MAN:STOMACH +CREATURE:MAGPIE_MAN:GIZZARD +CREATURE:MAGPIE_MAN:PANCREAS +CREATURE:MAGPIE_MAN:SPLEEN +CREATURE:MAGPIE_MAN:KIDNEY +CREATURE:GIANT_MAGPIE:MUSCLE +CREATURE:GIANT_MAGPIE:EYE +CREATURE:GIANT_MAGPIE:BRAIN +CREATURE:GIANT_MAGPIE:LUNG +CREATURE:GIANT_MAGPIE:HEART +CREATURE:GIANT_MAGPIE:LIVER +CREATURE:GIANT_MAGPIE:GUT +CREATURE:GIANT_MAGPIE:STOMACH +CREATURE:GIANT_MAGPIE:GIZZARD +CREATURE:GIANT_MAGPIE:PANCREAS +CREATURE:GIANT_MAGPIE:SPLEEN +CREATURE:GIANT_MAGPIE:KIDNEY +CREATURE:BIRD_KESTREL:MUSCLE +CREATURE:BIRD_KESTREL:EYE +CREATURE:BIRD_KESTREL:BRAIN +CREATURE:BIRD_KESTREL:LUNG +CREATURE:BIRD_KESTREL:HEART +CREATURE:BIRD_KESTREL:LIVER +CREATURE:BIRD_KESTREL:GUT +CREATURE:BIRD_KESTREL:STOMACH +CREATURE:BIRD_KESTREL:GIZZARD +CREATURE:BIRD_KESTREL:PANCREAS +CREATURE:BIRD_KESTREL:SPLEEN +CREATURE:BIRD_KESTREL:KIDNEY +CREATURE:KESTREL_MAN:MUSCLE +CREATURE:KESTREL_MAN:EYE +CREATURE:KESTREL_MAN:BRAIN +CREATURE:KESTREL_MAN:LUNG +CREATURE:KESTREL_MAN:HEART +CREATURE:KESTREL_MAN:LIVER +CREATURE:KESTREL_MAN:GUT +CREATURE:KESTREL_MAN:STOMACH +CREATURE:KESTREL_MAN:GIZZARD +CREATURE:KESTREL_MAN:PANCREAS +CREATURE:KESTREL_MAN:SPLEEN +CREATURE:KESTREL_MAN:KIDNEY +CREATURE:GIANT_KESTREL:MUSCLE +CREATURE:GIANT_KESTREL:EYE +CREATURE:GIANT_KESTREL:BRAIN +CREATURE:GIANT_KESTREL:LUNG +CREATURE:GIANT_KESTREL:HEART +CREATURE:GIANT_KESTREL:LIVER +CREATURE:GIANT_KESTREL:GUT +CREATURE:GIANT_KESTREL:STOMACH +CREATURE:GIANT_KESTREL:GIZZARD +CREATURE:GIANT_KESTREL:PANCREAS +CREATURE:GIANT_KESTREL:SPLEEN +CREATURE:GIANT_KESTREL:KIDNEY +CREATURE:BIRD_ALBATROSS:MUSCLE +CREATURE:BIRD_ALBATROSS:EYE +CREATURE:BIRD_ALBATROSS:BRAIN +CREATURE:BIRD_ALBATROSS:LUNG +CREATURE:BIRD_ALBATROSS:HEART +CREATURE:BIRD_ALBATROSS:LIVER +CREATURE:BIRD_ALBATROSS:GUT +CREATURE:BIRD_ALBATROSS:STOMACH +CREATURE:BIRD_ALBATROSS:GIZZARD + CREATURE:BIRD_ALBATROSS:PANCREAS +CREATURE:BIRD_ALBATROSS:SPLEEN +CREATURE:BIRD_ALBATROSS:KIDNEY +CREATURE:ALBATROSS_MAN:MUSCLE +CREATURE:ALBATROSS_MAN:EYE +CREATURE:ALBATROSS_MAN:BRAIN +CREATURE:ALBATROSS_MAN:LUNG +CREATURE:ALBATROSS_MAN:HEART +CREATURE:ALBATROSS_MAN:LIVER +CREATURE:ALBATROSS_MAN:GUT +CREATURE:ALBATROSS_MAN:STOMACH +CREATURE:ALBATROSS_MAN:GIZZARD +CREATURE:ALBATROSS_MAN:PANCREAS +CREATURE:ALBATROSS_MAN:SPLEEN +CREATURE:ALBATROSS_MAN:KIDNEY +CREATURE:GIANT_ALBATROSS:MUSCLE +CREATURE:GIANT_ALBATROSS:EYE +CREATURE:GIANT_ALBATROSS:BRAIN +CREATURE:GIANT_ALBATROSS:LUNG +CREATURE:GIANT_ALBATROSS:HEART +CREATURE:GIANT_ALBATROSS:LIVER +CREATURE:GIANT_ALBATROSS:GUT + CREATURE:GIANT_ALBATROSS:STOMACH + CREATURE:GIANT_ALBATROSS:GIZZARD +!CREATURE:GIANT_ALBATROSS:PANCREAS +CREATURE:GIANT_ALBATROSS:SPLEEN +CREATURE:GIANT_ALBATROSS:KIDNEY +%CREATURE:BIRD_OWL_GREAT_HORNED:MUSCLE +"CREATURE:BIRD_OWL_GREAT_HORNED:EYE +$CREATURE:BIRD_OWL_GREAT_HORNED:BRAIN +#CREATURE:BIRD_OWL_GREAT_HORNED:LUNG +$CREATURE:BIRD_OWL_GREAT_HORNED:HEART +$CREATURE:BIRD_OWL_GREAT_HORNED:LIVER +"CREATURE:BIRD_OWL_GREAT_HORNED:GUT +&CREATURE:BIRD_OWL_GREAT_HORNED:STOMACH +&CREATURE:BIRD_OWL_GREAT_HORNED:GIZZARD +'CREATURE:BIRD_OWL_GREAT_HORNED:PANCREAS +%CREATURE:BIRD_OWL_GREAT_HORNED:SPLEEN +%CREATURE:BIRD_OWL_GREAT_HORNED:KIDNEY +$CREATURE:GREAT_HORNED_OWL_MAN:MUSCLE +!CREATURE:GREAT_HORNED_OWL_MAN:EYE +#CREATURE:GREAT_HORNED_OWL_MAN:BRAIN +"CREATURE:GREAT_HORNED_OWL_MAN:LUNG +#CREATURE:GREAT_HORNED_OWL_MAN:HEART +#CREATURE:GREAT_HORNED_OWL_MAN:LIVER +!CREATURE:GREAT_HORNED_OWL_MAN:GUT +%CREATURE:GREAT_HORNED_OWL_MAN:STOMACH +%CREATURE:GREAT_HORNED_OWL_MAN:GIZZARD +&CREATURE:GREAT_HORNED_OWL_MAN:PANCREAS +$CREATURE:GREAT_HORNED_OWL_MAN:SPLEEN +$CREATURE:GREAT_HORNED_OWL_MAN:KIDNEY +&CREATURE:GIANT_GREAT_HORNED_OWL:MUSCLE +#CREATURE:GIANT_GREAT_HORNED_OWL:EYE +%CREATURE:GIANT_GREAT_HORNED_OWL:BRAIN +$CREATURE:GIANT_GREAT_HORNED_OWL:LUNG +%CREATURE:GIANT_GREAT_HORNED_OWL:HEART +%CREATURE:GIANT_GREAT_HORNED_OWL:LIVER +#CREATURE:GIANT_GREAT_HORNED_OWL:GUT +'CREATURE:GIANT_GREAT_HORNED_OWL:STOMACH +'CREATURE:GIANT_GREAT_HORNED_OWL:GIZZARD +(CREATURE:GIANT_GREAT_HORNED_OWL:PANCREAS +&CREATURE:GIANT_GREAT_HORNED_OWL:SPLEEN +&CREATURE:GIANT_GREAT_HORNED_OWL:KIDNEY +CREATURE:BIRD_EAGLE:MUSCLE +CREATURE:BIRD_EAGLE:EYE +CREATURE:BIRD_EAGLE:BRAIN +CREATURE:BIRD_EAGLE:LUNG +CREATURE:BIRD_EAGLE:HEART +CREATURE:BIRD_EAGLE:LIVER +CREATURE:BIRD_EAGLE:GUT +CREATURE:BIRD_EAGLE:STOMACH +CREATURE:BIRD_EAGLE:GIZZARD +CREATURE:BIRD_EAGLE:PANCREAS +CREATURE:BIRD_EAGLE:SPLEEN +CREATURE:BIRD_EAGLE:KIDNEY +CREATURE:EAGLE_MAN:MUSCLE +CREATURE:EAGLE_MAN:EYE +CREATURE:EAGLE_MAN:BRAIN +CREATURE:EAGLE_MAN:LUNG +CREATURE:EAGLE_MAN:HEART +CREATURE:EAGLE_MAN:LIVER +CREATURE:EAGLE_MAN:GUT +CREATURE:EAGLE_MAN:STOMACH +CREATURE:EAGLE_MAN:GIZZARD +CREATURE:EAGLE_MAN:PANCREAS +CREATURE:EAGLE_MAN:SPLEEN +CREATURE:EAGLE_MAN:KIDNEY +CREATURE:GIANT_EAGLE:MUSCLE +CREATURE:GIANT_EAGLE:EYE +CREATURE:GIANT_EAGLE:BRAIN +CREATURE:GIANT_EAGLE:LUNG +CREATURE:GIANT_EAGLE:HEART +CREATURE:GIANT_EAGLE:LIVER +CREATURE:GIANT_EAGLE:GUT +CREATURE:GIANT_EAGLE:STOMACH +CREATURE:GIANT_EAGLE:GIZZARD +CREATURE:GIANT_EAGLE:PANCREAS +CREATURE:GIANT_EAGLE:SPLEEN +CREATURE:GIANT_EAGLE:KIDNEY +CREATURE:BIRD_HORNBILL:MUSCLE +CREATURE:BIRD_HORNBILL:EYE +CREATURE:BIRD_HORNBILL:BRAIN +CREATURE:BIRD_HORNBILL:LUNG +CREATURE:BIRD_HORNBILL:HEART +CREATURE:BIRD_HORNBILL:LIVER +CREATURE:BIRD_HORNBILL:GUT +CREATURE:BIRD_HORNBILL:STOMACH +CREATURE:BIRD_HORNBILL:GIZZARD +CREATURE:BIRD_HORNBILL:PANCREAS +CREATURE:BIRD_HORNBILL:SPLEEN +CREATURE:BIRD_HORNBILL:KIDNEY +CREATURE:HORNBILL_MAN:MUSCLE +CREATURE:HORNBILL_MAN:EYE +CREATURE:HORNBILL_MAN:BRAIN +CREATURE:HORNBILL_MAN:LUNG +CREATURE:HORNBILL_MAN:HEART +CREATURE:HORNBILL_MAN:LIVER +CREATURE:HORNBILL_MAN:GUT +CREATURE:HORNBILL_MAN:STOMACH +CREATURE:HORNBILL_MAN:GIZZARD +CREATURE:HORNBILL_MAN:PANCREAS +CREATURE:HORNBILL_MAN:SPLEEN +CREATURE:HORNBILL_MAN:KIDNEY +CREATURE:GIANT_HORNBILL:MUSCLE +CREATURE:GIANT_HORNBILL:EYE +CREATURE:GIANT_HORNBILL:BRAIN +CREATURE:GIANT_HORNBILL:LUNG +CREATURE:GIANT_HORNBILL:HEART +CREATURE:GIANT_HORNBILL:LIVER +CREATURE:GIANT_HORNBILL:GUT +CREATURE:GIANT_HORNBILL:STOMACH +CREATURE:GIANT_HORNBILL:GIZZARD + CREATURE:GIANT_HORNBILL:PANCREAS +CREATURE:GIANT_HORNBILL:SPLEEN +CREATURE:GIANT_HORNBILL:KIDNEY +$CREATURE:BIRD_LOVEBIRD_MASKED:MUSCLE +!CREATURE:BIRD_LOVEBIRD_MASKED:EYE +#CREATURE:BIRD_LOVEBIRD_MASKED:BRAIN +"CREATURE:BIRD_LOVEBIRD_MASKED:LUNG +#CREATURE:BIRD_LOVEBIRD_MASKED:HEART +#CREATURE:BIRD_LOVEBIRD_MASKED:LIVER +!CREATURE:BIRD_LOVEBIRD_MASKED:GUT +%CREATURE:BIRD_LOVEBIRD_MASKED:STOMACH +%CREATURE:BIRD_LOVEBIRD_MASKED:GIZZARD +&CREATURE:BIRD_LOVEBIRD_MASKED:PANCREAS +$CREATURE:BIRD_LOVEBIRD_MASKED:SPLEEN +$CREATURE:BIRD_LOVEBIRD_MASKED:KIDNEY +#CREATURE:MASKED_LOVEBIRD_MAN:MUSCLE + CREATURE:MASKED_LOVEBIRD_MAN:EYE +"CREATURE:MASKED_LOVEBIRD_MAN:BRAIN +!CREATURE:MASKED_LOVEBIRD_MAN:LUNG +"CREATURE:MASKED_LOVEBIRD_MAN:HEART +"CREATURE:MASKED_LOVEBIRD_MAN:LIVER + CREATURE:MASKED_LOVEBIRD_MAN:GUT +$CREATURE:MASKED_LOVEBIRD_MAN:STOMACH +$CREATURE:MASKED_LOVEBIRD_MAN:GIZZARD +%CREATURE:MASKED_LOVEBIRD_MAN:PANCREAS +#CREATURE:MASKED_LOVEBIRD_MAN:SPLEEN +#CREATURE:MASKED_LOVEBIRD_MAN:KIDNEY +%CREATURE:GIANT_MASKED_LOVEBIRD:MUSCLE +"CREATURE:GIANT_MASKED_LOVEBIRD:EYE +$CREATURE:GIANT_MASKED_LOVEBIRD:BRAIN +#CREATURE:GIANT_MASKED_LOVEBIRD:LUNG +$CREATURE:GIANT_MASKED_LOVEBIRD:HEART +$CREATURE:GIANT_MASKED_LOVEBIRD:LIVER +"CREATURE:GIANT_MASKED_LOVEBIRD:GUT +&CREATURE:GIANT_MASKED_LOVEBIRD:STOMACH +&CREATURE:GIANT_MASKED_LOVEBIRD:GIZZARD +'CREATURE:GIANT_MASKED_LOVEBIRD:PANCREAS +%CREATURE:GIANT_MASKED_LOVEBIRD:SPLEEN +%CREATURE:GIANT_MASKED_LOVEBIRD:KIDNEY +CREATURE:BIRD_BUSHTIT:MUSCLE +CREATURE:BIRD_BUSHTIT:EYE +CREATURE:BIRD_BUSHTIT:BRAIN +CREATURE:BIRD_BUSHTIT:LUNG +CREATURE:BIRD_BUSHTIT:HEART +CREATURE:BIRD_BUSHTIT:LIVER +CREATURE:BIRD_BUSHTIT:GUT +CREATURE:BIRD_BUSHTIT:STOMACH +CREATURE:BIRD_BUSHTIT:GIZZARD +CREATURE:BIRD_BUSHTIT:PANCREAS +CREATURE:BIRD_BUSHTIT:SPLEEN +CREATURE:BIRD_BUSHTIT:KIDNEY +CREATURE:BUSHTIT_MAN:MUSCLE +CREATURE:BUSHTIT_MAN:EYE +CREATURE:BUSHTIT_MAN:BRAIN +CREATURE:BUSHTIT_MAN:LUNG +CREATURE:BUSHTIT_MAN:HEART +CREATURE:BUSHTIT_MAN:LIVER +CREATURE:BUSHTIT_MAN:GUT +CREATURE:BUSHTIT_MAN:STOMACH +CREATURE:BUSHTIT_MAN:GIZZARD +CREATURE:BUSHTIT_MAN:PANCREAS +CREATURE:BUSHTIT_MAN:SPLEEN +CREATURE:BUSHTIT_MAN:KIDNEY +CREATURE:GIANT_BUSHTIT:MUSCLE +CREATURE:GIANT_BUSHTIT:EYE +CREATURE:GIANT_BUSHTIT:BRAIN +CREATURE:GIANT_BUSHTIT:LUNG +CREATURE:GIANT_BUSHTIT:HEART +CREATURE:GIANT_BUSHTIT:LIVER +CREATURE:GIANT_BUSHTIT:GUT +CREATURE:GIANT_BUSHTIT:STOMACH +CREATURE:GIANT_BUSHTIT:GIZZARD +CREATURE:GIANT_BUSHTIT:PANCREAS +CREATURE:GIANT_BUSHTIT:SPLEEN +CREATURE:GIANT_BUSHTIT:KIDNEY +CREATURE:DAMSELFLY:MUSCLE +CREATURE:DAMSELFLY:EYE +CREATURE:DAMSELFLY:BRAIN +CREATURE:DAMSELFLY:LUNG +CREATURE:DAMSELFLY:HEART +CREATURE:DAMSELFLY:LIVER +CREATURE:DAMSELFLY:GUT +CREATURE:DAMSELFLY:STOMACH +CREATURE:DAMSELFLY:GIZZARD +CREATURE:DAMSELFLY:PANCREAS +CREATURE:DAMSELFLY:SPLEEN +CREATURE:DAMSELFLY:KIDNEY +CREATURE:DAMSELFLY_MAN:MUSCLE +CREATURE:DAMSELFLY_MAN:EYE +CREATURE:DAMSELFLY_MAN:BRAIN +CREATURE:DAMSELFLY_MAN:LUNG +CREATURE:DAMSELFLY_MAN:HEART +CREATURE:DAMSELFLY_MAN:LIVER +CREATURE:DAMSELFLY_MAN:GUT +CREATURE:DAMSELFLY_MAN:STOMACH +CREATURE:DAMSELFLY_MAN:GIZZARD +CREATURE:DAMSELFLY_MAN:PANCREAS +CREATURE:DAMSELFLY_MAN:SPLEEN +CREATURE:DAMSELFLY_MAN:KIDNEY +CREATURE:GIANT_DAMSELFLY:MUSCLE +CREATURE:GIANT_DAMSELFLY:EYE +CREATURE:GIANT_DAMSELFLY:BRAIN +CREATURE:GIANT_DAMSELFLY:LUNG +CREATURE:GIANT_DAMSELFLY:HEART +CREATURE:GIANT_DAMSELFLY:LIVER +CREATURE:GIANT_DAMSELFLY:GUT + CREATURE:GIANT_DAMSELFLY:STOMACH + CREATURE:GIANT_DAMSELFLY:GIZZARD +!CREATURE:GIANT_DAMSELFLY:PANCREAS +CREATURE:GIANT_DAMSELFLY:SPLEEN +CREATURE:GIANT_DAMSELFLY:KIDNEY +CREATURE:MOTH:MUSCLE +CREATURE:MOTH:EYE +CREATURE:MOTH:BRAIN +CREATURE:MOTH:LUNG +CREATURE:MOTH:HEART +CREATURE:MOTH:LIVER +CREATURE:MOTH:GUT +CREATURE:MOTH:STOMACH +CREATURE:MOTH:GIZZARD +CREATURE:MOTH:PANCREAS +CREATURE:MOTH:SPLEEN +CREATURE:MOTH:KIDNEY +CREATURE:MOTH_MAN:MUSCLE +CREATURE:MOTH_MAN:EYE +CREATURE:MOTH_MAN:BRAIN +CREATURE:MOTH_MAN:LUNG +CREATURE:MOTH_MAN:HEART +CREATURE:MOTH_MAN:LIVER +CREATURE:MOTH_MAN:GUT +CREATURE:MOTH_MAN:STOMACH +CREATURE:MOTH_MAN:GIZZARD +CREATURE:MOTH_MAN:PANCREAS +CREATURE:MOTH_MAN:SPLEEN +CREATURE:MOTH_MAN:KIDNEY +CREATURE:GIANT_MOTH:MUSCLE +CREATURE:GIANT_MOTH:EYE +CREATURE:GIANT_MOTH:BRAIN +CREATURE:GIANT_MOTH:LUNG +CREATURE:GIANT_MOTH:HEART +CREATURE:GIANT_MOTH:LIVER +CREATURE:GIANT_MOTH:GUT +CREATURE:GIANT_MOTH:STOMACH +CREATURE:GIANT_MOTH:GIZZARD +CREATURE:GIANT_MOTH:PANCREAS +CREATURE:GIANT_MOTH:SPLEEN +CREATURE:GIANT_MOTH:KIDNEY +CREATURE:GRASSHOPPER:MUSCLE +CREATURE:GRASSHOPPER:EYE +CREATURE:GRASSHOPPER:BRAIN +CREATURE:GRASSHOPPER:LUNG +CREATURE:GRASSHOPPER:HEART +CREATURE:GRASSHOPPER:LIVER +CREATURE:GRASSHOPPER:GUT +CREATURE:GRASSHOPPER:STOMACH +CREATURE:GRASSHOPPER:GIZZARD +CREATURE:GRASSHOPPER:PANCREAS +CREATURE:GRASSHOPPER:SPLEEN +CREATURE:GRASSHOPPER:KIDNEY +CREATURE:GRASSHOPPER_MAN:MUSCLE +CREATURE:GRASSHOPPER_MAN:EYE +CREATURE:GRASSHOPPER_MAN:BRAIN +CREATURE:GRASSHOPPER_MAN:LUNG +CREATURE:GRASSHOPPER_MAN:HEART +CREATURE:GRASSHOPPER_MAN:LIVER +CREATURE:GRASSHOPPER_MAN:GUT + CREATURE:GRASSHOPPER_MAN:STOMACH + CREATURE:GRASSHOPPER_MAN:GIZZARD +!CREATURE:GRASSHOPPER_MAN:PANCREAS +CREATURE:GRASSHOPPER_MAN:SPLEEN +CREATURE:GRASSHOPPER_MAN:KIDNEY +!CREATURE:GIANT_GRASSHOPPER:MUSCLE +CREATURE:GIANT_GRASSHOPPER:EYE + CREATURE:GIANT_GRASSHOPPER:BRAIN +CREATURE:GIANT_GRASSHOPPER:LUNG + CREATURE:GIANT_GRASSHOPPER:HEART + CREATURE:GIANT_GRASSHOPPER:LIVER +CREATURE:GIANT_GRASSHOPPER:GUT +"CREATURE:GIANT_GRASSHOPPER:STOMACH +"CREATURE:GIANT_GRASSHOPPER:GIZZARD +#CREATURE:GIANT_GRASSHOPPER:PANCREAS +!CREATURE:GIANT_GRASSHOPPER:SPLEEN +!CREATURE:GIANT_GRASSHOPPER:KIDNEY +CREATURE:BARK_SCORPION:MUSCLE +CREATURE:BARK_SCORPION:EYE +CREATURE:BARK_SCORPION:BRAIN +CREATURE:BARK_SCORPION:LUNG +CREATURE:BARK_SCORPION:HEART +CREATURE:BARK_SCORPION:LIVER +CREATURE:BARK_SCORPION:GUT +CREATURE:BARK_SCORPION:STOMACH +CREATURE:BARK_SCORPION:GIZZARD +CREATURE:BARK_SCORPION:PANCREAS +CREATURE:BARK_SCORPION:SPLEEN +CREATURE:BARK_SCORPION:KIDNEY +!CREATURE:BARK_SCORPION_MAN:MUSCLE +CREATURE:BARK_SCORPION_MAN:EYE + CREATURE:BARK_SCORPION_MAN:BRAIN +CREATURE:BARK_SCORPION_MAN:LUNG + CREATURE:BARK_SCORPION_MAN:HEART + CREATURE:BARK_SCORPION_MAN:LIVER +CREATURE:BARK_SCORPION_MAN:GUT +"CREATURE:BARK_SCORPION_MAN:STOMACH +"CREATURE:BARK_SCORPION_MAN:GIZZARD +#CREATURE:BARK_SCORPION_MAN:PANCREAS +!CREATURE:BARK_SCORPION_MAN:SPLEEN +!CREATURE:BARK_SCORPION_MAN:KIDNEY +#CREATURE:GIANT_BARK_SCORPION:MUSCLE + CREATURE:GIANT_BARK_SCORPION:EYE +"CREATURE:GIANT_BARK_SCORPION:BRAIN +!CREATURE:GIANT_BARK_SCORPION:LUNG +"CREATURE:GIANT_BARK_SCORPION:HEART +"CREATURE:GIANT_BARK_SCORPION:LIVER + CREATURE:GIANT_BARK_SCORPION:GUT +$CREATURE:GIANT_BARK_SCORPION:STOMACH +$CREATURE:GIANT_BARK_SCORPION:GIZZARD +%CREATURE:GIANT_BARK_SCORPION:PANCREAS +#CREATURE:GIANT_BARK_SCORPION:SPLEEN +#CREATURE:GIANT_BARK_SCORPION:KIDNEY +CREATURE:MANTIS:MUSCLE +CREATURE:MANTIS:EYE +CREATURE:MANTIS:BRAIN +CREATURE:MANTIS:LUNG +CREATURE:MANTIS:HEART +CREATURE:MANTIS:LIVER +CREATURE:MANTIS:GUT +CREATURE:MANTIS:STOMACH +CREATURE:MANTIS:GIZZARD +CREATURE:MANTIS:PANCREAS +CREATURE:MANTIS:SPLEEN +CREATURE:MANTIS:KIDNEY +CREATURE:MANTIS_MAN:MUSCLE +CREATURE:MANTIS_MAN:EYE +CREATURE:MANTIS_MAN:BRAIN +CREATURE:MANTIS_MAN:LUNG +CREATURE:MANTIS_MAN:HEART +CREATURE:MANTIS_MAN:LIVER +CREATURE:MANTIS_MAN:GUT +CREATURE:MANTIS_MAN:STOMACH +CREATURE:MANTIS_MAN:GIZZARD +CREATURE:MANTIS_MAN:PANCREAS +CREATURE:MANTIS_MAN:SPLEEN +CREATURE:MANTIS_MAN:KIDNEY +CREATURE:GIANT_MANTIS:MUSCLE +CREATURE:GIANT_MANTIS:EYE +CREATURE:GIANT_MANTIS:BRAIN +CREATURE:GIANT_MANTIS:LUNG +CREATURE:GIANT_MANTIS:HEART +CREATURE:GIANT_MANTIS:LIVER +CREATURE:GIANT_MANTIS:GUT +CREATURE:GIANT_MANTIS:STOMACH +CREATURE:GIANT_MANTIS:GIZZARD +CREATURE:GIANT_MANTIS:PANCREAS +CREATURE:GIANT_MANTIS:SPLEEN +CREATURE:GIANT_MANTIS:KIDNEY +CREATURE:TICK:MUSCLE +CREATURE:TICK:EYE +CREATURE:TICK:BRAIN +CREATURE:TICK:LUNG +CREATURE:TICK:HEART +CREATURE:TICK:LIVER +CREATURE:TICK:GUT +CREATURE:TICK:STOMACH +CREATURE:TICK:GIZZARD +CREATURE:TICK:PANCREAS +CREATURE:TICK:SPLEEN +CREATURE:TICK:KIDNEY +CREATURE:TICK_MAN:MUSCLE +CREATURE:TICK_MAN:EYE +CREATURE:TICK_MAN:BRAIN +CREATURE:TICK_MAN:LUNG +CREATURE:TICK_MAN:HEART +CREATURE:TICK_MAN:LIVER +CREATURE:TICK_MAN:GUT +CREATURE:TICK_MAN:STOMACH +CREATURE:TICK_MAN:GIZZARD +CREATURE:TICK_MAN:PANCREAS +CREATURE:TICK_MAN:SPLEEN +CREATURE:TICK_MAN:KIDNEY +CREATURE:GIANT_TICK:MUSCLE +CREATURE:GIANT_TICK:EYE +CREATURE:GIANT_TICK:BRAIN +CREATURE:GIANT_TICK:LUNG +CREATURE:GIANT_TICK:HEART +CREATURE:GIANT_TICK:LIVER +CREATURE:GIANT_TICK:GUT +CREATURE:GIANT_TICK:STOMACH +CREATURE:GIANT_TICK:GIZZARD +CREATURE:GIANT_TICK:PANCREAS +CREATURE:GIANT_TICK:SPLEEN +CREATURE:GIANT_TICK:KIDNEY +CREATURE:LOUSE:MUSCLE +CREATURE:LOUSE:EYE +CREATURE:LOUSE:BRAIN +CREATURE:LOUSE:LUNG +CREATURE:LOUSE:HEART +CREATURE:LOUSE:LIVER +CREATURE:LOUSE:GUT +CREATURE:LOUSE:STOMACH +CREATURE:LOUSE:GIZZARD +CREATURE:LOUSE:PANCREAS +CREATURE:LOUSE:SPLEEN +CREATURE:LOUSE:KIDNEY +CREATURE:LOUSE_MAN:MUSCLE +CREATURE:LOUSE_MAN:EYE +CREATURE:LOUSE_MAN:BRAIN +CREATURE:LOUSE_MAN:LUNG +CREATURE:LOUSE_MAN:HEART +CREATURE:LOUSE_MAN:LIVER +CREATURE:LOUSE_MAN:GUT +CREATURE:LOUSE_MAN:STOMACH +CREATURE:LOUSE_MAN:GIZZARD +CREATURE:LOUSE_MAN:PANCREAS +CREATURE:LOUSE_MAN:SPLEEN +CREATURE:LOUSE_MAN:KIDNEY +CREATURE:GIANT_LOUSE:MUSCLE +CREATURE:GIANT_LOUSE:EYE +CREATURE:GIANT_LOUSE:BRAIN +CREATURE:GIANT_LOUSE:LUNG +CREATURE:GIANT_LOUSE:HEART +CREATURE:GIANT_LOUSE:LIVER +CREATURE:GIANT_LOUSE:GUT +CREATURE:GIANT_LOUSE:STOMACH +CREATURE:GIANT_LOUSE:GIZZARD +CREATURE:GIANT_LOUSE:PANCREAS +CREATURE:GIANT_LOUSE:SPLEEN +CREATURE:GIANT_LOUSE:KIDNEY +CREATURE:THRIPS:MUSCLE +CREATURE:THRIPS:EYE +CREATURE:THRIPS:BRAIN +CREATURE:THRIPS:LUNG +CREATURE:THRIPS:HEART +CREATURE:THRIPS:LIVER +CREATURE:THRIPS:GUT +CREATURE:THRIPS:STOMACH +CREATURE:THRIPS:GIZZARD +CREATURE:THRIPS:PANCREAS +CREATURE:THRIPS:SPLEEN +CREATURE:THRIPS:KIDNEY +CREATURE:THRIPS_MAN:MUSCLE +CREATURE:THRIPS_MAN:EYE +CREATURE:THRIPS_MAN:BRAIN +CREATURE:THRIPS_MAN:LUNG +CREATURE:THRIPS_MAN:HEART +CREATURE:THRIPS_MAN:LIVER +CREATURE:THRIPS_MAN:GUT +CREATURE:THRIPS_MAN:STOMACH +CREATURE:THRIPS_MAN:GIZZARD +CREATURE:THRIPS_MAN:PANCREAS +CREATURE:THRIPS_MAN:SPLEEN +CREATURE:THRIPS_MAN:KIDNEY +CREATURE:GIANT_THRIPS:MUSCLE +CREATURE:GIANT_THRIPS:EYE +CREATURE:GIANT_THRIPS:BRAIN +CREATURE:GIANT_THRIPS:LUNG +CREATURE:GIANT_THRIPS:HEART +CREATURE:GIANT_THRIPS:LIVER +CREATURE:GIANT_THRIPS:GUT +CREATURE:GIANT_THRIPS:STOMACH +CREATURE:GIANT_THRIPS:GIZZARD +CREATURE:GIANT_THRIPS:PANCREAS +CREATURE:GIANT_THRIPS:SPLEEN +CREATURE:GIANT_THRIPS:KIDNEY +CREATURE:SLUG:MUSCLE +CREATURE:SLUG:EYE +CREATURE:SLUG:BRAIN +CREATURE:SLUG:LUNG +CREATURE:SLUG:HEART +CREATURE:SLUG:LIVER +CREATURE:SLUG:GUT +CREATURE:SLUG:STOMACH +CREATURE:SLUG:GIZZARD +CREATURE:SLUG:PANCREAS +CREATURE:SLUG:SPLEEN +CREATURE:SLUG:KIDNEY +CREATURE:SLUG_MAN:MUSCLE +CREATURE:SLUG_MAN:EYE +CREATURE:SLUG_MAN:BRAIN +CREATURE:SLUG_MAN:LUNG +CREATURE:SLUG_MAN:HEART +CREATURE:SLUG_MAN:LIVER +CREATURE:SLUG_MAN:GUT +CREATURE:SLUG_MAN:STOMACH +CREATURE:SLUG_MAN:GIZZARD +CREATURE:SLUG_MAN:PANCREAS +CREATURE:SLUG_MAN:SPLEEN +CREATURE:SLUG_MAN:KIDNEY +CREATURE:GIANT_SLUG:MUSCLE +CREATURE:GIANT_SLUG:EYE +CREATURE:GIANT_SLUG:BRAIN +CREATURE:GIANT_SLUG:LUNG +CREATURE:GIANT_SLUG:HEART +CREATURE:GIANT_SLUG:LIVER +CREATURE:GIANT_SLUG:GUT +CREATURE:GIANT_SLUG:STOMACH +CREATURE:GIANT_SLUG:GIZZARD +CREATURE:GIANT_SLUG:PANCREAS +CREATURE:GIANT_SLUG:SPLEEN +CREATURE:GIANT_SLUG:KIDNEY +CREATURE:MOSQUITO:MUSCLE +CREATURE:MOSQUITO:EYE +CREATURE:MOSQUITO:BRAIN +CREATURE:MOSQUITO:LUNG +CREATURE:MOSQUITO:HEART +CREATURE:MOSQUITO:LIVER +CREATURE:MOSQUITO:GUT +CREATURE:MOSQUITO:STOMACH +CREATURE:MOSQUITO:GIZZARD +CREATURE:MOSQUITO:PANCREAS +CREATURE:MOSQUITO:SPLEEN +CREATURE:MOSQUITO:KIDNEY +CREATURE:MOSQUITO_MAN:MUSCLE +CREATURE:MOSQUITO_MAN:EYE +CREATURE:MOSQUITO_MAN:BRAIN +CREATURE:MOSQUITO_MAN:LUNG +CREATURE:MOSQUITO_MAN:HEART +CREATURE:MOSQUITO_MAN:LIVER +CREATURE:MOSQUITO_MAN:GUT +CREATURE:MOSQUITO_MAN:STOMACH +CREATURE:MOSQUITO_MAN:GIZZARD +CREATURE:MOSQUITO_MAN:PANCREAS +CREATURE:MOSQUITO_MAN:SPLEEN +CREATURE:MOSQUITO_MAN:KIDNEY +CREATURE:GIANT_MOSQUITO:MUSCLE +CREATURE:GIANT_MOSQUITO:EYE +CREATURE:GIANT_MOSQUITO:BRAIN +CREATURE:GIANT_MOSQUITO:LUNG +CREATURE:GIANT_MOSQUITO:HEART +CREATURE:GIANT_MOSQUITO:LIVER +CREATURE:GIANT_MOSQUITO:GUT +CREATURE:GIANT_MOSQUITO:STOMACH +CREATURE:GIANT_MOSQUITO:GIZZARD + CREATURE:GIANT_MOSQUITO:PANCREAS +CREATURE:GIANT_MOSQUITO:SPLEEN +CREATURE:GIANT_MOSQUITO:KIDNEY +CREATURE:SPIDER_JUMPING:MUSCLE +CREATURE:SPIDER_JUMPING:EYE +CREATURE:SPIDER_JUMPING:BRAIN +CREATURE:SPIDER_JUMPING:LUNG +CREATURE:SPIDER_JUMPING:HEART +CREATURE:SPIDER_JUMPING:LIVER +CREATURE:SPIDER_JUMPING:GUT +CREATURE:SPIDER_JUMPING:STOMACH +CREATURE:SPIDER_JUMPING:GIZZARD + CREATURE:SPIDER_JUMPING:PANCREAS +CREATURE:SPIDER_JUMPING:SPLEEN +CREATURE:SPIDER_JUMPING:KIDNEY +"CREATURE:JUMPING_SPIDER_MAN:MUSCLE +CREATURE:JUMPING_SPIDER_MAN:EYE +!CREATURE:JUMPING_SPIDER_MAN:BRAIN + CREATURE:JUMPING_SPIDER_MAN:LUNG +!CREATURE:JUMPING_SPIDER_MAN:HEART +!CREATURE:JUMPING_SPIDER_MAN:LIVER +CREATURE:JUMPING_SPIDER_MAN:GUT +#CREATURE:JUMPING_SPIDER_MAN:STOMACH +#CREATURE:JUMPING_SPIDER_MAN:GIZZARD +$CREATURE:JUMPING_SPIDER_MAN:PANCREAS +"CREATURE:JUMPING_SPIDER_MAN:SPLEEN +"CREATURE:JUMPING_SPIDER_MAN:KIDNEY +$CREATURE:GIANT_JUMPING_SPIDER:MUSCLE +!CREATURE:GIANT_JUMPING_SPIDER:EYE +#CREATURE:GIANT_JUMPING_SPIDER:BRAIN +"CREATURE:GIANT_JUMPING_SPIDER:LUNG +#CREATURE:GIANT_JUMPING_SPIDER:HEART +#CREATURE:GIANT_JUMPING_SPIDER:LIVER +!CREATURE:GIANT_JUMPING_SPIDER:GUT +%CREATURE:GIANT_JUMPING_SPIDER:STOMACH +%CREATURE:GIANT_JUMPING_SPIDER:GIZZARD +&CREATURE:GIANT_JUMPING_SPIDER:PANCREAS +$CREATURE:GIANT_JUMPING_SPIDER:SPLEEN +$CREATURE:GIANT_JUMPING_SPIDER:KIDNEY +CREATURE:TERMITE:MUSCLE +CREATURE:TERMITE:EYE +CREATURE:TERMITE:BRAIN +CREATURE:TERMITE:LUNG +CREATURE:TERMITE:HEART +CREATURE:TERMITE:LIVER +CREATURE:TERMITE:GUT +CREATURE:TERMITE:STOMACH +CREATURE:TERMITE:GIZZARD +CREATURE:TERMITE:PANCREAS +CREATURE:TERMITE:SPLEEN +CREATURE:TERMITE:KIDNEY +CREATURE:MOON_SNAIL:MUSCLE +CREATURE:MOON_SNAIL:EYE +CREATURE:MOON_SNAIL:BRAIN +CREATURE:MOON_SNAIL:LUNG +CREATURE:MOON_SNAIL:HEART +CREATURE:MOON_SNAIL:LIVER +CREATURE:MOON_SNAIL:GUT +CREATURE:MOON_SNAIL:STOMACH +CREATURE:MOON_SNAIL:GIZZARD +CREATURE:MOON_SNAIL:PANCREAS +CREATURE:MOON_SNAIL:SPLEEN +CREATURE:MOON_SNAIL:KIDNEY +CREATURE:MOON_SNAIL_MAN:MUSCLE +CREATURE:MOON_SNAIL_MAN:EYE +CREATURE:MOON_SNAIL_MAN:BRAIN +CREATURE:MOON_SNAIL_MAN:LUNG +CREATURE:MOON_SNAIL_MAN:HEART +CREATURE:MOON_SNAIL_MAN:LIVER +CREATURE:MOON_SNAIL_MAN:GUT +CREATURE:MOON_SNAIL_MAN:STOMACH +CREATURE:MOON_SNAIL_MAN:GIZZARD + CREATURE:MOON_SNAIL_MAN:PANCREAS +CREATURE:MOON_SNAIL_MAN:SPLEEN +CREATURE:MOON_SNAIL_MAN:KIDNEY + CREATURE:GIANT_MOON_SNAIL:MUSCLE +CREATURE:GIANT_MOON_SNAIL:EYE +CREATURE:GIANT_MOON_SNAIL:BRAIN +CREATURE:GIANT_MOON_SNAIL:LUNG +CREATURE:GIANT_MOON_SNAIL:HEART +CREATURE:GIANT_MOON_SNAIL:LIVER +CREATURE:GIANT_MOON_SNAIL:GUT +!CREATURE:GIANT_MOON_SNAIL:STOMACH +!CREATURE:GIANT_MOON_SNAIL:GIZZARD +"CREATURE:GIANT_MOON_SNAIL:PANCREAS + CREATURE:GIANT_MOON_SNAIL:SPLEEN + CREATURE:GIANT_MOON_SNAIL:KIDNEY +$CREATURE:SPIDER_BROWN_RECLUSE:MUSCLE +!CREATURE:SPIDER_BROWN_RECLUSE:EYE +#CREATURE:SPIDER_BROWN_RECLUSE:BRAIN +"CREATURE:SPIDER_BROWN_RECLUSE:LUNG +#CREATURE:SPIDER_BROWN_RECLUSE:HEART +#CREATURE:SPIDER_BROWN_RECLUSE:LIVER +!CREATURE:SPIDER_BROWN_RECLUSE:GUT +%CREATURE:SPIDER_BROWN_RECLUSE:STOMACH +%CREATURE:SPIDER_BROWN_RECLUSE:GIZZARD +&CREATURE:SPIDER_BROWN_RECLUSE:PANCREAS +$CREATURE:SPIDER_BROWN_RECLUSE:SPLEEN +$CREATURE:SPIDER_BROWN_RECLUSE:KIDNEY +(CREATURE:BROWN_RECLUSE_SPIDER_MAN:MUSCLE +%CREATURE:BROWN_RECLUSE_SPIDER_MAN:EYE +'CREATURE:BROWN_RECLUSE_SPIDER_MAN:BRAIN +&CREATURE:BROWN_RECLUSE_SPIDER_MAN:LUNG +'CREATURE:BROWN_RECLUSE_SPIDER_MAN:HEART +'CREATURE:BROWN_RECLUSE_SPIDER_MAN:LIVER +%CREATURE:BROWN_RECLUSE_SPIDER_MAN:GUT +)CREATURE:BROWN_RECLUSE_SPIDER_MAN:STOMACH +)CREATURE:BROWN_RECLUSE_SPIDER_MAN:GIZZARD +*CREATURE:BROWN_RECLUSE_SPIDER_MAN:PANCREAS +(CREATURE:BROWN_RECLUSE_SPIDER_MAN:SPLEEN +(CREATURE:BROWN_RECLUSE_SPIDER_MAN:KIDNEY +*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:MUSCLE +'CREATURE:GIANT_BROWN_RECLUSE_SPIDER:EYE +)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:BRAIN +(CREATURE:GIANT_BROWN_RECLUSE_SPIDER:LUNG +)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:HEART +)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:LIVER +'CREATURE:GIANT_BROWN_RECLUSE_SPIDER:GUT ++CREATURE:GIANT_BROWN_RECLUSE_SPIDER:STOMACH ++CREATURE:GIANT_BROWN_RECLUSE_SPIDER:GIZZARD +,CREATURE:GIANT_BROWN_RECLUSE_SPIDER:PANCREAS +*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:SPLEEN +*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:KIDNEY +CREATURE:SNAIL:MUSCLE +CREATURE:SNAIL:EYE +CREATURE:SNAIL:BRAIN +CREATURE:SNAIL:LUNG +CREATURE:SNAIL:HEART +CREATURE:SNAIL:LIVER +CREATURE:SNAIL:GUT +CREATURE:SNAIL:STOMACH +CREATURE:SNAIL:GIZZARD +CREATURE:SNAIL:PANCREAS +CREATURE:SNAIL:SPLEEN +CREATURE:SNAIL:KIDNEY +CREATURE:SNAIL_MAN:MUSCLE +CREATURE:SNAIL_MAN:EYE +CREATURE:SNAIL_MAN:BRAIN +CREATURE:SNAIL_MAN:LUNG +CREATURE:SNAIL_MAN:HEART +CREATURE:SNAIL_MAN:LIVER +CREATURE:SNAIL_MAN:GUT +CREATURE:SNAIL_MAN:STOMACH +CREATURE:SNAIL_MAN:GIZZARD +CREATURE:SNAIL_MAN:PANCREAS +CREATURE:SNAIL_MAN:SPLEEN +CREATURE:SNAIL_MAN:KIDNEY +CREATURE:GIANT_SNAIL:MUSCLE +CREATURE:GIANT_SNAIL:EYE +CREATURE:GIANT_SNAIL:BRAIN +CREATURE:GIANT_SNAIL:LUNG +CREATURE:GIANT_SNAIL:HEART +CREATURE:GIANT_SNAIL:LIVER +CREATURE:GIANT_SNAIL:GUT +CREATURE:GIANT_SNAIL:STOMACH +CREATURE:GIANT_SNAIL:GIZZARD +CREATURE:GIANT_SNAIL:PANCREAS +CREATURE:GIANT_SNAIL:SPLEEN +CREATURE:GIANT_SNAIL:KIDNEY +CREATURE:GECKO_LEOPARD:MUSCLE +CREATURE:GECKO_LEOPARD:EYE +CREATURE:GECKO_LEOPARD:BRAIN +CREATURE:GECKO_LEOPARD:LUNG +CREATURE:GECKO_LEOPARD:HEART +CREATURE:GECKO_LEOPARD:LIVER +CREATURE:GECKO_LEOPARD:GUT +CREATURE:GECKO_LEOPARD:STOMACH +CREATURE:GECKO_LEOPARD:GIZZARD +CREATURE:GECKO_LEOPARD:PANCREAS +CREATURE:GECKO_LEOPARD:SPLEEN +CREATURE:GECKO_LEOPARD:KIDNEY +!CREATURE:LEOPARD_GECKO_MAN:MUSCLE +CREATURE:LEOPARD_GECKO_MAN:EYE + CREATURE:LEOPARD_GECKO_MAN:BRAIN +CREATURE:LEOPARD_GECKO_MAN:LUNG + CREATURE:LEOPARD_GECKO_MAN:HEART + CREATURE:LEOPARD_GECKO_MAN:LIVER +CREATURE:LEOPARD_GECKO_MAN:GUT +"CREATURE:LEOPARD_GECKO_MAN:STOMACH +"CREATURE:LEOPARD_GECKO_MAN:GIZZARD +#CREATURE:LEOPARD_GECKO_MAN:PANCREAS +!CREATURE:LEOPARD_GECKO_MAN:SPLEEN +!CREATURE:LEOPARD_GECKO_MAN:KIDNEY +#CREATURE:GIANT_LEOPARD_GECKO:MUSCLE + CREATURE:GIANT_LEOPARD_GECKO:EYE +"CREATURE:GIANT_LEOPARD_GECKO:BRAIN +!CREATURE:GIANT_LEOPARD_GECKO:LUNG +"CREATURE:GIANT_LEOPARD_GECKO:HEART +"CREATURE:GIANT_LEOPARD_GECKO:LIVER + CREATURE:GIANT_LEOPARD_GECKO:GUT +$CREATURE:GIANT_LEOPARD_GECKO:STOMACH +$CREATURE:GIANT_LEOPARD_GECKO:GIZZARD +%CREATURE:GIANT_LEOPARD_GECKO:PANCREAS +#CREATURE:GIANT_LEOPARD_GECKO:SPLEEN +#CREATURE:GIANT_LEOPARD_GECKO:KIDNEY +CREATURE:DESERT TORTOISE:MUSCLE +CREATURE:DESERT TORTOISE:EYE +CREATURE:DESERT TORTOISE:BRAIN +CREATURE:DESERT TORTOISE:LUNG +CREATURE:DESERT TORTOISE:HEART +CREATURE:DESERT TORTOISE:LIVER +CREATURE:DESERT TORTOISE:GUT + CREATURE:DESERT TORTOISE:STOMACH + CREATURE:DESERT TORTOISE:GIZZARD +!CREATURE:DESERT TORTOISE:PANCREAS +CREATURE:DESERT TORTOISE:SPLEEN +CREATURE:DESERT TORTOISE:KIDNEY +#CREATURE:DESERT_TORTOISE_MAN:MUSCLE + CREATURE:DESERT_TORTOISE_MAN:EYE +"CREATURE:DESERT_TORTOISE_MAN:BRAIN +!CREATURE:DESERT_TORTOISE_MAN:LUNG +"CREATURE:DESERT_TORTOISE_MAN:HEART +"CREATURE:DESERT_TORTOISE_MAN:LIVER + CREATURE:DESERT_TORTOISE_MAN:GUT +$CREATURE:DESERT_TORTOISE_MAN:STOMACH +$CREATURE:DESERT_TORTOISE_MAN:GIZZARD +%CREATURE:DESERT_TORTOISE_MAN:PANCREAS +#CREATURE:DESERT_TORTOISE_MAN:SPLEEN +#CREATURE:DESERT_TORTOISE_MAN:KIDNEY +%CREATURE:GIANT_DESERT_TORTOISE:MUSCLE +"CREATURE:GIANT_DESERT_TORTOISE:EYE +$CREATURE:GIANT_DESERT_TORTOISE:BRAIN +#CREATURE:GIANT_DESERT_TORTOISE:LUNG +$CREATURE:GIANT_DESERT_TORTOISE:HEART +$CREATURE:GIANT_DESERT_TORTOISE:LIVER +"CREATURE:GIANT_DESERT_TORTOISE:GUT +&CREATURE:GIANT_DESERT_TORTOISE:STOMACH +&CREATURE:GIANT_DESERT_TORTOISE:GIZZARD +'CREATURE:GIANT_DESERT_TORTOISE:PANCREAS +%CREATURE:GIANT_DESERT_TORTOISE:SPLEEN +%CREATURE:GIANT_DESERT_TORTOISE:KIDNEY +CREATURE:GILA_MONSTER:MUSCLE +CREATURE:GILA_MONSTER:EYE +CREATURE:GILA_MONSTER:BRAIN +CREATURE:GILA_MONSTER:LUNG +CREATURE:GILA_MONSTER:HEART +CREATURE:GILA_MONSTER:LIVER +CREATURE:GILA_MONSTER:GUT +CREATURE:GILA_MONSTER:STOMACH +CREATURE:GILA_MONSTER:GIZZARD +CREATURE:GILA_MONSTER:PANCREAS +CREATURE:GILA_MONSTER:SPLEEN +CREATURE:GILA_MONSTER:KIDNEY + CREATURE:GILA_MONSTER_MAN:MUSCLE +CREATURE:GILA_MONSTER_MAN:EYE +CREATURE:GILA_MONSTER_MAN:BRAIN +CREATURE:GILA_MONSTER_MAN:LUNG +CREATURE:GILA_MONSTER_MAN:HEART +CREATURE:GILA_MONSTER_MAN:LIVER +CREATURE:GILA_MONSTER_MAN:GUT +!CREATURE:GILA_MONSTER_MAN:STOMACH +!CREATURE:GILA_MONSTER_MAN:GIZZARD +"CREATURE:GILA_MONSTER_MAN:PANCREAS + CREATURE:GILA_MONSTER_MAN:SPLEEN + CREATURE:GILA_MONSTER_MAN:KIDNEY +"CREATURE:GIANT_GILA_MONSTER:MUSCLE +CREATURE:GIANT_GILA_MONSTER:EYE +!CREATURE:GIANT_GILA_MONSTER:BRAIN + CREATURE:GIANT_GILA_MONSTER:LUNG +!CREATURE:GIANT_GILA_MONSTER:HEART +!CREATURE:GIANT_GILA_MONSTER:LIVER +CREATURE:GIANT_GILA_MONSTER:GUT +#CREATURE:GIANT_GILA_MONSTER:STOMACH +#CREATURE:GIANT_GILA_MONSTER:GIZZARD +$CREATURE:GIANT_GILA_MONSTER:PANCREAS +"CREATURE:GIANT_GILA_MONSTER:SPLEEN +"CREATURE:GIANT_GILA_MONSTER:KIDNEY +CREATURE:DOG:MUSCLE +CREATURE:DOG:EYE +CREATURE:DOG:BRAIN +CREATURE:DOG:LUNG +CREATURE:DOG:HEART +CREATURE:DOG:LIVER +CREATURE:DOG:GUT +CREATURE:DOG:STOMACH +CREATURE:DOG:GIZZARD +CREATURE:DOG:PANCREAS +CREATURE:DOG:SPLEEN +CREATURE:DOG:KIDNEY +CREATURE:CAT:MUSCLE +CREATURE:CAT:EYE +CREATURE:CAT:BRAIN +CREATURE:CAT:LUNG +CREATURE:CAT:HEART +CREATURE:CAT:LIVER +CREATURE:CAT:GUT +CREATURE:CAT:STOMACH +CREATURE:CAT:GIZZARD +CREATURE:CAT:PANCREAS +CREATURE:CAT:SPLEEN +CREATURE:CAT:KIDNEY +CREATURE:MULE:MUSCLE +CREATURE:MULE:EYE +CREATURE:MULE:BRAIN +CREATURE:MULE:LUNG +CREATURE:MULE:HEART +CREATURE:MULE:LIVER +CREATURE:MULE:GUT +CREATURE:MULE:STOMACH +CREATURE:MULE:GIZZARD +CREATURE:MULE:PANCREAS +CREATURE:MULE:SPLEEN +CREATURE:MULE:KIDNEY +CREATURE:DONKEY:MUSCLE +CREATURE:DONKEY:EYE +CREATURE:DONKEY:BRAIN +CREATURE:DONKEY:LUNG +CREATURE:DONKEY:HEART +CREATURE:DONKEY:LIVER +CREATURE:DONKEY:GUT +CREATURE:DONKEY:STOMACH +CREATURE:DONKEY:GIZZARD +CREATURE:DONKEY:PANCREAS +CREATURE:DONKEY:SPLEEN +CREATURE:DONKEY:KIDNEY +CREATURE:HORSE:MUSCLE +CREATURE:HORSE:EYE +CREATURE:HORSE:BRAIN +CREATURE:HORSE:LUNG +CREATURE:HORSE:HEART +CREATURE:HORSE:LIVER +CREATURE:HORSE:GUT +CREATURE:HORSE:STOMACH +CREATURE:HORSE:GIZZARD +CREATURE:HORSE:PANCREAS +CREATURE:HORSE:SPLEEN +CREATURE:HORSE:KIDNEY +CREATURE:COW:MUSCLE +CREATURE:COW:EYE +CREATURE:COW:BRAIN +CREATURE:COW:LUNG +CREATURE:COW:HEART +CREATURE:COW:LIVER +CREATURE:COW:GUT +CREATURE:COW:STOMACH +CREATURE:COW:GIZZARD +CREATURE:COW:PANCREAS +CREATURE:COW:SPLEEN +CREATURE:COW:KIDNEY +CREATURE:SHEEP:MUSCLE +CREATURE:SHEEP:EYE +CREATURE:SHEEP:BRAIN +CREATURE:SHEEP:LUNG +CREATURE:SHEEP:HEART +CREATURE:SHEEP:LIVER +CREATURE:SHEEP:GUT +CREATURE:SHEEP:STOMACH +CREATURE:SHEEP:GIZZARD +CREATURE:SHEEP:PANCREAS +CREATURE:SHEEP:SPLEEN +CREATURE:SHEEP:KIDNEY +CREATURE:PIG:MUSCLE +CREATURE:PIG:EYE +CREATURE:PIG:BRAIN +CREATURE:PIG:LUNG +CREATURE:PIG:HEART +CREATURE:PIG:LIVER +CREATURE:PIG:GUT +CREATURE:PIG:STOMACH +CREATURE:PIG:GIZZARD +CREATURE:PIG:PANCREAS +CREATURE:PIG:SPLEEN +CREATURE:PIG:KIDNEY +CREATURE:GOAT:MUSCLE +CREATURE:GOAT:EYE +CREATURE:GOAT:BRAIN +CREATURE:GOAT:LUNG +CREATURE:GOAT:HEART +CREATURE:GOAT:LIVER +CREATURE:GOAT:GUT +CREATURE:GOAT:STOMACH +CREATURE:GOAT:GIZZARD +CREATURE:GOAT:PANCREAS +CREATURE:GOAT:SPLEEN +CREATURE:GOAT:KIDNEY +CREATURE:BIRD_CHICKEN:MUSCLE +CREATURE:BIRD_CHICKEN:EYE +CREATURE:BIRD_CHICKEN:BRAIN +CREATURE:BIRD_CHICKEN:LUNG +CREATURE:BIRD_CHICKEN:HEART +CREATURE:BIRD_CHICKEN:LIVER +CREATURE:BIRD_CHICKEN:GUT +CREATURE:BIRD_CHICKEN:STOMACH +CREATURE:BIRD_CHICKEN:GIZZARD +CREATURE:BIRD_CHICKEN:PANCREAS +CREATURE:BIRD_CHICKEN:SPLEEN +CREATURE:BIRD_CHICKEN:KIDNEY +CREATURE:CAVY:MUSCLE +CREATURE:CAVY:EYE +CREATURE:CAVY:BRAIN +CREATURE:CAVY:LUNG +CREATURE:CAVY:HEART +CREATURE:CAVY:LIVER +CREATURE:CAVY:GUT +CREATURE:CAVY:STOMACH +CREATURE:CAVY:GIZZARD +CREATURE:CAVY:PANCREAS +CREATURE:CAVY:SPLEEN +CREATURE:CAVY:KIDNEY +CREATURE:BIRD_DUCK:MUSCLE +CREATURE:BIRD_DUCK:EYE +CREATURE:BIRD_DUCK:BRAIN +CREATURE:BIRD_DUCK:LUNG +CREATURE:BIRD_DUCK:HEART +CREATURE:BIRD_DUCK:LIVER +CREATURE:BIRD_DUCK:GUT +CREATURE:BIRD_DUCK:STOMACH +CREATURE:BIRD_DUCK:GIZZARD +CREATURE:BIRD_DUCK:PANCREAS +CREATURE:BIRD_DUCK:SPLEEN +CREATURE:BIRD_DUCK:KIDNEY +CREATURE:WATER_BUFFALO:MUSCLE +CREATURE:WATER_BUFFALO:EYE +CREATURE:WATER_BUFFALO:BRAIN +CREATURE:WATER_BUFFALO:LUNG +CREATURE:WATER_BUFFALO:HEART +CREATURE:WATER_BUFFALO:LIVER +CREATURE:WATER_BUFFALO:GUT +CREATURE:WATER_BUFFALO:STOMACH +CREATURE:WATER_BUFFALO:GIZZARD +CREATURE:WATER_BUFFALO:PANCREAS +CREATURE:WATER_BUFFALO:SPLEEN +CREATURE:WATER_BUFFALO:KIDNEY +CREATURE:REINDEER:MUSCLE +CREATURE:REINDEER:EYE +CREATURE:REINDEER:BRAIN +CREATURE:REINDEER:LUNG +CREATURE:REINDEER:HEART +CREATURE:REINDEER:LIVER +CREATURE:REINDEER:GUT +CREATURE:REINDEER:STOMACH +CREATURE:REINDEER:GIZZARD +CREATURE:REINDEER:PANCREAS +CREATURE:REINDEER:SPLEEN +CREATURE:REINDEER:KIDNEY +CREATURE:BIRD_GOOSE:MUSCLE +CREATURE:BIRD_GOOSE:EYE +CREATURE:BIRD_GOOSE:BRAIN +CREATURE:BIRD_GOOSE:LUNG +CREATURE:BIRD_GOOSE:HEART +CREATURE:BIRD_GOOSE:LIVER +CREATURE:BIRD_GOOSE:GUT +CREATURE:BIRD_GOOSE:STOMACH +CREATURE:BIRD_GOOSE:GIZZARD +CREATURE:BIRD_GOOSE:PANCREAS +CREATURE:BIRD_GOOSE:SPLEEN +CREATURE:BIRD_GOOSE:KIDNEY +CREATURE:YAK:MUSCLE +CREATURE:YAK:EYE +CREATURE:YAK:BRAIN +CREATURE:YAK:LUNG +CREATURE:YAK:HEART +CREATURE:YAK:LIVER +CREATURE:YAK:GUT +CREATURE:YAK:STOMACH +CREATURE:YAK:GIZZARD +CREATURE:YAK:PANCREAS +CREATURE:YAK:SPLEEN +CREATURE:YAK:KIDNEY +CREATURE:LLAMA:MUSCLE +CREATURE:LLAMA:EYE +CREATURE:LLAMA:BRAIN +CREATURE:LLAMA:LUNG +CREATURE:LLAMA:HEART +CREATURE:LLAMA:LIVER +CREATURE:LLAMA:GUT +CREATURE:LLAMA:STOMACH +CREATURE:LLAMA:GIZZARD +CREATURE:LLAMA:PANCREAS +CREATURE:LLAMA:SPLEEN +CREATURE:LLAMA:KIDNEY +CREATURE:ALPACA:MUSCLE +CREATURE:ALPACA:EYE +CREATURE:ALPACA:BRAIN +CREATURE:ALPACA:LUNG +CREATURE:ALPACA:HEART +CREATURE:ALPACA:LIVER +CREATURE:ALPACA:GUT +CREATURE:ALPACA:STOMACH +CREATURE:ALPACA:GIZZARD +CREATURE:ALPACA:PANCREAS +CREATURE:ALPACA:SPLEEN +CREATURE:ALPACA:KIDNEY +CREATURE:BIRD_GUINEAFOWL:MUSCLE +CREATURE:BIRD_GUINEAFOWL:EYE +CREATURE:BIRD_GUINEAFOWL:BRAIN +CREATURE:BIRD_GUINEAFOWL:LUNG +CREATURE:BIRD_GUINEAFOWL:HEART +CREATURE:BIRD_GUINEAFOWL:LIVER +CREATURE:BIRD_GUINEAFOWL:GUT + CREATURE:BIRD_GUINEAFOWL:STOMACH + CREATURE:BIRD_GUINEAFOWL:GIZZARD +!CREATURE:BIRD_GUINEAFOWL:PANCREAS +CREATURE:BIRD_GUINEAFOWL:SPLEEN +CREATURE:BIRD_GUINEAFOWL:KIDNEY +!CREATURE:BIRD_PEAFOWL_BLUE:MUSCLE +CREATURE:BIRD_PEAFOWL_BLUE:EYE + CREATURE:BIRD_PEAFOWL_BLUE:BRAIN +CREATURE:BIRD_PEAFOWL_BLUE:LUNG + CREATURE:BIRD_PEAFOWL_BLUE:HEART + CREATURE:BIRD_PEAFOWL_BLUE:LIVER +CREATURE:BIRD_PEAFOWL_BLUE:GUT +"CREATURE:BIRD_PEAFOWL_BLUE:STOMACH +"CREATURE:BIRD_PEAFOWL_BLUE:GIZZARD +#CREATURE:BIRD_PEAFOWL_BLUE:PANCREAS +!CREATURE:BIRD_PEAFOWL_BLUE:SPLEEN +!CREATURE:BIRD_PEAFOWL_BLUE:KIDNEY +CREATURE:BIRD_TURKEY:MUSCLE +CREATURE:BIRD_TURKEY:EYE +CREATURE:BIRD_TURKEY:BRAIN +CREATURE:BIRD_TURKEY:LUNG +CREATURE:BIRD_TURKEY:HEART +CREATURE:BIRD_TURKEY:LIVER +CREATURE:BIRD_TURKEY:GUT +CREATURE:BIRD_TURKEY:STOMACH +CREATURE:BIRD_TURKEY:GIZZARD +CREATURE:BIRD_TURKEY:PANCREAS +CREATURE:BIRD_TURKEY:SPLEEN +CREATURE:BIRD_TURKEY:KIDNEY +CREATURE:RABBIT:MUSCLE +CREATURE:RABBIT:EYE +CREATURE:RABBIT:BRAIN +CREATURE:RABBIT:LUNG +CREATURE:RABBIT:HEART +CREATURE:RABBIT:LIVER +CREATURE:RABBIT:GUT +CREATURE:RABBIT:STOMACH +CREATURE:RABBIT:GIZZARD +CREATURE:RABBIT:PANCREAS +CREATURE:RABBIT:SPLEEN +CREATURE:RABBIT:KIDNEY +CREATURE:FLY:MUSCLE +CREATURE:FLY:EYE +CREATURE:FLY:BRAIN +CREATURE:FLY:LUNG +CREATURE:FLY:HEART +CREATURE:FLY:LIVER +CREATURE:FLY:GUT +CREATURE:FLY:STOMACH +CREATURE:FLY:GIZZARD +CREATURE:FLY:PANCREAS +CREATURE:FLY:SPLEEN +CREATURE:FLY:KIDNEY +CREATURE:FLY_MAN:MUSCLE +CREATURE:FLY_MAN:EYE +CREATURE:FLY_MAN:BRAIN +CREATURE:FLY_MAN:LUNG +CREATURE:FLY_MAN:HEART +CREATURE:FLY_MAN:LIVER +CREATURE:FLY_MAN:GUT +CREATURE:FLY_MAN:STOMACH +CREATURE:FLY_MAN:GIZZARD +CREATURE:FLY_MAN:PANCREAS +CREATURE:FLY_MAN:SPLEEN +CREATURE:FLY_MAN:KIDNEY +CREATURE:GIANT_FLY:MUSCLE +CREATURE:GIANT_FLY:EYE +CREATURE:GIANT_FLY:BRAIN +CREATURE:GIANT_FLY:LUNG +CREATURE:GIANT_FLY:HEART +CREATURE:GIANT_FLY:LIVER +CREATURE:GIANT_FLY:GUT +CREATURE:GIANT_FLY:STOMACH +CREATURE:GIANT_FLY:GIZZARD +CREATURE:GIANT_FLY:PANCREAS +CREATURE:GIANT_FLY:SPLEEN +CREATURE:GIANT_FLY:KIDNEY +CREATURE:ROACH_LARGE:MUSCLE +CREATURE:ROACH_LARGE:EYE +CREATURE:ROACH_LARGE:BRAIN +CREATURE:ROACH_LARGE:LUNG +CREATURE:ROACH_LARGE:HEART +CREATURE:ROACH_LARGE:LIVER +CREATURE:ROACH_LARGE:GUT +CREATURE:ROACH_LARGE:STOMACH +CREATURE:ROACH_LARGE:GIZZARD +CREATURE:ROACH_LARGE:PANCREAS +CREATURE:ROACH_LARGE:SPLEEN +CREATURE:ROACH_LARGE:KIDNEY +CREATURE:ROACH_MAN:MUSCLE +CREATURE:ROACH_MAN:EYE +CREATURE:ROACH_MAN:BRAIN +CREATURE:ROACH_MAN:LUNG +CREATURE:ROACH_MAN:HEART +CREATURE:ROACH_MAN:LIVER +CREATURE:ROACH_MAN:GUT +CREATURE:ROACH_MAN:STOMACH +CREATURE:ROACH_MAN:GIZZARD +CREATURE:ROACH_MAN:PANCREAS +CREATURE:ROACH_MAN:SPLEEN +CREATURE:ROACH_MAN:KIDNEY +CREATURE:GIANT_ROACH:MUSCLE +CREATURE:GIANT_ROACH:EYE +CREATURE:GIANT_ROACH:BRAIN +CREATURE:GIANT_ROACH:LUNG +CREATURE:GIANT_ROACH:HEART +CREATURE:GIANT_ROACH:LIVER +CREATURE:GIANT_ROACH:GUT +CREATURE:GIANT_ROACH:STOMACH +CREATURE:GIANT_ROACH:GIZZARD +CREATURE:GIANT_ROACH:PANCREAS +CREATURE:GIANT_ROACH:SPLEEN +CREATURE:GIANT_ROACH:KIDNEY +CREATURE:BEETLE:MUSCLE +CREATURE:BEETLE:EYE +CREATURE:BEETLE:BRAIN +CREATURE:BEETLE:LUNG +CREATURE:BEETLE:HEART +CREATURE:BEETLE:LIVER +CREATURE:BEETLE:GUT +CREATURE:BEETLE:STOMACH +CREATURE:BEETLE:GIZZARD +CREATURE:BEETLE:PANCREAS +CREATURE:BEETLE:SPLEEN +CREATURE:BEETLE:KIDNEY +CREATURE:BEETLE_MAN:MUSCLE +CREATURE:BEETLE_MAN:EYE +CREATURE:BEETLE_MAN:BRAIN +CREATURE:BEETLE_MAN:LUNG +CREATURE:BEETLE_MAN:HEART +CREATURE:BEETLE_MAN:LIVER +CREATURE:BEETLE_MAN:GUT +CREATURE:BEETLE_MAN:STOMACH +CREATURE:BEETLE_MAN:GIZZARD +CREATURE:BEETLE_MAN:PANCREAS +CREATURE:BEETLE_MAN:SPLEEN +CREATURE:BEETLE_MAN:KIDNEY +CREATURE:GIANT_BEETLE:MUSCLE +CREATURE:GIANT_BEETLE:EYE +CREATURE:GIANT_BEETLE:BRAIN +CREATURE:GIANT_BEETLE:LUNG +CREATURE:GIANT_BEETLE:HEART +CREATURE:GIANT_BEETLE:LIVER +CREATURE:GIANT_BEETLE:GUT +CREATURE:GIANT_BEETLE:STOMACH +CREATURE:GIANT_BEETLE:GIZZARD +CREATURE:GIANT_BEETLE:PANCREAS +CREATURE:GIANT_BEETLE:SPLEEN +CREATURE:GIANT_BEETLE:KIDNEY +CREATURE:ANT:MUSCLE +CREATURE:ANT:EYE +CREATURE:ANT:BRAIN +CREATURE:ANT:LUNG +CREATURE:ANT:HEART +CREATURE:ANT:LIVER +CREATURE:ANT:GUT +CREATURE:ANT:STOMACH +CREATURE:ANT:GIZZARD +CREATURE:ANT:PANCREAS +CREATURE:ANT:SPLEEN +CREATURE:ANT:KIDNEY +!CREATURE:BUTTERFLY_MONARCH:MUSCLE +CREATURE:BUTTERFLY_MONARCH:EYE + CREATURE:BUTTERFLY_MONARCH:BRAIN +CREATURE:BUTTERFLY_MONARCH:LUNG + CREATURE:BUTTERFLY_MONARCH:HEART + CREATURE:BUTTERFLY_MONARCH:LIVER +CREATURE:BUTTERFLY_MONARCH:GUT +"CREATURE:BUTTERFLY_MONARCH:STOMACH +"CREATURE:BUTTERFLY_MONARCH:GIZZARD +#CREATURE:BUTTERFLY_MONARCH:PANCREAS +!CREATURE:BUTTERFLY_MONARCH:SPLEEN +!CREATURE:BUTTERFLY_MONARCH:KIDNEY +%CREATURE:BUTTERFLY_MONARCH_MAN:MUSCLE +"CREATURE:BUTTERFLY_MONARCH_MAN:EYE +$CREATURE:BUTTERFLY_MONARCH_MAN:BRAIN +#CREATURE:BUTTERFLY_MONARCH_MAN:LUNG +$CREATURE:BUTTERFLY_MONARCH_MAN:HEART +$CREATURE:BUTTERFLY_MONARCH_MAN:LIVER +"CREATURE:BUTTERFLY_MONARCH_MAN:GUT +&CREATURE:BUTTERFLY_MONARCH_MAN:STOMACH +&CREATURE:BUTTERFLY_MONARCH_MAN:GIZZARD +'CREATURE:BUTTERFLY_MONARCH_MAN:PANCREAS +%CREATURE:BUTTERFLY_MONARCH_MAN:SPLEEN +%CREATURE:BUTTERFLY_MONARCH_MAN:KIDNEY +'CREATURE:GIANT_BUTTERFLY_MONARCH:MUSCLE +$CREATURE:GIANT_BUTTERFLY_MONARCH:EYE +&CREATURE:GIANT_BUTTERFLY_MONARCH:BRAIN +%CREATURE:GIANT_BUTTERFLY_MONARCH:LUNG +&CREATURE:GIANT_BUTTERFLY_MONARCH:HEART +&CREATURE:GIANT_BUTTERFLY_MONARCH:LIVER +$CREATURE:GIANT_BUTTERFLY_MONARCH:GUT +(CREATURE:GIANT_BUTTERFLY_MONARCH:STOMACH +(CREATURE:GIANT_BUTTERFLY_MONARCH:GIZZARD +)CREATURE:GIANT_BUTTERFLY_MONARCH:PANCREAS +'CREATURE:GIANT_BUTTERFLY_MONARCH:SPLEEN +'CREATURE:GIANT_BUTTERFLY_MONARCH:KIDNEY +CREATURE:FIREFLY:MUSCLE +CREATURE:FIREFLY:EYE +CREATURE:FIREFLY:BRAIN +CREATURE:FIREFLY:LUNG +CREATURE:FIREFLY:HEART +CREATURE:FIREFLY:LIVER +CREATURE:FIREFLY:GUT +CREATURE:FIREFLY:STOMACH +CREATURE:FIREFLY:GIZZARD +CREATURE:FIREFLY:PANCREAS +CREATURE:FIREFLY:SPLEEN +CREATURE:FIREFLY:KIDNEY +CREATURE:FIREFLY_MAN:MUSCLE +CREATURE:FIREFLY_MAN:EYE +CREATURE:FIREFLY_MAN:BRAIN +CREATURE:FIREFLY_MAN:LUNG +CREATURE:FIREFLY_MAN:HEART +CREATURE:FIREFLY_MAN:LIVER +CREATURE:FIREFLY_MAN:GUT +CREATURE:FIREFLY_MAN:STOMACH +CREATURE:FIREFLY_MAN:GIZZARD +CREATURE:FIREFLY_MAN:PANCREAS +CREATURE:FIREFLY_MAN:SPLEEN +CREATURE:FIREFLY_MAN:KIDNEY +CREATURE:GIANT_FIREFLY:MUSCLE +CREATURE:GIANT_FIREFLY:EYE +CREATURE:GIANT_FIREFLY:BRAIN +CREATURE:GIANT_FIREFLY:LUNG +CREATURE:GIANT_FIREFLY:HEART +CREATURE:GIANT_FIREFLY:LIVER +CREATURE:GIANT_FIREFLY:GUT +CREATURE:GIANT_FIREFLY:STOMACH +CREATURE:GIANT_FIREFLY:GIZZARD +CREATURE:GIANT_FIREFLY:PANCREAS +CREATURE:GIANT_FIREFLY:SPLEEN +CREATURE:GIANT_FIREFLY:KIDNEY +CREATURE:DRAGONFLY:MUSCLE +CREATURE:DRAGONFLY:EYE +CREATURE:DRAGONFLY:BRAIN +CREATURE:DRAGONFLY:LUNG +CREATURE:DRAGONFLY:HEART +CREATURE:DRAGONFLY:LIVER +CREATURE:DRAGONFLY:GUT +CREATURE:DRAGONFLY:STOMACH +CREATURE:DRAGONFLY:GIZZARD +CREATURE:DRAGONFLY:PANCREAS +CREATURE:DRAGONFLY:SPLEEN +CREATURE:DRAGONFLY:KIDNEY +CREATURE:DRAGONFLY_MAN:MUSCLE +CREATURE:DRAGONFLY_MAN:EYE +CREATURE:DRAGONFLY_MAN:BRAIN +CREATURE:DRAGONFLY_MAN:LUNG +CREATURE:DRAGONFLY_MAN:HEART +CREATURE:DRAGONFLY_MAN:LIVER +CREATURE:DRAGONFLY_MAN:GUT +CREATURE:DRAGONFLY_MAN:STOMACH +CREATURE:DRAGONFLY_MAN:GIZZARD +CREATURE:DRAGONFLY_MAN:PANCREAS +CREATURE:DRAGONFLY_MAN:SPLEEN +CREATURE:DRAGONFLY_MAN:KIDNEY +CREATURE:GIANT_DRAGONFLY:MUSCLE +CREATURE:GIANT_DRAGONFLY:EYE +CREATURE:GIANT_DRAGONFLY:BRAIN +CREATURE:GIANT_DRAGONFLY:LUNG +CREATURE:GIANT_DRAGONFLY:HEART +CREATURE:GIANT_DRAGONFLY:LIVER +CREATURE:GIANT_DRAGONFLY:GUT + CREATURE:GIANT_DRAGONFLY:STOMACH + CREATURE:GIANT_DRAGONFLY:GIZZARD +!CREATURE:GIANT_DRAGONFLY:PANCREAS +CREATURE:GIANT_DRAGONFLY:SPLEEN +CREATURE:GIANT_DRAGONFLY:KIDNEY +CREATURE:HONEY_BEE:MUSCLE +CREATURE:HONEY_BEE:EYE +CREATURE:HONEY_BEE:BRAIN +CREATURE:HONEY_BEE:LUNG +CREATURE:HONEY_BEE:HEART +CREATURE:HONEY_BEE:LIVER +CREATURE:HONEY_BEE:GUT +CREATURE:HONEY_BEE:STOMACH +CREATURE:HONEY_BEE:GIZZARD +CREATURE:HONEY_BEE:PANCREAS +CREATURE:HONEY_BEE:SPLEEN +CREATURE:HONEY_BEE:KIDNEY +CREATURE:BUMBLEBEE:MUSCLE +CREATURE:BUMBLEBEE:EYE +CREATURE:BUMBLEBEE:BRAIN +CREATURE:BUMBLEBEE:LUNG +CREATURE:BUMBLEBEE:HEART +CREATURE:BUMBLEBEE:LIVER +CREATURE:BUMBLEBEE:GUT +CREATURE:BUMBLEBEE:STOMACH +CREATURE:BUMBLEBEE:GIZZARD +CREATURE:BUMBLEBEE:PANCREAS +CREATURE:BUMBLEBEE:SPLEEN +CREATURE:BUMBLEBEE:KIDNEY +CREATURE:GOAT_MOUNTAIN:MUSCLE +CREATURE:GOAT_MOUNTAIN:EYE +CREATURE:GOAT_MOUNTAIN:BRAIN +CREATURE:GOAT_MOUNTAIN:LUNG +CREATURE:GOAT_MOUNTAIN:HEART +CREATURE:GOAT_MOUNTAIN:LIVER +CREATURE:GOAT_MOUNTAIN:GUT +CREATURE:GOAT_MOUNTAIN:STOMACH +CREATURE:GOAT_MOUNTAIN:GIZZARD +CREATURE:GOAT_MOUNTAIN:PANCREAS +CREATURE:GOAT_MOUNTAIN:SPLEEN +CREATURE:GOAT_MOUNTAIN:KIDNEY +!CREATURE:GOAT_MOUNTAIN_MAN:MUSCLE +CREATURE:GOAT_MOUNTAIN_MAN:EYE + CREATURE:GOAT_MOUNTAIN_MAN:BRAIN +CREATURE:GOAT_MOUNTAIN_MAN:LUNG + CREATURE:GOAT_MOUNTAIN_MAN:HEART + CREATURE:GOAT_MOUNTAIN_MAN:LIVER +CREATURE:GOAT_MOUNTAIN_MAN:GUT +"CREATURE:GOAT_MOUNTAIN_MAN:STOMACH +"CREATURE:GOAT_MOUNTAIN_MAN:GIZZARD +#CREATURE:GOAT_MOUNTAIN_MAN:PANCREAS +!CREATURE:GOAT_MOUNTAIN_MAN:SPLEEN +!CREATURE:GOAT_MOUNTAIN_MAN:KIDNEY +#CREATURE:GIANT_GOAT_MOUNTAIN:MUSCLE + CREATURE:GIANT_GOAT_MOUNTAIN:EYE +"CREATURE:GIANT_GOAT_MOUNTAIN:BRAIN +!CREATURE:GIANT_GOAT_MOUNTAIN:LUNG +"CREATURE:GIANT_GOAT_MOUNTAIN:HEART +"CREATURE:GIANT_GOAT_MOUNTAIN:LIVER + CREATURE:GIANT_GOAT_MOUNTAIN:GUT +$CREATURE:GIANT_GOAT_MOUNTAIN:STOMACH +$CREATURE:GIANT_GOAT_MOUNTAIN:GIZZARD +%CREATURE:GIANT_GOAT_MOUNTAIN:PANCREAS +#CREATURE:GIANT_GOAT_MOUNTAIN:SPLEEN +#CREATURE:GIANT_GOAT_MOUNTAIN:KIDNEY +CREATURE:MARMOT_HOARY:MUSCLE +CREATURE:MARMOT_HOARY:EYE +CREATURE:MARMOT_HOARY:BRAIN +CREATURE:MARMOT_HOARY:LUNG +CREATURE:MARMOT_HOARY:HEART +CREATURE:MARMOT_HOARY:LIVER +CREATURE:MARMOT_HOARY:GUT +CREATURE:MARMOT_HOARY:STOMACH +CREATURE:MARMOT_HOARY:GIZZARD +CREATURE:MARMOT_HOARY:PANCREAS +CREATURE:MARMOT_HOARY:SPLEEN +CREATURE:MARMOT_HOARY:KIDNEY + CREATURE:MARMOT_HOARY_MAN:MUSCLE +CREATURE:MARMOT_HOARY_MAN:EYE +CREATURE:MARMOT_HOARY_MAN:BRAIN +CREATURE:MARMOT_HOARY_MAN:LUNG +CREATURE:MARMOT_HOARY_MAN:HEART +CREATURE:MARMOT_HOARY_MAN:LIVER +CREATURE:MARMOT_HOARY_MAN:GUT +!CREATURE:MARMOT_HOARY_MAN:STOMACH +!CREATURE:MARMOT_HOARY_MAN:GIZZARD +"CREATURE:MARMOT_HOARY_MAN:PANCREAS + CREATURE:MARMOT_HOARY_MAN:SPLEEN + CREATURE:MARMOT_HOARY_MAN:KIDNEY +"CREATURE:GIANT_MARMOT_HOARY:MUSCLE +CREATURE:GIANT_MARMOT_HOARY:EYE +!CREATURE:GIANT_MARMOT_HOARY:BRAIN + CREATURE:GIANT_MARMOT_HOARY:LUNG +!CREATURE:GIANT_MARMOT_HOARY:HEART +!CREATURE:GIANT_MARMOT_HOARY:LIVER +CREATURE:GIANT_MARMOT_HOARY:GUT +#CREATURE:GIANT_MARMOT_HOARY:STOMACH +#CREATURE:GIANT_MARMOT_HOARY:GIZZARD +$CREATURE:GIANT_MARMOT_HOARY:PANCREAS +"CREATURE:GIANT_MARMOT_HOARY:SPLEEN +"CREATURE:GIANT_MARMOT_HOARY:KIDNEY +CREATURE:GNOME_MOUNTAIN:MUSCLE +CREATURE:GNOME_MOUNTAIN:EYE +CREATURE:GNOME_MOUNTAIN:BRAIN +CREATURE:GNOME_MOUNTAIN:LUNG +CREATURE:GNOME_MOUNTAIN:HEART +CREATURE:GNOME_MOUNTAIN:LIVER +CREATURE:GNOME_MOUNTAIN:GUT +CREATURE:GNOME_MOUNTAIN:STOMACH +CREATURE:GNOME_MOUNTAIN:GIZZARD + CREATURE:GNOME_MOUNTAIN:PANCREAS +CREATURE:GNOME_MOUNTAIN:SPLEEN +CREATURE:GNOME_MOUNTAIN:KIDNEY +CREATURE:GNOME_DARK:MUSCLE +CREATURE:GNOME_DARK:EYE +CREATURE:GNOME_DARK:BRAIN +CREATURE:GNOME_DARK:LUNG +CREATURE:GNOME_DARK:HEART +CREATURE:GNOME_DARK:LIVER +CREATURE:GNOME_DARK:GUT +CREATURE:GNOME_DARK:STOMACH +CREATURE:GNOME_DARK:GIZZARD +CREATURE:GNOME_DARK:PANCREAS +CREATURE:GNOME_DARK:SPLEEN +CREATURE:GNOME_DARK:KIDNEY +CREATURE:WALRUS:MUSCLE +CREATURE:WALRUS:EYE +CREATURE:WALRUS:BRAIN +CREATURE:WALRUS:LUNG +CREATURE:WALRUS:HEART +CREATURE:WALRUS:LIVER +CREATURE:WALRUS:GUT +CREATURE:WALRUS:STOMACH +CREATURE:WALRUS:GIZZARD +CREATURE:WALRUS:PANCREAS +CREATURE:WALRUS:SPLEEN +CREATURE:WALRUS:KIDNEY +CREATURE:WALRUS_MAN:MUSCLE +CREATURE:WALRUS_MAN:EYE +CREATURE:WALRUS_MAN:BRAIN +CREATURE:WALRUS_MAN:LUNG +CREATURE:WALRUS_MAN:HEART +CREATURE:WALRUS_MAN:LIVER +CREATURE:WALRUS_MAN:GUT +CREATURE:WALRUS_MAN:STOMACH +CREATURE:WALRUS_MAN:GIZZARD +CREATURE:WALRUS_MAN:PANCREAS +CREATURE:WALRUS_MAN:SPLEEN +CREATURE:WALRUS_MAN:KIDNEY +CREATURE:GIANT_WALRUS:MUSCLE +CREATURE:GIANT_WALRUS:EYE +CREATURE:GIANT_WALRUS:BRAIN +CREATURE:GIANT_WALRUS:LUNG +CREATURE:GIANT_WALRUS:HEART +CREATURE:GIANT_WALRUS:LIVER +CREATURE:GIANT_WALRUS:GUT +CREATURE:GIANT_WALRUS:STOMACH +CREATURE:GIANT_WALRUS:GIZZARD +CREATURE:GIANT_WALRUS:PANCREAS +CREATURE:GIANT_WALRUS:SPLEEN +CREATURE:GIANT_WALRUS:KIDNEY + CREATURE:FISH_LAMPREY_SEA:MUSCLE +CREATURE:FISH_LAMPREY_SEA:EYE +CREATURE:FISH_LAMPREY_SEA:BRAIN +CREATURE:FISH_LAMPREY_SEA:LUNG +CREATURE:FISH_LAMPREY_SEA:HEART +CREATURE:FISH_LAMPREY_SEA:LIVER +CREATURE:FISH_LAMPREY_SEA:GUT +!CREATURE:FISH_LAMPREY_SEA:STOMACH +!CREATURE:FISH_LAMPREY_SEA:GIZZARD +"CREATURE:FISH_LAMPREY_SEA:PANCREAS + CREATURE:FISH_LAMPREY_SEA:SPLEEN + CREATURE:FISH_LAMPREY_SEA:KIDNEY +!CREATURE:SHARK_GREAT_WHITE:MUSCLE +CREATURE:SHARK_GREAT_WHITE:EYE + CREATURE:SHARK_GREAT_WHITE:BRAIN +CREATURE:SHARK_GREAT_WHITE:LUNG + CREATURE:SHARK_GREAT_WHITE:HEART + CREATURE:SHARK_GREAT_WHITE:LIVER +CREATURE:SHARK_GREAT_WHITE:GUT +"CREATURE:SHARK_GREAT_WHITE:STOMACH +"CREATURE:SHARK_GREAT_WHITE:GIZZARD +#CREATURE:SHARK_GREAT_WHITE:PANCREAS +!CREATURE:SHARK_GREAT_WHITE:SPLEEN +!CREATURE:SHARK_GREAT_WHITE:KIDNEY +CREATURE:SHARK_FRILL:MUSCLE +CREATURE:SHARK_FRILL:EYE +CREATURE:SHARK_FRILL:BRAIN +CREATURE:SHARK_FRILL:LUNG +CREATURE:SHARK_FRILL:HEART +CREATURE:SHARK_FRILL:LIVER +CREATURE:SHARK_FRILL:GUT +CREATURE:SHARK_FRILL:STOMACH +CREATURE:SHARK_FRILL:GIZZARD +CREATURE:SHARK_FRILL:PANCREAS +CREATURE:SHARK_FRILL:SPLEEN +CREATURE:SHARK_FRILL:KIDNEY +#CREATURE:SHARK_SPINY_DOGFISH:MUSCLE + CREATURE:SHARK_SPINY_DOGFISH:EYE +"CREATURE:SHARK_SPINY_DOGFISH:BRAIN +!CREATURE:SHARK_SPINY_DOGFISH:LUNG +"CREATURE:SHARK_SPINY_DOGFISH:HEART +"CREATURE:SHARK_SPINY_DOGFISH:LIVER + CREATURE:SHARK_SPINY_DOGFISH:GUT +$CREATURE:SHARK_SPINY_DOGFISH:STOMACH +$CREATURE:SHARK_SPINY_DOGFISH:GIZZARD +%CREATURE:SHARK_SPINY_DOGFISH:PANCREAS +#CREATURE:SHARK_SPINY_DOGFISH:SPLEEN +#CREATURE:SHARK_SPINY_DOGFISH:KIDNEY +'CREATURE:SHARK_WOBBEGONG_SPOTTED:MUSCLE +$CREATURE:SHARK_WOBBEGONG_SPOTTED:EYE +&CREATURE:SHARK_WOBBEGONG_SPOTTED:BRAIN +%CREATURE:SHARK_WOBBEGONG_SPOTTED:LUNG +&CREATURE:SHARK_WOBBEGONG_SPOTTED:HEART +&CREATURE:SHARK_WOBBEGONG_SPOTTED:LIVER +$CREATURE:SHARK_WOBBEGONG_SPOTTED:GUT +(CREATURE:SHARK_WOBBEGONG_SPOTTED:STOMACH +(CREATURE:SHARK_WOBBEGONG_SPOTTED:GIZZARD +)CREATURE:SHARK_WOBBEGONG_SPOTTED:PANCREAS +'CREATURE:SHARK_WOBBEGONG_SPOTTED:SPLEEN +'CREATURE:SHARK_WOBBEGONG_SPOTTED:KIDNEY +CREATURE:SHARK_WHALE:MUSCLE +CREATURE:SHARK_WHALE:EYE +CREATURE:SHARK_WHALE:BRAIN +CREATURE:SHARK_WHALE:LUNG +CREATURE:SHARK_WHALE:HEART +CREATURE:SHARK_WHALE:LIVER +CREATURE:SHARK_WHALE:GUT +CREATURE:SHARK_WHALE:STOMACH +CREATURE:SHARK_WHALE:GIZZARD +CREATURE:SHARK_WHALE:PANCREAS +CREATURE:SHARK_WHALE:SPLEEN +CREATURE:SHARK_WHALE:KIDNEY +CREATURE:SHARK_BASKING:MUSCLE +CREATURE:SHARK_BASKING:EYE +CREATURE:SHARK_BASKING:BRAIN +CREATURE:SHARK_BASKING:LUNG +CREATURE:SHARK_BASKING:HEART +CREATURE:SHARK_BASKING:LIVER +CREATURE:SHARK_BASKING:GUT +CREATURE:SHARK_BASKING:STOMACH +CREATURE:SHARK_BASKING:GIZZARD +CREATURE:SHARK_BASKING:PANCREAS +CREATURE:SHARK_BASKING:SPLEEN +CREATURE:SHARK_BASKING:KIDNEY +CREATURE:SHARK_NURSE:MUSCLE +CREATURE:SHARK_NURSE:EYE +CREATURE:SHARK_NURSE:BRAIN +CREATURE:SHARK_NURSE:LUNG +CREATURE:SHARK_NURSE:HEART +CREATURE:SHARK_NURSE:LIVER +CREATURE:SHARK_NURSE:GUT +CREATURE:SHARK_NURSE:STOMACH +CREATURE:SHARK_NURSE:GIZZARD +CREATURE:SHARK_NURSE:PANCREAS +CREATURE:SHARK_NURSE:SPLEEN +CREATURE:SHARK_NURSE:KIDNEY +#CREATURE:SHARK_MAKO_SHORTFIN:MUSCLE + CREATURE:SHARK_MAKO_SHORTFIN:EYE +"CREATURE:SHARK_MAKO_SHORTFIN:BRAIN +!CREATURE:SHARK_MAKO_SHORTFIN:LUNG +"CREATURE:SHARK_MAKO_SHORTFIN:HEART +"CREATURE:SHARK_MAKO_SHORTFIN:LIVER + CREATURE:SHARK_MAKO_SHORTFIN:GUT +$CREATURE:SHARK_MAKO_SHORTFIN:STOMACH +$CREATURE:SHARK_MAKO_SHORTFIN:GIZZARD +%CREATURE:SHARK_MAKO_SHORTFIN:PANCREAS +#CREATURE:SHARK_MAKO_SHORTFIN:SPLEEN +#CREATURE:SHARK_MAKO_SHORTFIN:KIDNEY +"CREATURE:SHARK_MAKO_LONGFIN:MUSCLE +CREATURE:SHARK_MAKO_LONGFIN:EYE +!CREATURE:SHARK_MAKO_LONGFIN:BRAIN + CREATURE:SHARK_MAKO_LONGFIN:LUNG +!CREATURE:SHARK_MAKO_LONGFIN:HEART +!CREATURE:SHARK_MAKO_LONGFIN:LIVER +CREATURE:SHARK_MAKO_LONGFIN:GUT +#CREATURE:SHARK_MAKO_LONGFIN:STOMACH +#CREATURE:SHARK_MAKO_LONGFIN:GIZZARD +$CREATURE:SHARK_MAKO_LONGFIN:PANCREAS +"CREATURE:SHARK_MAKO_LONGFIN:SPLEEN +"CREATURE:SHARK_MAKO_LONGFIN:KIDNEY +CREATURE:SHARK_TIGER:MUSCLE +CREATURE:SHARK_TIGER:EYE +CREATURE:SHARK_TIGER:BRAIN +CREATURE:SHARK_TIGER:LUNG +CREATURE:SHARK_TIGER:HEART +CREATURE:SHARK_TIGER:LIVER +CREATURE:SHARK_TIGER:GUT +CREATURE:SHARK_TIGER:STOMACH +CREATURE:SHARK_TIGER:GIZZARD +CREATURE:SHARK_TIGER:PANCREAS +CREATURE:SHARK_TIGER:SPLEEN +CREATURE:SHARK_TIGER:KIDNEY +CREATURE:SHARK_BULL:MUSCLE +CREATURE:SHARK_BULL:EYE +CREATURE:SHARK_BULL:BRAIN +CREATURE:SHARK_BULL:LUNG +CREATURE:SHARK_BULL:HEART +CREATURE:SHARK_BULL:LIVER +CREATURE:SHARK_BULL:GUT +CREATURE:SHARK_BULL:STOMACH +CREATURE:SHARK_BULL:GIZZARD +CREATURE:SHARK_BULL:PANCREAS +CREATURE:SHARK_BULL:SPLEEN +CREATURE:SHARK_BULL:KIDNEY +#CREATURE:SHARK_REEF_BLACKTIP:MUSCLE + CREATURE:SHARK_REEF_BLACKTIP:EYE +"CREATURE:SHARK_REEF_BLACKTIP:BRAIN +!CREATURE:SHARK_REEF_BLACKTIP:LUNG +"CREATURE:SHARK_REEF_BLACKTIP:HEART +"CREATURE:SHARK_REEF_BLACKTIP:LIVER + CREATURE:SHARK_REEF_BLACKTIP:GUT +$CREATURE:SHARK_REEF_BLACKTIP:STOMACH +$CREATURE:SHARK_REEF_BLACKTIP:GIZZARD +%CREATURE:SHARK_REEF_BLACKTIP:PANCREAS +#CREATURE:SHARK_REEF_BLACKTIP:SPLEEN +#CREATURE:SHARK_REEF_BLACKTIP:KIDNEY +#CREATURE:SHARK_REEF_WHITETIP:MUSCLE + CREATURE:SHARK_REEF_WHITETIP:EYE +"CREATURE:SHARK_REEF_WHITETIP:BRAIN +!CREATURE:SHARK_REEF_WHITETIP:LUNG +"CREATURE:SHARK_REEF_WHITETIP:HEART +"CREATURE:SHARK_REEF_WHITETIP:LIVER + CREATURE:SHARK_REEF_WHITETIP:GUT +$CREATURE:SHARK_REEF_WHITETIP:STOMACH +$CREATURE:SHARK_REEF_WHITETIP:GIZZARD +%CREATURE:SHARK_REEF_WHITETIP:PANCREAS +#CREATURE:SHARK_REEF_WHITETIP:SPLEEN +#CREATURE:SHARK_REEF_WHITETIP:KIDNEY +CREATURE:SHARK_BLUE:MUSCLE +CREATURE:SHARK_BLUE:EYE +CREATURE:SHARK_BLUE:BRAIN +CREATURE:SHARK_BLUE:LUNG +CREATURE:SHARK_BLUE:HEART +CREATURE:SHARK_BLUE:LIVER +CREATURE:SHARK_BLUE:GUT +CREATURE:SHARK_BLUE:STOMACH +CREATURE:SHARK_BLUE:GIZZARD +CREATURE:SHARK_BLUE:PANCREAS +CREATURE:SHARK_BLUE:SPLEEN +CREATURE:SHARK_BLUE:KIDNEY + CREATURE:SHARK_HAMMERHEAD:MUSCLE +CREATURE:SHARK_HAMMERHEAD:EYE +CREATURE:SHARK_HAMMERHEAD:BRAIN +CREATURE:SHARK_HAMMERHEAD:LUNG +CREATURE:SHARK_HAMMERHEAD:HEART +CREATURE:SHARK_HAMMERHEAD:LIVER +CREATURE:SHARK_HAMMERHEAD:GUT +!CREATURE:SHARK_HAMMERHEAD:STOMACH +!CREATURE:SHARK_HAMMERHEAD:GIZZARD +"CREATURE:SHARK_HAMMERHEAD:PANCREAS + CREATURE:SHARK_HAMMERHEAD:SPLEEN + CREATURE:SHARK_HAMMERHEAD:KIDNEY +CREATURE:SHARK_ANGEL:MUSCLE +CREATURE:SHARK_ANGEL:EYE +CREATURE:SHARK_ANGEL:BRAIN +CREATURE:SHARK_ANGEL:LUNG +CREATURE:SHARK_ANGEL:HEART +CREATURE:SHARK_ANGEL:LIVER +CREATURE:SHARK_ANGEL:GUT +CREATURE:SHARK_ANGEL:STOMACH +CREATURE:SHARK_ANGEL:GIZZARD +CREATURE:SHARK_ANGEL:PANCREAS +CREATURE:SHARK_ANGEL:SPLEEN +CREATURE:SHARK_ANGEL:KIDNEY +!CREATURE:FISH_SKATE_COMMON:MUSCLE +CREATURE:FISH_SKATE_COMMON:EYE + CREATURE:FISH_SKATE_COMMON:BRAIN +CREATURE:FISH_SKATE_COMMON:LUNG + CREATURE:FISH_SKATE_COMMON:HEART + CREATURE:FISH_SKATE_COMMON:LIVER +CREATURE:FISH_SKATE_COMMON:GUT +"CREATURE:FISH_SKATE_COMMON:STOMACH +"CREATURE:FISH_SKATE_COMMON:GIZZARD +#CREATURE:FISH_SKATE_COMMON:PANCREAS +!CREATURE:FISH_SKATE_COMMON:SPLEEN +!CREATURE:FISH_SKATE_COMMON:KIDNEY +CREATURE:FISH_RAY_MANTA:MUSCLE +CREATURE:FISH_RAY_MANTA:EYE +CREATURE:FISH_RAY_MANTA:BRAIN +CREATURE:FISH_RAY_MANTA:LUNG +CREATURE:FISH_RAY_MANTA:HEART +CREATURE:FISH_RAY_MANTA:LIVER +CREATURE:FISH_RAY_MANTA:GUT +CREATURE:FISH_RAY_MANTA:STOMACH +CREATURE:FISH_RAY_MANTA:GIZZARD + CREATURE:FISH_RAY_MANTA:PANCREAS +CREATURE:FISH_RAY_MANTA:SPLEEN +CREATURE:FISH_RAY_MANTA:KIDNEY +CREATURE:FISH_STINGRAY:MUSCLE +CREATURE:FISH_STINGRAY:EYE +CREATURE:FISH_STINGRAY:BRAIN +CREATURE:FISH_STINGRAY:LUNG +CREATURE:FISH_STINGRAY:HEART +CREATURE:FISH_STINGRAY:LIVER +CREATURE:FISH_STINGRAY:GUT +CREATURE:FISH_STINGRAY:STOMACH +CREATURE:FISH_STINGRAY:GIZZARD +CREATURE:FISH_STINGRAY:PANCREAS +CREATURE:FISH_STINGRAY:SPLEEN +CREATURE:FISH_STINGRAY:KIDNEY +CREATURE:FISH_COELACANTH:MUSCLE +CREATURE:FISH_COELACANTH:EYE +CREATURE:FISH_COELACANTH:BRAIN +CREATURE:FISH_COELACANTH:LUNG +CREATURE:FISH_COELACANTH:HEART +CREATURE:FISH_COELACANTH:LIVER +CREATURE:FISH_COELACANTH:GUT + CREATURE:FISH_COELACANTH:STOMACH + CREATURE:FISH_COELACANTH:GIZZARD +!CREATURE:FISH_COELACANTH:PANCREAS +CREATURE:FISH_COELACANTH:SPLEEN +CREATURE:FISH_COELACANTH:KIDNEY +CREATURE:FISH_STURGEON:MUSCLE +CREATURE:FISH_STURGEON:EYE +CREATURE:FISH_STURGEON:BRAIN +CREATURE:FISH_STURGEON:LUNG +CREATURE:FISH_STURGEON:HEART +CREATURE:FISH_STURGEON:LIVER +CREATURE:FISH_STURGEON:GUT +CREATURE:FISH_STURGEON:STOMACH +CREATURE:FISH_STURGEON:GIZZARD +CREATURE:FISH_STURGEON:PANCREAS +CREATURE:FISH_STURGEON:SPLEEN +CREATURE:FISH_STURGEON:KIDNEY +CREATURE:FISH_CONGER_EEL:MUSCLE +CREATURE:FISH_CONGER_EEL:EYE +CREATURE:FISH_CONGER_EEL:BRAIN +CREATURE:FISH_CONGER_EEL:LUNG +CREATURE:FISH_CONGER_EEL:HEART +CREATURE:FISH_CONGER_EEL:LIVER +CREATURE:FISH_CONGER_EEL:GUT + CREATURE:FISH_CONGER_EEL:STOMACH + CREATURE:FISH_CONGER_EEL:GIZZARD +!CREATURE:FISH_CONGER_EEL:PANCREAS +CREATURE:FISH_CONGER_EEL:SPLEEN +CREATURE:FISH_CONGER_EEL:KIDNEY +CREATURE:FISH_MILKFISH:MUSCLE +CREATURE:FISH_MILKFISH:EYE +CREATURE:FISH_MILKFISH:BRAIN +CREATURE:FISH_MILKFISH:LUNG +CREATURE:FISH_MILKFISH:HEART +CREATURE:FISH_MILKFISH:LIVER +CREATURE:FISH_MILKFISH:GUT +CREATURE:FISH_MILKFISH:STOMACH +CREATURE:FISH_MILKFISH:GIZZARD +CREATURE:FISH_MILKFISH:PANCREAS +CREATURE:FISH_MILKFISH:SPLEEN +CREATURE:FISH_MILKFISH:KIDNEY +CREATURE:FISH_COD:MUSCLE +CREATURE:FISH_COD:EYE +CREATURE:FISH_COD:BRAIN +CREATURE:FISH_COD:LUNG +CREATURE:FISH_COD:HEART +CREATURE:FISH_COD:LIVER +CREATURE:FISH_COD:GUT +CREATURE:FISH_COD:STOMACH +CREATURE:FISH_COD:GIZZARD +CREATURE:FISH_COD:PANCREAS +CREATURE:FISH_COD:SPLEEN +CREATURE:FISH_COD:KIDNEY +CREATURE:FISH_OPAH:MUSCLE +CREATURE:FISH_OPAH:EYE +CREATURE:FISH_OPAH:BRAIN +CREATURE:FISH_OPAH:LUNG +CREATURE:FISH_OPAH:HEART +CREATURE:FISH_OPAH:LIVER +CREATURE:FISH_OPAH:GUT +CREATURE:FISH_OPAH:STOMACH +CREATURE:FISH_OPAH:GIZZARD +CREATURE:FISH_OPAH:PANCREAS +CREATURE:FISH_OPAH:SPLEEN +CREATURE:FISH_OPAH:KIDNEY +"CREATURE:FISH_GROUPER_GIANT:MUSCLE +CREATURE:FISH_GROUPER_GIANT:EYE +!CREATURE:FISH_GROUPER_GIANT:BRAIN + CREATURE:FISH_GROUPER_GIANT:LUNG +!CREATURE:FISH_GROUPER_GIANT:HEART +!CREATURE:FISH_GROUPER_GIANT:LIVER +CREATURE:FISH_GROUPER_GIANT:GUT +#CREATURE:FISH_GROUPER_GIANT:STOMACH +#CREATURE:FISH_GROUPER_GIANT:GIZZARD +$CREATURE:FISH_GROUPER_GIANT:PANCREAS +"CREATURE:FISH_GROUPER_GIANT:SPLEEN +"CREATURE:FISH_GROUPER_GIANT:KIDNEY +CREATURE:FISH_BLUEFISH:MUSCLE +CREATURE:FISH_BLUEFISH:EYE +CREATURE:FISH_BLUEFISH:BRAIN +CREATURE:FISH_BLUEFISH:LUNG +CREATURE:FISH_BLUEFISH:HEART +CREATURE:FISH_BLUEFISH:LIVER +CREATURE:FISH_BLUEFISH:GUT +CREATURE:FISH_BLUEFISH:STOMACH +CREATURE:FISH_BLUEFISH:GIZZARD +CREATURE:FISH_BLUEFISH:PANCREAS +CREATURE:FISH_BLUEFISH:SPLEEN +CREATURE:FISH_BLUEFISH:KIDNEY +"CREATURE:FISH_SUNFISH_OCEAN:MUSCLE +CREATURE:FISH_SUNFISH_OCEAN:EYE +!CREATURE:FISH_SUNFISH_OCEAN:BRAIN + CREATURE:FISH_SUNFISH_OCEAN:LUNG +!CREATURE:FISH_SUNFISH_OCEAN:HEART +!CREATURE:FISH_SUNFISH_OCEAN:LIVER +CREATURE:FISH_SUNFISH_OCEAN:GUT +#CREATURE:FISH_SUNFISH_OCEAN:STOMACH +#CREATURE:FISH_SUNFISH_OCEAN:GIZZARD +$CREATURE:FISH_SUNFISH_OCEAN:PANCREAS +"CREATURE:FISH_SUNFISH_OCEAN:SPLEEN +"CREATURE:FISH_SUNFISH_OCEAN:KIDNEY +CREATURE:FISH_SWORDFISH:MUSCLE +CREATURE:FISH_SWORDFISH:EYE +CREATURE:FISH_SWORDFISH:BRAIN +CREATURE:FISH_SWORDFISH:LUNG +CREATURE:FISH_SWORDFISH:HEART +CREATURE:FISH_SWORDFISH:LIVER +CREATURE:FISH_SWORDFISH:GUT +CREATURE:FISH_SWORDFISH:STOMACH +CREATURE:FISH_SWORDFISH:GIZZARD + CREATURE:FISH_SWORDFISH:PANCREAS +CREATURE:FISH_SWORDFISH:SPLEEN +CREATURE:FISH_SWORDFISH:KIDNEY +CREATURE:FISH_MARLIN:MUSCLE +CREATURE:FISH_MARLIN:EYE +CREATURE:FISH_MARLIN:BRAIN +CREATURE:FISH_MARLIN:LUNG +CREATURE:FISH_MARLIN:HEART +CREATURE:FISH_MARLIN:LIVER +CREATURE:FISH_MARLIN:GUT +CREATURE:FISH_MARLIN:STOMACH +CREATURE:FISH_MARLIN:GIZZARD +CREATURE:FISH_MARLIN:PANCREAS +CREATURE:FISH_MARLIN:SPLEEN +CREATURE:FISH_MARLIN:KIDNEY +CREATURE:FISH_HALIBUT:MUSCLE +CREATURE:FISH_HALIBUT:EYE +CREATURE:FISH_HALIBUT:BRAIN +CREATURE:FISH_HALIBUT:LUNG +CREATURE:FISH_HALIBUT:HEART +CREATURE:FISH_HALIBUT:LIVER +CREATURE:FISH_HALIBUT:GUT +CREATURE:FISH_HALIBUT:STOMACH +CREATURE:FISH_HALIBUT:GIZZARD +CREATURE:FISH_HALIBUT:PANCREAS +CREATURE:FISH_HALIBUT:SPLEEN +CREATURE:FISH_HALIBUT:KIDNEY +$CREATURE:FISH_BARRACUDA_GREAT:MUSCLE +!CREATURE:FISH_BARRACUDA_GREAT:EYE +#CREATURE:FISH_BARRACUDA_GREAT:BRAIN +"CREATURE:FISH_BARRACUDA_GREAT:LUNG +#CREATURE:FISH_BARRACUDA_GREAT:HEART +#CREATURE:FISH_BARRACUDA_GREAT:LIVER +!CREATURE:FISH_BARRACUDA_GREAT:GUT +%CREATURE:FISH_BARRACUDA_GREAT:STOMACH +%CREATURE:FISH_BARRACUDA_GREAT:GIZZARD +&CREATURE:FISH_BARRACUDA_GREAT:PANCREAS +$CREATURE:FISH_BARRACUDA_GREAT:SPLEEN +$CREATURE:FISH_BARRACUDA_GREAT:KIDNEY +!CREATURE:FISH_TUNA_BLUEFIN:MUSCLE +CREATURE:FISH_TUNA_BLUEFIN:EYE + CREATURE:FISH_TUNA_BLUEFIN:BRAIN +CREATURE:FISH_TUNA_BLUEFIN:LUNG + CREATURE:FISH_TUNA_BLUEFIN:HEART + CREATURE:FISH_TUNA_BLUEFIN:LIVER +CREATURE:FISH_TUNA_BLUEFIN:GUT +"CREATURE:FISH_TUNA_BLUEFIN:STOMACH +"CREATURE:FISH_TUNA_BLUEFIN:GIZZARD +#CREATURE:FISH_TUNA_BLUEFIN:PANCREAS +!CREATURE:FISH_TUNA_BLUEFIN:SPLEEN +!CREATURE:FISH_TUNA_BLUEFIN:KIDNEY +CREATURE:NARWHAL:MUSCLE +CREATURE:NARWHAL:EYE +CREATURE:NARWHAL:BRAIN +CREATURE:NARWHAL:LUNG +CREATURE:NARWHAL:HEART +CREATURE:NARWHAL:LIVER +CREATURE:NARWHAL:GUT +CREATURE:NARWHAL:STOMACH +CREATURE:NARWHAL:GIZZARD +CREATURE:NARWHAL:PANCREAS +CREATURE:NARWHAL:SPLEEN +CREATURE:NARWHAL:KIDNEY +CREATURE:NARWHAL MAN:MUSCLE +CREATURE:NARWHAL MAN:EYE +CREATURE:NARWHAL MAN:BRAIN +CREATURE:NARWHAL MAN:LUNG +CREATURE:NARWHAL MAN:HEART +CREATURE:NARWHAL MAN:LIVER +CREATURE:NARWHAL MAN:GUT +CREATURE:NARWHAL MAN:STOMACH +CREATURE:NARWHAL MAN:GIZZARD +CREATURE:NARWHAL MAN:PANCREAS +CREATURE:NARWHAL MAN:SPLEEN +CREATURE:NARWHAL MAN:KIDNEY +CREATURE:NARWHAL, GIANT:MUSCLE +CREATURE:NARWHAL, GIANT:EYE +CREATURE:NARWHAL, GIANT:BRAIN +CREATURE:NARWHAL, GIANT:LUNG +CREATURE:NARWHAL, GIANT:HEART +CREATURE:NARWHAL, GIANT:LIVER +CREATURE:NARWHAL, GIANT:GUT +CREATURE:NARWHAL, GIANT:STOMACH +CREATURE:NARWHAL, GIANT:GIZZARD + CREATURE:NARWHAL, GIANT:PANCREAS +CREATURE:NARWHAL, GIANT:SPLEEN +CREATURE:NARWHAL, GIANT:KIDNEY +CREATURE:HIPPO:MUSCLE +CREATURE:HIPPO:EYE +CREATURE:HIPPO:BRAIN +CREATURE:HIPPO:LUNG +CREATURE:HIPPO:HEART +CREATURE:HIPPO:LIVER +CREATURE:HIPPO:GUT +CREATURE:HIPPO:STOMACH +CREATURE:HIPPO:GIZZARD +CREATURE:HIPPO:PANCREAS +CREATURE:HIPPO:SPLEEN +CREATURE:HIPPO:KIDNEY +CREATURE:HIPPO_MAN:MUSCLE +CREATURE:HIPPO_MAN:EYE +CREATURE:HIPPO_MAN:BRAIN +CREATURE:HIPPO_MAN:LUNG +CREATURE:HIPPO_MAN:HEART +CREATURE:HIPPO_MAN:LIVER +CREATURE:HIPPO_MAN:GUT +CREATURE:HIPPO_MAN:STOMACH +CREATURE:HIPPO_MAN:GIZZARD +CREATURE:HIPPO_MAN:PANCREAS +CREATURE:HIPPO_MAN:SPLEEN +CREATURE:HIPPO_MAN:KIDNEY +CREATURE:GIANT_HIPPO:MUSCLE +CREATURE:GIANT_HIPPO:EYE +CREATURE:GIANT_HIPPO:BRAIN +CREATURE:GIANT_HIPPO:LUNG +CREATURE:GIANT_HIPPO:HEART +CREATURE:GIANT_HIPPO:LIVER +CREATURE:GIANT_HIPPO:GUT +CREATURE:GIANT_HIPPO:STOMACH +CREATURE:GIANT_HIPPO:GIZZARD +CREATURE:GIANT_HIPPO:PANCREAS +CREATURE:GIANT_HIPPO:SPLEEN +CREATURE:GIANT_HIPPO:KIDNEY +!CREATURE:FISH_GAR_LONGNOSE:MUSCLE +CREATURE:FISH_GAR_LONGNOSE:EYE + CREATURE:FISH_GAR_LONGNOSE:BRAIN +CREATURE:FISH_GAR_LONGNOSE:LUNG + CREATURE:FISH_GAR_LONGNOSE:HEART + CREATURE:FISH_GAR_LONGNOSE:LIVER +CREATURE:FISH_GAR_LONGNOSE:GUT +"CREATURE:FISH_GAR_LONGNOSE:STOMACH +"CREATURE:FISH_GAR_LONGNOSE:GIZZARD +#CREATURE:FISH_GAR_LONGNOSE:PANCREAS +!CREATURE:FISH_GAR_LONGNOSE:SPLEEN +!CREATURE:FISH_GAR_LONGNOSE:KIDNEY +CREATURE:FISH_CARP:MUSCLE +CREATURE:FISH_CARP:EYE +CREATURE:FISH_CARP:BRAIN +CREATURE:FISH_CARP:LUNG +CREATURE:FISH_CARP:HEART +CREATURE:FISH_CARP:LIVER +CREATURE:FISH_CARP:GUT +CREATURE:FISH_CARP:STOMACH +CREATURE:FISH_CARP:GIZZARD +CREATURE:FISH_CARP:PANCREAS +CREATURE:FISH_CARP:SPLEEN +CREATURE:FISH_CARP:KIDNEY +CREATURE:FISH_TIGERFISH:MUSCLE +CREATURE:FISH_TIGERFISH:EYE +CREATURE:FISH_TIGERFISH:BRAIN +CREATURE:FISH_TIGERFISH:LUNG +CREATURE:FISH_TIGERFISH:HEART +CREATURE:FISH_TIGERFISH:LIVER +CREATURE:FISH_TIGERFISH:GUT +CREATURE:FISH_TIGERFISH:STOMACH +CREATURE:FISH_TIGERFISH:GIZZARD + CREATURE:FISH_TIGERFISH:PANCREAS +CREATURE:FISH_TIGERFISH:SPLEEN +CREATURE:FISH_TIGERFISH:KIDNEY +CREATURE:FISH_PIKE:MUSCLE +CREATURE:FISH_PIKE:EYE +CREATURE:FISH_PIKE:BRAIN +CREATURE:FISH_PIKE:LUNG +CREATURE:FISH_PIKE:HEART +CREATURE:FISH_PIKE:LIVER +CREATURE:FISH_PIKE:GUT +CREATURE:FISH_PIKE:STOMACH +CREATURE:FISH_PIKE:GIZZARD +CREATURE:FISH_PIKE:PANCREAS +CREATURE:FISH_PIKE:SPLEEN +CREATURE:FISH_PIKE:KIDNEY +CREATURE:PLATYPUS:MUSCLE +CREATURE:PLATYPUS:EYE +CREATURE:PLATYPUS:BRAIN +CREATURE:PLATYPUS:LUNG +CREATURE:PLATYPUS:HEART +CREATURE:PLATYPUS:LIVER +CREATURE:PLATYPUS:GUT +CREATURE:PLATYPUS:STOMACH +CREATURE:PLATYPUS:GIZZARD +CREATURE:PLATYPUS:PANCREAS +CREATURE:PLATYPUS:SPLEEN +CREATURE:PLATYPUS:KIDNEY +CREATURE:PLATYPUS MAN:MUSCLE +CREATURE:PLATYPUS MAN:EYE +CREATURE:PLATYPUS MAN:BRAIN +CREATURE:PLATYPUS MAN:LUNG +CREATURE:PLATYPUS MAN:HEART +CREATURE:PLATYPUS MAN:LIVER +CREATURE:PLATYPUS MAN:GUT +CREATURE:PLATYPUS MAN:STOMACH +CREATURE:PLATYPUS MAN:GIZZARD +CREATURE:PLATYPUS MAN:PANCREAS +CREATURE:PLATYPUS MAN:SPLEEN +CREATURE:PLATYPUS MAN:KIDNEY +CREATURE:PLATYPUS, GIANT:MUSCLE +CREATURE:PLATYPUS, GIANT:EYE +CREATURE:PLATYPUS, GIANT:BRAIN +CREATURE:PLATYPUS, GIANT:LUNG +CREATURE:PLATYPUS, GIANT:HEART +CREATURE:PLATYPUS, GIANT:LIVER +CREATURE:PLATYPUS, GIANT:GUT + CREATURE:PLATYPUS, GIANT:STOMACH + CREATURE:PLATYPUS, GIANT:GIZZARD +!CREATURE:PLATYPUS, GIANT:PANCREAS +CREATURE:PLATYPUS, GIANT:SPLEEN +CREATURE:PLATYPUS, GIANT:KIDNEY +CREATURE:BEAR_GRIZZLY:MUSCLE +CREATURE:BEAR_GRIZZLY:EYE +CREATURE:BEAR_GRIZZLY:BRAIN +CREATURE:BEAR_GRIZZLY:LUNG +CREATURE:BEAR_GRIZZLY:HEART +CREATURE:BEAR_GRIZZLY:LIVER +CREATURE:BEAR_GRIZZLY:GUT +CREATURE:BEAR_GRIZZLY:STOMACH +CREATURE:BEAR_GRIZZLY:GIZZARD +CREATURE:BEAR_GRIZZLY:PANCREAS +CREATURE:BEAR_GRIZZLY:SPLEEN +CREATURE:BEAR_GRIZZLY:KIDNEY + CREATURE:BEAR_GRIZZLY_MAN:MUSCLE +CREATURE:BEAR_GRIZZLY_MAN:EYE +CREATURE:BEAR_GRIZZLY_MAN:BRAIN +CREATURE:BEAR_GRIZZLY_MAN:LUNG +CREATURE:BEAR_GRIZZLY_MAN:HEART +CREATURE:BEAR_GRIZZLY_MAN:LIVER +CREATURE:BEAR_GRIZZLY_MAN:GUT +!CREATURE:BEAR_GRIZZLY_MAN:STOMACH +!CREATURE:BEAR_GRIZZLY_MAN:GIZZARD +"CREATURE:BEAR_GRIZZLY_MAN:PANCREAS + CREATURE:BEAR_GRIZZLY_MAN:SPLEEN + CREATURE:BEAR_GRIZZLY_MAN:KIDNEY +"CREATURE:GIANT_BEAR_GRIZZLY:MUSCLE +CREATURE:GIANT_BEAR_GRIZZLY:EYE +!CREATURE:GIANT_BEAR_GRIZZLY:BRAIN + CREATURE:GIANT_BEAR_GRIZZLY:LUNG +!CREATURE:GIANT_BEAR_GRIZZLY:HEART +!CREATURE:GIANT_BEAR_GRIZZLY:LIVER +CREATURE:GIANT_BEAR_GRIZZLY:GUT +#CREATURE:GIANT_BEAR_GRIZZLY:STOMACH +#CREATURE:GIANT_BEAR_GRIZZLY:GIZZARD +$CREATURE:GIANT_BEAR_GRIZZLY:PANCREAS +"CREATURE:GIANT_BEAR_GRIZZLY:SPLEEN +"CREATURE:GIANT_BEAR_GRIZZLY:KIDNEY +CREATURE:BEAR_BLACK:MUSCLE +CREATURE:BEAR_BLACK:EYE +CREATURE:BEAR_BLACK:BRAIN +CREATURE:BEAR_BLACK:LUNG +CREATURE:BEAR_BLACK:HEART +CREATURE:BEAR_BLACK:LIVER +CREATURE:BEAR_BLACK:GUT +CREATURE:BEAR_BLACK:STOMACH +CREATURE:BEAR_BLACK:GIZZARD +CREATURE:BEAR_BLACK:PANCREAS +CREATURE:BEAR_BLACK:SPLEEN +CREATURE:BEAR_BLACK:KIDNEY +CREATURE:BEAR_BLACK_MAN:MUSCLE +CREATURE:BEAR_BLACK_MAN:EYE +CREATURE:BEAR_BLACK_MAN:BRAIN +CREATURE:BEAR_BLACK_MAN:LUNG +CREATURE:BEAR_BLACK_MAN:HEART +CREATURE:BEAR_BLACK_MAN:LIVER +CREATURE:BEAR_BLACK_MAN:GUT +CREATURE:BEAR_BLACK_MAN:STOMACH +CREATURE:BEAR_BLACK_MAN:GIZZARD + CREATURE:BEAR_BLACK_MAN:PANCREAS +CREATURE:BEAR_BLACK_MAN:SPLEEN +CREATURE:BEAR_BLACK_MAN:KIDNEY + CREATURE:GIANT_BEAR_BLACK:MUSCLE +CREATURE:GIANT_BEAR_BLACK:EYE +CREATURE:GIANT_BEAR_BLACK:BRAIN +CREATURE:GIANT_BEAR_BLACK:LUNG +CREATURE:GIANT_BEAR_BLACK:HEART +CREATURE:GIANT_BEAR_BLACK:LIVER +CREATURE:GIANT_BEAR_BLACK:GUT +!CREATURE:GIANT_BEAR_BLACK:STOMACH +!CREATURE:GIANT_BEAR_BLACK:GIZZARD +"CREATURE:GIANT_BEAR_BLACK:PANCREAS + CREATURE:GIANT_BEAR_BLACK:SPLEEN + CREATURE:GIANT_BEAR_BLACK:KIDNEY +CREATURE:DEER:MUSCLE +CREATURE:DEER:EYE +CREATURE:DEER:BRAIN +CREATURE:DEER:LUNG +CREATURE:DEER:HEART +CREATURE:DEER:LIVER +CREATURE:DEER:GUT +CREATURE:DEER:STOMACH +CREATURE:DEER:GIZZARD +CREATURE:DEER:PANCREAS +CREATURE:DEER:SPLEEN +CREATURE:DEER:KIDNEY +CREATURE:DEER_MAN:MUSCLE +CREATURE:DEER_MAN:EYE +CREATURE:DEER_MAN:BRAIN +CREATURE:DEER_MAN:LUNG +CREATURE:DEER_MAN:HEART +CREATURE:DEER_MAN:LIVER +CREATURE:DEER_MAN:GUT +CREATURE:DEER_MAN:STOMACH +CREATURE:DEER_MAN:GIZZARD +CREATURE:DEER_MAN:PANCREAS +CREATURE:DEER_MAN:SPLEEN +CREATURE:DEER_MAN:KIDNEY +CREATURE:GIANT_DEER:MUSCLE +CREATURE:GIANT_DEER:EYE +CREATURE:GIANT_DEER:BRAIN +CREATURE:GIANT_DEER:LUNG +CREATURE:GIANT_DEER:HEART +CREATURE:GIANT_DEER:LIVER +CREATURE:GIANT_DEER:GUT +CREATURE:GIANT_DEER:STOMACH +CREATURE:GIANT_DEER:GIZZARD +CREATURE:GIANT_DEER:PANCREAS +CREATURE:GIANT_DEER:SPLEEN +CREATURE:GIANT_DEER:KIDNEY +CREATURE:FOX:MUSCLE +CREATURE:FOX:EYE +CREATURE:FOX:BRAIN +CREATURE:FOX:LUNG +CREATURE:FOX:HEART +CREATURE:FOX:LIVER +CREATURE:FOX:GUT +CREATURE:FOX:STOMACH +CREATURE:FOX:GIZZARD +CREATURE:FOX:PANCREAS +CREATURE:FOX:SPLEEN +CREATURE:FOX:KIDNEY +CREATURE:FOX_MAN:MUSCLE +CREATURE:FOX_MAN:EYE +CREATURE:FOX_MAN:BRAIN +CREATURE:FOX_MAN:LUNG +CREATURE:FOX_MAN:HEART +CREATURE:FOX_MAN:LIVER +CREATURE:FOX_MAN:GUT +CREATURE:FOX_MAN:STOMACH +CREATURE:FOX_MAN:GIZZARD +CREATURE:FOX_MAN:PANCREAS +CREATURE:FOX_MAN:SPLEEN +CREATURE:FOX_MAN:KIDNEY +CREATURE:GIANT_FOX:MUSCLE +CREATURE:GIANT_FOX:EYE +CREATURE:GIANT_FOX:BRAIN +CREATURE:GIANT_FOX:LUNG +CREATURE:GIANT_FOX:HEART +CREATURE:GIANT_FOX:LIVER +CREATURE:GIANT_FOX:GUT +CREATURE:GIANT_FOX:STOMACH +CREATURE:GIANT_FOX:GIZZARD +CREATURE:GIANT_FOX:PANCREAS +CREATURE:GIANT_FOX:SPLEEN +CREATURE:GIANT_FOX:KIDNEY +CREATURE:RACCOON:MUSCLE +CREATURE:RACCOON:EYE +CREATURE:RACCOON:BRAIN +CREATURE:RACCOON:LUNG +CREATURE:RACCOON:HEART +CREATURE:RACCOON:LIVER +CREATURE:RACCOON:GUT +CREATURE:RACCOON:STOMACH +CREATURE:RACCOON:GIZZARD +CREATURE:RACCOON:PANCREAS +CREATURE:RACCOON:SPLEEN +CREATURE:RACCOON:KIDNEY +CREATURE:RACCOON_MAN:MUSCLE +CREATURE:RACCOON_MAN:EYE +CREATURE:RACCOON_MAN:BRAIN +CREATURE:RACCOON_MAN:LUNG +CREATURE:RACCOON_MAN:HEART +CREATURE:RACCOON_MAN:LIVER +CREATURE:RACCOON_MAN:GUT +CREATURE:RACCOON_MAN:STOMACH +CREATURE:RACCOON_MAN:GIZZARD +CREATURE:RACCOON_MAN:PANCREAS +CREATURE:RACCOON_MAN:SPLEEN +CREATURE:RACCOON_MAN:KIDNEY +CREATURE:GIANT_RACCOON:MUSCLE +CREATURE:GIANT_RACCOON:EYE +CREATURE:GIANT_RACCOON:BRAIN +CREATURE:GIANT_RACCOON:LUNG +CREATURE:GIANT_RACCOON:HEART +CREATURE:GIANT_RACCOON:LIVER +CREATURE:GIANT_RACCOON:GUT +CREATURE:GIANT_RACCOON:STOMACH +CREATURE:GIANT_RACCOON:GIZZARD +CREATURE:GIANT_RACCOON:PANCREAS +CREATURE:GIANT_RACCOON:SPLEEN +CREATURE:GIANT_RACCOON:KIDNEY +CREATURE:MACAQUE_RHESUS:MUSCLE +CREATURE:MACAQUE_RHESUS:EYE +CREATURE:MACAQUE_RHESUS:BRAIN +CREATURE:MACAQUE_RHESUS:LUNG +CREATURE:MACAQUE_RHESUS:HEART +CREATURE:MACAQUE_RHESUS:LIVER +CREATURE:MACAQUE_RHESUS:GUT +CREATURE:MACAQUE_RHESUS:STOMACH +CREATURE:MACAQUE_RHESUS:GIZZARD + CREATURE:MACAQUE_RHESUS:PANCREAS +CREATURE:MACAQUE_RHESUS:SPLEEN +CREATURE:MACAQUE_RHESUS:KIDNEY +"CREATURE:MACAQUE_RHESUS_MAN:MUSCLE +CREATURE:MACAQUE_RHESUS_MAN:EYE +!CREATURE:MACAQUE_RHESUS_MAN:BRAIN + CREATURE:MACAQUE_RHESUS_MAN:LUNG +!CREATURE:MACAQUE_RHESUS_MAN:HEART +!CREATURE:MACAQUE_RHESUS_MAN:LIVER +CREATURE:MACAQUE_RHESUS_MAN:GUT +#CREATURE:MACAQUE_RHESUS_MAN:STOMACH +#CREATURE:MACAQUE_RHESUS_MAN:GIZZARD +$CREATURE:MACAQUE_RHESUS_MAN:PANCREAS +"CREATURE:MACAQUE_RHESUS_MAN:SPLEEN +"CREATURE:MACAQUE_RHESUS_MAN:KIDNEY +$CREATURE:GIANT_MACAQUE_RHESUS:MUSCLE +!CREATURE:GIANT_MACAQUE_RHESUS:EYE +#CREATURE:GIANT_MACAQUE_RHESUS:BRAIN +"CREATURE:GIANT_MACAQUE_RHESUS:LUNG +#CREATURE:GIANT_MACAQUE_RHESUS:HEART +#CREATURE:GIANT_MACAQUE_RHESUS:LIVER +!CREATURE:GIANT_MACAQUE_RHESUS:GUT +%CREATURE:GIANT_MACAQUE_RHESUS:STOMACH +%CREATURE:GIANT_MACAQUE_RHESUS:GIZZARD +&CREATURE:GIANT_MACAQUE_RHESUS:PANCREAS +$CREATURE:GIANT_MACAQUE_RHESUS:SPLEEN +$CREATURE:GIANT_MACAQUE_RHESUS:KIDNEY +CREATURE:COUGAR:MUSCLE +CREATURE:COUGAR:EYE +CREATURE:COUGAR:BRAIN +CREATURE:COUGAR:LUNG +CREATURE:COUGAR:HEART +CREATURE:COUGAR:LIVER +CREATURE:COUGAR:GUT +CREATURE:COUGAR:STOMACH +CREATURE:COUGAR:GIZZARD +CREATURE:COUGAR:PANCREAS +CREATURE:COUGAR:SPLEEN +CREATURE:COUGAR:KIDNEY +CREATURE:COUGAR_MAN:MUSCLE +CREATURE:COUGAR_MAN:EYE +CREATURE:COUGAR_MAN:BRAIN +CREATURE:COUGAR_MAN:LUNG +CREATURE:COUGAR_MAN:HEART +CREATURE:COUGAR_MAN:LIVER +CREATURE:COUGAR_MAN:GUT +CREATURE:COUGAR_MAN:STOMACH +CREATURE:COUGAR_MAN:GIZZARD +CREATURE:COUGAR_MAN:PANCREAS +CREATURE:COUGAR_MAN:SPLEEN +CREATURE:COUGAR_MAN:KIDNEY +CREATURE:GIANT_COUGAR:MUSCLE +CREATURE:GIANT_COUGAR:EYE +CREATURE:GIANT_COUGAR:BRAIN +CREATURE:GIANT_COUGAR:LUNG +CREATURE:GIANT_COUGAR:HEART +CREATURE:GIANT_COUGAR:LIVER +CREATURE:GIANT_COUGAR:GUT +CREATURE:GIANT_COUGAR:STOMACH +CREATURE:GIANT_COUGAR:GIZZARD +CREATURE:GIANT_COUGAR:PANCREAS +CREATURE:GIANT_COUGAR:SPLEEN +CREATURE:GIANT_COUGAR:KIDNEY +CREATURE:WOLF:MUSCLE +CREATURE:WOLF:EYE +CREATURE:WOLF:BRAIN +CREATURE:WOLF:LUNG +CREATURE:WOLF:HEART +CREATURE:WOLF:LIVER +CREATURE:WOLF:GUT +CREATURE:WOLF:STOMACH +CREATURE:WOLF:GIZZARD +CREATURE:WOLF:PANCREAS +CREATURE:WOLF:SPLEEN +CREATURE:WOLF:KIDNEY +CREATURE:WOLF_MAN:MUSCLE +CREATURE:WOLF_MAN:EYE +CREATURE:WOLF_MAN:BRAIN +CREATURE:WOLF_MAN:LUNG +CREATURE:WOLF_MAN:HEART +CREATURE:WOLF_MAN:LIVER +CREATURE:WOLF_MAN:GUT +CREATURE:WOLF_MAN:STOMACH +CREATURE:WOLF_MAN:GIZZARD +CREATURE:WOLF_MAN:PANCREAS +CREATURE:WOLF_MAN:SPLEEN +CREATURE:WOLF_MAN:KIDNEY +CREATURE:GIANT_WOLF:MUSCLE +CREATURE:GIANT_WOLF:EYE +CREATURE:GIANT_WOLF:BRAIN +CREATURE:GIANT_WOLF:LUNG +CREATURE:GIANT_WOLF:HEART +CREATURE:GIANT_WOLF:LIVER +CREATURE:GIANT_WOLF:GUT +CREATURE:GIANT_WOLF:STOMACH +CREATURE:GIANT_WOLF:GIZZARD +CREATURE:GIANT_WOLF:PANCREAS +CREATURE:GIANT_WOLF:SPLEEN +CREATURE:GIANT_WOLF:KIDNEY +CREATURE:GROUNDHOG:MUSCLE +CREATURE:GROUNDHOG:EYE +CREATURE:GROUNDHOG:BRAIN +CREATURE:GROUNDHOG:LUNG +CREATURE:GROUNDHOG:HEART +CREATURE:GROUNDHOG:LIVER +CREATURE:GROUNDHOG:GUT +CREATURE:GROUNDHOG:STOMACH +CREATURE:GROUNDHOG:GIZZARD +CREATURE:GROUNDHOG:PANCREAS +CREATURE:GROUNDHOG:SPLEEN +CREATURE:GROUNDHOG:KIDNEY +CREATURE:GROUNDHOG_MAN:MUSCLE +CREATURE:GROUNDHOG_MAN:EYE +CREATURE:GROUNDHOG_MAN:BRAIN +CREATURE:GROUNDHOG_MAN:LUNG +CREATURE:GROUNDHOG_MAN:HEART +CREATURE:GROUNDHOG_MAN:LIVER +CREATURE:GROUNDHOG_MAN:GUT +CREATURE:GROUNDHOG_MAN:STOMACH +CREATURE:GROUNDHOG_MAN:GIZZARD +CREATURE:GROUNDHOG_MAN:PANCREAS +CREATURE:GROUNDHOG_MAN:SPLEEN +CREATURE:GROUNDHOG_MAN:KIDNEY +CREATURE:GIANT_GROUNDHOG:MUSCLE +CREATURE:GIANT_GROUNDHOG:EYE +CREATURE:GIANT_GROUNDHOG:BRAIN +CREATURE:GIANT_GROUNDHOG:LUNG +CREATURE:GIANT_GROUNDHOG:HEART +CREATURE:GIANT_GROUNDHOG:LIVER +CREATURE:GIANT_GROUNDHOG:GUT + CREATURE:GIANT_GROUNDHOG:STOMACH + CREATURE:GIANT_GROUNDHOG:GIZZARD +!CREATURE:GIANT_GROUNDHOG:PANCREAS +CREATURE:GIANT_GROUNDHOG:SPLEEN +CREATURE:GIANT_GROUNDHOG:KIDNEY +CREATURE:ALLIGATOR:MUSCLE +CREATURE:ALLIGATOR:EYE +CREATURE:ALLIGATOR:BRAIN +CREATURE:ALLIGATOR:LUNG +CREATURE:ALLIGATOR:HEART +CREATURE:ALLIGATOR:LIVER +CREATURE:ALLIGATOR:GUT +CREATURE:ALLIGATOR:STOMACH +CREATURE:ALLIGATOR:GIZZARD +CREATURE:ALLIGATOR:PANCREAS +CREATURE:ALLIGATOR:SPLEEN +CREATURE:ALLIGATOR:KIDNEY +CREATURE:ALLIGATOR_MAN:MUSCLE +CREATURE:ALLIGATOR_MAN:EYE +CREATURE:ALLIGATOR_MAN:BRAIN +CREATURE:ALLIGATOR_MAN:LUNG +CREATURE:ALLIGATOR_MAN:HEART +CREATURE:ALLIGATOR_MAN:LIVER +CREATURE:ALLIGATOR_MAN:GUT +CREATURE:ALLIGATOR_MAN:STOMACH +CREATURE:ALLIGATOR_MAN:GIZZARD +CREATURE:ALLIGATOR_MAN:PANCREAS +CREATURE:ALLIGATOR_MAN:SPLEEN +CREATURE:ALLIGATOR_MAN:KIDNEY +CREATURE:GIANT_ALLIGATOR:MUSCLE +CREATURE:GIANT_ALLIGATOR:EYE +CREATURE:GIANT_ALLIGATOR:BRAIN +CREATURE:GIANT_ALLIGATOR:LUNG +CREATURE:GIANT_ALLIGATOR:HEART +CREATURE:GIANT_ALLIGATOR:LIVER +CREATURE:GIANT_ALLIGATOR:GUT + CREATURE:GIANT_ALLIGATOR:STOMACH + CREATURE:GIANT_ALLIGATOR:GIZZARD +!CREATURE:GIANT_ALLIGATOR:PANCREAS +CREATURE:GIANT_ALLIGATOR:SPLEEN +CREATURE:GIANT_ALLIGATOR:KIDNEY +CREATURE:BIRD_BUZZARD:MUSCLE +CREATURE:BIRD_BUZZARD:EYE +CREATURE:BIRD_BUZZARD:BRAIN +CREATURE:BIRD_BUZZARD:LUNG +CREATURE:BIRD_BUZZARD:HEART +CREATURE:BIRD_BUZZARD:LIVER +CREATURE:BIRD_BUZZARD:GUT +CREATURE:BIRD_BUZZARD:STOMACH +CREATURE:BIRD_BUZZARD:GIZZARD +CREATURE:BIRD_BUZZARD:PANCREAS +CREATURE:BIRD_BUZZARD:SPLEEN +CREATURE:BIRD_BUZZARD:KIDNEY +CREATURE:BUZZARD_MAN:MUSCLE +CREATURE:BUZZARD_MAN:EYE +CREATURE:BUZZARD_MAN:BRAIN +CREATURE:BUZZARD_MAN:LUNG +CREATURE:BUZZARD_MAN:HEART +CREATURE:BUZZARD_MAN:LIVER +CREATURE:BUZZARD_MAN:GUT +CREATURE:BUZZARD_MAN:STOMACH +CREATURE:BUZZARD_MAN:GIZZARD +CREATURE:BUZZARD_MAN:PANCREAS +CREATURE:BUZZARD_MAN:SPLEEN +CREATURE:BUZZARD_MAN:KIDNEY +CREATURE:GIANT_BUZZARD:MUSCLE +CREATURE:GIANT_BUZZARD:EYE +CREATURE:GIANT_BUZZARD:BRAIN +CREATURE:GIANT_BUZZARD:LUNG +CREATURE:GIANT_BUZZARD:HEART +CREATURE:GIANT_BUZZARD:LIVER +CREATURE:GIANT_BUZZARD:GUT +CREATURE:GIANT_BUZZARD:STOMACH +CREATURE:GIANT_BUZZARD:GIZZARD +CREATURE:GIANT_BUZZARD:PANCREAS +CREATURE:GIANT_BUZZARD:SPLEEN +CREATURE:GIANT_BUZZARD:KIDNEY +CREATURE:PANDA:MUSCLE +CREATURE:PANDA:EYE +CREATURE:PANDA:BRAIN +CREATURE:PANDA:LUNG +CREATURE:PANDA:HEART +CREATURE:PANDA:LIVER +CREATURE:PANDA:GUT +CREATURE:PANDA:STOMACH +CREATURE:PANDA:GIZZARD +CREATURE:PANDA:PANCREAS +CREATURE:PANDA:SPLEEN +CREATURE:PANDA:KIDNEY +CREATURE:PANDA, GIGANTIC:MUSCLE +CREATURE:PANDA, GIGANTIC:EYE +CREATURE:PANDA, GIGANTIC:BRAIN +CREATURE:PANDA, GIGANTIC:LUNG +CREATURE:PANDA, GIGANTIC:HEART +CREATURE:PANDA, GIGANTIC:LIVER +CREATURE:PANDA, GIGANTIC:GUT + CREATURE:PANDA, GIGANTIC:STOMACH + CREATURE:PANDA, GIGANTIC:GIZZARD +!CREATURE:PANDA, GIGANTIC:PANCREAS +CREATURE:PANDA, GIGANTIC:SPLEEN +CREATURE:PANDA, GIGANTIC:KIDNEY +CREATURE:PANDA MAN:MUSCLE +CREATURE:PANDA MAN:EYE +CREATURE:PANDA MAN:BRAIN +CREATURE:PANDA MAN:LUNG +CREATURE:PANDA MAN:HEART +CREATURE:PANDA MAN:LIVER +CREATURE:PANDA MAN:GUT +CREATURE:PANDA MAN:STOMACH +CREATURE:PANDA MAN:GIZZARD +CREATURE:PANDA MAN:PANCREAS +CREATURE:PANDA MAN:SPLEEN +CREATURE:PANDA MAN:KIDNEY +CREATURE:CAPYBARA:MUSCLE +CREATURE:CAPYBARA:EYE +CREATURE:CAPYBARA:BRAIN +CREATURE:CAPYBARA:LUNG +CREATURE:CAPYBARA:HEART +CREATURE:CAPYBARA:LIVER +CREATURE:CAPYBARA:GUT +CREATURE:CAPYBARA:STOMACH +CREATURE:CAPYBARA:GIZZARD +CREATURE:CAPYBARA:PANCREAS +CREATURE:CAPYBARA:SPLEEN +CREATURE:CAPYBARA:KIDNEY +CREATURE:CAPYBARA, GIANT:MUSCLE +CREATURE:CAPYBARA, GIANT:EYE +CREATURE:CAPYBARA, GIANT:BRAIN +CREATURE:CAPYBARA, GIANT:LUNG +CREATURE:CAPYBARA, GIANT:HEART +CREATURE:CAPYBARA, GIANT:LIVER +CREATURE:CAPYBARA, GIANT:GUT + CREATURE:CAPYBARA, GIANT:STOMACH + CREATURE:CAPYBARA, GIANT:GIZZARD +!CREATURE:CAPYBARA, GIANT:PANCREAS +CREATURE:CAPYBARA, GIANT:SPLEEN +CREATURE:CAPYBARA, GIANT:KIDNEY +CREATURE:CAPYBARA MAN:MUSCLE +CREATURE:CAPYBARA MAN:EYE +CREATURE:CAPYBARA MAN:BRAIN +CREATURE:CAPYBARA MAN:LUNG +CREATURE:CAPYBARA MAN:HEART +CREATURE:CAPYBARA MAN:LIVER +CREATURE:CAPYBARA MAN:GUT +CREATURE:CAPYBARA MAN:STOMACH +CREATURE:CAPYBARA MAN:GIZZARD +CREATURE:CAPYBARA MAN:PANCREAS +CREATURE:CAPYBARA MAN:SPLEEN +CREATURE:CAPYBARA MAN:KIDNEY +CREATURE:BADGER:MUSCLE +CREATURE:BADGER:EYE +CREATURE:BADGER:BRAIN +CREATURE:BADGER:LUNG +CREATURE:BADGER:HEART +CREATURE:BADGER:LIVER +CREATURE:BADGER:GUT +CREATURE:BADGER:STOMACH +CREATURE:BADGER:GIZZARD +CREATURE:BADGER:PANCREAS +CREATURE:BADGER:SPLEEN +CREATURE:BADGER:KIDNEY +CREATURE:BADGER MAN:MUSCLE +CREATURE:BADGER MAN:EYE +CREATURE:BADGER MAN:BRAIN +CREATURE:BADGER MAN:LUNG +CREATURE:BADGER MAN:HEART +CREATURE:BADGER MAN:LIVER +CREATURE:BADGER MAN:GUT +CREATURE:BADGER MAN:STOMACH +CREATURE:BADGER MAN:GIZZARD +CREATURE:BADGER MAN:PANCREAS +CREATURE:BADGER MAN:SPLEEN +CREATURE:BADGER MAN:KIDNEY +CREATURE:BADGER, GIANT:MUSCLE +CREATURE:BADGER, GIANT:EYE +CREATURE:BADGER, GIANT:BRAIN +CREATURE:BADGER, GIANT:LUNG +CREATURE:BADGER, GIANT:HEART +CREATURE:BADGER, GIANT:LIVER +CREATURE:BADGER, GIANT:GUT +CREATURE:BADGER, GIANT:STOMACH +CREATURE:BADGER, GIANT:GIZZARD +CREATURE:BADGER, GIANT:PANCREAS +CREATURE:BADGER, GIANT:SPLEEN +CREATURE:BADGER, GIANT:KIDNEY +CREATURE:MOOSE:MUSCLE +CREATURE:MOOSE:EYE +CREATURE:MOOSE:BRAIN +CREATURE:MOOSE:LUNG +CREATURE:MOOSE:HEART +CREATURE:MOOSE:LIVER +CREATURE:MOOSE:GUT +CREATURE:MOOSE:STOMACH +CREATURE:MOOSE:GIZZARD +CREATURE:MOOSE:PANCREAS +CREATURE:MOOSE:SPLEEN +CREATURE:MOOSE:KIDNEY +CREATURE:MOOSE MAN:MUSCLE +CREATURE:MOOSE MAN:EYE +CREATURE:MOOSE MAN:BRAIN +CREATURE:MOOSE MAN:LUNG +CREATURE:MOOSE MAN:HEART +CREATURE:MOOSE MAN:LIVER +CREATURE:MOOSE MAN:GUT +CREATURE:MOOSE MAN:STOMACH +CREATURE:MOOSE MAN:GIZZARD +CREATURE:MOOSE MAN:PANCREAS +CREATURE:MOOSE MAN:SPLEEN +CREATURE:MOOSE MAN:KIDNEY +CREATURE:MOOSE, GIANT:MUSCLE +CREATURE:MOOSE, GIANT:EYE +CREATURE:MOOSE, GIANT:BRAIN +CREATURE:MOOSE, GIANT:LUNG +CREATURE:MOOSE, GIANT:HEART +CREATURE:MOOSE, GIANT:LIVER +CREATURE:MOOSE, GIANT:GUT +CREATURE:MOOSE, GIANT:STOMACH +CREATURE:MOOSE, GIANT:GIZZARD +CREATURE:MOOSE, GIANT:PANCREAS +CREATURE:MOOSE, GIANT:SPLEEN +CREATURE:MOOSE, GIANT:KIDNEY +CREATURE:RED PANDA:MUSCLE +CREATURE:RED PANDA:EYE +CREATURE:RED PANDA:BRAIN +CREATURE:RED PANDA:LUNG +CREATURE:RED PANDA:HEART +CREATURE:RED PANDA:LIVER +CREATURE:RED PANDA:GUT +CREATURE:RED PANDA:STOMACH +CREATURE:RED PANDA:GIZZARD +CREATURE:RED PANDA:PANCREAS +CREATURE:RED PANDA:SPLEEN +CREATURE:RED PANDA:KIDNEY +CREATURE:RED PANDA MAN:MUSCLE +CREATURE:RED PANDA MAN:EYE +CREATURE:RED PANDA MAN:BRAIN +CREATURE:RED PANDA MAN:LUNG +CREATURE:RED PANDA MAN:HEART +CREATURE:RED PANDA MAN:LIVER +CREATURE:RED PANDA MAN:GUT +CREATURE:RED PANDA MAN:STOMACH +CREATURE:RED PANDA MAN:GIZZARD +CREATURE:RED PANDA MAN:PANCREAS +CREATURE:RED PANDA MAN:SPLEEN +CREATURE:RED PANDA MAN:KIDNEY + CREATURE:RED PANDA, GIANT:MUSCLE +CREATURE:RED PANDA, GIANT:EYE +CREATURE:RED PANDA, GIANT:BRAIN +CREATURE:RED PANDA, GIANT:LUNG +CREATURE:RED PANDA, GIANT:HEART +CREATURE:RED PANDA, GIANT:LIVER +CREATURE:RED PANDA, GIANT:GUT +!CREATURE:RED PANDA, GIANT:STOMACH +!CREATURE:RED PANDA, GIANT:GIZZARD +"CREATURE:RED PANDA, GIANT:PANCREAS + CREATURE:RED PANDA, GIANT:SPLEEN + CREATURE:RED PANDA, GIANT:KIDNEY +CREATURE:ELEPHANT:MUSCLE +CREATURE:ELEPHANT:EYE +CREATURE:ELEPHANT:BRAIN +CREATURE:ELEPHANT:LUNG +CREATURE:ELEPHANT:HEART +CREATURE:ELEPHANT:LIVER +CREATURE:ELEPHANT:GUT +CREATURE:ELEPHANT:STOMACH +CREATURE:ELEPHANT:GIZZARD +CREATURE:ELEPHANT:PANCREAS +CREATURE:ELEPHANT:SPLEEN +CREATURE:ELEPHANT:KIDNEY +CREATURE:ELEPHANT_MAN:MUSCLE +CREATURE:ELEPHANT_MAN:EYE +CREATURE:ELEPHANT_MAN:BRAIN +CREATURE:ELEPHANT_MAN:LUNG +CREATURE:ELEPHANT_MAN:HEART +CREATURE:ELEPHANT_MAN:LIVER +CREATURE:ELEPHANT_MAN:GUT +CREATURE:ELEPHANT_MAN:STOMACH +CREATURE:ELEPHANT_MAN:GIZZARD +CREATURE:ELEPHANT_MAN:PANCREAS +CREATURE:ELEPHANT_MAN:SPLEEN +CREATURE:ELEPHANT_MAN:KIDNEY +CREATURE:GIANT_ELEPHANT:MUSCLE +CREATURE:GIANT_ELEPHANT:EYE +CREATURE:GIANT_ELEPHANT:BRAIN +CREATURE:GIANT_ELEPHANT:LUNG +CREATURE:GIANT_ELEPHANT:HEART +CREATURE:GIANT_ELEPHANT:LIVER +CREATURE:GIANT_ELEPHANT:GUT +CREATURE:GIANT_ELEPHANT:STOMACH +CREATURE:GIANT_ELEPHANT:GIZZARD + CREATURE:GIANT_ELEPHANT:PANCREAS +CREATURE:GIANT_ELEPHANT:SPLEEN +CREATURE:GIANT_ELEPHANT:KIDNEY +CREATURE:WARTHOG:MUSCLE +CREATURE:WARTHOG:EYE +CREATURE:WARTHOG:BRAIN +CREATURE:WARTHOG:LUNG +CREATURE:WARTHOG:HEART +CREATURE:WARTHOG:LIVER +CREATURE:WARTHOG:GUT +CREATURE:WARTHOG:STOMACH +CREATURE:WARTHOG:GIZZARD +CREATURE:WARTHOG:PANCREAS +CREATURE:WARTHOG:SPLEEN +CREATURE:WARTHOG:KIDNEY +CREATURE:WARTHOG_MAN:MUSCLE +CREATURE:WARTHOG_MAN:EYE +CREATURE:WARTHOG_MAN:BRAIN +CREATURE:WARTHOG_MAN:LUNG +CREATURE:WARTHOG_MAN:HEART +CREATURE:WARTHOG_MAN:LIVER +CREATURE:WARTHOG_MAN:GUT +CREATURE:WARTHOG_MAN:STOMACH +CREATURE:WARTHOG_MAN:GIZZARD +CREATURE:WARTHOG_MAN:PANCREAS +CREATURE:WARTHOG_MAN:SPLEEN +CREATURE:WARTHOG_MAN:KIDNEY +CREATURE:GIANT_WARTHOG:MUSCLE +CREATURE:GIANT_WARTHOG:EYE +CREATURE:GIANT_WARTHOG:BRAIN +CREATURE:GIANT_WARTHOG:LUNG +CREATURE:GIANT_WARTHOG:HEART +CREATURE:GIANT_WARTHOG:LIVER +CREATURE:GIANT_WARTHOG:GUT +CREATURE:GIANT_WARTHOG:STOMACH +CREATURE:GIANT_WARTHOG:GIZZARD +CREATURE:GIANT_WARTHOG:PANCREAS +CREATURE:GIANT_WARTHOG:SPLEEN +CREATURE:GIANT_WARTHOG:KIDNEY +CREATURE:LION:MUSCLE +CREATURE:LION:EYE +CREATURE:LION:BRAIN +CREATURE:LION:LUNG +CREATURE:LION:HEART +CREATURE:LION:LIVER +CREATURE:LION:GUT +CREATURE:LION:STOMACH +CREATURE:LION:GIZZARD +CREATURE:LION:PANCREAS +CREATURE:LION:SPLEEN +CREATURE:LION:KIDNEY +CREATURE:LION_MAN:MUSCLE +CREATURE:LION_MAN:EYE +CREATURE:LION_MAN:BRAIN +CREATURE:LION_MAN:LUNG +CREATURE:LION_MAN:HEART +CREATURE:LION_MAN:LIVER +CREATURE:LION_MAN:GUT +CREATURE:LION_MAN:STOMACH +CREATURE:LION_MAN:GIZZARD +CREATURE:LION_MAN:PANCREAS +CREATURE:LION_MAN:SPLEEN +CREATURE:LION_MAN:KIDNEY +CREATURE:GIANT_LION:MUSCLE +CREATURE:GIANT_LION:EYE +CREATURE:GIANT_LION:BRAIN +CREATURE:GIANT_LION:LUNG +CREATURE:GIANT_LION:HEART +CREATURE:GIANT_LION:LIVER +CREATURE:GIANT_LION:GUT +CREATURE:GIANT_LION:STOMACH +CREATURE:GIANT_LION:GIZZARD +CREATURE:GIANT_LION:PANCREAS +CREATURE:GIANT_LION:SPLEEN +CREATURE:GIANT_LION:KIDNEY +CREATURE:LEOPARD:MUSCLE +CREATURE:LEOPARD:EYE +CREATURE:LEOPARD:BRAIN +CREATURE:LEOPARD:LUNG +CREATURE:LEOPARD:HEART +CREATURE:LEOPARD:LIVER +CREATURE:LEOPARD:GUT +CREATURE:LEOPARD:STOMACH +CREATURE:LEOPARD:GIZZARD +CREATURE:LEOPARD:PANCREAS +CREATURE:LEOPARD:SPLEEN +CREATURE:LEOPARD:KIDNEY +CREATURE:LEOPARD_MAN:MUSCLE +CREATURE:LEOPARD_MAN:EYE +CREATURE:LEOPARD_MAN:BRAIN +CREATURE:LEOPARD_MAN:LUNG +CREATURE:LEOPARD_MAN:HEART +CREATURE:LEOPARD_MAN:LIVER +CREATURE:LEOPARD_MAN:GUT +CREATURE:LEOPARD_MAN:STOMACH +CREATURE:LEOPARD_MAN:GIZZARD +CREATURE:LEOPARD_MAN:PANCREAS +CREATURE:LEOPARD_MAN:SPLEEN +CREATURE:LEOPARD_MAN:KIDNEY +CREATURE:GIANT_LEOPARD:MUSCLE +CREATURE:GIANT_LEOPARD:EYE +CREATURE:GIANT_LEOPARD:BRAIN +CREATURE:GIANT_LEOPARD:LUNG +CREATURE:GIANT_LEOPARD:HEART +CREATURE:GIANT_LEOPARD:LIVER +CREATURE:GIANT_LEOPARD:GUT +CREATURE:GIANT_LEOPARD:STOMACH +CREATURE:GIANT_LEOPARD:GIZZARD +CREATURE:GIANT_LEOPARD:PANCREAS +CREATURE:GIANT_LEOPARD:SPLEEN +CREATURE:GIANT_LEOPARD:KIDNEY +CREATURE:JAGUAR:MUSCLE +CREATURE:JAGUAR:EYE +CREATURE:JAGUAR:BRAIN +CREATURE:JAGUAR:LUNG +CREATURE:JAGUAR:HEART +CREATURE:JAGUAR:LIVER +CREATURE:JAGUAR:GUT +CREATURE:JAGUAR:STOMACH +CREATURE:JAGUAR:GIZZARD +CREATURE:JAGUAR:PANCREAS +CREATURE:JAGUAR:SPLEEN +CREATURE:JAGUAR:KIDNEY +CREATURE:JAGUAR_MAN:MUSCLE +CREATURE:JAGUAR_MAN:EYE +CREATURE:JAGUAR_MAN:BRAIN +CREATURE:JAGUAR_MAN:LUNG +CREATURE:JAGUAR_MAN:HEART +CREATURE:JAGUAR_MAN:LIVER +CREATURE:JAGUAR_MAN:GUT +CREATURE:JAGUAR_MAN:STOMACH +CREATURE:JAGUAR_MAN:GIZZARD +CREATURE:JAGUAR_MAN:PANCREAS +CREATURE:JAGUAR_MAN:SPLEEN +CREATURE:JAGUAR_MAN:KIDNEY +CREATURE:GIANT_JAGUAR:MUSCLE +CREATURE:GIANT_JAGUAR:EYE +CREATURE:GIANT_JAGUAR:BRAIN +CREATURE:GIANT_JAGUAR:LUNG +CREATURE:GIANT_JAGUAR:HEART +CREATURE:GIANT_JAGUAR:LIVER +CREATURE:GIANT_JAGUAR:GUT +CREATURE:GIANT_JAGUAR:STOMACH +CREATURE:GIANT_JAGUAR:GIZZARD +CREATURE:GIANT_JAGUAR:PANCREAS +CREATURE:GIANT_JAGUAR:SPLEEN +CREATURE:GIANT_JAGUAR:KIDNEY +CREATURE:TIGER:MUSCLE +CREATURE:TIGER:EYE +CREATURE:TIGER:BRAIN +CREATURE:TIGER:LUNG +CREATURE:TIGER:HEART +CREATURE:TIGER:LIVER +CREATURE:TIGER:GUT +CREATURE:TIGER:STOMACH +CREATURE:TIGER:GIZZARD +CREATURE:TIGER:PANCREAS +CREATURE:TIGER:SPLEEN +CREATURE:TIGER:KIDNEY +CREATURE:TIGER_MAN:MUSCLE +CREATURE:TIGER_MAN:EYE +CREATURE:TIGER_MAN:BRAIN +CREATURE:TIGER_MAN:LUNG +CREATURE:TIGER_MAN:HEART +CREATURE:TIGER_MAN:LIVER +CREATURE:TIGER_MAN:GUT +CREATURE:TIGER_MAN:STOMACH +CREATURE:TIGER_MAN:GIZZARD +CREATURE:TIGER_MAN:PANCREAS +CREATURE:TIGER_MAN:SPLEEN +CREATURE:TIGER_MAN:KIDNEY +CREATURE:GIANT_TIGER:MUSCLE +CREATURE:GIANT_TIGER:EYE +CREATURE:GIANT_TIGER:BRAIN +CREATURE:GIANT_TIGER:LUNG +CREATURE:GIANT_TIGER:HEART +CREATURE:GIANT_TIGER:LIVER +CREATURE:GIANT_TIGER:GUT +CREATURE:GIANT_TIGER:STOMACH +CREATURE:GIANT_TIGER:GIZZARD +CREATURE:GIANT_TIGER:PANCREAS +CREATURE:GIANT_TIGER:SPLEEN +CREATURE:GIANT_TIGER:KIDNEY +CREATURE:CHEETAH:MUSCLE +CREATURE:CHEETAH:EYE +CREATURE:CHEETAH:BRAIN +CREATURE:CHEETAH:LUNG +CREATURE:CHEETAH:HEART +CREATURE:CHEETAH:LIVER +CREATURE:CHEETAH:GUT +CREATURE:CHEETAH:STOMACH +CREATURE:CHEETAH:GIZZARD +CREATURE:CHEETAH:PANCREAS +CREATURE:CHEETAH:SPLEEN +CREATURE:CHEETAH:KIDNEY +CREATURE:CHEETAH_MAN:MUSCLE +CREATURE:CHEETAH_MAN:EYE +CREATURE:CHEETAH_MAN:BRAIN +CREATURE:CHEETAH_MAN:LUNG +CREATURE:CHEETAH_MAN:HEART +CREATURE:CHEETAH_MAN:LIVER +CREATURE:CHEETAH_MAN:GUT +CREATURE:CHEETAH_MAN:STOMACH +CREATURE:CHEETAH_MAN:GIZZARD +CREATURE:CHEETAH_MAN:PANCREAS +CREATURE:CHEETAH_MAN:SPLEEN +CREATURE:CHEETAH_MAN:KIDNEY +CREATURE:GIANT_CHEETAH:MUSCLE +CREATURE:GIANT_CHEETAH:EYE +CREATURE:GIANT_CHEETAH:BRAIN +CREATURE:GIANT_CHEETAH:LUNG +CREATURE:GIANT_CHEETAH:HEART +CREATURE:GIANT_CHEETAH:LIVER +CREATURE:GIANT_CHEETAH:GUT +CREATURE:GIANT_CHEETAH:STOMACH +CREATURE:GIANT_CHEETAH:GIZZARD +CREATURE:GIANT_CHEETAH:PANCREAS +CREATURE:GIANT_CHEETAH:SPLEEN +CREATURE:GIANT_CHEETAH:KIDNEY +CREATURE:GAZELLE:MUSCLE +CREATURE:GAZELLE:EYE +CREATURE:GAZELLE:BRAIN +CREATURE:GAZELLE:LUNG +CREATURE:GAZELLE:HEART +CREATURE:GAZELLE:LIVER +CREATURE:GAZELLE:GUT +CREATURE:GAZELLE:STOMACH +CREATURE:GAZELLE:GIZZARD +CREATURE:GAZELLE:PANCREAS +CREATURE:GAZELLE:SPLEEN +CREATURE:GAZELLE:KIDNEY +CREATURE:GAZELLE_MAN:MUSCLE +CREATURE:GAZELLE_MAN:EYE +CREATURE:GAZELLE_MAN:BRAIN +CREATURE:GAZELLE_MAN:LUNG +CREATURE:GAZELLE_MAN:HEART +CREATURE:GAZELLE_MAN:LIVER +CREATURE:GAZELLE_MAN:GUT +CREATURE:GAZELLE_MAN:STOMACH +CREATURE:GAZELLE_MAN:GIZZARD +CREATURE:GAZELLE_MAN:PANCREAS +CREATURE:GAZELLE_MAN:SPLEEN +CREATURE:GAZELLE_MAN:KIDNEY +CREATURE:GIANT_GAZELLE:MUSCLE +CREATURE:GIANT_GAZELLE:EYE +CREATURE:GIANT_GAZELLE:BRAIN +CREATURE:GIANT_GAZELLE:LUNG +CREATURE:GIANT_GAZELLE:HEART +CREATURE:GIANT_GAZELLE:LIVER +CREATURE:GIANT_GAZELLE:GUT +CREATURE:GIANT_GAZELLE:STOMACH +CREATURE:GIANT_GAZELLE:GIZZARD +CREATURE:GIANT_GAZELLE:PANCREAS +CREATURE:GIANT_GAZELLE:SPLEEN +CREATURE:GIANT_GAZELLE:KIDNEY +CREATURE:MANDRILL:MUSCLE +CREATURE:MANDRILL:EYE +CREATURE:MANDRILL:BRAIN +CREATURE:MANDRILL:LUNG +CREATURE:MANDRILL:HEART +CREATURE:MANDRILL:LIVER +CREATURE:MANDRILL:GUT +CREATURE:MANDRILL:STOMACH +CREATURE:MANDRILL:GIZZARD +CREATURE:MANDRILL:PANCREAS +CREATURE:MANDRILL:SPLEEN +CREATURE:MANDRILL:KIDNEY +CREATURE:MANDRILL_MAN:MUSCLE +CREATURE:MANDRILL_MAN:EYE +CREATURE:MANDRILL_MAN:BRAIN +CREATURE:MANDRILL_MAN:LUNG +CREATURE:MANDRILL_MAN:HEART +CREATURE:MANDRILL_MAN:LIVER +CREATURE:MANDRILL_MAN:GUT +CREATURE:MANDRILL_MAN:STOMACH +CREATURE:MANDRILL_MAN:GIZZARD +CREATURE:MANDRILL_MAN:PANCREAS +CREATURE:MANDRILL_MAN:SPLEEN +CREATURE:MANDRILL_MAN:KIDNEY +CREATURE:GIANT_MANDRILL:MUSCLE +CREATURE:GIANT_MANDRILL:EYE +CREATURE:GIANT_MANDRILL:BRAIN +CREATURE:GIANT_MANDRILL:LUNG +CREATURE:GIANT_MANDRILL:HEART +CREATURE:GIANT_MANDRILL:LIVER +CREATURE:GIANT_MANDRILL:GUT +CREATURE:GIANT_MANDRILL:STOMACH +CREATURE:GIANT_MANDRILL:GIZZARD + CREATURE:GIANT_MANDRILL:PANCREAS +CREATURE:GIANT_MANDRILL:SPLEEN +CREATURE:GIANT_MANDRILL:KIDNEY +CREATURE:CHIMPANZEE:MUSCLE +CREATURE:CHIMPANZEE:EYE +CREATURE:CHIMPANZEE:BRAIN +CREATURE:CHIMPANZEE:LUNG +CREATURE:CHIMPANZEE:HEART +CREATURE:CHIMPANZEE:LIVER +CREATURE:CHIMPANZEE:GUT +CREATURE:CHIMPANZEE:STOMACH +CREATURE:CHIMPANZEE:GIZZARD +CREATURE:CHIMPANZEE:PANCREAS +CREATURE:CHIMPANZEE:SPLEEN +CREATURE:CHIMPANZEE:KIDNEY +CREATURE:BONOBO:MUSCLE +CREATURE:BONOBO:EYE +CREATURE:BONOBO:BRAIN +CREATURE:BONOBO:LUNG +CREATURE:BONOBO:HEART +CREATURE:BONOBO:LIVER +CREATURE:BONOBO:GUT +CREATURE:BONOBO:STOMACH +CREATURE:BONOBO:GIZZARD +CREATURE:BONOBO:PANCREAS +CREATURE:BONOBO:SPLEEN +CREATURE:BONOBO:KIDNEY +CREATURE:GORILLA:MUSCLE +CREATURE:GORILLA:EYE +CREATURE:GORILLA:BRAIN +CREATURE:GORILLA:LUNG +CREATURE:GORILLA:HEART +CREATURE:GORILLA:LIVER +CREATURE:GORILLA:GUT +CREATURE:GORILLA:STOMACH +CREATURE:GORILLA:GIZZARD +CREATURE:GORILLA:PANCREAS +CREATURE:GORILLA:SPLEEN +CREATURE:GORILLA:KIDNEY +CREATURE:ORANGUTAN:MUSCLE +CREATURE:ORANGUTAN:EYE +CREATURE:ORANGUTAN:BRAIN +CREATURE:ORANGUTAN:LUNG +CREATURE:ORANGUTAN:HEART +CREATURE:ORANGUTAN:LIVER +CREATURE:ORANGUTAN:GUT +CREATURE:ORANGUTAN:STOMACH +CREATURE:ORANGUTAN:GIZZARD +CREATURE:ORANGUTAN:PANCREAS +CREATURE:ORANGUTAN:SPLEEN +CREATURE:ORANGUTAN:KIDNEY +CREATURE:GIBBON_SIAMANG:MUSCLE +CREATURE:GIBBON_SIAMANG:EYE +CREATURE:GIBBON_SIAMANG:BRAIN +CREATURE:GIBBON_SIAMANG:LUNG +CREATURE:GIBBON_SIAMANG:HEART +CREATURE:GIBBON_SIAMANG:LIVER +CREATURE:GIBBON_SIAMANG:GUT +CREATURE:GIBBON_SIAMANG:STOMACH +CREATURE:GIBBON_SIAMANG:GIZZARD + CREATURE:GIBBON_SIAMANG:PANCREAS +CREATURE:GIBBON_SIAMANG:SPLEEN +CREATURE:GIBBON_SIAMANG:KIDNEY +#CREATURE:GIBBON_WHITE_HANDED:MUSCLE + CREATURE:GIBBON_WHITE_HANDED:EYE +"CREATURE:GIBBON_WHITE_HANDED:BRAIN +!CREATURE:GIBBON_WHITE_HANDED:LUNG +"CREATURE:GIBBON_WHITE_HANDED:HEART +"CREATURE:GIBBON_WHITE_HANDED:LIVER + CREATURE:GIBBON_WHITE_HANDED:GUT +$CREATURE:GIBBON_WHITE_HANDED:STOMACH +$CREATURE:GIBBON_WHITE_HANDED:GIZZARD +%CREATURE:GIBBON_WHITE_HANDED:PANCREAS +#CREATURE:GIBBON_WHITE_HANDED:SPLEEN +#CREATURE:GIBBON_WHITE_HANDED:KIDNEY +#CREATURE:GIBBON_BLACK_HANDED:MUSCLE + CREATURE:GIBBON_BLACK_HANDED:EYE +"CREATURE:GIBBON_BLACK_HANDED:BRAIN +!CREATURE:GIBBON_BLACK_HANDED:LUNG +"CREATURE:GIBBON_BLACK_HANDED:HEART +"CREATURE:GIBBON_BLACK_HANDED:LIVER + CREATURE:GIBBON_BLACK_HANDED:GUT +$CREATURE:GIBBON_BLACK_HANDED:STOMACH +$CREATURE:GIBBON_BLACK_HANDED:GIZZARD +%CREATURE:GIBBON_BLACK_HANDED:PANCREAS +#CREATURE:GIBBON_BLACK_HANDED:SPLEEN +#CREATURE:GIBBON_BLACK_HANDED:KIDNEY +CREATURE:GIBBON_GRAY:MUSCLE +CREATURE:GIBBON_GRAY:EYE +CREATURE:GIBBON_GRAY:BRAIN +CREATURE:GIBBON_GRAY:LUNG +CREATURE:GIBBON_GRAY:HEART +CREATURE:GIBBON_GRAY:LIVER +CREATURE:GIBBON_GRAY:GUT +CREATURE:GIBBON_GRAY:STOMACH +CREATURE:GIBBON_GRAY:GIZZARD +CREATURE:GIBBON_GRAY:PANCREAS +CREATURE:GIBBON_GRAY:SPLEEN +CREATURE:GIBBON_GRAY:KIDNEY +CREATURE:GIBBON_SILVERY:MUSCLE +CREATURE:GIBBON_SILVERY:EYE +CREATURE:GIBBON_SILVERY:BRAIN +CREATURE:GIBBON_SILVERY:LUNG +CREATURE:GIBBON_SILVERY:HEART +CREATURE:GIBBON_SILVERY:LIVER +CREATURE:GIBBON_SILVERY:GUT +CREATURE:GIBBON_SILVERY:STOMACH +CREATURE:GIBBON_SILVERY:GIZZARD + CREATURE:GIBBON_SILVERY:PANCREAS +CREATURE:GIBBON_SILVERY:SPLEEN +CREATURE:GIBBON_SILVERY:KIDNEY +CREATURE:GIBBON_PILEATED:MUSCLE +CREATURE:GIBBON_PILEATED:EYE +CREATURE:GIBBON_PILEATED:BRAIN +CREATURE:GIBBON_PILEATED:LUNG +CREATURE:GIBBON_PILEATED:HEART +CREATURE:GIBBON_PILEATED:LIVER +CREATURE:GIBBON_PILEATED:GUT + CREATURE:GIBBON_PILEATED:STOMACH + CREATURE:GIBBON_PILEATED:GIZZARD +!CREATURE:GIBBON_PILEATED:PANCREAS +CREATURE:GIBBON_PILEATED:SPLEEN +CREATURE:GIBBON_PILEATED:KIDNEY +CREATURE:GIBBON_BILOU:MUSCLE +CREATURE:GIBBON_BILOU:EYE +CREATURE:GIBBON_BILOU:BRAIN +CREATURE:GIBBON_BILOU:LUNG +CREATURE:GIBBON_BILOU:HEART +CREATURE:GIBBON_BILOU:LIVER +CREATURE:GIBBON_BILOU:GUT +CREATURE:GIBBON_BILOU:STOMACH +CREATURE:GIBBON_BILOU:GIZZARD +CREATURE:GIBBON_BILOU:PANCREAS +CREATURE:GIBBON_BILOU:SPLEEN +CREATURE:GIBBON_BILOU:KIDNEY +#CREATURE:GIBBON_WHITE_BROWED:MUSCLE + CREATURE:GIBBON_WHITE_BROWED:EYE +"CREATURE:GIBBON_WHITE_BROWED:BRAIN +!CREATURE:GIBBON_WHITE_BROWED:LUNG +"CREATURE:GIBBON_WHITE_BROWED:HEART +"CREATURE:GIBBON_WHITE_BROWED:LIVER + CREATURE:GIBBON_WHITE_BROWED:GUT +$CREATURE:GIBBON_WHITE_BROWED:STOMACH +$CREATURE:GIBBON_WHITE_BROWED:GIZZARD +%CREATURE:GIBBON_WHITE_BROWED:PANCREAS +#CREATURE:GIBBON_WHITE_BROWED:SPLEEN +#CREATURE:GIBBON_WHITE_BROWED:KIDNEY +$CREATURE:GIBBON_BLACK_CRESTED:MUSCLE +!CREATURE:GIBBON_BLACK_CRESTED:EYE +#CREATURE:GIBBON_BLACK_CRESTED:BRAIN +"CREATURE:GIBBON_BLACK_CRESTED:LUNG +#CREATURE:GIBBON_BLACK_CRESTED:HEART +#CREATURE:GIBBON_BLACK_CRESTED:LIVER +!CREATURE:GIBBON_BLACK_CRESTED:GUT +%CREATURE:GIBBON_BLACK_CRESTED:STOMACH +%CREATURE:GIBBON_BLACK_CRESTED:GIZZARD +&CREATURE:GIBBON_BLACK_CRESTED:PANCREAS +$CREATURE:GIBBON_BLACK_CRESTED:SPLEEN +$CREATURE:GIBBON_BLACK_CRESTED:KIDNEY +CREATURE:CAMEL_1_HUMP:MUSCLE +CREATURE:CAMEL_1_HUMP:EYE +CREATURE:CAMEL_1_HUMP:BRAIN +CREATURE:CAMEL_1_HUMP:LUNG +CREATURE:CAMEL_1_HUMP:HEART +CREATURE:CAMEL_1_HUMP:LIVER +CREATURE:CAMEL_1_HUMP:GUT +CREATURE:CAMEL_1_HUMP:STOMACH +CREATURE:CAMEL_1_HUMP:GIZZARD +CREATURE:CAMEL_1_HUMP:PANCREAS +CREATURE:CAMEL_1_HUMP:SPLEEN +CREATURE:CAMEL_1_HUMP:KIDNEY + CREATURE:CAMEL_1_HUMP_MAN:MUSCLE +CREATURE:CAMEL_1_HUMP_MAN:EYE +CREATURE:CAMEL_1_HUMP_MAN:BRAIN +CREATURE:CAMEL_1_HUMP_MAN:LUNG +CREATURE:CAMEL_1_HUMP_MAN:HEART +CREATURE:CAMEL_1_HUMP_MAN:LIVER +CREATURE:CAMEL_1_HUMP_MAN:GUT +!CREATURE:CAMEL_1_HUMP_MAN:STOMACH +!CREATURE:CAMEL_1_HUMP_MAN:GIZZARD +"CREATURE:CAMEL_1_HUMP_MAN:PANCREAS + CREATURE:CAMEL_1_HUMP_MAN:SPLEEN + CREATURE:CAMEL_1_HUMP_MAN:KIDNEY +"CREATURE:GIANT_CAMEL_1_HUMP:MUSCLE +CREATURE:GIANT_CAMEL_1_HUMP:EYE +!CREATURE:GIANT_CAMEL_1_HUMP:BRAIN + CREATURE:GIANT_CAMEL_1_HUMP:LUNG +!CREATURE:GIANT_CAMEL_1_HUMP:HEART +!CREATURE:GIANT_CAMEL_1_HUMP:LIVER +CREATURE:GIANT_CAMEL_1_HUMP:GUT +#CREATURE:GIANT_CAMEL_1_HUMP:STOMACH +#CREATURE:GIANT_CAMEL_1_HUMP:GIZZARD +$CREATURE:GIANT_CAMEL_1_HUMP:PANCREAS +"CREATURE:GIANT_CAMEL_1_HUMP:SPLEEN +"CREATURE:GIANT_CAMEL_1_HUMP:KIDNEY +CREATURE:CAMEL_2_HUMP:MUSCLE +CREATURE:CAMEL_2_HUMP:EYE +CREATURE:CAMEL_2_HUMP:BRAIN +CREATURE:CAMEL_2_HUMP:LUNG +CREATURE:CAMEL_2_HUMP:HEART +CREATURE:CAMEL_2_HUMP:LIVER +CREATURE:CAMEL_2_HUMP:GUT +CREATURE:CAMEL_2_HUMP:STOMACH +CREATURE:CAMEL_2_HUMP:GIZZARD +CREATURE:CAMEL_2_HUMP:PANCREAS +CREATURE:CAMEL_2_HUMP:SPLEEN +CREATURE:CAMEL_2_HUMP:KIDNEY + CREATURE:CAMEL_2_HUMP_MAN:MUSCLE +CREATURE:CAMEL_2_HUMP_MAN:EYE +CREATURE:CAMEL_2_HUMP_MAN:BRAIN +CREATURE:CAMEL_2_HUMP_MAN:LUNG +CREATURE:CAMEL_2_HUMP_MAN:HEART +CREATURE:CAMEL_2_HUMP_MAN:LIVER +CREATURE:CAMEL_2_HUMP_MAN:GUT +!CREATURE:CAMEL_2_HUMP_MAN:STOMACH +!CREATURE:CAMEL_2_HUMP_MAN:GIZZARD +"CREATURE:CAMEL_2_HUMP_MAN:PANCREAS + CREATURE:CAMEL_2_HUMP_MAN:SPLEEN + CREATURE:CAMEL_2_HUMP_MAN:KIDNEY +"CREATURE:GIANT_CAMEL_2_HUMP:MUSCLE +CREATURE:GIANT_CAMEL_2_HUMP:EYE +!CREATURE:GIANT_CAMEL_2_HUMP:BRAIN + CREATURE:GIANT_CAMEL_2_HUMP:LUNG +!CREATURE:GIANT_CAMEL_2_HUMP:HEART +!CREATURE:GIANT_CAMEL_2_HUMP:LIVER +CREATURE:GIANT_CAMEL_2_HUMP:GUT +#CREATURE:GIANT_CAMEL_2_HUMP:STOMACH +#CREATURE:GIANT_CAMEL_2_HUMP:GIZZARD +$CREATURE:GIANT_CAMEL_2_HUMP:PANCREAS +"CREATURE:GIANT_CAMEL_2_HUMP:SPLEEN +"CREATURE:GIANT_CAMEL_2_HUMP:KIDNEY +#CREATURE:CROCODILE_SALTWATER:MUSCLE + CREATURE:CROCODILE_SALTWATER:EYE +"CREATURE:CROCODILE_SALTWATER:BRAIN +!CREATURE:CROCODILE_SALTWATER:LUNG +"CREATURE:CROCODILE_SALTWATER:HEART +"CREATURE:CROCODILE_SALTWATER:LIVER + CREATURE:CROCODILE_SALTWATER:GUT +$CREATURE:CROCODILE_SALTWATER:STOMACH +$CREATURE:CROCODILE_SALTWATER:GIZZARD +%CREATURE:CROCODILE_SALTWATER:PANCREAS +#CREATURE:CROCODILE_SALTWATER:SPLEEN +#CREATURE:CROCODILE_SALTWATER:KIDNEY +'CREATURE:CROCODILE_SALTWATER_MAN:MUSCLE +$CREATURE:CROCODILE_SALTWATER_MAN:EYE +&CREATURE:CROCODILE_SALTWATER_MAN:BRAIN +%CREATURE:CROCODILE_SALTWATER_MAN:LUNG +&CREATURE:CROCODILE_SALTWATER_MAN:HEART +&CREATURE:CROCODILE_SALTWATER_MAN:LIVER +$CREATURE:CROCODILE_SALTWATER_MAN:GUT +(CREATURE:CROCODILE_SALTWATER_MAN:STOMACH +(CREATURE:CROCODILE_SALTWATER_MAN:GIZZARD +)CREATURE:CROCODILE_SALTWATER_MAN:PANCREAS +'CREATURE:CROCODILE_SALTWATER_MAN:SPLEEN +'CREATURE:CROCODILE_SALTWATER_MAN:KIDNEY +)CREATURE:GIANT_CROCODILE_SALTWATER:MUSCLE +&CREATURE:GIANT_CROCODILE_SALTWATER:EYE +(CREATURE:GIANT_CROCODILE_SALTWATER:BRAIN +'CREATURE:GIANT_CROCODILE_SALTWATER:LUNG +(CREATURE:GIANT_CROCODILE_SALTWATER:HEART +(CREATURE:GIANT_CROCODILE_SALTWATER:LIVER +&CREATURE:GIANT_CROCODILE_SALTWATER:GUT +*CREATURE:GIANT_CROCODILE_SALTWATER:STOMACH +*CREATURE:GIANT_CROCODILE_SALTWATER:GIZZARD ++CREATURE:GIANT_CROCODILE_SALTWATER:PANCREAS +)CREATURE:GIANT_CROCODILE_SALTWATER:SPLEEN +)CREATURE:GIANT_CROCODILE_SALTWATER:KIDNEY +CREATURE:BIRD_VULTURE:MUSCLE +CREATURE:BIRD_VULTURE:EYE +CREATURE:BIRD_VULTURE:BRAIN +CREATURE:BIRD_VULTURE:LUNG +CREATURE:BIRD_VULTURE:HEART +CREATURE:BIRD_VULTURE:LIVER +CREATURE:BIRD_VULTURE:GUT +CREATURE:BIRD_VULTURE:STOMACH +CREATURE:BIRD_VULTURE:GIZZARD +CREATURE:BIRD_VULTURE:PANCREAS +CREATURE:BIRD_VULTURE:SPLEEN +CREATURE:BIRD_VULTURE:KIDNEY +CREATURE:VULTURE_MAN:MUSCLE +CREATURE:VULTURE_MAN:EYE +CREATURE:VULTURE_MAN:BRAIN +CREATURE:VULTURE_MAN:LUNG +CREATURE:VULTURE_MAN:HEART +CREATURE:VULTURE_MAN:LIVER +CREATURE:VULTURE_MAN:GUT +CREATURE:VULTURE_MAN:STOMACH +CREATURE:VULTURE_MAN:GIZZARD +CREATURE:VULTURE_MAN:PANCREAS +CREATURE:VULTURE_MAN:SPLEEN +CREATURE:VULTURE_MAN:KIDNEY +CREATURE:GIANT_VULTURE:MUSCLE +CREATURE:GIANT_VULTURE:EYE +CREATURE:GIANT_VULTURE:BRAIN +CREATURE:GIANT_VULTURE:LUNG +CREATURE:GIANT_VULTURE:HEART +CREATURE:GIANT_VULTURE:LIVER +CREATURE:GIANT_VULTURE:GUT +CREATURE:GIANT_VULTURE:STOMACH +CREATURE:GIANT_VULTURE:GIZZARD +CREATURE:GIANT_VULTURE:PANCREAS +CREATURE:GIANT_VULTURE:SPLEEN +CREATURE:GIANT_VULTURE:KIDNEY +CREATURE:RHINOCEROS:MUSCLE +CREATURE:RHINOCEROS:EYE +CREATURE:RHINOCEROS:BRAIN +CREATURE:RHINOCEROS:LUNG +CREATURE:RHINOCEROS:HEART +CREATURE:RHINOCEROS:LIVER +CREATURE:RHINOCEROS:GUT +CREATURE:RHINOCEROS:STOMACH +CREATURE:RHINOCEROS:GIZZARD +CREATURE:RHINOCEROS:PANCREAS +CREATURE:RHINOCEROS:SPLEEN +CREATURE:RHINOCEROS:KIDNEY +CREATURE:RHINOCEROS_MAN:MUSCLE +CREATURE:RHINOCEROS_MAN:EYE +CREATURE:RHINOCEROS_MAN:BRAIN +CREATURE:RHINOCEROS_MAN:LUNG +CREATURE:RHINOCEROS_MAN:HEART +CREATURE:RHINOCEROS_MAN:LIVER +CREATURE:RHINOCEROS_MAN:GUT +CREATURE:RHINOCEROS_MAN:STOMACH +CREATURE:RHINOCEROS_MAN:GIZZARD + CREATURE:RHINOCEROS_MAN:PANCREAS +CREATURE:RHINOCEROS_MAN:SPLEEN +CREATURE:RHINOCEROS_MAN:KIDNEY + CREATURE:GIANT_RHINOCEROS:MUSCLE +CREATURE:GIANT_RHINOCEROS:EYE +CREATURE:GIANT_RHINOCEROS:BRAIN +CREATURE:GIANT_RHINOCEROS:LUNG +CREATURE:GIANT_RHINOCEROS:HEART +CREATURE:GIANT_RHINOCEROS:LIVER +CREATURE:GIANT_RHINOCEROS:GUT +!CREATURE:GIANT_RHINOCEROS:STOMACH +!CREATURE:GIANT_RHINOCEROS:GIZZARD +"CREATURE:GIANT_RHINOCEROS:PANCREAS + CREATURE:GIANT_RHINOCEROS:SPLEEN + CREATURE:GIANT_RHINOCEROS:KIDNEY +CREATURE:GIRAFFE:MUSCLE +CREATURE:GIRAFFE:EYE +CREATURE:GIRAFFE:BRAIN +CREATURE:GIRAFFE:LUNG +CREATURE:GIRAFFE:HEART +CREATURE:GIRAFFE:LIVER +CREATURE:GIRAFFE:GUT +CREATURE:GIRAFFE:STOMACH +CREATURE:GIRAFFE:GIZZARD +CREATURE:GIRAFFE:PANCREAS +CREATURE:GIRAFFE:SPLEEN +CREATURE:GIRAFFE:KIDNEY +CREATURE:GIRAFFE_MAN:MUSCLE +CREATURE:GIRAFFE_MAN:EYE +CREATURE:GIRAFFE_MAN:BRAIN +CREATURE:GIRAFFE_MAN:LUNG +CREATURE:GIRAFFE_MAN:HEART +CREATURE:GIRAFFE_MAN:LIVER +CREATURE:GIRAFFE_MAN:GUT +CREATURE:GIRAFFE_MAN:STOMACH +CREATURE:GIRAFFE_MAN:GIZZARD +CREATURE:GIRAFFE_MAN:PANCREAS +CREATURE:GIRAFFE_MAN:SPLEEN +CREATURE:GIRAFFE_MAN:KIDNEY +CREATURE:GIANT_GIRAFFE:MUSCLE +CREATURE:GIANT_GIRAFFE:EYE +CREATURE:GIANT_GIRAFFE:BRAIN +CREATURE:GIANT_GIRAFFE:LUNG +CREATURE:GIANT_GIRAFFE:HEART +CREATURE:GIANT_GIRAFFE:LIVER +CREATURE:GIANT_GIRAFFE:GUT +CREATURE:GIANT_GIRAFFE:STOMACH +CREATURE:GIANT_GIRAFFE:GIZZARD +CREATURE:GIANT_GIRAFFE:PANCREAS +CREATURE:GIANT_GIRAFFE:SPLEEN +CREATURE:GIANT_GIRAFFE:KIDNEY +CREATURE:HONEY BADGER:MUSCLE +CREATURE:HONEY BADGER:EYE +CREATURE:HONEY BADGER:BRAIN +CREATURE:HONEY BADGER:LUNG +CREATURE:HONEY BADGER:HEART +CREATURE:HONEY BADGER:LIVER +CREATURE:HONEY BADGER:GUT +CREATURE:HONEY BADGER:STOMACH +CREATURE:HONEY BADGER:GIZZARD +CREATURE:HONEY BADGER:PANCREAS +CREATURE:HONEY BADGER:SPLEEN +CREATURE:HONEY BADGER:KIDNEY + CREATURE:HONEY BADGER MAN:MUSCLE +CREATURE:HONEY BADGER MAN:EYE +CREATURE:HONEY BADGER MAN:BRAIN +CREATURE:HONEY BADGER MAN:LUNG +CREATURE:HONEY BADGER MAN:HEART +CREATURE:HONEY BADGER MAN:LIVER +CREATURE:HONEY BADGER MAN:GUT +!CREATURE:HONEY BADGER MAN:STOMACH +!CREATURE:HONEY BADGER MAN:GIZZARD +"CREATURE:HONEY BADGER MAN:PANCREAS + CREATURE:HONEY BADGER MAN:SPLEEN + CREATURE:HONEY BADGER MAN:KIDNEY +#CREATURE:HONEY BADGER, GIANT:MUSCLE + CREATURE:HONEY BADGER, GIANT:EYE +"CREATURE:HONEY BADGER, GIANT:BRAIN +!CREATURE:HONEY BADGER, GIANT:LUNG +"CREATURE:HONEY BADGER, GIANT:HEART +"CREATURE:HONEY BADGER, GIANT:LIVER + CREATURE:HONEY BADGER, GIANT:GUT +$CREATURE:HONEY BADGER, GIANT:STOMACH +$CREATURE:HONEY BADGER, GIANT:GIZZARD +%CREATURE:HONEY BADGER, GIANT:PANCREAS +#CREATURE:HONEY BADGER, GIANT:SPLEEN +#CREATURE:HONEY BADGER, GIANT:KIDNEY +CREATURE:GIANT TORTOISE:MUSCLE +CREATURE:GIANT TORTOISE:EYE +CREATURE:GIANT TORTOISE:BRAIN +CREATURE:GIANT TORTOISE:LUNG +CREATURE:GIANT TORTOISE:HEART +CREATURE:GIANT TORTOISE:LIVER +CREATURE:GIANT TORTOISE:GUT +CREATURE:GIANT TORTOISE:STOMACH +CREATURE:GIANT TORTOISE:GIZZARD + CREATURE:GIANT TORTOISE:PANCREAS +CREATURE:GIANT TORTOISE:SPLEEN +CREATURE:GIANT TORTOISE:KIDNEY +"CREATURE:GIANT TORTOISE MAN:MUSCLE +CREATURE:GIANT TORTOISE MAN:EYE +!CREATURE:GIANT TORTOISE MAN:BRAIN + CREATURE:GIANT TORTOISE MAN:LUNG +!CREATURE:GIANT TORTOISE MAN:HEART +!CREATURE:GIANT TORTOISE MAN:LIVER +CREATURE:GIANT TORTOISE MAN:GUT +#CREATURE:GIANT TORTOISE MAN:STOMACH +#CREATURE:GIANT TORTOISE MAN:GIZZARD +$CREATURE:GIANT TORTOISE MAN:PANCREAS +"CREATURE:GIANT TORTOISE MAN:SPLEEN +"CREATURE:GIANT TORTOISE MAN:KIDNEY +!CREATURE:GIGANTIC TORTOISE:MUSCLE +CREATURE:GIGANTIC TORTOISE:EYE + CREATURE:GIGANTIC TORTOISE:BRAIN +CREATURE:GIGANTIC TORTOISE:LUNG + CREATURE:GIGANTIC TORTOISE:HEART + CREATURE:GIGANTIC TORTOISE:LIVER +CREATURE:GIGANTIC TORTOISE:GUT +"CREATURE:GIGANTIC TORTOISE:STOMACH +"CREATURE:GIGANTIC TORTOISE:GIZZARD +#CREATURE:GIGANTIC TORTOISE:PANCREAS +!CREATURE:GIGANTIC TORTOISE:SPLEEN +!CREATURE:GIGANTIC TORTOISE:KIDNEY +CREATURE:ARMADILLO:MUSCLE +CREATURE:ARMADILLO:EYE +CREATURE:ARMADILLO:BRAIN +CREATURE:ARMADILLO:LUNG +CREATURE:ARMADILLO:HEART +CREATURE:ARMADILLO:LIVER +CREATURE:ARMADILLO:GUT +CREATURE:ARMADILLO:STOMACH +CREATURE:ARMADILLO:GIZZARD +CREATURE:ARMADILLO:PANCREAS +CREATURE:ARMADILLO:SPLEEN +CREATURE:ARMADILLO:KIDNEY +CREATURE:ARMADILLO MAN:MUSCLE +CREATURE:ARMADILLO MAN:EYE +CREATURE:ARMADILLO MAN:BRAIN +CREATURE:ARMADILLO MAN:LUNG +CREATURE:ARMADILLO MAN:HEART +CREATURE:ARMADILLO MAN:LIVER +CREATURE:ARMADILLO MAN:GUT +CREATURE:ARMADILLO MAN:STOMACH +CREATURE:ARMADILLO MAN:GIZZARD +CREATURE:ARMADILLO MAN:PANCREAS +CREATURE:ARMADILLO MAN:SPLEEN +CREATURE:ARMADILLO MAN:KIDNEY + CREATURE:ARMADILLO, GIANT:MUSCLE +CREATURE:ARMADILLO, GIANT:EYE +CREATURE:ARMADILLO, GIANT:BRAIN +CREATURE:ARMADILLO, GIANT:LUNG +CREATURE:ARMADILLO, GIANT:HEART +CREATURE:ARMADILLO, GIANT:LIVER +CREATURE:ARMADILLO, GIANT:GUT +!CREATURE:ARMADILLO, GIANT:STOMACH +!CREATURE:ARMADILLO, GIANT:GIZZARD +"CREATURE:ARMADILLO, GIANT:PANCREAS + CREATURE:ARMADILLO, GIANT:SPLEEN + CREATURE:ARMADILLO, GIANT:KIDNEY +CREATURE:MUSKOX:MUSCLE +CREATURE:MUSKOX:EYE +CREATURE:MUSKOX:BRAIN +CREATURE:MUSKOX:LUNG +CREATURE:MUSKOX:HEART +CREATURE:MUSKOX:LIVER +CREATURE:MUSKOX:GUT +CREATURE:MUSKOX:STOMACH +CREATURE:MUSKOX:GIZZARD +CREATURE:MUSKOX:PANCREAS +CREATURE:MUSKOX:SPLEEN +CREATURE:MUSKOX:KIDNEY +CREATURE:MUSKOX_MAN:MUSCLE +CREATURE:MUSKOX_MAN:EYE +CREATURE:MUSKOX_MAN:BRAIN +CREATURE:MUSKOX_MAN:LUNG +CREATURE:MUSKOX_MAN:HEART +CREATURE:MUSKOX_MAN:LIVER +CREATURE:MUSKOX_MAN:GUT +CREATURE:MUSKOX_MAN:STOMACH +CREATURE:MUSKOX_MAN:GIZZARD +CREATURE:MUSKOX_MAN:PANCREAS +CREATURE:MUSKOX_MAN:SPLEEN +CREATURE:MUSKOX_MAN:KIDNEY +CREATURE:GIANT_MUSKOX:MUSCLE +CREATURE:GIANT_MUSKOX:EYE +CREATURE:GIANT_MUSKOX:BRAIN +CREATURE:GIANT_MUSKOX:LUNG +CREATURE:GIANT_MUSKOX:HEART +CREATURE:GIANT_MUSKOX:LIVER +CREATURE:GIANT_MUSKOX:GUT +CREATURE:GIANT_MUSKOX:STOMACH +CREATURE:GIANT_MUSKOX:GIZZARD +CREATURE:GIANT_MUSKOX:PANCREAS +CREATURE:GIANT_MUSKOX:SPLEEN +CREATURE:GIANT_MUSKOX:KIDNEY +CREATURE:ELK:MUSCLE +CREATURE:ELK:EYE +CREATURE:ELK:BRAIN +CREATURE:ELK:LUNG +CREATURE:ELK:HEART +CREATURE:ELK:LIVER +CREATURE:ELK:GUT +CREATURE:ELK:STOMACH +CREATURE:ELK:GIZZARD +CREATURE:ELK:PANCREAS +CREATURE:ELK:SPLEEN +CREATURE:ELK:KIDNEY +CREATURE:ELK_MAN:MUSCLE +CREATURE:ELK_MAN:EYE +CREATURE:ELK_MAN:BRAIN +CREATURE:ELK_MAN:LUNG +CREATURE:ELK_MAN:HEART +CREATURE:ELK_MAN:LIVER +CREATURE:ELK_MAN:GUT +CREATURE:ELK_MAN:STOMACH +CREATURE:ELK_MAN:GIZZARD +CREATURE:ELK_MAN:PANCREAS +CREATURE:ELK_MAN:SPLEEN +CREATURE:ELK_MAN:KIDNEY +CREATURE:GIANT_ELK:MUSCLE +CREATURE:GIANT_ELK:EYE +CREATURE:GIANT_ELK:BRAIN +CREATURE:GIANT_ELK:LUNG +CREATURE:GIANT_ELK:HEART +CREATURE:GIANT_ELK:LIVER +CREATURE:GIANT_ELK:GUT +CREATURE:GIANT_ELK:STOMACH +CREATURE:GIANT_ELK:GIZZARD +CREATURE:GIANT_ELK:PANCREAS +CREATURE:GIANT_ELK:SPLEEN +CREATURE:GIANT_ELK:KIDNEY +CREATURE:BEAR_POLAR:MUSCLE +CREATURE:BEAR_POLAR:EYE +CREATURE:BEAR_POLAR:BRAIN +CREATURE:BEAR_POLAR:LUNG +CREATURE:BEAR_POLAR:HEART +CREATURE:BEAR_POLAR:LIVER +CREATURE:BEAR_POLAR:GUT +CREATURE:BEAR_POLAR:STOMACH +CREATURE:BEAR_POLAR:GIZZARD +CREATURE:BEAR_POLAR:PANCREAS +CREATURE:BEAR_POLAR:SPLEEN +CREATURE:BEAR_POLAR:KIDNEY +CREATURE:BEAR_POLAR_MAN:MUSCLE +CREATURE:BEAR_POLAR_MAN:EYE +CREATURE:BEAR_POLAR_MAN:BRAIN +CREATURE:BEAR_POLAR_MAN:LUNG +CREATURE:BEAR_POLAR_MAN:HEART +CREATURE:BEAR_POLAR_MAN:LIVER +CREATURE:BEAR_POLAR_MAN:GUT +CREATURE:BEAR_POLAR_MAN:STOMACH +CREATURE:BEAR_POLAR_MAN:GIZZARD + CREATURE:BEAR_POLAR_MAN:PANCREAS +CREATURE:BEAR_POLAR_MAN:SPLEEN +CREATURE:BEAR_POLAR_MAN:KIDNEY + CREATURE:GIANT_BEAR_POLAR:MUSCLE +CREATURE:GIANT_BEAR_POLAR:EYE +CREATURE:GIANT_BEAR_POLAR:BRAIN +CREATURE:GIANT_BEAR_POLAR:LUNG +CREATURE:GIANT_BEAR_POLAR:HEART +CREATURE:GIANT_BEAR_POLAR:LIVER +CREATURE:GIANT_BEAR_POLAR:GUT +!CREATURE:GIANT_BEAR_POLAR:STOMACH +!CREATURE:GIANT_BEAR_POLAR:GIZZARD +"CREATURE:GIANT_BEAR_POLAR:PANCREAS + CREATURE:GIANT_BEAR_POLAR:SPLEEN + CREATURE:GIANT_BEAR_POLAR:KIDNEY +CREATURE:WOLVERINE:MUSCLE +CREATURE:WOLVERINE:EYE +CREATURE:WOLVERINE:BRAIN +CREATURE:WOLVERINE:LUNG +CREATURE:WOLVERINE:HEART +CREATURE:WOLVERINE:LIVER +CREATURE:WOLVERINE:GUT +CREATURE:WOLVERINE:STOMACH +CREATURE:WOLVERINE:GIZZARD +CREATURE:WOLVERINE:PANCREAS +CREATURE:WOLVERINE:SPLEEN +CREATURE:WOLVERINE:KIDNEY +CREATURE:WOLVERINE_MAN:MUSCLE +CREATURE:WOLVERINE_MAN:EYE +CREATURE:WOLVERINE_MAN:BRAIN +CREATURE:WOLVERINE_MAN:LUNG +CREATURE:WOLVERINE_MAN:HEART +CREATURE:WOLVERINE_MAN:LIVER +CREATURE:WOLVERINE_MAN:GUT +CREATURE:WOLVERINE_MAN:STOMACH +CREATURE:WOLVERINE_MAN:GIZZARD +CREATURE:WOLVERINE_MAN:PANCREAS +CREATURE:WOLVERINE_MAN:SPLEEN +CREATURE:WOLVERINE_MAN:KIDNEY +CREATURE:GIANT_WOLVERINE:MUSCLE +CREATURE:GIANT_WOLVERINE:EYE +CREATURE:GIANT_WOLVERINE:BRAIN +CREATURE:GIANT_WOLVERINE:LUNG +CREATURE:GIANT_WOLVERINE:HEART +CREATURE:GIANT_WOLVERINE:LIVER +CREATURE:GIANT_WOLVERINE:GUT + CREATURE:GIANT_WOLVERINE:STOMACH + CREATURE:GIANT_WOLVERINE:GIZZARD +!CREATURE:GIANT_WOLVERINE:PANCREAS +CREATURE:GIANT_WOLVERINE:SPLEEN +CREATURE:GIANT_WOLVERINE:KIDNEY +CREATURE:CHINCHILLA:MUSCLE +CREATURE:CHINCHILLA:EYE +CREATURE:CHINCHILLA:BRAIN +CREATURE:CHINCHILLA:LUNG +CREATURE:CHINCHILLA:HEART +CREATURE:CHINCHILLA:LIVER +CREATURE:CHINCHILLA:GUT +CREATURE:CHINCHILLA:STOMACH +CREATURE:CHINCHILLA:GIZZARD +CREATURE:CHINCHILLA:PANCREAS +CREATURE:CHINCHILLA:SPLEEN +CREATURE:CHINCHILLA:KIDNEY +CREATURE:CHINCHILLA_MAN:MUSCLE +CREATURE:CHINCHILLA_MAN:EYE +CREATURE:CHINCHILLA_MAN:BRAIN +CREATURE:CHINCHILLA_MAN:LUNG +CREATURE:CHINCHILLA_MAN:HEART +CREATURE:CHINCHILLA_MAN:LIVER +CREATURE:CHINCHILLA_MAN:GUT +CREATURE:CHINCHILLA_MAN:STOMACH +CREATURE:CHINCHILLA_MAN:GIZZARD + CREATURE:CHINCHILLA_MAN:PANCREAS +CREATURE:CHINCHILLA_MAN:SPLEEN +CREATURE:CHINCHILLA_MAN:KIDNEY + CREATURE:GIANT_CHINCHILLA:MUSCLE +CREATURE:GIANT_CHINCHILLA:EYE +CREATURE:GIANT_CHINCHILLA:BRAIN +CREATURE:GIANT_CHINCHILLA:LUNG +CREATURE:GIANT_CHINCHILLA:HEART +CREATURE:GIANT_CHINCHILLA:LIVER +CREATURE:GIANT_CHINCHILLA:GUT +!CREATURE:GIANT_CHINCHILLA:STOMACH +!CREATURE:GIANT_CHINCHILLA:GIZZARD +"CREATURE:GIANT_CHINCHILLA:PANCREAS + CREATURE:GIANT_CHINCHILLA:SPLEEN + CREATURE:GIANT_CHINCHILLA:KIDNEY +CREATURE:FLOATING_GUTS:MUSCLE +CREATURE:FLOATING_GUTS:EYE +CREATURE:FLOATING_GUTS:BRAIN +CREATURE:FLOATING_GUTS:LUNG +CREATURE:FLOATING_GUTS:HEART +CREATURE:FLOATING_GUTS:LIVER +CREATURE:FLOATING_GUTS:GUT +CREATURE:FLOATING_GUTS:STOMACH +CREATURE:FLOATING_GUTS:GIZZARD +CREATURE:FLOATING_GUTS:PANCREAS +CREATURE:FLOATING_GUTS:SPLEEN +CREATURE:FLOATING_GUTS:KIDNEY +CREATURE:DRUNIAN:MUSCLE +CREATURE:DRUNIAN:EYE +CREATURE:DRUNIAN:BRAIN +CREATURE:DRUNIAN:LUNG +CREATURE:DRUNIAN:HEART +CREATURE:DRUNIAN:LIVER +CREATURE:DRUNIAN:GUT +CREATURE:DRUNIAN:STOMACH +CREATURE:DRUNIAN:GIZZARD +CREATURE:DRUNIAN:PANCREAS +CREATURE:DRUNIAN:SPLEEN +CREATURE:DRUNIAN:KIDNEY +CREATURE:CREEPING_EYE:MUSCLE +CREATURE:CREEPING_EYE:EYE +CREATURE:CREEPING_EYE:BRAIN +CREATURE:CREEPING_EYE:LUNG +CREATURE:CREEPING_EYE:HEART +CREATURE:CREEPING_EYE:LIVER +CREATURE:CREEPING_EYE:GUT +CREATURE:CREEPING_EYE:STOMACH +CREATURE:CREEPING_EYE:GIZZARD +CREATURE:CREEPING_EYE:PANCREAS +CREATURE:CREEPING_EYE:SPLEEN +CREATURE:CREEPING_EYE:KIDNEY +&CREATURE:VORACIOUS_CAVE_CRAWLER:MUSCLE +#CREATURE:VORACIOUS_CAVE_CRAWLER:EYE +%CREATURE:VORACIOUS_CAVE_CRAWLER:BRAIN +$CREATURE:VORACIOUS_CAVE_CRAWLER:LUNG +%CREATURE:VORACIOUS_CAVE_CRAWLER:HEART +%CREATURE:VORACIOUS_CAVE_CRAWLER:LIVER +#CREATURE:VORACIOUS_CAVE_CRAWLER:GUT +'CREATURE:VORACIOUS_CAVE_CRAWLER:STOMACH +'CREATURE:VORACIOUS_CAVE_CRAWLER:GIZZARD +(CREATURE:VORACIOUS_CAVE_CRAWLER:PANCREAS +&CREATURE:VORACIOUS_CAVE_CRAWLER:SPLEEN +&CREATURE:VORACIOUS_CAVE_CRAWLER:KIDNEY +CREATURE:BLIND_CAVE_OGRE:MUSCLE +CREATURE:BLIND_CAVE_OGRE:EYE +CREATURE:BLIND_CAVE_OGRE:BRAIN +CREATURE:BLIND_CAVE_OGRE:LUNG +CREATURE:BLIND_CAVE_OGRE:HEART +CREATURE:BLIND_CAVE_OGRE:LIVER +CREATURE:BLIND_CAVE_OGRE:GUT + CREATURE:BLIND_CAVE_OGRE:STOMACH + CREATURE:BLIND_CAVE_OGRE:GIZZARD +!CREATURE:BLIND_CAVE_OGRE:PANCREAS +CREATURE:BLIND_CAVE_OGRE:SPLEEN +CREATURE:BLIND_CAVE_OGRE:KIDNEY +CREATURE:CAP_HOPPER:MUSCLE +CREATURE:CAP_HOPPER:EYE +CREATURE:CAP_HOPPER:BRAIN +CREATURE:CAP_HOPPER:LUNG +CREATURE:CAP_HOPPER:HEART +CREATURE:CAP_HOPPER:LIVER +CREATURE:CAP_HOPPER:GUT +CREATURE:CAP_HOPPER:STOMACH +CREATURE:CAP_HOPPER:GIZZARD +CREATURE:CAP_HOPPER:PANCREAS +CREATURE:CAP_HOPPER:SPLEEN +CREATURE:CAP_HOPPER:KIDNEY +CREATURE:MAGMA_CRAB:MUSCLE +CREATURE:MAGMA_CRAB:EYE +CREATURE:MAGMA_CRAB:BRAIN +CREATURE:MAGMA_CRAB:LUNG +CREATURE:MAGMA_CRAB:HEART +CREATURE:MAGMA_CRAB:LIVER +CREATURE:MAGMA_CRAB:GUT +CREATURE:MAGMA_CRAB:STOMACH +CREATURE:MAGMA_CRAB:GIZZARD +CREATURE:MAGMA_CRAB:PANCREAS +CREATURE:MAGMA_CRAB:SPLEEN +CREATURE:MAGMA_CRAB:KIDNEY +CREATURE:CRUNDLE:MUSCLE +CREATURE:CRUNDLE:EYE +CREATURE:CRUNDLE:BRAIN +CREATURE:CRUNDLE:LUNG +CREATURE:CRUNDLE:HEART +CREATURE:CRUNDLE:LIVER +CREATURE:CRUNDLE:GUT +CREATURE:CRUNDLE:STOMACH +CREATURE:CRUNDLE:GIZZARD +CREATURE:CRUNDLE:PANCREAS +CREATURE:CRUNDLE:SPLEEN +CREATURE:CRUNDLE:KIDNEY +CREATURE:HUNGRY_HEAD:MUSCLE +CREATURE:HUNGRY_HEAD:EYE +CREATURE:HUNGRY_HEAD:BRAIN +CREATURE:HUNGRY_HEAD:LUNG +CREATURE:HUNGRY_HEAD:HEART +CREATURE:HUNGRY_HEAD:LIVER +CREATURE:HUNGRY_HEAD:GUT +CREATURE:HUNGRY_HEAD:STOMACH +CREATURE:HUNGRY_HEAD:GIZZARD +CREATURE:HUNGRY_HEAD:PANCREAS +CREATURE:HUNGRY_HEAD:SPLEEN +CREATURE:HUNGRY_HEAD:KIDNEY +CREATURE:FLESH_BALL:MUSCLE +CREATURE:ELK_BIRD:MUSCLE +CREATURE:ELK_BIRD:EYE +CREATURE:ELK_BIRD:BRAIN +CREATURE:ELK_BIRD:LUNG +CREATURE:ELK_BIRD:HEART +CREATURE:ELK_BIRD:LIVER +CREATURE:ELK_BIRD:GUT +CREATURE:ELK_BIRD:STOMACH +CREATURE:ELK_BIRD:GIZZARD +CREATURE:ELK_BIRD:PANCREAS +CREATURE:ELK_BIRD:SPLEEN +CREATURE:ELK_BIRD:KIDNEY +CREATURE:HELMET_SNAKE:MUSCLE +CREATURE:HELMET_SNAKE:EYE +CREATURE:HELMET_SNAKE:BRAIN +CREATURE:HELMET_SNAKE:LUNG +CREATURE:HELMET_SNAKE:HEART +CREATURE:HELMET_SNAKE:LIVER +CREATURE:HELMET_SNAKE:GUT +CREATURE:HELMET_SNAKE:STOMACH +CREATURE:HELMET_SNAKE:GIZZARD +CREATURE:HELMET_SNAKE:PANCREAS +CREATURE:HELMET_SNAKE:SPLEEN +CREATURE:HELMET_SNAKE:KIDNEY +CREATURE:GREEN_DEVOURER:MUSCLE +CREATURE:GREEN_DEVOURER:EYE +CREATURE:GREEN_DEVOURER:BRAIN +CREATURE:GREEN_DEVOURER:LUNG +CREATURE:GREEN_DEVOURER:HEART +CREATURE:GREEN_DEVOURER:LIVER +CREATURE:GREEN_DEVOURER:GUT +CREATURE:GREEN_DEVOURER:STOMACH +CREATURE:GREEN_DEVOURER:GIZZARD + CREATURE:GREEN_DEVOURER:PANCREAS +CREATURE:GREEN_DEVOURER:SPLEEN +CREATURE:GREEN_DEVOURER:KIDNEY +CREATURE:RUTHERER:MUSCLE +CREATURE:RUTHERER:EYE +CREATURE:RUTHERER:BRAIN +CREATURE:RUTHERER:LUNG +CREATURE:RUTHERER:HEART +CREATURE:RUTHERER:LIVER +CREATURE:RUTHERER:GUT +CREATURE:RUTHERER:STOMACH +CREATURE:RUTHERER:GIZZARD +CREATURE:RUTHERER:PANCREAS +CREATURE:RUTHERER:SPLEEN +CREATURE:RUTHERER:KIDNEY +CREATURE:CREEPY_CRAWLER:MUSCLE +CREATURE:CREEPY_CRAWLER:EYE +CREATURE:CREEPY_CRAWLER:BRAIN +CREATURE:CREEPY_CRAWLER:LUNG +CREATURE:CREEPY_CRAWLER:HEART +CREATURE:CREEPY_CRAWLER:LIVER +CREATURE:CREEPY_CRAWLER:GUT +CREATURE:CREEPY_CRAWLER:STOMACH +CREATURE:CREEPY_CRAWLER:GIZZARD + CREATURE:CREEPY_CRAWLER:PANCREAS +CREATURE:CREEPY_CRAWLER:SPLEEN +CREATURE:CREEPY_CRAWLER:KIDNEY +CREATURE:DRALTHA:MUSCLE +CREATURE:DRALTHA:EYE +CREATURE:DRALTHA:BRAIN +CREATURE:DRALTHA:LUNG +CREATURE:DRALTHA:HEART +CREATURE:DRALTHA:LIVER +CREATURE:DRALTHA:GUT +CREATURE:DRALTHA:STOMACH +CREATURE:DRALTHA:GIZZARD +CREATURE:DRALTHA:PANCREAS +CREATURE:DRALTHA:SPLEEN +CREATURE:DRALTHA:KIDNEY +CREATURE:GIANT_EARTHWORM:MUSCLE +CREATURE:GIANT_EARTHWORM:EYE +CREATURE:GIANT_EARTHWORM:BRAIN +CREATURE:GIANT_EARTHWORM:LUNG +CREATURE:GIANT_EARTHWORM:HEART +CREATURE:GIANT_EARTHWORM:LIVER +CREATURE:GIANT_EARTHWORM:GUT + CREATURE:GIANT_EARTHWORM:STOMACH + CREATURE:GIANT_EARTHWORM:GIZZARD +!CREATURE:GIANT_EARTHWORM:PANCREAS +CREATURE:GIANT_EARTHWORM:SPLEEN +CREATURE:GIANT_EARTHWORM:KIDNEY +CREATURE:BUGBAT:MUSCLE +CREATURE:BUGBAT:EYE +CREATURE:BUGBAT:BRAIN +CREATURE:BUGBAT:LUNG +CREATURE:BUGBAT:HEART +CREATURE:BUGBAT:LIVER +CREATURE:BUGBAT:GUT +CREATURE:BUGBAT:STOMACH +CREATURE:BUGBAT:GIZZARD +CREATURE:BUGBAT:PANCREAS +CREATURE:BUGBAT:SPLEEN +CREATURE:BUGBAT:KIDNEY +CREATURE:MANERA:MUSCLE +CREATURE:MANERA:EYE +CREATURE:MANERA:BRAIN +CREATURE:MANERA:LUNG +CREATURE:MANERA:HEART +CREATURE:MANERA:LIVER +CREATURE:MANERA:GUT +CREATURE:MANERA:STOMACH +CREATURE:MANERA:GIZZARD +CREATURE:MANERA:PANCREAS +CREATURE:MANERA:SPLEEN +CREATURE:MANERA:KIDNEY +CREATURE:MOLEMARIAN:MUSCLE +CREATURE:MOLEMARIAN:EYE +CREATURE:MOLEMARIAN:BRAIN +CREATURE:MOLEMARIAN:LUNG +CREATURE:MOLEMARIAN:HEART +CREATURE:MOLEMARIAN:LIVER +CREATURE:MOLEMARIAN:GUT +CREATURE:MOLEMARIAN:STOMACH +CREATURE:MOLEMARIAN:GIZZARD +CREATURE:MOLEMARIAN:PANCREAS +CREATURE:MOLEMARIAN:SPLEEN +CREATURE:MOLEMARIAN:KIDNEY +CREATURE:JABBERER:MUSCLE +CREATURE:JABBERER:EYE +CREATURE:JABBERER:BRAIN +CREATURE:JABBERER:LUNG +CREATURE:JABBERER:HEART +CREATURE:JABBERER:LIVER +CREATURE:JABBERER:GUT +CREATURE:JABBERER:STOMACH +CREATURE:JABBERER:GIZZARD +CREATURE:JABBERER:PANCREAS +CREATURE:JABBERER:SPLEEN +CREATURE:JABBERER:KIDNEY +CREATURE:POND_GRABBER:MUSCLE +CREATURE:POND_GRABBER:EYE +CREATURE:POND_GRABBER:BRAIN +CREATURE:POND_GRABBER:LUNG +CREATURE:POND_GRABBER:HEART +CREATURE:POND_GRABBER:LIVER +CREATURE:POND_GRABBER:GUT +CREATURE:POND_GRABBER:STOMACH +CREATURE:POND_GRABBER:GIZZARD +CREATURE:POND_GRABBER:PANCREAS +CREATURE:POND_GRABBER:SPLEEN +CREATURE:POND_GRABBER:KIDNEY +CREATURE:BLIND_CAVE_BEAR:MUSCLE +CREATURE:BLIND_CAVE_BEAR:EYE +CREATURE:BLIND_CAVE_BEAR:BRAIN +CREATURE:BLIND_CAVE_BEAR:LUNG +CREATURE:BLIND_CAVE_BEAR:HEART +CREATURE:BLIND_CAVE_BEAR:LIVER +CREATURE:BLIND_CAVE_BEAR:GUT + CREATURE:BLIND_CAVE_BEAR:STOMACH + CREATURE:BLIND_CAVE_BEAR:GIZZARD +!CREATURE:BLIND_CAVE_BEAR:PANCREAS +CREATURE:BLIND_CAVE_BEAR:SPLEEN +CREATURE:BLIND_CAVE_BEAR:KIDNEY +CREATURE:CAVE_DRAGON:MUSCLE +CREATURE:CAVE_DRAGON:EYE +CREATURE:CAVE_DRAGON:BRAIN +CREATURE:CAVE_DRAGON:LUNG +CREATURE:CAVE_DRAGON:HEART +CREATURE:CAVE_DRAGON:LIVER +CREATURE:CAVE_DRAGON:GUT +CREATURE:CAVE_DRAGON:STOMACH +CREATURE:CAVE_DRAGON:GIZZARD +CREATURE:CAVE_DRAGON:PANCREAS +CREATURE:CAVE_DRAGON:SPLEEN +CREATURE:CAVE_DRAGON:KIDNEY +CREATURE:REACHER:MUSCLE +CREATURE:REACHER:EYE +CREATURE:REACHER:BRAIN +CREATURE:REACHER:LUNG +CREATURE:REACHER:HEART +CREATURE:REACHER:LIVER +CREATURE:REACHER:GUT +CREATURE:REACHER:STOMACH +CREATURE:REACHER:GIZZARD +CREATURE:REACHER:PANCREAS +CREATURE:REACHER:SPLEEN +CREATURE:REACHER:KIDNEY +CREATURE:GORLAK:MUSCLE +CREATURE:GORLAK:EYE +CREATURE:GORLAK:BRAIN +CREATURE:GORLAK:LUNG +CREATURE:GORLAK:HEART +CREATURE:GORLAK:LIVER +CREATURE:GORLAK:GUT +CREATURE:GORLAK:STOMACH +CREATURE:GORLAK:GIZZARD +CREATURE:GORLAK:PANCREAS +CREATURE:GORLAK:SPLEEN +CREATURE:GORLAK:KIDNEY +CREATURE:OCTOPUS:MUSCLE +CREATURE:OCTOPUS:EYE +CREATURE:OCTOPUS:BRAIN +CREATURE:OCTOPUS:LUNG +CREATURE:OCTOPUS:HEART +CREATURE:OCTOPUS:LIVER +CREATURE:OCTOPUS:GUT +CREATURE:OCTOPUS:STOMACH +CREATURE:OCTOPUS:GIZZARD +CREATURE:OCTOPUS:PANCREAS +CREATURE:OCTOPUS:SPLEEN +CREATURE:OCTOPUS:KIDNEY +CREATURE:OCTOPUS_MAN:MUSCLE +CREATURE:OCTOPUS_MAN:EYE +CREATURE:OCTOPUS_MAN:BRAIN +CREATURE:OCTOPUS_MAN:LUNG +CREATURE:OCTOPUS_MAN:HEART +CREATURE:OCTOPUS_MAN:LIVER +CREATURE:OCTOPUS_MAN:GUT +CREATURE:OCTOPUS_MAN:STOMACH +CREATURE:OCTOPUS_MAN:GIZZARD +CREATURE:OCTOPUS_MAN:PANCREAS +CREATURE:OCTOPUS_MAN:SPLEEN +CREATURE:OCTOPUS_MAN:KIDNEY +CREATURE:GIANT_OCTOPUS:MUSCLE +CREATURE:GIANT_OCTOPUS:EYE +CREATURE:GIANT_OCTOPUS:BRAIN +CREATURE:GIANT_OCTOPUS:LUNG +CREATURE:GIANT_OCTOPUS:HEART +CREATURE:GIANT_OCTOPUS:LIVER +CREATURE:GIANT_OCTOPUS:GUT +CREATURE:GIANT_OCTOPUS:STOMACH +CREATURE:GIANT_OCTOPUS:GIZZARD +CREATURE:GIANT_OCTOPUS:PANCREAS +CREATURE:GIANT_OCTOPUS:SPLEEN +CREATURE:GIANT_OCTOPUS:KIDNEY +CREATURE:CRAB:MUSCLE +CREATURE:CRAB:EYE +CREATURE:CRAB:BRAIN +CREATURE:CRAB:LUNG +CREATURE:CRAB:HEART +CREATURE:CRAB:LIVER +CREATURE:CRAB:GUT +CREATURE:CRAB:STOMACH +CREATURE:CRAB:GIZZARD +CREATURE:CRAB:PANCREAS +CREATURE:CRAB:SPLEEN +CREATURE:CRAB:KIDNEY +CREATURE:CRAB_MAN:MUSCLE +CREATURE:CRAB_MAN:EYE +CREATURE:CRAB_MAN:BRAIN +CREATURE:CRAB_MAN:LUNG +CREATURE:CRAB_MAN:HEART +CREATURE:CRAB_MAN:LIVER +CREATURE:CRAB_MAN:GUT +CREATURE:CRAB_MAN:STOMACH +CREATURE:CRAB_MAN:GIZZARD +CREATURE:CRAB_MAN:PANCREAS +CREATURE:CRAB_MAN:SPLEEN +CREATURE:CRAB_MAN:KIDNEY +CREATURE:GIANT_CRAB:MUSCLE +CREATURE:GIANT_CRAB:EYE +CREATURE:GIANT_CRAB:BRAIN +CREATURE:GIANT_CRAB:LUNG +CREATURE:GIANT_CRAB:HEART +CREATURE:GIANT_CRAB:LIVER +CREATURE:GIANT_CRAB:GUT +CREATURE:GIANT_CRAB:STOMACH +CREATURE:GIANT_CRAB:GIZZARD +CREATURE:GIANT_CRAB:PANCREAS +CREATURE:GIANT_CRAB:SPLEEN +CREATURE:GIANT_CRAB:KIDNEY +CREATURE:LEOPARD_SEAL:MUSCLE +CREATURE:LEOPARD_SEAL:EYE +CREATURE:LEOPARD_SEAL:BRAIN +CREATURE:LEOPARD_SEAL:LUNG +CREATURE:LEOPARD_SEAL:HEART +CREATURE:LEOPARD_SEAL:LIVER +CREATURE:LEOPARD_SEAL:GUT +CREATURE:LEOPARD_SEAL:STOMACH +CREATURE:LEOPARD_SEAL:GIZZARD +CREATURE:LEOPARD_SEAL:PANCREAS +CREATURE:LEOPARD_SEAL:SPLEEN +CREATURE:LEOPARD_SEAL:KIDNEY + CREATURE:LEOPARD_SEAL_MAN:MUSCLE +CREATURE:LEOPARD_SEAL_MAN:EYE +CREATURE:LEOPARD_SEAL_MAN:BRAIN +CREATURE:LEOPARD_SEAL_MAN:LUNG +CREATURE:LEOPARD_SEAL_MAN:HEART +CREATURE:LEOPARD_SEAL_MAN:LIVER +CREATURE:LEOPARD_SEAL_MAN:GUT +!CREATURE:LEOPARD_SEAL_MAN:STOMACH +!CREATURE:LEOPARD_SEAL_MAN:GIZZARD +"CREATURE:LEOPARD_SEAL_MAN:PANCREAS + CREATURE:LEOPARD_SEAL_MAN:SPLEEN + CREATURE:LEOPARD_SEAL_MAN:KIDNEY +"CREATURE:GIANT_LEOPARD_SEAL:MUSCLE +CREATURE:GIANT_LEOPARD_SEAL:EYE +!CREATURE:GIANT_LEOPARD_SEAL:BRAIN + CREATURE:GIANT_LEOPARD_SEAL:LUNG +!CREATURE:GIANT_LEOPARD_SEAL:HEART +!CREATURE:GIANT_LEOPARD_SEAL:LIVER +CREATURE:GIANT_LEOPARD_SEAL:GUT +#CREATURE:GIANT_LEOPARD_SEAL:STOMACH +#CREATURE:GIANT_LEOPARD_SEAL:GIZZARD +$CREATURE:GIANT_LEOPARD_SEAL:PANCREAS +"CREATURE:GIANT_LEOPARD_SEAL:SPLEEN +"CREATURE:GIANT_LEOPARD_SEAL:KIDNEY +CREATURE:CUTTLEFISH:MUSCLE +CREATURE:CUTTLEFISH:EYE +CREATURE:CUTTLEFISH:BRAIN +CREATURE:CUTTLEFISH:LUNG +CREATURE:CUTTLEFISH:HEART +CREATURE:CUTTLEFISH:LIVER +CREATURE:CUTTLEFISH:GUT +CREATURE:CUTTLEFISH:STOMACH +CREATURE:CUTTLEFISH:GIZZARD +CREATURE:CUTTLEFISH:PANCREAS +CREATURE:CUTTLEFISH:SPLEEN +CREATURE:CUTTLEFISH:KIDNEY +CREATURE:CUTTLEFISH_MAN:MUSCLE +CREATURE:CUTTLEFISH_MAN:EYE +CREATURE:CUTTLEFISH_MAN:BRAIN +CREATURE:CUTTLEFISH_MAN:LUNG +CREATURE:CUTTLEFISH_MAN:HEART +CREATURE:CUTTLEFISH_MAN:LIVER +CREATURE:CUTTLEFISH_MAN:GUT +CREATURE:CUTTLEFISH_MAN:STOMACH +CREATURE:CUTTLEFISH_MAN:GIZZARD + CREATURE:CUTTLEFISH_MAN:PANCREAS +CREATURE:CUTTLEFISH_MAN:SPLEEN +CREATURE:CUTTLEFISH_MAN:KIDNEY + CREATURE:GIANT_CUTTLEFISH:MUSCLE +CREATURE:GIANT_CUTTLEFISH:EYE +CREATURE:GIANT_CUTTLEFISH:BRAIN +CREATURE:GIANT_CUTTLEFISH:LUNG +CREATURE:GIANT_CUTTLEFISH:HEART +CREATURE:GIANT_CUTTLEFISH:LIVER +CREATURE:GIANT_CUTTLEFISH:GUT +!CREATURE:GIANT_CUTTLEFISH:STOMACH +!CREATURE:GIANT_CUTTLEFISH:GIZZARD +"CREATURE:GIANT_CUTTLEFISH:PANCREAS + CREATURE:GIANT_CUTTLEFISH:SPLEEN + CREATURE:GIANT_CUTTLEFISH:KIDNEY +CREATURE:ORCA:MUSCLE +CREATURE:ORCA:EYE +CREATURE:ORCA:BRAIN +CREATURE:ORCA:LUNG +CREATURE:ORCA:HEART +CREATURE:ORCA:LIVER +CREATURE:ORCA:GUT +CREATURE:ORCA:STOMACH +CREATURE:ORCA:GIZZARD +CREATURE:ORCA:PANCREAS +CREATURE:ORCA:SPLEEN +CREATURE:ORCA:KIDNEY +CREATURE:ORCA_MAN:MUSCLE +CREATURE:ORCA_MAN:EYE +CREATURE:ORCA_MAN:BRAIN +CREATURE:ORCA_MAN:LUNG +CREATURE:ORCA_MAN:HEART +CREATURE:ORCA_MAN:LIVER +CREATURE:ORCA_MAN:GUT +CREATURE:ORCA_MAN:STOMACH +CREATURE:ORCA_MAN:GIZZARD +CREATURE:ORCA_MAN:PANCREAS +CREATURE:ORCA_MAN:SPLEEN +CREATURE:ORCA_MAN:KIDNEY +CREATURE:GIANT_ORCA:MUSCLE +CREATURE:GIANT_ORCA:EYE +CREATURE:GIANT_ORCA:BRAIN +CREATURE:GIANT_ORCA:LUNG +CREATURE:GIANT_ORCA:HEART +CREATURE:GIANT_ORCA:LIVER +CREATURE:GIANT_ORCA:GUT +CREATURE:GIANT_ORCA:STOMACH +CREATURE:GIANT_ORCA:GIZZARD +CREATURE:GIANT_ORCA:PANCREAS +CREATURE:GIANT_ORCA:SPLEEN +CREATURE:GIANT_ORCA:KIDNEY +CREATURE:HORSESHOE_CRAB:MUSCLE +CREATURE:HORSESHOE_CRAB:EYE +CREATURE:HORSESHOE_CRAB:BRAIN +CREATURE:HORSESHOE_CRAB:LUNG +CREATURE:HORSESHOE_CRAB:HEART +CREATURE:HORSESHOE_CRAB:LIVER +CREATURE:HORSESHOE_CRAB:GUT +CREATURE:HORSESHOE_CRAB:STOMACH +CREATURE:HORSESHOE_CRAB:GIZZARD + CREATURE:HORSESHOE_CRAB:PANCREAS +CREATURE:HORSESHOE_CRAB:SPLEEN +CREATURE:HORSESHOE_CRAB:KIDNEY +"CREATURE:HORSESHOE_CRAB_MAN:MUSCLE +CREATURE:HORSESHOE_CRAB_MAN:EYE +!CREATURE:HORSESHOE_CRAB_MAN:BRAIN + CREATURE:HORSESHOE_CRAB_MAN:LUNG +!CREATURE:HORSESHOE_CRAB_MAN:HEART +!CREATURE:HORSESHOE_CRAB_MAN:LIVER +CREATURE:HORSESHOE_CRAB_MAN:GUT +#CREATURE:HORSESHOE_CRAB_MAN:STOMACH +#CREATURE:HORSESHOE_CRAB_MAN:GIZZARD +$CREATURE:HORSESHOE_CRAB_MAN:PANCREAS +"CREATURE:HORSESHOE_CRAB_MAN:SPLEEN +"CREATURE:HORSESHOE_CRAB_MAN:KIDNEY +$CREATURE:GIANT_HORSESHOE_CRAB:MUSCLE +!CREATURE:GIANT_HORSESHOE_CRAB:EYE +#CREATURE:GIANT_HORSESHOE_CRAB:BRAIN +"CREATURE:GIANT_HORSESHOE_CRAB:LUNG +#CREATURE:GIANT_HORSESHOE_CRAB:HEART +#CREATURE:GIANT_HORSESHOE_CRAB:LIVER +!CREATURE:GIANT_HORSESHOE_CRAB:GUT +%CREATURE:GIANT_HORSESHOE_CRAB:STOMACH +%CREATURE:GIANT_HORSESHOE_CRAB:GIZZARD +&CREATURE:GIANT_HORSESHOE_CRAB:PANCREAS +$CREATURE:GIANT_HORSESHOE_CRAB:SPLEEN +$CREATURE:GIANT_HORSESHOE_CRAB:KIDNEY +CREATURE:SPERM_WHALE:MUSCLE +CREATURE:SPERM_WHALE:EYE +CREATURE:SPERM_WHALE:BRAIN +CREATURE:SPERM_WHALE:LUNG +CREATURE:SPERM_WHALE:HEART +CREATURE:SPERM_WHALE:LIVER +CREATURE:SPERM_WHALE:GUT +CREATURE:SPERM_WHALE:STOMACH +CREATURE:SPERM_WHALE:GIZZARD +CREATURE:SPERM_WHALE:PANCREAS +CREATURE:SPERM_WHALE:SPLEEN +CREATURE:SPERM_WHALE:KIDNEY +CREATURE:SPERM_WHALE_MAN:MUSCLE +CREATURE:SPERM_WHALE_MAN:EYE +CREATURE:SPERM_WHALE_MAN:BRAIN +CREATURE:SPERM_WHALE_MAN:LUNG +CREATURE:SPERM_WHALE_MAN:HEART +CREATURE:SPERM_WHALE_MAN:LIVER +CREATURE:SPERM_WHALE_MAN:GUT + CREATURE:SPERM_WHALE_MAN:STOMACH + CREATURE:SPERM_WHALE_MAN:GIZZARD +!CREATURE:SPERM_WHALE_MAN:PANCREAS +CREATURE:SPERM_WHALE_MAN:SPLEEN +CREATURE:SPERM_WHALE_MAN:KIDNEY +!CREATURE:GIANT_SPERM_WHALE:MUSCLE +CREATURE:GIANT_SPERM_WHALE:EYE + CREATURE:GIANT_SPERM_WHALE:BRAIN +CREATURE:GIANT_SPERM_WHALE:LUNG + CREATURE:GIANT_SPERM_WHALE:HEART + CREATURE:GIANT_SPERM_WHALE:LIVER +CREATURE:GIANT_SPERM_WHALE:GUT +"CREATURE:GIANT_SPERM_WHALE:STOMACH +"CREATURE:GIANT_SPERM_WHALE:GIZZARD +#CREATURE:GIANT_SPERM_WHALE:PANCREAS +!CREATURE:GIANT_SPERM_WHALE:SPLEEN +!CREATURE:GIANT_SPERM_WHALE:KIDNEY +CREATURE:ELEPHANT_SEAL:MUSCLE +CREATURE:ELEPHANT_SEAL:EYE +CREATURE:ELEPHANT_SEAL:BRAIN +CREATURE:ELEPHANT_SEAL:LUNG +CREATURE:ELEPHANT_SEAL:HEART +CREATURE:ELEPHANT_SEAL:LIVER +CREATURE:ELEPHANT_SEAL:GUT +CREATURE:ELEPHANT_SEAL:STOMACH +CREATURE:ELEPHANT_SEAL:GIZZARD +CREATURE:ELEPHANT_SEAL:PANCREAS +CREATURE:ELEPHANT_SEAL:SPLEEN +CREATURE:ELEPHANT_SEAL:KIDNEY +!CREATURE:ELEPHANT_SEAL_MAN:MUSCLE +CREATURE:ELEPHANT_SEAL_MAN:EYE + CREATURE:ELEPHANT_SEAL_MAN:BRAIN +CREATURE:ELEPHANT_SEAL_MAN:LUNG + CREATURE:ELEPHANT_SEAL_MAN:HEART + CREATURE:ELEPHANT_SEAL_MAN:LIVER +CREATURE:ELEPHANT_SEAL_MAN:GUT +"CREATURE:ELEPHANT_SEAL_MAN:STOMACH +"CREATURE:ELEPHANT_SEAL_MAN:GIZZARD +#CREATURE:ELEPHANT_SEAL_MAN:PANCREAS +!CREATURE:ELEPHANT_SEAL_MAN:SPLEEN +!CREATURE:ELEPHANT_SEAL_MAN:KIDNEY +#CREATURE:GIANT_ELEPHANT_SEAL:MUSCLE + CREATURE:GIANT_ELEPHANT_SEAL:EYE +"CREATURE:GIANT_ELEPHANT_SEAL:BRAIN +!CREATURE:GIANT_ELEPHANT_SEAL:LUNG +"CREATURE:GIANT_ELEPHANT_SEAL:HEART +"CREATURE:GIANT_ELEPHANT_SEAL:LIVER + CREATURE:GIANT_ELEPHANT_SEAL:GUT +$CREATURE:GIANT_ELEPHANT_SEAL:STOMACH +$CREATURE:GIANT_ELEPHANT_SEAL:GIZZARD +%CREATURE:GIANT_ELEPHANT_SEAL:PANCREAS +#CREATURE:GIANT_ELEPHANT_SEAL:SPLEEN +#CREATURE:GIANT_ELEPHANT_SEAL:KIDNEY +CREATURE:HARP_SEAL:MUSCLE +CREATURE:HARP_SEAL:EYE +CREATURE:HARP_SEAL:BRAIN +CREATURE:HARP_SEAL:LUNG +CREATURE:HARP_SEAL:HEART +CREATURE:HARP_SEAL:LIVER +CREATURE:HARP_SEAL:GUT +CREATURE:HARP_SEAL:STOMACH +CREATURE:HARP_SEAL:GIZZARD +CREATURE:HARP_SEAL:PANCREAS +CREATURE:HARP_SEAL:SPLEEN +CREATURE:HARP_SEAL:KIDNEY +CREATURE:HARP_SEAL_MAN:MUSCLE +CREATURE:HARP_SEAL_MAN:EYE +CREATURE:HARP_SEAL_MAN:BRAIN +CREATURE:HARP_SEAL_MAN:LUNG +CREATURE:HARP_SEAL_MAN:HEART +CREATURE:HARP_SEAL_MAN:LIVER +CREATURE:HARP_SEAL_MAN:GUT +CREATURE:HARP_SEAL_MAN:STOMACH +CREATURE:HARP_SEAL_MAN:GIZZARD +CREATURE:HARP_SEAL_MAN:PANCREAS +CREATURE:HARP_SEAL_MAN:SPLEEN +CREATURE:HARP_SEAL_MAN:KIDNEY +CREATURE:GIANT_HARP_SEAL:MUSCLE +CREATURE:GIANT_HARP_SEAL:EYE +CREATURE:GIANT_HARP_SEAL:BRAIN +CREATURE:GIANT_HARP_SEAL:LUNG +CREATURE:GIANT_HARP_SEAL:HEART +CREATURE:GIANT_HARP_SEAL:LIVER +CREATURE:GIANT_HARP_SEAL:GUT + CREATURE:GIANT_HARP_SEAL:STOMACH + CREATURE:GIANT_HARP_SEAL:GIZZARD +!CREATURE:GIANT_HARP_SEAL:PANCREAS +CREATURE:GIANT_HARP_SEAL:SPLEEN +CREATURE:GIANT_HARP_SEAL:KIDNEY +CREATURE:NAUTILUS:MUSCLE +CREATURE:NAUTILUS:EYE +CREATURE:NAUTILUS:BRAIN +CREATURE:NAUTILUS:LUNG +CREATURE:NAUTILUS:HEART +CREATURE:NAUTILUS:LIVER +CREATURE:NAUTILUS:GUT +CREATURE:NAUTILUS:STOMACH +CREATURE:NAUTILUS:GIZZARD +CREATURE:NAUTILUS:PANCREAS +CREATURE:NAUTILUS:SPLEEN +CREATURE:NAUTILUS:KIDNEY +CREATURE:NAUTILUS_MAN:MUSCLE +CREATURE:NAUTILUS_MAN:EYE +CREATURE:NAUTILUS_MAN:BRAIN +CREATURE:NAUTILUS_MAN:LUNG +CREATURE:NAUTILUS_MAN:HEART +CREATURE:NAUTILUS_MAN:LIVER +CREATURE:NAUTILUS_MAN:GUT +CREATURE:NAUTILUS_MAN:STOMACH +CREATURE:NAUTILUS_MAN:GIZZARD +CREATURE:NAUTILUS_MAN:PANCREAS +CREATURE:NAUTILUS_MAN:SPLEEN +CREATURE:NAUTILUS_MAN:KIDNEY +CREATURE:GIANT_NAUTILUS:MUSCLE +CREATURE:GIANT_NAUTILUS:EYE +CREATURE:GIANT_NAUTILUS:BRAIN +CREATURE:GIANT_NAUTILUS:LUNG +CREATURE:GIANT_NAUTILUS:HEART +CREATURE:GIANT_NAUTILUS:LIVER +CREATURE:GIANT_NAUTILUS:GUT +CREATURE:GIANT_NAUTILUS:STOMACH +CREATURE:GIANT_NAUTILUS:GIZZARD + CREATURE:GIANT_NAUTILUS:PANCREAS +CREATURE:GIANT_NAUTILUS:SPLEEN +CREATURE:GIANT_NAUTILUS:KIDNEY +CREATURE:FOXSQUIRREL:MUSCLE +CREATURE:FOXSQUIRREL:EYE +CREATURE:FOXSQUIRREL:BRAIN +CREATURE:FOXSQUIRREL:LUNG +CREATURE:FOXSQUIRREL:HEART +CREATURE:FOXSQUIRREL:LIVER +CREATURE:FOXSQUIRREL:GUT +CREATURE:FOXSQUIRREL:STOMACH +CREATURE:FOXSQUIRREL:GIZZARD +CREATURE:FOXSQUIRREL:PANCREAS +CREATURE:FOXSQUIRREL:SPLEEN +CREATURE:FOXSQUIRREL:KIDNEY +CREATURE:MOGHOPPER:MUSCLE +CREATURE:MOGHOPPER:EYE +CREATURE:MOGHOPPER:BRAIN +CREATURE:MOGHOPPER:LUNG +CREATURE:MOGHOPPER:HEART +CREATURE:MOGHOPPER:LIVER +CREATURE:MOGHOPPER:GUT +CREATURE:MOGHOPPER:STOMACH +CREATURE:MOGHOPPER:GIZZARD +CREATURE:MOGHOPPER:PANCREAS +CREATURE:MOGHOPPER:SPLEEN +CREATURE:MOGHOPPER:KIDNEY +CREATURE:RAT_DEMON:MUSCLE +CREATURE:RAT_DEMON:EYE +CREATURE:RAT_DEMON:BRAIN +CREATURE:RAT_DEMON:LUNG +CREATURE:RAT_DEMON:HEART +CREATURE:RAT_DEMON:LIVER +CREATURE:RAT_DEMON:GUT +CREATURE:RAT_DEMON:STOMACH +CREATURE:RAT_DEMON:GIZZARD +CREATURE:RAT_DEMON:PANCREAS +CREATURE:RAT_DEMON:SPLEEN +CREATURE:RAT_DEMON:KIDNEY +CREATURE:WAMBLER_FLUFFY:EYE +'CREATURE:LIZARD_RHINO_TWO_LEGGED:MUSCLE +$CREATURE:LIZARD_RHINO_TWO_LEGGED:EYE +&CREATURE:LIZARD_RHINO_TWO_LEGGED:BRAIN +%CREATURE:LIZARD_RHINO_TWO_LEGGED:LUNG +&CREATURE:LIZARD_RHINO_TWO_LEGGED:HEART +&CREATURE:LIZARD_RHINO_TWO_LEGGED:LIVER +$CREATURE:LIZARD_RHINO_TWO_LEGGED:GUT +(CREATURE:LIZARD_RHINO_TWO_LEGGED:STOMACH +(CREATURE:LIZARD_RHINO_TWO_LEGGED:GIZZARD +)CREATURE:LIZARD_RHINO_TWO_LEGGED:PANCREAS +'CREATURE:LIZARD_RHINO_TWO_LEGGED:SPLEEN +'CREATURE:LIZARD_RHINO_TWO_LEGGED:KIDNEY +CREATURE:WORM_KNUCKLE:MUSCLE +CREATURE:WORM_KNUCKLE:EYE +CREATURE:WORM_KNUCKLE:BRAIN +CREATURE:WORM_KNUCKLE:LUNG +CREATURE:WORM_KNUCKLE:HEART +CREATURE:WORM_KNUCKLE:LIVER +CREATURE:WORM_KNUCKLE:GUT +CREATURE:WORM_KNUCKLE:STOMACH +CREATURE:WORM_KNUCKLE:GIZZARD +CREATURE:WORM_KNUCKLE:PANCREAS +CREATURE:WORM_KNUCKLE:SPLEEN +CREATURE:WORM_KNUCKLE:KIDNEY +CREATURE:SPIDER_PHANTOM:MUSCLE +CREATURE:SPIDER_PHANTOM:EYE +CREATURE:SPIDER_PHANTOM:BRAIN +CREATURE:SPIDER_PHANTOM:LUNG +CREATURE:SPIDER_PHANTOM:HEART +CREATURE:SPIDER_PHANTOM:LIVER +CREATURE:SPIDER_PHANTOM:GUT +CREATURE:SPIDER_PHANTOM:STOMACH +CREATURE:SPIDER_PHANTOM:GIZZARD + CREATURE:SPIDER_PHANTOM:PANCREAS +CREATURE:SPIDER_PHANTOM:SPLEEN +CREATURE:SPIDER_PHANTOM:KIDNEY +CREATURE:FLY_ACORN:MUSCLE +CREATURE:FLY_ACORN:EYE +CREATURE:FLY_ACORN:BRAIN +CREATURE:FLY_ACORN:LUNG +CREATURE:FLY_ACORN:HEART +CREATURE:FLY_ACORN:LIVER +CREATURE:FLY_ACORN:GUT +CREATURE:FLY_ACORN:STOMACH +CREATURE:FLY_ACORN:GIZZARD +CREATURE:FLY_ACORN:PANCREAS +CREATURE:FLY_ACORN:SPLEEN +CREATURE:FLY_ACORN:KIDNEY +CREATURE:GNAT_BLOOD:MUSCLE +CREATURE:GNAT_BLOOD:EYE +CREATURE:GNAT_BLOOD:BRAIN +CREATURE:GNAT_BLOOD:LUNG +CREATURE:GNAT_BLOOD:HEART +CREATURE:GNAT_BLOOD:LIVER +CREATURE:GNAT_BLOOD:GUT +CREATURE:GNAT_BLOOD:STOMACH +CREATURE:GNAT_BLOOD:GIZZARD +CREATURE:GNAT_BLOOD:PANCREAS +CREATURE:GNAT_BLOOD:SPLEEN +CREATURE:GNAT_BLOOD:KIDNEY +CREATURE:LIZARD:MUSCLE +CREATURE:LIZARD:EYE +CREATURE:LIZARD:BRAIN +CREATURE:LIZARD:LUNG +CREATURE:LIZARD:HEART +CREATURE:LIZARD:LIVER +CREATURE:LIZARD:GUT +CREATURE:LIZARD:STOMACH +CREATURE:LIZARD:GIZZARD +CREATURE:LIZARD:PANCREAS +CREATURE:LIZARD:SPLEEN +CREATURE:LIZARD:KIDNEY +CREATURE:LIZARD_MAN:MUSCLE +CREATURE:LIZARD_MAN:EYE +CREATURE:LIZARD_MAN:BRAIN +CREATURE:LIZARD_MAN:LUNG +CREATURE:LIZARD_MAN:HEART +CREATURE:LIZARD_MAN:LIVER +CREATURE:LIZARD_MAN:GUT +CREATURE:LIZARD_MAN:STOMACH +CREATURE:LIZARD_MAN:GIZZARD +CREATURE:LIZARD_MAN:PANCREAS +CREATURE:LIZARD_MAN:SPLEEN +CREATURE:LIZARD_MAN:KIDNEY +CREATURE:GIANT_LIZARD:MUSCLE +CREATURE:GIANT_LIZARD:EYE +CREATURE:GIANT_LIZARD:BRAIN +CREATURE:GIANT_LIZARD:LUNG +CREATURE:GIANT_LIZARD:HEART +CREATURE:GIANT_LIZARD:LIVER +CREATURE:GIANT_LIZARD:GUT +CREATURE:GIANT_LIZARD:STOMACH +CREATURE:GIANT_LIZARD:GIZZARD +CREATURE:GIANT_LIZARD:PANCREAS +CREATURE:GIANT_LIZARD:SPLEEN +CREATURE:GIANT_LIZARD:KIDNEY +CREATURE:SKINK:MUSCLE +CREATURE:SKINK:EYE +CREATURE:SKINK:BRAIN +CREATURE:SKINK:LUNG +CREATURE:SKINK:HEART +CREATURE:SKINK:LIVER +CREATURE:SKINK:GUT +CREATURE:SKINK:STOMACH +CREATURE:SKINK:GIZZARD +CREATURE:SKINK:PANCREAS +CREATURE:SKINK:SPLEEN +CREATURE:SKINK:KIDNEY +CREATURE:SKINK_MAN:MUSCLE +CREATURE:SKINK_MAN:EYE +CREATURE:SKINK_MAN:BRAIN +CREATURE:SKINK_MAN:LUNG +CREATURE:SKINK_MAN:HEART +CREATURE:SKINK_MAN:LIVER +CREATURE:SKINK_MAN:GUT +CREATURE:SKINK_MAN:STOMACH +CREATURE:SKINK_MAN:GIZZARD +CREATURE:SKINK_MAN:PANCREAS +CREATURE:SKINK_MAN:SPLEEN +CREATURE:SKINK_MAN:KIDNEY +CREATURE:GIANT_SKINK:MUSCLE +CREATURE:GIANT_SKINK:EYE +CREATURE:GIANT_SKINK:BRAIN +CREATURE:GIANT_SKINK:LUNG +CREATURE:GIANT_SKINK:HEART +CREATURE:GIANT_SKINK:LIVER +CREATURE:GIANT_SKINK:GUT +CREATURE:GIANT_SKINK:STOMACH +CREATURE:GIANT_SKINK:GIZZARD +CREATURE:GIANT_SKINK:PANCREAS +CREATURE:GIANT_SKINK:SPLEEN +CREATURE:GIANT_SKINK:KIDNEY +CREATURE:CHAMELEON:MUSCLE +CREATURE:CHAMELEON:EYE +CREATURE:CHAMELEON:BRAIN +CREATURE:CHAMELEON:LUNG +CREATURE:CHAMELEON:HEART +CREATURE:CHAMELEON:LIVER +CREATURE:CHAMELEON:GUT +CREATURE:CHAMELEON:STOMACH +CREATURE:CHAMELEON:GIZZARD +CREATURE:CHAMELEON:PANCREAS +CREATURE:CHAMELEON:SPLEEN +CREATURE:CHAMELEON:KIDNEY +CREATURE:CHAMELEON_MAN:MUSCLE +CREATURE:CHAMELEON_MAN:EYE +CREATURE:CHAMELEON_MAN:BRAIN +CREATURE:CHAMELEON_MAN:LUNG +CREATURE:CHAMELEON_MAN:HEART +CREATURE:CHAMELEON_MAN:LIVER +CREATURE:CHAMELEON_MAN:GUT +CREATURE:CHAMELEON_MAN:STOMACH +CREATURE:CHAMELEON_MAN:GIZZARD +CREATURE:CHAMELEON_MAN:PANCREAS +CREATURE:CHAMELEON_MAN:SPLEEN +CREATURE:CHAMELEON_MAN:KIDNEY +CREATURE:GIANT_CHAMELEON:MUSCLE +CREATURE:GIANT_CHAMELEON:EYE +CREATURE:GIANT_CHAMELEON:BRAIN +CREATURE:GIANT_CHAMELEON:LUNG +CREATURE:GIANT_CHAMELEON:HEART +CREATURE:GIANT_CHAMELEON:LIVER +CREATURE:GIANT_CHAMELEON:GUT + CREATURE:GIANT_CHAMELEON:STOMACH + CREATURE:GIANT_CHAMELEON:GIZZARD +!CREATURE:GIANT_CHAMELEON:PANCREAS +CREATURE:GIANT_CHAMELEON:SPLEEN +CREATURE:GIANT_CHAMELEON:KIDNEY +CREATURE:ANOLE:MUSCLE +CREATURE:ANOLE:EYE +CREATURE:ANOLE:BRAIN +CREATURE:ANOLE:LUNG +CREATURE:ANOLE:HEART +CREATURE:ANOLE:LIVER +CREATURE:ANOLE:GUT +CREATURE:ANOLE:STOMACH +CREATURE:ANOLE:GIZZARD +CREATURE:ANOLE:PANCREAS +CREATURE:ANOLE:SPLEEN +CREATURE:ANOLE:KIDNEY +CREATURE:ANOLE_MAN:MUSCLE +CREATURE:ANOLE_MAN:EYE +CREATURE:ANOLE_MAN:BRAIN +CREATURE:ANOLE_MAN:LUNG +CREATURE:ANOLE_MAN:HEART +CREATURE:ANOLE_MAN:LIVER +CREATURE:ANOLE_MAN:GUT +CREATURE:ANOLE_MAN:STOMACH +CREATURE:ANOLE_MAN:GIZZARD +CREATURE:ANOLE_MAN:PANCREAS +CREATURE:ANOLE_MAN:SPLEEN +CREATURE:ANOLE_MAN:KIDNEY +CREATURE:GIANT_ANOLE:MUSCLE +CREATURE:GIANT_ANOLE:EYE +CREATURE:GIANT_ANOLE:BRAIN +CREATURE:GIANT_ANOLE:LUNG +CREATURE:GIANT_ANOLE:HEART +CREATURE:GIANT_ANOLE:LIVER +CREATURE:GIANT_ANOLE:GUT +CREATURE:GIANT_ANOLE:STOMACH +CREATURE:GIANT_ANOLE:GIZZARD +CREATURE:GIANT_ANOLE:PANCREAS +CREATURE:GIANT_ANOLE:SPLEEN +CREATURE:GIANT_ANOLE:KIDNEY +CREATURE:IGUANA:MUSCLE +CREATURE:IGUANA:EYE +CREATURE:IGUANA:BRAIN +CREATURE:IGUANA:LUNG +CREATURE:IGUANA:HEART +CREATURE:IGUANA:LIVER +CREATURE:IGUANA:GUT +CREATURE:IGUANA:STOMACH +CREATURE:IGUANA:GIZZARD +CREATURE:IGUANA:PANCREAS +CREATURE:IGUANA:SPLEEN +CREATURE:IGUANA:KIDNEY +CREATURE:IGUANA_MAN:MUSCLE +CREATURE:IGUANA_MAN:EYE +CREATURE:IGUANA_MAN:BRAIN +CREATURE:IGUANA_MAN:LUNG +CREATURE:IGUANA_MAN:HEART +CREATURE:IGUANA_MAN:LIVER +CREATURE:IGUANA_MAN:GUT +CREATURE:IGUANA_MAN:STOMACH +CREATURE:IGUANA_MAN:GIZZARD +CREATURE:IGUANA_MAN:PANCREAS +CREATURE:IGUANA_MAN:SPLEEN +CREATURE:IGUANA_MAN:KIDNEY +CREATURE:GIANT_IGUANA:MUSCLE +CREATURE:GIANT_IGUANA:EYE +CREATURE:GIANT_IGUANA:BRAIN +CREATURE:GIANT_IGUANA:LUNG +CREATURE:GIANT_IGUANA:HEART +CREATURE:GIANT_IGUANA:LIVER +CREATURE:GIANT_IGUANA:GUT +CREATURE:GIANT_IGUANA:STOMACH +CREATURE:GIANT_IGUANA:GIZZARD +CREATURE:GIANT_IGUANA:PANCREAS +CREATURE:GIANT_IGUANA:SPLEEN +CREATURE:GIANT_IGUANA:KIDNEY +CREATURE:RIVER OTTER:MUSCLE +CREATURE:RIVER OTTER:EYE +CREATURE:RIVER OTTER:BRAIN +CREATURE:RIVER OTTER:LUNG +CREATURE:RIVER OTTER:HEART +CREATURE:RIVER OTTER:LIVER +CREATURE:RIVER OTTER:GUT +CREATURE:RIVER OTTER:STOMACH +CREATURE:RIVER OTTER:GIZZARD +CREATURE:RIVER OTTER:PANCREAS +CREATURE:RIVER OTTER:SPLEEN +CREATURE:RIVER OTTER:KIDNEY +CREATURE:SEA OTTER:MUSCLE +CREATURE:SEA OTTER:EYE +CREATURE:SEA OTTER:BRAIN +CREATURE:SEA OTTER:LUNG +CREATURE:SEA OTTER:HEART +CREATURE:SEA OTTER:LIVER +CREATURE:SEA OTTER:GUT +CREATURE:SEA OTTER:STOMACH +CREATURE:SEA OTTER:GIZZARD +CREATURE:SEA OTTER:PANCREAS +CREATURE:SEA OTTER:SPLEEN +CREATURE:SEA OTTER:KIDNEY +CREATURE:OTTER_MAN:MUSCLE +CREATURE:OTTER_MAN:EYE +CREATURE:OTTER_MAN:BRAIN +CREATURE:OTTER_MAN:LUNG +CREATURE:OTTER_MAN:HEART +CREATURE:OTTER_MAN:LIVER +CREATURE:OTTER_MAN:GUT +CREATURE:OTTER_MAN:STOMACH +CREATURE:OTTER_MAN:GIZZARD +CREATURE:OTTER_MAN:PANCREAS +CREATURE:OTTER_MAN:SPLEEN +CREATURE:OTTER_MAN:KIDNEY +CREATURE:GIANT_OTTER:MUSCLE +CREATURE:GIANT_OTTER:EYE +CREATURE:GIANT_OTTER:BRAIN +CREATURE:GIANT_OTTER:LUNG +CREATURE:GIANT_OTTER:HEART +CREATURE:GIANT_OTTER:LIVER +CREATURE:GIANT_OTTER:GUT +CREATURE:GIANT_OTTER:STOMACH +CREATURE:GIANT_OTTER:GIZZARD +CREATURE:GIANT_OTTER:PANCREAS +CREATURE:GIANT_OTTER:SPLEEN +CREATURE:GIANT_OTTER:KIDNEY +CREATURE:SNAPPING TURTLE:MUSCLE +CREATURE:SNAPPING TURTLE:EYE +CREATURE:SNAPPING TURTLE:BRAIN +CREATURE:SNAPPING TURTLE:LUNG +CREATURE:SNAPPING TURTLE:HEART +CREATURE:SNAPPING TURTLE:LIVER +CREATURE:SNAPPING TURTLE:GUT + CREATURE:SNAPPING TURTLE:STOMACH + CREATURE:SNAPPING TURTLE:GIZZARD +!CREATURE:SNAPPING TURTLE:PANCREAS +CREATURE:SNAPPING TURTLE:SPLEEN +CREATURE:SNAPPING TURTLE:KIDNEY +)CREATURE:ALLIGATOR SNAPPING TURTLE:MUSCLE +&CREATURE:ALLIGATOR SNAPPING TURTLE:EYE +(CREATURE:ALLIGATOR SNAPPING TURTLE:BRAIN +'CREATURE:ALLIGATOR SNAPPING TURTLE:LUNG +(CREATURE:ALLIGATOR SNAPPING TURTLE:HEART +(CREATURE:ALLIGATOR SNAPPING TURTLE:LIVER +&CREATURE:ALLIGATOR SNAPPING TURTLE:GUT +*CREATURE:ALLIGATOR SNAPPING TURTLE:STOMACH +*CREATURE:ALLIGATOR SNAPPING TURTLE:GIZZARD ++CREATURE:ALLIGATOR SNAPPING TURTLE:PANCREAS +)CREATURE:ALLIGATOR SNAPPING TURTLE:SPLEEN +)CREATURE:ALLIGATOR SNAPPING TURTLE:KIDNEY +#CREATURE:SNAPPING_TURTLE_MAN:MUSCLE + CREATURE:SNAPPING_TURTLE_MAN:EYE +"CREATURE:SNAPPING_TURTLE_MAN:BRAIN +!CREATURE:SNAPPING_TURTLE_MAN:LUNG +"CREATURE:SNAPPING_TURTLE_MAN:HEART +"CREATURE:SNAPPING_TURTLE_MAN:LIVER + CREATURE:SNAPPING_TURTLE_MAN:GUT +$CREATURE:SNAPPING_TURTLE_MAN:STOMACH +$CREATURE:SNAPPING_TURTLE_MAN:GIZZARD +%CREATURE:SNAPPING_TURTLE_MAN:PANCREAS +#CREATURE:SNAPPING_TURTLE_MAN:SPLEEN +#CREATURE:SNAPPING_TURTLE_MAN:KIDNEY +%CREATURE:GIANT_SNAPPING_TURTLE:MUSCLE +"CREATURE:GIANT_SNAPPING_TURTLE:EYE +$CREATURE:GIANT_SNAPPING_TURTLE:BRAIN +#CREATURE:GIANT_SNAPPING_TURTLE:LUNG +$CREATURE:GIANT_SNAPPING_TURTLE:HEART +$CREATURE:GIANT_SNAPPING_TURTLE:LIVER +"CREATURE:GIANT_SNAPPING_TURTLE:GUT +&CREATURE:GIANT_SNAPPING_TURTLE:STOMACH +&CREATURE:GIANT_SNAPPING_TURTLE:GIZZARD +'CREATURE:GIANT_SNAPPING_TURTLE:PANCREAS +%CREATURE:GIANT_SNAPPING_TURTLE:SPLEEN +%CREATURE:GIANT_SNAPPING_TURTLE:KIDNEY +CREATURE:BEAVER:MUSCLE +CREATURE:BEAVER:EYE +CREATURE:BEAVER:BRAIN +CREATURE:BEAVER:LUNG +CREATURE:BEAVER:HEART +CREATURE:BEAVER:LIVER +CREATURE:BEAVER:GUT +CREATURE:BEAVER:STOMACH +CREATURE:BEAVER:GIZZARD +CREATURE:BEAVER:PANCREAS +CREATURE:BEAVER:SPLEEN +CREATURE:BEAVER:KIDNEY +CREATURE:BEAVER_MAN:MUSCLE +CREATURE:BEAVER_MAN:EYE +CREATURE:BEAVER_MAN:BRAIN +CREATURE:BEAVER_MAN:LUNG +CREATURE:BEAVER_MAN:HEART +CREATURE:BEAVER_MAN:LIVER +CREATURE:BEAVER_MAN:GUT +CREATURE:BEAVER_MAN:STOMACH +CREATURE:BEAVER_MAN:GIZZARD +CREATURE:BEAVER_MAN:PANCREAS +CREATURE:BEAVER_MAN:SPLEEN +CREATURE:BEAVER_MAN:KIDNEY +CREATURE:GIANT_BEAVER:MUSCLE +CREATURE:GIANT_BEAVER:EYE +CREATURE:GIANT_BEAVER:BRAIN +CREATURE:GIANT_BEAVER:LUNG +CREATURE:GIANT_BEAVER:HEART +CREATURE:GIANT_BEAVER:LIVER +CREATURE:GIANT_BEAVER:GUT +CREATURE:GIANT_BEAVER:STOMACH +CREATURE:GIANT_BEAVER:GIZZARD +CREATURE:GIANT_BEAVER:PANCREAS +CREATURE:GIANT_BEAVER:SPLEEN +CREATURE:GIANT_BEAVER:KIDNEY +CREATURE:LEECH:MUSCLE +CREATURE:LEECH:EYE +CREATURE:LEECH:BRAIN +CREATURE:LEECH:LUNG +CREATURE:LEECH:HEART +CREATURE:LEECH:LIVER +CREATURE:LEECH:GUT +CREATURE:LEECH:STOMACH +CREATURE:LEECH:GIZZARD +CREATURE:LEECH:PANCREAS +CREATURE:LEECH:SPLEEN +CREATURE:LEECH:KIDNEY +CREATURE:LEECH_MAN:MUSCLE +CREATURE:LEECH_MAN:EYE +CREATURE:LEECH_MAN:BRAIN +CREATURE:LEECH_MAN:LUNG +CREATURE:LEECH_MAN:HEART +CREATURE:LEECH_MAN:LIVER +CREATURE:LEECH_MAN:GUT +CREATURE:LEECH_MAN:STOMACH +CREATURE:LEECH_MAN:GIZZARD +CREATURE:LEECH_MAN:PANCREAS +CREATURE:LEECH_MAN:SPLEEN +CREATURE:LEECH_MAN:KIDNEY +CREATURE:GIANT_LEECH:MUSCLE +CREATURE:GIANT_LEECH:EYE +CREATURE:GIANT_LEECH:BRAIN +CREATURE:GIANT_LEECH:LUNG +CREATURE:GIANT_LEECH:HEART +CREATURE:GIANT_LEECH:LIVER +CREATURE:GIANT_LEECH:GUT +CREATURE:GIANT_LEECH:STOMACH +CREATURE:GIANT_LEECH:GIZZARD +CREATURE:GIANT_LEECH:PANCREAS +CREATURE:GIANT_LEECH:SPLEEN +CREATURE:GIANT_LEECH:KIDNEY +CREATURE:AXOLOTL:MUSCLE +CREATURE:AXOLOTL:EYE +CREATURE:AXOLOTL:BRAIN +CREATURE:AXOLOTL:LUNG +CREATURE:AXOLOTL:HEART +CREATURE:AXOLOTL:LIVER +CREATURE:AXOLOTL:GUT +CREATURE:AXOLOTL:STOMACH +CREATURE:AXOLOTL:GIZZARD +CREATURE:AXOLOTL:PANCREAS +CREATURE:AXOLOTL:SPLEEN +CREATURE:AXOLOTL:KIDNEY +CREATURE:AXOLOTL_MAN:MUSCLE +CREATURE:AXOLOTL_MAN:EYE +CREATURE:AXOLOTL_MAN:BRAIN +CREATURE:AXOLOTL_MAN:LUNG +CREATURE:AXOLOTL_MAN:HEART +CREATURE:AXOLOTL_MAN:LIVER +CREATURE:AXOLOTL_MAN:GUT +CREATURE:AXOLOTL_MAN:STOMACH +CREATURE:AXOLOTL_MAN:GIZZARD +CREATURE:AXOLOTL_MAN:PANCREAS +CREATURE:AXOLOTL_MAN:SPLEEN +CREATURE:AXOLOTL_MAN:KIDNEY +CREATURE:GIANT_AXOLOTL:MUSCLE +CREATURE:GIANT_AXOLOTL:EYE +CREATURE:GIANT_AXOLOTL:BRAIN +CREATURE:GIANT_AXOLOTL:LUNG +CREATURE:GIANT_AXOLOTL:HEART +CREATURE:GIANT_AXOLOTL:LIVER +CREATURE:GIANT_AXOLOTL:GUT +CREATURE:GIANT_AXOLOTL:STOMACH +CREATURE:GIANT_AXOLOTL:GIZZARD +CREATURE:GIANT_AXOLOTL:PANCREAS +CREATURE:GIANT_AXOLOTL:SPLEEN +CREATURE:GIANT_AXOLOTL:KIDNEY +CREATURE:MINK:MUSCLE +CREATURE:MINK:EYE +CREATURE:MINK:BRAIN +CREATURE:MINK:LUNG +CREATURE:MINK:HEART +CREATURE:MINK:LIVER +CREATURE:MINK:GUT +CREATURE:MINK:STOMACH +CREATURE:MINK:GIZZARD +CREATURE:MINK:PANCREAS +CREATURE:MINK:SPLEEN +CREATURE:MINK:KIDNEY +CREATURE:MINK_MAN:MUSCLE +CREATURE:MINK_MAN:EYE +CREATURE:MINK_MAN:BRAIN +CREATURE:MINK_MAN:LUNG +CREATURE:MINK_MAN:HEART +CREATURE:MINK_MAN:LIVER +CREATURE:MINK_MAN:GUT +CREATURE:MINK_MAN:STOMACH +CREATURE:MINK_MAN:GIZZARD +CREATURE:MINK_MAN:PANCREAS +CREATURE:MINK_MAN:SPLEEN +CREATURE:MINK_MAN:KIDNEY +CREATURE:GIANT_MINK:MUSCLE +CREATURE:GIANT_MINK:EYE +CREATURE:GIANT_MINK:BRAIN +CREATURE:GIANT_MINK:LUNG +CREATURE:GIANT_MINK:HEART +CREATURE:GIANT_MINK:LIVER +CREATURE:GIANT_MINK:GUT +CREATURE:GIANT_MINK:STOMACH +CREATURE:GIANT_MINK:GIZZARD +CREATURE:GIANT_MINK:PANCREAS +CREATURE:GIANT_MINK:SPLEEN +CREATURE:GIANT_MINK:KIDNEY +CREATURE:POND_TURTLE:MUSCLE +CREATURE:POND_TURTLE:EYE +CREATURE:POND_TURTLE:BRAIN +CREATURE:POND_TURTLE:LUNG +CREATURE:POND_TURTLE:HEART +CREATURE:POND_TURTLE:LIVER +CREATURE:POND_TURTLE:GUT +CREATURE:POND_TURTLE:STOMACH +CREATURE:POND_TURTLE:GIZZARD +CREATURE:POND_TURTLE:PANCREAS +CREATURE:POND_TURTLE:SPLEEN +CREATURE:POND_TURTLE:KIDNEY +CREATURE:POND_TURTLE_MAN:MUSCLE +CREATURE:POND_TURTLE_MAN:EYE +CREATURE:POND_TURTLE_MAN:BRAIN +CREATURE:POND_TURTLE_MAN:LUNG +CREATURE:POND_TURTLE_MAN:HEART +CREATURE:POND_TURTLE_MAN:LIVER +CREATURE:POND_TURTLE_MAN:GUT + CREATURE:POND_TURTLE_MAN:STOMACH + CREATURE:POND_TURTLE_MAN:GIZZARD +!CREATURE:POND_TURTLE_MAN:PANCREAS +CREATURE:POND_TURTLE_MAN:SPLEEN +CREATURE:POND_TURTLE_MAN:KIDNEY +!CREATURE:GIANT_POND_TURTLE:MUSCLE +CREATURE:GIANT_POND_TURTLE:EYE + CREATURE:GIANT_POND_TURTLE:BRAIN +CREATURE:GIANT_POND_TURTLE:LUNG + CREATURE:GIANT_POND_TURTLE:HEART + CREATURE:GIANT_POND_TURTLE:LIVER +CREATURE:GIANT_POND_TURTLE:GUT +"CREATURE:GIANT_POND_TURTLE:STOMACH +"CREATURE:GIANT_POND_TURTLE:GIZZARD +#CREATURE:GIANT_POND_TURTLE:PANCREAS +!CREATURE:GIANT_POND_TURTLE:SPLEEN +!CREATURE:GIANT_POND_TURTLE:KIDNEY +CREATURE:RAT:MUSCLE +CREATURE:RAT:EYE +CREATURE:RAT:BRAIN +CREATURE:RAT:LUNG +CREATURE:RAT:HEART +CREATURE:RAT:LIVER +CREATURE:RAT:GUT +CREATURE:RAT:STOMACH +CREATURE:RAT:GIZZARD +CREATURE:RAT:PANCREAS +CREATURE:RAT:SPLEEN +CREATURE:RAT:KIDNEY +CREATURE:RAT_MAN:MUSCLE +CREATURE:RAT_MAN:EYE +CREATURE:RAT_MAN:BRAIN +CREATURE:RAT_MAN:LUNG +CREATURE:RAT_MAN:HEART +CREATURE:RAT_MAN:LIVER +CREATURE:RAT_MAN:GUT +CREATURE:RAT_MAN:STOMACH +CREATURE:RAT_MAN:GIZZARD +CREATURE:RAT_MAN:PANCREAS +CREATURE:RAT_MAN:SPLEEN +CREATURE:RAT_MAN:KIDNEY +CREATURE:SQUIRREL_GRAY:MUSCLE +CREATURE:SQUIRREL_GRAY:EYE +CREATURE:SQUIRREL_GRAY:BRAIN +CREATURE:SQUIRREL_GRAY:LUNG +CREATURE:SQUIRREL_GRAY:HEART +CREATURE:SQUIRREL_GRAY:LIVER +CREATURE:SQUIRREL_GRAY:GUT +CREATURE:SQUIRREL_GRAY:STOMACH +CREATURE:SQUIRREL_GRAY:GIZZARD +CREATURE:SQUIRREL_GRAY:PANCREAS +CREATURE:SQUIRREL_GRAY:SPLEEN +CREATURE:SQUIRREL_GRAY:KIDNEY +!CREATURE:SQUIRREL_GRAY_MAN:MUSCLE +CREATURE:SQUIRREL_GRAY_MAN:EYE + CREATURE:SQUIRREL_GRAY_MAN:BRAIN +CREATURE:SQUIRREL_GRAY_MAN:LUNG + CREATURE:SQUIRREL_GRAY_MAN:HEART + CREATURE:SQUIRREL_GRAY_MAN:LIVER +CREATURE:SQUIRREL_GRAY_MAN:GUT +"CREATURE:SQUIRREL_GRAY_MAN:STOMACH +"CREATURE:SQUIRREL_GRAY_MAN:GIZZARD +#CREATURE:SQUIRREL_GRAY_MAN:PANCREAS +!CREATURE:SQUIRREL_GRAY_MAN:SPLEEN +!CREATURE:SQUIRREL_GRAY_MAN:KIDNEY +#CREATURE:GIANT_SQUIRREL_GRAY:MUSCLE + CREATURE:GIANT_SQUIRREL_GRAY:EYE +"CREATURE:GIANT_SQUIRREL_GRAY:BRAIN +!CREATURE:GIANT_SQUIRREL_GRAY:LUNG +"CREATURE:GIANT_SQUIRREL_GRAY:HEART +"CREATURE:GIANT_SQUIRREL_GRAY:LIVER + CREATURE:GIANT_SQUIRREL_GRAY:GUT +$CREATURE:GIANT_SQUIRREL_GRAY:STOMACH +$CREATURE:GIANT_SQUIRREL_GRAY:GIZZARD +%CREATURE:GIANT_SQUIRREL_GRAY:PANCREAS +#CREATURE:GIANT_SQUIRREL_GRAY:SPLEEN +#CREATURE:GIANT_SQUIRREL_GRAY:KIDNEY +CREATURE:SQUIRREL_RED:MUSCLE +CREATURE:SQUIRREL_RED:EYE +CREATURE:SQUIRREL_RED:BRAIN +CREATURE:SQUIRREL_RED:LUNG +CREATURE:SQUIRREL_RED:HEART +CREATURE:SQUIRREL_RED:LIVER +CREATURE:SQUIRREL_RED:GUT +CREATURE:SQUIRREL_RED:STOMACH +CREATURE:SQUIRREL_RED:GIZZARD +CREATURE:SQUIRREL_RED:PANCREAS +CREATURE:SQUIRREL_RED:SPLEEN +CREATURE:SQUIRREL_RED:KIDNEY + CREATURE:SQUIRREL_RED_MAN:MUSCLE +CREATURE:SQUIRREL_RED_MAN:EYE +CREATURE:SQUIRREL_RED_MAN:BRAIN +CREATURE:SQUIRREL_RED_MAN:LUNG +CREATURE:SQUIRREL_RED_MAN:HEART +CREATURE:SQUIRREL_RED_MAN:LIVER +CREATURE:SQUIRREL_RED_MAN:GUT +!CREATURE:SQUIRREL_RED_MAN:STOMACH +!CREATURE:SQUIRREL_RED_MAN:GIZZARD +"CREATURE:SQUIRREL_RED_MAN:PANCREAS + CREATURE:SQUIRREL_RED_MAN:SPLEEN + CREATURE:SQUIRREL_RED_MAN:KIDNEY +"CREATURE:GIANT_SQUIRREL_RED:MUSCLE +CREATURE:GIANT_SQUIRREL_RED:EYE +!CREATURE:GIANT_SQUIRREL_RED:BRAIN + CREATURE:GIANT_SQUIRREL_RED:LUNG +!CREATURE:GIANT_SQUIRREL_RED:HEART +!CREATURE:GIANT_SQUIRREL_RED:LIVER +CREATURE:GIANT_SQUIRREL_RED:GUT +#CREATURE:GIANT_SQUIRREL_RED:STOMACH +#CREATURE:GIANT_SQUIRREL_RED:GIZZARD +$CREATURE:GIANT_SQUIRREL_RED:PANCREAS +"CREATURE:GIANT_SQUIRREL_RED:SPLEEN +"CREATURE:GIANT_SQUIRREL_RED:KIDNEY +CREATURE:CHIPMUNK:MUSCLE +CREATURE:CHIPMUNK:EYE +CREATURE:CHIPMUNK:BRAIN +CREATURE:CHIPMUNK:LUNG +CREATURE:CHIPMUNK:HEART +CREATURE:CHIPMUNK:LIVER +CREATURE:CHIPMUNK:GUT +CREATURE:CHIPMUNK:STOMACH +CREATURE:CHIPMUNK:GIZZARD +CREATURE:CHIPMUNK:PANCREAS +CREATURE:CHIPMUNK:SPLEEN +CREATURE:CHIPMUNK:KIDNEY +CREATURE:CHIPMUNK_MAN:MUSCLE +CREATURE:CHIPMUNK_MAN:EYE +CREATURE:CHIPMUNK_MAN:BRAIN +CREATURE:CHIPMUNK_MAN:LUNG +CREATURE:CHIPMUNK_MAN:HEART +CREATURE:CHIPMUNK_MAN:LIVER +CREATURE:CHIPMUNK_MAN:GUT +CREATURE:CHIPMUNK_MAN:STOMACH +CREATURE:CHIPMUNK_MAN:GIZZARD +CREATURE:CHIPMUNK_MAN:PANCREAS +CREATURE:CHIPMUNK_MAN:SPLEEN +CREATURE:CHIPMUNK_MAN:KIDNEY +CREATURE:GIANT_CHIPMUNK:MUSCLE +CREATURE:GIANT_CHIPMUNK:EYE +CREATURE:GIANT_CHIPMUNK:BRAIN +CREATURE:GIANT_CHIPMUNK:LUNG +CREATURE:GIANT_CHIPMUNK:HEART +CREATURE:GIANT_CHIPMUNK:LIVER +CREATURE:GIANT_CHIPMUNK:GUT +CREATURE:GIANT_CHIPMUNK:STOMACH +CREATURE:GIANT_CHIPMUNK:GIZZARD + CREATURE:GIANT_CHIPMUNK:PANCREAS +CREATURE:GIANT_CHIPMUNK:SPLEEN +CREATURE:GIANT_CHIPMUNK:KIDNEY +CREATURE:HAMSTER:MUSCLE +CREATURE:HAMSTER:EYE +CREATURE:HAMSTER:BRAIN +CREATURE:HAMSTER:LUNG +CREATURE:HAMSTER:HEART +CREATURE:HAMSTER:LIVER +CREATURE:HAMSTER:GUT +CREATURE:HAMSTER:STOMACH +CREATURE:HAMSTER:GIZZARD +CREATURE:HAMSTER:PANCREAS +CREATURE:HAMSTER:SPLEEN +CREATURE:HAMSTER:KIDNEY +CREATURE:HAMSTER_MAN:MUSCLE +CREATURE:HAMSTER_MAN:EYE +CREATURE:HAMSTER_MAN:BRAIN +CREATURE:HAMSTER_MAN:LUNG +CREATURE:HAMSTER_MAN:HEART +CREATURE:HAMSTER_MAN:LIVER +CREATURE:HAMSTER_MAN:GUT +CREATURE:HAMSTER_MAN:STOMACH +CREATURE:HAMSTER_MAN:GIZZARD +CREATURE:HAMSTER_MAN:PANCREAS +CREATURE:HAMSTER_MAN:SPLEEN +CREATURE:HAMSTER_MAN:KIDNEY +CREATURE:GIANT_HAMSTER:MUSCLE +CREATURE:GIANT_HAMSTER:EYE +CREATURE:GIANT_HAMSTER:BRAIN +CREATURE:GIANT_HAMSTER:LUNG +CREATURE:GIANT_HAMSTER:HEART +CREATURE:GIANT_HAMSTER:LIVER +CREATURE:GIANT_HAMSTER:GUT +CREATURE:GIANT_HAMSTER:STOMACH +CREATURE:GIANT_HAMSTER:GIZZARD +CREATURE:GIANT_HAMSTER:PANCREAS +CREATURE:GIANT_HAMSTER:SPLEEN +CREATURE:GIANT_HAMSTER:KIDNEY +CREATURE:HEDGEHOG:MUSCLE +CREATURE:HEDGEHOG:EYE +CREATURE:HEDGEHOG:BRAIN +CREATURE:HEDGEHOG:LUNG +CREATURE:HEDGEHOG:HEART +CREATURE:HEDGEHOG:LIVER +CREATURE:HEDGEHOG:GUT +CREATURE:HEDGEHOG:STOMACH +CREATURE:HEDGEHOG:GIZZARD +CREATURE:HEDGEHOG:PANCREAS +CREATURE:HEDGEHOG:SPLEEN +CREATURE:HEDGEHOG:KIDNEY +CREATURE:HEDGEHOG_MAN:MUSCLE +CREATURE:HEDGEHOG_MAN:EYE +CREATURE:HEDGEHOG_MAN:BRAIN +CREATURE:HEDGEHOG_MAN:LUNG +CREATURE:HEDGEHOG_MAN:HEART +CREATURE:HEDGEHOG_MAN:LIVER +CREATURE:HEDGEHOG_MAN:GUT +CREATURE:HEDGEHOG_MAN:STOMACH +CREATURE:HEDGEHOG_MAN:GIZZARD +CREATURE:HEDGEHOG_MAN:PANCREAS +CREATURE:HEDGEHOG_MAN:SPLEEN +CREATURE:HEDGEHOG_MAN:KIDNEY +CREATURE:GIANT_HEDGEHOG:MUSCLE +CREATURE:GIANT_HEDGEHOG:EYE +CREATURE:GIANT_HEDGEHOG:BRAIN +CREATURE:GIANT_HEDGEHOG:LUNG +CREATURE:GIANT_HEDGEHOG:HEART +CREATURE:GIANT_HEDGEHOG:LIVER +CREATURE:GIANT_HEDGEHOG:GUT +CREATURE:GIANT_HEDGEHOG:STOMACH +CREATURE:GIANT_HEDGEHOG:GIZZARD + CREATURE:GIANT_HEDGEHOG:PANCREAS +CREATURE:GIANT_HEDGEHOG:SPLEEN +CREATURE:GIANT_HEDGEHOG:KIDNEY +CREATURE:SQUIRREL_FLYING:MUSCLE +CREATURE:SQUIRREL_FLYING:EYE +CREATURE:SQUIRREL_FLYING:BRAIN +CREATURE:SQUIRREL_FLYING:LUNG +CREATURE:SQUIRREL_FLYING:HEART +CREATURE:SQUIRREL_FLYING:LIVER +CREATURE:SQUIRREL_FLYING:GUT + CREATURE:SQUIRREL_FLYING:STOMACH + CREATURE:SQUIRREL_FLYING:GIZZARD +!CREATURE:SQUIRREL_FLYING:PANCREAS +CREATURE:SQUIRREL_FLYING:SPLEEN +CREATURE:SQUIRREL_FLYING:KIDNEY +#CREATURE:FLYING_SQUIRREL_MAN:MUSCLE + CREATURE:FLYING_SQUIRREL_MAN:EYE +"CREATURE:FLYING_SQUIRREL_MAN:BRAIN +!CREATURE:FLYING_SQUIRREL_MAN:LUNG +"CREATURE:FLYING_SQUIRREL_MAN:HEART +"CREATURE:FLYING_SQUIRREL_MAN:LIVER + CREATURE:FLYING_SQUIRREL_MAN:GUT +$CREATURE:FLYING_SQUIRREL_MAN:STOMACH +$CREATURE:FLYING_SQUIRREL_MAN:GIZZARD +%CREATURE:FLYING_SQUIRREL_MAN:PANCREAS +#CREATURE:FLYING_SQUIRREL_MAN:SPLEEN +#CREATURE:FLYING_SQUIRREL_MAN:KIDNEY +%CREATURE:GIANT_FLYING_SQUIRREL:MUSCLE +"CREATURE:GIANT_FLYING_SQUIRREL:EYE +$CREATURE:GIANT_FLYING_SQUIRREL:BRAIN +#CREATURE:GIANT_FLYING_SQUIRREL:LUNG +$CREATURE:GIANT_FLYING_SQUIRREL:HEART +$CREATURE:GIANT_FLYING_SQUIRREL:LIVER +"CREATURE:GIANT_FLYING_SQUIRREL:GUT +&CREATURE:GIANT_FLYING_SQUIRREL:STOMACH +&CREATURE:GIANT_FLYING_SQUIRREL:GIZZARD +'CREATURE:GIANT_FLYING_SQUIRREL:PANCREAS +%CREATURE:GIANT_FLYING_SQUIRREL:SPLEEN +%CREATURE:GIANT_FLYING_SQUIRREL:KIDNEY +CREATURE:MUSSEL:MUSCLE +CREATURE:OYSTER:MUSCLE +CREATURE:FISH_SALMON:MUSCLE +CREATURE:FISH_SALMON:EYE +CREATURE:FISH_SALMON:BRAIN +CREATURE:FISH_SALMON:LUNG +CREATURE:FISH_SALMON:HEART +CREATURE:FISH_SALMON:LIVER +CREATURE:FISH_SALMON:GUT +CREATURE:FISH_SALMON:STOMACH +CREATURE:FISH_SALMON:GIZZARD +CREATURE:FISH_SALMON:PANCREAS +CREATURE:FISH_SALMON:SPLEEN +CREATURE:FISH_SALMON:KIDNEY +CREATURE:FISH_CLOWNFISH:MUSCLE +CREATURE:FISH_CLOWNFISH:EYE +CREATURE:FISH_CLOWNFISH:BRAIN +CREATURE:FISH_CLOWNFISH:LUNG +CREATURE:FISH_CLOWNFISH:HEART +CREATURE:FISH_CLOWNFISH:LIVER +CREATURE:FISH_CLOWNFISH:GUT +CREATURE:FISH_CLOWNFISH:STOMACH +CREATURE:FISH_CLOWNFISH:GIZZARD + CREATURE:FISH_CLOWNFISH:PANCREAS +CREATURE:FISH_CLOWNFISH:SPLEEN +CREATURE:FISH_CLOWNFISH:KIDNEY +CREATURE:FISH_HAGFISH:MUSCLE +CREATURE:FISH_HAGFISH:EYE +CREATURE:FISH_HAGFISH:BRAIN +CREATURE:FISH_HAGFISH:LUNG +CREATURE:FISH_HAGFISH:HEART +CREATURE:FISH_HAGFISH:LIVER +CREATURE:FISH_HAGFISH:GUT +CREATURE:FISH_HAGFISH:STOMACH +CREATURE:FISH_HAGFISH:GIZZARD +CREATURE:FISH_HAGFISH:PANCREAS +CREATURE:FISH_HAGFISH:SPLEEN +CREATURE:FISH_HAGFISH:KIDNEY +"CREATURE:FISH_LAMPREY_BROOK:MUSCLE +CREATURE:FISH_LAMPREY_BROOK:EYE +!CREATURE:FISH_LAMPREY_BROOK:BRAIN + CREATURE:FISH_LAMPREY_BROOK:LUNG +!CREATURE:FISH_LAMPREY_BROOK:HEART +!CREATURE:FISH_LAMPREY_BROOK:LIVER +CREATURE:FISH_LAMPREY_BROOK:GUT +#CREATURE:FISH_LAMPREY_BROOK:STOMACH +#CREATURE:FISH_LAMPREY_BROOK:GIZZARD +$CREATURE:FISH_LAMPREY_BROOK:PANCREAS +"CREATURE:FISH_LAMPREY_BROOK:SPLEEN +"CREATURE:FISH_LAMPREY_BROOK:KIDNEY +CREATURE:FISH_RAY_BAT:MUSCLE +CREATURE:FISH_RAY_BAT:EYE +CREATURE:FISH_RAY_BAT:BRAIN +CREATURE:FISH_RAY_BAT:LUNG +CREATURE:FISH_RAY_BAT:HEART +CREATURE:FISH_RAY_BAT:LIVER +CREATURE:FISH_RAY_BAT:GUT +CREATURE:FISH_RAY_BAT:STOMACH +CREATURE:FISH_RAY_BAT:GIZZARD +CREATURE:FISH_RAY_BAT:PANCREAS +CREATURE:FISH_RAY_BAT:SPLEEN +CREATURE:FISH_RAY_BAT:KIDNEY +"CREATURE:FISH_RAY_THORNBACK:MUSCLE +CREATURE:FISH_RAY_THORNBACK:EYE +!CREATURE:FISH_RAY_THORNBACK:BRAIN + CREATURE:FISH_RAY_THORNBACK:LUNG +!CREATURE:FISH_RAY_THORNBACK:HEART +!CREATURE:FISH_RAY_THORNBACK:LIVER +CREATURE:FISH_RAY_THORNBACK:GUT +#CREATURE:FISH_RAY_THORNBACK:STOMACH +#CREATURE:FISH_RAY_THORNBACK:GIZZARD +$CREATURE:FISH_RAY_THORNBACK:PANCREAS +"CREATURE:FISH_RAY_THORNBACK:SPLEEN +"CREATURE:FISH_RAY_THORNBACK:KIDNEY +$CREATURE:FISH_RATFISH_SPOTTED:MUSCLE +!CREATURE:FISH_RATFISH_SPOTTED:EYE +#CREATURE:FISH_RATFISH_SPOTTED:BRAIN +"CREATURE:FISH_RATFISH_SPOTTED:LUNG +#CREATURE:FISH_RATFISH_SPOTTED:HEART +#CREATURE:FISH_RATFISH_SPOTTED:LIVER +!CREATURE:FISH_RATFISH_SPOTTED:GUT +%CREATURE:FISH_RATFISH_SPOTTED:STOMACH +%CREATURE:FISH_RATFISH_SPOTTED:GIZZARD +&CREATURE:FISH_RATFISH_SPOTTED:PANCREAS +$CREATURE:FISH_RATFISH_SPOTTED:SPLEEN +$CREATURE:FISH_RATFISH_SPOTTED:KIDNEY +CREATURE:FISH_HERRING:MUSCLE +CREATURE:FISH_HERRING:EYE +CREATURE:FISH_HERRING:BRAIN +CREATURE:FISH_HERRING:LUNG +CREATURE:FISH_HERRING:HEART +CREATURE:FISH_HERRING:LIVER +CREATURE:FISH_HERRING:GUT +CREATURE:FISH_HERRING:STOMACH +CREATURE:FISH_HERRING:GIZZARD +CREATURE:FISH_HERRING:PANCREAS +CREATURE:FISH_HERRING:SPLEEN +CREATURE:FISH_HERRING:KIDNEY +CREATURE:FISH_SHAD:MUSCLE +CREATURE:FISH_SHAD:EYE +CREATURE:FISH_SHAD:BRAIN +CREATURE:FISH_SHAD:LUNG +CREATURE:FISH_SHAD:HEART +CREATURE:FISH_SHAD:LIVER +CREATURE:FISH_SHAD:GUT +CREATURE:FISH_SHAD:STOMACH +CREATURE:FISH_SHAD:GIZZARD +CREATURE:FISH_SHAD:PANCREAS +CREATURE:FISH_SHAD:SPLEEN +CREATURE:FISH_SHAD:KIDNEY +CREATURE:FISH_ANCHOVY:MUSCLE +CREATURE:FISH_ANCHOVY:EYE +CREATURE:FISH_ANCHOVY:BRAIN +CREATURE:FISH_ANCHOVY:LUNG +CREATURE:FISH_ANCHOVY:HEART +CREATURE:FISH_ANCHOVY:LIVER +CREATURE:FISH_ANCHOVY:GUT +CREATURE:FISH_ANCHOVY:STOMACH +CREATURE:FISH_ANCHOVY:GIZZARD +CREATURE:FISH_ANCHOVY:PANCREAS +CREATURE:FISH_ANCHOVY:SPLEEN +CREATURE:FISH_ANCHOVY:KIDNEY +$CREATURE:FISH_TROUT_STEELHEAD:MUSCLE +!CREATURE:FISH_TROUT_STEELHEAD:EYE +#CREATURE:FISH_TROUT_STEELHEAD:BRAIN +"CREATURE:FISH_TROUT_STEELHEAD:LUNG +#CREATURE:FISH_TROUT_STEELHEAD:HEART +#CREATURE:FISH_TROUT_STEELHEAD:LIVER +!CREATURE:FISH_TROUT_STEELHEAD:GUT +%CREATURE:FISH_TROUT_STEELHEAD:STOMACH +%CREATURE:FISH_TROUT_STEELHEAD:GIZZARD +&CREATURE:FISH_TROUT_STEELHEAD:PANCREAS +$CREATURE:FISH_TROUT_STEELHEAD:SPLEEN +$CREATURE:FISH_TROUT_STEELHEAD:KIDNEY +CREATURE:FISH_HAKE:MUSCLE +CREATURE:FISH_HAKE:EYE +CREATURE:FISH_HAKE:BRAIN +CREATURE:FISH_HAKE:LUNG +CREATURE:FISH_HAKE:HEART +CREATURE:FISH_HAKE:LIVER +CREATURE:FISH_HAKE:GUT +CREATURE:FISH_HAKE:STOMACH +CREATURE:FISH_HAKE:GIZZARD +CREATURE:FISH_HAKE:PANCREAS +CREATURE:FISH_HAKE:SPLEEN +CREATURE:FISH_HAKE:KIDNEY +CREATURE:FISH_SEAHORSE:MUSCLE +CREATURE:FISH_SEAHORSE:EYE +CREATURE:FISH_SEAHORSE:BRAIN +CREATURE:FISH_SEAHORSE:LUNG +CREATURE:FISH_SEAHORSE:HEART +CREATURE:FISH_SEAHORSE:LIVER +CREATURE:FISH_SEAHORSE:GUT +CREATURE:FISH_SEAHORSE:STOMACH +CREATURE:FISH_SEAHORSE:GIZZARD +CREATURE:FISH_SEAHORSE:PANCREAS +CREATURE:FISH_SEAHORSE:SPLEEN +CREATURE:FISH_SEAHORSE:KIDNEY +CREATURE:FISH_GLASSEYE:MUSCLE +CREATURE:FISH_GLASSEYE:EYE +CREATURE:FISH_GLASSEYE:BRAIN +CREATURE:FISH_GLASSEYE:LUNG +CREATURE:FISH_GLASSEYE:HEART +CREATURE:FISH_GLASSEYE:LIVER +CREATURE:FISH_GLASSEYE:GUT +CREATURE:FISH_GLASSEYE:STOMACH +CREATURE:FISH_GLASSEYE:GIZZARD +CREATURE:FISH_GLASSEYE:PANCREAS +CREATURE:FISH_GLASSEYE:SPLEEN +CREATURE:FISH_GLASSEYE:KIDNEY +)CREATURE:FISH_PUFFER_WHITE_SPOTTED:MUSCLE +&CREATURE:FISH_PUFFER_WHITE_SPOTTED:EYE +(CREATURE:FISH_PUFFER_WHITE_SPOTTED:BRAIN +'CREATURE:FISH_PUFFER_WHITE_SPOTTED:LUNG +(CREATURE:FISH_PUFFER_WHITE_SPOTTED:HEART +(CREATURE:FISH_PUFFER_WHITE_SPOTTED:LIVER +&CREATURE:FISH_PUFFER_WHITE_SPOTTED:GUT +*CREATURE:FISH_PUFFER_WHITE_SPOTTED:STOMACH +*CREATURE:FISH_PUFFER_WHITE_SPOTTED:GIZZARD ++CREATURE:FISH_PUFFER_WHITE_SPOTTED:PANCREAS +)CREATURE:FISH_PUFFER_WHITE_SPOTTED:SPLEEN +)CREATURE:FISH_PUFFER_WHITE_SPOTTED:KIDNEY +CREATURE:FISH_SOLE:MUSCLE +CREATURE:FISH_SOLE:EYE +CREATURE:FISH_SOLE:BRAIN +CREATURE:FISH_SOLE:LUNG +CREATURE:FISH_SOLE:HEART +CREATURE:FISH_SOLE:LIVER +CREATURE:FISH_SOLE:GUT +CREATURE:FISH_SOLE:STOMACH +CREATURE:FISH_SOLE:GIZZARD +CREATURE:FISH_SOLE:PANCREAS +CREATURE:FISH_SOLE:SPLEEN +CREATURE:FISH_SOLE:KIDNEY +CREATURE:FISH_FLOUNDER:MUSCLE +CREATURE:FISH_FLOUNDER:EYE +CREATURE:FISH_FLOUNDER:BRAIN +CREATURE:FISH_FLOUNDER:LUNG +CREATURE:FISH_FLOUNDER:HEART +CREATURE:FISH_FLOUNDER:LIVER +CREATURE:FISH_FLOUNDER:GUT +CREATURE:FISH_FLOUNDER:STOMACH +CREATURE:FISH_FLOUNDER:GIZZARD +CREATURE:FISH_FLOUNDER:PANCREAS +CREATURE:FISH_FLOUNDER:SPLEEN +CREATURE:FISH_FLOUNDER:KIDNEY +CREATURE:FISH_MACKEREL:MUSCLE +CREATURE:FISH_MACKEREL:EYE +CREATURE:FISH_MACKEREL:BRAIN +CREATURE:FISH_MACKEREL:LUNG +CREATURE:FISH_MACKEREL:HEART +CREATURE:FISH_MACKEREL:LIVER +CREATURE:FISH_MACKEREL:GUT +CREATURE:FISH_MACKEREL:STOMACH +CREATURE:FISH_MACKEREL:GIZZARD +CREATURE:FISH_MACKEREL:PANCREAS +CREATURE:FISH_MACKEREL:SPLEEN +CREATURE:FISH_MACKEREL:KIDNEY +$CREATURE:JELLYFISH_SEA_NETTLE:MUSCLE +!CREATURE:JELLYFISH_SEA_NETTLE:EYE +#CREATURE:JELLYFISH_SEA_NETTLE:BRAIN +"CREATURE:JELLYFISH_SEA_NETTLE:LUNG +#CREATURE:JELLYFISH_SEA_NETTLE:HEART +#CREATURE:JELLYFISH_SEA_NETTLE:LIVER +!CREATURE:JELLYFISH_SEA_NETTLE:GUT +%CREATURE:JELLYFISH_SEA_NETTLE:STOMACH +%CREATURE:JELLYFISH_SEA_NETTLE:GIZZARD +&CREATURE:JELLYFISH_SEA_NETTLE:PANCREAS +$CREATURE:JELLYFISH_SEA_NETTLE:SPLEEN +$CREATURE:JELLYFISH_SEA_NETTLE:KIDNEY +CREATURE:SQUID:MUSCLE +CREATURE:SQUID:EYE +CREATURE:SQUID:BRAIN +CREATURE:SQUID:LUNG +CREATURE:SQUID:HEART +CREATURE:SQUID:LIVER +CREATURE:SQUID:GUT +CREATURE:SQUID:STOMACH +CREATURE:SQUID:GIZZARD +CREATURE:SQUID:PANCREAS +CREATURE:SQUID:SPLEEN +CREATURE:SQUID:KIDNEY +CREATURE:SQUID MAN:MUSCLE +CREATURE:SQUID MAN:EYE +CREATURE:SQUID MAN:BRAIN +CREATURE:SQUID MAN:LUNG +CREATURE:SQUID MAN:HEART +CREATURE:SQUID MAN:LIVER +CREATURE:SQUID MAN:GUT +CREATURE:SQUID MAN:STOMACH +CREATURE:SQUID MAN:GIZZARD +CREATURE:SQUID MAN:PANCREAS +CREATURE:SQUID MAN:SPLEEN +CREATURE:SQUID MAN:KIDNEY +CREATURE:GIGANTIC SQUID:MUSCLE +CREATURE:GIGANTIC SQUID:EYE +CREATURE:GIGANTIC SQUID:BRAIN +CREATURE:GIGANTIC SQUID:LUNG +CREATURE:GIGANTIC SQUID:HEART +CREATURE:GIGANTIC SQUID:LIVER +CREATURE:GIGANTIC SQUID:GUT +CREATURE:GIGANTIC SQUID:STOMACH +CREATURE:GIGANTIC SQUID:GIZZARD + CREATURE:GIGANTIC SQUID:PANCREAS +CREATURE:GIGANTIC SQUID:SPLEEN +CREATURE:GIGANTIC SQUID:KIDNEY +CREATURE:FISH_LUNGFISH:MUSCLE +CREATURE:FISH_LUNGFISH:EYE +CREATURE:FISH_LUNGFISH:BRAIN +CREATURE:FISH_LUNGFISH:LUNG +CREATURE:FISH_LUNGFISH:HEART +CREATURE:FISH_LUNGFISH:LIVER +CREATURE:FISH_LUNGFISH:GUT +CREATURE:FISH_LUNGFISH:STOMACH +CREATURE:FISH_LUNGFISH:GIZZARD +CREATURE:FISH_LUNGFISH:PANCREAS +CREATURE:FISH_LUNGFISH:SPLEEN +CREATURE:FISH_LUNGFISH:KIDNEY + CREATURE:FISH_LOACH_CLOWN:MUSCLE +CREATURE:FISH_LOACH_CLOWN:EYE +CREATURE:FISH_LOACH_CLOWN:BRAIN +CREATURE:FISH_LOACH_CLOWN:LUNG +CREATURE:FISH_LOACH_CLOWN:HEART +CREATURE:FISH_LOACH_CLOWN:LIVER +CREATURE:FISH_LOACH_CLOWN:GUT +!CREATURE:FISH_LOACH_CLOWN:STOMACH +!CREATURE:FISH_LOACH_CLOWN:GIZZARD +"CREATURE:FISH_LOACH_CLOWN:PANCREAS + CREATURE:FISH_LOACH_CLOWN:SPLEEN + CREATURE:FISH_LOACH_CLOWN:KIDNEY +#CREATURE:FISH_BULLHEAD_BROWN:MUSCLE + CREATURE:FISH_BULLHEAD_BROWN:EYE +"CREATURE:FISH_BULLHEAD_BROWN:BRAIN +!CREATURE:FISH_BULLHEAD_BROWN:LUNG +"CREATURE:FISH_BULLHEAD_BROWN:HEART +"CREATURE:FISH_BULLHEAD_BROWN:LIVER + CREATURE:FISH_BULLHEAD_BROWN:GUT +$CREATURE:FISH_BULLHEAD_BROWN:STOMACH +$CREATURE:FISH_BULLHEAD_BROWN:GIZZARD +%CREATURE:FISH_BULLHEAD_BROWN:PANCREAS +#CREATURE:FISH_BULLHEAD_BROWN:SPLEEN +#CREATURE:FISH_BULLHEAD_BROWN:KIDNEY +$CREATURE:FISH_BULLHEAD_YELLOW:MUSCLE +!CREATURE:FISH_BULLHEAD_YELLOW:EYE +#CREATURE:FISH_BULLHEAD_YELLOW:BRAIN +"CREATURE:FISH_BULLHEAD_YELLOW:LUNG +#CREATURE:FISH_BULLHEAD_YELLOW:HEART +#CREATURE:FISH_BULLHEAD_YELLOW:LIVER +!CREATURE:FISH_BULLHEAD_YELLOW:GUT +%CREATURE:FISH_BULLHEAD_YELLOW:STOMACH +%CREATURE:FISH_BULLHEAD_YELLOW:GIZZARD +&CREATURE:FISH_BULLHEAD_YELLOW:PANCREAS +$CREATURE:FISH_BULLHEAD_YELLOW:SPLEEN +$CREATURE:FISH_BULLHEAD_YELLOW:KIDNEY +#CREATURE:FISH_BULLHEAD_BLACK:MUSCLE + CREATURE:FISH_BULLHEAD_BLACK:EYE +"CREATURE:FISH_BULLHEAD_BLACK:BRAIN +!CREATURE:FISH_BULLHEAD_BLACK:LUNG +"CREATURE:FISH_BULLHEAD_BLACK:HEART +"CREATURE:FISH_BULLHEAD_BLACK:LIVER + CREATURE:FISH_BULLHEAD_BLACK:GUT +$CREATURE:FISH_BULLHEAD_BLACK:STOMACH +$CREATURE:FISH_BULLHEAD_BLACK:GIZZARD +%CREATURE:FISH_BULLHEAD_BLACK:PANCREAS +#CREATURE:FISH_BULLHEAD_BLACK:SPLEEN +#CREATURE:FISH_BULLHEAD_BLACK:KIDNEY +%CREATURE:FISH_KNIFEFISH_BANDED:MUSCLE +"CREATURE:FISH_KNIFEFISH_BANDED:EYE +$CREATURE:FISH_KNIFEFISH_BANDED:BRAIN +#CREATURE:FISH_KNIFEFISH_BANDED:LUNG +$CREATURE:FISH_KNIFEFISH_BANDED:HEART +$CREATURE:FISH_KNIFEFISH_BANDED:LIVER +"CREATURE:FISH_KNIFEFISH_BANDED:GUT +&CREATURE:FISH_KNIFEFISH_BANDED:STOMACH +&CREATURE:FISH_KNIFEFISH_BANDED:GIZZARD +'CREATURE:FISH_KNIFEFISH_BANDED:PANCREAS +%CREATURE:FISH_KNIFEFISH_BANDED:SPLEEN +%CREATURE:FISH_KNIFEFISH_BANDED:KIDNEY +CREATURE:FISH_CHAR:MUSCLE +CREATURE:FISH_CHAR:EYE +CREATURE:FISH_CHAR:BRAIN +CREATURE:FISH_CHAR:LUNG +CREATURE:FISH_CHAR:HEART +CREATURE:FISH_CHAR:LIVER +CREATURE:FISH_CHAR:GUT +CREATURE:FISH_CHAR:STOMACH +CREATURE:FISH_CHAR:GIZZARD +CREATURE:FISH_CHAR:PANCREAS +CREATURE:FISH_CHAR:SPLEEN +CREATURE:FISH_CHAR:KIDNEY +"CREATURE:FISH_TROUT_RAINBOW:MUSCLE +CREATURE:FISH_TROUT_RAINBOW:EYE +!CREATURE:FISH_TROUT_RAINBOW:BRAIN + CREATURE:FISH_TROUT_RAINBOW:LUNG +!CREATURE:FISH_TROUT_RAINBOW:HEART +!CREATURE:FISH_TROUT_RAINBOW:LIVER +CREATURE:FISH_TROUT_RAINBOW:GUT +#CREATURE:FISH_TROUT_RAINBOW:STOMACH +#CREATURE:FISH_TROUT_RAINBOW:GIZZARD +$CREATURE:FISH_TROUT_RAINBOW:PANCREAS +"CREATURE:FISH_TROUT_RAINBOW:SPLEEN +"CREATURE:FISH_TROUT_RAINBOW:KIDNEY +"CREATURE:FISH_MOLLY_SAILFIN:MUSCLE +CREATURE:FISH_MOLLY_SAILFIN:EYE +!CREATURE:FISH_MOLLY_SAILFIN:BRAIN + CREATURE:FISH_MOLLY_SAILFIN:LUNG +!CREATURE:FISH_MOLLY_SAILFIN:HEART +!CREATURE:FISH_MOLLY_SAILFIN:LIVER +CREATURE:FISH_MOLLY_SAILFIN:GUT +#CREATURE:FISH_MOLLY_SAILFIN:STOMACH +#CREATURE:FISH_MOLLY_SAILFIN:GIZZARD +$CREATURE:FISH_MOLLY_SAILFIN:PANCREAS +"CREATURE:FISH_MOLLY_SAILFIN:SPLEEN +"CREATURE:FISH_MOLLY_SAILFIN:KIDNEY +CREATURE:FISH_GUPPY:MUSCLE +CREATURE:FISH_GUPPY:EYE +CREATURE:FISH_GUPPY:BRAIN +CREATURE:FISH_GUPPY:LUNG +CREATURE:FISH_GUPPY:HEART +CREATURE:FISH_GUPPY:LIVER +CREATURE:FISH_GUPPY:GUT +CREATURE:FISH_GUPPY:STOMACH +CREATURE:FISH_GUPPY:GIZZARD +CREATURE:FISH_GUPPY:PANCREAS +CREATURE:FISH_GUPPY:SPLEEN +CREATURE:FISH_GUPPY:KIDNEY +CREATURE:FISH_PERCH:MUSCLE +CREATURE:FISH_PERCH:EYE +CREATURE:FISH_PERCH:BRAIN +CREATURE:FISH_PERCH:LUNG +CREATURE:FISH_PERCH:HEART +CREATURE:FISH_PERCH:LIVER +CREATURE:FISH_PERCH:GUT +CREATURE:FISH_PERCH:STOMACH +CREATURE:FISH_PERCH:GIZZARD +CREATURE:FISH_PERCH:PANCREAS +CREATURE:FISH_PERCH:SPLEEN +CREATURE:FISH_PERCH:KIDNEY +CREATURE:DWARF:MUSCLE +CREATURE:DWARF:EYE +CREATURE:DWARF:BRAIN +CREATURE:DWARF:LUNG +CREATURE:DWARF:HEART +CREATURE:DWARF:LIVER +CREATURE:DWARF:GUT +CREATURE:DWARF:STOMACH +CREATURE:DWARF:GIZZARD +CREATURE:DWARF:PANCREAS +CREATURE:DWARF:SPLEEN +CREATURE:DWARF:KIDNEY +CREATURE:HUMAN:MUSCLE +CREATURE:HUMAN:EYE +CREATURE:HUMAN:BRAIN +CREATURE:HUMAN:LUNG +CREATURE:HUMAN:HEART +CREATURE:HUMAN:LIVER +CREATURE:HUMAN:GUT +CREATURE:HUMAN:STOMACH +CREATURE:HUMAN:GIZZARD +CREATURE:HUMAN:PANCREAS +CREATURE:HUMAN:SPLEEN +CREATURE:HUMAN:KIDNEY +CREATURE:ELF:MUSCLE +CREATURE:ELF:EYE +CREATURE:ELF:BRAIN +CREATURE:ELF:LUNG +CREATURE:ELF:HEART +CREATURE:ELF:LIVER +CREATURE:ELF:GUT +CREATURE:ELF:STOMACH +CREATURE:ELF:GIZZARD +CREATURE:ELF:PANCREAS +CREATURE:ELF:SPLEEN +CREATURE:ELF:KIDNEY +CREATURE:GOBLIN:MUSCLE +CREATURE:GOBLIN:EYE +CREATURE:GOBLIN:BRAIN +CREATURE:GOBLIN:LUNG +CREATURE:GOBLIN:HEART +CREATURE:GOBLIN:LIVER +CREATURE:GOBLIN:GUT +CREATURE:GOBLIN:STOMACH +CREATURE:GOBLIN:GIZZARD +CREATURE:GOBLIN:PANCREAS +CREATURE:GOBLIN:SPLEEN +CREATURE:GOBLIN:KIDNEY +CREATURE:KOBOLD:MUSCLE +CREATURE:KOBOLD:EYE +CREATURE:KOBOLD:BRAIN +CREATURE:KOBOLD:LUNG +CREATURE:KOBOLD:HEART +CREATURE:KOBOLD:LIVER +CREATURE:KOBOLD:GUT +CREATURE:KOBOLD:STOMACH +CREATURE:KOBOLD:GIZZARD +CREATURE:KOBOLD:PANCREAS +CREATURE:KOBOLD:SPLEEN +CREATURE:KOBOLD:KIDNEY +CREATURE:GREMLIN:MUSCLE +CREATURE:GREMLIN:EYE +CREATURE:GREMLIN:BRAIN +CREATURE:GREMLIN:LUNG +CREATURE:GREMLIN:HEART +CREATURE:GREMLIN:LIVER +CREATURE:GREMLIN:GUT +CREATURE:GREMLIN:STOMACH +CREATURE:GREMLIN:GIZZARD +CREATURE:GREMLIN:PANCREAS +CREATURE:GREMLIN:SPLEEN +CREATURE:GREMLIN:KIDNEY +CREATURE:TROLL:MUSCLE +CREATURE:TROLL:EYE +CREATURE:TROLL:BRAIN +CREATURE:TROLL:LUNG +CREATURE:TROLL:HEART +CREATURE:TROLL:LIVER +CREATURE:TROLL:GUT +CREATURE:TROLL:STOMACH +CREATURE:TROLL:GIZZARD +CREATURE:TROLL:PANCREAS +CREATURE:TROLL:SPLEEN +CREATURE:TROLL:KIDNEY +CREATURE:OGRE:MUSCLE +CREATURE:OGRE:EYE +CREATURE:OGRE:BRAIN +CREATURE:OGRE:LUNG +CREATURE:OGRE:HEART +CREATURE:OGRE:LIVER +CREATURE:OGRE:GUT +CREATURE:OGRE:STOMACH +CREATURE:OGRE:GIZZARD +CREATURE:OGRE:PANCREAS +CREATURE:OGRE:SPLEEN +CREATURE:OGRE:KIDNEY +CREATURE:UNICORN:MUSCLE +CREATURE:UNICORN:EYE +CREATURE:UNICORN:BRAIN +CREATURE:UNICORN:LUNG +CREATURE:UNICORN:HEART +CREATURE:UNICORN:LIVER +CREATURE:UNICORN:GUT +CREATURE:UNICORN:STOMACH +CREATURE:UNICORN:GIZZARD +CREATURE:UNICORN:PANCREAS +CREATURE:UNICORN:SPLEEN +CREATURE:UNICORN:KIDNEY +CREATURE:DRAGON:MUSCLE +CREATURE:DRAGON:EYE +CREATURE:DRAGON:BRAIN +CREATURE:DRAGON:LUNG +CREATURE:DRAGON:HEART +CREATURE:DRAGON:LIVER +CREATURE:DRAGON:GUT +CREATURE:DRAGON:STOMACH +CREATURE:DRAGON:GIZZARD +CREATURE:DRAGON:PANCREAS +CREATURE:DRAGON:SPLEEN +CREATURE:DRAGON:KIDNEY +CREATURE:SATYR:MUSCLE +CREATURE:SATYR:EYE +CREATURE:SATYR:BRAIN +CREATURE:SATYR:LUNG +CREATURE:SATYR:HEART +CREATURE:SATYR:LIVER +CREATURE:SATYR:GUT +CREATURE:SATYR:STOMACH +CREATURE:SATYR:GIZZARD +CREATURE:SATYR:PANCREAS +CREATURE:SATYR:SPLEEN +CREATURE:SATYR:KIDNEY +CREATURE:GIANT:MUSCLE +CREATURE:GIANT:EYE +CREATURE:GIANT:BRAIN +CREATURE:GIANT:LUNG +CREATURE:GIANT:HEART +CREATURE:GIANT:LIVER +CREATURE:GIANT:GUT +CREATURE:GIANT:STOMACH +CREATURE:GIANT:GIZZARD +CREATURE:GIANT:PANCREAS +CREATURE:GIANT:SPLEEN +CREATURE:GIANT:KIDNEY +CREATURE:CYCLOPS:MUSCLE +CREATURE:CYCLOPS:EYE +CREATURE:CYCLOPS:BRAIN +CREATURE:CYCLOPS:LUNG +CREATURE:CYCLOPS:HEART +CREATURE:CYCLOPS:LIVER +CREATURE:CYCLOPS:GUT +CREATURE:CYCLOPS:STOMACH +CREATURE:CYCLOPS:GIZZARD +CREATURE:CYCLOPS:PANCREAS +CREATURE:CYCLOPS:SPLEEN +CREATURE:CYCLOPS:KIDNEY +CREATURE:ETTIN:MUSCLE +CREATURE:ETTIN:EYE +CREATURE:ETTIN:BRAIN +CREATURE:ETTIN:LUNG +CREATURE:ETTIN:HEART +CREATURE:ETTIN:LIVER +CREATURE:ETTIN:GUT +CREATURE:ETTIN:STOMACH +CREATURE:ETTIN:GIZZARD +CREATURE:ETTIN:PANCREAS +CREATURE:ETTIN:SPLEEN +CREATURE:ETTIN:KIDNEY +CREATURE:MINOTAUR:MUSCLE +CREATURE:MINOTAUR:EYE +CREATURE:MINOTAUR:BRAIN +CREATURE:MINOTAUR:LUNG +CREATURE:MINOTAUR:HEART +CREATURE:MINOTAUR:LIVER +CREATURE:MINOTAUR:GUT +CREATURE:MINOTAUR:STOMACH +CREATURE:MINOTAUR:GIZZARD +CREATURE:MINOTAUR:PANCREAS +CREATURE:MINOTAUR:SPLEEN +CREATURE:MINOTAUR:KIDNEY +CREATURE:YETI:MUSCLE +CREATURE:YETI:EYE +CREATURE:YETI:BRAIN +CREATURE:YETI:LUNG +CREATURE:YETI:HEART +CREATURE:YETI:LIVER +CREATURE:YETI:GUT +CREATURE:YETI:STOMACH +CREATURE:YETI:GIZZARD +CREATURE:YETI:PANCREAS +CREATURE:YETI:SPLEEN +CREATURE:YETI:KIDNEY +CREATURE:SASQUATCH:MUSCLE +CREATURE:SASQUATCH:EYE +CREATURE:SASQUATCH:BRAIN +CREATURE:SASQUATCH:LUNG +CREATURE:SASQUATCH:HEART +CREATURE:SASQUATCH:LIVER +CREATURE:SASQUATCH:GUT +CREATURE:SASQUATCH:STOMACH +CREATURE:SASQUATCH:GIZZARD +CREATURE:SASQUATCH:PANCREAS +CREATURE:SASQUATCH:SPLEEN +CREATURE:SASQUATCH:KIDNEY +CREATURE:BLIZZARD_MAN:MUSCLE +CREATURE:BLIZZARD_MAN:EYE +CREATURE:BLIZZARD_MAN:BRAIN +CREATURE:BLIZZARD_MAN:LUNG +CREATURE:BLIZZARD_MAN:HEART +CREATURE:BLIZZARD_MAN:LIVER +CREATURE:BLIZZARD_MAN:GUT +CREATURE:BLIZZARD_MAN:STOMACH +CREATURE:BLIZZARD_MAN:GIZZARD +CREATURE:BLIZZARD_MAN:PANCREAS +CREATURE:BLIZZARD_MAN:SPLEEN +CREATURE:BLIZZARD_MAN:KIDNEY +CREATURE:WOLF_ICE:MUSCLE +CREATURE:WOLF_ICE:EYE +CREATURE:WOLF_ICE:BRAIN +CREATURE:WOLF_ICE:LUNG +CREATURE:WOLF_ICE:HEART +CREATURE:WOLF_ICE:LIVER +CREATURE:WOLF_ICE:GUT +CREATURE:WOLF_ICE:STOMACH +CREATURE:WOLF_ICE:GIZZARD +CREATURE:WOLF_ICE:PANCREAS +CREATURE:WOLF_ICE:SPLEEN +CREATURE:WOLF_ICE:KIDNEY +CREATURE:FAIRY:MUSCLE +CREATURE:FAIRY:EYE +CREATURE:FAIRY:BRAIN +CREATURE:FAIRY:LUNG +CREATURE:FAIRY:HEART +CREATURE:FAIRY:LIVER +CREATURE:FAIRY:GUT +CREATURE:FAIRY:STOMACH +CREATURE:FAIRY:GIZZARD +CREATURE:FAIRY:PANCREAS +CREATURE:FAIRY:SPLEEN +CREATURE:FAIRY:KIDNEY +CREATURE:PIXIE:MUSCLE +CREATURE:PIXIE:EYE +CREATURE:PIXIE:BRAIN +CREATURE:PIXIE:LUNG +CREATURE:PIXIE:HEART +CREATURE:PIXIE:LIVER +CREATURE:PIXIE:GUT +CREATURE:PIXIE:STOMACH +CREATURE:PIXIE:GIZZARD +CREATURE:PIXIE:PANCREAS +CREATURE:PIXIE:SPLEEN +CREATURE:PIXIE:KIDNEY +CREATURE:BEAK_DOG:MUSCLE +CREATURE:BEAK_DOG:EYE +CREATURE:BEAK_DOG:BRAIN +CREATURE:BEAK_DOG:LUNG +CREATURE:BEAK_DOG:HEART +CREATURE:BEAK_DOG:LIVER +CREATURE:BEAK_DOG:GUT +CREATURE:BEAK_DOG:STOMACH +CREATURE:BEAK_DOG:GIZZARD +CREATURE:BEAK_DOG:PANCREAS +CREATURE:BEAK_DOG:SPLEEN +CREATURE:BEAK_DOG:KIDNEY +CREATURE:GRIMELING:MUSCLE +CREATURE:GRIMELING:EYE +CREATURE:GRIMELING:BRAIN +CREATURE:GRIMELING:LUNG +CREATURE:GRIMELING:HEART +CREATURE:GRIMELING:LIVER +CREATURE:GRIMELING:GUT +CREATURE:GRIMELING:STOMACH +CREATURE:GRIMELING:GIZZARD +CREATURE:GRIMELING:PANCREAS +CREATURE:GRIMELING:SPLEEN +CREATURE:GRIMELING:KIDNEY +CREATURE:BLENDEC_FOUL:MUSCLE +CREATURE:BLENDEC_FOUL:EYE +CREATURE:BLENDEC_FOUL:BRAIN +CREATURE:BLENDEC_FOUL:LUNG +CREATURE:BLENDEC_FOUL:HEART +CREATURE:BLENDEC_FOUL:LIVER +CREATURE:BLENDEC_FOUL:GUT +CREATURE:BLENDEC_FOUL:STOMACH +CREATURE:BLENDEC_FOUL:GIZZARD +CREATURE:BLENDEC_FOUL:PANCREAS +CREATURE:BLENDEC_FOUL:SPLEEN +CREATURE:BLENDEC_FOUL:KIDNEY +CREATURE:STRANGLER:MUSCLE +CREATURE:STRANGLER:EYE +CREATURE:STRANGLER:BRAIN +CREATURE:STRANGLER:LUNG +CREATURE:STRANGLER:HEART +CREATURE:STRANGLER:LIVER +CREATURE:STRANGLER:GUT +CREATURE:STRANGLER:STOMACH +CREATURE:STRANGLER:GIZZARD +CREATURE:STRANGLER:PANCREAS +CREATURE:STRANGLER:SPLEEN +CREATURE:STRANGLER:KIDNEY +CREATURE:NIGHTWING:MUSCLE +CREATURE:NIGHTWING:EYE +CREATURE:NIGHTWING:BRAIN +CREATURE:NIGHTWING:LUNG +CREATURE:NIGHTWING:HEART +CREATURE:NIGHTWING:LIVER +CREATURE:NIGHTWING:GUT +CREATURE:NIGHTWING:STOMACH +CREATURE:NIGHTWING:GIZZARD +CREATURE:NIGHTWING:PANCREAS +CREATURE:NIGHTWING:SPLEEN +CREATURE:NIGHTWING:KIDNEY +CREATURE:HARPY:MUSCLE +CREATURE:HARPY:EYE +CREATURE:HARPY:BRAIN +CREATURE:HARPY:LUNG +CREATURE:HARPY:HEART +CREATURE:HARPY:LIVER +CREATURE:HARPY:GUT +CREATURE:HARPY:STOMACH +CREATURE:HARPY:GIZZARD +CREATURE:HARPY:PANCREAS +CREATURE:HARPY:SPLEEN +CREATURE:HARPY:KIDNEY +CREATURE:HYDRA:MUSCLE +CREATURE:HYDRA:EYE +CREATURE:HYDRA:BRAIN +CREATURE:HYDRA:LUNG +CREATURE:HYDRA:HEART +CREATURE:HYDRA:LIVER +CREATURE:HYDRA:GUT +CREATURE:HYDRA:STOMACH +CREATURE:HYDRA:GIZZARD +CREATURE:HYDRA:PANCREAS +CREATURE:HYDRA:SPLEEN +CREATURE:HYDRA:KIDNEY +CREATURE:MERPERSON:MUSCLE +CREATURE:MERPERSON:EYE +CREATURE:MERPERSON:BRAIN +CREATURE:MERPERSON:LUNG +CREATURE:MERPERSON:HEART +CREATURE:MERPERSON:LIVER +CREATURE:MERPERSON:GUT +CREATURE:MERPERSON:STOMACH +CREATURE:MERPERSON:GIZZARD +CREATURE:MERPERSON:PANCREAS +CREATURE:MERPERSON:SPLEEN +CREATURE:MERPERSON:KIDNEY +CREATURE:SEA_SERPENT:MUSCLE +CREATURE:SEA_SERPENT:EYE +CREATURE:SEA_SERPENT:BRAIN +CREATURE:SEA_SERPENT:LUNG +CREATURE:SEA_SERPENT:HEART +CREATURE:SEA_SERPENT:LIVER +CREATURE:SEA_SERPENT:GUT +CREATURE:SEA_SERPENT:STOMACH +CREATURE:SEA_SERPENT:GIZZARD +CREATURE:SEA_SERPENT:PANCREAS +CREATURE:SEA_SERPENT:SPLEEN +CREATURE:SEA_SERPENT:KIDNEY +CREATURE:SEA_MONSTER:MUSCLE +CREATURE:SEA_MONSTER:EYE +CREATURE:SEA_MONSTER:BRAIN +CREATURE:SEA_MONSTER:LUNG +CREATURE:SEA_MONSTER:HEART +CREATURE:SEA_MONSTER:LIVER +CREATURE:SEA_MONSTER:GUT +CREATURE:SEA_MONSTER:STOMACH +CREATURE:SEA_MONSTER:GIZZARD +CREATURE:SEA_MONSTER:PANCREAS +CREATURE:SEA_MONSTER:SPLEEN +CREATURE:SEA_MONSTER:KIDNEY +CREATURE:BIRD_ROC:MUSCLE +CREATURE:BIRD_ROC:EYE +CREATURE:BIRD_ROC:BRAIN +CREATURE:BIRD_ROC:LUNG +CREATURE:BIRD_ROC:HEART +CREATURE:BIRD_ROC:LIVER +CREATURE:BIRD_ROC:GUT +CREATURE:BIRD_ROC:STOMACH +CREATURE:BIRD_ROC:GIZZARD +CREATURE:BIRD_ROC:PANCREAS +CREATURE:BIRD_ROC:SPLEEN +CREATURE:BIRD_ROC:KIDNEY +CREATURE:CROCODILE_CAVE:MUSCLE +CREATURE:CROCODILE_CAVE:EYE +CREATURE:CROCODILE_CAVE:BRAIN +CREATURE:CROCODILE_CAVE:LUNG +CREATURE:CROCODILE_CAVE:HEART +CREATURE:CROCODILE_CAVE:LIVER +CREATURE:CROCODILE_CAVE:GUT +CREATURE:CROCODILE_CAVE:STOMACH +CREATURE:CROCODILE_CAVE:GIZZARD + CREATURE:CROCODILE_CAVE:PANCREAS +CREATURE:CROCODILE_CAVE:SPLEEN +CREATURE:CROCODILE_CAVE:KIDNEY +CREATURE:TOAD_GIANT_CAVE:MUSCLE +CREATURE:TOAD_GIANT_CAVE:EYE +CREATURE:TOAD_GIANT_CAVE:BRAIN +CREATURE:TOAD_GIANT_CAVE:LUNG +CREATURE:TOAD_GIANT_CAVE:HEART +CREATURE:TOAD_GIANT_CAVE:LIVER +CREATURE:TOAD_GIANT_CAVE:GUT + CREATURE:TOAD_GIANT_CAVE:STOMACH + CREATURE:TOAD_GIANT_CAVE:GIZZARD +!CREATURE:TOAD_GIANT_CAVE:PANCREAS +CREATURE:TOAD_GIANT_CAVE:SPLEEN +CREATURE:TOAD_GIANT_CAVE:KIDNEY +CREATURE:OLM_GIANT:MUSCLE +CREATURE:OLM_GIANT:EYE +CREATURE:OLM_GIANT:BRAIN +CREATURE:OLM_GIANT:LUNG +CREATURE:OLM_GIANT:HEART +CREATURE:OLM_GIANT:LIVER +CREATURE:OLM_GIANT:GUT +CREATURE:OLM_GIANT:STOMACH +CREATURE:OLM_GIANT:GIZZARD +CREATURE:OLM_GIANT:PANCREAS +CREATURE:OLM_GIANT:SPLEEN +CREATURE:OLM_GIANT:KIDNEY +CREATURE:BAT_GIANT:MUSCLE +CREATURE:BAT_GIANT:EYE +CREATURE:BAT_GIANT:BRAIN +CREATURE:BAT_GIANT:LUNG +CREATURE:BAT_GIANT:HEART +CREATURE:BAT_GIANT:LIVER +CREATURE:BAT_GIANT:GUT +CREATURE:BAT_GIANT:STOMACH +CREATURE:BAT_GIANT:GIZZARD +CREATURE:BAT_GIANT:PANCREAS +CREATURE:BAT_GIANT:SPLEEN +CREATURE:BAT_GIANT:KIDNEY +CREATURE:RAT_GIANT:MUSCLE +CREATURE:RAT_GIANT:EYE +CREATURE:RAT_GIANT:BRAIN +CREATURE:RAT_GIANT:LUNG +CREATURE:RAT_GIANT:HEART +CREATURE:RAT_GIANT:LIVER +CREATURE:RAT_GIANT:GUT +CREATURE:RAT_GIANT:STOMACH +CREATURE:RAT_GIANT:GIZZARD +CREATURE:RAT_GIANT:PANCREAS +CREATURE:RAT_GIANT:SPLEEN +CREATURE:RAT_GIANT:KIDNEY +CREATURE:RAT_LARGE:MUSCLE +CREATURE:RAT_LARGE:EYE +CREATURE:RAT_LARGE:BRAIN +CREATURE:RAT_LARGE:LUNG +CREATURE:RAT_LARGE:HEART +CREATURE:RAT_LARGE:LIVER +CREATURE:RAT_LARGE:GUT +CREATURE:RAT_LARGE:STOMACH +CREATURE:RAT_LARGE:GIZZARD +CREATURE:RAT_LARGE:PANCREAS +CREATURE:RAT_LARGE:SPLEEN +CREATURE:RAT_LARGE:KIDNEY +CREATURE:MOLE_DOG_NAKED:MUSCLE +CREATURE:MOLE_DOG_NAKED:EYE +CREATURE:MOLE_DOG_NAKED:BRAIN +CREATURE:MOLE_DOG_NAKED:LUNG +CREATURE:MOLE_DOG_NAKED:HEART +CREATURE:MOLE_DOG_NAKED:LIVER +CREATURE:MOLE_DOG_NAKED:GUT +CREATURE:MOLE_DOG_NAKED:STOMACH +CREATURE:MOLE_DOG_NAKED:GIZZARD + CREATURE:MOLE_DOG_NAKED:PANCREAS +CREATURE:MOLE_DOG_NAKED:SPLEEN +CREATURE:MOLE_DOG_NAKED:KIDNEY +CREATURE:TROGLODYTE:MUSCLE +CREATURE:TROGLODYTE:EYE +CREATURE:TROGLODYTE:BRAIN +CREATURE:TROGLODYTE:LUNG +CREATURE:TROGLODYTE:HEART +CREATURE:TROGLODYTE:LIVER +CREATURE:TROGLODYTE:GUT +CREATURE:TROGLODYTE:STOMACH +CREATURE:TROGLODYTE:GIZZARD +CREATURE:TROGLODYTE:PANCREAS +CREATURE:TROGLODYTE:SPLEEN +CREATURE:TROGLODYTE:KIDNEY +CREATURE:MOLE_GIANT:MUSCLE +CREATURE:MOLE_GIANT:EYE +CREATURE:MOLE_GIANT:BRAIN +CREATURE:MOLE_GIANT:LUNG +CREATURE:MOLE_GIANT:HEART +CREATURE:MOLE_GIANT:LIVER +CREATURE:MOLE_GIANT:GUT +CREATURE:MOLE_GIANT:STOMACH +CREATURE:MOLE_GIANT:GIZZARD +CREATURE:MOLE_GIANT:PANCREAS +CREATURE:MOLE_GIANT:SPLEEN +CREATURE:MOLE_GIANT:KIDNEY +CREATURE:IMP_FIRE:MUSCLE +CREATURE:IMP_FIRE:EYE +CREATURE:IMP_FIRE:BRAIN +CREATURE:IMP_FIRE:LUNG +CREATURE:IMP_FIRE:HEART +CREATURE:IMP_FIRE:LIVER +CREATURE:IMP_FIRE:GUT +CREATURE:IMP_FIRE:STOMACH +CREATURE:IMP_FIRE:GIZZARD +CREATURE:IMP_FIRE:PANCREAS +CREATURE:IMP_FIRE:SPLEEN +CREATURE:IMP_FIRE:KIDNEY +!CREATURE:SPIDER_CAVE_GIANT:MUSCLE +CREATURE:SPIDER_CAVE_GIANT:EYE + CREATURE:SPIDER_CAVE_GIANT:BRAIN +CREATURE:SPIDER_CAVE_GIANT:LUNG + CREATURE:SPIDER_CAVE_GIANT:HEART + CREATURE:SPIDER_CAVE_GIANT:LIVER +CREATURE:SPIDER_CAVE_GIANT:GUT +"CREATURE:SPIDER_CAVE_GIANT:STOMACH +"CREATURE:SPIDER_CAVE_GIANT:GIZZARD +#CREATURE:SPIDER_CAVE_GIANT:PANCREAS +!CREATURE:SPIDER_CAVE_GIANT:SPLEEN +!CREATURE:SPIDER_CAVE_GIANT:KIDNEY +CREATURE:SPIDER_CAVE:MUSCLE +CREATURE:SPIDER_CAVE:EYE +CREATURE:SPIDER_CAVE:BRAIN +CREATURE:SPIDER_CAVE:LUNG +CREATURE:SPIDER_CAVE:HEART +CREATURE:SPIDER_CAVE:LIVER +CREATURE:SPIDER_CAVE:GUT +CREATURE:SPIDER_CAVE:STOMACH +CREATURE:SPIDER_CAVE:GIZZARD +CREATURE:SPIDER_CAVE:PANCREAS +CREATURE:SPIDER_CAVE:SPLEEN +CREATURE:SPIDER_CAVE:KIDNEY +CREATURE:FISH_CAVE:MUSCLE +CREATURE:FISH_CAVE:BRAIN +CREATURE:FISH_CAVE:LUNG +CREATURE:FISH_CAVE:HEART +CREATURE:FISH_CAVE:LIVER +CREATURE:FISH_CAVE:GUT +CREATURE:FISH_CAVE:STOMACH +CREATURE:FISH_CAVE:GIZZARD +CREATURE:FISH_CAVE:PANCREAS +CREATURE:FISH_CAVE:SPLEEN +CREATURE:FISH_CAVE:KIDNEY +CREATURE:CAVE_FISH_MAN:MUSCLE +CREATURE:CAVE_FISH_MAN:BRAIN +CREATURE:CAVE_FISH_MAN:LUNG +CREATURE:CAVE_FISH_MAN:HEART +CREATURE:CAVE_FISH_MAN:LIVER +CREATURE:CAVE_FISH_MAN:GUT +CREATURE:CAVE_FISH_MAN:STOMACH +CREATURE:CAVE_FISH_MAN:GIZZARD +CREATURE:CAVE_FISH_MAN:PANCREAS +CREATURE:CAVE_FISH_MAN:SPLEEN +CREATURE:CAVE_FISH_MAN:KIDNEY +CREATURE:LOBSTER_CAVE:MUSCLE +CREATURE:LOBSTER_CAVE:EYE +CREATURE:LOBSTER_CAVE:BRAIN +CREATURE:LOBSTER_CAVE:LUNG +CREATURE:LOBSTER_CAVE:HEART +CREATURE:LOBSTER_CAVE:LIVER +CREATURE:LOBSTER_CAVE:GUT +CREATURE:LOBSTER_CAVE:STOMACH +CREATURE:LOBSTER_CAVE:GIZZARD +CREATURE:LOBSTER_CAVE:PANCREAS +CREATURE:LOBSTER_CAVE:SPLEEN +CREATURE:LOBSTER_CAVE:KIDNEY +CREATURE:OLM:MUSCLE +CREATURE:OLM:EYE +CREATURE:OLM:BRAIN +CREATURE:OLM:LUNG +CREATURE:OLM:HEART +CREATURE:OLM:LIVER +CREATURE:OLM:GUT +CREATURE:OLM:STOMACH +CREATURE:OLM:GIZZARD +CREATURE:OLM:PANCREAS +CREATURE:OLM:SPLEEN +CREATURE:OLM:KIDNEY +CREATURE:OLM_MAN:MUSCLE +CREATURE:OLM_MAN:EYE +CREATURE:OLM_MAN:BRAIN +CREATURE:OLM_MAN:LUNG +CREATURE:OLM_MAN:HEART +CREATURE:OLM_MAN:LIVER +CREATURE:OLM_MAN:GUT +CREATURE:OLM_MAN:STOMACH +CREATURE:OLM_MAN:GIZZARD +CREATURE:OLM_MAN:PANCREAS +CREATURE:OLM_MAN:SPLEEN +CREATURE:OLM_MAN:KIDNEY +CREATURE:BAT:MUSCLE +CREATURE:BAT:EYE +CREATURE:BAT:BRAIN +CREATURE:BAT:LUNG +CREATURE:BAT:HEART +CREATURE:BAT:LIVER +CREATURE:BAT:GUT +CREATURE:BAT:STOMACH +CREATURE:BAT:GIZZARD +CREATURE:BAT:PANCREAS +CREATURE:BAT:SPLEEN +CREATURE:BAT:KIDNEY +CREATURE:BAT_MAN:MUSCLE +CREATURE:BAT_MAN:EYE +CREATURE:BAT_MAN:BRAIN +CREATURE:BAT_MAN:LUNG +CREATURE:BAT_MAN:HEART +CREATURE:BAT_MAN:LIVER +CREATURE:BAT_MAN:GUT +CREATURE:BAT_MAN:STOMACH +CREATURE:BAT_MAN:GIZZARD +CREATURE:BAT_MAN:PANCREAS +CREATURE:BAT_MAN:SPLEEN +CREATURE:BAT_MAN:KIDNEY +CREATURE:MAGGOT_PURRING:MUSCLE +CREATURE:MAGGOT_PURRING:EYE +CREATURE:MAGGOT_PURRING:BRAIN +CREATURE:MAGGOT_PURRING:LUNG +CREATURE:MAGGOT_PURRING:HEART +CREATURE:MAGGOT_PURRING:LIVER +CREATURE:MAGGOT_PURRING:GUT +CREATURE:MAGGOT_PURRING:STOMACH +CREATURE:MAGGOT_PURRING:GIZZARD + CREATURE:MAGGOT_PURRING:PANCREAS +CREATURE:MAGGOT_PURRING:SPLEEN +CREATURE:MAGGOT_PURRING:KIDNEY +!CREATURE:BIRD_SWALLOW_CAVE:MUSCLE +CREATURE:BIRD_SWALLOW_CAVE:EYE + CREATURE:BIRD_SWALLOW_CAVE:BRAIN +CREATURE:BIRD_SWALLOW_CAVE:LUNG + CREATURE:BIRD_SWALLOW_CAVE:HEART + CREATURE:BIRD_SWALLOW_CAVE:LIVER +CREATURE:BIRD_SWALLOW_CAVE:GUT +"CREATURE:BIRD_SWALLOW_CAVE:STOMACH +"CREATURE:BIRD_SWALLOW_CAVE:GIZZARD +#CREATURE:BIRD_SWALLOW_CAVE:PANCREAS +!CREATURE:BIRD_SWALLOW_CAVE:SPLEEN +!CREATURE:BIRD_SWALLOW_CAVE:KIDNEY + CREATURE:CAVE_SWALLOW_MAN:MUSCLE +CREATURE:CAVE_SWALLOW_MAN:EYE +CREATURE:CAVE_SWALLOW_MAN:BRAIN +CREATURE:CAVE_SWALLOW_MAN:LUNG +CREATURE:CAVE_SWALLOW_MAN:HEART +CREATURE:CAVE_SWALLOW_MAN:LIVER +CREATURE:CAVE_SWALLOW_MAN:GUT +!CREATURE:CAVE_SWALLOW_MAN:STOMACH +!CREATURE:CAVE_SWALLOW_MAN:GIZZARD +"CREATURE:CAVE_SWALLOW_MAN:PANCREAS + CREATURE:CAVE_SWALLOW_MAN:SPLEEN + CREATURE:CAVE_SWALLOW_MAN:KIDNEY +'CREATURE:BIRD_SWALLOW_CAVE_GIANT:MUSCLE +$CREATURE:BIRD_SWALLOW_CAVE_GIANT:EYE +&CREATURE:BIRD_SWALLOW_CAVE_GIANT:BRAIN +%CREATURE:BIRD_SWALLOW_CAVE_GIANT:LUNG +&CREATURE:BIRD_SWALLOW_CAVE_GIANT:HEART +&CREATURE:BIRD_SWALLOW_CAVE_GIANT:LIVER +$CREATURE:BIRD_SWALLOW_CAVE_GIANT:GUT +(CREATURE:BIRD_SWALLOW_CAVE_GIANT:STOMACH +(CREATURE:BIRD_SWALLOW_CAVE_GIANT:GIZZARD +)CREATURE:BIRD_SWALLOW_CAVE_GIANT:PANCREAS +'CREATURE:BIRD_SWALLOW_CAVE_GIANT:SPLEEN +'CREATURE:BIRD_SWALLOW_CAVE_GIANT:KIDNEY +CREATURE:AMPHIBIAN_MAN:MUSCLE +CREATURE:AMPHIBIAN_MAN:EYE +CREATURE:AMPHIBIAN_MAN:BRAIN +CREATURE:AMPHIBIAN_MAN:LUNG +CREATURE:AMPHIBIAN_MAN:HEART +CREATURE:AMPHIBIAN_MAN:LIVER +CREATURE:AMPHIBIAN_MAN:GUT +CREATURE:AMPHIBIAN_MAN:STOMACH +CREATURE:AMPHIBIAN_MAN:GIZZARD +CREATURE:AMPHIBIAN_MAN:PANCREAS +CREATURE:AMPHIBIAN_MAN:SPLEEN +CREATURE:AMPHIBIAN_MAN:KIDNEY +CREATURE:REPTILE_MAN:MUSCLE +CREATURE:REPTILE_MAN:EYE +CREATURE:REPTILE_MAN:BRAIN +CREATURE:REPTILE_MAN:LUNG +CREATURE:REPTILE_MAN:HEART +CREATURE:REPTILE_MAN:LIVER +CREATURE:REPTILE_MAN:GUT +CREATURE:REPTILE_MAN:STOMACH +CREATURE:REPTILE_MAN:GIZZARD +CREATURE:REPTILE_MAN:PANCREAS +CREATURE:REPTILE_MAN:SPLEEN +CREATURE:REPTILE_MAN:KIDNEY +CREATURE:SERPENT_MAN:MUSCLE +CREATURE:SERPENT_MAN:EYE +CREATURE:SERPENT_MAN:BRAIN +CREATURE:SERPENT_MAN:LUNG +CREATURE:SERPENT_MAN:HEART +CREATURE:SERPENT_MAN:LIVER +CREATURE:SERPENT_MAN:GUT +CREATURE:SERPENT_MAN:STOMACH +CREATURE:SERPENT_MAN:GIZZARD +CREATURE:SERPENT_MAN:PANCREAS +CREATURE:SERPENT_MAN:SPLEEN +CREATURE:SERPENT_MAN:KIDNEY +CREATURE:ANT_MAN:MUSCLE +CREATURE:ANT_MAN:EYE +CREATURE:ANT_MAN:BRAIN +CREATURE:ANT_MAN:LUNG +CREATURE:ANT_MAN:HEART +CREATURE:ANT_MAN:LIVER +CREATURE:ANT_MAN:GUT +CREATURE:ANT_MAN:STOMACH +CREATURE:ANT_MAN:GIZZARD +CREATURE:ANT_MAN:PANCREAS +CREATURE:ANT_MAN:SPLEEN +CREATURE:ANT_MAN:KIDNEY +CREATURE:RODENT MAN:MUSCLE +CREATURE:RODENT MAN:EYE +CREATURE:RODENT MAN:BRAIN +CREATURE:RODENT MAN:LUNG +CREATURE:RODENT MAN:HEART +CREATURE:RODENT MAN:LIVER +CREATURE:RODENT MAN:GUT +CREATURE:RODENT MAN:STOMACH +CREATURE:RODENT MAN:GIZZARD +CREATURE:RODENT MAN:PANCREAS +CREATURE:RODENT MAN:SPLEEN +CREATURE:RODENT MAN:KIDNEY +CREATURE:WILD_BOAR:MUSCLE +CREATURE:WILD_BOAR:EYE +CREATURE:WILD_BOAR:BRAIN +CREATURE:WILD_BOAR:LUNG +CREATURE:WILD_BOAR:HEART +CREATURE:WILD_BOAR:LIVER +CREATURE:WILD_BOAR:GUT +CREATURE:WILD_BOAR:STOMACH +CREATURE:WILD_BOAR:GIZZARD +CREATURE:WILD_BOAR:PANCREAS +CREATURE:WILD_BOAR:SPLEEN +CREATURE:WILD_BOAR:KIDNEY +CREATURE:WILD_BOAR_MAN:MUSCLE +CREATURE:WILD_BOAR_MAN:EYE +CREATURE:WILD_BOAR_MAN:BRAIN +CREATURE:WILD_BOAR_MAN:LUNG +CREATURE:WILD_BOAR_MAN:HEART +CREATURE:WILD_BOAR_MAN:LIVER +CREATURE:WILD_BOAR_MAN:GUT +CREATURE:WILD_BOAR_MAN:STOMACH +CREATURE:WILD_BOAR_MAN:GIZZARD +CREATURE:WILD_BOAR_MAN:PANCREAS +CREATURE:WILD_BOAR_MAN:SPLEEN +CREATURE:WILD_BOAR_MAN:KIDNEY +CREATURE:GIANT_WILD_BOAR:MUSCLE +CREATURE:GIANT_WILD_BOAR:EYE +CREATURE:GIANT_WILD_BOAR:BRAIN +CREATURE:GIANT_WILD_BOAR:LUNG +CREATURE:GIANT_WILD_BOAR:HEART +CREATURE:GIANT_WILD_BOAR:LIVER +CREATURE:GIANT_WILD_BOAR:GUT + CREATURE:GIANT_WILD_BOAR:STOMACH + CREATURE:GIANT_WILD_BOAR:GIZZARD +!CREATURE:GIANT_WILD_BOAR:PANCREAS +CREATURE:GIANT_WILD_BOAR:SPLEEN +CREATURE:GIANT_WILD_BOAR:KIDNEY +CREATURE:COYOTE:MUSCLE +CREATURE:COYOTE:EYE +CREATURE:COYOTE:BRAIN +CREATURE:COYOTE:LUNG +CREATURE:COYOTE:HEART +CREATURE:COYOTE:LIVER +CREATURE:COYOTE:GUT +CREATURE:COYOTE:STOMACH +CREATURE:COYOTE:GIZZARD +CREATURE:COYOTE:PANCREAS +CREATURE:COYOTE:SPLEEN +CREATURE:COYOTE:KIDNEY +CREATURE:COYOTE_MAN:MUSCLE +CREATURE:COYOTE_MAN:EYE +CREATURE:COYOTE_MAN:BRAIN +CREATURE:COYOTE_MAN:LUNG +CREATURE:COYOTE_MAN:HEART +CREATURE:COYOTE_MAN:LIVER +CREATURE:COYOTE_MAN:GUT +CREATURE:COYOTE_MAN:STOMACH +CREATURE:COYOTE_MAN:GIZZARD +CREATURE:COYOTE_MAN:PANCREAS +CREATURE:COYOTE_MAN:SPLEEN +CREATURE:COYOTE_MAN:KIDNEY +CREATURE:GIANT_COYOTE:MUSCLE +CREATURE:GIANT_COYOTE:EYE +CREATURE:GIANT_COYOTE:BRAIN +CREATURE:GIANT_COYOTE:LUNG +CREATURE:GIANT_COYOTE:HEART +CREATURE:GIANT_COYOTE:LIVER +CREATURE:GIANT_COYOTE:GUT +CREATURE:GIANT_COYOTE:STOMACH +CREATURE:GIANT_COYOTE:GIZZARD +CREATURE:GIANT_COYOTE:PANCREAS +CREATURE:GIANT_COYOTE:SPLEEN +CREATURE:GIANT_COYOTE:KIDNEY +CREATURE:KANGAROO:MUSCLE +CREATURE:KANGAROO:EYE +CREATURE:KANGAROO:BRAIN +CREATURE:KANGAROO:LUNG +CREATURE:KANGAROO:HEART +CREATURE:KANGAROO:LIVER +CREATURE:KANGAROO:GUT +CREATURE:KANGAROO:STOMACH +CREATURE:KANGAROO:GIZZARD +CREATURE:KANGAROO:PANCREAS +CREATURE:KANGAROO:SPLEEN +CREATURE:KANGAROO:KIDNEY +CREATURE:KANGAROO_MAN:MUSCLE +CREATURE:KANGAROO_MAN:EYE +CREATURE:KANGAROO_MAN:BRAIN +CREATURE:KANGAROO_MAN:LUNG +CREATURE:KANGAROO_MAN:HEART +CREATURE:KANGAROO_MAN:LIVER +CREATURE:KANGAROO_MAN:GUT +CREATURE:KANGAROO_MAN:STOMACH +CREATURE:KANGAROO_MAN:GIZZARD +CREATURE:KANGAROO_MAN:PANCREAS +CREATURE:KANGAROO_MAN:SPLEEN +CREATURE:KANGAROO_MAN:KIDNEY +CREATURE:GIANT_KANGAROO:MUSCLE +CREATURE:GIANT_KANGAROO:EYE +CREATURE:GIANT_KANGAROO:BRAIN +CREATURE:GIANT_KANGAROO:LUNG +CREATURE:GIANT_KANGAROO:HEART +CREATURE:GIANT_KANGAROO:LIVER +CREATURE:GIANT_KANGAROO:GUT +CREATURE:GIANT_KANGAROO:STOMACH +CREATURE:GIANT_KANGAROO:GIZZARD + CREATURE:GIANT_KANGAROO:PANCREAS +CREATURE:GIANT_KANGAROO:SPLEEN +CREATURE:GIANT_KANGAROO:KIDNEY +CREATURE:KOALA:MUSCLE +CREATURE:KOALA:EYE +CREATURE:KOALA:BRAIN +CREATURE:KOALA:LUNG +CREATURE:KOALA:HEART +CREATURE:KOALA:LIVER +CREATURE:KOALA:GUT +CREATURE:KOALA:STOMACH +CREATURE:KOALA:GIZZARD +CREATURE:KOALA:PANCREAS +CREATURE:KOALA:SPLEEN +CREATURE:KOALA:KIDNEY +CREATURE:KOALA_MAN:MUSCLE +CREATURE:KOALA_MAN:EYE +CREATURE:KOALA_MAN:BRAIN +CREATURE:KOALA_MAN:LUNG +CREATURE:KOALA_MAN:HEART +CREATURE:KOALA_MAN:LIVER +CREATURE:KOALA_MAN:GUT +CREATURE:KOALA_MAN:STOMACH +CREATURE:KOALA_MAN:GIZZARD +CREATURE:KOALA_MAN:PANCREAS +CREATURE:KOALA_MAN:SPLEEN +CREATURE:KOALA_MAN:KIDNEY +CREATURE:GIANT_KOALA:MUSCLE +CREATURE:GIANT_KOALA:EYE +CREATURE:GIANT_KOALA:BRAIN +CREATURE:GIANT_KOALA:LUNG +CREATURE:GIANT_KOALA:HEART +CREATURE:GIANT_KOALA:LIVER +CREATURE:GIANT_KOALA:GUT +CREATURE:GIANT_KOALA:STOMACH +CREATURE:GIANT_KOALA:GIZZARD +CREATURE:GIANT_KOALA:PANCREAS +CREATURE:GIANT_KOALA:SPLEEN +CREATURE:GIANT_KOALA:KIDNEY +CREATURE:ADDER:MUSCLE +CREATURE:ADDER:EYE +CREATURE:ADDER:BRAIN +CREATURE:ADDER:LUNG +CREATURE:ADDER:HEART +CREATURE:ADDER:LIVER +CREATURE:ADDER:GUT +CREATURE:ADDER:STOMACH +CREATURE:ADDER:GIZZARD +CREATURE:ADDER:PANCREAS +CREATURE:ADDER:SPLEEN +CREATURE:ADDER:KIDNEY +CREATURE:ADDER_MAN:MUSCLE +CREATURE:ADDER_MAN:EYE +CREATURE:ADDER_MAN:BRAIN +CREATURE:ADDER_MAN:LUNG +CREATURE:ADDER_MAN:HEART +CREATURE:ADDER_MAN:LIVER +CREATURE:ADDER_MAN:GUT +CREATURE:ADDER_MAN:STOMACH +CREATURE:ADDER_MAN:GIZZARD +CREATURE:ADDER_MAN:PANCREAS +CREATURE:ADDER_MAN:SPLEEN +CREATURE:ADDER_MAN:KIDNEY +CREATURE:GIANT_ADDER:MUSCLE +CREATURE:GIANT_ADDER:EYE +CREATURE:GIANT_ADDER:BRAIN +CREATURE:GIANT_ADDER:LUNG +CREATURE:GIANT_ADDER:HEART +CREATURE:GIANT_ADDER:LIVER +CREATURE:GIANT_ADDER:GUT +CREATURE:GIANT_ADDER:STOMACH +CREATURE:GIANT_ADDER:GIZZARD +CREATURE:GIANT_ADDER:PANCREAS +CREATURE:GIANT_ADDER:SPLEEN +CREATURE:GIANT_ADDER:KIDNEY +CREATURE:ECHIDNA:MUSCLE +CREATURE:ECHIDNA:EYE +CREATURE:ECHIDNA:BRAIN +CREATURE:ECHIDNA:LUNG +CREATURE:ECHIDNA:HEART +CREATURE:ECHIDNA:LIVER +CREATURE:ECHIDNA:GUT +CREATURE:ECHIDNA:STOMACH +CREATURE:ECHIDNA:GIZZARD +CREATURE:ECHIDNA:PANCREAS +CREATURE:ECHIDNA:SPLEEN +CREATURE:ECHIDNA:KIDNEY +CREATURE:ECHIDNA_MAN:MUSCLE +CREATURE:ECHIDNA_MAN:EYE +CREATURE:ECHIDNA_MAN:BRAIN +CREATURE:ECHIDNA_MAN:LUNG +CREATURE:ECHIDNA_MAN:HEART +CREATURE:ECHIDNA_MAN:LIVER +CREATURE:ECHIDNA_MAN:GUT +CREATURE:ECHIDNA_MAN:STOMACH +CREATURE:ECHIDNA_MAN:GIZZARD +CREATURE:ECHIDNA_MAN:PANCREAS +CREATURE:ECHIDNA_MAN:SPLEEN +CREATURE:ECHIDNA_MAN:KIDNEY +CREATURE:GIANT_ECHIDNA:MUSCLE +CREATURE:GIANT_ECHIDNA:EYE +CREATURE:GIANT_ECHIDNA:BRAIN +CREATURE:GIANT_ECHIDNA:LUNG +CREATURE:GIANT_ECHIDNA:HEART +CREATURE:GIANT_ECHIDNA:LIVER +CREATURE:GIANT_ECHIDNA:GUT +CREATURE:GIANT_ECHIDNA:STOMACH +CREATURE:GIANT_ECHIDNA:GIZZARD +CREATURE:GIANT_ECHIDNA:PANCREAS +CREATURE:GIANT_ECHIDNA:SPLEEN +CREATURE:GIANT_ECHIDNA:KIDNEY +CREATURE:PORCUPINE:MUSCLE +CREATURE:PORCUPINE:EYE +CREATURE:PORCUPINE:BRAIN +CREATURE:PORCUPINE:LUNG +CREATURE:PORCUPINE:HEART +CREATURE:PORCUPINE:LIVER +CREATURE:PORCUPINE:GUT +CREATURE:PORCUPINE:STOMACH +CREATURE:PORCUPINE:GIZZARD +CREATURE:PORCUPINE:PANCREAS +CREATURE:PORCUPINE:SPLEEN +CREATURE:PORCUPINE:KIDNEY +CREATURE:PORCUPINE_MAN:MUSCLE +CREATURE:PORCUPINE_MAN:EYE +CREATURE:PORCUPINE_MAN:BRAIN +CREATURE:PORCUPINE_MAN:LUNG +CREATURE:PORCUPINE_MAN:HEART +CREATURE:PORCUPINE_MAN:LIVER +CREATURE:PORCUPINE_MAN:GUT +CREATURE:PORCUPINE_MAN:STOMACH +CREATURE:PORCUPINE_MAN:GIZZARD +CREATURE:PORCUPINE_MAN:PANCREAS +CREATURE:PORCUPINE_MAN:SPLEEN +CREATURE:PORCUPINE_MAN:KIDNEY +CREATURE:GIANT_PORCUPINE:MUSCLE +CREATURE:GIANT_PORCUPINE:EYE +CREATURE:GIANT_PORCUPINE:BRAIN +CREATURE:GIANT_PORCUPINE:LUNG +CREATURE:GIANT_PORCUPINE:HEART +CREATURE:GIANT_PORCUPINE:LIVER +CREATURE:GIANT_PORCUPINE:GUT + CREATURE:GIANT_PORCUPINE:STOMACH + CREATURE:GIANT_PORCUPINE:GIZZARD +!CREATURE:GIANT_PORCUPINE:PANCREAS +CREATURE:GIANT_PORCUPINE:SPLEEN +CREATURE:GIANT_PORCUPINE:KIDNEY +CREATURE:KINGSNAKE:MUSCLE +CREATURE:KINGSNAKE:EYE +CREATURE:KINGSNAKE:BRAIN +CREATURE:KINGSNAKE:LUNG +CREATURE:KINGSNAKE:HEART +CREATURE:KINGSNAKE:LIVER +CREATURE:KINGSNAKE:GUT +CREATURE:KINGSNAKE:STOMACH +CREATURE:KINGSNAKE:GIZZARD +CREATURE:KINGSNAKE:PANCREAS +CREATURE:KINGSNAKE:SPLEEN +CREATURE:KINGSNAKE:KIDNEY +CREATURE:KINGSNAKE_MAN:MUSCLE +CREATURE:KINGSNAKE_MAN:EYE +CREATURE:KINGSNAKE_MAN:BRAIN +CREATURE:KINGSNAKE_MAN:LUNG +CREATURE:KINGSNAKE_MAN:HEART +CREATURE:KINGSNAKE_MAN:LIVER +CREATURE:KINGSNAKE_MAN:GUT +CREATURE:KINGSNAKE_MAN:STOMACH +CREATURE:KINGSNAKE_MAN:GIZZARD +CREATURE:KINGSNAKE_MAN:PANCREAS +CREATURE:KINGSNAKE_MAN:SPLEEN +CREATURE:KINGSNAKE_MAN:KIDNEY +CREATURE:GIANT_KINGSNAKE:MUSCLE +CREATURE:GIANT_KINGSNAKE:EYE +CREATURE:GIANT_KINGSNAKE:BRAIN +CREATURE:GIANT_KINGSNAKE:LUNG +CREATURE:GIANT_KINGSNAKE:HEART +CREATURE:GIANT_KINGSNAKE:LIVER +CREATURE:GIANT_KINGSNAKE:GUT + CREATURE:GIANT_KINGSNAKE:STOMACH + CREATURE:GIANT_KINGSNAKE:GIZZARD +!CREATURE:GIANT_KINGSNAKE:PANCREAS +CREATURE:GIANT_KINGSNAKE:SPLEEN +CREATURE:GIANT_KINGSNAKE:KIDNEY +CREATURE:GRAY_LANGUR:MUSCLE +CREATURE:GRAY_LANGUR:EYE +CREATURE:GRAY_LANGUR:BRAIN +CREATURE:GRAY_LANGUR:LUNG +CREATURE:GRAY_LANGUR:HEART +CREATURE:GRAY_LANGUR:LIVER +CREATURE:GRAY_LANGUR:GUT +CREATURE:GRAY_LANGUR:STOMACH +CREATURE:GRAY_LANGUR:GIZZARD +CREATURE:GRAY_LANGUR:PANCREAS +CREATURE:GRAY_LANGUR:SPLEEN +CREATURE:GRAY_LANGUR:KIDNEY +CREATURE:GRAY_LANGUR_MAN:MUSCLE +CREATURE:GRAY_LANGUR_MAN:EYE +CREATURE:GRAY_LANGUR_MAN:BRAIN +CREATURE:GRAY_LANGUR_MAN:LUNG +CREATURE:GRAY_LANGUR_MAN:HEART +CREATURE:GRAY_LANGUR_MAN:LIVER +CREATURE:GRAY_LANGUR_MAN:GUT + CREATURE:GRAY_LANGUR_MAN:STOMACH + CREATURE:GRAY_LANGUR_MAN:GIZZARD +!CREATURE:GRAY_LANGUR_MAN:PANCREAS +CREATURE:GRAY_LANGUR_MAN:SPLEEN +CREATURE:GRAY_LANGUR_MAN:KIDNEY +!CREATURE:GIANT_GRAY_LANGUR:MUSCLE +CREATURE:GIANT_GRAY_LANGUR:EYE + CREATURE:GIANT_GRAY_LANGUR:BRAIN +CREATURE:GIANT_GRAY_LANGUR:LUNG + CREATURE:GIANT_GRAY_LANGUR:HEART + CREATURE:GIANT_GRAY_LANGUR:LIVER +CREATURE:GIANT_GRAY_LANGUR:GUT +"CREATURE:GIANT_GRAY_LANGUR:STOMACH +"CREATURE:GIANT_GRAY_LANGUR:GIZZARD +#CREATURE:GIANT_GRAY_LANGUR:PANCREAS +!CREATURE:GIANT_GRAY_LANGUR:SPLEEN +!CREATURE:GIANT_GRAY_LANGUR:KIDNEY +CREATURE:BOBCAT:MUSCLE +CREATURE:BOBCAT:EYE +CREATURE:BOBCAT:BRAIN +CREATURE:BOBCAT:LUNG +CREATURE:BOBCAT:HEART +CREATURE:BOBCAT:LIVER +CREATURE:BOBCAT:GUT +CREATURE:BOBCAT:STOMACH +CREATURE:BOBCAT:GIZZARD +CREATURE:BOBCAT:PANCREAS +CREATURE:BOBCAT:SPLEEN +CREATURE:BOBCAT:KIDNEY +CREATURE:BOBCAT_MAN:MUSCLE +CREATURE:BOBCAT_MAN:EYE +CREATURE:BOBCAT_MAN:BRAIN +CREATURE:BOBCAT_MAN:LUNG +CREATURE:BOBCAT_MAN:HEART +CREATURE:BOBCAT_MAN:LIVER +CREATURE:BOBCAT_MAN:GUT +CREATURE:BOBCAT_MAN:STOMACH +CREATURE:BOBCAT_MAN:GIZZARD +CREATURE:BOBCAT_MAN:PANCREAS +CREATURE:BOBCAT_MAN:SPLEEN +CREATURE:BOBCAT_MAN:KIDNEY +CREATURE:GIANT_BOBCAT:MUSCLE +CREATURE:GIANT_BOBCAT:EYE +CREATURE:GIANT_BOBCAT:BRAIN +CREATURE:GIANT_BOBCAT:LUNG +CREATURE:GIANT_BOBCAT:HEART +CREATURE:GIANT_BOBCAT:LIVER +CREATURE:GIANT_BOBCAT:GUT +CREATURE:GIANT_BOBCAT:STOMACH +CREATURE:GIANT_BOBCAT:GIZZARD +CREATURE:GIANT_BOBCAT:PANCREAS +CREATURE:GIANT_BOBCAT:SPLEEN +CREATURE:GIANT_BOBCAT:KIDNEY +CREATURE:SKUNK:MUSCLE +CREATURE:SKUNK:EYE +CREATURE:SKUNK:BRAIN +CREATURE:SKUNK:LUNG +CREATURE:SKUNK:HEART +CREATURE:SKUNK:LIVER +CREATURE:SKUNK:GUT +CREATURE:SKUNK:STOMACH +CREATURE:SKUNK:GIZZARD +CREATURE:SKUNK:PANCREAS +CREATURE:SKUNK:SPLEEN +CREATURE:SKUNK:KIDNEY +CREATURE:SKUNK_MAN:MUSCLE +CREATURE:SKUNK_MAN:EYE +CREATURE:SKUNK_MAN:BRAIN +CREATURE:SKUNK_MAN:LUNG +CREATURE:SKUNK_MAN:HEART +CREATURE:SKUNK_MAN:LIVER +CREATURE:SKUNK_MAN:GUT +CREATURE:SKUNK_MAN:STOMACH +CREATURE:SKUNK_MAN:GIZZARD +CREATURE:SKUNK_MAN:PANCREAS +CREATURE:SKUNK_MAN:SPLEEN +CREATURE:SKUNK_MAN:KIDNEY +CREATURE:GIANT_SKUNK:MUSCLE +CREATURE:GIANT_SKUNK:EYE +CREATURE:GIANT_SKUNK:BRAIN +CREATURE:GIANT_SKUNK:LUNG +CREATURE:GIANT_SKUNK:HEART +CREATURE:GIANT_SKUNK:LIVER +CREATURE:GIANT_SKUNK:GUT +CREATURE:GIANT_SKUNK:STOMACH +CREATURE:GIANT_SKUNK:GIZZARD +CREATURE:GIANT_SKUNK:PANCREAS +CREATURE:GIANT_SKUNK:SPLEEN +CREATURE:GIANT_SKUNK:KIDNEY +CREATURE:GREEN_TREE_FROG:MUSCLE +CREATURE:GREEN_TREE_FROG:EYE +CREATURE:GREEN_TREE_FROG:BRAIN +CREATURE:GREEN_TREE_FROG:LUNG +CREATURE:GREEN_TREE_FROG:HEART +CREATURE:GREEN_TREE_FROG:LIVER +CREATURE:GREEN_TREE_FROG:GUT + CREATURE:GREEN_TREE_FROG:STOMACH + CREATURE:GREEN_TREE_FROG:GIZZARD +!CREATURE:GREEN_TREE_FROG:PANCREAS +CREATURE:GREEN_TREE_FROG:SPLEEN +CREATURE:GREEN_TREE_FROG:KIDNEY +#CREATURE:GREEN_TREE_FROG_MAN:MUSCLE + CREATURE:GREEN_TREE_FROG_MAN:EYE +"CREATURE:GREEN_TREE_FROG_MAN:BRAIN +!CREATURE:GREEN_TREE_FROG_MAN:LUNG +"CREATURE:GREEN_TREE_FROG_MAN:HEART +"CREATURE:GREEN_TREE_FROG_MAN:LIVER + CREATURE:GREEN_TREE_FROG_MAN:GUT +$CREATURE:GREEN_TREE_FROG_MAN:STOMACH +$CREATURE:GREEN_TREE_FROG_MAN:GIZZARD +%CREATURE:GREEN_TREE_FROG_MAN:PANCREAS +#CREATURE:GREEN_TREE_FROG_MAN:SPLEEN +#CREATURE:GREEN_TREE_FROG_MAN:KIDNEY +%CREATURE:GIANT_GREEN_TREE_FROG:MUSCLE +"CREATURE:GIANT_GREEN_TREE_FROG:EYE +$CREATURE:GIANT_GREEN_TREE_FROG:BRAIN +#CREATURE:GIANT_GREEN_TREE_FROG:LUNG +$CREATURE:GIANT_GREEN_TREE_FROG:HEART +$CREATURE:GIANT_GREEN_TREE_FROG:LIVER +"CREATURE:GIANT_GREEN_TREE_FROG:GUT +&CREATURE:GIANT_GREEN_TREE_FROG:STOMACH +&CREATURE:GIANT_GREEN_TREE_FROG:GIZZARD +'CREATURE:GIANT_GREEN_TREE_FROG:PANCREAS +%CREATURE:GIANT_GREEN_TREE_FROG:SPLEEN +%CREATURE:GIANT_GREEN_TREE_FROG:KIDNEY +CREATURE:HARE:MUSCLE +CREATURE:HARE:EYE +CREATURE:HARE:BRAIN +CREATURE:HARE:LUNG +CREATURE:HARE:HEART +CREATURE:HARE:LIVER +CREATURE:HARE:GUT +CREATURE:HARE:STOMACH +CREATURE:HARE:GIZZARD +CREATURE:HARE:PANCREAS +CREATURE:HARE:SPLEEN +CREATURE:HARE:KIDNEY +CREATURE:HARE_MAN:MUSCLE +CREATURE:HARE_MAN:EYE +CREATURE:HARE_MAN:BRAIN +CREATURE:HARE_MAN:LUNG +CREATURE:HARE_MAN:HEART +CREATURE:HARE_MAN:LIVER +CREATURE:HARE_MAN:GUT +CREATURE:HARE_MAN:STOMACH +CREATURE:HARE_MAN:GIZZARD +CREATURE:HARE_MAN:PANCREAS +CREATURE:HARE_MAN:SPLEEN +CREATURE:HARE_MAN:KIDNEY +CREATURE:GIANT_HARE:MUSCLE +CREATURE:GIANT_HARE:EYE +CREATURE:GIANT_HARE:BRAIN +CREATURE:GIANT_HARE:LUNG +CREATURE:GIANT_HARE:HEART +CREATURE:GIANT_HARE:LIVER +CREATURE:GIANT_HARE:GUT +CREATURE:GIANT_HARE:STOMACH +CREATURE:GIANT_HARE:GIZZARD +CREATURE:GIANT_HARE:PANCREAS +CREATURE:GIANT_HARE:SPLEEN +CREATURE:GIANT_HARE:KIDNEY +CREATURE:RATTLESNAKE:MUSCLE +CREATURE:RATTLESNAKE:EYE +CREATURE:RATTLESNAKE:BRAIN +CREATURE:RATTLESNAKE:LUNG +CREATURE:RATTLESNAKE:HEART +CREATURE:RATTLESNAKE:LIVER +CREATURE:RATTLESNAKE:GUT +CREATURE:RATTLESNAKE:STOMACH +CREATURE:RATTLESNAKE:GIZZARD +CREATURE:RATTLESNAKE:PANCREAS +CREATURE:RATTLESNAKE:SPLEEN +CREATURE:RATTLESNAKE:KIDNEY +CREATURE:RATTLESNAKE_MAN:MUSCLE +CREATURE:RATTLESNAKE_MAN:EYE +CREATURE:RATTLESNAKE_MAN:BRAIN +CREATURE:RATTLESNAKE_MAN:LUNG +CREATURE:RATTLESNAKE_MAN:HEART +CREATURE:RATTLESNAKE_MAN:LIVER +CREATURE:RATTLESNAKE_MAN:GUT + CREATURE:RATTLESNAKE_MAN:STOMACH + CREATURE:RATTLESNAKE_MAN:GIZZARD +!CREATURE:RATTLESNAKE_MAN:PANCREAS +CREATURE:RATTLESNAKE_MAN:SPLEEN +CREATURE:RATTLESNAKE_MAN:KIDNEY +!CREATURE:GIANT_RATTLESNAKE:MUSCLE +CREATURE:GIANT_RATTLESNAKE:EYE + CREATURE:GIANT_RATTLESNAKE:BRAIN +CREATURE:GIANT_RATTLESNAKE:LUNG + CREATURE:GIANT_RATTLESNAKE:HEART + CREATURE:GIANT_RATTLESNAKE:LIVER +CREATURE:GIANT_RATTLESNAKE:GUT +"CREATURE:GIANT_RATTLESNAKE:STOMACH +"CREATURE:GIANT_RATTLESNAKE:GIZZARD +#CREATURE:GIANT_RATTLESNAKE:PANCREAS +!CREATURE:GIANT_RATTLESNAKE:SPLEEN +!CREATURE:GIANT_RATTLESNAKE:KIDNEY +CREATURE:WEASEL:MUSCLE +CREATURE:WEASEL:EYE +CREATURE:WEASEL:BRAIN +CREATURE:WEASEL:LUNG +CREATURE:WEASEL:HEART +CREATURE:WEASEL:LIVER +CREATURE:WEASEL:GUT +CREATURE:WEASEL:STOMACH +CREATURE:WEASEL:GIZZARD +CREATURE:WEASEL:PANCREAS +CREATURE:WEASEL:SPLEEN +CREATURE:WEASEL:KIDNEY +CREATURE:WEASEL_MAN:MUSCLE +CREATURE:WEASEL_MAN:EYE +CREATURE:WEASEL_MAN:BRAIN +CREATURE:WEASEL_MAN:LUNG +CREATURE:WEASEL_MAN:HEART +CREATURE:WEASEL_MAN:LIVER +CREATURE:WEASEL_MAN:GUT +CREATURE:WEASEL_MAN:STOMACH +CREATURE:WEASEL_MAN:GIZZARD +CREATURE:WEASEL_MAN:PANCREAS +CREATURE:WEASEL_MAN:SPLEEN +CREATURE:WEASEL_MAN:KIDNEY +CREATURE:GIANT_WEASEL:MUSCLE +CREATURE:GIANT_WEASEL:EYE +CREATURE:GIANT_WEASEL:BRAIN +CREATURE:GIANT_WEASEL:LUNG +CREATURE:GIANT_WEASEL:HEART +CREATURE:GIANT_WEASEL:LIVER +CREATURE:GIANT_WEASEL:GUT +CREATURE:GIANT_WEASEL:STOMACH +CREATURE:GIANT_WEASEL:GIZZARD +CREATURE:GIANT_WEASEL:PANCREAS +CREATURE:GIANT_WEASEL:SPLEEN +CREATURE:GIANT_WEASEL:KIDNEY + CREATURE:COPPERHEAD_SNAKE:MUSCLE +CREATURE:COPPERHEAD_SNAKE:EYE +CREATURE:COPPERHEAD_SNAKE:BRAIN +CREATURE:COPPERHEAD_SNAKE:LUNG +CREATURE:COPPERHEAD_SNAKE:HEART +CREATURE:COPPERHEAD_SNAKE:LIVER +CREATURE:COPPERHEAD_SNAKE:GUT +!CREATURE:COPPERHEAD_SNAKE:STOMACH +!CREATURE:COPPERHEAD_SNAKE:GIZZARD +"CREATURE:COPPERHEAD_SNAKE:PANCREAS + CREATURE:COPPERHEAD_SNAKE:SPLEEN + CREATURE:COPPERHEAD_SNAKE:KIDNEY +$CREATURE:COPPERHEAD_SNAKE_MAN:MUSCLE +!CREATURE:COPPERHEAD_SNAKE_MAN:EYE +#CREATURE:COPPERHEAD_SNAKE_MAN:BRAIN +"CREATURE:COPPERHEAD_SNAKE_MAN:LUNG +#CREATURE:COPPERHEAD_SNAKE_MAN:HEART +#CREATURE:COPPERHEAD_SNAKE_MAN:LIVER +!CREATURE:COPPERHEAD_SNAKE_MAN:GUT +%CREATURE:COPPERHEAD_SNAKE_MAN:STOMACH +%CREATURE:COPPERHEAD_SNAKE_MAN:GIZZARD +&CREATURE:COPPERHEAD_SNAKE_MAN:PANCREAS +$CREATURE:COPPERHEAD_SNAKE_MAN:SPLEEN +$CREATURE:COPPERHEAD_SNAKE_MAN:KIDNEY +&CREATURE:GIANT_COPPERHEAD_SNAKE:MUSCLE +#CREATURE:GIANT_COPPERHEAD_SNAKE:EYE +%CREATURE:GIANT_COPPERHEAD_SNAKE:BRAIN +$CREATURE:GIANT_COPPERHEAD_SNAKE:LUNG +%CREATURE:GIANT_COPPERHEAD_SNAKE:HEART +%CREATURE:GIANT_COPPERHEAD_SNAKE:LIVER +#CREATURE:GIANT_COPPERHEAD_SNAKE:GUT +'CREATURE:GIANT_COPPERHEAD_SNAKE:STOMACH +'CREATURE:GIANT_COPPERHEAD_SNAKE:GIZZARD +(CREATURE:GIANT_COPPERHEAD_SNAKE:PANCREAS +&CREATURE:GIANT_COPPERHEAD_SNAKE:SPLEEN +&CREATURE:GIANT_COPPERHEAD_SNAKE:KIDNEY +CREATURE:IBEX:MUSCLE +CREATURE:IBEX:EYE +CREATURE:IBEX:BRAIN +CREATURE:IBEX:LUNG +CREATURE:IBEX:HEART +CREATURE:IBEX:LIVER +CREATURE:IBEX:GUT +CREATURE:IBEX:STOMACH +CREATURE:IBEX:GIZZARD +CREATURE:IBEX:PANCREAS +CREATURE:IBEX:SPLEEN +CREATURE:IBEX:KIDNEY +CREATURE:IBEX_MAN:MUSCLE +CREATURE:IBEX_MAN:EYE +CREATURE:IBEX_MAN:BRAIN +CREATURE:IBEX_MAN:LUNG +CREATURE:IBEX_MAN:HEART +CREATURE:IBEX_MAN:LIVER +CREATURE:IBEX_MAN:GUT +CREATURE:IBEX_MAN:STOMACH +CREATURE:IBEX_MAN:GIZZARD +CREATURE:IBEX_MAN:PANCREAS +CREATURE:IBEX_MAN:SPLEEN +CREATURE:IBEX_MAN:KIDNEY +CREATURE:GIANT_IBEX:MUSCLE +CREATURE:GIANT_IBEX:EYE +CREATURE:GIANT_IBEX:BRAIN +CREATURE:GIANT_IBEX:LUNG +CREATURE:GIANT_IBEX:HEART +CREATURE:GIANT_IBEX:LIVER +CREATURE:GIANT_IBEX:GUT +CREATURE:GIANT_IBEX:STOMACH +CREATURE:GIANT_IBEX:GIZZARD +CREATURE:GIANT_IBEX:PANCREAS +CREATURE:GIANT_IBEX:SPLEEN +CREATURE:GIANT_IBEX:KIDNEY +CREATURE:WOMBAT:MUSCLE +CREATURE:WOMBAT:EYE +CREATURE:WOMBAT:BRAIN +CREATURE:WOMBAT:LUNG +CREATURE:WOMBAT:HEART +CREATURE:WOMBAT:LIVER +CREATURE:WOMBAT:GUT +CREATURE:WOMBAT:STOMACH +CREATURE:WOMBAT:GIZZARD +CREATURE:WOMBAT:PANCREAS +CREATURE:WOMBAT:SPLEEN +CREATURE:WOMBAT:KIDNEY +CREATURE:WOMBAT_MAN:MUSCLE +CREATURE:WOMBAT_MAN:EYE +CREATURE:WOMBAT_MAN:BRAIN +CREATURE:WOMBAT_MAN:LUNG +CREATURE:WOMBAT_MAN:HEART +CREATURE:WOMBAT_MAN:LIVER +CREATURE:WOMBAT_MAN:GUT +CREATURE:WOMBAT_MAN:STOMACH +CREATURE:WOMBAT_MAN:GIZZARD +CREATURE:WOMBAT_MAN:PANCREAS +CREATURE:WOMBAT_MAN:SPLEEN +CREATURE:WOMBAT_MAN:KIDNEY +CREATURE:GIANT_WOMBAT:MUSCLE +CREATURE:GIANT_WOMBAT:EYE +CREATURE:GIANT_WOMBAT:BRAIN +CREATURE:GIANT_WOMBAT:LUNG +CREATURE:GIANT_WOMBAT:HEART +CREATURE:GIANT_WOMBAT:LIVER +CREATURE:GIANT_WOMBAT:GUT +CREATURE:GIANT_WOMBAT:STOMACH +CREATURE:GIANT_WOMBAT:GIZZARD +CREATURE:GIANT_WOMBAT:PANCREAS +CREATURE:GIANT_WOMBAT:SPLEEN +CREATURE:GIANT_WOMBAT:KIDNEY +CREATURE:DINGO:MUSCLE +CREATURE:DINGO:EYE +CREATURE:DINGO:BRAIN +CREATURE:DINGO:LUNG +CREATURE:DINGO:HEART +CREATURE:DINGO:LIVER +CREATURE:DINGO:GUT +CREATURE:DINGO:STOMACH +CREATURE:DINGO:GIZZARD +CREATURE:DINGO:PANCREAS +CREATURE:DINGO:SPLEEN +CREATURE:DINGO:KIDNEY +CREATURE:DINGO_MAN:MUSCLE +CREATURE:DINGO_MAN:EYE +CREATURE:DINGO_MAN:BRAIN +CREATURE:DINGO_MAN:LUNG +CREATURE:DINGO_MAN:HEART +CREATURE:DINGO_MAN:LIVER +CREATURE:DINGO_MAN:GUT +CREATURE:DINGO_MAN:STOMACH +CREATURE:DINGO_MAN:GIZZARD +CREATURE:DINGO_MAN:PANCREAS +CREATURE:DINGO_MAN:SPLEEN +CREATURE:DINGO_MAN:KIDNEY +CREATURE:GIANT_DINGO:MUSCLE +CREATURE:GIANT_DINGO:EYE +CREATURE:GIANT_DINGO:BRAIN +CREATURE:GIANT_DINGO:LUNG +CREATURE:GIANT_DINGO:HEART +CREATURE:GIANT_DINGO:LIVER +CREATURE:GIANT_DINGO:GUT +CREATURE:GIANT_DINGO:STOMACH +CREATURE:GIANT_DINGO:GIZZARD +CREATURE:GIANT_DINGO:PANCREAS +CREATURE:GIANT_DINGO:SPLEEN +CREATURE:GIANT_DINGO:KIDNEY +CREATURE:COATI:MUSCLE +CREATURE:COATI:EYE +CREATURE:COATI:BRAIN +CREATURE:COATI:LUNG +CREATURE:COATI:HEART +CREATURE:COATI:LIVER +CREATURE:COATI:GUT +CREATURE:COATI:STOMACH +CREATURE:COATI:GIZZARD +CREATURE:COATI:PANCREAS +CREATURE:COATI:SPLEEN +CREATURE:COATI:KIDNEY +CREATURE:COATI_MAN:MUSCLE +CREATURE:COATI_MAN:EYE +CREATURE:COATI_MAN:BRAIN +CREATURE:COATI_MAN:LUNG +CREATURE:COATI_MAN:HEART +CREATURE:COATI_MAN:LIVER +CREATURE:COATI_MAN:GUT +CREATURE:COATI_MAN:STOMACH +CREATURE:COATI_MAN:GIZZARD +CREATURE:COATI_MAN:PANCREAS +CREATURE:COATI_MAN:SPLEEN +CREATURE:COATI_MAN:KIDNEY +CREATURE:GIANT_COATI:MUSCLE +CREATURE:GIANT_COATI:EYE +CREATURE:GIANT_COATI:BRAIN +CREATURE:GIANT_COATI:LUNG +CREATURE:GIANT_COATI:HEART +CREATURE:GIANT_COATI:LIVER +CREATURE:GIANT_COATI:GUT +CREATURE:GIANT_COATI:STOMACH +CREATURE:GIANT_COATI:GIZZARD +CREATURE:GIANT_COATI:PANCREAS +CREATURE:GIANT_COATI:SPLEEN +CREATURE:GIANT_COATI:KIDNEY +CREATURE:OPOSSUM:MUSCLE +CREATURE:OPOSSUM:EYE +CREATURE:OPOSSUM:BRAIN +CREATURE:OPOSSUM:LUNG +CREATURE:OPOSSUM:HEART +CREATURE:OPOSSUM:LIVER +CREATURE:OPOSSUM:GUT +CREATURE:OPOSSUM:STOMACH +CREATURE:OPOSSUM:GIZZARD +CREATURE:OPOSSUM:PANCREAS +CREATURE:OPOSSUM:SPLEEN +CREATURE:OPOSSUM:KIDNEY +CREATURE:OPOSSUM_MAN:MUSCLE +CREATURE:OPOSSUM_MAN:EYE +CREATURE:OPOSSUM_MAN:BRAIN +CREATURE:OPOSSUM_MAN:LUNG +CREATURE:OPOSSUM_MAN:HEART +CREATURE:OPOSSUM_MAN:LIVER +CREATURE:OPOSSUM_MAN:GUT +CREATURE:OPOSSUM_MAN:STOMACH +CREATURE:OPOSSUM_MAN:GIZZARD +CREATURE:OPOSSUM_MAN:PANCREAS +CREATURE:OPOSSUM_MAN:SPLEEN +CREATURE:OPOSSUM_MAN:KIDNEY +CREATURE:GIANT_OPOSSUM:MUSCLE +CREATURE:GIANT_OPOSSUM:EYE +CREATURE:GIANT_OPOSSUM:BRAIN +CREATURE:GIANT_OPOSSUM:LUNG +CREATURE:GIANT_OPOSSUM:HEART +CREATURE:GIANT_OPOSSUM:LIVER +CREATURE:GIANT_OPOSSUM:GUT +CREATURE:GIANT_OPOSSUM:STOMACH +CREATURE:GIANT_OPOSSUM:GIZZARD +CREATURE:GIANT_OPOSSUM:PANCREAS +CREATURE:GIANT_OPOSSUM:SPLEEN +CREATURE:GIANT_OPOSSUM:KIDNEY +CREATURE:MONGOOSE:MUSCLE +CREATURE:MONGOOSE:EYE +CREATURE:MONGOOSE:BRAIN +CREATURE:MONGOOSE:LUNG +CREATURE:MONGOOSE:HEART +CREATURE:MONGOOSE:LIVER +CREATURE:MONGOOSE:GUT +CREATURE:MONGOOSE:STOMACH +CREATURE:MONGOOSE:GIZZARD +CREATURE:MONGOOSE:PANCREAS +CREATURE:MONGOOSE:SPLEEN +CREATURE:MONGOOSE:KIDNEY +CREATURE:MONGOOSE_MAN:MUSCLE +CREATURE:MONGOOSE_MAN:EYE +CREATURE:MONGOOSE_MAN:BRAIN +CREATURE:MONGOOSE_MAN:LUNG +CREATURE:MONGOOSE_MAN:HEART +CREATURE:MONGOOSE_MAN:LIVER +CREATURE:MONGOOSE_MAN:GUT +CREATURE:MONGOOSE_MAN:STOMACH +CREATURE:MONGOOSE_MAN:GIZZARD +CREATURE:MONGOOSE_MAN:PANCREAS +CREATURE:MONGOOSE_MAN:SPLEEN +CREATURE:MONGOOSE_MAN:KIDNEY +CREATURE:GIANT_MONGOOSE:MUSCLE +CREATURE:GIANT_MONGOOSE:EYE +CREATURE:GIANT_MONGOOSE:BRAIN +CREATURE:GIANT_MONGOOSE:LUNG +CREATURE:GIANT_MONGOOSE:HEART +CREATURE:GIANT_MONGOOSE:LIVER +CREATURE:GIANT_MONGOOSE:GUT +CREATURE:GIANT_MONGOOSE:STOMACH +CREATURE:GIANT_MONGOOSE:GIZZARD + CREATURE:GIANT_MONGOOSE:PANCREAS +CREATURE:GIANT_MONGOOSE:SPLEEN +CREATURE:GIANT_MONGOOSE:KIDNEY +CREATURE:HYENA:MUSCLE +CREATURE:HYENA:EYE +CREATURE:HYENA:BRAIN +CREATURE:HYENA:LUNG +CREATURE:HYENA:HEART +CREATURE:HYENA:LIVER +CREATURE:HYENA:GUT +CREATURE:HYENA:STOMACH +CREATURE:HYENA:GIZZARD +CREATURE:HYENA:PANCREAS +CREATURE:HYENA:SPLEEN +CREATURE:HYENA:KIDNEY +CREATURE:HYENA_MAN:MUSCLE +CREATURE:HYENA_MAN:EYE +CREATURE:HYENA_MAN:BRAIN +CREATURE:HYENA_MAN:LUNG +CREATURE:HYENA_MAN:HEART +CREATURE:HYENA_MAN:LIVER +CREATURE:HYENA_MAN:GUT +CREATURE:HYENA_MAN:STOMACH +CREATURE:HYENA_MAN:GIZZARD +CREATURE:HYENA_MAN:PANCREAS +CREATURE:HYENA_MAN:SPLEEN +CREATURE:HYENA_MAN:KIDNEY +CREATURE:GIANT_HYENA:MUSCLE +CREATURE:GIANT_HYENA:EYE +CREATURE:GIANT_HYENA:BRAIN +CREATURE:GIANT_HYENA:LUNG +CREATURE:GIANT_HYENA:HEART +CREATURE:GIANT_HYENA:LIVER +CREATURE:GIANT_HYENA:GUT +CREATURE:GIANT_HYENA:STOMACH +CREATURE:GIANT_HYENA:GIZZARD +CREATURE:GIANT_HYENA:PANCREAS +CREATURE:GIANT_HYENA:SPLEEN +CREATURE:GIANT_HYENA:KIDNEY +CREATURE:ANACONDA:MUSCLE +CREATURE:ANACONDA:EYE +CREATURE:ANACONDA:BRAIN +CREATURE:ANACONDA:LUNG +CREATURE:ANACONDA:HEART +CREATURE:ANACONDA:LIVER +CREATURE:ANACONDA:GUT +CREATURE:ANACONDA:STOMACH +CREATURE:ANACONDA:GIZZARD +CREATURE:ANACONDA:PANCREAS +CREATURE:ANACONDA:SPLEEN +CREATURE:ANACONDA:KIDNEY +CREATURE:ANACONDA_MAN:MUSCLE +CREATURE:ANACONDA_MAN:EYE +CREATURE:ANACONDA_MAN:BRAIN +CREATURE:ANACONDA_MAN:LUNG +CREATURE:ANACONDA_MAN:HEART +CREATURE:ANACONDA_MAN:LIVER +CREATURE:ANACONDA_MAN:GUT +CREATURE:ANACONDA_MAN:STOMACH +CREATURE:ANACONDA_MAN:GIZZARD +CREATURE:ANACONDA_MAN:PANCREAS +CREATURE:ANACONDA_MAN:SPLEEN +CREATURE:ANACONDA_MAN:KIDNEY +CREATURE:GIANT_ANACONDA:MUSCLE +CREATURE:GIANT_ANACONDA:EYE +CREATURE:GIANT_ANACONDA:BRAIN +CREATURE:GIANT_ANACONDA:LUNG +CREATURE:GIANT_ANACONDA:HEART +CREATURE:GIANT_ANACONDA:LIVER +CREATURE:GIANT_ANACONDA:GUT +CREATURE:GIANT_ANACONDA:STOMACH +CREATURE:GIANT_ANACONDA:GIZZARD + CREATURE:GIANT_ANACONDA:PANCREAS +CREATURE:GIANT_ANACONDA:SPLEEN +CREATURE:GIANT_ANACONDA:KIDNEY +CREATURE:MONITOR_LIZARD:MUSCLE +CREATURE:MONITOR_LIZARD:EYE +CREATURE:MONITOR_LIZARD:BRAIN +CREATURE:MONITOR_LIZARD:LUNG +CREATURE:MONITOR_LIZARD:HEART +CREATURE:MONITOR_LIZARD:LIVER +CREATURE:MONITOR_LIZARD:GUT +CREATURE:MONITOR_LIZARD:STOMACH +CREATURE:MONITOR_LIZARD:GIZZARD + CREATURE:MONITOR_LIZARD:PANCREAS +CREATURE:MONITOR_LIZARD:SPLEEN +CREATURE:MONITOR_LIZARD:KIDNEY +"CREATURE:MONITOR_LIZARD_MAN:MUSCLE +CREATURE:MONITOR_LIZARD_MAN:EYE +!CREATURE:MONITOR_LIZARD_MAN:BRAIN + CREATURE:MONITOR_LIZARD_MAN:LUNG +!CREATURE:MONITOR_LIZARD_MAN:HEART +!CREATURE:MONITOR_LIZARD_MAN:LIVER +CREATURE:MONITOR_LIZARD_MAN:GUT +#CREATURE:MONITOR_LIZARD_MAN:STOMACH +#CREATURE:MONITOR_LIZARD_MAN:GIZZARD +$CREATURE:MONITOR_LIZARD_MAN:PANCREAS +"CREATURE:MONITOR_LIZARD_MAN:SPLEEN +"CREATURE:MONITOR_LIZARD_MAN:KIDNEY +$CREATURE:GIANT_MONITOR_LIZARD:MUSCLE +!CREATURE:GIANT_MONITOR_LIZARD:EYE +#CREATURE:GIANT_MONITOR_LIZARD:BRAIN +"CREATURE:GIANT_MONITOR_LIZARD:LUNG +#CREATURE:GIANT_MONITOR_LIZARD:HEART +#CREATURE:GIANT_MONITOR_LIZARD:LIVER +!CREATURE:GIANT_MONITOR_LIZARD:GUT +%CREATURE:GIANT_MONITOR_LIZARD:STOMACH +%CREATURE:GIANT_MONITOR_LIZARD:GIZZARD +&CREATURE:GIANT_MONITOR_LIZARD:PANCREAS +$CREATURE:GIANT_MONITOR_LIZARD:SPLEEN +$CREATURE:GIANT_MONITOR_LIZARD:KIDNEY +CREATURE:KING_COBRA:MUSCLE +CREATURE:KING_COBRA:EYE +CREATURE:KING_COBRA:BRAIN +CREATURE:KING_COBRA:LUNG +CREATURE:KING_COBRA:HEART +CREATURE:KING_COBRA:LIVER +CREATURE:KING_COBRA:GUT +CREATURE:KING_COBRA:STOMACH +CREATURE:KING_COBRA:GIZZARD +CREATURE:KING_COBRA:PANCREAS +CREATURE:KING_COBRA:SPLEEN +CREATURE:KING_COBRA:KIDNEY +CREATURE:KING_COBRA_MAN:MUSCLE +CREATURE:KING_COBRA_MAN:EYE +CREATURE:KING_COBRA_MAN:BRAIN +CREATURE:KING_COBRA_MAN:LUNG +CREATURE:KING_COBRA_MAN:HEART +CREATURE:KING_COBRA_MAN:LIVER +CREATURE:KING_COBRA_MAN:GUT +CREATURE:KING_COBRA_MAN:STOMACH +CREATURE:KING_COBRA_MAN:GIZZARD + CREATURE:KING_COBRA_MAN:PANCREAS +CREATURE:KING_COBRA_MAN:SPLEEN +CREATURE:KING_COBRA_MAN:KIDNEY + CREATURE:GIANT_KING_COBRA:MUSCLE +CREATURE:GIANT_KING_COBRA:EYE +CREATURE:GIANT_KING_COBRA:BRAIN +CREATURE:GIANT_KING_COBRA:LUNG +CREATURE:GIANT_KING_COBRA:HEART +CREATURE:GIANT_KING_COBRA:LIVER +CREATURE:GIANT_KING_COBRA:GUT +!CREATURE:GIANT_KING_COBRA:STOMACH +!CREATURE:GIANT_KING_COBRA:GIZZARD +"CREATURE:GIANT_KING_COBRA:PANCREAS + CREATURE:GIANT_KING_COBRA:SPLEEN + CREATURE:GIANT_KING_COBRA:KIDNEY +CREATURE:OCELOT:MUSCLE +CREATURE:OCELOT:EYE +CREATURE:OCELOT:BRAIN +CREATURE:OCELOT:LUNG +CREATURE:OCELOT:HEART +CREATURE:OCELOT:LIVER +CREATURE:OCELOT:GUT +CREATURE:OCELOT:STOMACH +CREATURE:OCELOT:GIZZARD +CREATURE:OCELOT:PANCREAS +CREATURE:OCELOT:SPLEEN +CREATURE:OCELOT:KIDNEY +CREATURE:OCELOT_MAN:MUSCLE +CREATURE:OCELOT_MAN:EYE +CREATURE:OCELOT_MAN:BRAIN +CREATURE:OCELOT_MAN:LUNG +CREATURE:OCELOT_MAN:HEART +CREATURE:OCELOT_MAN:LIVER +CREATURE:OCELOT_MAN:GUT +CREATURE:OCELOT_MAN:STOMACH +CREATURE:OCELOT_MAN:GIZZARD +CREATURE:OCELOT_MAN:PANCREAS +CREATURE:OCELOT_MAN:SPLEEN +CREATURE:OCELOT_MAN:KIDNEY +CREATURE:GIANT_OCELOT:MUSCLE +CREATURE:GIANT_OCELOT:EYE +CREATURE:GIANT_OCELOT:BRAIN +CREATURE:GIANT_OCELOT:LUNG +CREATURE:GIANT_OCELOT:HEART +CREATURE:GIANT_OCELOT:LIVER +CREATURE:GIANT_OCELOT:GUT +CREATURE:GIANT_OCELOT:STOMACH +CREATURE:GIANT_OCELOT:GIZZARD +CREATURE:GIANT_OCELOT:PANCREAS +CREATURE:GIANT_OCELOT:SPLEEN +CREATURE:GIANT_OCELOT:KIDNEY +CREATURE:JACKAL:MUSCLE +CREATURE:JACKAL:EYE +CREATURE:JACKAL:BRAIN +CREATURE:JACKAL:LUNG +CREATURE:JACKAL:HEART +CREATURE:JACKAL:LIVER +CREATURE:JACKAL:GUT +CREATURE:JACKAL:STOMACH +CREATURE:JACKAL:GIZZARD +CREATURE:JACKAL:PANCREAS +CREATURE:JACKAL:SPLEEN +CREATURE:JACKAL:KIDNEY +CREATURE:JACKAL_MAN:MUSCLE +CREATURE:JACKAL_MAN:EYE +CREATURE:JACKAL_MAN:BRAIN +CREATURE:JACKAL_MAN:LUNG +CREATURE:JACKAL_MAN:HEART +CREATURE:JACKAL_MAN:LIVER +CREATURE:JACKAL_MAN:GUT +CREATURE:JACKAL_MAN:STOMACH +CREATURE:JACKAL_MAN:GIZZARD +CREATURE:JACKAL_MAN:PANCREAS +CREATURE:JACKAL_MAN:SPLEEN +CREATURE:JACKAL_MAN:KIDNEY +CREATURE:GIANT_JACKAL:MUSCLE +CREATURE:GIANT_JACKAL:EYE +CREATURE:GIANT_JACKAL:BRAIN +CREATURE:GIANT_JACKAL:LUNG +CREATURE:GIANT_JACKAL:HEART +CREATURE:GIANT_JACKAL:LIVER +CREATURE:GIANT_JACKAL:GUT +CREATURE:GIANT_JACKAL:STOMACH +CREATURE:GIANT_JACKAL:GIZZARD +CREATURE:GIANT_JACKAL:PANCREAS +CREATURE:GIANT_JACKAL:SPLEEN +CREATURE:GIANT_JACKAL:KIDNEY +CREATURE:CAPUCHIN:MUSCLE +CREATURE:CAPUCHIN:EYE +CREATURE:CAPUCHIN:BRAIN +CREATURE:CAPUCHIN:LUNG +CREATURE:CAPUCHIN:HEART +CREATURE:CAPUCHIN:LIVER +CREATURE:CAPUCHIN:GUT +CREATURE:CAPUCHIN:STOMACH +CREATURE:CAPUCHIN:GIZZARD +CREATURE:CAPUCHIN:PANCREAS +CREATURE:CAPUCHIN:SPLEEN +CREATURE:CAPUCHIN:KIDNEY +CREATURE:CAPUCHIN_MAN:MUSCLE +CREATURE:CAPUCHIN_MAN:EYE +CREATURE:CAPUCHIN_MAN:BRAIN +CREATURE:CAPUCHIN_MAN:LUNG +CREATURE:CAPUCHIN_MAN:HEART +CREATURE:CAPUCHIN_MAN:LIVER +CREATURE:CAPUCHIN_MAN:GUT +CREATURE:CAPUCHIN_MAN:STOMACH +CREATURE:CAPUCHIN_MAN:GIZZARD +CREATURE:CAPUCHIN_MAN:PANCREAS +CREATURE:CAPUCHIN_MAN:SPLEEN +CREATURE:CAPUCHIN_MAN:KIDNEY +CREATURE:GIANT_CAPUCHIN:MUSCLE +CREATURE:GIANT_CAPUCHIN:EYE +CREATURE:GIANT_CAPUCHIN:BRAIN +CREATURE:GIANT_CAPUCHIN:LUNG +CREATURE:GIANT_CAPUCHIN:HEART +CREATURE:GIANT_CAPUCHIN:LIVER +CREATURE:GIANT_CAPUCHIN:GUT +CREATURE:GIANT_CAPUCHIN:STOMACH +CREATURE:GIANT_CAPUCHIN:GIZZARD + CREATURE:GIANT_CAPUCHIN:PANCREAS +CREATURE:GIANT_CAPUCHIN:SPLEEN +CREATURE:GIANT_CAPUCHIN:KIDNEY +CREATURE:SLOTH:MUSCLE +CREATURE:SLOTH:EYE +CREATURE:SLOTH:BRAIN +CREATURE:SLOTH:LUNG +CREATURE:SLOTH:HEART +CREATURE:SLOTH:LIVER +CREATURE:SLOTH:GUT +CREATURE:SLOTH:STOMACH +CREATURE:SLOTH:GIZZARD +CREATURE:SLOTH:PANCREAS +CREATURE:SLOTH:SPLEEN +CREATURE:SLOTH:KIDNEY +CREATURE:SLOTH_MAN:MUSCLE +CREATURE:SLOTH_MAN:EYE +CREATURE:SLOTH_MAN:BRAIN +CREATURE:SLOTH_MAN:LUNG +CREATURE:SLOTH_MAN:HEART +CREATURE:SLOTH_MAN:LIVER +CREATURE:SLOTH_MAN:GUT +CREATURE:SLOTH_MAN:STOMACH +CREATURE:SLOTH_MAN:GIZZARD +CREATURE:SLOTH_MAN:PANCREAS +CREATURE:SLOTH_MAN:SPLEEN +CREATURE:SLOTH_MAN:KIDNEY +CREATURE:GIANT_SLOTH:MUSCLE +CREATURE:GIANT_SLOTH:EYE +CREATURE:GIANT_SLOTH:BRAIN +CREATURE:GIANT_SLOTH:LUNG +CREATURE:GIANT_SLOTH:HEART +CREATURE:GIANT_SLOTH:LIVER +CREATURE:GIANT_SLOTH:GUT +CREATURE:GIANT_SLOTH:STOMACH +CREATURE:GIANT_SLOTH:GIZZARD +CREATURE:GIANT_SLOTH:PANCREAS +CREATURE:GIANT_SLOTH:SPLEEN +CREATURE:GIANT_SLOTH:KIDNEY +CREATURE:SPIDER_MONKEY:MUSCLE +CREATURE:SPIDER_MONKEY:EYE +CREATURE:SPIDER_MONKEY:BRAIN +CREATURE:SPIDER_MONKEY:LUNG +CREATURE:SPIDER_MONKEY:HEART +CREATURE:SPIDER_MONKEY:LIVER +CREATURE:SPIDER_MONKEY:GUT +CREATURE:SPIDER_MONKEY:STOMACH +CREATURE:SPIDER_MONKEY:GIZZARD +CREATURE:SPIDER_MONKEY:PANCREAS +CREATURE:SPIDER_MONKEY:SPLEEN +CREATURE:SPIDER_MONKEY:KIDNEY +!CREATURE:SPIDER_MONKEY_MAN:MUSCLE +CREATURE:SPIDER_MONKEY_MAN:EYE + CREATURE:SPIDER_MONKEY_MAN:BRAIN +CREATURE:SPIDER_MONKEY_MAN:LUNG + CREATURE:SPIDER_MONKEY_MAN:HEART + CREATURE:SPIDER_MONKEY_MAN:LIVER +CREATURE:SPIDER_MONKEY_MAN:GUT +"CREATURE:SPIDER_MONKEY_MAN:STOMACH +"CREATURE:SPIDER_MONKEY_MAN:GIZZARD +#CREATURE:SPIDER_MONKEY_MAN:PANCREAS +!CREATURE:SPIDER_MONKEY_MAN:SPLEEN +!CREATURE:SPIDER_MONKEY_MAN:KIDNEY +#CREATURE:GIANT_SPIDER_MONKEY:MUSCLE + CREATURE:GIANT_SPIDER_MONKEY:EYE +"CREATURE:GIANT_SPIDER_MONKEY:BRAIN +!CREATURE:GIANT_SPIDER_MONKEY:LUNG +"CREATURE:GIANT_SPIDER_MONKEY:HEART +"CREATURE:GIANT_SPIDER_MONKEY:LIVER + CREATURE:GIANT_SPIDER_MONKEY:GUT +$CREATURE:GIANT_SPIDER_MONKEY:STOMACH +$CREATURE:GIANT_SPIDER_MONKEY:GIZZARD +%CREATURE:GIANT_SPIDER_MONKEY:PANCREAS +#CREATURE:GIANT_SPIDER_MONKEY:SPLEEN +#CREATURE:GIANT_SPIDER_MONKEY:KIDNEY +CREATURE:PANGOLIN:MUSCLE +CREATURE:PANGOLIN:EYE +CREATURE:PANGOLIN:BRAIN +CREATURE:PANGOLIN:LUNG +CREATURE:PANGOLIN:HEART +CREATURE:PANGOLIN:LIVER +CREATURE:PANGOLIN:GUT +CREATURE:PANGOLIN:STOMACH +CREATURE:PANGOLIN:GIZZARD +CREATURE:PANGOLIN:PANCREAS +CREATURE:PANGOLIN:SPLEEN +CREATURE:PANGOLIN:KIDNEY +CREATURE:PANGOLIN_MAN:MUSCLE +CREATURE:PANGOLIN_MAN:EYE +CREATURE:PANGOLIN_MAN:BRAIN +CREATURE:PANGOLIN_MAN:LUNG +CREATURE:PANGOLIN_MAN:HEART +CREATURE:PANGOLIN_MAN:LIVER +CREATURE:PANGOLIN_MAN:GUT +CREATURE:PANGOLIN_MAN:STOMACH +CREATURE:PANGOLIN_MAN:GIZZARD +CREATURE:PANGOLIN_MAN:PANCREAS +CREATURE:PANGOLIN_MAN:SPLEEN +CREATURE:PANGOLIN_MAN:KIDNEY +CREATURE:GIANT_PANGOLIN:MUSCLE +CREATURE:GIANT_PANGOLIN:EYE +CREATURE:GIANT_PANGOLIN:BRAIN +CREATURE:GIANT_PANGOLIN:LUNG +CREATURE:GIANT_PANGOLIN:HEART +CREATURE:GIANT_PANGOLIN:LIVER +CREATURE:GIANT_PANGOLIN:GUT +CREATURE:GIANT_PANGOLIN:STOMACH +CREATURE:GIANT_PANGOLIN:GIZZARD + CREATURE:GIANT_PANGOLIN:PANCREAS +CREATURE:GIANT_PANGOLIN:SPLEEN +CREATURE:GIANT_PANGOLIN:KIDNEY +CREATURE:BLACK_MAMBA:MUSCLE +CREATURE:BLACK_MAMBA:EYE +CREATURE:BLACK_MAMBA:BRAIN +CREATURE:BLACK_MAMBA:LUNG +CREATURE:BLACK_MAMBA:HEART +CREATURE:BLACK_MAMBA:LIVER +CREATURE:BLACK_MAMBA:GUT +CREATURE:BLACK_MAMBA:STOMACH +CREATURE:BLACK_MAMBA:GIZZARD +CREATURE:BLACK_MAMBA:PANCREAS +CREATURE:BLACK_MAMBA:SPLEEN +CREATURE:BLACK_MAMBA:KIDNEY +CREATURE:BLACK_MAMBA_MAN:MUSCLE +CREATURE:BLACK_MAMBA_MAN:EYE +CREATURE:BLACK_MAMBA_MAN:BRAIN +CREATURE:BLACK_MAMBA_MAN:LUNG +CREATURE:BLACK_MAMBA_MAN:HEART +CREATURE:BLACK_MAMBA_MAN:LIVER +CREATURE:BLACK_MAMBA_MAN:GUT + CREATURE:BLACK_MAMBA_MAN:STOMACH + CREATURE:BLACK_MAMBA_MAN:GIZZARD +!CREATURE:BLACK_MAMBA_MAN:PANCREAS +CREATURE:BLACK_MAMBA_MAN:SPLEEN +CREATURE:BLACK_MAMBA_MAN:KIDNEY +!CREATURE:GIANT_BLACK_MAMBA:MUSCLE +CREATURE:GIANT_BLACK_MAMBA:EYE + CREATURE:GIANT_BLACK_MAMBA:BRAIN +CREATURE:GIANT_BLACK_MAMBA:LUNG + CREATURE:GIANT_BLACK_MAMBA:HEART + CREATURE:GIANT_BLACK_MAMBA:LIVER +CREATURE:GIANT_BLACK_MAMBA:GUT +"CREATURE:GIANT_BLACK_MAMBA:STOMACH +"CREATURE:GIANT_BLACK_MAMBA:GIZZARD +#CREATURE:GIANT_BLACK_MAMBA:PANCREAS +!CREATURE:GIANT_BLACK_MAMBA:SPLEEN +!CREATURE:GIANT_BLACK_MAMBA:KIDNEY +CREATURE:BEAR_SLOTH:MUSCLE +CREATURE:BEAR_SLOTH:EYE +CREATURE:BEAR_SLOTH:BRAIN +CREATURE:BEAR_SLOTH:LUNG +CREATURE:BEAR_SLOTH:HEART +CREATURE:BEAR_SLOTH:LIVER +CREATURE:BEAR_SLOTH:GUT +CREATURE:BEAR_SLOTH:STOMACH +CREATURE:BEAR_SLOTH:GIZZARD +CREATURE:BEAR_SLOTH:PANCREAS +CREATURE:BEAR_SLOTH:SPLEEN +CREATURE:BEAR_SLOTH:KIDNEY +CREATURE:SLOTH_BEAR_MAN:MUSCLE +CREATURE:SLOTH_BEAR_MAN:EYE +CREATURE:SLOTH_BEAR_MAN:BRAIN +CREATURE:SLOTH_BEAR_MAN:LUNG +CREATURE:SLOTH_BEAR_MAN:HEART +CREATURE:SLOTH_BEAR_MAN:LIVER +CREATURE:SLOTH_BEAR_MAN:GUT +CREATURE:SLOTH_BEAR_MAN:STOMACH +CREATURE:SLOTH_BEAR_MAN:GIZZARD + CREATURE:SLOTH_BEAR_MAN:PANCREAS +CREATURE:SLOTH_BEAR_MAN:SPLEEN +CREATURE:SLOTH_BEAR_MAN:KIDNEY + CREATURE:GIANT_SLOTH_BEAR:MUSCLE +CREATURE:GIANT_SLOTH_BEAR:EYE +CREATURE:GIANT_SLOTH_BEAR:BRAIN +CREATURE:GIANT_SLOTH_BEAR:LUNG +CREATURE:GIANT_SLOTH_BEAR:HEART +CREATURE:GIANT_SLOTH_BEAR:LIVER +CREATURE:GIANT_SLOTH_BEAR:GUT +!CREATURE:GIANT_SLOTH_BEAR:STOMACH +!CREATURE:GIANT_SLOTH_BEAR:GIZZARD +"CREATURE:GIANT_SLOTH_BEAR:PANCREAS + CREATURE:GIANT_SLOTH_BEAR:SPLEEN + CREATURE:GIANT_SLOTH_BEAR:KIDNEY +CREATURE:AYE-AYE:MUSCLE +CREATURE:AYE-AYE:EYE +CREATURE:AYE-AYE:BRAIN +CREATURE:AYE-AYE:LUNG +CREATURE:AYE-AYE:HEART +CREATURE:AYE-AYE:LIVER +CREATURE:AYE-AYE:GUT +CREATURE:AYE-AYE:STOMACH +CREATURE:AYE-AYE:GIZZARD +CREATURE:AYE-AYE:PANCREAS +CREATURE:AYE-AYE:SPLEEN +CREATURE:AYE-AYE:KIDNEY +CREATURE:AYE-AYE_MAN:MUSCLE +CREATURE:AYE-AYE_MAN:EYE +CREATURE:AYE-AYE_MAN:BRAIN +CREATURE:AYE-AYE_MAN:LUNG +CREATURE:AYE-AYE_MAN:HEART +CREATURE:AYE-AYE_MAN:LIVER +CREATURE:AYE-AYE_MAN:GUT +CREATURE:AYE-AYE_MAN:STOMACH +CREATURE:AYE-AYE_MAN:GIZZARD +CREATURE:AYE-AYE_MAN:PANCREAS +CREATURE:AYE-AYE_MAN:SPLEEN +CREATURE:AYE-AYE_MAN:KIDNEY +CREATURE:GIANT_AYE-AYE:MUSCLE +CREATURE:GIANT_AYE-AYE:EYE +CREATURE:GIANT_AYE-AYE:BRAIN +CREATURE:GIANT_AYE-AYE:LUNG +CREATURE:GIANT_AYE-AYE:HEART +CREATURE:GIANT_AYE-AYE:LIVER +CREATURE:GIANT_AYE-AYE:GUT +CREATURE:GIANT_AYE-AYE:STOMACH +CREATURE:GIANT_AYE-AYE:GIZZARD +CREATURE:GIANT_AYE-AYE:PANCREAS +CREATURE:GIANT_AYE-AYE:SPLEEN +CREATURE:GIANT_AYE-AYE:KIDNEY +CREATURE:BUSHMASTER:MUSCLE +CREATURE:BUSHMASTER:EYE +CREATURE:BUSHMASTER:BRAIN +CREATURE:BUSHMASTER:LUNG +CREATURE:BUSHMASTER:HEART +CREATURE:BUSHMASTER:LIVER +CREATURE:BUSHMASTER:GUT +CREATURE:BUSHMASTER:STOMACH +CREATURE:BUSHMASTER:GIZZARD +CREATURE:BUSHMASTER:PANCREAS +CREATURE:BUSHMASTER:SPLEEN +CREATURE:BUSHMASTER:KIDNEY +CREATURE:BUSHMASTER_MAN:MUSCLE +CREATURE:BUSHMASTER_MAN:EYE +CREATURE:BUSHMASTER_MAN:BRAIN +CREATURE:BUSHMASTER_MAN:LUNG +CREATURE:BUSHMASTER_MAN:HEART +CREATURE:BUSHMASTER_MAN:LIVER +CREATURE:BUSHMASTER_MAN:GUT +CREATURE:BUSHMASTER_MAN:STOMACH +CREATURE:BUSHMASTER_MAN:GIZZARD + CREATURE:BUSHMASTER_MAN:PANCREAS +CREATURE:BUSHMASTER_MAN:SPLEEN +CREATURE:BUSHMASTER_MAN:KIDNEY + CREATURE:GIANT_BUSHMASTER:MUSCLE +CREATURE:GIANT_BUSHMASTER:EYE +CREATURE:GIANT_BUSHMASTER:BRAIN +CREATURE:GIANT_BUSHMASTER:LUNG +CREATURE:GIANT_BUSHMASTER:HEART +CREATURE:GIANT_BUSHMASTER:LIVER +CREATURE:GIANT_BUSHMASTER:GUT +!CREATURE:GIANT_BUSHMASTER:STOMACH +!CREATURE:GIANT_BUSHMASTER:GIZZARD +"CREATURE:GIANT_BUSHMASTER:PANCREAS + CREATURE:GIANT_BUSHMASTER:SPLEEN + CREATURE:GIANT_BUSHMASTER:KIDNEY +CREATURE:PYTHON:MUSCLE +CREATURE:PYTHON:EYE +CREATURE:PYTHON:BRAIN +CREATURE:PYTHON:LUNG +CREATURE:PYTHON:HEART +CREATURE:PYTHON:LIVER +CREATURE:PYTHON:GUT +CREATURE:PYTHON:STOMACH +CREATURE:PYTHON:GIZZARD +CREATURE:PYTHON:PANCREAS +CREATURE:PYTHON:SPLEEN +CREATURE:PYTHON:KIDNEY +CREATURE:PYTHON_MAN:MUSCLE +CREATURE:PYTHON_MAN:EYE +CREATURE:PYTHON_MAN:BRAIN +CREATURE:PYTHON_MAN:LUNG +CREATURE:PYTHON_MAN:HEART +CREATURE:PYTHON_MAN:LIVER +CREATURE:PYTHON_MAN:GUT +CREATURE:PYTHON_MAN:STOMACH +CREATURE:PYTHON_MAN:GIZZARD +CREATURE:PYTHON_MAN:PANCREAS +CREATURE:PYTHON_MAN:SPLEEN +CREATURE:PYTHON_MAN:KIDNEY +CREATURE:GIANT_PYTHON:MUSCLE +CREATURE:GIANT_PYTHON:EYE +CREATURE:GIANT_PYTHON:BRAIN +CREATURE:GIANT_PYTHON:LUNG +CREATURE:GIANT_PYTHON:HEART +CREATURE:GIANT_PYTHON:LIVER +CREATURE:GIANT_PYTHON:GUT +CREATURE:GIANT_PYTHON:STOMACH +CREATURE:GIANT_PYTHON:GIZZARD +CREATURE:GIANT_PYTHON:PANCREAS +CREATURE:GIANT_PYTHON:SPLEEN +CREATURE:GIANT_PYTHON:KIDNEY +CREATURE:TAPIR:MUSCLE +CREATURE:TAPIR:EYE +CREATURE:TAPIR:BRAIN +CREATURE:TAPIR:LUNG +CREATURE:TAPIR:HEART +CREATURE:TAPIR:LIVER +CREATURE:TAPIR:GUT +CREATURE:TAPIR:STOMACH +CREATURE:TAPIR:GIZZARD +CREATURE:TAPIR:PANCREAS +CREATURE:TAPIR:SPLEEN +CREATURE:TAPIR:KIDNEY +CREATURE:TAPIR_MAN:MUSCLE +CREATURE:TAPIR_MAN:EYE +CREATURE:TAPIR_MAN:BRAIN +CREATURE:TAPIR_MAN:LUNG +CREATURE:TAPIR_MAN:HEART +CREATURE:TAPIR_MAN:LIVER +CREATURE:TAPIR_MAN:GUT +CREATURE:TAPIR_MAN:STOMACH +CREATURE:TAPIR_MAN:GIZZARD +CREATURE:TAPIR_MAN:PANCREAS +CREATURE:TAPIR_MAN:SPLEEN +CREATURE:TAPIR_MAN:KIDNEY +CREATURE:GIANT_TAPIR:MUSCLE +CREATURE:GIANT_TAPIR:EYE +CREATURE:GIANT_TAPIR:BRAIN +CREATURE:GIANT_TAPIR:LUNG +CREATURE:GIANT_TAPIR:HEART +CREATURE:GIANT_TAPIR:LIVER +CREATURE:GIANT_TAPIR:GUT +CREATURE:GIANT_TAPIR:STOMACH +CREATURE:GIANT_TAPIR:GIZZARD +CREATURE:GIANT_TAPIR:PANCREAS +CREATURE:GIANT_TAPIR:SPLEEN +CREATURE:GIANT_TAPIR:KIDNEY +CREATURE:IMPALA:MUSCLE +CREATURE:IMPALA:EYE +CREATURE:IMPALA:BRAIN +CREATURE:IMPALA:LUNG +CREATURE:IMPALA:HEART +CREATURE:IMPALA:LIVER +CREATURE:IMPALA:GUT +CREATURE:IMPALA:STOMACH +CREATURE:IMPALA:GIZZARD +CREATURE:IMPALA:PANCREAS +CREATURE:IMPALA:SPLEEN +CREATURE:IMPALA:KIDNEY +CREATURE:IMPALA_MAN:MUSCLE +CREATURE:IMPALA_MAN:EYE +CREATURE:IMPALA_MAN:BRAIN +CREATURE:IMPALA_MAN:LUNG +CREATURE:IMPALA_MAN:HEART +CREATURE:IMPALA_MAN:LIVER +CREATURE:IMPALA_MAN:GUT +CREATURE:IMPALA_MAN:STOMACH +CREATURE:IMPALA_MAN:GIZZARD +CREATURE:IMPALA_MAN:PANCREAS +CREATURE:IMPALA_MAN:SPLEEN +CREATURE:IMPALA_MAN:KIDNEY +CREATURE:GIANT_IMPALA:MUSCLE +CREATURE:GIANT_IMPALA:EYE +CREATURE:GIANT_IMPALA:BRAIN +CREATURE:GIANT_IMPALA:LUNG +CREATURE:GIANT_IMPALA:HEART +CREATURE:GIANT_IMPALA:LIVER +CREATURE:GIANT_IMPALA:GUT +CREATURE:GIANT_IMPALA:STOMACH +CREATURE:GIANT_IMPALA:GIZZARD +CREATURE:GIANT_IMPALA:PANCREAS +CREATURE:GIANT_IMPALA:SPLEEN +CREATURE:GIANT_IMPALA:KIDNEY +CREATURE:AARDVARK:MUSCLE +CREATURE:AARDVARK:EYE +CREATURE:AARDVARK:BRAIN +CREATURE:AARDVARK:LUNG +CREATURE:AARDVARK:HEART +CREATURE:AARDVARK:LIVER +CREATURE:AARDVARK:GUT +CREATURE:AARDVARK:STOMACH +CREATURE:AARDVARK:GIZZARD +CREATURE:AARDVARK:PANCREAS +CREATURE:AARDVARK:SPLEEN +CREATURE:AARDVARK:KIDNEY +CREATURE:AARDVARK_MAN:MUSCLE +CREATURE:AARDVARK_MAN:EYE +CREATURE:AARDVARK_MAN:BRAIN +CREATURE:AARDVARK_MAN:LUNG +CREATURE:AARDVARK_MAN:HEART +CREATURE:AARDVARK_MAN:LIVER +CREATURE:AARDVARK_MAN:GUT +CREATURE:AARDVARK_MAN:STOMACH +CREATURE:AARDVARK_MAN:GIZZARD +CREATURE:AARDVARK_MAN:PANCREAS +CREATURE:AARDVARK_MAN:SPLEEN +CREATURE:AARDVARK_MAN:KIDNEY +CREATURE:GIANT_AARDVARK:MUSCLE +CREATURE:GIANT_AARDVARK:EYE +CREATURE:GIANT_AARDVARK:BRAIN +CREATURE:GIANT_AARDVARK:LUNG +CREATURE:GIANT_AARDVARK:HEART +CREATURE:GIANT_AARDVARK:LIVER +CREATURE:GIANT_AARDVARK:GUT +CREATURE:GIANT_AARDVARK:STOMACH +CREATURE:GIANT_AARDVARK:GIZZARD + CREATURE:GIANT_AARDVARK:PANCREAS +CREATURE:GIANT_AARDVARK:SPLEEN +CREATURE:GIANT_AARDVARK:KIDNEY +CREATURE:LION_TAMARIN:MUSCLE +CREATURE:LION_TAMARIN:EYE +CREATURE:LION_TAMARIN:BRAIN +CREATURE:LION_TAMARIN:LUNG +CREATURE:LION_TAMARIN:HEART +CREATURE:LION_TAMARIN:LIVER +CREATURE:LION_TAMARIN:GUT +CREATURE:LION_TAMARIN:STOMACH +CREATURE:LION_TAMARIN:GIZZARD +CREATURE:LION_TAMARIN:PANCREAS +CREATURE:LION_TAMARIN:SPLEEN +CREATURE:LION_TAMARIN:KIDNEY + CREATURE:LION_TAMARIN_MAN:MUSCLE +CREATURE:LION_TAMARIN_MAN:EYE +CREATURE:LION_TAMARIN_MAN:BRAIN +CREATURE:LION_TAMARIN_MAN:LUNG +CREATURE:LION_TAMARIN_MAN:HEART +CREATURE:LION_TAMARIN_MAN:LIVER +CREATURE:LION_TAMARIN_MAN:GUT +!CREATURE:LION_TAMARIN_MAN:STOMACH +!CREATURE:LION_TAMARIN_MAN:GIZZARD +"CREATURE:LION_TAMARIN_MAN:PANCREAS + CREATURE:LION_TAMARIN_MAN:SPLEEN + CREATURE:LION_TAMARIN_MAN:KIDNEY +"CREATURE:GIANT_LION_TAMARIN:MUSCLE +CREATURE:GIANT_LION_TAMARIN:EYE +!CREATURE:GIANT_LION_TAMARIN:BRAIN + CREATURE:GIANT_LION_TAMARIN:LUNG +!CREATURE:GIANT_LION_TAMARIN:HEART +!CREATURE:GIANT_LION_TAMARIN:LIVER +CREATURE:GIANT_LION_TAMARIN:GUT +#CREATURE:GIANT_LION_TAMARIN:STOMACH +#CREATURE:GIANT_LION_TAMARIN:GIZZARD +$CREATURE:GIANT_LION_TAMARIN:PANCREAS +"CREATURE:GIANT_LION_TAMARIN:SPLEEN +"CREATURE:GIANT_LION_TAMARIN:KIDNEY +CREATURE:STOAT:MUSCLE +CREATURE:STOAT:EYE +CREATURE:STOAT:BRAIN +CREATURE:STOAT:LUNG +CREATURE:STOAT:HEART +CREATURE:STOAT:LIVER +CREATURE:STOAT:GUT +CREATURE:STOAT:STOMACH +CREATURE:STOAT:GIZZARD +CREATURE:STOAT:PANCREAS +CREATURE:STOAT:SPLEEN +CREATURE:STOAT:KIDNEY +CREATURE:STOAT_MAN:MUSCLE +CREATURE:STOAT_MAN:EYE +CREATURE:STOAT_MAN:BRAIN +CREATURE:STOAT_MAN:LUNG +CREATURE:STOAT_MAN:HEART +CREATURE:STOAT_MAN:LIVER +CREATURE:STOAT_MAN:GUT +CREATURE:STOAT_MAN:STOMACH +CREATURE:STOAT_MAN:GIZZARD +CREATURE:STOAT_MAN:PANCREAS +CREATURE:STOAT_MAN:SPLEEN +CREATURE:STOAT_MAN:KIDNEY +CREATURE:GIANT_STOAT:MUSCLE +CREATURE:GIANT_STOAT:EYE +CREATURE:GIANT_STOAT:BRAIN +CREATURE:GIANT_STOAT:LUNG +CREATURE:GIANT_STOAT:HEART +CREATURE:GIANT_STOAT:LIVER +CREATURE:GIANT_STOAT:GUT +CREATURE:GIANT_STOAT:STOMACH +CREATURE:GIANT_STOAT:GIZZARD +CREATURE:GIANT_STOAT:PANCREAS +CREATURE:GIANT_STOAT:SPLEEN +CREATURE:GIANT_STOAT:KIDNEY +CREATURE:LYNX:MUSCLE +CREATURE:LYNX:EYE +CREATURE:LYNX:BRAIN +CREATURE:LYNX:LUNG +CREATURE:LYNX:HEART +CREATURE:LYNX:LIVER +CREATURE:LYNX:GUT +CREATURE:LYNX:STOMACH +CREATURE:LYNX:GIZZARD +CREATURE:LYNX:PANCREAS +CREATURE:LYNX:SPLEEN +CREATURE:LYNX:KIDNEY +CREATURE:LYNX_MAN:MUSCLE +CREATURE:LYNX_MAN:EYE +CREATURE:LYNX_MAN:BRAIN +CREATURE:LYNX_MAN:LUNG +CREATURE:LYNX_MAN:HEART +CREATURE:LYNX_MAN:LIVER +CREATURE:LYNX_MAN:GUT +CREATURE:LYNX_MAN:STOMACH +CREATURE:LYNX_MAN:GIZZARD +CREATURE:LYNX_MAN:PANCREAS +CREATURE:LYNX_MAN:SPLEEN +CREATURE:LYNX_MAN:KIDNEY +CREATURE:GIANT_LYNX:MUSCLE +CREATURE:GIANT_LYNX:EYE +CREATURE:GIANT_LYNX:BRAIN +CREATURE:GIANT_LYNX:LUNG +CREATURE:GIANT_LYNX:HEART +CREATURE:GIANT_LYNX:LIVER +CREATURE:GIANT_LYNX:GUT +CREATURE:GIANT_LYNX:STOMACH +CREATURE:GIANT_LYNX:GIZZARD +CREATURE:GIANT_LYNX:PANCREAS +CREATURE:GIANT_LYNX:SPLEEN +CREATURE:GIANT_LYNX:KIDNEY +CREATURE:GNOLL:MUSCLE +CREATURE:GNOLL:EYE +CREATURE:GNOLL:BRAIN +CREATURE:GNOLL:LUNG +CREATURE:GNOLL:HEART +CREATURE:GNOLL:LIVER +CREATURE:GNOLL:GUT +CREATURE:GNOLL:STOMACH +CREATURE:GNOLL:GIZZARD +CREATURE:GNOLL:PANCREAS +CREATURE:GNOLL:SPLEEN +CREATURE:GNOLL:KIDNEY +CREATURE:NAGA:MUSCLE +CREATURE:NAGA:EYE +CREATURE:NAGA:BRAIN +CREATURE:NAGA:LUNG +CREATURE:NAGA:HEART +CREATURE:NAGA:LIVER +CREATURE:NAGA:GUT +CREATURE:NAGA:STOMACH +CREATURE:NAGA:GIZZARD +CREATURE:NAGA:PANCREAS +CREATURE:NAGA:SPLEEN +CREATURE:NAGA:KIDNEY +!CREATURE:FORGOTTEN_BEAST_2:MUSCLE +CREATURE:FORGOTTEN_BEAST_2:EYE + CREATURE:FORGOTTEN_BEAST_2:BRAIN +CREATURE:FORGOTTEN_BEAST_2:LUNG + CREATURE:FORGOTTEN_BEAST_2:HEART + CREATURE:FORGOTTEN_BEAST_2:LIVER +CREATURE:FORGOTTEN_BEAST_2:GUT +"CREATURE:FORGOTTEN_BEAST_2:STOMACH +"CREATURE:FORGOTTEN_BEAST_2:GIZZARD +#CREATURE:FORGOTTEN_BEAST_2:PANCREAS +!CREATURE:FORGOTTEN_BEAST_2:SPLEEN +!CREATURE:FORGOTTEN_BEAST_2:KIDNEY +!CREATURE:FORGOTTEN_BEAST_4:MUSCLE +CREATURE:FORGOTTEN_BEAST_4:EYE + CREATURE:FORGOTTEN_BEAST_4:BRAIN +CREATURE:FORGOTTEN_BEAST_4:LUNG + CREATURE:FORGOTTEN_BEAST_4:HEART + CREATURE:FORGOTTEN_BEAST_4:LIVER +CREATURE:FORGOTTEN_BEAST_4:GUT +"CREATURE:FORGOTTEN_BEAST_4:STOMACH +"CREATURE:FORGOTTEN_BEAST_4:GIZZARD +#CREATURE:FORGOTTEN_BEAST_4:PANCREAS +!CREATURE:FORGOTTEN_BEAST_4:SPLEEN +!CREATURE:FORGOTTEN_BEAST_4:KIDNEY +!CREATURE:FORGOTTEN_BEAST_5:MUSCLE +CREATURE:FORGOTTEN_BEAST_5:EYE + CREATURE:FORGOTTEN_BEAST_5:BRAIN +CREATURE:FORGOTTEN_BEAST_5:LUNG + CREATURE:FORGOTTEN_BEAST_5:HEART + CREATURE:FORGOTTEN_BEAST_5:LIVER +CREATURE:FORGOTTEN_BEAST_5:GUT +"CREATURE:FORGOTTEN_BEAST_5:STOMACH +"CREATURE:FORGOTTEN_BEAST_5:GIZZARD +#CREATURE:FORGOTTEN_BEAST_5:PANCREAS +!CREATURE:FORGOTTEN_BEAST_5:SPLEEN +!CREATURE:FORGOTTEN_BEAST_5:KIDNEY +!CREATURE:FORGOTTEN_BEAST_6:MUSCLE +CREATURE:FORGOTTEN_BEAST_6:EYE + CREATURE:FORGOTTEN_BEAST_6:BRAIN +CREATURE:FORGOTTEN_BEAST_6:LUNG + CREATURE:FORGOTTEN_BEAST_6:HEART + CREATURE:FORGOTTEN_BEAST_6:LIVER +CREATURE:FORGOTTEN_BEAST_6:GUT +"CREATURE:FORGOTTEN_BEAST_6:STOMACH +"CREATURE:FORGOTTEN_BEAST_6:GIZZARD +#CREATURE:FORGOTTEN_BEAST_6:PANCREAS +!CREATURE:FORGOTTEN_BEAST_6:SPLEEN +!CREATURE:FORGOTTEN_BEAST_6:KIDNEY +!CREATURE:FORGOTTEN_BEAST_7:MUSCLE +CREATURE:FORGOTTEN_BEAST_7:EYE + CREATURE:FORGOTTEN_BEAST_7:BRAIN +CREATURE:FORGOTTEN_BEAST_7:LUNG + CREATURE:FORGOTTEN_BEAST_7:HEART + CREATURE:FORGOTTEN_BEAST_7:LIVER +CREATURE:FORGOTTEN_BEAST_7:GUT +"CREATURE:FORGOTTEN_BEAST_7:STOMACH +"CREATURE:FORGOTTEN_BEAST_7:GIZZARD +#CREATURE:FORGOTTEN_BEAST_7:PANCREAS +!CREATURE:FORGOTTEN_BEAST_7:SPLEEN +!CREATURE:FORGOTTEN_BEAST_7:KIDNEY +"CREATURE:FORGOTTEN_BEAST_10:MUSCLE +CREATURE:FORGOTTEN_BEAST_10:EYE +!CREATURE:FORGOTTEN_BEAST_10:BRAIN + CREATURE:FORGOTTEN_BEAST_10:LUNG +!CREATURE:FORGOTTEN_BEAST_10:HEART +!CREATURE:FORGOTTEN_BEAST_10:LIVER +CREATURE:FORGOTTEN_BEAST_10:GUT +#CREATURE:FORGOTTEN_BEAST_10:STOMACH +#CREATURE:FORGOTTEN_BEAST_10:GIZZARD +$CREATURE:FORGOTTEN_BEAST_10:PANCREAS +"CREATURE:FORGOTTEN_BEAST_10:SPLEEN +"CREATURE:FORGOTTEN_BEAST_10:KIDNEY +"CREATURE:FORGOTTEN_BEAST_12:MUSCLE +CREATURE:FORGOTTEN_BEAST_12:EYE +!CREATURE:FORGOTTEN_BEAST_12:BRAIN + CREATURE:FORGOTTEN_BEAST_12:LUNG +!CREATURE:FORGOTTEN_BEAST_12:HEART +!CREATURE:FORGOTTEN_BEAST_12:LIVER +CREATURE:FORGOTTEN_BEAST_12:GUT +#CREATURE:FORGOTTEN_BEAST_12:STOMACH +#CREATURE:FORGOTTEN_BEAST_12:GIZZARD +$CREATURE:FORGOTTEN_BEAST_12:PANCREAS +"CREATURE:FORGOTTEN_BEAST_12:SPLEEN +"CREATURE:FORGOTTEN_BEAST_12:KIDNEY +"CREATURE:FORGOTTEN_BEAST_13:MUSCLE +CREATURE:FORGOTTEN_BEAST_13:EYE +!CREATURE:FORGOTTEN_BEAST_13:BRAIN + CREATURE:FORGOTTEN_BEAST_13:LUNG +!CREATURE:FORGOTTEN_BEAST_13:HEART +!CREATURE:FORGOTTEN_BEAST_13:LIVER +CREATURE:FORGOTTEN_BEAST_13:GUT +#CREATURE:FORGOTTEN_BEAST_13:STOMACH +#CREATURE:FORGOTTEN_BEAST_13:GIZZARD +$CREATURE:FORGOTTEN_BEAST_13:PANCREAS +"CREATURE:FORGOTTEN_BEAST_13:SPLEEN +"CREATURE:FORGOTTEN_BEAST_13:KIDNEY +"CREATURE:FORGOTTEN_BEAST_16:MUSCLE +CREATURE:FORGOTTEN_BEAST_16:EYE +!CREATURE:FORGOTTEN_BEAST_16:BRAIN + CREATURE:FORGOTTEN_BEAST_16:LUNG +!CREATURE:FORGOTTEN_BEAST_16:HEART +!CREATURE:FORGOTTEN_BEAST_16:LIVER +CREATURE:FORGOTTEN_BEAST_16:GUT +#CREATURE:FORGOTTEN_BEAST_16:STOMACH +#CREATURE:FORGOTTEN_BEAST_16:GIZZARD +$CREATURE:FORGOTTEN_BEAST_16:PANCREAS +"CREATURE:FORGOTTEN_BEAST_16:SPLEEN +"CREATURE:FORGOTTEN_BEAST_16:KIDNEY +"CREATURE:FORGOTTEN_BEAST_17:MUSCLE +CREATURE:FORGOTTEN_BEAST_17:EYE +!CREATURE:FORGOTTEN_BEAST_17:BRAIN + CREATURE:FORGOTTEN_BEAST_17:LUNG +!CREATURE:FORGOTTEN_BEAST_17:HEART +!CREATURE:FORGOTTEN_BEAST_17:LIVER +CREATURE:FORGOTTEN_BEAST_17:GUT +#CREATURE:FORGOTTEN_BEAST_17:STOMACH +#CREATURE:FORGOTTEN_BEAST_17:GIZZARD +$CREATURE:FORGOTTEN_BEAST_17:PANCREAS +"CREATURE:FORGOTTEN_BEAST_17:SPLEEN +"CREATURE:FORGOTTEN_BEAST_17:KIDNEY +"CREATURE:FORGOTTEN_BEAST_18:MUSCLE +CREATURE:FORGOTTEN_BEAST_18:EYE +!CREATURE:FORGOTTEN_BEAST_18:BRAIN + CREATURE:FORGOTTEN_BEAST_18:LUNG +!CREATURE:FORGOTTEN_BEAST_18:HEART +!CREATURE:FORGOTTEN_BEAST_18:LIVER +CREATURE:FORGOTTEN_BEAST_18:GUT +#CREATURE:FORGOTTEN_BEAST_18:STOMACH +#CREATURE:FORGOTTEN_BEAST_18:GIZZARD +$CREATURE:FORGOTTEN_BEAST_18:PANCREAS +"CREATURE:FORGOTTEN_BEAST_18:SPLEEN +"CREATURE:FORGOTTEN_BEAST_18:KIDNEY +"CREATURE:FORGOTTEN_BEAST_19:MUSCLE +CREATURE:FORGOTTEN_BEAST_19:EYE +!CREATURE:FORGOTTEN_BEAST_19:BRAIN + CREATURE:FORGOTTEN_BEAST_19:LUNG +!CREATURE:FORGOTTEN_BEAST_19:HEART +!CREATURE:FORGOTTEN_BEAST_19:LIVER +CREATURE:FORGOTTEN_BEAST_19:GUT +#CREATURE:FORGOTTEN_BEAST_19:STOMACH +#CREATURE:FORGOTTEN_BEAST_19:GIZZARD +$CREATURE:FORGOTTEN_BEAST_19:PANCREAS +"CREATURE:FORGOTTEN_BEAST_19:SPLEEN +"CREATURE:FORGOTTEN_BEAST_19:KIDNEY +"CREATURE:FORGOTTEN_BEAST_20:MUSCLE +CREATURE:FORGOTTEN_BEAST_20:EYE +!CREATURE:FORGOTTEN_BEAST_20:BRAIN + CREATURE:FORGOTTEN_BEAST_20:LUNG +!CREATURE:FORGOTTEN_BEAST_20:HEART +!CREATURE:FORGOTTEN_BEAST_20:LIVER +CREATURE:FORGOTTEN_BEAST_20:GUT +#CREATURE:FORGOTTEN_BEAST_20:STOMACH +#CREATURE:FORGOTTEN_BEAST_20:GIZZARD +$CREATURE:FORGOTTEN_BEAST_20:PANCREAS +"CREATURE:FORGOTTEN_BEAST_20:SPLEEN +"CREATURE:FORGOTTEN_BEAST_20:KIDNEY +"CREATURE:FORGOTTEN_BEAST_22:MUSCLE +CREATURE:FORGOTTEN_BEAST_22:EYE +!CREATURE:FORGOTTEN_BEAST_22:BRAIN + CREATURE:FORGOTTEN_BEAST_22:LUNG +!CREATURE:FORGOTTEN_BEAST_22:HEART +!CREATURE:FORGOTTEN_BEAST_22:LIVER +CREATURE:FORGOTTEN_BEAST_22:GUT +#CREATURE:FORGOTTEN_BEAST_22:STOMACH +#CREATURE:FORGOTTEN_BEAST_22:GIZZARD +$CREATURE:FORGOTTEN_BEAST_22:PANCREAS +"CREATURE:FORGOTTEN_BEAST_22:SPLEEN +"CREATURE:FORGOTTEN_BEAST_22:KIDNEY +"CREATURE:FORGOTTEN_BEAST_23:MUSCLE +CREATURE:FORGOTTEN_BEAST_23:EYE +!CREATURE:FORGOTTEN_BEAST_23:BRAIN + CREATURE:FORGOTTEN_BEAST_23:LUNG +!CREATURE:FORGOTTEN_BEAST_23:HEART +!CREATURE:FORGOTTEN_BEAST_23:LIVER +CREATURE:FORGOTTEN_BEAST_23:GUT +#CREATURE:FORGOTTEN_BEAST_23:STOMACH +#CREATURE:FORGOTTEN_BEAST_23:GIZZARD +$CREATURE:FORGOTTEN_BEAST_23:PANCREAS +"CREATURE:FORGOTTEN_BEAST_23:SPLEEN +"CREATURE:FORGOTTEN_BEAST_23:KIDNEY +"CREATURE:FORGOTTEN_BEAST_24:MUSCLE +CREATURE:FORGOTTEN_BEAST_24:EYE +!CREATURE:FORGOTTEN_BEAST_24:BRAIN + CREATURE:FORGOTTEN_BEAST_24:LUNG +!CREATURE:FORGOTTEN_BEAST_24:HEART +!CREATURE:FORGOTTEN_BEAST_24:LIVER +CREATURE:FORGOTTEN_BEAST_24:GUT +#CREATURE:FORGOTTEN_BEAST_24:STOMACH +#CREATURE:FORGOTTEN_BEAST_24:GIZZARD +$CREATURE:FORGOTTEN_BEAST_24:PANCREAS +"CREATURE:FORGOTTEN_BEAST_24:SPLEEN +"CREATURE:FORGOTTEN_BEAST_24:KIDNEY +"CREATURE:FORGOTTEN_BEAST_25:MUSCLE +CREATURE:FORGOTTEN_BEAST_25:EYE +!CREATURE:FORGOTTEN_BEAST_25:BRAIN + CREATURE:FORGOTTEN_BEAST_25:LUNG +!CREATURE:FORGOTTEN_BEAST_25:HEART +!CREATURE:FORGOTTEN_BEAST_25:LIVER +CREATURE:FORGOTTEN_BEAST_25:GUT +#CREATURE:FORGOTTEN_BEAST_25:STOMACH +#CREATURE:FORGOTTEN_BEAST_25:GIZZARD +$CREATURE:FORGOTTEN_BEAST_25:PANCREAS +"CREATURE:FORGOTTEN_BEAST_25:SPLEEN +"CREATURE:FORGOTTEN_BEAST_25:KIDNEY +"CREATURE:FORGOTTEN_BEAST_26:MUSCLE +CREATURE:FORGOTTEN_BEAST_26:EYE +!CREATURE:FORGOTTEN_BEAST_26:BRAIN + CREATURE:FORGOTTEN_BEAST_26:LUNG +!CREATURE:FORGOTTEN_BEAST_26:HEART +!CREATURE:FORGOTTEN_BEAST_26:LIVER +CREATURE:FORGOTTEN_BEAST_26:GUT +#CREATURE:FORGOTTEN_BEAST_26:STOMACH +#CREATURE:FORGOTTEN_BEAST_26:GIZZARD +$CREATURE:FORGOTTEN_BEAST_26:PANCREAS +"CREATURE:FORGOTTEN_BEAST_26:SPLEEN +"CREATURE:FORGOTTEN_BEAST_26:KIDNEY +"CREATURE:FORGOTTEN_BEAST_27:MUSCLE +CREATURE:FORGOTTEN_BEAST_27:EYE +!CREATURE:FORGOTTEN_BEAST_27:BRAIN + CREATURE:FORGOTTEN_BEAST_27:LUNG +!CREATURE:FORGOTTEN_BEAST_27:HEART +!CREATURE:FORGOTTEN_BEAST_27:LIVER +CREATURE:FORGOTTEN_BEAST_27:GUT +#CREATURE:FORGOTTEN_BEAST_27:STOMACH +#CREATURE:FORGOTTEN_BEAST_27:GIZZARD +$CREATURE:FORGOTTEN_BEAST_27:PANCREAS +"CREATURE:FORGOTTEN_BEAST_27:SPLEEN +"CREATURE:FORGOTTEN_BEAST_27:KIDNEY +"CREATURE:FORGOTTEN_BEAST_28:MUSCLE +CREATURE:FORGOTTEN_BEAST_28:EYE +!CREATURE:FORGOTTEN_BEAST_28:BRAIN + CREATURE:FORGOTTEN_BEAST_28:LUNG +!CREATURE:FORGOTTEN_BEAST_28:HEART +!CREATURE:FORGOTTEN_BEAST_28:LIVER +CREATURE:FORGOTTEN_BEAST_28:GUT +#CREATURE:FORGOTTEN_BEAST_28:STOMACH +#CREATURE:FORGOTTEN_BEAST_28:GIZZARD +$CREATURE:FORGOTTEN_BEAST_28:PANCREAS +"CREATURE:FORGOTTEN_BEAST_28:SPLEEN +"CREATURE:FORGOTTEN_BEAST_28:KIDNEY +"CREATURE:FORGOTTEN_BEAST_29:MUSCLE +CREATURE:FORGOTTEN_BEAST_29:EYE +!CREATURE:FORGOTTEN_BEAST_29:BRAIN + CREATURE:FORGOTTEN_BEAST_29:LUNG +!CREATURE:FORGOTTEN_BEAST_29:HEART +!CREATURE:FORGOTTEN_BEAST_29:LIVER +CREATURE:FORGOTTEN_BEAST_29:GUT +#CREATURE:FORGOTTEN_BEAST_29:STOMACH +#CREATURE:FORGOTTEN_BEAST_29:GIZZARD +$CREATURE:FORGOTTEN_BEAST_29:PANCREAS +"CREATURE:FORGOTTEN_BEAST_29:SPLEEN +"CREATURE:FORGOTTEN_BEAST_29:KIDNEY +"CREATURE:FORGOTTEN_BEAST_32:MUSCLE +CREATURE:FORGOTTEN_BEAST_32:EYE +!CREATURE:FORGOTTEN_BEAST_32:BRAIN + CREATURE:FORGOTTEN_BEAST_32:LUNG +!CREATURE:FORGOTTEN_BEAST_32:HEART +!CREATURE:FORGOTTEN_BEAST_32:LIVER +CREATURE:FORGOTTEN_BEAST_32:GUT +#CREATURE:FORGOTTEN_BEAST_32:STOMACH +#CREATURE:FORGOTTEN_BEAST_32:GIZZARD +$CREATURE:FORGOTTEN_BEAST_32:PANCREAS +"CREATURE:FORGOTTEN_BEAST_32:SPLEEN +"CREATURE:FORGOTTEN_BEAST_32:KIDNEY +"CREATURE:FORGOTTEN_BEAST_33:MUSCLE +CREATURE:FORGOTTEN_BEAST_33:EYE +!CREATURE:FORGOTTEN_BEAST_33:BRAIN + CREATURE:FORGOTTEN_BEAST_33:LUNG +!CREATURE:FORGOTTEN_BEAST_33:HEART +!CREATURE:FORGOTTEN_BEAST_33:LIVER +CREATURE:FORGOTTEN_BEAST_33:GUT +#CREATURE:FORGOTTEN_BEAST_33:STOMACH +#CREATURE:FORGOTTEN_BEAST_33:GIZZARD +$CREATURE:FORGOTTEN_BEAST_33:PANCREAS +"CREATURE:FORGOTTEN_BEAST_33:SPLEEN +"CREATURE:FORGOTTEN_BEAST_33:KIDNEY +"CREATURE:FORGOTTEN_BEAST_34:MUSCLE +CREATURE:FORGOTTEN_BEAST_34:EYE +!CREATURE:FORGOTTEN_BEAST_34:BRAIN + CREATURE:FORGOTTEN_BEAST_34:LUNG +!CREATURE:FORGOTTEN_BEAST_34:HEART +!CREATURE:FORGOTTEN_BEAST_34:LIVER +CREATURE:FORGOTTEN_BEAST_34:GUT +#CREATURE:FORGOTTEN_BEAST_34:STOMACH +#CREATURE:FORGOTTEN_BEAST_34:GIZZARD +$CREATURE:FORGOTTEN_BEAST_34:PANCREAS +"CREATURE:FORGOTTEN_BEAST_34:SPLEEN +"CREATURE:FORGOTTEN_BEAST_34:KIDNEY +"CREATURE:FORGOTTEN_BEAST_35:MUSCLE +CREATURE:FORGOTTEN_BEAST_35:EYE +!CREATURE:FORGOTTEN_BEAST_35:BRAIN + CREATURE:FORGOTTEN_BEAST_35:LUNG +!CREATURE:FORGOTTEN_BEAST_35:HEART +!CREATURE:FORGOTTEN_BEAST_35:LIVER +CREATURE:FORGOTTEN_BEAST_35:GUT +#CREATURE:FORGOTTEN_BEAST_35:STOMACH +#CREATURE:FORGOTTEN_BEAST_35:GIZZARD +$CREATURE:FORGOTTEN_BEAST_35:PANCREAS +"CREATURE:FORGOTTEN_BEAST_35:SPLEEN +"CREATURE:FORGOTTEN_BEAST_35:KIDNEY +"CREATURE:FORGOTTEN_BEAST_36:MUSCLE +CREATURE:FORGOTTEN_BEAST_36:EYE +!CREATURE:FORGOTTEN_BEAST_36:BRAIN + CREATURE:FORGOTTEN_BEAST_36:LUNG +!CREATURE:FORGOTTEN_BEAST_36:HEART +!CREATURE:FORGOTTEN_BEAST_36:LIVER +CREATURE:FORGOTTEN_BEAST_36:GUT +#CREATURE:FORGOTTEN_BEAST_36:STOMACH +#CREATURE:FORGOTTEN_BEAST_36:GIZZARD +$CREATURE:FORGOTTEN_BEAST_36:PANCREAS +"CREATURE:FORGOTTEN_BEAST_36:SPLEEN +"CREATURE:FORGOTTEN_BEAST_36:KIDNEY +"CREATURE:FORGOTTEN_BEAST_39:MUSCLE +CREATURE:FORGOTTEN_BEAST_39:EYE +!CREATURE:FORGOTTEN_BEAST_39:BRAIN + CREATURE:FORGOTTEN_BEAST_39:LUNG +!CREATURE:FORGOTTEN_BEAST_39:HEART +!CREATURE:FORGOTTEN_BEAST_39:LIVER +CREATURE:FORGOTTEN_BEAST_39:GUT +#CREATURE:FORGOTTEN_BEAST_39:STOMACH +#CREATURE:FORGOTTEN_BEAST_39:GIZZARD +$CREATURE:FORGOTTEN_BEAST_39:PANCREAS +"CREATURE:FORGOTTEN_BEAST_39:SPLEEN +"CREATURE:FORGOTTEN_BEAST_39:KIDNEY +"CREATURE:FORGOTTEN_BEAST_41:MUSCLE +CREATURE:FORGOTTEN_BEAST_41:EYE +!CREATURE:FORGOTTEN_BEAST_41:BRAIN + CREATURE:FORGOTTEN_BEAST_41:LUNG +!CREATURE:FORGOTTEN_BEAST_41:HEART +!CREATURE:FORGOTTEN_BEAST_41:LIVER +CREATURE:FORGOTTEN_BEAST_41:GUT +#CREATURE:FORGOTTEN_BEAST_41:STOMACH +#CREATURE:FORGOTTEN_BEAST_41:GIZZARD +$CREATURE:FORGOTTEN_BEAST_41:PANCREAS +"CREATURE:FORGOTTEN_BEAST_41:SPLEEN +"CREATURE:FORGOTTEN_BEAST_41:KIDNEY +"CREATURE:FORGOTTEN_BEAST_42:MUSCLE +CREATURE:FORGOTTEN_BEAST_42:EYE +!CREATURE:FORGOTTEN_BEAST_42:BRAIN + CREATURE:FORGOTTEN_BEAST_42:LUNG +!CREATURE:FORGOTTEN_BEAST_42:HEART +!CREATURE:FORGOTTEN_BEAST_42:LIVER +CREATURE:FORGOTTEN_BEAST_42:GUT +#CREATURE:FORGOTTEN_BEAST_42:STOMACH +#CREATURE:FORGOTTEN_BEAST_42:GIZZARD +$CREATURE:FORGOTTEN_BEAST_42:PANCREAS +"CREATURE:FORGOTTEN_BEAST_42:SPLEEN +"CREATURE:FORGOTTEN_BEAST_42:KIDNEY +"CREATURE:FORGOTTEN_BEAST_43:MUSCLE +CREATURE:FORGOTTEN_BEAST_43:EYE +!CREATURE:FORGOTTEN_BEAST_43:BRAIN + CREATURE:FORGOTTEN_BEAST_43:LUNG +!CREATURE:FORGOTTEN_BEAST_43:HEART +!CREATURE:FORGOTTEN_BEAST_43:LIVER +CREATURE:FORGOTTEN_BEAST_43:GUT +#CREATURE:FORGOTTEN_BEAST_43:STOMACH +#CREATURE:FORGOTTEN_BEAST_43:GIZZARD +$CREATURE:FORGOTTEN_BEAST_43:PANCREAS +"CREATURE:FORGOTTEN_BEAST_43:SPLEEN +"CREATURE:FORGOTTEN_BEAST_43:KIDNEY +"CREATURE:FORGOTTEN_BEAST_44:MUSCLE +CREATURE:FORGOTTEN_BEAST_44:EYE +!CREATURE:FORGOTTEN_BEAST_44:BRAIN + CREATURE:FORGOTTEN_BEAST_44:LUNG +!CREATURE:FORGOTTEN_BEAST_44:HEART +!CREATURE:FORGOTTEN_BEAST_44:LIVER +CREATURE:FORGOTTEN_BEAST_44:GUT +#CREATURE:FORGOTTEN_BEAST_44:STOMACH +#CREATURE:FORGOTTEN_BEAST_44:GIZZARD +$CREATURE:FORGOTTEN_BEAST_44:PANCREAS +"CREATURE:FORGOTTEN_BEAST_44:SPLEEN +"CREATURE:FORGOTTEN_BEAST_44:KIDNEY +"CREATURE:FORGOTTEN_BEAST_45:MUSCLE +CREATURE:FORGOTTEN_BEAST_45:EYE +!CREATURE:FORGOTTEN_BEAST_45:BRAIN + CREATURE:FORGOTTEN_BEAST_45:LUNG +!CREATURE:FORGOTTEN_BEAST_45:HEART +!CREATURE:FORGOTTEN_BEAST_45:LIVER +CREATURE:FORGOTTEN_BEAST_45:GUT +#CREATURE:FORGOTTEN_BEAST_45:STOMACH +#CREATURE:FORGOTTEN_BEAST_45:GIZZARD +$CREATURE:FORGOTTEN_BEAST_45:PANCREAS +"CREATURE:FORGOTTEN_BEAST_45:SPLEEN +"CREATURE:FORGOTTEN_BEAST_45:KIDNEY +"CREATURE:FORGOTTEN_BEAST_47:MUSCLE +CREATURE:FORGOTTEN_BEAST_47:EYE +!CREATURE:FORGOTTEN_BEAST_47:BRAIN + CREATURE:FORGOTTEN_BEAST_47:LUNG +!CREATURE:FORGOTTEN_BEAST_47:HEART +!CREATURE:FORGOTTEN_BEAST_47:LIVER +CREATURE:FORGOTTEN_BEAST_47:GUT +#CREATURE:FORGOTTEN_BEAST_47:STOMACH +#CREATURE:FORGOTTEN_BEAST_47:GIZZARD +$CREATURE:FORGOTTEN_BEAST_47:PANCREAS +"CREATURE:FORGOTTEN_BEAST_47:SPLEEN +"CREATURE:FORGOTTEN_BEAST_47:KIDNEY +"CREATURE:FORGOTTEN_BEAST_50:MUSCLE +CREATURE:FORGOTTEN_BEAST_50:EYE +!CREATURE:FORGOTTEN_BEAST_50:BRAIN + CREATURE:FORGOTTEN_BEAST_50:LUNG +!CREATURE:FORGOTTEN_BEAST_50:HEART +!CREATURE:FORGOTTEN_BEAST_50:LIVER +CREATURE:FORGOTTEN_BEAST_50:GUT +#CREATURE:FORGOTTEN_BEAST_50:STOMACH +#CREATURE:FORGOTTEN_BEAST_50:GIZZARD +$CREATURE:FORGOTTEN_BEAST_50:PANCREAS +"CREATURE:FORGOTTEN_BEAST_50:SPLEEN +"CREATURE:FORGOTTEN_BEAST_50:KIDNEY +"CREATURE:FORGOTTEN_BEAST_52:MUSCLE +CREATURE:FORGOTTEN_BEAST_52:EYE +!CREATURE:FORGOTTEN_BEAST_52:BRAIN + CREATURE:FORGOTTEN_BEAST_52:LUNG +!CREATURE:FORGOTTEN_BEAST_52:HEART +!CREATURE:FORGOTTEN_BEAST_52:LIVER +CREATURE:FORGOTTEN_BEAST_52:GUT +#CREATURE:FORGOTTEN_BEAST_52:STOMACH +#CREATURE:FORGOTTEN_BEAST_52:GIZZARD +$CREATURE:FORGOTTEN_BEAST_52:PANCREAS +"CREATURE:FORGOTTEN_BEAST_52:SPLEEN +"CREATURE:FORGOTTEN_BEAST_52:KIDNEY +"CREATURE:FORGOTTEN_BEAST_53:MUSCLE +CREATURE:FORGOTTEN_BEAST_53:EYE +!CREATURE:FORGOTTEN_BEAST_53:BRAIN + CREATURE:FORGOTTEN_BEAST_53:LUNG +!CREATURE:FORGOTTEN_BEAST_53:HEART +!CREATURE:FORGOTTEN_BEAST_53:LIVER +CREATURE:FORGOTTEN_BEAST_53:GUT +#CREATURE:FORGOTTEN_BEAST_53:STOMACH +#CREATURE:FORGOTTEN_BEAST_53:GIZZARD +$CREATURE:FORGOTTEN_BEAST_53:PANCREAS +"CREATURE:FORGOTTEN_BEAST_53:SPLEEN +"CREATURE:FORGOTTEN_BEAST_53:KIDNEY +"CREATURE:FORGOTTEN_BEAST_55:MUSCLE +CREATURE:FORGOTTEN_BEAST_55:EYE +!CREATURE:FORGOTTEN_BEAST_55:BRAIN + CREATURE:FORGOTTEN_BEAST_55:LUNG +!CREATURE:FORGOTTEN_BEAST_55:HEART +!CREATURE:FORGOTTEN_BEAST_55:LIVER +CREATURE:FORGOTTEN_BEAST_55:GUT +#CREATURE:FORGOTTEN_BEAST_55:STOMACH +#CREATURE:FORGOTTEN_BEAST_55:GIZZARD +$CREATURE:FORGOTTEN_BEAST_55:PANCREAS +"CREATURE:FORGOTTEN_BEAST_55:SPLEEN +"CREATURE:FORGOTTEN_BEAST_55:KIDNEY +"CREATURE:FORGOTTEN_BEAST_56:MUSCLE +CREATURE:FORGOTTEN_BEAST_56:EYE +!CREATURE:FORGOTTEN_BEAST_56:BRAIN + CREATURE:FORGOTTEN_BEAST_56:LUNG +!CREATURE:FORGOTTEN_BEAST_56:HEART +!CREATURE:FORGOTTEN_BEAST_56:LIVER +CREATURE:FORGOTTEN_BEAST_56:GUT +#CREATURE:FORGOTTEN_BEAST_56:STOMACH +#CREATURE:FORGOTTEN_BEAST_56:GIZZARD +$CREATURE:FORGOTTEN_BEAST_56:PANCREAS +"CREATURE:FORGOTTEN_BEAST_56:SPLEEN +"CREATURE:FORGOTTEN_BEAST_56:KIDNEY +"CREATURE:FORGOTTEN_BEAST_58:MUSCLE +CREATURE:FORGOTTEN_BEAST_58:EYE +!CREATURE:FORGOTTEN_BEAST_58:BRAIN + CREATURE:FORGOTTEN_BEAST_58:LUNG +!CREATURE:FORGOTTEN_BEAST_58:HEART +!CREATURE:FORGOTTEN_BEAST_58:LIVER +CREATURE:FORGOTTEN_BEAST_58:GUT +#CREATURE:FORGOTTEN_BEAST_58:STOMACH +#CREATURE:FORGOTTEN_BEAST_58:GIZZARD +$CREATURE:FORGOTTEN_BEAST_58:PANCREAS +"CREATURE:FORGOTTEN_BEAST_58:SPLEEN +"CREATURE:FORGOTTEN_BEAST_58:KIDNEY +"CREATURE:FORGOTTEN_BEAST_59:MUSCLE +CREATURE:FORGOTTEN_BEAST_59:EYE +!CREATURE:FORGOTTEN_BEAST_59:BRAIN + CREATURE:FORGOTTEN_BEAST_59:LUNG +!CREATURE:FORGOTTEN_BEAST_59:HEART +!CREATURE:FORGOTTEN_BEAST_59:LIVER +CREATURE:FORGOTTEN_BEAST_59:GUT +#CREATURE:FORGOTTEN_BEAST_59:STOMACH +#CREATURE:FORGOTTEN_BEAST_59:GIZZARD +$CREATURE:FORGOTTEN_BEAST_59:PANCREAS +"CREATURE:FORGOTTEN_BEAST_59:SPLEEN +"CREATURE:FORGOTTEN_BEAST_59:KIDNEY +"CREATURE:FORGOTTEN_BEAST_60:MUSCLE +CREATURE:FORGOTTEN_BEAST_60:EYE +!CREATURE:FORGOTTEN_BEAST_60:BRAIN + CREATURE:FORGOTTEN_BEAST_60:LUNG +!CREATURE:FORGOTTEN_BEAST_60:HEART +!CREATURE:FORGOTTEN_BEAST_60:LIVER +CREATURE:FORGOTTEN_BEAST_60:GUT +#CREATURE:FORGOTTEN_BEAST_60:STOMACH +#CREATURE:FORGOTTEN_BEAST_60:GIZZARD +$CREATURE:FORGOTTEN_BEAST_60:PANCREAS +"CREATURE:FORGOTTEN_BEAST_60:SPLEEN +"CREATURE:FORGOTTEN_BEAST_60:KIDNEY +"CREATURE:FORGOTTEN_BEAST_61:MUSCLE +CREATURE:FORGOTTEN_BEAST_61:EYE +!CREATURE:FORGOTTEN_BEAST_61:BRAIN + CREATURE:FORGOTTEN_BEAST_61:LUNG +!CREATURE:FORGOTTEN_BEAST_61:HEART +!CREATURE:FORGOTTEN_BEAST_61:LIVER +CREATURE:FORGOTTEN_BEAST_61:GUT +#CREATURE:FORGOTTEN_BEAST_61:STOMACH +#CREATURE:FORGOTTEN_BEAST_61:GIZZARD +$CREATURE:FORGOTTEN_BEAST_61:PANCREAS +"CREATURE:FORGOTTEN_BEAST_61:SPLEEN +"CREATURE:FORGOTTEN_BEAST_61:KIDNEY +"CREATURE:FORGOTTEN_BEAST_64:MUSCLE +CREATURE:FORGOTTEN_BEAST_64:EYE +!CREATURE:FORGOTTEN_BEAST_64:BRAIN + CREATURE:FORGOTTEN_BEAST_64:LUNG +!CREATURE:FORGOTTEN_BEAST_64:HEART +!CREATURE:FORGOTTEN_BEAST_64:LIVER +CREATURE:FORGOTTEN_BEAST_64:GUT +#CREATURE:FORGOTTEN_BEAST_64:STOMACH +#CREATURE:FORGOTTEN_BEAST_64:GIZZARD +$CREATURE:FORGOTTEN_BEAST_64:PANCREAS +"CREATURE:FORGOTTEN_BEAST_64:SPLEEN +"CREATURE:FORGOTTEN_BEAST_64:KIDNEY +"CREATURE:FORGOTTEN_BEAST_66:MUSCLE +CREATURE:FORGOTTEN_BEAST_66:EYE +!CREATURE:FORGOTTEN_BEAST_66:BRAIN + CREATURE:FORGOTTEN_BEAST_66:LUNG +!CREATURE:FORGOTTEN_BEAST_66:HEART +!CREATURE:FORGOTTEN_BEAST_66:LIVER +CREATURE:FORGOTTEN_BEAST_66:GUT +#CREATURE:FORGOTTEN_BEAST_66:STOMACH +#CREATURE:FORGOTTEN_BEAST_66:GIZZARD +$CREATURE:FORGOTTEN_BEAST_66:PANCREAS +"CREATURE:FORGOTTEN_BEAST_66:SPLEEN +"CREATURE:FORGOTTEN_BEAST_66:KIDNEY +"CREATURE:FORGOTTEN_BEAST_67:MUSCLE +CREATURE:FORGOTTEN_BEAST_67:EYE +!CREATURE:FORGOTTEN_BEAST_67:BRAIN + CREATURE:FORGOTTEN_BEAST_67:LUNG +!CREATURE:FORGOTTEN_BEAST_67:HEART +!CREATURE:FORGOTTEN_BEAST_67:LIVER +CREATURE:FORGOTTEN_BEAST_67:GUT +#CREATURE:FORGOTTEN_BEAST_67:STOMACH +#CREATURE:FORGOTTEN_BEAST_67:GIZZARD +$CREATURE:FORGOTTEN_BEAST_67:PANCREAS +"CREATURE:FORGOTTEN_BEAST_67:SPLEEN +"CREATURE:FORGOTTEN_BEAST_67:KIDNEY +"CREATURE:FORGOTTEN_BEAST_69:MUSCLE +CREATURE:FORGOTTEN_BEAST_69:EYE +!CREATURE:FORGOTTEN_BEAST_69:BRAIN + CREATURE:FORGOTTEN_BEAST_69:LUNG +!CREATURE:FORGOTTEN_BEAST_69:HEART +!CREATURE:FORGOTTEN_BEAST_69:LIVER +CREATURE:FORGOTTEN_BEAST_69:GUT +#CREATURE:FORGOTTEN_BEAST_69:STOMACH +#CREATURE:FORGOTTEN_BEAST_69:GIZZARD +$CREATURE:FORGOTTEN_BEAST_69:PANCREAS +"CREATURE:FORGOTTEN_BEAST_69:SPLEEN +"CREATURE:FORGOTTEN_BEAST_69:KIDNEY +"CREATURE:FORGOTTEN_BEAST_71:MUSCLE +CREATURE:FORGOTTEN_BEAST_71:EYE +!CREATURE:FORGOTTEN_BEAST_71:BRAIN + CREATURE:FORGOTTEN_BEAST_71:LUNG +!CREATURE:FORGOTTEN_BEAST_71:HEART +!CREATURE:FORGOTTEN_BEAST_71:LIVER +CREATURE:FORGOTTEN_BEAST_71:GUT +#CREATURE:FORGOTTEN_BEAST_71:STOMACH +#CREATURE:FORGOTTEN_BEAST_71:GIZZARD +$CREATURE:FORGOTTEN_BEAST_71:PANCREAS +"CREATURE:FORGOTTEN_BEAST_71:SPLEEN +"CREATURE:FORGOTTEN_BEAST_71:KIDNEY +"CREATURE:FORGOTTEN_BEAST_72:MUSCLE +CREATURE:FORGOTTEN_BEAST_72:EYE +!CREATURE:FORGOTTEN_BEAST_72:BRAIN + CREATURE:FORGOTTEN_BEAST_72:LUNG +!CREATURE:FORGOTTEN_BEAST_72:HEART +!CREATURE:FORGOTTEN_BEAST_72:LIVER +CREATURE:FORGOTTEN_BEAST_72:GUT +#CREATURE:FORGOTTEN_BEAST_72:STOMACH +#CREATURE:FORGOTTEN_BEAST_72:GIZZARD +$CREATURE:FORGOTTEN_BEAST_72:PANCREAS +"CREATURE:FORGOTTEN_BEAST_72:SPLEEN +"CREATURE:FORGOTTEN_BEAST_72:KIDNEY +"CREATURE:FORGOTTEN_BEAST_73:MUSCLE +CREATURE:FORGOTTEN_BEAST_73:EYE +!CREATURE:FORGOTTEN_BEAST_73:BRAIN + CREATURE:FORGOTTEN_BEAST_73:LUNG +!CREATURE:FORGOTTEN_BEAST_73:HEART +!CREATURE:FORGOTTEN_BEAST_73:LIVER +CREATURE:FORGOTTEN_BEAST_73:GUT +#CREATURE:FORGOTTEN_BEAST_73:STOMACH +#CREATURE:FORGOTTEN_BEAST_73:GIZZARD +$CREATURE:FORGOTTEN_BEAST_73:PANCREAS +"CREATURE:FORGOTTEN_BEAST_73:SPLEEN +"CREATURE:FORGOTTEN_BEAST_73:KIDNEY +"CREATURE:FORGOTTEN_BEAST_74:MUSCLE +CREATURE:FORGOTTEN_BEAST_74:EYE +!CREATURE:FORGOTTEN_BEAST_74:BRAIN + CREATURE:FORGOTTEN_BEAST_74:LUNG +!CREATURE:FORGOTTEN_BEAST_74:HEART +!CREATURE:FORGOTTEN_BEAST_74:LIVER +CREATURE:FORGOTTEN_BEAST_74:GUT +#CREATURE:FORGOTTEN_BEAST_74:STOMACH +#CREATURE:FORGOTTEN_BEAST_74:GIZZARD +$CREATURE:FORGOTTEN_BEAST_74:PANCREAS +"CREATURE:FORGOTTEN_BEAST_74:SPLEEN +"CREATURE:FORGOTTEN_BEAST_74:KIDNEY +"CREATURE:FORGOTTEN_BEAST_75:MUSCLE +CREATURE:FORGOTTEN_BEAST_75:EYE +!CREATURE:FORGOTTEN_BEAST_75:BRAIN + CREATURE:FORGOTTEN_BEAST_75:LUNG +!CREATURE:FORGOTTEN_BEAST_75:HEART +!CREATURE:FORGOTTEN_BEAST_75:LIVER +CREATURE:FORGOTTEN_BEAST_75:GUT +#CREATURE:FORGOTTEN_BEAST_75:STOMACH +#CREATURE:FORGOTTEN_BEAST_75:GIZZARD +$CREATURE:FORGOTTEN_BEAST_75:PANCREAS +"CREATURE:FORGOTTEN_BEAST_75:SPLEEN +"CREATURE:FORGOTTEN_BEAST_75:KIDNEY +"CREATURE:FORGOTTEN_BEAST_78:MUSCLE +CREATURE:FORGOTTEN_BEAST_78:EYE +!CREATURE:FORGOTTEN_BEAST_78:BRAIN + CREATURE:FORGOTTEN_BEAST_78:LUNG +!CREATURE:FORGOTTEN_BEAST_78:HEART +!CREATURE:FORGOTTEN_BEAST_78:LIVER +CREATURE:FORGOTTEN_BEAST_78:GUT +#CREATURE:FORGOTTEN_BEAST_78:STOMACH +#CREATURE:FORGOTTEN_BEAST_78:GIZZARD +$CREATURE:FORGOTTEN_BEAST_78:PANCREAS +"CREATURE:FORGOTTEN_BEAST_78:SPLEEN +"CREATURE:FORGOTTEN_BEAST_78:KIDNEY +"CREATURE:FORGOTTEN_BEAST_80:MUSCLE +CREATURE:FORGOTTEN_BEAST_80:EYE +!CREATURE:FORGOTTEN_BEAST_80:BRAIN + CREATURE:FORGOTTEN_BEAST_80:LUNG +!CREATURE:FORGOTTEN_BEAST_80:HEART +!CREATURE:FORGOTTEN_BEAST_80:LIVER +CREATURE:FORGOTTEN_BEAST_80:GUT +#CREATURE:FORGOTTEN_BEAST_80:STOMACH +#CREATURE:FORGOTTEN_BEAST_80:GIZZARD +$CREATURE:FORGOTTEN_BEAST_80:PANCREAS +"CREATURE:FORGOTTEN_BEAST_80:SPLEEN +"CREATURE:FORGOTTEN_BEAST_80:KIDNEY +"CREATURE:FORGOTTEN_BEAST_81:MUSCLE +CREATURE:FORGOTTEN_BEAST_81:EYE +!CREATURE:FORGOTTEN_BEAST_81:BRAIN + CREATURE:FORGOTTEN_BEAST_81:LUNG +!CREATURE:FORGOTTEN_BEAST_81:HEART +!CREATURE:FORGOTTEN_BEAST_81:LIVER +CREATURE:FORGOTTEN_BEAST_81:GUT +#CREATURE:FORGOTTEN_BEAST_81:STOMACH +#CREATURE:FORGOTTEN_BEAST_81:GIZZARD +$CREATURE:FORGOTTEN_BEAST_81:PANCREAS +"CREATURE:FORGOTTEN_BEAST_81:SPLEEN +"CREATURE:FORGOTTEN_BEAST_81:KIDNEY +"CREATURE:FORGOTTEN_BEAST_82:MUSCLE +CREATURE:FORGOTTEN_BEAST_82:EYE +!CREATURE:FORGOTTEN_BEAST_82:BRAIN + CREATURE:FORGOTTEN_BEAST_82:LUNG +!CREATURE:FORGOTTEN_BEAST_82:HEART +!CREATURE:FORGOTTEN_BEAST_82:LIVER +CREATURE:FORGOTTEN_BEAST_82:GUT +#CREATURE:FORGOTTEN_BEAST_82:STOMACH +#CREATURE:FORGOTTEN_BEAST_82:GIZZARD +$CREATURE:FORGOTTEN_BEAST_82:PANCREAS +"CREATURE:FORGOTTEN_BEAST_82:SPLEEN +"CREATURE:FORGOTTEN_BEAST_82:KIDNEY +"CREATURE:FORGOTTEN_BEAST_83:MUSCLE +CREATURE:FORGOTTEN_BEAST_83:EYE +!CREATURE:FORGOTTEN_BEAST_83:BRAIN + CREATURE:FORGOTTEN_BEAST_83:LUNG +!CREATURE:FORGOTTEN_BEAST_83:HEART +!CREATURE:FORGOTTEN_BEAST_83:LIVER +CREATURE:FORGOTTEN_BEAST_83:GUT +#CREATURE:FORGOTTEN_BEAST_83:STOMACH +#CREATURE:FORGOTTEN_BEAST_83:GIZZARD +$CREATURE:FORGOTTEN_BEAST_83:PANCREAS +"CREATURE:FORGOTTEN_BEAST_83:SPLEEN +"CREATURE:FORGOTTEN_BEAST_83:KIDNEY +"CREATURE:FORGOTTEN_BEAST_84:MUSCLE +CREATURE:FORGOTTEN_BEAST_84:EYE +!CREATURE:FORGOTTEN_BEAST_84:BRAIN + CREATURE:FORGOTTEN_BEAST_84:LUNG +!CREATURE:FORGOTTEN_BEAST_84:HEART +!CREATURE:FORGOTTEN_BEAST_84:LIVER +CREATURE:FORGOTTEN_BEAST_84:GUT +#CREATURE:FORGOTTEN_BEAST_84:STOMACH +#CREATURE:FORGOTTEN_BEAST_84:GIZZARD +$CREATURE:FORGOTTEN_BEAST_84:PANCREAS +"CREATURE:FORGOTTEN_BEAST_84:SPLEEN +"CREATURE:FORGOTTEN_BEAST_84:KIDNEY +"CREATURE:FORGOTTEN_BEAST_86:MUSCLE +CREATURE:FORGOTTEN_BEAST_86:EYE +!CREATURE:FORGOTTEN_BEAST_86:BRAIN + CREATURE:FORGOTTEN_BEAST_86:LUNG +!CREATURE:FORGOTTEN_BEAST_86:HEART +!CREATURE:FORGOTTEN_BEAST_86:LIVER +CREATURE:FORGOTTEN_BEAST_86:GUT +#CREATURE:FORGOTTEN_BEAST_86:STOMACH +#CREATURE:FORGOTTEN_BEAST_86:GIZZARD +$CREATURE:FORGOTTEN_BEAST_86:PANCREAS +"CREATURE:FORGOTTEN_BEAST_86:SPLEEN +"CREATURE:FORGOTTEN_BEAST_86:KIDNEY +"CREATURE:FORGOTTEN_BEAST_87:MUSCLE +CREATURE:FORGOTTEN_BEAST_87:EYE +!CREATURE:FORGOTTEN_BEAST_87:BRAIN + CREATURE:FORGOTTEN_BEAST_87:LUNG +!CREATURE:FORGOTTEN_BEAST_87:HEART +!CREATURE:FORGOTTEN_BEAST_87:LIVER +CREATURE:FORGOTTEN_BEAST_87:GUT +#CREATURE:FORGOTTEN_BEAST_87:STOMACH +#CREATURE:FORGOTTEN_BEAST_87:GIZZARD +$CREATURE:FORGOTTEN_BEAST_87:PANCREAS +"CREATURE:FORGOTTEN_BEAST_87:SPLEEN +"CREATURE:FORGOTTEN_BEAST_87:KIDNEY +"CREATURE:FORGOTTEN_BEAST_89:MUSCLE +CREATURE:FORGOTTEN_BEAST_89:EYE +!CREATURE:FORGOTTEN_BEAST_89:BRAIN + CREATURE:FORGOTTEN_BEAST_89:LUNG +!CREATURE:FORGOTTEN_BEAST_89:HEART +!CREATURE:FORGOTTEN_BEAST_89:LIVER +CREATURE:FORGOTTEN_BEAST_89:GUT +#CREATURE:FORGOTTEN_BEAST_89:STOMACH +#CREATURE:FORGOTTEN_BEAST_89:GIZZARD +$CREATURE:FORGOTTEN_BEAST_89:PANCREAS +"CREATURE:FORGOTTEN_BEAST_89:SPLEEN +"CREATURE:FORGOTTEN_BEAST_89:KIDNEY +"CREATURE:FORGOTTEN_BEAST_90:MUSCLE +CREATURE:FORGOTTEN_BEAST_90:EYE +!CREATURE:FORGOTTEN_BEAST_90:BRAIN + CREATURE:FORGOTTEN_BEAST_90:LUNG +!CREATURE:FORGOTTEN_BEAST_90:HEART +!CREATURE:FORGOTTEN_BEAST_90:LIVER +CREATURE:FORGOTTEN_BEAST_90:GUT +#CREATURE:FORGOTTEN_BEAST_90:STOMACH +#CREATURE:FORGOTTEN_BEAST_90:GIZZARD +$CREATURE:FORGOTTEN_BEAST_90:PANCREAS +"CREATURE:FORGOTTEN_BEAST_90:SPLEEN +"CREATURE:FORGOTTEN_BEAST_90:KIDNEY +"CREATURE:FORGOTTEN_BEAST_92:MUSCLE +CREATURE:FORGOTTEN_BEAST_92:EYE +!CREATURE:FORGOTTEN_BEAST_92:BRAIN + CREATURE:FORGOTTEN_BEAST_92:LUNG +!CREATURE:FORGOTTEN_BEAST_92:HEART +!CREATURE:FORGOTTEN_BEAST_92:LIVER +CREATURE:FORGOTTEN_BEAST_92:GUT +#CREATURE:FORGOTTEN_BEAST_92:STOMACH +#CREATURE:FORGOTTEN_BEAST_92:GIZZARD +$CREATURE:FORGOTTEN_BEAST_92:PANCREAS +"CREATURE:FORGOTTEN_BEAST_92:SPLEEN +"CREATURE:FORGOTTEN_BEAST_92:KIDNEY +"CREATURE:FORGOTTEN_BEAST_94:MUSCLE +CREATURE:FORGOTTEN_BEAST_94:EYE +!CREATURE:FORGOTTEN_BEAST_94:BRAIN + CREATURE:FORGOTTEN_BEAST_94:LUNG +!CREATURE:FORGOTTEN_BEAST_94:HEART +!CREATURE:FORGOTTEN_BEAST_94:LIVER +CREATURE:FORGOTTEN_BEAST_94:GUT +#CREATURE:FORGOTTEN_BEAST_94:STOMACH +#CREATURE:FORGOTTEN_BEAST_94:GIZZARD +$CREATURE:FORGOTTEN_BEAST_94:PANCREAS +"CREATURE:FORGOTTEN_BEAST_94:SPLEEN +"CREATURE:FORGOTTEN_BEAST_94:KIDNEY +"CREATURE:FORGOTTEN_BEAST_95:MUSCLE +CREATURE:FORGOTTEN_BEAST_95:EYE +!CREATURE:FORGOTTEN_BEAST_95:BRAIN + CREATURE:FORGOTTEN_BEAST_95:LUNG +!CREATURE:FORGOTTEN_BEAST_95:HEART +!CREATURE:FORGOTTEN_BEAST_95:LIVER +CREATURE:FORGOTTEN_BEAST_95:GUT +#CREATURE:FORGOTTEN_BEAST_95:STOMACH +#CREATURE:FORGOTTEN_BEAST_95:GIZZARD +$CREATURE:FORGOTTEN_BEAST_95:PANCREAS +"CREATURE:FORGOTTEN_BEAST_95:SPLEEN +"CREATURE:FORGOTTEN_BEAST_95:KIDNEY +"CREATURE:FORGOTTEN_BEAST_96:MUSCLE +CREATURE:FORGOTTEN_BEAST_96:EYE +!CREATURE:FORGOTTEN_BEAST_96:BRAIN + CREATURE:FORGOTTEN_BEAST_96:LUNG +!CREATURE:FORGOTTEN_BEAST_96:HEART +!CREATURE:FORGOTTEN_BEAST_96:LIVER +CREATURE:FORGOTTEN_BEAST_96:GUT +#CREATURE:FORGOTTEN_BEAST_96:STOMACH +#CREATURE:FORGOTTEN_BEAST_96:GIZZARD +$CREATURE:FORGOTTEN_BEAST_96:PANCREAS +"CREATURE:FORGOTTEN_BEAST_96:SPLEEN +"CREATURE:FORGOTTEN_BEAST_96:KIDNEY +"CREATURE:FORGOTTEN_BEAST_97:MUSCLE +CREATURE:FORGOTTEN_BEAST_97:EYE +!CREATURE:FORGOTTEN_BEAST_97:BRAIN + CREATURE:FORGOTTEN_BEAST_97:LUNG +!CREATURE:FORGOTTEN_BEAST_97:HEART +!CREATURE:FORGOTTEN_BEAST_97:LIVER +CREATURE:FORGOTTEN_BEAST_97:GUT +#CREATURE:FORGOTTEN_BEAST_97:STOMACH +#CREATURE:FORGOTTEN_BEAST_97:GIZZARD +$CREATURE:FORGOTTEN_BEAST_97:PANCREAS +"CREATURE:FORGOTTEN_BEAST_97:SPLEEN +"CREATURE:FORGOTTEN_BEAST_97:KIDNEY +"CREATURE:FORGOTTEN_BEAST_98:MUSCLE +CREATURE:FORGOTTEN_BEAST_98:EYE +!CREATURE:FORGOTTEN_BEAST_98:BRAIN + CREATURE:FORGOTTEN_BEAST_98:LUNG +!CREATURE:FORGOTTEN_BEAST_98:HEART +!CREATURE:FORGOTTEN_BEAST_98:LIVER +CREATURE:FORGOTTEN_BEAST_98:GUT +#CREATURE:FORGOTTEN_BEAST_98:STOMACH +#CREATURE:FORGOTTEN_BEAST_98:GIZZARD +$CREATURE:FORGOTTEN_BEAST_98:PANCREAS +"CREATURE:FORGOTTEN_BEAST_98:SPLEEN +"CREATURE:FORGOTTEN_BEAST_98:KIDNEY +#CREATURE:FORGOTTEN_BEAST_100:MUSCLE + CREATURE:FORGOTTEN_BEAST_100:EYE +"CREATURE:FORGOTTEN_BEAST_100:BRAIN +!CREATURE:FORGOTTEN_BEAST_100:LUNG +"CREATURE:FORGOTTEN_BEAST_100:HEART +"CREATURE:FORGOTTEN_BEAST_100:LIVER + CREATURE:FORGOTTEN_BEAST_100:GUT +$CREATURE:FORGOTTEN_BEAST_100:STOMACH +$CREATURE:FORGOTTEN_BEAST_100:GIZZARD +%CREATURE:FORGOTTEN_BEAST_100:PANCREAS +#CREATURE:FORGOTTEN_BEAST_100:SPLEEN +#CREATURE:FORGOTTEN_BEAST_100:KIDNEY +#CREATURE:FORGOTTEN_BEAST_105:MUSCLE + CREATURE:FORGOTTEN_BEAST_105:EYE +"CREATURE:FORGOTTEN_BEAST_105:BRAIN +!CREATURE:FORGOTTEN_BEAST_105:LUNG +"CREATURE:FORGOTTEN_BEAST_105:HEART +"CREATURE:FORGOTTEN_BEAST_105:LIVER + CREATURE:FORGOTTEN_BEAST_105:GUT +$CREATURE:FORGOTTEN_BEAST_105:STOMACH +$CREATURE:FORGOTTEN_BEAST_105:GIZZARD +%CREATURE:FORGOTTEN_BEAST_105:PANCREAS +#CREATURE:FORGOTTEN_BEAST_105:SPLEEN +#CREATURE:FORGOTTEN_BEAST_105:KIDNEY +#CREATURE:FORGOTTEN_BEAST_106:MUSCLE + CREATURE:FORGOTTEN_BEAST_106:EYE +"CREATURE:FORGOTTEN_BEAST_106:BRAIN +!CREATURE:FORGOTTEN_BEAST_106:LUNG +"CREATURE:FORGOTTEN_BEAST_106:HEART +"CREATURE:FORGOTTEN_BEAST_106:LIVER + CREATURE:FORGOTTEN_BEAST_106:GUT +$CREATURE:FORGOTTEN_BEAST_106:STOMACH +$CREATURE:FORGOTTEN_BEAST_106:GIZZARD +%CREATURE:FORGOTTEN_BEAST_106:PANCREAS +#CREATURE:FORGOTTEN_BEAST_106:SPLEEN +#CREATURE:FORGOTTEN_BEAST_106:KIDNEY +#CREATURE:FORGOTTEN_BEAST_107:MUSCLE + CREATURE:FORGOTTEN_BEAST_107:EYE +"CREATURE:FORGOTTEN_BEAST_107:BRAIN +!CREATURE:FORGOTTEN_BEAST_107:LUNG +"CREATURE:FORGOTTEN_BEAST_107:HEART +"CREATURE:FORGOTTEN_BEAST_107:LIVER + CREATURE:FORGOTTEN_BEAST_107:GUT +$CREATURE:FORGOTTEN_BEAST_107:STOMACH +$CREATURE:FORGOTTEN_BEAST_107:GIZZARD +%CREATURE:FORGOTTEN_BEAST_107:PANCREAS +#CREATURE:FORGOTTEN_BEAST_107:SPLEEN +#CREATURE:FORGOTTEN_BEAST_107:KIDNEY +#CREATURE:FORGOTTEN_BEAST_108:MUSCLE + CREATURE:FORGOTTEN_BEAST_108:EYE +"CREATURE:FORGOTTEN_BEAST_108:BRAIN +!CREATURE:FORGOTTEN_BEAST_108:LUNG +"CREATURE:FORGOTTEN_BEAST_108:HEART +"CREATURE:FORGOTTEN_BEAST_108:LIVER + CREATURE:FORGOTTEN_BEAST_108:GUT +$CREATURE:FORGOTTEN_BEAST_108:STOMACH +$CREATURE:FORGOTTEN_BEAST_108:GIZZARD +%CREATURE:FORGOTTEN_BEAST_108:PANCREAS +#CREATURE:FORGOTTEN_BEAST_108:SPLEEN +#CREATURE:FORGOTTEN_BEAST_108:KIDNEY +#CREATURE:FORGOTTEN_BEAST_109:MUSCLE + CREATURE:FORGOTTEN_BEAST_109:EYE +"CREATURE:FORGOTTEN_BEAST_109:BRAIN +!CREATURE:FORGOTTEN_BEAST_109:LUNG +"CREATURE:FORGOTTEN_BEAST_109:HEART +"CREATURE:FORGOTTEN_BEAST_109:LIVER + CREATURE:FORGOTTEN_BEAST_109:GUT +$CREATURE:FORGOTTEN_BEAST_109:STOMACH +$CREATURE:FORGOTTEN_BEAST_109:GIZZARD +%CREATURE:FORGOTTEN_BEAST_109:PANCREAS +#CREATURE:FORGOTTEN_BEAST_109:SPLEEN +#CREATURE:FORGOTTEN_BEAST_109:KIDNEY +#CREATURE:FORGOTTEN_BEAST_111:MUSCLE + CREATURE:FORGOTTEN_BEAST_111:EYE +"CREATURE:FORGOTTEN_BEAST_111:BRAIN +!CREATURE:FORGOTTEN_BEAST_111:LUNG +"CREATURE:FORGOTTEN_BEAST_111:HEART +"CREATURE:FORGOTTEN_BEAST_111:LIVER + CREATURE:FORGOTTEN_BEAST_111:GUT +$CREATURE:FORGOTTEN_BEAST_111:STOMACH +$CREATURE:FORGOTTEN_BEAST_111:GIZZARD +%CREATURE:FORGOTTEN_BEAST_111:PANCREAS +#CREATURE:FORGOTTEN_BEAST_111:SPLEEN +#CREATURE:FORGOTTEN_BEAST_111:KIDNEY +#CREATURE:FORGOTTEN_BEAST_112:MUSCLE + CREATURE:FORGOTTEN_BEAST_112:EYE +"CREATURE:FORGOTTEN_BEAST_112:BRAIN +!CREATURE:FORGOTTEN_BEAST_112:LUNG +"CREATURE:FORGOTTEN_BEAST_112:HEART +"CREATURE:FORGOTTEN_BEAST_112:LIVER + CREATURE:FORGOTTEN_BEAST_112:GUT +$CREATURE:FORGOTTEN_BEAST_112:STOMACH +$CREATURE:FORGOTTEN_BEAST_112:GIZZARD +%CREATURE:FORGOTTEN_BEAST_112:PANCREAS +#CREATURE:FORGOTTEN_BEAST_112:SPLEEN +#CREATURE:FORGOTTEN_BEAST_112:KIDNEY +#CREATURE:FORGOTTEN_BEAST_113:MUSCLE + CREATURE:FORGOTTEN_BEAST_113:EYE +"CREATURE:FORGOTTEN_BEAST_113:BRAIN +!CREATURE:FORGOTTEN_BEAST_113:LUNG +"CREATURE:FORGOTTEN_BEAST_113:HEART +"CREATURE:FORGOTTEN_BEAST_113:LIVER + CREATURE:FORGOTTEN_BEAST_113:GUT +$CREATURE:FORGOTTEN_BEAST_113:STOMACH +$CREATURE:FORGOTTEN_BEAST_113:GIZZARD +%CREATURE:FORGOTTEN_BEAST_113:PANCREAS +#CREATURE:FORGOTTEN_BEAST_113:SPLEEN +#CREATURE:FORGOTTEN_BEAST_113:KIDNEY +#CREATURE:FORGOTTEN_BEAST_114:MUSCLE + CREATURE:FORGOTTEN_BEAST_114:EYE +"CREATURE:FORGOTTEN_BEAST_114:BRAIN +!CREATURE:FORGOTTEN_BEAST_114:LUNG +"CREATURE:FORGOTTEN_BEAST_114:HEART +"CREATURE:FORGOTTEN_BEAST_114:LIVER + CREATURE:FORGOTTEN_BEAST_114:GUT +$CREATURE:FORGOTTEN_BEAST_114:STOMACH +$CREATURE:FORGOTTEN_BEAST_114:GIZZARD +%CREATURE:FORGOTTEN_BEAST_114:PANCREAS +#CREATURE:FORGOTTEN_BEAST_114:SPLEEN +#CREATURE:FORGOTTEN_BEAST_114:KIDNEY +#CREATURE:FORGOTTEN_BEAST_115:MUSCLE + CREATURE:FORGOTTEN_BEAST_115:EYE +"CREATURE:FORGOTTEN_BEAST_115:BRAIN +!CREATURE:FORGOTTEN_BEAST_115:LUNG +"CREATURE:FORGOTTEN_BEAST_115:HEART +"CREATURE:FORGOTTEN_BEAST_115:LIVER + CREATURE:FORGOTTEN_BEAST_115:GUT +$CREATURE:FORGOTTEN_BEAST_115:STOMACH +$CREATURE:FORGOTTEN_BEAST_115:GIZZARD +%CREATURE:FORGOTTEN_BEAST_115:PANCREAS +#CREATURE:FORGOTTEN_BEAST_115:SPLEEN +#CREATURE:FORGOTTEN_BEAST_115:KIDNEY +#CREATURE:FORGOTTEN_BEAST_116:MUSCLE + CREATURE:FORGOTTEN_BEAST_116:EYE +"CREATURE:FORGOTTEN_BEAST_116:BRAIN +!CREATURE:FORGOTTEN_BEAST_116:LUNG +"CREATURE:FORGOTTEN_BEAST_116:HEART +"CREATURE:FORGOTTEN_BEAST_116:LIVER + CREATURE:FORGOTTEN_BEAST_116:GUT +$CREATURE:FORGOTTEN_BEAST_116:STOMACH +$CREATURE:FORGOTTEN_BEAST_116:GIZZARD +%CREATURE:FORGOTTEN_BEAST_116:PANCREAS +#CREATURE:FORGOTTEN_BEAST_116:SPLEEN +#CREATURE:FORGOTTEN_BEAST_116:KIDNEY +#CREATURE:FORGOTTEN_BEAST_117:MUSCLE + CREATURE:FORGOTTEN_BEAST_117:EYE +"CREATURE:FORGOTTEN_BEAST_117:BRAIN +!CREATURE:FORGOTTEN_BEAST_117:LUNG +"CREATURE:FORGOTTEN_BEAST_117:HEART +"CREATURE:FORGOTTEN_BEAST_117:LIVER + CREATURE:FORGOTTEN_BEAST_117:GUT +$CREATURE:FORGOTTEN_BEAST_117:STOMACH +$CREATURE:FORGOTTEN_BEAST_117:GIZZARD +%CREATURE:FORGOTTEN_BEAST_117:PANCREAS +#CREATURE:FORGOTTEN_BEAST_117:SPLEEN +#CREATURE:FORGOTTEN_BEAST_117:KIDNEY +#CREATURE:FORGOTTEN_BEAST_118:MUSCLE + CREATURE:FORGOTTEN_BEAST_118:EYE +"CREATURE:FORGOTTEN_BEAST_118:BRAIN +!CREATURE:FORGOTTEN_BEAST_118:LUNG +"CREATURE:FORGOTTEN_BEAST_118:HEART +"CREATURE:FORGOTTEN_BEAST_118:LIVER + CREATURE:FORGOTTEN_BEAST_118:GUT +$CREATURE:FORGOTTEN_BEAST_118:STOMACH +$CREATURE:FORGOTTEN_BEAST_118:GIZZARD +%CREATURE:FORGOTTEN_BEAST_118:PANCREAS +#CREATURE:FORGOTTEN_BEAST_118:SPLEEN +#CREATURE:FORGOTTEN_BEAST_118:KIDNEY +#CREATURE:FORGOTTEN_BEAST_119:MUSCLE + CREATURE:FORGOTTEN_BEAST_119:EYE +"CREATURE:FORGOTTEN_BEAST_119:BRAIN +!CREATURE:FORGOTTEN_BEAST_119:LUNG +"CREATURE:FORGOTTEN_BEAST_119:HEART +"CREATURE:FORGOTTEN_BEAST_119:LIVER + CREATURE:FORGOTTEN_BEAST_119:GUT +$CREATURE:FORGOTTEN_BEAST_119:STOMACH +$CREATURE:FORGOTTEN_BEAST_119:GIZZARD +%CREATURE:FORGOTTEN_BEAST_119:PANCREAS +#CREATURE:FORGOTTEN_BEAST_119:SPLEEN +#CREATURE:FORGOTTEN_BEAST_119:KIDNEY +#CREATURE:FORGOTTEN_BEAST_120:MUSCLE + CREATURE:FORGOTTEN_BEAST_120:EYE +"CREATURE:FORGOTTEN_BEAST_120:BRAIN +!CREATURE:FORGOTTEN_BEAST_120:LUNG +"CREATURE:FORGOTTEN_BEAST_120:HEART +"CREATURE:FORGOTTEN_BEAST_120:LIVER + CREATURE:FORGOTTEN_BEAST_120:GUT +$CREATURE:FORGOTTEN_BEAST_120:STOMACH +$CREATURE:FORGOTTEN_BEAST_120:GIZZARD +%CREATURE:FORGOTTEN_BEAST_120:PANCREAS +#CREATURE:FORGOTTEN_BEAST_120:SPLEEN +#CREATURE:FORGOTTEN_BEAST_120:KIDNEY +#CREATURE:FORGOTTEN_BEAST_122:MUSCLE + CREATURE:FORGOTTEN_BEAST_122:EYE +"CREATURE:FORGOTTEN_BEAST_122:BRAIN +!CREATURE:FORGOTTEN_BEAST_122:LUNG +"CREATURE:FORGOTTEN_BEAST_122:HEART +"CREATURE:FORGOTTEN_BEAST_122:LIVER + CREATURE:FORGOTTEN_BEAST_122:GUT +$CREATURE:FORGOTTEN_BEAST_122:STOMACH +$CREATURE:FORGOTTEN_BEAST_122:GIZZARD +%CREATURE:FORGOTTEN_BEAST_122:PANCREAS +#CREATURE:FORGOTTEN_BEAST_122:SPLEEN +#CREATURE:FORGOTTEN_BEAST_122:KIDNEY +#CREATURE:FORGOTTEN_BEAST_123:MUSCLE + CREATURE:FORGOTTEN_BEAST_123:EYE +"CREATURE:FORGOTTEN_BEAST_123:BRAIN +!CREATURE:FORGOTTEN_BEAST_123:LUNG +"CREATURE:FORGOTTEN_BEAST_123:HEART +"CREATURE:FORGOTTEN_BEAST_123:LIVER + CREATURE:FORGOTTEN_BEAST_123:GUT +$CREATURE:FORGOTTEN_BEAST_123:STOMACH +$CREATURE:FORGOTTEN_BEAST_123:GIZZARD +%CREATURE:FORGOTTEN_BEAST_123:PANCREAS +#CREATURE:FORGOTTEN_BEAST_123:SPLEEN +#CREATURE:FORGOTTEN_BEAST_123:KIDNEY +#CREATURE:FORGOTTEN_BEAST_124:MUSCLE + CREATURE:FORGOTTEN_BEAST_124:EYE +"CREATURE:FORGOTTEN_BEAST_124:BRAIN +!CREATURE:FORGOTTEN_BEAST_124:LUNG +"CREATURE:FORGOTTEN_BEAST_124:HEART +"CREATURE:FORGOTTEN_BEAST_124:LIVER + CREATURE:FORGOTTEN_BEAST_124:GUT +$CREATURE:FORGOTTEN_BEAST_124:STOMACH +$CREATURE:FORGOTTEN_BEAST_124:GIZZARD +%CREATURE:FORGOTTEN_BEAST_124:PANCREAS +#CREATURE:FORGOTTEN_BEAST_124:SPLEEN +#CREATURE:FORGOTTEN_BEAST_124:KIDNEY +#CREATURE:FORGOTTEN_BEAST_125:MUSCLE + CREATURE:FORGOTTEN_BEAST_125:EYE +"CREATURE:FORGOTTEN_BEAST_125:BRAIN +!CREATURE:FORGOTTEN_BEAST_125:LUNG +"CREATURE:FORGOTTEN_BEAST_125:HEART +"CREATURE:FORGOTTEN_BEAST_125:LIVER + CREATURE:FORGOTTEN_BEAST_125:GUT +$CREATURE:FORGOTTEN_BEAST_125:STOMACH +$CREATURE:FORGOTTEN_BEAST_125:GIZZARD +%CREATURE:FORGOTTEN_BEAST_125:PANCREAS +#CREATURE:FORGOTTEN_BEAST_125:SPLEEN +#CREATURE:FORGOTTEN_BEAST_125:KIDNEY +#CREATURE:FORGOTTEN_BEAST_127:MUSCLE + CREATURE:FORGOTTEN_BEAST_127:EYE +"CREATURE:FORGOTTEN_BEAST_127:BRAIN +!CREATURE:FORGOTTEN_BEAST_127:LUNG +"CREATURE:FORGOTTEN_BEAST_127:HEART +"CREATURE:FORGOTTEN_BEAST_127:LIVER + CREATURE:FORGOTTEN_BEAST_127:GUT +$CREATURE:FORGOTTEN_BEAST_127:STOMACH +$CREATURE:FORGOTTEN_BEAST_127:GIZZARD +%CREATURE:FORGOTTEN_BEAST_127:PANCREAS +#CREATURE:FORGOTTEN_BEAST_127:SPLEEN +#CREATURE:FORGOTTEN_BEAST_127:KIDNEY +#CREATURE:FORGOTTEN_BEAST_128:MUSCLE + CREATURE:FORGOTTEN_BEAST_128:EYE +"CREATURE:FORGOTTEN_BEAST_128:BRAIN +!CREATURE:FORGOTTEN_BEAST_128:LUNG +"CREATURE:FORGOTTEN_BEAST_128:HEART +"CREATURE:FORGOTTEN_BEAST_128:LIVER + CREATURE:FORGOTTEN_BEAST_128:GUT +$CREATURE:FORGOTTEN_BEAST_128:STOMACH +$CREATURE:FORGOTTEN_BEAST_128:GIZZARD +%CREATURE:FORGOTTEN_BEAST_128:PANCREAS +#CREATURE:FORGOTTEN_BEAST_128:SPLEEN +#CREATURE:FORGOTTEN_BEAST_128:KIDNEY +#CREATURE:FORGOTTEN_BEAST_130:MUSCLE + CREATURE:FORGOTTEN_BEAST_130:EYE +"CREATURE:FORGOTTEN_BEAST_130:BRAIN +!CREATURE:FORGOTTEN_BEAST_130:LUNG +"CREATURE:FORGOTTEN_BEAST_130:HEART +"CREATURE:FORGOTTEN_BEAST_130:LIVER + CREATURE:FORGOTTEN_BEAST_130:GUT +$CREATURE:FORGOTTEN_BEAST_130:STOMACH +$CREATURE:FORGOTTEN_BEAST_130:GIZZARD +%CREATURE:FORGOTTEN_BEAST_130:PANCREAS +#CREATURE:FORGOTTEN_BEAST_130:SPLEEN +#CREATURE:FORGOTTEN_BEAST_130:KIDNEY +#CREATURE:FORGOTTEN_BEAST_131:MUSCLE + CREATURE:FORGOTTEN_BEAST_131:EYE +"CREATURE:FORGOTTEN_BEAST_131:BRAIN +!CREATURE:FORGOTTEN_BEAST_131:LUNG +"CREATURE:FORGOTTEN_BEAST_131:HEART +"CREATURE:FORGOTTEN_BEAST_131:LIVER + CREATURE:FORGOTTEN_BEAST_131:GUT +$CREATURE:FORGOTTEN_BEAST_131:STOMACH +$CREATURE:FORGOTTEN_BEAST_131:GIZZARD +%CREATURE:FORGOTTEN_BEAST_131:PANCREAS +#CREATURE:FORGOTTEN_BEAST_131:SPLEEN +#CREATURE:FORGOTTEN_BEAST_131:KIDNEY +#CREATURE:FORGOTTEN_BEAST_132:MUSCLE + CREATURE:FORGOTTEN_BEAST_132:EYE +"CREATURE:FORGOTTEN_BEAST_132:BRAIN +!CREATURE:FORGOTTEN_BEAST_132:LUNG +"CREATURE:FORGOTTEN_BEAST_132:HEART +"CREATURE:FORGOTTEN_BEAST_132:LIVER + CREATURE:FORGOTTEN_BEAST_132:GUT +$CREATURE:FORGOTTEN_BEAST_132:STOMACH +$CREATURE:FORGOTTEN_BEAST_132:GIZZARD +%CREATURE:FORGOTTEN_BEAST_132:PANCREAS +#CREATURE:FORGOTTEN_BEAST_132:SPLEEN +#CREATURE:FORGOTTEN_BEAST_132:KIDNEY +#CREATURE:FORGOTTEN_BEAST_133:MUSCLE + CREATURE:FORGOTTEN_BEAST_133:EYE +"CREATURE:FORGOTTEN_BEAST_133:BRAIN +!CREATURE:FORGOTTEN_BEAST_133:LUNG +"CREATURE:FORGOTTEN_BEAST_133:HEART +"CREATURE:FORGOTTEN_BEAST_133:LIVER + CREATURE:FORGOTTEN_BEAST_133:GUT +$CREATURE:FORGOTTEN_BEAST_133:STOMACH +$CREATURE:FORGOTTEN_BEAST_133:GIZZARD +%CREATURE:FORGOTTEN_BEAST_133:PANCREAS +#CREATURE:FORGOTTEN_BEAST_133:SPLEEN +#CREATURE:FORGOTTEN_BEAST_133:KIDNEY +#CREATURE:FORGOTTEN_BEAST_134:MUSCLE + CREATURE:FORGOTTEN_BEAST_134:EYE +"CREATURE:FORGOTTEN_BEAST_134:BRAIN +!CREATURE:FORGOTTEN_BEAST_134:LUNG +"CREATURE:FORGOTTEN_BEAST_134:HEART +"CREATURE:FORGOTTEN_BEAST_134:LIVER + CREATURE:FORGOTTEN_BEAST_134:GUT +$CREATURE:FORGOTTEN_BEAST_134:STOMACH +$CREATURE:FORGOTTEN_BEAST_134:GIZZARD +%CREATURE:FORGOTTEN_BEAST_134:PANCREAS +#CREATURE:FORGOTTEN_BEAST_134:SPLEEN +#CREATURE:FORGOTTEN_BEAST_134:KIDNEY +#CREATURE:FORGOTTEN_BEAST_135:MUSCLE + CREATURE:FORGOTTEN_BEAST_135:EYE +"CREATURE:FORGOTTEN_BEAST_135:BRAIN +!CREATURE:FORGOTTEN_BEAST_135:LUNG +"CREATURE:FORGOTTEN_BEAST_135:HEART +"CREATURE:FORGOTTEN_BEAST_135:LIVER + CREATURE:FORGOTTEN_BEAST_135:GUT +$CREATURE:FORGOTTEN_BEAST_135:STOMACH +$CREATURE:FORGOTTEN_BEAST_135:GIZZARD +%CREATURE:FORGOTTEN_BEAST_135:PANCREAS +#CREATURE:FORGOTTEN_BEAST_135:SPLEEN +#CREATURE:FORGOTTEN_BEAST_135:KIDNEY +#CREATURE:FORGOTTEN_BEAST_137:MUSCLE + CREATURE:FORGOTTEN_BEAST_137:EYE +"CREATURE:FORGOTTEN_BEAST_137:BRAIN +!CREATURE:FORGOTTEN_BEAST_137:LUNG +"CREATURE:FORGOTTEN_BEAST_137:HEART +"CREATURE:FORGOTTEN_BEAST_137:LIVER + CREATURE:FORGOTTEN_BEAST_137:GUT +$CREATURE:FORGOTTEN_BEAST_137:STOMACH +$CREATURE:FORGOTTEN_BEAST_137:GIZZARD +%CREATURE:FORGOTTEN_BEAST_137:PANCREAS +#CREATURE:FORGOTTEN_BEAST_137:SPLEEN +#CREATURE:FORGOTTEN_BEAST_137:KIDNEY +#CREATURE:FORGOTTEN_BEAST_138:MUSCLE + CREATURE:FORGOTTEN_BEAST_138:EYE +"CREATURE:FORGOTTEN_BEAST_138:BRAIN +!CREATURE:FORGOTTEN_BEAST_138:LUNG +"CREATURE:FORGOTTEN_BEAST_138:HEART +"CREATURE:FORGOTTEN_BEAST_138:LIVER + CREATURE:FORGOTTEN_BEAST_138:GUT +$CREATURE:FORGOTTEN_BEAST_138:STOMACH +$CREATURE:FORGOTTEN_BEAST_138:GIZZARD +%CREATURE:FORGOTTEN_BEAST_138:PANCREAS +#CREATURE:FORGOTTEN_BEAST_138:SPLEEN +#CREATURE:FORGOTTEN_BEAST_138:KIDNEY +#CREATURE:FORGOTTEN_BEAST_139:MUSCLE + CREATURE:FORGOTTEN_BEAST_139:EYE +"CREATURE:FORGOTTEN_BEAST_139:BRAIN +!CREATURE:FORGOTTEN_BEAST_139:LUNG +"CREATURE:FORGOTTEN_BEAST_139:HEART +"CREATURE:FORGOTTEN_BEAST_139:LIVER + CREATURE:FORGOTTEN_BEAST_139:GUT +$CREATURE:FORGOTTEN_BEAST_139:STOMACH +$CREATURE:FORGOTTEN_BEAST_139:GIZZARD +%CREATURE:FORGOTTEN_BEAST_139:PANCREAS +#CREATURE:FORGOTTEN_BEAST_139:SPLEEN +#CREATURE:FORGOTTEN_BEAST_139:KIDNEY +#CREATURE:FORGOTTEN_BEAST_141:MUSCLE + CREATURE:FORGOTTEN_BEAST_141:EYE +"CREATURE:FORGOTTEN_BEAST_141:BRAIN +!CREATURE:FORGOTTEN_BEAST_141:LUNG +"CREATURE:FORGOTTEN_BEAST_141:HEART +"CREATURE:FORGOTTEN_BEAST_141:LIVER + CREATURE:FORGOTTEN_BEAST_141:GUT +$CREATURE:FORGOTTEN_BEAST_141:STOMACH +$CREATURE:FORGOTTEN_BEAST_141:GIZZARD +%CREATURE:FORGOTTEN_BEAST_141:PANCREAS +#CREATURE:FORGOTTEN_BEAST_141:SPLEEN +#CREATURE:FORGOTTEN_BEAST_141:KIDNEY +#CREATURE:FORGOTTEN_BEAST_142:MUSCLE + CREATURE:FORGOTTEN_BEAST_142:EYE +"CREATURE:FORGOTTEN_BEAST_142:BRAIN +!CREATURE:FORGOTTEN_BEAST_142:LUNG +"CREATURE:FORGOTTEN_BEAST_142:HEART +"CREATURE:FORGOTTEN_BEAST_142:LIVER + CREATURE:FORGOTTEN_BEAST_142:GUT +$CREATURE:FORGOTTEN_BEAST_142:STOMACH +$CREATURE:FORGOTTEN_BEAST_142:GIZZARD +%CREATURE:FORGOTTEN_BEAST_142:PANCREAS +#CREATURE:FORGOTTEN_BEAST_142:SPLEEN +#CREATURE:FORGOTTEN_BEAST_142:KIDNEY +#CREATURE:FORGOTTEN_BEAST_144:MUSCLE + CREATURE:FORGOTTEN_BEAST_144:EYE +"CREATURE:FORGOTTEN_BEAST_144:BRAIN +!CREATURE:FORGOTTEN_BEAST_144:LUNG +"CREATURE:FORGOTTEN_BEAST_144:HEART +"CREATURE:FORGOTTEN_BEAST_144:LIVER + CREATURE:FORGOTTEN_BEAST_144:GUT +$CREATURE:FORGOTTEN_BEAST_144:STOMACH +$CREATURE:FORGOTTEN_BEAST_144:GIZZARD +%CREATURE:FORGOTTEN_BEAST_144:PANCREAS +#CREATURE:FORGOTTEN_BEAST_144:SPLEEN +#CREATURE:FORGOTTEN_BEAST_144:KIDNEY +#CREATURE:FORGOTTEN_BEAST_146:MUSCLE + CREATURE:FORGOTTEN_BEAST_146:EYE +"CREATURE:FORGOTTEN_BEAST_146:BRAIN +!CREATURE:FORGOTTEN_BEAST_146:LUNG +"CREATURE:FORGOTTEN_BEAST_146:HEART +"CREATURE:FORGOTTEN_BEAST_146:LIVER + CREATURE:FORGOTTEN_BEAST_146:GUT +$CREATURE:FORGOTTEN_BEAST_146:STOMACH +$CREATURE:FORGOTTEN_BEAST_146:GIZZARD +%CREATURE:FORGOTTEN_BEAST_146:PANCREAS +#CREATURE:FORGOTTEN_BEAST_146:SPLEEN +#CREATURE:FORGOTTEN_BEAST_146:KIDNEY +#CREATURE:FORGOTTEN_BEAST_148:MUSCLE + CREATURE:FORGOTTEN_BEAST_148:EYE +"CREATURE:FORGOTTEN_BEAST_148:BRAIN +!CREATURE:FORGOTTEN_BEAST_148:LUNG +"CREATURE:FORGOTTEN_BEAST_148:HEART +"CREATURE:FORGOTTEN_BEAST_148:LIVER + CREATURE:FORGOTTEN_BEAST_148:GUT +$CREATURE:FORGOTTEN_BEAST_148:STOMACH +$CREATURE:FORGOTTEN_BEAST_148:GIZZARD +%CREATURE:FORGOTTEN_BEAST_148:PANCREAS +#CREATURE:FORGOTTEN_BEAST_148:SPLEEN +#CREATURE:FORGOTTEN_BEAST_148:KIDNEY +#CREATURE:FORGOTTEN_BEAST_149:MUSCLE + CREATURE:FORGOTTEN_BEAST_149:EYE +"CREATURE:FORGOTTEN_BEAST_149:BRAIN +!CREATURE:FORGOTTEN_BEAST_149:LUNG +"CREATURE:FORGOTTEN_BEAST_149:HEART +"CREATURE:FORGOTTEN_BEAST_149:LIVER + CREATURE:FORGOTTEN_BEAST_149:GUT +$CREATURE:FORGOTTEN_BEAST_149:STOMACH +$CREATURE:FORGOTTEN_BEAST_149:GIZZARD +%CREATURE:FORGOTTEN_BEAST_149:PANCREAS +#CREATURE:FORGOTTEN_BEAST_149:SPLEEN +#CREATURE:FORGOTTEN_BEAST_149:KIDNEY +#CREATURE:FORGOTTEN_BEAST_150:MUSCLE + CREATURE:FORGOTTEN_BEAST_150:EYE +"CREATURE:FORGOTTEN_BEAST_150:BRAIN +!CREATURE:FORGOTTEN_BEAST_150:LUNG +"CREATURE:FORGOTTEN_BEAST_150:HEART +"CREATURE:FORGOTTEN_BEAST_150:LIVER + CREATURE:FORGOTTEN_BEAST_150:GUT +$CREATURE:FORGOTTEN_BEAST_150:STOMACH +$CREATURE:FORGOTTEN_BEAST_150:GIZZARD +%CREATURE:FORGOTTEN_BEAST_150:PANCREAS +#CREATURE:FORGOTTEN_BEAST_150:SPLEEN +#CREATURE:FORGOTTEN_BEAST_150:KIDNEY +#CREATURE:FORGOTTEN_BEAST_152:MUSCLE + CREATURE:FORGOTTEN_BEAST_152:EYE +"CREATURE:FORGOTTEN_BEAST_152:BRAIN +!CREATURE:FORGOTTEN_BEAST_152:LUNG +"CREATURE:FORGOTTEN_BEAST_152:HEART +"CREATURE:FORGOTTEN_BEAST_152:LIVER + CREATURE:FORGOTTEN_BEAST_152:GUT +$CREATURE:FORGOTTEN_BEAST_152:STOMACH +$CREATURE:FORGOTTEN_BEAST_152:GIZZARD +%CREATURE:FORGOTTEN_BEAST_152:PANCREAS +#CREATURE:FORGOTTEN_BEAST_152:SPLEEN +#CREATURE:FORGOTTEN_BEAST_152:KIDNEY +#CREATURE:FORGOTTEN_BEAST_154:MUSCLE + CREATURE:FORGOTTEN_BEAST_154:EYE +"CREATURE:FORGOTTEN_BEAST_154:BRAIN +!CREATURE:FORGOTTEN_BEAST_154:LUNG +"CREATURE:FORGOTTEN_BEAST_154:HEART +"CREATURE:FORGOTTEN_BEAST_154:LIVER + CREATURE:FORGOTTEN_BEAST_154:GUT +$CREATURE:FORGOTTEN_BEAST_154:STOMACH +$CREATURE:FORGOTTEN_BEAST_154:GIZZARD +%CREATURE:FORGOTTEN_BEAST_154:PANCREAS +#CREATURE:FORGOTTEN_BEAST_154:SPLEEN +#CREATURE:FORGOTTEN_BEAST_154:KIDNEY +#CREATURE:FORGOTTEN_BEAST_156:MUSCLE + CREATURE:FORGOTTEN_BEAST_156:EYE +"CREATURE:FORGOTTEN_BEAST_156:BRAIN +!CREATURE:FORGOTTEN_BEAST_156:LUNG +"CREATURE:FORGOTTEN_BEAST_156:HEART +"CREATURE:FORGOTTEN_BEAST_156:LIVER + CREATURE:FORGOTTEN_BEAST_156:GUT +$CREATURE:FORGOTTEN_BEAST_156:STOMACH +$CREATURE:FORGOTTEN_BEAST_156:GIZZARD +%CREATURE:FORGOTTEN_BEAST_156:PANCREAS +#CREATURE:FORGOTTEN_BEAST_156:SPLEEN +#CREATURE:FORGOTTEN_BEAST_156:KIDNEY +#CREATURE:FORGOTTEN_BEAST_157:MUSCLE + CREATURE:FORGOTTEN_BEAST_157:EYE +"CREATURE:FORGOTTEN_BEAST_157:BRAIN +!CREATURE:FORGOTTEN_BEAST_157:LUNG +"CREATURE:FORGOTTEN_BEAST_157:HEART +"CREATURE:FORGOTTEN_BEAST_157:LIVER + CREATURE:FORGOTTEN_BEAST_157:GUT +$CREATURE:FORGOTTEN_BEAST_157:STOMACH +$CREATURE:FORGOTTEN_BEAST_157:GIZZARD +%CREATURE:FORGOTTEN_BEAST_157:PANCREAS +#CREATURE:FORGOTTEN_BEAST_157:SPLEEN +#CREATURE:FORGOTTEN_BEAST_157:KIDNEY +#CREATURE:FORGOTTEN_BEAST_158:MUSCLE + CREATURE:FORGOTTEN_BEAST_158:EYE +"CREATURE:FORGOTTEN_BEAST_158:BRAIN +!CREATURE:FORGOTTEN_BEAST_158:LUNG +"CREATURE:FORGOTTEN_BEAST_158:HEART +"CREATURE:FORGOTTEN_BEAST_158:LIVER + CREATURE:FORGOTTEN_BEAST_158:GUT +$CREATURE:FORGOTTEN_BEAST_158:STOMACH +$CREATURE:FORGOTTEN_BEAST_158:GIZZARD +%CREATURE:FORGOTTEN_BEAST_158:PANCREAS +#CREATURE:FORGOTTEN_BEAST_158:SPLEEN +#CREATURE:FORGOTTEN_BEAST_158:KIDNEY +#CREATURE:FORGOTTEN_BEAST_159:MUSCLE + CREATURE:FORGOTTEN_BEAST_159:EYE +"CREATURE:FORGOTTEN_BEAST_159:BRAIN +!CREATURE:FORGOTTEN_BEAST_159:LUNG +"CREATURE:FORGOTTEN_BEAST_159:HEART +"CREATURE:FORGOTTEN_BEAST_159:LIVER + CREATURE:FORGOTTEN_BEAST_159:GUT +$CREATURE:FORGOTTEN_BEAST_159:STOMACH +$CREATURE:FORGOTTEN_BEAST_159:GIZZARD +%CREATURE:FORGOTTEN_BEAST_159:PANCREAS +#CREATURE:FORGOTTEN_BEAST_159:SPLEEN +#CREATURE:FORGOTTEN_BEAST_159:KIDNEY +#CREATURE:FORGOTTEN_BEAST_161:MUSCLE + CREATURE:FORGOTTEN_BEAST_161:EYE +"CREATURE:FORGOTTEN_BEAST_161:BRAIN +!CREATURE:FORGOTTEN_BEAST_161:LUNG +"CREATURE:FORGOTTEN_BEAST_161:HEART +"CREATURE:FORGOTTEN_BEAST_161:LIVER + CREATURE:FORGOTTEN_BEAST_161:GUT +$CREATURE:FORGOTTEN_BEAST_161:STOMACH +$CREATURE:FORGOTTEN_BEAST_161:GIZZARD +%CREATURE:FORGOTTEN_BEAST_161:PANCREAS +#CREATURE:FORGOTTEN_BEAST_161:SPLEEN +#CREATURE:FORGOTTEN_BEAST_161:KIDNEY +#CREATURE:FORGOTTEN_BEAST_162:MUSCLE + CREATURE:FORGOTTEN_BEAST_162:EYE +"CREATURE:FORGOTTEN_BEAST_162:BRAIN +!CREATURE:FORGOTTEN_BEAST_162:LUNG +"CREATURE:FORGOTTEN_BEAST_162:HEART +"CREATURE:FORGOTTEN_BEAST_162:LIVER + CREATURE:FORGOTTEN_BEAST_162:GUT +$CREATURE:FORGOTTEN_BEAST_162:STOMACH +$CREATURE:FORGOTTEN_BEAST_162:GIZZARD +%CREATURE:FORGOTTEN_BEAST_162:PANCREAS +#CREATURE:FORGOTTEN_BEAST_162:SPLEEN +#CREATURE:FORGOTTEN_BEAST_162:KIDNEY +#CREATURE:FORGOTTEN_BEAST_163:MUSCLE + CREATURE:FORGOTTEN_BEAST_163:EYE +"CREATURE:FORGOTTEN_BEAST_163:BRAIN +!CREATURE:FORGOTTEN_BEAST_163:LUNG +"CREATURE:FORGOTTEN_BEAST_163:HEART +"CREATURE:FORGOTTEN_BEAST_163:LIVER + CREATURE:FORGOTTEN_BEAST_163:GUT +$CREATURE:FORGOTTEN_BEAST_163:STOMACH +$CREATURE:FORGOTTEN_BEAST_163:GIZZARD +%CREATURE:FORGOTTEN_BEAST_163:PANCREAS +#CREATURE:FORGOTTEN_BEAST_163:SPLEEN +#CREATURE:FORGOTTEN_BEAST_163:KIDNEY +#CREATURE:FORGOTTEN_BEAST_165:MUSCLE + CREATURE:FORGOTTEN_BEAST_165:EYE +"CREATURE:FORGOTTEN_BEAST_165:BRAIN +!CREATURE:FORGOTTEN_BEAST_165:LUNG +"CREATURE:FORGOTTEN_BEAST_165:HEART +"CREATURE:FORGOTTEN_BEAST_165:LIVER + CREATURE:FORGOTTEN_BEAST_165:GUT +$CREATURE:FORGOTTEN_BEAST_165:STOMACH +$CREATURE:FORGOTTEN_BEAST_165:GIZZARD +%CREATURE:FORGOTTEN_BEAST_165:PANCREAS +#CREATURE:FORGOTTEN_BEAST_165:SPLEEN +#CREATURE:FORGOTTEN_BEAST_165:KIDNEY +#CREATURE:FORGOTTEN_BEAST_167:MUSCLE + CREATURE:FORGOTTEN_BEAST_167:EYE +"CREATURE:FORGOTTEN_BEAST_167:BRAIN +!CREATURE:FORGOTTEN_BEAST_167:LUNG +"CREATURE:FORGOTTEN_BEAST_167:HEART +"CREATURE:FORGOTTEN_BEAST_167:LIVER + CREATURE:FORGOTTEN_BEAST_167:GUT +$CREATURE:FORGOTTEN_BEAST_167:STOMACH +$CREATURE:FORGOTTEN_BEAST_167:GIZZARD +%CREATURE:FORGOTTEN_BEAST_167:PANCREAS +#CREATURE:FORGOTTEN_BEAST_167:SPLEEN +#CREATURE:FORGOTTEN_BEAST_167:KIDNEY +#CREATURE:FORGOTTEN_BEAST_168:MUSCLE + CREATURE:FORGOTTEN_BEAST_168:EYE +"CREATURE:FORGOTTEN_BEAST_168:BRAIN +!CREATURE:FORGOTTEN_BEAST_168:LUNG +"CREATURE:FORGOTTEN_BEAST_168:HEART +"CREATURE:FORGOTTEN_BEAST_168:LIVER + CREATURE:FORGOTTEN_BEAST_168:GUT +$CREATURE:FORGOTTEN_BEAST_168:STOMACH +$CREATURE:FORGOTTEN_BEAST_168:GIZZARD +%CREATURE:FORGOTTEN_BEAST_168:PANCREAS +#CREATURE:FORGOTTEN_BEAST_168:SPLEEN +#CREATURE:FORGOTTEN_BEAST_168:KIDNEY +#CREATURE:FORGOTTEN_BEAST_169:MUSCLE + CREATURE:FORGOTTEN_BEAST_169:EYE +"CREATURE:FORGOTTEN_BEAST_169:BRAIN +!CREATURE:FORGOTTEN_BEAST_169:LUNG +"CREATURE:FORGOTTEN_BEAST_169:HEART +"CREATURE:FORGOTTEN_BEAST_169:LIVER + CREATURE:FORGOTTEN_BEAST_169:GUT +$CREATURE:FORGOTTEN_BEAST_169:STOMACH +$CREATURE:FORGOTTEN_BEAST_169:GIZZARD +%CREATURE:FORGOTTEN_BEAST_169:PANCREAS +#CREATURE:FORGOTTEN_BEAST_169:SPLEEN +#CREATURE:FORGOTTEN_BEAST_169:KIDNEY +#CREATURE:FORGOTTEN_BEAST_170:MUSCLE + CREATURE:FORGOTTEN_BEAST_170:EYE +"CREATURE:FORGOTTEN_BEAST_170:BRAIN +!CREATURE:FORGOTTEN_BEAST_170:LUNG +"CREATURE:FORGOTTEN_BEAST_170:HEART +"CREATURE:FORGOTTEN_BEAST_170:LIVER + CREATURE:FORGOTTEN_BEAST_170:GUT +$CREATURE:FORGOTTEN_BEAST_170:STOMACH +$CREATURE:FORGOTTEN_BEAST_170:GIZZARD +%CREATURE:FORGOTTEN_BEAST_170:PANCREAS +#CREATURE:FORGOTTEN_BEAST_170:SPLEEN +#CREATURE:FORGOTTEN_BEAST_170:KIDNEY +#CREATURE:FORGOTTEN_BEAST_171:MUSCLE + CREATURE:FORGOTTEN_BEAST_171:EYE +"CREATURE:FORGOTTEN_BEAST_171:BRAIN +!CREATURE:FORGOTTEN_BEAST_171:LUNG +"CREATURE:FORGOTTEN_BEAST_171:HEART +"CREATURE:FORGOTTEN_BEAST_171:LIVER + CREATURE:FORGOTTEN_BEAST_171:GUT +$CREATURE:FORGOTTEN_BEAST_171:STOMACH +$CREATURE:FORGOTTEN_BEAST_171:GIZZARD +%CREATURE:FORGOTTEN_BEAST_171:PANCREAS +#CREATURE:FORGOTTEN_BEAST_171:SPLEEN +#CREATURE:FORGOTTEN_BEAST_171:KIDNEY +#CREATURE:FORGOTTEN_BEAST_172:MUSCLE + CREATURE:FORGOTTEN_BEAST_172:EYE +"CREATURE:FORGOTTEN_BEAST_172:BRAIN +!CREATURE:FORGOTTEN_BEAST_172:LUNG +"CREATURE:FORGOTTEN_BEAST_172:HEART +"CREATURE:FORGOTTEN_BEAST_172:LIVER + CREATURE:FORGOTTEN_BEAST_172:GUT +$CREATURE:FORGOTTEN_BEAST_172:STOMACH +$CREATURE:FORGOTTEN_BEAST_172:GIZZARD +%CREATURE:FORGOTTEN_BEAST_172:PANCREAS +#CREATURE:FORGOTTEN_BEAST_172:SPLEEN +#CREATURE:FORGOTTEN_BEAST_172:KIDNEY +#CREATURE:FORGOTTEN_BEAST_173:MUSCLE + CREATURE:FORGOTTEN_BEAST_173:EYE +"CREATURE:FORGOTTEN_BEAST_173:BRAIN +!CREATURE:FORGOTTEN_BEAST_173:LUNG +"CREATURE:FORGOTTEN_BEAST_173:HEART +"CREATURE:FORGOTTEN_BEAST_173:LIVER + CREATURE:FORGOTTEN_BEAST_173:GUT +$CREATURE:FORGOTTEN_BEAST_173:STOMACH +$CREATURE:FORGOTTEN_BEAST_173:GIZZARD +%CREATURE:FORGOTTEN_BEAST_173:PANCREAS +#CREATURE:FORGOTTEN_BEAST_173:SPLEEN +#CREATURE:FORGOTTEN_BEAST_173:KIDNEY +#CREATURE:FORGOTTEN_BEAST_176:MUSCLE + CREATURE:FORGOTTEN_BEAST_176:EYE +"CREATURE:FORGOTTEN_BEAST_176:BRAIN +!CREATURE:FORGOTTEN_BEAST_176:LUNG +"CREATURE:FORGOTTEN_BEAST_176:HEART +"CREATURE:FORGOTTEN_BEAST_176:LIVER + CREATURE:FORGOTTEN_BEAST_176:GUT +$CREATURE:FORGOTTEN_BEAST_176:STOMACH +$CREATURE:FORGOTTEN_BEAST_176:GIZZARD +%CREATURE:FORGOTTEN_BEAST_176:PANCREAS +#CREATURE:FORGOTTEN_BEAST_176:SPLEEN +#CREATURE:FORGOTTEN_BEAST_176:KIDNEY +#CREATURE:FORGOTTEN_BEAST_177:MUSCLE + CREATURE:FORGOTTEN_BEAST_177:EYE +"CREATURE:FORGOTTEN_BEAST_177:BRAIN +!CREATURE:FORGOTTEN_BEAST_177:LUNG +"CREATURE:FORGOTTEN_BEAST_177:HEART +"CREATURE:FORGOTTEN_BEAST_177:LIVER + CREATURE:FORGOTTEN_BEAST_177:GUT +$CREATURE:FORGOTTEN_BEAST_177:STOMACH +$CREATURE:FORGOTTEN_BEAST_177:GIZZARD +%CREATURE:FORGOTTEN_BEAST_177:PANCREAS +#CREATURE:FORGOTTEN_BEAST_177:SPLEEN +#CREATURE:FORGOTTEN_BEAST_177:KIDNEY +#CREATURE:FORGOTTEN_BEAST_178:MUSCLE + CREATURE:FORGOTTEN_BEAST_178:EYE +"CREATURE:FORGOTTEN_BEAST_178:BRAIN +!CREATURE:FORGOTTEN_BEAST_178:LUNG +"CREATURE:FORGOTTEN_BEAST_178:HEART +"CREATURE:FORGOTTEN_BEAST_178:LIVER + CREATURE:FORGOTTEN_BEAST_178:GUT +$CREATURE:FORGOTTEN_BEAST_178:STOMACH +$CREATURE:FORGOTTEN_BEAST_178:GIZZARD +%CREATURE:FORGOTTEN_BEAST_178:PANCREAS +#CREATURE:FORGOTTEN_BEAST_178:SPLEEN +#CREATURE:FORGOTTEN_BEAST_178:KIDNEY +#CREATURE:FORGOTTEN_BEAST_179:MUSCLE + CREATURE:FORGOTTEN_BEAST_179:EYE +"CREATURE:FORGOTTEN_BEAST_179:BRAIN +!CREATURE:FORGOTTEN_BEAST_179:LUNG +"CREATURE:FORGOTTEN_BEAST_179:HEART +"CREATURE:FORGOTTEN_BEAST_179:LIVER + CREATURE:FORGOTTEN_BEAST_179:GUT +$CREATURE:FORGOTTEN_BEAST_179:STOMACH +$CREATURE:FORGOTTEN_BEAST_179:GIZZARD +%CREATURE:FORGOTTEN_BEAST_179:PANCREAS +#CREATURE:FORGOTTEN_BEAST_179:SPLEEN +#CREATURE:FORGOTTEN_BEAST_179:KIDNEY +#CREATURE:FORGOTTEN_BEAST_180:MUSCLE + CREATURE:FORGOTTEN_BEAST_180:EYE +"CREATURE:FORGOTTEN_BEAST_180:BRAIN +!CREATURE:FORGOTTEN_BEAST_180:LUNG +"CREATURE:FORGOTTEN_BEAST_180:HEART +"CREATURE:FORGOTTEN_BEAST_180:LIVER + CREATURE:FORGOTTEN_BEAST_180:GUT +$CREATURE:FORGOTTEN_BEAST_180:STOMACH +$CREATURE:FORGOTTEN_BEAST_180:GIZZARD +%CREATURE:FORGOTTEN_BEAST_180:PANCREAS +#CREATURE:FORGOTTEN_BEAST_180:SPLEEN +#CREATURE:FORGOTTEN_BEAST_180:KIDNEY +#CREATURE:FORGOTTEN_BEAST_181:MUSCLE + CREATURE:FORGOTTEN_BEAST_181:EYE +"CREATURE:FORGOTTEN_BEAST_181:BRAIN +!CREATURE:FORGOTTEN_BEAST_181:LUNG +"CREATURE:FORGOTTEN_BEAST_181:HEART +"CREATURE:FORGOTTEN_BEAST_181:LIVER + CREATURE:FORGOTTEN_BEAST_181:GUT +$CREATURE:FORGOTTEN_BEAST_181:STOMACH +$CREATURE:FORGOTTEN_BEAST_181:GIZZARD +%CREATURE:FORGOTTEN_BEAST_181:PANCREAS +#CREATURE:FORGOTTEN_BEAST_181:SPLEEN +#CREATURE:FORGOTTEN_BEAST_181:KIDNEY +#CREATURE:FORGOTTEN_BEAST_182:MUSCLE + CREATURE:FORGOTTEN_BEAST_182:EYE +"CREATURE:FORGOTTEN_BEAST_182:BRAIN +!CREATURE:FORGOTTEN_BEAST_182:LUNG +"CREATURE:FORGOTTEN_BEAST_182:HEART +"CREATURE:FORGOTTEN_BEAST_182:LIVER + CREATURE:FORGOTTEN_BEAST_182:GUT +$CREATURE:FORGOTTEN_BEAST_182:STOMACH +$CREATURE:FORGOTTEN_BEAST_182:GIZZARD +%CREATURE:FORGOTTEN_BEAST_182:PANCREAS +#CREATURE:FORGOTTEN_BEAST_182:SPLEEN +#CREATURE:FORGOTTEN_BEAST_182:KIDNEY +#CREATURE:FORGOTTEN_BEAST_183:MUSCLE + CREATURE:FORGOTTEN_BEAST_183:EYE +"CREATURE:FORGOTTEN_BEAST_183:BRAIN +!CREATURE:FORGOTTEN_BEAST_183:LUNG +"CREATURE:FORGOTTEN_BEAST_183:HEART +"CREATURE:FORGOTTEN_BEAST_183:LIVER + CREATURE:FORGOTTEN_BEAST_183:GUT +$CREATURE:FORGOTTEN_BEAST_183:STOMACH +$CREATURE:FORGOTTEN_BEAST_183:GIZZARD +%CREATURE:FORGOTTEN_BEAST_183:PANCREAS +#CREATURE:FORGOTTEN_BEAST_183:SPLEEN +#CREATURE:FORGOTTEN_BEAST_183:KIDNEY +#CREATURE:FORGOTTEN_BEAST_184:MUSCLE + CREATURE:FORGOTTEN_BEAST_184:EYE +"CREATURE:FORGOTTEN_BEAST_184:BRAIN +!CREATURE:FORGOTTEN_BEAST_184:LUNG +"CREATURE:FORGOTTEN_BEAST_184:HEART +"CREATURE:FORGOTTEN_BEAST_184:LIVER + CREATURE:FORGOTTEN_BEAST_184:GUT +$CREATURE:FORGOTTEN_BEAST_184:STOMACH +$CREATURE:FORGOTTEN_BEAST_184:GIZZARD +%CREATURE:FORGOTTEN_BEAST_184:PANCREAS +#CREATURE:FORGOTTEN_BEAST_184:SPLEEN +#CREATURE:FORGOTTEN_BEAST_184:KIDNEY +#CREATURE:FORGOTTEN_BEAST_185:MUSCLE + CREATURE:FORGOTTEN_BEAST_185:EYE +"CREATURE:FORGOTTEN_BEAST_185:BRAIN +!CREATURE:FORGOTTEN_BEAST_185:LUNG +"CREATURE:FORGOTTEN_BEAST_185:HEART +"CREATURE:FORGOTTEN_BEAST_185:LIVER + CREATURE:FORGOTTEN_BEAST_185:GUT +$CREATURE:FORGOTTEN_BEAST_185:STOMACH +$CREATURE:FORGOTTEN_BEAST_185:GIZZARD +%CREATURE:FORGOTTEN_BEAST_185:PANCREAS +#CREATURE:FORGOTTEN_BEAST_185:SPLEEN +#CREATURE:FORGOTTEN_BEAST_185:KIDNEY +#CREATURE:FORGOTTEN_BEAST_186:MUSCLE + CREATURE:FORGOTTEN_BEAST_186:EYE +"CREATURE:FORGOTTEN_BEAST_186:BRAIN +!CREATURE:FORGOTTEN_BEAST_186:LUNG +"CREATURE:FORGOTTEN_BEAST_186:HEART +"CREATURE:FORGOTTEN_BEAST_186:LIVER + CREATURE:FORGOTTEN_BEAST_186:GUT +$CREATURE:FORGOTTEN_BEAST_186:STOMACH +$CREATURE:FORGOTTEN_BEAST_186:GIZZARD +%CREATURE:FORGOTTEN_BEAST_186:PANCREAS +#CREATURE:FORGOTTEN_BEAST_186:SPLEEN +#CREATURE:FORGOTTEN_BEAST_186:KIDNEY +#CREATURE:FORGOTTEN_BEAST_188:MUSCLE + CREATURE:FORGOTTEN_BEAST_188:EYE +"CREATURE:FORGOTTEN_BEAST_188:BRAIN +!CREATURE:FORGOTTEN_BEAST_188:LUNG +"CREATURE:FORGOTTEN_BEAST_188:HEART +"CREATURE:FORGOTTEN_BEAST_188:LIVER + CREATURE:FORGOTTEN_BEAST_188:GUT +$CREATURE:FORGOTTEN_BEAST_188:STOMACH +$CREATURE:FORGOTTEN_BEAST_188:GIZZARD +%CREATURE:FORGOTTEN_BEAST_188:PANCREAS +#CREATURE:FORGOTTEN_BEAST_188:SPLEEN +#CREATURE:FORGOTTEN_BEAST_188:KIDNEY +#CREATURE:FORGOTTEN_BEAST_189:MUSCLE + CREATURE:FORGOTTEN_BEAST_189:EYE +"CREATURE:FORGOTTEN_BEAST_189:BRAIN +!CREATURE:FORGOTTEN_BEAST_189:LUNG +"CREATURE:FORGOTTEN_BEAST_189:HEART +"CREATURE:FORGOTTEN_BEAST_189:LIVER + CREATURE:FORGOTTEN_BEAST_189:GUT +$CREATURE:FORGOTTEN_BEAST_189:STOMACH +$CREATURE:FORGOTTEN_BEAST_189:GIZZARD +%CREATURE:FORGOTTEN_BEAST_189:PANCREAS +#CREATURE:FORGOTTEN_BEAST_189:SPLEEN +#CREATURE:FORGOTTEN_BEAST_189:KIDNEY +#CREATURE:FORGOTTEN_BEAST_190:MUSCLE + CREATURE:FORGOTTEN_BEAST_190:EYE +"CREATURE:FORGOTTEN_BEAST_190:BRAIN +!CREATURE:FORGOTTEN_BEAST_190:LUNG +"CREATURE:FORGOTTEN_BEAST_190:HEART +"CREATURE:FORGOTTEN_BEAST_190:LIVER + CREATURE:FORGOTTEN_BEAST_190:GUT +$CREATURE:FORGOTTEN_BEAST_190:STOMACH +$CREATURE:FORGOTTEN_BEAST_190:GIZZARD +%CREATURE:FORGOTTEN_BEAST_190:PANCREAS +#CREATURE:FORGOTTEN_BEAST_190:SPLEEN +#CREATURE:FORGOTTEN_BEAST_190:KIDNEY +#CREATURE:FORGOTTEN_BEAST_191:MUSCLE + CREATURE:FORGOTTEN_BEAST_191:EYE +"CREATURE:FORGOTTEN_BEAST_191:BRAIN +!CREATURE:FORGOTTEN_BEAST_191:LUNG +"CREATURE:FORGOTTEN_BEAST_191:HEART +"CREATURE:FORGOTTEN_BEAST_191:LIVER + CREATURE:FORGOTTEN_BEAST_191:GUT +$CREATURE:FORGOTTEN_BEAST_191:STOMACH +$CREATURE:FORGOTTEN_BEAST_191:GIZZARD +%CREATURE:FORGOTTEN_BEAST_191:PANCREAS +#CREATURE:FORGOTTEN_BEAST_191:SPLEEN +#CREATURE:FORGOTTEN_BEAST_191:KIDNEY +#CREATURE:FORGOTTEN_BEAST_193:MUSCLE + CREATURE:FORGOTTEN_BEAST_193:EYE +"CREATURE:FORGOTTEN_BEAST_193:BRAIN +!CREATURE:FORGOTTEN_BEAST_193:LUNG +"CREATURE:FORGOTTEN_BEAST_193:HEART +"CREATURE:FORGOTTEN_BEAST_193:LIVER + CREATURE:FORGOTTEN_BEAST_193:GUT +$CREATURE:FORGOTTEN_BEAST_193:STOMACH +$CREATURE:FORGOTTEN_BEAST_193:GIZZARD +%CREATURE:FORGOTTEN_BEAST_193:PANCREAS +#CREATURE:FORGOTTEN_BEAST_193:SPLEEN +#CREATURE:FORGOTTEN_BEAST_193:KIDNEY +#CREATURE:FORGOTTEN_BEAST_194:MUSCLE + CREATURE:FORGOTTEN_BEAST_194:EYE +"CREATURE:FORGOTTEN_BEAST_194:BRAIN +!CREATURE:FORGOTTEN_BEAST_194:LUNG +"CREATURE:FORGOTTEN_BEAST_194:HEART +"CREATURE:FORGOTTEN_BEAST_194:LIVER + CREATURE:FORGOTTEN_BEAST_194:GUT +$CREATURE:FORGOTTEN_BEAST_194:STOMACH +$CREATURE:FORGOTTEN_BEAST_194:GIZZARD +%CREATURE:FORGOTTEN_BEAST_194:PANCREAS +#CREATURE:FORGOTTEN_BEAST_194:SPLEEN +#CREATURE:FORGOTTEN_BEAST_194:KIDNEY +#CREATURE:FORGOTTEN_BEAST_195:MUSCLE + CREATURE:FORGOTTEN_BEAST_195:EYE +"CREATURE:FORGOTTEN_BEAST_195:BRAIN +!CREATURE:FORGOTTEN_BEAST_195:LUNG +"CREATURE:FORGOTTEN_BEAST_195:HEART +"CREATURE:FORGOTTEN_BEAST_195:LIVER + CREATURE:FORGOTTEN_BEAST_195:GUT +$CREATURE:FORGOTTEN_BEAST_195:STOMACH +$CREATURE:FORGOTTEN_BEAST_195:GIZZARD +%CREATURE:FORGOTTEN_BEAST_195:PANCREAS +#CREATURE:FORGOTTEN_BEAST_195:SPLEEN +#CREATURE:FORGOTTEN_BEAST_195:KIDNEY +#CREATURE:FORGOTTEN_BEAST_196:MUSCLE + CREATURE:FORGOTTEN_BEAST_196:EYE +"CREATURE:FORGOTTEN_BEAST_196:BRAIN +!CREATURE:FORGOTTEN_BEAST_196:LUNG +"CREATURE:FORGOTTEN_BEAST_196:HEART +"CREATURE:FORGOTTEN_BEAST_196:LIVER + CREATURE:FORGOTTEN_BEAST_196:GUT +$CREATURE:FORGOTTEN_BEAST_196:STOMACH +$CREATURE:FORGOTTEN_BEAST_196:GIZZARD +%CREATURE:FORGOTTEN_BEAST_196:PANCREAS +#CREATURE:FORGOTTEN_BEAST_196:SPLEEN +#CREATURE:FORGOTTEN_BEAST_196:KIDNEY +#CREATURE:FORGOTTEN_BEAST_197:MUSCLE + CREATURE:FORGOTTEN_BEAST_197:EYE +"CREATURE:FORGOTTEN_BEAST_197:BRAIN +!CREATURE:FORGOTTEN_BEAST_197:LUNG +"CREATURE:FORGOTTEN_BEAST_197:HEART +"CREATURE:FORGOTTEN_BEAST_197:LIVER + CREATURE:FORGOTTEN_BEAST_197:GUT +$CREATURE:FORGOTTEN_BEAST_197:STOMACH +$CREATURE:FORGOTTEN_BEAST_197:GIZZARD +%CREATURE:FORGOTTEN_BEAST_197:PANCREAS +#CREATURE:FORGOTTEN_BEAST_197:SPLEEN +#CREATURE:FORGOTTEN_BEAST_197:KIDNEY +#CREATURE:FORGOTTEN_BEAST_199:MUSCLE + CREATURE:FORGOTTEN_BEAST_199:EYE +"CREATURE:FORGOTTEN_BEAST_199:BRAIN +!CREATURE:FORGOTTEN_BEAST_199:LUNG +"CREATURE:FORGOTTEN_BEAST_199:HEART +"CREATURE:FORGOTTEN_BEAST_199:LIVER + CREATURE:FORGOTTEN_BEAST_199:GUT +$CREATURE:FORGOTTEN_BEAST_199:STOMACH +$CREATURE:FORGOTTEN_BEAST_199:GIZZARD +%CREATURE:FORGOTTEN_BEAST_199:PANCREAS +#CREATURE:FORGOTTEN_BEAST_199:SPLEEN +#CREATURE:FORGOTTEN_BEAST_199:KIDNEY +#CREATURE:FORGOTTEN_BEAST_200:MUSCLE + CREATURE:FORGOTTEN_BEAST_200:EYE +"CREATURE:FORGOTTEN_BEAST_200:BRAIN +!CREATURE:FORGOTTEN_BEAST_200:LUNG +"CREATURE:FORGOTTEN_BEAST_200:HEART +"CREATURE:FORGOTTEN_BEAST_200:LIVER + CREATURE:FORGOTTEN_BEAST_200:GUT +$CREATURE:FORGOTTEN_BEAST_200:STOMACH +$CREATURE:FORGOTTEN_BEAST_200:GIZZARD +%CREATURE:FORGOTTEN_BEAST_200:PANCREAS +#CREATURE:FORGOTTEN_BEAST_200:SPLEEN +#CREATURE:FORGOTTEN_BEAST_200:KIDNEY +#CREATURE:FORGOTTEN_BEAST_201:MUSCLE + CREATURE:FORGOTTEN_BEAST_201:EYE +"CREATURE:FORGOTTEN_BEAST_201:BRAIN +!CREATURE:FORGOTTEN_BEAST_201:LUNG +"CREATURE:FORGOTTEN_BEAST_201:HEART +"CREATURE:FORGOTTEN_BEAST_201:LIVER + CREATURE:FORGOTTEN_BEAST_201:GUT +$CREATURE:FORGOTTEN_BEAST_201:STOMACH +$CREATURE:FORGOTTEN_BEAST_201:GIZZARD +%CREATURE:FORGOTTEN_BEAST_201:PANCREAS +#CREATURE:FORGOTTEN_BEAST_201:SPLEEN +#CREATURE:FORGOTTEN_BEAST_201:KIDNEY +#CREATURE:FORGOTTEN_BEAST_204:MUSCLE + CREATURE:FORGOTTEN_BEAST_204:EYE +"CREATURE:FORGOTTEN_BEAST_204:BRAIN +!CREATURE:FORGOTTEN_BEAST_204:LUNG +"CREATURE:FORGOTTEN_BEAST_204:HEART +"CREATURE:FORGOTTEN_BEAST_204:LIVER + CREATURE:FORGOTTEN_BEAST_204:GUT +$CREATURE:FORGOTTEN_BEAST_204:STOMACH +$CREATURE:FORGOTTEN_BEAST_204:GIZZARD +%CREATURE:FORGOTTEN_BEAST_204:PANCREAS +#CREATURE:FORGOTTEN_BEAST_204:SPLEEN +#CREATURE:FORGOTTEN_BEAST_204:KIDNEY +#CREATURE:FORGOTTEN_BEAST_205:MUSCLE + CREATURE:FORGOTTEN_BEAST_205:EYE +"CREATURE:FORGOTTEN_BEAST_205:BRAIN +!CREATURE:FORGOTTEN_BEAST_205:LUNG +"CREATURE:FORGOTTEN_BEAST_205:HEART +"CREATURE:FORGOTTEN_BEAST_205:LIVER + CREATURE:FORGOTTEN_BEAST_205:GUT +$CREATURE:FORGOTTEN_BEAST_205:STOMACH +$CREATURE:FORGOTTEN_BEAST_205:GIZZARD +%CREATURE:FORGOTTEN_BEAST_205:PANCREAS +#CREATURE:FORGOTTEN_BEAST_205:SPLEEN +#CREATURE:FORGOTTEN_BEAST_205:KIDNEY +#CREATURE:FORGOTTEN_BEAST_206:MUSCLE + CREATURE:FORGOTTEN_BEAST_206:EYE +"CREATURE:FORGOTTEN_BEAST_206:BRAIN +!CREATURE:FORGOTTEN_BEAST_206:LUNG +"CREATURE:FORGOTTEN_BEAST_206:HEART +"CREATURE:FORGOTTEN_BEAST_206:LIVER + CREATURE:FORGOTTEN_BEAST_206:GUT +$CREATURE:FORGOTTEN_BEAST_206:STOMACH +$CREATURE:FORGOTTEN_BEAST_206:GIZZARD +%CREATURE:FORGOTTEN_BEAST_206:PANCREAS +#CREATURE:FORGOTTEN_BEAST_206:SPLEEN +#CREATURE:FORGOTTEN_BEAST_206:KIDNEY +#CREATURE:FORGOTTEN_BEAST_207:MUSCLE + CREATURE:FORGOTTEN_BEAST_207:EYE +"CREATURE:FORGOTTEN_BEAST_207:BRAIN +!CREATURE:FORGOTTEN_BEAST_207:LUNG +"CREATURE:FORGOTTEN_BEAST_207:HEART +"CREATURE:FORGOTTEN_BEAST_207:LIVER + CREATURE:FORGOTTEN_BEAST_207:GUT +$CREATURE:FORGOTTEN_BEAST_207:STOMACH +$CREATURE:FORGOTTEN_BEAST_207:GIZZARD +%CREATURE:FORGOTTEN_BEAST_207:PANCREAS +#CREATURE:FORGOTTEN_BEAST_207:SPLEEN +#CREATURE:FORGOTTEN_BEAST_207:KIDNEY +#CREATURE:FORGOTTEN_BEAST_208:MUSCLE + CREATURE:FORGOTTEN_BEAST_208:EYE +"CREATURE:FORGOTTEN_BEAST_208:BRAIN +!CREATURE:FORGOTTEN_BEAST_208:LUNG +"CREATURE:FORGOTTEN_BEAST_208:HEART +"CREATURE:FORGOTTEN_BEAST_208:LIVER + CREATURE:FORGOTTEN_BEAST_208:GUT +$CREATURE:FORGOTTEN_BEAST_208:STOMACH +$CREATURE:FORGOTTEN_BEAST_208:GIZZARD +%CREATURE:FORGOTTEN_BEAST_208:PANCREAS +#CREATURE:FORGOTTEN_BEAST_208:SPLEEN +#CREATURE:FORGOTTEN_BEAST_208:KIDNEY +#CREATURE:FORGOTTEN_BEAST_209:MUSCLE + CREATURE:FORGOTTEN_BEAST_209:EYE +"CREATURE:FORGOTTEN_BEAST_209:BRAIN +!CREATURE:FORGOTTEN_BEAST_209:LUNG +"CREATURE:FORGOTTEN_BEAST_209:HEART +"CREATURE:FORGOTTEN_BEAST_209:LIVER + CREATURE:FORGOTTEN_BEAST_209:GUT +$CREATURE:FORGOTTEN_BEAST_209:STOMACH +$CREATURE:FORGOTTEN_BEAST_209:GIZZARD +%CREATURE:FORGOTTEN_BEAST_209:PANCREAS +#CREATURE:FORGOTTEN_BEAST_209:SPLEEN +#CREATURE:FORGOTTEN_BEAST_209:KIDNEY +#CREATURE:FORGOTTEN_BEAST_210:MUSCLE + CREATURE:FORGOTTEN_BEAST_210:EYE +"CREATURE:FORGOTTEN_BEAST_210:BRAIN +!CREATURE:FORGOTTEN_BEAST_210:LUNG +"CREATURE:FORGOTTEN_BEAST_210:HEART +"CREATURE:FORGOTTEN_BEAST_210:LIVER + CREATURE:FORGOTTEN_BEAST_210:GUT +$CREATURE:FORGOTTEN_BEAST_210:STOMACH +$CREATURE:FORGOTTEN_BEAST_210:GIZZARD +%CREATURE:FORGOTTEN_BEAST_210:PANCREAS +#CREATURE:FORGOTTEN_BEAST_210:SPLEEN +#CREATURE:FORGOTTEN_BEAST_210:KIDNEY +#CREATURE:FORGOTTEN_BEAST_211:MUSCLE + CREATURE:FORGOTTEN_BEAST_211:EYE +"CREATURE:FORGOTTEN_BEAST_211:BRAIN +!CREATURE:FORGOTTEN_BEAST_211:LUNG +"CREATURE:FORGOTTEN_BEAST_211:HEART +"CREATURE:FORGOTTEN_BEAST_211:LIVER + CREATURE:FORGOTTEN_BEAST_211:GUT +$CREATURE:FORGOTTEN_BEAST_211:STOMACH +$CREATURE:FORGOTTEN_BEAST_211:GIZZARD +%CREATURE:FORGOTTEN_BEAST_211:PANCREAS +#CREATURE:FORGOTTEN_BEAST_211:SPLEEN +#CREATURE:FORGOTTEN_BEAST_211:KIDNEY +#CREATURE:FORGOTTEN_BEAST_212:MUSCLE + CREATURE:FORGOTTEN_BEAST_212:EYE +"CREATURE:FORGOTTEN_BEAST_212:BRAIN +!CREATURE:FORGOTTEN_BEAST_212:LUNG +"CREATURE:FORGOTTEN_BEAST_212:HEART +"CREATURE:FORGOTTEN_BEAST_212:LIVER + CREATURE:FORGOTTEN_BEAST_212:GUT +$CREATURE:FORGOTTEN_BEAST_212:STOMACH +$CREATURE:FORGOTTEN_BEAST_212:GIZZARD +%CREATURE:FORGOTTEN_BEAST_212:PANCREAS +#CREATURE:FORGOTTEN_BEAST_212:SPLEEN +#CREATURE:FORGOTTEN_BEAST_212:KIDNEY +#CREATURE:FORGOTTEN_BEAST_213:MUSCLE + CREATURE:FORGOTTEN_BEAST_213:EYE +"CREATURE:FORGOTTEN_BEAST_213:BRAIN +!CREATURE:FORGOTTEN_BEAST_213:LUNG +"CREATURE:FORGOTTEN_BEAST_213:HEART +"CREATURE:FORGOTTEN_BEAST_213:LIVER + CREATURE:FORGOTTEN_BEAST_213:GUT +$CREATURE:FORGOTTEN_BEAST_213:STOMACH +$CREATURE:FORGOTTEN_BEAST_213:GIZZARD +%CREATURE:FORGOTTEN_BEAST_213:PANCREAS +#CREATURE:FORGOTTEN_BEAST_213:SPLEEN +#CREATURE:FORGOTTEN_BEAST_213:KIDNEY +#CREATURE:FORGOTTEN_BEAST_214:MUSCLE + CREATURE:FORGOTTEN_BEAST_214:EYE +"CREATURE:FORGOTTEN_BEAST_214:BRAIN +!CREATURE:FORGOTTEN_BEAST_214:LUNG +"CREATURE:FORGOTTEN_BEAST_214:HEART +"CREATURE:FORGOTTEN_BEAST_214:LIVER + CREATURE:FORGOTTEN_BEAST_214:GUT +$CREATURE:FORGOTTEN_BEAST_214:STOMACH +$CREATURE:FORGOTTEN_BEAST_214:GIZZARD +%CREATURE:FORGOTTEN_BEAST_214:PANCREAS +#CREATURE:FORGOTTEN_BEAST_214:SPLEEN +#CREATURE:FORGOTTEN_BEAST_214:KIDNEY +#CREATURE:FORGOTTEN_BEAST_215:MUSCLE + CREATURE:FORGOTTEN_BEAST_215:EYE +"CREATURE:FORGOTTEN_BEAST_215:BRAIN +!CREATURE:FORGOTTEN_BEAST_215:LUNG +"CREATURE:FORGOTTEN_BEAST_215:HEART +"CREATURE:FORGOTTEN_BEAST_215:LIVER + CREATURE:FORGOTTEN_BEAST_215:GUT +$CREATURE:FORGOTTEN_BEAST_215:STOMACH +$CREATURE:FORGOTTEN_BEAST_215:GIZZARD +%CREATURE:FORGOTTEN_BEAST_215:PANCREAS +#CREATURE:FORGOTTEN_BEAST_215:SPLEEN +#CREATURE:FORGOTTEN_BEAST_215:KIDNEY +#CREATURE:FORGOTTEN_BEAST_216:MUSCLE + CREATURE:FORGOTTEN_BEAST_216:EYE +"CREATURE:FORGOTTEN_BEAST_216:BRAIN +!CREATURE:FORGOTTEN_BEAST_216:LUNG +"CREATURE:FORGOTTEN_BEAST_216:HEART +"CREATURE:FORGOTTEN_BEAST_216:LIVER + CREATURE:FORGOTTEN_BEAST_216:GUT +$CREATURE:FORGOTTEN_BEAST_216:STOMACH +$CREATURE:FORGOTTEN_BEAST_216:GIZZARD +%CREATURE:FORGOTTEN_BEAST_216:PANCREAS +#CREATURE:FORGOTTEN_BEAST_216:SPLEEN +#CREATURE:FORGOTTEN_BEAST_216:KIDNEY +#CREATURE:FORGOTTEN_BEAST_217:MUSCLE + CREATURE:FORGOTTEN_BEAST_217:EYE +"CREATURE:FORGOTTEN_BEAST_217:BRAIN +!CREATURE:FORGOTTEN_BEAST_217:LUNG +"CREATURE:FORGOTTEN_BEAST_217:HEART +"CREATURE:FORGOTTEN_BEAST_217:LIVER + CREATURE:FORGOTTEN_BEAST_217:GUT +$CREATURE:FORGOTTEN_BEAST_217:STOMACH +$CREATURE:FORGOTTEN_BEAST_217:GIZZARD +%CREATURE:FORGOTTEN_BEAST_217:PANCREAS +#CREATURE:FORGOTTEN_BEAST_217:SPLEEN +#CREATURE:FORGOTTEN_BEAST_217:KIDNEY +#CREATURE:FORGOTTEN_BEAST_218:MUSCLE + CREATURE:FORGOTTEN_BEAST_218:EYE +"CREATURE:FORGOTTEN_BEAST_218:BRAIN +!CREATURE:FORGOTTEN_BEAST_218:LUNG +"CREATURE:FORGOTTEN_BEAST_218:HEART +"CREATURE:FORGOTTEN_BEAST_218:LIVER + CREATURE:FORGOTTEN_BEAST_218:GUT +$CREATURE:FORGOTTEN_BEAST_218:STOMACH +$CREATURE:FORGOTTEN_BEAST_218:GIZZARD +%CREATURE:FORGOTTEN_BEAST_218:PANCREAS +#CREATURE:FORGOTTEN_BEAST_218:SPLEEN +#CREATURE:FORGOTTEN_BEAST_218:KIDNEY +#CREATURE:FORGOTTEN_BEAST_220:MUSCLE + CREATURE:FORGOTTEN_BEAST_220:EYE +"CREATURE:FORGOTTEN_BEAST_220:BRAIN +!CREATURE:FORGOTTEN_BEAST_220:LUNG +"CREATURE:FORGOTTEN_BEAST_220:HEART +"CREATURE:FORGOTTEN_BEAST_220:LIVER + CREATURE:FORGOTTEN_BEAST_220:GUT +$CREATURE:FORGOTTEN_BEAST_220:STOMACH +$CREATURE:FORGOTTEN_BEAST_220:GIZZARD +%CREATURE:FORGOTTEN_BEAST_220:PANCREAS +#CREATURE:FORGOTTEN_BEAST_220:SPLEEN +#CREATURE:FORGOTTEN_BEAST_220:KIDNEY +#CREATURE:FORGOTTEN_BEAST_221:MUSCLE + CREATURE:FORGOTTEN_BEAST_221:EYE +"CREATURE:FORGOTTEN_BEAST_221:BRAIN +!CREATURE:FORGOTTEN_BEAST_221:LUNG +"CREATURE:FORGOTTEN_BEAST_221:HEART +"CREATURE:FORGOTTEN_BEAST_221:LIVER + CREATURE:FORGOTTEN_BEAST_221:GUT +$CREATURE:FORGOTTEN_BEAST_221:STOMACH +$CREATURE:FORGOTTEN_BEAST_221:GIZZARD +%CREATURE:FORGOTTEN_BEAST_221:PANCREAS +#CREATURE:FORGOTTEN_BEAST_221:SPLEEN +#CREATURE:FORGOTTEN_BEAST_221:KIDNEY +#CREATURE:FORGOTTEN_BEAST_224:MUSCLE + CREATURE:FORGOTTEN_BEAST_224:EYE +"CREATURE:FORGOTTEN_BEAST_224:BRAIN +!CREATURE:FORGOTTEN_BEAST_224:LUNG +"CREATURE:FORGOTTEN_BEAST_224:HEART +"CREATURE:FORGOTTEN_BEAST_224:LIVER + CREATURE:FORGOTTEN_BEAST_224:GUT +$CREATURE:FORGOTTEN_BEAST_224:STOMACH +$CREATURE:FORGOTTEN_BEAST_224:GIZZARD +%CREATURE:FORGOTTEN_BEAST_224:PANCREAS +#CREATURE:FORGOTTEN_BEAST_224:SPLEEN +#CREATURE:FORGOTTEN_BEAST_224:KIDNEY +#CREATURE:FORGOTTEN_BEAST_225:MUSCLE + CREATURE:FORGOTTEN_BEAST_225:EYE +"CREATURE:FORGOTTEN_BEAST_225:BRAIN +!CREATURE:FORGOTTEN_BEAST_225:LUNG +"CREATURE:FORGOTTEN_BEAST_225:HEART +"CREATURE:FORGOTTEN_BEAST_225:LIVER + CREATURE:FORGOTTEN_BEAST_225:GUT +$CREATURE:FORGOTTEN_BEAST_225:STOMACH +$CREATURE:FORGOTTEN_BEAST_225:GIZZARD +%CREATURE:FORGOTTEN_BEAST_225:PANCREAS +#CREATURE:FORGOTTEN_BEAST_225:SPLEEN +#CREATURE:FORGOTTEN_BEAST_225:KIDNEY +#CREATURE:FORGOTTEN_BEAST_228:MUSCLE + CREATURE:FORGOTTEN_BEAST_228:EYE +"CREATURE:FORGOTTEN_BEAST_228:BRAIN +!CREATURE:FORGOTTEN_BEAST_228:LUNG +"CREATURE:FORGOTTEN_BEAST_228:HEART +"CREATURE:FORGOTTEN_BEAST_228:LIVER + CREATURE:FORGOTTEN_BEAST_228:GUT +$CREATURE:FORGOTTEN_BEAST_228:STOMACH +$CREATURE:FORGOTTEN_BEAST_228:GIZZARD +%CREATURE:FORGOTTEN_BEAST_228:PANCREAS +#CREATURE:FORGOTTEN_BEAST_228:SPLEEN +#CREATURE:FORGOTTEN_BEAST_228:KIDNEY +#CREATURE:FORGOTTEN_BEAST_231:MUSCLE + CREATURE:FORGOTTEN_BEAST_231:EYE +"CREATURE:FORGOTTEN_BEAST_231:BRAIN +!CREATURE:FORGOTTEN_BEAST_231:LUNG +"CREATURE:FORGOTTEN_BEAST_231:HEART +"CREATURE:FORGOTTEN_BEAST_231:LIVER + CREATURE:FORGOTTEN_BEAST_231:GUT +$CREATURE:FORGOTTEN_BEAST_231:STOMACH +$CREATURE:FORGOTTEN_BEAST_231:GIZZARD +%CREATURE:FORGOTTEN_BEAST_231:PANCREAS +#CREATURE:FORGOTTEN_BEAST_231:SPLEEN +#CREATURE:FORGOTTEN_BEAST_231:KIDNEY +#CREATURE:FORGOTTEN_BEAST_232:MUSCLE + CREATURE:FORGOTTEN_BEAST_232:EYE +"CREATURE:FORGOTTEN_BEAST_232:BRAIN +!CREATURE:FORGOTTEN_BEAST_232:LUNG +"CREATURE:FORGOTTEN_BEAST_232:HEART +"CREATURE:FORGOTTEN_BEAST_232:LIVER + CREATURE:FORGOTTEN_BEAST_232:GUT +$CREATURE:FORGOTTEN_BEAST_232:STOMACH +$CREATURE:FORGOTTEN_BEAST_232:GIZZARD +%CREATURE:FORGOTTEN_BEAST_232:PANCREAS +#CREATURE:FORGOTTEN_BEAST_232:SPLEEN +#CREATURE:FORGOTTEN_BEAST_232:KIDNEY +#CREATURE:FORGOTTEN_BEAST_235:MUSCLE + CREATURE:FORGOTTEN_BEAST_235:EYE +"CREATURE:FORGOTTEN_BEAST_235:BRAIN +!CREATURE:FORGOTTEN_BEAST_235:LUNG +"CREATURE:FORGOTTEN_BEAST_235:HEART +"CREATURE:FORGOTTEN_BEAST_235:LIVER + CREATURE:FORGOTTEN_BEAST_235:GUT +$CREATURE:FORGOTTEN_BEAST_235:STOMACH +$CREATURE:FORGOTTEN_BEAST_235:GIZZARD +%CREATURE:FORGOTTEN_BEAST_235:PANCREAS +#CREATURE:FORGOTTEN_BEAST_235:SPLEEN +#CREATURE:FORGOTTEN_BEAST_235:KIDNEY +#CREATURE:FORGOTTEN_BEAST_236:MUSCLE + CREATURE:FORGOTTEN_BEAST_236:EYE +"CREATURE:FORGOTTEN_BEAST_236:BRAIN +!CREATURE:FORGOTTEN_BEAST_236:LUNG +"CREATURE:FORGOTTEN_BEAST_236:HEART +"CREATURE:FORGOTTEN_BEAST_236:LIVER + CREATURE:FORGOTTEN_BEAST_236:GUT +$CREATURE:FORGOTTEN_BEAST_236:STOMACH +$CREATURE:FORGOTTEN_BEAST_236:GIZZARD +%CREATURE:FORGOTTEN_BEAST_236:PANCREAS +#CREATURE:FORGOTTEN_BEAST_236:SPLEEN +#CREATURE:FORGOTTEN_BEAST_236:KIDNEY +#CREATURE:FORGOTTEN_BEAST_237:MUSCLE + CREATURE:FORGOTTEN_BEAST_237:EYE +"CREATURE:FORGOTTEN_BEAST_237:BRAIN +!CREATURE:FORGOTTEN_BEAST_237:LUNG +"CREATURE:FORGOTTEN_BEAST_237:HEART +"CREATURE:FORGOTTEN_BEAST_237:LIVER + CREATURE:FORGOTTEN_BEAST_237:GUT +$CREATURE:FORGOTTEN_BEAST_237:STOMACH +$CREATURE:FORGOTTEN_BEAST_237:GIZZARD +%CREATURE:FORGOTTEN_BEAST_237:PANCREAS +#CREATURE:FORGOTTEN_BEAST_237:SPLEEN +#CREATURE:FORGOTTEN_BEAST_237:KIDNEY +#CREATURE:FORGOTTEN_BEAST_239:MUSCLE + CREATURE:FORGOTTEN_BEAST_239:EYE +"CREATURE:FORGOTTEN_BEAST_239:BRAIN +!CREATURE:FORGOTTEN_BEAST_239:LUNG +"CREATURE:FORGOTTEN_BEAST_239:HEART +"CREATURE:FORGOTTEN_BEAST_239:LIVER + CREATURE:FORGOTTEN_BEAST_239:GUT +$CREATURE:FORGOTTEN_BEAST_239:STOMACH +$CREATURE:FORGOTTEN_BEAST_239:GIZZARD +%CREATURE:FORGOTTEN_BEAST_239:PANCREAS +#CREATURE:FORGOTTEN_BEAST_239:SPLEEN +#CREATURE:FORGOTTEN_BEAST_239:KIDNEY +#CREATURE:FORGOTTEN_BEAST_240:MUSCLE + CREATURE:FORGOTTEN_BEAST_240:EYE +"CREATURE:FORGOTTEN_BEAST_240:BRAIN +!CREATURE:FORGOTTEN_BEAST_240:LUNG +"CREATURE:FORGOTTEN_BEAST_240:HEART +"CREATURE:FORGOTTEN_BEAST_240:LIVER + CREATURE:FORGOTTEN_BEAST_240:GUT +$CREATURE:FORGOTTEN_BEAST_240:STOMACH +$CREATURE:FORGOTTEN_BEAST_240:GIZZARD +%CREATURE:FORGOTTEN_BEAST_240:PANCREAS +#CREATURE:FORGOTTEN_BEAST_240:SPLEEN +#CREATURE:FORGOTTEN_BEAST_240:KIDNEY +#CREATURE:FORGOTTEN_BEAST_242:MUSCLE + CREATURE:FORGOTTEN_BEAST_242:EYE +"CREATURE:FORGOTTEN_BEAST_242:BRAIN +!CREATURE:FORGOTTEN_BEAST_242:LUNG +"CREATURE:FORGOTTEN_BEAST_242:HEART +"CREATURE:FORGOTTEN_BEAST_242:LIVER + CREATURE:FORGOTTEN_BEAST_242:GUT +$CREATURE:FORGOTTEN_BEAST_242:STOMACH +$CREATURE:FORGOTTEN_BEAST_242:GIZZARD +%CREATURE:FORGOTTEN_BEAST_242:PANCREAS +#CREATURE:FORGOTTEN_BEAST_242:SPLEEN +#CREATURE:FORGOTTEN_BEAST_242:KIDNEY +#CREATURE:FORGOTTEN_BEAST_243:MUSCLE + CREATURE:FORGOTTEN_BEAST_243:EYE +"CREATURE:FORGOTTEN_BEAST_243:BRAIN +!CREATURE:FORGOTTEN_BEAST_243:LUNG +"CREATURE:FORGOTTEN_BEAST_243:HEART +"CREATURE:FORGOTTEN_BEAST_243:LIVER + CREATURE:FORGOTTEN_BEAST_243:GUT +$CREATURE:FORGOTTEN_BEAST_243:STOMACH +$CREATURE:FORGOTTEN_BEAST_243:GIZZARD +%CREATURE:FORGOTTEN_BEAST_243:PANCREAS +#CREATURE:FORGOTTEN_BEAST_243:SPLEEN +#CREATURE:FORGOTTEN_BEAST_243:KIDNEY +#CREATURE:FORGOTTEN_BEAST_244:MUSCLE + CREATURE:FORGOTTEN_BEAST_244:EYE +"CREATURE:FORGOTTEN_BEAST_244:BRAIN +!CREATURE:FORGOTTEN_BEAST_244:LUNG +"CREATURE:FORGOTTEN_BEAST_244:HEART +"CREATURE:FORGOTTEN_BEAST_244:LIVER + CREATURE:FORGOTTEN_BEAST_244:GUT +$CREATURE:FORGOTTEN_BEAST_244:STOMACH +$CREATURE:FORGOTTEN_BEAST_244:GIZZARD +%CREATURE:FORGOTTEN_BEAST_244:PANCREAS +#CREATURE:FORGOTTEN_BEAST_244:SPLEEN +#CREATURE:FORGOTTEN_BEAST_244:KIDNEY +#CREATURE:FORGOTTEN_BEAST_245:MUSCLE + CREATURE:FORGOTTEN_BEAST_245:EYE +"CREATURE:FORGOTTEN_BEAST_245:BRAIN +!CREATURE:FORGOTTEN_BEAST_245:LUNG +"CREATURE:FORGOTTEN_BEAST_245:HEART +"CREATURE:FORGOTTEN_BEAST_245:LIVER + CREATURE:FORGOTTEN_BEAST_245:GUT +$CREATURE:FORGOTTEN_BEAST_245:STOMACH +$CREATURE:FORGOTTEN_BEAST_245:GIZZARD +%CREATURE:FORGOTTEN_BEAST_245:PANCREAS +#CREATURE:FORGOTTEN_BEAST_245:SPLEEN +#CREATURE:FORGOTTEN_BEAST_245:KIDNEY +#CREATURE:FORGOTTEN_BEAST_246:MUSCLE + CREATURE:FORGOTTEN_BEAST_246:EYE +"CREATURE:FORGOTTEN_BEAST_246:BRAIN +!CREATURE:FORGOTTEN_BEAST_246:LUNG +"CREATURE:FORGOTTEN_BEAST_246:HEART +"CREATURE:FORGOTTEN_BEAST_246:LIVER + CREATURE:FORGOTTEN_BEAST_246:GUT +$CREATURE:FORGOTTEN_BEAST_246:STOMACH +$CREATURE:FORGOTTEN_BEAST_246:GIZZARD +%CREATURE:FORGOTTEN_BEAST_246:PANCREAS +#CREATURE:FORGOTTEN_BEAST_246:SPLEEN +#CREATURE:FORGOTTEN_BEAST_246:KIDNEY +#CREATURE:FORGOTTEN_BEAST_247:MUSCLE + CREATURE:FORGOTTEN_BEAST_247:EYE +"CREATURE:FORGOTTEN_BEAST_247:BRAIN +!CREATURE:FORGOTTEN_BEAST_247:LUNG +"CREATURE:FORGOTTEN_BEAST_247:HEART +"CREATURE:FORGOTTEN_BEAST_247:LIVER + CREATURE:FORGOTTEN_BEAST_247:GUT +$CREATURE:FORGOTTEN_BEAST_247:STOMACH +$CREATURE:FORGOTTEN_BEAST_247:GIZZARD +%CREATURE:FORGOTTEN_BEAST_247:PANCREAS +#CREATURE:FORGOTTEN_BEAST_247:SPLEEN +#CREATURE:FORGOTTEN_BEAST_247:KIDNEY +#CREATURE:FORGOTTEN_BEAST_248:MUSCLE + CREATURE:FORGOTTEN_BEAST_248:EYE +"CREATURE:FORGOTTEN_BEAST_248:BRAIN +!CREATURE:FORGOTTEN_BEAST_248:LUNG +"CREATURE:FORGOTTEN_BEAST_248:HEART +"CREATURE:FORGOTTEN_BEAST_248:LIVER + CREATURE:FORGOTTEN_BEAST_248:GUT +$CREATURE:FORGOTTEN_BEAST_248:STOMACH +$CREATURE:FORGOTTEN_BEAST_248:GIZZARD +%CREATURE:FORGOTTEN_BEAST_248:PANCREAS +#CREATURE:FORGOTTEN_BEAST_248:SPLEEN +#CREATURE:FORGOTTEN_BEAST_248:KIDNEY +#CREATURE:FORGOTTEN_BEAST_249:MUSCLE + CREATURE:FORGOTTEN_BEAST_249:EYE +"CREATURE:FORGOTTEN_BEAST_249:BRAIN +!CREATURE:FORGOTTEN_BEAST_249:LUNG +"CREATURE:FORGOTTEN_BEAST_249:HEART +"CREATURE:FORGOTTEN_BEAST_249:LIVER + CREATURE:FORGOTTEN_BEAST_249:GUT +$CREATURE:FORGOTTEN_BEAST_249:STOMACH +$CREATURE:FORGOTTEN_BEAST_249:GIZZARD +%CREATURE:FORGOTTEN_BEAST_249:PANCREAS +#CREATURE:FORGOTTEN_BEAST_249:SPLEEN +#CREATURE:FORGOTTEN_BEAST_249:KIDNEY +#CREATURE:FORGOTTEN_BEAST_253:MUSCLE + CREATURE:FORGOTTEN_BEAST_253:EYE +"CREATURE:FORGOTTEN_BEAST_253:BRAIN +!CREATURE:FORGOTTEN_BEAST_253:LUNG +"CREATURE:FORGOTTEN_BEAST_253:HEART +"CREATURE:FORGOTTEN_BEAST_253:LIVER + CREATURE:FORGOTTEN_BEAST_253:GUT +$CREATURE:FORGOTTEN_BEAST_253:STOMACH +$CREATURE:FORGOTTEN_BEAST_253:GIZZARD +%CREATURE:FORGOTTEN_BEAST_253:PANCREAS +#CREATURE:FORGOTTEN_BEAST_253:SPLEEN +#CREATURE:FORGOTTEN_BEAST_253:KIDNEY +#CREATURE:FORGOTTEN_BEAST_255:MUSCLE + CREATURE:FORGOTTEN_BEAST_255:EYE +"CREATURE:FORGOTTEN_BEAST_255:BRAIN +!CREATURE:FORGOTTEN_BEAST_255:LUNG +"CREATURE:FORGOTTEN_BEAST_255:HEART +"CREATURE:FORGOTTEN_BEAST_255:LIVER + CREATURE:FORGOTTEN_BEAST_255:GUT +$CREATURE:FORGOTTEN_BEAST_255:STOMACH +$CREATURE:FORGOTTEN_BEAST_255:GIZZARD +%CREATURE:FORGOTTEN_BEAST_255:PANCREAS +#CREATURE:FORGOTTEN_BEAST_255:SPLEEN +#CREATURE:FORGOTTEN_BEAST_255:KIDNEY +#CREATURE:FORGOTTEN_BEAST_256:MUSCLE + CREATURE:FORGOTTEN_BEAST_256:EYE +"CREATURE:FORGOTTEN_BEAST_256:BRAIN +!CREATURE:FORGOTTEN_BEAST_256:LUNG +"CREATURE:FORGOTTEN_BEAST_256:HEART +"CREATURE:FORGOTTEN_BEAST_256:LIVER + CREATURE:FORGOTTEN_BEAST_256:GUT +$CREATURE:FORGOTTEN_BEAST_256:STOMACH +$CREATURE:FORGOTTEN_BEAST_256:GIZZARD +%CREATURE:FORGOTTEN_BEAST_256:PANCREAS +#CREATURE:FORGOTTEN_BEAST_256:SPLEEN +#CREATURE:FORGOTTEN_BEAST_256:KIDNEY +#CREATURE:FORGOTTEN_BEAST_259:MUSCLE + CREATURE:FORGOTTEN_BEAST_259:EYE +"CREATURE:FORGOTTEN_BEAST_259:BRAIN +!CREATURE:FORGOTTEN_BEAST_259:LUNG +"CREATURE:FORGOTTEN_BEAST_259:HEART +"CREATURE:FORGOTTEN_BEAST_259:LIVER + CREATURE:FORGOTTEN_BEAST_259:GUT +$CREATURE:FORGOTTEN_BEAST_259:STOMACH +$CREATURE:FORGOTTEN_BEAST_259:GIZZARD +%CREATURE:FORGOTTEN_BEAST_259:PANCREAS +#CREATURE:FORGOTTEN_BEAST_259:SPLEEN +#CREATURE:FORGOTTEN_BEAST_259:KIDNEY +#CREATURE:FORGOTTEN_BEAST_260:MUSCLE + CREATURE:FORGOTTEN_BEAST_260:EYE +"CREATURE:FORGOTTEN_BEAST_260:BRAIN +!CREATURE:FORGOTTEN_BEAST_260:LUNG +"CREATURE:FORGOTTEN_BEAST_260:HEART +"CREATURE:FORGOTTEN_BEAST_260:LIVER + CREATURE:FORGOTTEN_BEAST_260:GUT +$CREATURE:FORGOTTEN_BEAST_260:STOMACH +$CREATURE:FORGOTTEN_BEAST_260:GIZZARD +%CREATURE:FORGOTTEN_BEAST_260:PANCREAS +#CREATURE:FORGOTTEN_BEAST_260:SPLEEN +#CREATURE:FORGOTTEN_BEAST_260:KIDNEY +#CREATURE:FORGOTTEN_BEAST_263:MUSCLE + CREATURE:FORGOTTEN_BEAST_263:EYE +"CREATURE:FORGOTTEN_BEAST_263:BRAIN +!CREATURE:FORGOTTEN_BEAST_263:LUNG +"CREATURE:FORGOTTEN_BEAST_263:HEART +"CREATURE:FORGOTTEN_BEAST_263:LIVER + CREATURE:FORGOTTEN_BEAST_263:GUT +$CREATURE:FORGOTTEN_BEAST_263:STOMACH +$CREATURE:FORGOTTEN_BEAST_263:GIZZARD +%CREATURE:FORGOTTEN_BEAST_263:PANCREAS +#CREATURE:FORGOTTEN_BEAST_263:SPLEEN +#CREATURE:FORGOTTEN_BEAST_263:KIDNEY +#CREATURE:FORGOTTEN_BEAST_264:MUSCLE + CREATURE:FORGOTTEN_BEAST_264:EYE +"CREATURE:FORGOTTEN_BEAST_264:BRAIN +!CREATURE:FORGOTTEN_BEAST_264:LUNG +"CREATURE:FORGOTTEN_BEAST_264:HEART +"CREATURE:FORGOTTEN_BEAST_264:LIVER + CREATURE:FORGOTTEN_BEAST_264:GUT +$CREATURE:FORGOTTEN_BEAST_264:STOMACH +$CREATURE:FORGOTTEN_BEAST_264:GIZZARD +%CREATURE:FORGOTTEN_BEAST_264:PANCREAS +#CREATURE:FORGOTTEN_BEAST_264:SPLEEN +#CREATURE:FORGOTTEN_BEAST_264:KIDNEY +#CREATURE:FORGOTTEN_BEAST_265:MUSCLE + CREATURE:FORGOTTEN_BEAST_265:EYE +"CREATURE:FORGOTTEN_BEAST_265:BRAIN +!CREATURE:FORGOTTEN_BEAST_265:LUNG +"CREATURE:FORGOTTEN_BEAST_265:HEART +"CREATURE:FORGOTTEN_BEAST_265:LIVER + CREATURE:FORGOTTEN_BEAST_265:GUT +$CREATURE:FORGOTTEN_BEAST_265:STOMACH +$CREATURE:FORGOTTEN_BEAST_265:GIZZARD +%CREATURE:FORGOTTEN_BEAST_265:PANCREAS +#CREATURE:FORGOTTEN_BEAST_265:SPLEEN +#CREATURE:FORGOTTEN_BEAST_265:KIDNEY +#CREATURE:FORGOTTEN_BEAST_266:MUSCLE + CREATURE:FORGOTTEN_BEAST_266:EYE +"CREATURE:FORGOTTEN_BEAST_266:BRAIN +!CREATURE:FORGOTTEN_BEAST_266:LUNG +"CREATURE:FORGOTTEN_BEAST_266:HEART +"CREATURE:FORGOTTEN_BEAST_266:LIVER + CREATURE:FORGOTTEN_BEAST_266:GUT +$CREATURE:FORGOTTEN_BEAST_266:STOMACH +$CREATURE:FORGOTTEN_BEAST_266:GIZZARD +%CREATURE:FORGOTTEN_BEAST_266:PANCREAS +#CREATURE:FORGOTTEN_BEAST_266:SPLEEN +#CREATURE:FORGOTTEN_BEAST_266:KIDNEY +#CREATURE:FORGOTTEN_BEAST_268:MUSCLE + CREATURE:FORGOTTEN_BEAST_268:EYE +"CREATURE:FORGOTTEN_BEAST_268:BRAIN +!CREATURE:FORGOTTEN_BEAST_268:LUNG +"CREATURE:FORGOTTEN_BEAST_268:HEART +"CREATURE:FORGOTTEN_BEAST_268:LIVER + CREATURE:FORGOTTEN_BEAST_268:GUT +$CREATURE:FORGOTTEN_BEAST_268:STOMACH +$CREATURE:FORGOTTEN_BEAST_268:GIZZARD +%CREATURE:FORGOTTEN_BEAST_268:PANCREAS +#CREATURE:FORGOTTEN_BEAST_268:SPLEEN +#CREATURE:FORGOTTEN_BEAST_268:KIDNEY +#CREATURE:FORGOTTEN_BEAST_269:MUSCLE + CREATURE:FORGOTTEN_BEAST_269:EYE +"CREATURE:FORGOTTEN_BEAST_269:BRAIN +!CREATURE:FORGOTTEN_BEAST_269:LUNG +"CREATURE:FORGOTTEN_BEAST_269:HEART +"CREATURE:FORGOTTEN_BEAST_269:LIVER + CREATURE:FORGOTTEN_BEAST_269:GUT +$CREATURE:FORGOTTEN_BEAST_269:STOMACH +$CREATURE:FORGOTTEN_BEAST_269:GIZZARD +%CREATURE:FORGOTTEN_BEAST_269:PANCREAS +#CREATURE:FORGOTTEN_BEAST_269:SPLEEN +#CREATURE:FORGOTTEN_BEAST_269:KIDNEY +#CREATURE:FORGOTTEN_BEAST_270:MUSCLE + CREATURE:FORGOTTEN_BEAST_270:EYE +"CREATURE:FORGOTTEN_BEAST_270:BRAIN +!CREATURE:FORGOTTEN_BEAST_270:LUNG +"CREATURE:FORGOTTEN_BEAST_270:HEART +"CREATURE:FORGOTTEN_BEAST_270:LIVER + CREATURE:FORGOTTEN_BEAST_270:GUT +$CREATURE:FORGOTTEN_BEAST_270:STOMACH +$CREATURE:FORGOTTEN_BEAST_270:GIZZARD +%CREATURE:FORGOTTEN_BEAST_270:PANCREAS +#CREATURE:FORGOTTEN_BEAST_270:SPLEEN +#CREATURE:FORGOTTEN_BEAST_270:KIDNEY +#CREATURE:FORGOTTEN_BEAST_271:MUSCLE + CREATURE:FORGOTTEN_BEAST_271:EYE +"CREATURE:FORGOTTEN_BEAST_271:BRAIN +!CREATURE:FORGOTTEN_BEAST_271:LUNG +"CREATURE:FORGOTTEN_BEAST_271:HEART +"CREATURE:FORGOTTEN_BEAST_271:LIVER + CREATURE:FORGOTTEN_BEAST_271:GUT +$CREATURE:FORGOTTEN_BEAST_271:STOMACH +$CREATURE:FORGOTTEN_BEAST_271:GIZZARD +%CREATURE:FORGOTTEN_BEAST_271:PANCREAS +#CREATURE:FORGOTTEN_BEAST_271:SPLEEN +#CREATURE:FORGOTTEN_BEAST_271:KIDNEY +#CREATURE:FORGOTTEN_BEAST_272:MUSCLE + CREATURE:FORGOTTEN_BEAST_272:EYE +"CREATURE:FORGOTTEN_BEAST_272:BRAIN +!CREATURE:FORGOTTEN_BEAST_272:LUNG +"CREATURE:FORGOTTEN_BEAST_272:HEART +"CREATURE:FORGOTTEN_BEAST_272:LIVER + CREATURE:FORGOTTEN_BEAST_272:GUT +$CREATURE:FORGOTTEN_BEAST_272:STOMACH +$CREATURE:FORGOTTEN_BEAST_272:GIZZARD +%CREATURE:FORGOTTEN_BEAST_272:PANCREAS +#CREATURE:FORGOTTEN_BEAST_272:SPLEEN +#CREATURE:FORGOTTEN_BEAST_272:KIDNEY +#CREATURE:FORGOTTEN_BEAST_273:MUSCLE + CREATURE:FORGOTTEN_BEAST_273:EYE +"CREATURE:FORGOTTEN_BEAST_273:BRAIN +!CREATURE:FORGOTTEN_BEAST_273:LUNG +"CREATURE:FORGOTTEN_BEAST_273:HEART +"CREATURE:FORGOTTEN_BEAST_273:LIVER + CREATURE:FORGOTTEN_BEAST_273:GUT +$CREATURE:FORGOTTEN_BEAST_273:STOMACH +$CREATURE:FORGOTTEN_BEAST_273:GIZZARD +%CREATURE:FORGOTTEN_BEAST_273:PANCREAS +#CREATURE:FORGOTTEN_BEAST_273:SPLEEN +#CREATURE:FORGOTTEN_BEAST_273:KIDNEY +#CREATURE:FORGOTTEN_BEAST_274:MUSCLE + CREATURE:FORGOTTEN_BEAST_274:EYE +"CREATURE:FORGOTTEN_BEAST_274:BRAIN +!CREATURE:FORGOTTEN_BEAST_274:LUNG +"CREATURE:FORGOTTEN_BEAST_274:HEART +"CREATURE:FORGOTTEN_BEAST_274:LIVER + CREATURE:FORGOTTEN_BEAST_274:GUT +$CREATURE:FORGOTTEN_BEAST_274:STOMACH +$CREATURE:FORGOTTEN_BEAST_274:GIZZARD +%CREATURE:FORGOTTEN_BEAST_274:PANCREAS +#CREATURE:FORGOTTEN_BEAST_274:SPLEEN +#CREATURE:FORGOTTEN_BEAST_274:KIDNEY +#CREATURE:FORGOTTEN_BEAST_275:MUSCLE + CREATURE:FORGOTTEN_BEAST_275:EYE +"CREATURE:FORGOTTEN_BEAST_275:BRAIN +!CREATURE:FORGOTTEN_BEAST_275:LUNG +"CREATURE:FORGOTTEN_BEAST_275:HEART +"CREATURE:FORGOTTEN_BEAST_275:LIVER + CREATURE:FORGOTTEN_BEAST_275:GUT +$CREATURE:FORGOTTEN_BEAST_275:STOMACH +$CREATURE:FORGOTTEN_BEAST_275:GIZZARD +%CREATURE:FORGOTTEN_BEAST_275:PANCREAS +#CREATURE:FORGOTTEN_BEAST_275:SPLEEN +#CREATURE:FORGOTTEN_BEAST_275:KIDNEY +#CREATURE:FORGOTTEN_BEAST_276:MUSCLE + CREATURE:FORGOTTEN_BEAST_276:EYE +"CREATURE:FORGOTTEN_BEAST_276:BRAIN +!CREATURE:FORGOTTEN_BEAST_276:LUNG +"CREATURE:FORGOTTEN_BEAST_276:HEART +"CREATURE:FORGOTTEN_BEAST_276:LIVER + CREATURE:FORGOTTEN_BEAST_276:GUT +$CREATURE:FORGOTTEN_BEAST_276:STOMACH +$CREATURE:FORGOTTEN_BEAST_276:GIZZARD +%CREATURE:FORGOTTEN_BEAST_276:PANCREAS +#CREATURE:FORGOTTEN_BEAST_276:SPLEEN +#CREATURE:FORGOTTEN_BEAST_276:KIDNEY +#CREATURE:FORGOTTEN_BEAST_277:MUSCLE + CREATURE:FORGOTTEN_BEAST_277:EYE +"CREATURE:FORGOTTEN_BEAST_277:BRAIN +!CREATURE:FORGOTTEN_BEAST_277:LUNG +"CREATURE:FORGOTTEN_BEAST_277:HEART +"CREATURE:FORGOTTEN_BEAST_277:LIVER + CREATURE:FORGOTTEN_BEAST_277:GUT +$CREATURE:FORGOTTEN_BEAST_277:STOMACH +$CREATURE:FORGOTTEN_BEAST_277:GIZZARD +%CREATURE:FORGOTTEN_BEAST_277:PANCREAS +#CREATURE:FORGOTTEN_BEAST_277:SPLEEN +#CREATURE:FORGOTTEN_BEAST_277:KIDNEY +#CREATURE:FORGOTTEN_BEAST_279:MUSCLE + CREATURE:FORGOTTEN_BEAST_279:EYE +"CREATURE:FORGOTTEN_BEAST_279:BRAIN +!CREATURE:FORGOTTEN_BEAST_279:LUNG +"CREATURE:FORGOTTEN_BEAST_279:HEART +"CREATURE:FORGOTTEN_BEAST_279:LIVER + CREATURE:FORGOTTEN_BEAST_279:GUT +$CREATURE:FORGOTTEN_BEAST_279:STOMACH +$CREATURE:FORGOTTEN_BEAST_279:GIZZARD +%CREATURE:FORGOTTEN_BEAST_279:PANCREAS +#CREATURE:FORGOTTEN_BEAST_279:SPLEEN +#CREATURE:FORGOTTEN_BEAST_279:KIDNEY +#CREATURE:FORGOTTEN_BEAST_280:MUSCLE + CREATURE:FORGOTTEN_BEAST_280:EYE +"CREATURE:FORGOTTEN_BEAST_280:BRAIN +!CREATURE:FORGOTTEN_BEAST_280:LUNG +"CREATURE:FORGOTTEN_BEAST_280:HEART +"CREATURE:FORGOTTEN_BEAST_280:LIVER + CREATURE:FORGOTTEN_BEAST_280:GUT +$CREATURE:FORGOTTEN_BEAST_280:STOMACH +$CREATURE:FORGOTTEN_BEAST_280:GIZZARD +%CREATURE:FORGOTTEN_BEAST_280:PANCREAS +#CREATURE:FORGOTTEN_BEAST_280:SPLEEN +#CREATURE:FORGOTTEN_BEAST_280:KIDNEY +#CREATURE:FORGOTTEN_BEAST_281:MUSCLE + CREATURE:FORGOTTEN_BEAST_281:EYE +"CREATURE:FORGOTTEN_BEAST_281:BRAIN +!CREATURE:FORGOTTEN_BEAST_281:LUNG +"CREATURE:FORGOTTEN_BEAST_281:HEART +"CREATURE:FORGOTTEN_BEAST_281:LIVER + CREATURE:FORGOTTEN_BEAST_281:GUT +$CREATURE:FORGOTTEN_BEAST_281:STOMACH +$CREATURE:FORGOTTEN_BEAST_281:GIZZARD +%CREATURE:FORGOTTEN_BEAST_281:PANCREAS +#CREATURE:FORGOTTEN_BEAST_281:SPLEEN +#CREATURE:FORGOTTEN_BEAST_281:KIDNEY +#CREATURE:FORGOTTEN_BEAST_282:MUSCLE + CREATURE:FORGOTTEN_BEAST_282:EYE +"CREATURE:FORGOTTEN_BEAST_282:BRAIN +!CREATURE:FORGOTTEN_BEAST_282:LUNG +"CREATURE:FORGOTTEN_BEAST_282:HEART +"CREATURE:FORGOTTEN_BEAST_282:LIVER + CREATURE:FORGOTTEN_BEAST_282:GUT +$CREATURE:FORGOTTEN_BEAST_282:STOMACH +$CREATURE:FORGOTTEN_BEAST_282:GIZZARD +%CREATURE:FORGOTTEN_BEAST_282:PANCREAS +#CREATURE:FORGOTTEN_BEAST_282:SPLEEN +#CREATURE:FORGOTTEN_BEAST_282:KIDNEY +#CREATURE:FORGOTTEN_BEAST_283:MUSCLE + CREATURE:FORGOTTEN_BEAST_283:EYE +"CREATURE:FORGOTTEN_BEAST_283:BRAIN +!CREATURE:FORGOTTEN_BEAST_283:LUNG +"CREATURE:FORGOTTEN_BEAST_283:HEART +"CREATURE:FORGOTTEN_BEAST_283:LIVER + CREATURE:FORGOTTEN_BEAST_283:GUT +$CREATURE:FORGOTTEN_BEAST_283:STOMACH +$CREATURE:FORGOTTEN_BEAST_283:GIZZARD +%CREATURE:FORGOTTEN_BEAST_283:PANCREAS +#CREATURE:FORGOTTEN_BEAST_283:SPLEEN +#CREATURE:FORGOTTEN_BEAST_283:KIDNEY +#CREATURE:FORGOTTEN_BEAST_285:MUSCLE + CREATURE:FORGOTTEN_BEAST_285:EYE +"CREATURE:FORGOTTEN_BEAST_285:BRAIN +!CREATURE:FORGOTTEN_BEAST_285:LUNG +"CREATURE:FORGOTTEN_BEAST_285:HEART +"CREATURE:FORGOTTEN_BEAST_285:LIVER + CREATURE:FORGOTTEN_BEAST_285:GUT +$CREATURE:FORGOTTEN_BEAST_285:STOMACH +$CREATURE:FORGOTTEN_BEAST_285:GIZZARD +%CREATURE:FORGOTTEN_BEAST_285:PANCREAS +#CREATURE:FORGOTTEN_BEAST_285:SPLEEN +#CREATURE:FORGOTTEN_BEAST_285:KIDNEY +#CREATURE:FORGOTTEN_BEAST_286:MUSCLE + CREATURE:FORGOTTEN_BEAST_286:EYE +"CREATURE:FORGOTTEN_BEAST_286:BRAIN +!CREATURE:FORGOTTEN_BEAST_286:LUNG +"CREATURE:FORGOTTEN_BEAST_286:HEART +"CREATURE:FORGOTTEN_BEAST_286:LIVER + CREATURE:FORGOTTEN_BEAST_286:GUT +$CREATURE:FORGOTTEN_BEAST_286:STOMACH +$CREATURE:FORGOTTEN_BEAST_286:GIZZARD +%CREATURE:FORGOTTEN_BEAST_286:PANCREAS +#CREATURE:FORGOTTEN_BEAST_286:SPLEEN +#CREATURE:FORGOTTEN_BEAST_286:KIDNEY +#CREATURE:FORGOTTEN_BEAST_287:MUSCLE + CREATURE:FORGOTTEN_BEAST_287:EYE +"CREATURE:FORGOTTEN_BEAST_287:BRAIN +!CREATURE:FORGOTTEN_BEAST_287:LUNG +"CREATURE:FORGOTTEN_BEAST_287:HEART +"CREATURE:FORGOTTEN_BEAST_287:LIVER + CREATURE:FORGOTTEN_BEAST_287:GUT +$CREATURE:FORGOTTEN_BEAST_287:STOMACH +$CREATURE:FORGOTTEN_BEAST_287:GIZZARD +%CREATURE:FORGOTTEN_BEAST_287:PANCREAS +#CREATURE:FORGOTTEN_BEAST_287:SPLEEN +#CREATURE:FORGOTTEN_BEAST_287:KIDNEY +#CREATURE:FORGOTTEN_BEAST_288:MUSCLE + CREATURE:FORGOTTEN_BEAST_288:EYE +"CREATURE:FORGOTTEN_BEAST_288:BRAIN +!CREATURE:FORGOTTEN_BEAST_288:LUNG +"CREATURE:FORGOTTEN_BEAST_288:HEART +"CREATURE:FORGOTTEN_BEAST_288:LIVER + CREATURE:FORGOTTEN_BEAST_288:GUT +$CREATURE:FORGOTTEN_BEAST_288:STOMACH +$CREATURE:FORGOTTEN_BEAST_288:GIZZARD +%CREATURE:FORGOTTEN_BEAST_288:PANCREAS +#CREATURE:FORGOTTEN_BEAST_288:SPLEEN +#CREATURE:FORGOTTEN_BEAST_288:KIDNEY +#CREATURE:FORGOTTEN_BEAST_289:MUSCLE + CREATURE:FORGOTTEN_BEAST_289:EYE +"CREATURE:FORGOTTEN_BEAST_289:BRAIN +!CREATURE:FORGOTTEN_BEAST_289:LUNG +"CREATURE:FORGOTTEN_BEAST_289:HEART +"CREATURE:FORGOTTEN_BEAST_289:LIVER + CREATURE:FORGOTTEN_BEAST_289:GUT +$CREATURE:FORGOTTEN_BEAST_289:STOMACH +$CREATURE:FORGOTTEN_BEAST_289:GIZZARD +%CREATURE:FORGOTTEN_BEAST_289:PANCREAS +#CREATURE:FORGOTTEN_BEAST_289:SPLEEN +#CREATURE:FORGOTTEN_BEAST_289:KIDNEY +#CREATURE:FORGOTTEN_BEAST_290:MUSCLE + CREATURE:FORGOTTEN_BEAST_290:EYE +"CREATURE:FORGOTTEN_BEAST_290:BRAIN +!CREATURE:FORGOTTEN_BEAST_290:LUNG +"CREATURE:FORGOTTEN_BEAST_290:HEART +"CREATURE:FORGOTTEN_BEAST_290:LIVER + CREATURE:FORGOTTEN_BEAST_290:GUT +$CREATURE:FORGOTTEN_BEAST_290:STOMACH +$CREATURE:FORGOTTEN_BEAST_290:GIZZARD +%CREATURE:FORGOTTEN_BEAST_290:PANCREAS +#CREATURE:FORGOTTEN_BEAST_290:SPLEEN +#CREATURE:FORGOTTEN_BEAST_290:KIDNEY +#CREATURE:FORGOTTEN_BEAST_293:MUSCLE + CREATURE:FORGOTTEN_BEAST_293:EYE +"CREATURE:FORGOTTEN_BEAST_293:BRAIN +!CREATURE:FORGOTTEN_BEAST_293:LUNG +"CREATURE:FORGOTTEN_BEAST_293:HEART +"CREATURE:FORGOTTEN_BEAST_293:LIVER + CREATURE:FORGOTTEN_BEAST_293:GUT +$CREATURE:FORGOTTEN_BEAST_293:STOMACH +$CREATURE:FORGOTTEN_BEAST_293:GIZZARD +%CREATURE:FORGOTTEN_BEAST_293:PANCREAS +#CREATURE:FORGOTTEN_BEAST_293:SPLEEN +#CREATURE:FORGOTTEN_BEAST_293:KIDNEY +#CREATURE:FORGOTTEN_BEAST_295:MUSCLE + CREATURE:FORGOTTEN_BEAST_295:EYE +"CREATURE:FORGOTTEN_BEAST_295:BRAIN +!CREATURE:FORGOTTEN_BEAST_295:LUNG +"CREATURE:FORGOTTEN_BEAST_295:HEART +"CREATURE:FORGOTTEN_BEAST_295:LIVER + CREATURE:FORGOTTEN_BEAST_295:GUT +$CREATURE:FORGOTTEN_BEAST_295:STOMACH +$CREATURE:FORGOTTEN_BEAST_295:GIZZARD +%CREATURE:FORGOTTEN_BEAST_295:PANCREAS +#CREATURE:FORGOTTEN_BEAST_295:SPLEEN +#CREATURE:FORGOTTEN_BEAST_295:KIDNEY +#CREATURE:FORGOTTEN_BEAST_297:MUSCLE + CREATURE:FORGOTTEN_BEAST_297:EYE +"CREATURE:FORGOTTEN_BEAST_297:BRAIN +!CREATURE:FORGOTTEN_BEAST_297:LUNG +"CREATURE:FORGOTTEN_BEAST_297:HEART +"CREATURE:FORGOTTEN_BEAST_297:LIVER + CREATURE:FORGOTTEN_BEAST_297:GUT +$CREATURE:FORGOTTEN_BEAST_297:STOMACH +$CREATURE:FORGOTTEN_BEAST_297:GIZZARD +%CREATURE:FORGOTTEN_BEAST_297:PANCREAS +#CREATURE:FORGOTTEN_BEAST_297:SPLEEN +#CREATURE:FORGOTTEN_BEAST_297:KIDNEY +#CREATURE:FORGOTTEN_BEAST_300:MUSCLE + CREATURE:FORGOTTEN_BEAST_300:EYE +"CREATURE:FORGOTTEN_BEAST_300:BRAIN +!CREATURE:FORGOTTEN_BEAST_300:LUNG +"CREATURE:FORGOTTEN_BEAST_300:HEART +"CREATURE:FORGOTTEN_BEAST_300:LIVER + CREATURE:FORGOTTEN_BEAST_300:GUT +$CREATURE:FORGOTTEN_BEAST_300:STOMACH +$CREATURE:FORGOTTEN_BEAST_300:GIZZARD +%CREATURE:FORGOTTEN_BEAST_300:PANCREAS +#CREATURE:FORGOTTEN_BEAST_300:SPLEEN +#CREATURE:FORGOTTEN_BEAST_300:KIDNEY +#CREATURE:FORGOTTEN_BEAST_302:MUSCLE + CREATURE:FORGOTTEN_BEAST_302:EYE +"CREATURE:FORGOTTEN_BEAST_302:BRAIN +!CREATURE:FORGOTTEN_BEAST_302:LUNG +"CREATURE:FORGOTTEN_BEAST_302:HEART +"CREATURE:FORGOTTEN_BEAST_302:LIVER + CREATURE:FORGOTTEN_BEAST_302:GUT +$CREATURE:FORGOTTEN_BEAST_302:STOMACH +$CREATURE:FORGOTTEN_BEAST_302:GIZZARD +%CREATURE:FORGOTTEN_BEAST_302:PANCREAS +#CREATURE:FORGOTTEN_BEAST_302:SPLEEN +#CREATURE:FORGOTTEN_BEAST_302:KIDNEY +#CREATURE:FORGOTTEN_BEAST_306:MUSCLE + CREATURE:FORGOTTEN_BEAST_306:EYE +"CREATURE:FORGOTTEN_BEAST_306:BRAIN +!CREATURE:FORGOTTEN_BEAST_306:LUNG +"CREATURE:FORGOTTEN_BEAST_306:HEART +"CREATURE:FORGOTTEN_BEAST_306:LIVER + CREATURE:FORGOTTEN_BEAST_306:GUT +$CREATURE:FORGOTTEN_BEAST_306:STOMACH +$CREATURE:FORGOTTEN_BEAST_306:GIZZARD +%CREATURE:FORGOTTEN_BEAST_306:PANCREAS +#CREATURE:FORGOTTEN_BEAST_306:SPLEEN +#CREATURE:FORGOTTEN_BEAST_306:KIDNEY +#CREATURE:FORGOTTEN_BEAST_307:MUSCLE + CREATURE:FORGOTTEN_BEAST_307:EYE +"CREATURE:FORGOTTEN_BEAST_307:BRAIN +!CREATURE:FORGOTTEN_BEAST_307:LUNG +"CREATURE:FORGOTTEN_BEAST_307:HEART +"CREATURE:FORGOTTEN_BEAST_307:LIVER + CREATURE:FORGOTTEN_BEAST_307:GUT +$CREATURE:FORGOTTEN_BEAST_307:STOMACH +$CREATURE:FORGOTTEN_BEAST_307:GIZZARD +%CREATURE:FORGOTTEN_BEAST_307:PANCREAS +#CREATURE:FORGOTTEN_BEAST_307:SPLEEN +#CREATURE:FORGOTTEN_BEAST_307:KIDNEY +#CREATURE:FORGOTTEN_BEAST_310:MUSCLE + CREATURE:FORGOTTEN_BEAST_310:EYE +"CREATURE:FORGOTTEN_BEAST_310:BRAIN +!CREATURE:FORGOTTEN_BEAST_310:LUNG +"CREATURE:FORGOTTEN_BEAST_310:HEART +"CREATURE:FORGOTTEN_BEAST_310:LIVER + CREATURE:FORGOTTEN_BEAST_310:GUT +$CREATURE:FORGOTTEN_BEAST_310:STOMACH +$CREATURE:FORGOTTEN_BEAST_310:GIZZARD +%CREATURE:FORGOTTEN_BEAST_310:PANCREAS +#CREATURE:FORGOTTEN_BEAST_310:SPLEEN +#CREATURE:FORGOTTEN_BEAST_310:KIDNEY +#CREATURE:FORGOTTEN_BEAST_311:MUSCLE + CREATURE:FORGOTTEN_BEAST_311:EYE +"CREATURE:FORGOTTEN_BEAST_311:BRAIN +!CREATURE:FORGOTTEN_BEAST_311:LUNG +"CREATURE:FORGOTTEN_BEAST_311:HEART +"CREATURE:FORGOTTEN_BEAST_311:LIVER + CREATURE:FORGOTTEN_BEAST_311:GUT +$CREATURE:FORGOTTEN_BEAST_311:STOMACH +$CREATURE:FORGOTTEN_BEAST_311:GIZZARD +%CREATURE:FORGOTTEN_BEAST_311:PANCREAS +#CREATURE:FORGOTTEN_BEAST_311:SPLEEN +#CREATURE:FORGOTTEN_BEAST_311:KIDNEY +#CREATURE:FORGOTTEN_BEAST_312:MUSCLE + CREATURE:FORGOTTEN_BEAST_312:EYE +"CREATURE:FORGOTTEN_BEAST_312:BRAIN +!CREATURE:FORGOTTEN_BEAST_312:LUNG +"CREATURE:FORGOTTEN_BEAST_312:HEART +"CREATURE:FORGOTTEN_BEAST_312:LIVER + CREATURE:FORGOTTEN_BEAST_312:GUT +$CREATURE:FORGOTTEN_BEAST_312:STOMACH +$CREATURE:FORGOTTEN_BEAST_312:GIZZARD +%CREATURE:FORGOTTEN_BEAST_312:PANCREAS +#CREATURE:FORGOTTEN_BEAST_312:SPLEEN +#CREATURE:FORGOTTEN_BEAST_312:KIDNEY +#CREATURE:FORGOTTEN_BEAST_314:MUSCLE + CREATURE:FORGOTTEN_BEAST_314:EYE +"CREATURE:FORGOTTEN_BEAST_314:BRAIN +!CREATURE:FORGOTTEN_BEAST_314:LUNG +"CREATURE:FORGOTTEN_BEAST_314:HEART +"CREATURE:FORGOTTEN_BEAST_314:LIVER + CREATURE:FORGOTTEN_BEAST_314:GUT +$CREATURE:FORGOTTEN_BEAST_314:STOMACH +$CREATURE:FORGOTTEN_BEAST_314:GIZZARD +%CREATURE:FORGOTTEN_BEAST_314:PANCREAS +#CREATURE:FORGOTTEN_BEAST_314:SPLEEN +#CREATURE:FORGOTTEN_BEAST_314:KIDNEY +#CREATURE:FORGOTTEN_BEAST_316:MUSCLE + CREATURE:FORGOTTEN_BEAST_316:EYE +"CREATURE:FORGOTTEN_BEAST_316:BRAIN +!CREATURE:FORGOTTEN_BEAST_316:LUNG +"CREATURE:FORGOTTEN_BEAST_316:HEART +"CREATURE:FORGOTTEN_BEAST_316:LIVER + CREATURE:FORGOTTEN_BEAST_316:GUT +$CREATURE:FORGOTTEN_BEAST_316:STOMACH +$CREATURE:FORGOTTEN_BEAST_316:GIZZARD +%CREATURE:FORGOTTEN_BEAST_316:PANCREAS +#CREATURE:FORGOTTEN_BEAST_316:SPLEEN +#CREATURE:FORGOTTEN_BEAST_316:KIDNEY +#CREATURE:FORGOTTEN_BEAST_317:MUSCLE + CREATURE:FORGOTTEN_BEAST_317:EYE +"CREATURE:FORGOTTEN_BEAST_317:BRAIN +!CREATURE:FORGOTTEN_BEAST_317:LUNG +"CREATURE:FORGOTTEN_BEAST_317:HEART +"CREATURE:FORGOTTEN_BEAST_317:LIVER + CREATURE:FORGOTTEN_BEAST_317:GUT +$CREATURE:FORGOTTEN_BEAST_317:STOMACH +$CREATURE:FORGOTTEN_BEAST_317:GIZZARD +%CREATURE:FORGOTTEN_BEAST_317:PANCREAS +#CREATURE:FORGOTTEN_BEAST_317:SPLEEN +#CREATURE:FORGOTTEN_BEAST_317:KIDNEY +#CREATURE:FORGOTTEN_BEAST_318:MUSCLE + CREATURE:FORGOTTEN_BEAST_318:EYE +"CREATURE:FORGOTTEN_BEAST_318:BRAIN +!CREATURE:FORGOTTEN_BEAST_318:LUNG +"CREATURE:FORGOTTEN_BEAST_318:HEART +"CREATURE:FORGOTTEN_BEAST_318:LIVER + CREATURE:FORGOTTEN_BEAST_318:GUT +$CREATURE:FORGOTTEN_BEAST_318:STOMACH +$CREATURE:FORGOTTEN_BEAST_318:GIZZARD +%CREATURE:FORGOTTEN_BEAST_318:PANCREAS +#CREATURE:FORGOTTEN_BEAST_318:SPLEEN +#CREATURE:FORGOTTEN_BEAST_318:KIDNEY +#CREATURE:FORGOTTEN_BEAST_320:MUSCLE + CREATURE:FORGOTTEN_BEAST_320:EYE +"CREATURE:FORGOTTEN_BEAST_320:BRAIN +!CREATURE:FORGOTTEN_BEAST_320:LUNG +"CREATURE:FORGOTTEN_BEAST_320:HEART +"CREATURE:FORGOTTEN_BEAST_320:LIVER + CREATURE:FORGOTTEN_BEAST_320:GUT +$CREATURE:FORGOTTEN_BEAST_320:STOMACH +$CREATURE:FORGOTTEN_BEAST_320:GIZZARD +%CREATURE:FORGOTTEN_BEAST_320:PANCREAS +#CREATURE:FORGOTTEN_BEAST_320:SPLEEN +#CREATURE:FORGOTTEN_BEAST_320:KIDNEY +#CREATURE:FORGOTTEN_BEAST_321:MUSCLE + CREATURE:FORGOTTEN_BEAST_321:EYE +"CREATURE:FORGOTTEN_BEAST_321:BRAIN +!CREATURE:FORGOTTEN_BEAST_321:LUNG +"CREATURE:FORGOTTEN_BEAST_321:HEART +"CREATURE:FORGOTTEN_BEAST_321:LIVER + CREATURE:FORGOTTEN_BEAST_321:GUT +$CREATURE:FORGOTTEN_BEAST_321:STOMACH +$CREATURE:FORGOTTEN_BEAST_321:GIZZARD +%CREATURE:FORGOTTEN_BEAST_321:PANCREAS +#CREATURE:FORGOTTEN_BEAST_321:SPLEEN +#CREATURE:FORGOTTEN_BEAST_321:KIDNEY +#CREATURE:FORGOTTEN_BEAST_322:MUSCLE + CREATURE:FORGOTTEN_BEAST_322:EYE +"CREATURE:FORGOTTEN_BEAST_322:BRAIN +!CREATURE:FORGOTTEN_BEAST_322:LUNG +"CREATURE:FORGOTTEN_BEAST_322:HEART +"CREATURE:FORGOTTEN_BEAST_322:LIVER + CREATURE:FORGOTTEN_BEAST_322:GUT +$CREATURE:FORGOTTEN_BEAST_322:STOMACH +$CREATURE:FORGOTTEN_BEAST_322:GIZZARD +%CREATURE:FORGOTTEN_BEAST_322:PANCREAS +#CREATURE:FORGOTTEN_BEAST_322:SPLEEN +#CREATURE:FORGOTTEN_BEAST_322:KIDNEY +#CREATURE:FORGOTTEN_BEAST_323:MUSCLE + CREATURE:FORGOTTEN_BEAST_323:EYE +"CREATURE:FORGOTTEN_BEAST_323:BRAIN +!CREATURE:FORGOTTEN_BEAST_323:LUNG +"CREATURE:FORGOTTEN_BEAST_323:HEART +"CREATURE:FORGOTTEN_BEAST_323:LIVER + CREATURE:FORGOTTEN_BEAST_323:GUT +$CREATURE:FORGOTTEN_BEAST_323:STOMACH +$CREATURE:FORGOTTEN_BEAST_323:GIZZARD +%CREATURE:FORGOTTEN_BEAST_323:PANCREAS +#CREATURE:FORGOTTEN_BEAST_323:SPLEEN +#CREATURE:FORGOTTEN_BEAST_323:KIDNEY +#CREATURE:FORGOTTEN_BEAST_324:MUSCLE + CREATURE:FORGOTTEN_BEAST_324:EYE +"CREATURE:FORGOTTEN_BEAST_324:BRAIN +!CREATURE:FORGOTTEN_BEAST_324:LUNG +"CREATURE:FORGOTTEN_BEAST_324:HEART +"CREATURE:FORGOTTEN_BEAST_324:LIVER + CREATURE:FORGOTTEN_BEAST_324:GUT +$CREATURE:FORGOTTEN_BEAST_324:STOMACH +$CREATURE:FORGOTTEN_BEAST_324:GIZZARD +%CREATURE:FORGOTTEN_BEAST_324:PANCREAS +#CREATURE:FORGOTTEN_BEAST_324:SPLEEN +#CREATURE:FORGOTTEN_BEAST_324:KIDNEY +#CREATURE:FORGOTTEN_BEAST_325:MUSCLE + CREATURE:FORGOTTEN_BEAST_325:EYE +"CREATURE:FORGOTTEN_BEAST_325:BRAIN +!CREATURE:FORGOTTEN_BEAST_325:LUNG +"CREATURE:FORGOTTEN_BEAST_325:HEART +"CREATURE:FORGOTTEN_BEAST_325:LIVER + CREATURE:FORGOTTEN_BEAST_325:GUT +$CREATURE:FORGOTTEN_BEAST_325:STOMACH +$CREATURE:FORGOTTEN_BEAST_325:GIZZARD +%CREATURE:FORGOTTEN_BEAST_325:PANCREAS +#CREATURE:FORGOTTEN_BEAST_325:SPLEEN +#CREATURE:FORGOTTEN_BEAST_325:KIDNEY +#CREATURE:FORGOTTEN_BEAST_326:MUSCLE + CREATURE:FORGOTTEN_BEAST_326:EYE +"CREATURE:FORGOTTEN_BEAST_326:BRAIN +!CREATURE:FORGOTTEN_BEAST_326:LUNG +"CREATURE:FORGOTTEN_BEAST_326:HEART +"CREATURE:FORGOTTEN_BEAST_326:LIVER + CREATURE:FORGOTTEN_BEAST_326:GUT +$CREATURE:FORGOTTEN_BEAST_326:STOMACH +$CREATURE:FORGOTTEN_BEAST_326:GIZZARD +%CREATURE:FORGOTTEN_BEAST_326:PANCREAS +#CREATURE:FORGOTTEN_BEAST_326:SPLEEN +#CREATURE:FORGOTTEN_BEAST_326:KIDNEY +#CREATURE:FORGOTTEN_BEAST_327:MUSCLE + CREATURE:FORGOTTEN_BEAST_327:EYE +"CREATURE:FORGOTTEN_BEAST_327:BRAIN +!CREATURE:FORGOTTEN_BEAST_327:LUNG +"CREATURE:FORGOTTEN_BEAST_327:HEART +"CREATURE:FORGOTTEN_BEAST_327:LIVER + CREATURE:FORGOTTEN_BEAST_327:GUT +$CREATURE:FORGOTTEN_BEAST_327:STOMACH +$CREATURE:FORGOTTEN_BEAST_327:GIZZARD +%CREATURE:FORGOTTEN_BEAST_327:PANCREAS +#CREATURE:FORGOTTEN_BEAST_327:SPLEEN +#CREATURE:FORGOTTEN_BEAST_327:KIDNEY +#CREATURE:FORGOTTEN_BEAST_328:MUSCLE + CREATURE:FORGOTTEN_BEAST_328:EYE +"CREATURE:FORGOTTEN_BEAST_328:BRAIN +!CREATURE:FORGOTTEN_BEAST_328:LUNG +"CREATURE:FORGOTTEN_BEAST_328:HEART +"CREATURE:FORGOTTEN_BEAST_328:LIVER + CREATURE:FORGOTTEN_BEAST_328:GUT +$CREATURE:FORGOTTEN_BEAST_328:STOMACH +$CREATURE:FORGOTTEN_BEAST_328:GIZZARD +%CREATURE:FORGOTTEN_BEAST_328:PANCREAS +#CREATURE:FORGOTTEN_BEAST_328:SPLEEN +#CREATURE:FORGOTTEN_BEAST_328:KIDNEY +#CREATURE:FORGOTTEN_BEAST_329:MUSCLE + CREATURE:FORGOTTEN_BEAST_329:EYE +"CREATURE:FORGOTTEN_BEAST_329:BRAIN +!CREATURE:FORGOTTEN_BEAST_329:LUNG +"CREATURE:FORGOTTEN_BEAST_329:HEART +"CREATURE:FORGOTTEN_BEAST_329:LIVER + CREATURE:FORGOTTEN_BEAST_329:GUT +$CREATURE:FORGOTTEN_BEAST_329:STOMACH +$CREATURE:FORGOTTEN_BEAST_329:GIZZARD +%CREATURE:FORGOTTEN_BEAST_329:PANCREAS +#CREATURE:FORGOTTEN_BEAST_329:SPLEEN +#CREATURE:FORGOTTEN_BEAST_329:KIDNEY +#CREATURE:FORGOTTEN_BEAST_330:MUSCLE + CREATURE:FORGOTTEN_BEAST_330:EYE +"CREATURE:FORGOTTEN_BEAST_330:BRAIN +!CREATURE:FORGOTTEN_BEAST_330:LUNG +"CREATURE:FORGOTTEN_BEAST_330:HEART +"CREATURE:FORGOTTEN_BEAST_330:LIVER + CREATURE:FORGOTTEN_BEAST_330:GUT +$CREATURE:FORGOTTEN_BEAST_330:STOMACH +$CREATURE:FORGOTTEN_BEAST_330:GIZZARD +%CREATURE:FORGOTTEN_BEAST_330:PANCREAS +#CREATURE:FORGOTTEN_BEAST_330:SPLEEN +#CREATURE:FORGOTTEN_BEAST_330:KIDNEY +#CREATURE:FORGOTTEN_BEAST_332:MUSCLE + CREATURE:FORGOTTEN_BEAST_332:EYE +"CREATURE:FORGOTTEN_BEAST_332:BRAIN +!CREATURE:FORGOTTEN_BEAST_332:LUNG +"CREATURE:FORGOTTEN_BEAST_332:HEART +"CREATURE:FORGOTTEN_BEAST_332:LIVER + CREATURE:FORGOTTEN_BEAST_332:GUT +$CREATURE:FORGOTTEN_BEAST_332:STOMACH +$CREATURE:FORGOTTEN_BEAST_332:GIZZARD +%CREATURE:FORGOTTEN_BEAST_332:PANCREAS +#CREATURE:FORGOTTEN_BEAST_332:SPLEEN +#CREATURE:FORGOTTEN_BEAST_332:KIDNEY +#CREATURE:FORGOTTEN_BEAST_333:MUSCLE + CREATURE:FORGOTTEN_BEAST_333:EYE +"CREATURE:FORGOTTEN_BEAST_333:BRAIN +!CREATURE:FORGOTTEN_BEAST_333:LUNG +"CREATURE:FORGOTTEN_BEAST_333:HEART +"CREATURE:FORGOTTEN_BEAST_333:LIVER + CREATURE:FORGOTTEN_BEAST_333:GUT +$CREATURE:FORGOTTEN_BEAST_333:STOMACH +$CREATURE:FORGOTTEN_BEAST_333:GIZZARD +%CREATURE:FORGOTTEN_BEAST_333:PANCREAS +#CREATURE:FORGOTTEN_BEAST_333:SPLEEN +#CREATURE:FORGOTTEN_BEAST_333:KIDNEY +#CREATURE:FORGOTTEN_BEAST_335:MUSCLE + CREATURE:FORGOTTEN_BEAST_335:EYE +"CREATURE:FORGOTTEN_BEAST_335:BRAIN +!CREATURE:FORGOTTEN_BEAST_335:LUNG +"CREATURE:FORGOTTEN_BEAST_335:HEART +"CREATURE:FORGOTTEN_BEAST_335:LIVER + CREATURE:FORGOTTEN_BEAST_335:GUT +$CREATURE:FORGOTTEN_BEAST_335:STOMACH +$CREATURE:FORGOTTEN_BEAST_335:GIZZARD +%CREATURE:FORGOTTEN_BEAST_335:PANCREAS +#CREATURE:FORGOTTEN_BEAST_335:SPLEEN +#CREATURE:FORGOTTEN_BEAST_335:KIDNEY +#CREATURE:FORGOTTEN_BEAST_336:MUSCLE + CREATURE:FORGOTTEN_BEAST_336:EYE +"CREATURE:FORGOTTEN_BEAST_336:BRAIN +!CREATURE:FORGOTTEN_BEAST_336:LUNG +"CREATURE:FORGOTTEN_BEAST_336:HEART +"CREATURE:FORGOTTEN_BEAST_336:LIVER + CREATURE:FORGOTTEN_BEAST_336:GUT +$CREATURE:FORGOTTEN_BEAST_336:STOMACH +$CREATURE:FORGOTTEN_BEAST_336:GIZZARD +%CREATURE:FORGOTTEN_BEAST_336:PANCREAS +#CREATURE:FORGOTTEN_BEAST_336:SPLEEN +#CREATURE:FORGOTTEN_BEAST_336:KIDNEY +#CREATURE:FORGOTTEN_BEAST_337:MUSCLE + CREATURE:FORGOTTEN_BEAST_337:EYE +"CREATURE:FORGOTTEN_BEAST_337:BRAIN +!CREATURE:FORGOTTEN_BEAST_337:LUNG +"CREATURE:FORGOTTEN_BEAST_337:HEART +"CREATURE:FORGOTTEN_BEAST_337:LIVER + CREATURE:FORGOTTEN_BEAST_337:GUT +$CREATURE:FORGOTTEN_BEAST_337:STOMACH +$CREATURE:FORGOTTEN_BEAST_337:GIZZARD +%CREATURE:FORGOTTEN_BEAST_337:PANCREAS +#CREATURE:FORGOTTEN_BEAST_337:SPLEEN +#CREATURE:FORGOTTEN_BEAST_337:KIDNEY +#CREATURE:FORGOTTEN_BEAST_338:MUSCLE + CREATURE:FORGOTTEN_BEAST_338:EYE +"CREATURE:FORGOTTEN_BEAST_338:BRAIN +!CREATURE:FORGOTTEN_BEAST_338:LUNG +"CREATURE:FORGOTTEN_BEAST_338:HEART +"CREATURE:FORGOTTEN_BEAST_338:LIVER + CREATURE:FORGOTTEN_BEAST_338:GUT +$CREATURE:FORGOTTEN_BEAST_338:STOMACH +$CREATURE:FORGOTTEN_BEAST_338:GIZZARD +%CREATURE:FORGOTTEN_BEAST_338:PANCREAS +#CREATURE:FORGOTTEN_BEAST_338:SPLEEN +#CREATURE:FORGOTTEN_BEAST_338:KIDNEY +#CREATURE:FORGOTTEN_BEAST_339:MUSCLE + CREATURE:FORGOTTEN_BEAST_339:EYE +"CREATURE:FORGOTTEN_BEAST_339:BRAIN +!CREATURE:FORGOTTEN_BEAST_339:LUNG +"CREATURE:FORGOTTEN_BEAST_339:HEART +"CREATURE:FORGOTTEN_BEAST_339:LIVER + CREATURE:FORGOTTEN_BEAST_339:GUT +$CREATURE:FORGOTTEN_BEAST_339:STOMACH +$CREATURE:FORGOTTEN_BEAST_339:GIZZARD +%CREATURE:FORGOTTEN_BEAST_339:PANCREAS +#CREATURE:FORGOTTEN_BEAST_339:SPLEEN +#CREATURE:FORGOTTEN_BEAST_339:KIDNEY +#CREATURE:FORGOTTEN_BEAST_341:MUSCLE + CREATURE:FORGOTTEN_BEAST_341:EYE +"CREATURE:FORGOTTEN_BEAST_341:BRAIN +!CREATURE:FORGOTTEN_BEAST_341:LUNG +"CREATURE:FORGOTTEN_BEAST_341:HEART +"CREATURE:FORGOTTEN_BEAST_341:LIVER + CREATURE:FORGOTTEN_BEAST_341:GUT +$CREATURE:FORGOTTEN_BEAST_341:STOMACH +$CREATURE:FORGOTTEN_BEAST_341:GIZZARD +%CREATURE:FORGOTTEN_BEAST_341:PANCREAS +#CREATURE:FORGOTTEN_BEAST_341:SPLEEN +#CREATURE:FORGOTTEN_BEAST_341:KIDNEY +#CREATURE:FORGOTTEN_BEAST_342:MUSCLE + CREATURE:FORGOTTEN_BEAST_342:EYE +"CREATURE:FORGOTTEN_BEAST_342:BRAIN +!CREATURE:FORGOTTEN_BEAST_342:LUNG +"CREATURE:FORGOTTEN_BEAST_342:HEART +"CREATURE:FORGOTTEN_BEAST_342:LIVER + CREATURE:FORGOTTEN_BEAST_342:GUT +$CREATURE:FORGOTTEN_BEAST_342:STOMACH +$CREATURE:FORGOTTEN_BEAST_342:GIZZARD +%CREATURE:FORGOTTEN_BEAST_342:PANCREAS +#CREATURE:FORGOTTEN_BEAST_342:SPLEEN +#CREATURE:FORGOTTEN_BEAST_342:KIDNEY +#CREATURE:FORGOTTEN_BEAST_343:MUSCLE + CREATURE:FORGOTTEN_BEAST_343:EYE +"CREATURE:FORGOTTEN_BEAST_343:BRAIN +!CREATURE:FORGOTTEN_BEAST_343:LUNG +"CREATURE:FORGOTTEN_BEAST_343:HEART +"CREATURE:FORGOTTEN_BEAST_343:LIVER + CREATURE:FORGOTTEN_BEAST_343:GUT +$CREATURE:FORGOTTEN_BEAST_343:STOMACH +$CREATURE:FORGOTTEN_BEAST_343:GIZZARD +%CREATURE:FORGOTTEN_BEAST_343:PANCREAS +#CREATURE:FORGOTTEN_BEAST_343:SPLEEN +#CREATURE:FORGOTTEN_BEAST_343:KIDNEY +#CREATURE:FORGOTTEN_BEAST_344:MUSCLE + CREATURE:FORGOTTEN_BEAST_344:EYE +"CREATURE:FORGOTTEN_BEAST_344:BRAIN +!CREATURE:FORGOTTEN_BEAST_344:LUNG +"CREATURE:FORGOTTEN_BEAST_344:HEART +"CREATURE:FORGOTTEN_BEAST_344:LIVER + CREATURE:FORGOTTEN_BEAST_344:GUT +$CREATURE:FORGOTTEN_BEAST_344:STOMACH +$CREATURE:FORGOTTEN_BEAST_344:GIZZARD +%CREATURE:FORGOTTEN_BEAST_344:PANCREAS +#CREATURE:FORGOTTEN_BEAST_344:SPLEEN +#CREATURE:FORGOTTEN_BEAST_344:KIDNEY +#CREATURE:FORGOTTEN_BEAST_347:MUSCLE + CREATURE:FORGOTTEN_BEAST_347:EYE +"CREATURE:FORGOTTEN_BEAST_347:BRAIN +!CREATURE:FORGOTTEN_BEAST_347:LUNG +"CREATURE:FORGOTTEN_BEAST_347:HEART +"CREATURE:FORGOTTEN_BEAST_347:LIVER + CREATURE:FORGOTTEN_BEAST_347:GUT +$CREATURE:FORGOTTEN_BEAST_347:STOMACH +$CREATURE:FORGOTTEN_BEAST_347:GIZZARD +%CREATURE:FORGOTTEN_BEAST_347:PANCREAS +#CREATURE:FORGOTTEN_BEAST_347:SPLEEN +#CREATURE:FORGOTTEN_BEAST_347:KIDNEY +#CREATURE:FORGOTTEN_BEAST_348:MUSCLE + CREATURE:FORGOTTEN_BEAST_348:EYE +"CREATURE:FORGOTTEN_BEAST_348:BRAIN +!CREATURE:FORGOTTEN_BEAST_348:LUNG +"CREATURE:FORGOTTEN_BEAST_348:HEART +"CREATURE:FORGOTTEN_BEAST_348:LIVER + CREATURE:FORGOTTEN_BEAST_348:GUT +$CREATURE:FORGOTTEN_BEAST_348:STOMACH +$CREATURE:FORGOTTEN_BEAST_348:GIZZARD +%CREATURE:FORGOTTEN_BEAST_348:PANCREAS +#CREATURE:FORGOTTEN_BEAST_348:SPLEEN +#CREATURE:FORGOTTEN_BEAST_348:KIDNEY +#CREATURE:FORGOTTEN_BEAST_349:MUSCLE + CREATURE:FORGOTTEN_BEAST_349:EYE +"CREATURE:FORGOTTEN_BEAST_349:BRAIN +!CREATURE:FORGOTTEN_BEAST_349:LUNG +"CREATURE:FORGOTTEN_BEAST_349:HEART +"CREATURE:FORGOTTEN_BEAST_349:LIVER + CREATURE:FORGOTTEN_BEAST_349:GUT +$CREATURE:FORGOTTEN_BEAST_349:STOMACH +$CREATURE:FORGOTTEN_BEAST_349:GIZZARD +%CREATURE:FORGOTTEN_BEAST_349:PANCREAS +#CREATURE:FORGOTTEN_BEAST_349:SPLEEN +#CREATURE:FORGOTTEN_BEAST_349:KIDNEY +#CREATURE:FORGOTTEN_BEAST_351:MUSCLE + CREATURE:FORGOTTEN_BEAST_351:EYE +"CREATURE:FORGOTTEN_BEAST_351:BRAIN +!CREATURE:FORGOTTEN_BEAST_351:LUNG +"CREATURE:FORGOTTEN_BEAST_351:HEART +"CREATURE:FORGOTTEN_BEAST_351:LIVER + CREATURE:FORGOTTEN_BEAST_351:GUT +$CREATURE:FORGOTTEN_BEAST_351:STOMACH +$CREATURE:FORGOTTEN_BEAST_351:GIZZARD +%CREATURE:FORGOTTEN_BEAST_351:PANCREAS +#CREATURE:FORGOTTEN_BEAST_351:SPLEEN +#CREATURE:FORGOTTEN_BEAST_351:KIDNEY +#CREATURE:FORGOTTEN_BEAST_352:MUSCLE + CREATURE:FORGOTTEN_BEAST_352:EYE +"CREATURE:FORGOTTEN_BEAST_352:BRAIN +!CREATURE:FORGOTTEN_BEAST_352:LUNG +"CREATURE:FORGOTTEN_BEAST_352:HEART +"CREATURE:FORGOTTEN_BEAST_352:LIVER + CREATURE:FORGOTTEN_BEAST_352:GUT +$CREATURE:FORGOTTEN_BEAST_352:STOMACH +$CREATURE:FORGOTTEN_BEAST_352:GIZZARD +%CREATURE:FORGOTTEN_BEAST_352:PANCREAS +#CREATURE:FORGOTTEN_BEAST_352:SPLEEN +#CREATURE:FORGOTTEN_BEAST_352:KIDNEY +#CREATURE:FORGOTTEN_BEAST_353:MUSCLE + CREATURE:FORGOTTEN_BEAST_353:EYE +"CREATURE:FORGOTTEN_BEAST_353:BRAIN +!CREATURE:FORGOTTEN_BEAST_353:LUNG +"CREATURE:FORGOTTEN_BEAST_353:HEART +"CREATURE:FORGOTTEN_BEAST_353:LIVER + CREATURE:FORGOTTEN_BEAST_353:GUT +$CREATURE:FORGOTTEN_BEAST_353:STOMACH +$CREATURE:FORGOTTEN_BEAST_353:GIZZARD +%CREATURE:FORGOTTEN_BEAST_353:PANCREAS +#CREATURE:FORGOTTEN_BEAST_353:SPLEEN +#CREATURE:FORGOTTEN_BEAST_353:KIDNEY +#CREATURE:FORGOTTEN_BEAST_354:MUSCLE + CREATURE:FORGOTTEN_BEAST_354:EYE +"CREATURE:FORGOTTEN_BEAST_354:BRAIN +!CREATURE:FORGOTTEN_BEAST_354:LUNG +"CREATURE:FORGOTTEN_BEAST_354:HEART +"CREATURE:FORGOTTEN_BEAST_354:LIVER + CREATURE:FORGOTTEN_BEAST_354:GUT +$CREATURE:FORGOTTEN_BEAST_354:STOMACH +$CREATURE:FORGOTTEN_BEAST_354:GIZZARD +%CREATURE:FORGOTTEN_BEAST_354:PANCREAS +#CREATURE:FORGOTTEN_BEAST_354:SPLEEN +#CREATURE:FORGOTTEN_BEAST_354:KIDNEY +#CREATURE:FORGOTTEN_BEAST_355:MUSCLE + CREATURE:FORGOTTEN_BEAST_355:EYE +"CREATURE:FORGOTTEN_BEAST_355:BRAIN +!CREATURE:FORGOTTEN_BEAST_355:LUNG +"CREATURE:FORGOTTEN_BEAST_355:HEART +"CREATURE:FORGOTTEN_BEAST_355:LIVER + CREATURE:FORGOTTEN_BEAST_355:GUT +$CREATURE:FORGOTTEN_BEAST_355:STOMACH +$CREATURE:FORGOTTEN_BEAST_355:GIZZARD +%CREATURE:FORGOTTEN_BEAST_355:PANCREAS +#CREATURE:FORGOTTEN_BEAST_355:SPLEEN +#CREATURE:FORGOTTEN_BEAST_355:KIDNEY +#CREATURE:FORGOTTEN_BEAST_356:MUSCLE + CREATURE:FORGOTTEN_BEAST_356:EYE +"CREATURE:FORGOTTEN_BEAST_356:BRAIN +!CREATURE:FORGOTTEN_BEAST_356:LUNG +"CREATURE:FORGOTTEN_BEAST_356:HEART +"CREATURE:FORGOTTEN_BEAST_356:LIVER + CREATURE:FORGOTTEN_BEAST_356:GUT +$CREATURE:FORGOTTEN_BEAST_356:STOMACH +$CREATURE:FORGOTTEN_BEAST_356:GIZZARD +%CREATURE:FORGOTTEN_BEAST_356:PANCREAS +#CREATURE:FORGOTTEN_BEAST_356:SPLEEN +#CREATURE:FORGOTTEN_BEAST_356:KIDNEY +#CREATURE:FORGOTTEN_BEAST_357:MUSCLE + CREATURE:FORGOTTEN_BEAST_357:EYE +"CREATURE:FORGOTTEN_BEAST_357:BRAIN +!CREATURE:FORGOTTEN_BEAST_357:LUNG +"CREATURE:FORGOTTEN_BEAST_357:HEART +"CREATURE:FORGOTTEN_BEAST_357:LIVER + CREATURE:FORGOTTEN_BEAST_357:GUT +$CREATURE:FORGOTTEN_BEAST_357:STOMACH +$CREATURE:FORGOTTEN_BEAST_357:GIZZARD +%CREATURE:FORGOTTEN_BEAST_357:PANCREAS +#CREATURE:FORGOTTEN_BEAST_357:SPLEEN +#CREATURE:FORGOTTEN_BEAST_357:KIDNEY +#CREATURE:FORGOTTEN_BEAST_358:MUSCLE + CREATURE:FORGOTTEN_BEAST_358:EYE +"CREATURE:FORGOTTEN_BEAST_358:BRAIN +!CREATURE:FORGOTTEN_BEAST_358:LUNG +"CREATURE:FORGOTTEN_BEAST_358:HEART +"CREATURE:FORGOTTEN_BEAST_358:LIVER + CREATURE:FORGOTTEN_BEAST_358:GUT +$CREATURE:FORGOTTEN_BEAST_358:STOMACH +$CREATURE:FORGOTTEN_BEAST_358:GIZZARD +%CREATURE:FORGOTTEN_BEAST_358:PANCREAS +#CREATURE:FORGOTTEN_BEAST_358:SPLEEN +#CREATURE:FORGOTTEN_BEAST_358:KIDNEY +#CREATURE:FORGOTTEN_BEAST_359:MUSCLE + CREATURE:FORGOTTEN_BEAST_359:EYE +"CREATURE:FORGOTTEN_BEAST_359:BRAIN +!CREATURE:FORGOTTEN_BEAST_359:LUNG +"CREATURE:FORGOTTEN_BEAST_359:HEART +"CREATURE:FORGOTTEN_BEAST_359:LIVER + CREATURE:FORGOTTEN_BEAST_359:GUT +$CREATURE:FORGOTTEN_BEAST_359:STOMACH +$CREATURE:FORGOTTEN_BEAST_359:GIZZARD +%CREATURE:FORGOTTEN_BEAST_359:PANCREAS +#CREATURE:FORGOTTEN_BEAST_359:SPLEEN +#CREATURE:FORGOTTEN_BEAST_359:KIDNEY +#CREATURE:FORGOTTEN_BEAST_361:MUSCLE + CREATURE:FORGOTTEN_BEAST_361:EYE +"CREATURE:FORGOTTEN_BEAST_361:BRAIN +!CREATURE:FORGOTTEN_BEAST_361:LUNG +"CREATURE:FORGOTTEN_BEAST_361:HEART +"CREATURE:FORGOTTEN_BEAST_361:LIVER + CREATURE:FORGOTTEN_BEAST_361:GUT +$CREATURE:FORGOTTEN_BEAST_361:STOMACH +$CREATURE:FORGOTTEN_BEAST_361:GIZZARD +%CREATURE:FORGOTTEN_BEAST_361:PANCREAS +#CREATURE:FORGOTTEN_BEAST_361:SPLEEN +#CREATURE:FORGOTTEN_BEAST_361:KIDNEY +#CREATURE:FORGOTTEN_BEAST_363:MUSCLE + CREATURE:FORGOTTEN_BEAST_363:EYE +"CREATURE:FORGOTTEN_BEAST_363:BRAIN +!CREATURE:FORGOTTEN_BEAST_363:LUNG +"CREATURE:FORGOTTEN_BEAST_363:HEART +"CREATURE:FORGOTTEN_BEAST_363:LIVER + CREATURE:FORGOTTEN_BEAST_363:GUT +$CREATURE:FORGOTTEN_BEAST_363:STOMACH +$CREATURE:FORGOTTEN_BEAST_363:GIZZARD +%CREATURE:FORGOTTEN_BEAST_363:PANCREAS +#CREATURE:FORGOTTEN_BEAST_363:SPLEEN +#CREATURE:FORGOTTEN_BEAST_363:KIDNEY +#CREATURE:FORGOTTEN_BEAST_364:MUSCLE + CREATURE:FORGOTTEN_BEAST_364:EYE +"CREATURE:FORGOTTEN_BEAST_364:BRAIN +!CREATURE:FORGOTTEN_BEAST_364:LUNG +"CREATURE:FORGOTTEN_BEAST_364:HEART +"CREATURE:FORGOTTEN_BEAST_364:LIVER + CREATURE:FORGOTTEN_BEAST_364:GUT +$CREATURE:FORGOTTEN_BEAST_364:STOMACH +$CREATURE:FORGOTTEN_BEAST_364:GIZZARD +%CREATURE:FORGOTTEN_BEAST_364:PANCREAS +#CREATURE:FORGOTTEN_BEAST_364:SPLEEN +#CREATURE:FORGOTTEN_BEAST_364:KIDNEY +#CREATURE:FORGOTTEN_BEAST_365:MUSCLE + CREATURE:FORGOTTEN_BEAST_365:EYE +"CREATURE:FORGOTTEN_BEAST_365:BRAIN +!CREATURE:FORGOTTEN_BEAST_365:LUNG +"CREATURE:FORGOTTEN_BEAST_365:HEART +"CREATURE:FORGOTTEN_BEAST_365:LIVER + CREATURE:FORGOTTEN_BEAST_365:GUT +$CREATURE:FORGOTTEN_BEAST_365:STOMACH +$CREATURE:FORGOTTEN_BEAST_365:GIZZARD +%CREATURE:FORGOTTEN_BEAST_365:PANCREAS +#CREATURE:FORGOTTEN_BEAST_365:SPLEEN +#CREATURE:FORGOTTEN_BEAST_365:KIDNEY +#CREATURE:FORGOTTEN_BEAST_366:MUSCLE + CREATURE:FORGOTTEN_BEAST_366:EYE +"CREATURE:FORGOTTEN_BEAST_366:BRAIN +!CREATURE:FORGOTTEN_BEAST_366:LUNG +"CREATURE:FORGOTTEN_BEAST_366:HEART +"CREATURE:FORGOTTEN_BEAST_366:LIVER + CREATURE:FORGOTTEN_BEAST_366:GUT +$CREATURE:FORGOTTEN_BEAST_366:STOMACH +$CREATURE:FORGOTTEN_BEAST_366:GIZZARD +%CREATURE:FORGOTTEN_BEAST_366:PANCREAS +#CREATURE:FORGOTTEN_BEAST_366:SPLEEN +#CREATURE:FORGOTTEN_BEAST_366:KIDNEY +#CREATURE:FORGOTTEN_BEAST_367:MUSCLE + CREATURE:FORGOTTEN_BEAST_367:EYE +"CREATURE:FORGOTTEN_BEAST_367:BRAIN +!CREATURE:FORGOTTEN_BEAST_367:LUNG +"CREATURE:FORGOTTEN_BEAST_367:HEART +"CREATURE:FORGOTTEN_BEAST_367:LIVER + CREATURE:FORGOTTEN_BEAST_367:GUT +$CREATURE:FORGOTTEN_BEAST_367:STOMACH +$CREATURE:FORGOTTEN_BEAST_367:GIZZARD +%CREATURE:FORGOTTEN_BEAST_367:PANCREAS +#CREATURE:FORGOTTEN_BEAST_367:SPLEEN +#CREATURE:FORGOTTEN_BEAST_367:KIDNEY +#CREATURE:FORGOTTEN_BEAST_368:MUSCLE + CREATURE:FORGOTTEN_BEAST_368:EYE +"CREATURE:FORGOTTEN_BEAST_368:BRAIN +!CREATURE:FORGOTTEN_BEAST_368:LUNG +"CREATURE:FORGOTTEN_BEAST_368:HEART +"CREATURE:FORGOTTEN_BEAST_368:LIVER + CREATURE:FORGOTTEN_BEAST_368:GUT +$CREATURE:FORGOTTEN_BEAST_368:STOMACH +$CREATURE:FORGOTTEN_BEAST_368:GIZZARD +%CREATURE:FORGOTTEN_BEAST_368:PANCREAS +#CREATURE:FORGOTTEN_BEAST_368:SPLEEN +#CREATURE:FORGOTTEN_BEAST_368:KIDNEY +#CREATURE:FORGOTTEN_BEAST_370:MUSCLE + CREATURE:FORGOTTEN_BEAST_370:EYE +"CREATURE:FORGOTTEN_BEAST_370:BRAIN +!CREATURE:FORGOTTEN_BEAST_370:LUNG +"CREATURE:FORGOTTEN_BEAST_370:HEART +"CREATURE:FORGOTTEN_BEAST_370:LIVER + CREATURE:FORGOTTEN_BEAST_370:GUT +$CREATURE:FORGOTTEN_BEAST_370:STOMACH +$CREATURE:FORGOTTEN_BEAST_370:GIZZARD +%CREATURE:FORGOTTEN_BEAST_370:PANCREAS +#CREATURE:FORGOTTEN_BEAST_370:SPLEEN +#CREATURE:FORGOTTEN_BEAST_370:KIDNEY +#CREATURE:FORGOTTEN_BEAST_372:MUSCLE + CREATURE:FORGOTTEN_BEAST_372:EYE +"CREATURE:FORGOTTEN_BEAST_372:BRAIN +!CREATURE:FORGOTTEN_BEAST_372:LUNG +"CREATURE:FORGOTTEN_BEAST_372:HEART +"CREATURE:FORGOTTEN_BEAST_372:LIVER + CREATURE:FORGOTTEN_BEAST_372:GUT +$CREATURE:FORGOTTEN_BEAST_372:STOMACH +$CREATURE:FORGOTTEN_BEAST_372:GIZZARD +%CREATURE:FORGOTTEN_BEAST_372:PANCREAS +#CREATURE:FORGOTTEN_BEAST_372:SPLEEN +#CREATURE:FORGOTTEN_BEAST_372:KIDNEY +#CREATURE:FORGOTTEN_BEAST_374:MUSCLE + CREATURE:FORGOTTEN_BEAST_374:EYE +"CREATURE:FORGOTTEN_BEAST_374:BRAIN +!CREATURE:FORGOTTEN_BEAST_374:LUNG +"CREATURE:FORGOTTEN_BEAST_374:HEART +"CREATURE:FORGOTTEN_BEAST_374:LIVER + CREATURE:FORGOTTEN_BEAST_374:GUT +$CREATURE:FORGOTTEN_BEAST_374:STOMACH +$CREATURE:FORGOTTEN_BEAST_374:GIZZARD +%CREATURE:FORGOTTEN_BEAST_374:PANCREAS +#CREATURE:FORGOTTEN_BEAST_374:SPLEEN +#CREATURE:FORGOTTEN_BEAST_374:KIDNEY +#CREATURE:FORGOTTEN_BEAST_375:MUSCLE + CREATURE:FORGOTTEN_BEAST_375:EYE +"CREATURE:FORGOTTEN_BEAST_375:BRAIN +!CREATURE:FORGOTTEN_BEAST_375:LUNG +"CREATURE:FORGOTTEN_BEAST_375:HEART +"CREATURE:FORGOTTEN_BEAST_375:LIVER + CREATURE:FORGOTTEN_BEAST_375:GUT +$CREATURE:FORGOTTEN_BEAST_375:STOMACH +$CREATURE:FORGOTTEN_BEAST_375:GIZZARD +%CREATURE:FORGOTTEN_BEAST_375:PANCREAS +#CREATURE:FORGOTTEN_BEAST_375:SPLEEN +#CREATURE:FORGOTTEN_BEAST_375:KIDNEY +#CREATURE:FORGOTTEN_BEAST_377:MUSCLE + CREATURE:FORGOTTEN_BEAST_377:EYE +"CREATURE:FORGOTTEN_BEAST_377:BRAIN +!CREATURE:FORGOTTEN_BEAST_377:LUNG +"CREATURE:FORGOTTEN_BEAST_377:HEART +"CREATURE:FORGOTTEN_BEAST_377:LIVER + CREATURE:FORGOTTEN_BEAST_377:GUT +$CREATURE:FORGOTTEN_BEAST_377:STOMACH +$CREATURE:FORGOTTEN_BEAST_377:GIZZARD +%CREATURE:FORGOTTEN_BEAST_377:PANCREAS +#CREATURE:FORGOTTEN_BEAST_377:SPLEEN +#CREATURE:FORGOTTEN_BEAST_377:KIDNEY +#CREATURE:FORGOTTEN_BEAST_378:MUSCLE + CREATURE:FORGOTTEN_BEAST_378:EYE +"CREATURE:FORGOTTEN_BEAST_378:BRAIN +!CREATURE:FORGOTTEN_BEAST_378:LUNG +"CREATURE:FORGOTTEN_BEAST_378:HEART +"CREATURE:FORGOTTEN_BEAST_378:LIVER + CREATURE:FORGOTTEN_BEAST_378:GUT +$CREATURE:FORGOTTEN_BEAST_378:STOMACH +$CREATURE:FORGOTTEN_BEAST_378:GIZZARD +%CREATURE:FORGOTTEN_BEAST_378:PANCREAS +#CREATURE:FORGOTTEN_BEAST_378:SPLEEN +#CREATURE:FORGOTTEN_BEAST_378:KIDNEY +#CREATURE:FORGOTTEN_BEAST_379:MUSCLE + CREATURE:FORGOTTEN_BEAST_379:EYE +"CREATURE:FORGOTTEN_BEAST_379:BRAIN +!CREATURE:FORGOTTEN_BEAST_379:LUNG +"CREATURE:FORGOTTEN_BEAST_379:HEART +"CREATURE:FORGOTTEN_BEAST_379:LIVER + CREATURE:FORGOTTEN_BEAST_379:GUT +$CREATURE:FORGOTTEN_BEAST_379:STOMACH +$CREATURE:FORGOTTEN_BEAST_379:GIZZARD +%CREATURE:FORGOTTEN_BEAST_379:PANCREAS +#CREATURE:FORGOTTEN_BEAST_379:SPLEEN +#CREATURE:FORGOTTEN_BEAST_379:KIDNEY +#CREATURE:FORGOTTEN_BEAST_380:MUSCLE + CREATURE:FORGOTTEN_BEAST_380:EYE +"CREATURE:FORGOTTEN_BEAST_380:BRAIN +!CREATURE:FORGOTTEN_BEAST_380:LUNG +"CREATURE:FORGOTTEN_BEAST_380:HEART +"CREATURE:FORGOTTEN_BEAST_380:LIVER + CREATURE:FORGOTTEN_BEAST_380:GUT +$CREATURE:FORGOTTEN_BEAST_380:STOMACH +$CREATURE:FORGOTTEN_BEAST_380:GIZZARD +%CREATURE:FORGOTTEN_BEAST_380:PANCREAS +#CREATURE:FORGOTTEN_BEAST_380:SPLEEN +#CREATURE:FORGOTTEN_BEAST_380:KIDNEY +#CREATURE:FORGOTTEN_BEAST_381:MUSCLE + CREATURE:FORGOTTEN_BEAST_381:EYE +"CREATURE:FORGOTTEN_BEAST_381:BRAIN +!CREATURE:FORGOTTEN_BEAST_381:LUNG +"CREATURE:FORGOTTEN_BEAST_381:HEART +"CREATURE:FORGOTTEN_BEAST_381:LIVER + CREATURE:FORGOTTEN_BEAST_381:GUT +$CREATURE:FORGOTTEN_BEAST_381:STOMACH +$CREATURE:FORGOTTEN_BEAST_381:GIZZARD +%CREATURE:FORGOTTEN_BEAST_381:PANCREAS +#CREATURE:FORGOTTEN_BEAST_381:SPLEEN +#CREATURE:FORGOTTEN_BEAST_381:KIDNEY +#CREATURE:FORGOTTEN_BEAST_382:MUSCLE + CREATURE:FORGOTTEN_BEAST_382:EYE +"CREATURE:FORGOTTEN_BEAST_382:BRAIN +!CREATURE:FORGOTTEN_BEAST_382:LUNG +"CREATURE:FORGOTTEN_BEAST_382:HEART +"CREATURE:FORGOTTEN_BEAST_382:LIVER + CREATURE:FORGOTTEN_BEAST_382:GUT +$CREATURE:FORGOTTEN_BEAST_382:STOMACH +$CREATURE:FORGOTTEN_BEAST_382:GIZZARD +%CREATURE:FORGOTTEN_BEAST_382:PANCREAS +#CREATURE:FORGOTTEN_BEAST_382:SPLEEN +#CREATURE:FORGOTTEN_BEAST_382:KIDNEY +#CREATURE:FORGOTTEN_BEAST_383:MUSCLE + CREATURE:FORGOTTEN_BEAST_383:EYE +"CREATURE:FORGOTTEN_BEAST_383:BRAIN +!CREATURE:FORGOTTEN_BEAST_383:LUNG +"CREATURE:FORGOTTEN_BEAST_383:HEART +"CREATURE:FORGOTTEN_BEAST_383:LIVER + CREATURE:FORGOTTEN_BEAST_383:GUT +$CREATURE:FORGOTTEN_BEAST_383:STOMACH +$CREATURE:FORGOTTEN_BEAST_383:GIZZARD +%CREATURE:FORGOTTEN_BEAST_383:PANCREAS +#CREATURE:FORGOTTEN_BEAST_383:SPLEEN +#CREATURE:FORGOTTEN_BEAST_383:KIDNEY +#CREATURE:FORGOTTEN_BEAST_384:MUSCLE + CREATURE:FORGOTTEN_BEAST_384:EYE +"CREATURE:FORGOTTEN_BEAST_384:BRAIN +!CREATURE:FORGOTTEN_BEAST_384:LUNG +"CREATURE:FORGOTTEN_BEAST_384:HEART +"CREATURE:FORGOTTEN_BEAST_384:LIVER + CREATURE:FORGOTTEN_BEAST_384:GUT +$CREATURE:FORGOTTEN_BEAST_384:STOMACH +$CREATURE:FORGOTTEN_BEAST_384:GIZZARD +%CREATURE:FORGOTTEN_BEAST_384:PANCREAS +#CREATURE:FORGOTTEN_BEAST_384:SPLEEN +#CREATURE:FORGOTTEN_BEAST_384:KIDNEY +#CREATURE:FORGOTTEN_BEAST_385:MUSCLE + CREATURE:FORGOTTEN_BEAST_385:EYE +"CREATURE:FORGOTTEN_BEAST_385:BRAIN +!CREATURE:FORGOTTEN_BEAST_385:LUNG +"CREATURE:FORGOTTEN_BEAST_385:HEART +"CREATURE:FORGOTTEN_BEAST_385:LIVER + CREATURE:FORGOTTEN_BEAST_385:GUT +$CREATURE:FORGOTTEN_BEAST_385:STOMACH +$CREATURE:FORGOTTEN_BEAST_385:GIZZARD +%CREATURE:FORGOTTEN_BEAST_385:PANCREAS +#CREATURE:FORGOTTEN_BEAST_385:SPLEEN +#CREATURE:FORGOTTEN_BEAST_385:KIDNEY +#CREATURE:FORGOTTEN_BEAST_386:MUSCLE + CREATURE:FORGOTTEN_BEAST_386:EYE +"CREATURE:FORGOTTEN_BEAST_386:BRAIN +!CREATURE:FORGOTTEN_BEAST_386:LUNG +"CREATURE:FORGOTTEN_BEAST_386:HEART +"CREATURE:FORGOTTEN_BEAST_386:LIVER + CREATURE:FORGOTTEN_BEAST_386:GUT +$CREATURE:FORGOTTEN_BEAST_386:STOMACH +$CREATURE:FORGOTTEN_BEAST_386:GIZZARD +%CREATURE:FORGOTTEN_BEAST_386:PANCREAS +#CREATURE:FORGOTTEN_BEAST_386:SPLEEN +#CREATURE:FORGOTTEN_BEAST_386:KIDNEY +#CREATURE:FORGOTTEN_BEAST_387:MUSCLE + CREATURE:FORGOTTEN_BEAST_387:EYE +"CREATURE:FORGOTTEN_BEAST_387:BRAIN +!CREATURE:FORGOTTEN_BEAST_387:LUNG +"CREATURE:FORGOTTEN_BEAST_387:HEART +"CREATURE:FORGOTTEN_BEAST_387:LIVER + CREATURE:FORGOTTEN_BEAST_387:GUT +$CREATURE:FORGOTTEN_BEAST_387:STOMACH +$CREATURE:FORGOTTEN_BEAST_387:GIZZARD +%CREATURE:FORGOTTEN_BEAST_387:PANCREAS +#CREATURE:FORGOTTEN_BEAST_387:SPLEEN +#CREATURE:FORGOTTEN_BEAST_387:KIDNEY +#CREATURE:FORGOTTEN_BEAST_388:MUSCLE + CREATURE:FORGOTTEN_BEAST_388:EYE +"CREATURE:FORGOTTEN_BEAST_388:BRAIN +!CREATURE:FORGOTTEN_BEAST_388:LUNG +"CREATURE:FORGOTTEN_BEAST_388:HEART +"CREATURE:FORGOTTEN_BEAST_388:LIVER + CREATURE:FORGOTTEN_BEAST_388:GUT +$CREATURE:FORGOTTEN_BEAST_388:STOMACH +$CREATURE:FORGOTTEN_BEAST_388:GIZZARD +%CREATURE:FORGOTTEN_BEAST_388:PANCREAS +#CREATURE:FORGOTTEN_BEAST_388:SPLEEN +#CREATURE:FORGOTTEN_BEAST_388:KIDNEY +#CREATURE:FORGOTTEN_BEAST_389:MUSCLE + CREATURE:FORGOTTEN_BEAST_389:EYE +"CREATURE:FORGOTTEN_BEAST_389:BRAIN +!CREATURE:FORGOTTEN_BEAST_389:LUNG +"CREATURE:FORGOTTEN_BEAST_389:HEART +"CREATURE:FORGOTTEN_BEAST_389:LIVER + CREATURE:FORGOTTEN_BEAST_389:GUT +$CREATURE:FORGOTTEN_BEAST_389:STOMACH +$CREATURE:FORGOTTEN_BEAST_389:GIZZARD +%CREATURE:FORGOTTEN_BEAST_389:PANCREAS +#CREATURE:FORGOTTEN_BEAST_389:SPLEEN +#CREATURE:FORGOTTEN_BEAST_389:KIDNEY +#CREATURE:FORGOTTEN_BEAST_390:MUSCLE + CREATURE:FORGOTTEN_BEAST_390:EYE +"CREATURE:FORGOTTEN_BEAST_390:BRAIN +!CREATURE:FORGOTTEN_BEAST_390:LUNG +"CREATURE:FORGOTTEN_BEAST_390:HEART +"CREATURE:FORGOTTEN_BEAST_390:LIVER + CREATURE:FORGOTTEN_BEAST_390:GUT +$CREATURE:FORGOTTEN_BEAST_390:STOMACH +$CREATURE:FORGOTTEN_BEAST_390:GIZZARD +%CREATURE:FORGOTTEN_BEAST_390:PANCREAS +#CREATURE:FORGOTTEN_BEAST_390:SPLEEN +#CREATURE:FORGOTTEN_BEAST_390:KIDNEY +#CREATURE:FORGOTTEN_BEAST_391:MUSCLE + CREATURE:FORGOTTEN_BEAST_391:EYE +"CREATURE:FORGOTTEN_BEAST_391:BRAIN +!CREATURE:FORGOTTEN_BEAST_391:LUNG +"CREATURE:FORGOTTEN_BEAST_391:HEART +"CREATURE:FORGOTTEN_BEAST_391:LIVER + CREATURE:FORGOTTEN_BEAST_391:GUT +$CREATURE:FORGOTTEN_BEAST_391:STOMACH +$CREATURE:FORGOTTEN_BEAST_391:GIZZARD +%CREATURE:FORGOTTEN_BEAST_391:PANCREAS +#CREATURE:FORGOTTEN_BEAST_391:SPLEEN +#CREATURE:FORGOTTEN_BEAST_391:KIDNEY +#CREATURE:FORGOTTEN_BEAST_392:MUSCLE + CREATURE:FORGOTTEN_BEAST_392:EYE +"CREATURE:FORGOTTEN_BEAST_392:BRAIN +!CREATURE:FORGOTTEN_BEAST_392:LUNG +"CREATURE:FORGOTTEN_BEAST_392:HEART +"CREATURE:FORGOTTEN_BEAST_392:LIVER + CREATURE:FORGOTTEN_BEAST_392:GUT +$CREATURE:FORGOTTEN_BEAST_392:STOMACH +$CREATURE:FORGOTTEN_BEAST_392:GIZZARD +%CREATURE:FORGOTTEN_BEAST_392:PANCREAS +#CREATURE:FORGOTTEN_BEAST_392:SPLEEN +#CREATURE:FORGOTTEN_BEAST_392:KIDNEY +#CREATURE:FORGOTTEN_BEAST_393:MUSCLE + CREATURE:FORGOTTEN_BEAST_393:EYE +"CREATURE:FORGOTTEN_BEAST_393:BRAIN +!CREATURE:FORGOTTEN_BEAST_393:LUNG +"CREATURE:FORGOTTEN_BEAST_393:HEART +"CREATURE:FORGOTTEN_BEAST_393:LIVER + CREATURE:FORGOTTEN_BEAST_393:GUT +$CREATURE:FORGOTTEN_BEAST_393:STOMACH +$CREATURE:FORGOTTEN_BEAST_393:GIZZARD +%CREATURE:FORGOTTEN_BEAST_393:PANCREAS +#CREATURE:FORGOTTEN_BEAST_393:SPLEEN +#CREATURE:FORGOTTEN_BEAST_393:KIDNEY +#CREATURE:FORGOTTEN_BEAST_396:MUSCLE + CREATURE:FORGOTTEN_BEAST_396:EYE +"CREATURE:FORGOTTEN_BEAST_396:BRAIN +!CREATURE:FORGOTTEN_BEAST_396:LUNG +"CREATURE:FORGOTTEN_BEAST_396:HEART +"CREATURE:FORGOTTEN_BEAST_396:LIVER + CREATURE:FORGOTTEN_BEAST_396:GUT +$CREATURE:FORGOTTEN_BEAST_396:STOMACH +$CREATURE:FORGOTTEN_BEAST_396:GIZZARD +%CREATURE:FORGOTTEN_BEAST_396:PANCREAS +#CREATURE:FORGOTTEN_BEAST_396:SPLEEN +#CREATURE:FORGOTTEN_BEAST_396:KIDNEY +#CREATURE:FORGOTTEN_BEAST_397:MUSCLE + CREATURE:FORGOTTEN_BEAST_397:EYE +"CREATURE:FORGOTTEN_BEAST_397:BRAIN +!CREATURE:FORGOTTEN_BEAST_397:LUNG +"CREATURE:FORGOTTEN_BEAST_397:HEART +"CREATURE:FORGOTTEN_BEAST_397:LIVER + CREATURE:FORGOTTEN_BEAST_397:GUT +$CREATURE:FORGOTTEN_BEAST_397:STOMACH +$CREATURE:FORGOTTEN_BEAST_397:GIZZARD +%CREATURE:FORGOTTEN_BEAST_397:PANCREAS +#CREATURE:FORGOTTEN_BEAST_397:SPLEEN +#CREATURE:FORGOTTEN_BEAST_397:KIDNEY +#CREATURE:FORGOTTEN_BEAST_398:MUSCLE + CREATURE:FORGOTTEN_BEAST_398:EYE +"CREATURE:FORGOTTEN_BEAST_398:BRAIN +!CREATURE:FORGOTTEN_BEAST_398:LUNG +"CREATURE:FORGOTTEN_BEAST_398:HEART +"CREATURE:FORGOTTEN_BEAST_398:LIVER + CREATURE:FORGOTTEN_BEAST_398:GUT +$CREATURE:FORGOTTEN_BEAST_398:STOMACH +$CREATURE:FORGOTTEN_BEAST_398:GIZZARD +%CREATURE:FORGOTTEN_BEAST_398:PANCREAS +#CREATURE:FORGOTTEN_BEAST_398:SPLEEN +#CREATURE:FORGOTTEN_BEAST_398:KIDNEY +#CREATURE:FORGOTTEN_BEAST_400:MUSCLE + CREATURE:FORGOTTEN_BEAST_400:EYE +"CREATURE:FORGOTTEN_BEAST_400:BRAIN +!CREATURE:FORGOTTEN_BEAST_400:LUNG +"CREATURE:FORGOTTEN_BEAST_400:HEART +"CREATURE:FORGOTTEN_BEAST_400:LIVER + CREATURE:FORGOTTEN_BEAST_400:GUT +$CREATURE:FORGOTTEN_BEAST_400:STOMACH +$CREATURE:FORGOTTEN_BEAST_400:GIZZARD +%CREATURE:FORGOTTEN_BEAST_400:PANCREAS +#CREATURE:FORGOTTEN_BEAST_400:SPLEEN +#CREATURE:FORGOTTEN_BEAST_400:KIDNEY +#CREATURE:FORGOTTEN_BEAST_403:MUSCLE + CREATURE:FORGOTTEN_BEAST_403:EYE +"CREATURE:FORGOTTEN_BEAST_403:BRAIN +!CREATURE:FORGOTTEN_BEAST_403:LUNG +"CREATURE:FORGOTTEN_BEAST_403:HEART +"CREATURE:FORGOTTEN_BEAST_403:LIVER + CREATURE:FORGOTTEN_BEAST_403:GUT +$CREATURE:FORGOTTEN_BEAST_403:STOMACH +$CREATURE:FORGOTTEN_BEAST_403:GIZZARD +%CREATURE:FORGOTTEN_BEAST_403:PANCREAS +#CREATURE:FORGOTTEN_BEAST_403:SPLEEN +#CREATURE:FORGOTTEN_BEAST_403:KIDNEY +#CREATURE:FORGOTTEN_BEAST_404:MUSCLE + CREATURE:FORGOTTEN_BEAST_404:EYE +"CREATURE:FORGOTTEN_BEAST_404:BRAIN +!CREATURE:FORGOTTEN_BEAST_404:LUNG +"CREATURE:FORGOTTEN_BEAST_404:HEART +"CREATURE:FORGOTTEN_BEAST_404:LIVER + CREATURE:FORGOTTEN_BEAST_404:GUT +$CREATURE:FORGOTTEN_BEAST_404:STOMACH +$CREATURE:FORGOTTEN_BEAST_404:GIZZARD +%CREATURE:FORGOTTEN_BEAST_404:PANCREAS +#CREATURE:FORGOTTEN_BEAST_404:SPLEEN +#CREATURE:FORGOTTEN_BEAST_404:KIDNEY +#CREATURE:FORGOTTEN_BEAST_405:MUSCLE + CREATURE:FORGOTTEN_BEAST_405:EYE +"CREATURE:FORGOTTEN_BEAST_405:BRAIN +!CREATURE:FORGOTTEN_BEAST_405:LUNG +"CREATURE:FORGOTTEN_BEAST_405:HEART +"CREATURE:FORGOTTEN_BEAST_405:LIVER + CREATURE:FORGOTTEN_BEAST_405:GUT +$CREATURE:FORGOTTEN_BEAST_405:STOMACH +$CREATURE:FORGOTTEN_BEAST_405:GIZZARD +%CREATURE:FORGOTTEN_BEAST_405:PANCREAS +#CREATURE:FORGOTTEN_BEAST_405:SPLEEN +#CREATURE:FORGOTTEN_BEAST_405:KIDNEY +#CREATURE:FORGOTTEN_BEAST_406:MUSCLE + CREATURE:FORGOTTEN_BEAST_406:EYE +"CREATURE:FORGOTTEN_BEAST_406:BRAIN +!CREATURE:FORGOTTEN_BEAST_406:LUNG +"CREATURE:FORGOTTEN_BEAST_406:HEART +"CREATURE:FORGOTTEN_BEAST_406:LIVER + CREATURE:FORGOTTEN_BEAST_406:GUT +$CREATURE:FORGOTTEN_BEAST_406:STOMACH +$CREATURE:FORGOTTEN_BEAST_406:GIZZARD +%CREATURE:FORGOTTEN_BEAST_406:PANCREAS +#CREATURE:FORGOTTEN_BEAST_406:SPLEEN +#CREATURE:FORGOTTEN_BEAST_406:KIDNEY +#CREATURE:FORGOTTEN_BEAST_407:MUSCLE + CREATURE:FORGOTTEN_BEAST_407:EYE +"CREATURE:FORGOTTEN_BEAST_407:BRAIN +!CREATURE:FORGOTTEN_BEAST_407:LUNG +"CREATURE:FORGOTTEN_BEAST_407:HEART +"CREATURE:FORGOTTEN_BEAST_407:LIVER + CREATURE:FORGOTTEN_BEAST_407:GUT +$CREATURE:FORGOTTEN_BEAST_407:STOMACH +$CREATURE:FORGOTTEN_BEAST_407:GIZZARD +%CREATURE:FORGOTTEN_BEAST_407:PANCREAS +#CREATURE:FORGOTTEN_BEAST_407:SPLEEN +#CREATURE:FORGOTTEN_BEAST_407:KIDNEY +#CREATURE:FORGOTTEN_BEAST_408:MUSCLE + CREATURE:FORGOTTEN_BEAST_408:EYE +"CREATURE:FORGOTTEN_BEAST_408:BRAIN +!CREATURE:FORGOTTEN_BEAST_408:LUNG +"CREATURE:FORGOTTEN_BEAST_408:HEART +"CREATURE:FORGOTTEN_BEAST_408:LIVER + CREATURE:FORGOTTEN_BEAST_408:GUT +$CREATURE:FORGOTTEN_BEAST_408:STOMACH +$CREATURE:FORGOTTEN_BEAST_408:GIZZARD +%CREATURE:FORGOTTEN_BEAST_408:PANCREAS +#CREATURE:FORGOTTEN_BEAST_408:SPLEEN +#CREATURE:FORGOTTEN_BEAST_408:KIDNEY +#CREATURE:FORGOTTEN_BEAST_409:MUSCLE + CREATURE:FORGOTTEN_BEAST_409:EYE +"CREATURE:FORGOTTEN_BEAST_409:BRAIN +!CREATURE:FORGOTTEN_BEAST_409:LUNG +"CREATURE:FORGOTTEN_BEAST_409:HEART +"CREATURE:FORGOTTEN_BEAST_409:LIVER + CREATURE:FORGOTTEN_BEAST_409:GUT +$CREATURE:FORGOTTEN_BEAST_409:STOMACH +$CREATURE:FORGOTTEN_BEAST_409:GIZZARD +%CREATURE:FORGOTTEN_BEAST_409:PANCREAS +#CREATURE:FORGOTTEN_BEAST_409:SPLEEN +#CREATURE:FORGOTTEN_BEAST_409:KIDNEY +#CREATURE:FORGOTTEN_BEAST_410:MUSCLE + CREATURE:FORGOTTEN_BEAST_410:EYE +"CREATURE:FORGOTTEN_BEAST_410:BRAIN +!CREATURE:FORGOTTEN_BEAST_410:LUNG +"CREATURE:FORGOTTEN_BEAST_410:HEART +"CREATURE:FORGOTTEN_BEAST_410:LIVER + CREATURE:FORGOTTEN_BEAST_410:GUT +$CREATURE:FORGOTTEN_BEAST_410:STOMACH +$CREATURE:FORGOTTEN_BEAST_410:GIZZARD +%CREATURE:FORGOTTEN_BEAST_410:PANCREAS +#CREATURE:FORGOTTEN_BEAST_410:SPLEEN +#CREATURE:FORGOTTEN_BEAST_410:KIDNEY +#CREATURE:FORGOTTEN_BEAST_411:MUSCLE + CREATURE:FORGOTTEN_BEAST_411:EYE +"CREATURE:FORGOTTEN_BEAST_411:BRAIN +!CREATURE:FORGOTTEN_BEAST_411:LUNG +"CREATURE:FORGOTTEN_BEAST_411:HEART +"CREATURE:FORGOTTEN_BEAST_411:LIVER + CREATURE:FORGOTTEN_BEAST_411:GUT +$CREATURE:FORGOTTEN_BEAST_411:STOMACH +$CREATURE:FORGOTTEN_BEAST_411:GIZZARD +%CREATURE:FORGOTTEN_BEAST_411:PANCREAS +#CREATURE:FORGOTTEN_BEAST_411:SPLEEN +#CREATURE:FORGOTTEN_BEAST_411:KIDNEY +#CREATURE:FORGOTTEN_BEAST_412:MUSCLE + CREATURE:FORGOTTEN_BEAST_412:EYE +"CREATURE:FORGOTTEN_BEAST_412:BRAIN +!CREATURE:FORGOTTEN_BEAST_412:LUNG +"CREATURE:FORGOTTEN_BEAST_412:HEART +"CREATURE:FORGOTTEN_BEAST_412:LIVER + CREATURE:FORGOTTEN_BEAST_412:GUT +$CREATURE:FORGOTTEN_BEAST_412:STOMACH +$CREATURE:FORGOTTEN_BEAST_412:GIZZARD +%CREATURE:FORGOTTEN_BEAST_412:PANCREAS +#CREATURE:FORGOTTEN_BEAST_412:SPLEEN +#CREATURE:FORGOTTEN_BEAST_412:KIDNEY +#CREATURE:FORGOTTEN_BEAST_413:MUSCLE + CREATURE:FORGOTTEN_BEAST_413:EYE +"CREATURE:FORGOTTEN_BEAST_413:BRAIN +!CREATURE:FORGOTTEN_BEAST_413:LUNG +"CREATURE:FORGOTTEN_BEAST_413:HEART +"CREATURE:FORGOTTEN_BEAST_413:LIVER + CREATURE:FORGOTTEN_BEAST_413:GUT +$CREATURE:FORGOTTEN_BEAST_413:STOMACH +$CREATURE:FORGOTTEN_BEAST_413:GIZZARD +%CREATURE:FORGOTTEN_BEAST_413:PANCREAS +#CREATURE:FORGOTTEN_BEAST_413:SPLEEN +#CREATURE:FORGOTTEN_BEAST_413:KIDNEY +#CREATURE:FORGOTTEN_BEAST_414:MUSCLE + CREATURE:FORGOTTEN_BEAST_414:EYE +"CREATURE:FORGOTTEN_BEAST_414:BRAIN +!CREATURE:FORGOTTEN_BEAST_414:LUNG +"CREATURE:FORGOTTEN_BEAST_414:HEART +"CREATURE:FORGOTTEN_BEAST_414:LIVER + CREATURE:FORGOTTEN_BEAST_414:GUT +$CREATURE:FORGOTTEN_BEAST_414:STOMACH +$CREATURE:FORGOTTEN_BEAST_414:GIZZARD +%CREATURE:FORGOTTEN_BEAST_414:PANCREAS +#CREATURE:FORGOTTEN_BEAST_414:SPLEEN +#CREATURE:FORGOTTEN_BEAST_414:KIDNEY +#CREATURE:FORGOTTEN_BEAST_416:MUSCLE + CREATURE:FORGOTTEN_BEAST_416:EYE +"CREATURE:FORGOTTEN_BEAST_416:BRAIN +!CREATURE:FORGOTTEN_BEAST_416:LUNG +"CREATURE:FORGOTTEN_BEAST_416:HEART +"CREATURE:FORGOTTEN_BEAST_416:LIVER + CREATURE:FORGOTTEN_BEAST_416:GUT +$CREATURE:FORGOTTEN_BEAST_416:STOMACH +$CREATURE:FORGOTTEN_BEAST_416:GIZZARD +%CREATURE:FORGOTTEN_BEAST_416:PANCREAS +#CREATURE:FORGOTTEN_BEAST_416:SPLEEN +#CREATURE:FORGOTTEN_BEAST_416:KIDNEY +#CREATURE:FORGOTTEN_BEAST_417:MUSCLE + CREATURE:FORGOTTEN_BEAST_417:EYE +"CREATURE:FORGOTTEN_BEAST_417:BRAIN +!CREATURE:FORGOTTEN_BEAST_417:LUNG +"CREATURE:FORGOTTEN_BEAST_417:HEART +"CREATURE:FORGOTTEN_BEAST_417:LIVER + CREATURE:FORGOTTEN_BEAST_417:GUT +$CREATURE:FORGOTTEN_BEAST_417:STOMACH +$CREATURE:FORGOTTEN_BEAST_417:GIZZARD +%CREATURE:FORGOTTEN_BEAST_417:PANCREAS +#CREATURE:FORGOTTEN_BEAST_417:SPLEEN +#CREATURE:FORGOTTEN_BEAST_417:KIDNEY +#CREATURE:FORGOTTEN_BEAST_418:MUSCLE + CREATURE:FORGOTTEN_BEAST_418:EYE +"CREATURE:FORGOTTEN_BEAST_418:BRAIN +!CREATURE:FORGOTTEN_BEAST_418:LUNG +"CREATURE:FORGOTTEN_BEAST_418:HEART +"CREATURE:FORGOTTEN_BEAST_418:LIVER + CREATURE:FORGOTTEN_BEAST_418:GUT +$CREATURE:FORGOTTEN_BEAST_418:STOMACH +$CREATURE:FORGOTTEN_BEAST_418:GIZZARD +%CREATURE:FORGOTTEN_BEAST_418:PANCREAS +#CREATURE:FORGOTTEN_BEAST_418:SPLEEN +#CREATURE:FORGOTTEN_BEAST_418:KIDNEY +#CREATURE:FORGOTTEN_BEAST_420:MUSCLE + CREATURE:FORGOTTEN_BEAST_420:EYE +"CREATURE:FORGOTTEN_BEAST_420:BRAIN +!CREATURE:FORGOTTEN_BEAST_420:LUNG +"CREATURE:FORGOTTEN_BEAST_420:HEART +"CREATURE:FORGOTTEN_BEAST_420:LIVER + CREATURE:FORGOTTEN_BEAST_420:GUT +$CREATURE:FORGOTTEN_BEAST_420:STOMACH +$CREATURE:FORGOTTEN_BEAST_420:GIZZARD +%CREATURE:FORGOTTEN_BEAST_420:PANCREAS +#CREATURE:FORGOTTEN_BEAST_420:SPLEEN +#CREATURE:FORGOTTEN_BEAST_420:KIDNEY +#CREATURE:FORGOTTEN_BEAST_421:MUSCLE + CREATURE:FORGOTTEN_BEAST_421:EYE +"CREATURE:FORGOTTEN_BEAST_421:BRAIN +!CREATURE:FORGOTTEN_BEAST_421:LUNG +"CREATURE:FORGOTTEN_BEAST_421:HEART +"CREATURE:FORGOTTEN_BEAST_421:LIVER + CREATURE:FORGOTTEN_BEAST_421:GUT +$CREATURE:FORGOTTEN_BEAST_421:STOMACH +$CREATURE:FORGOTTEN_BEAST_421:GIZZARD +%CREATURE:FORGOTTEN_BEAST_421:PANCREAS +#CREATURE:FORGOTTEN_BEAST_421:SPLEEN +#CREATURE:FORGOTTEN_BEAST_421:KIDNEY +#CREATURE:FORGOTTEN_BEAST_422:MUSCLE + CREATURE:FORGOTTEN_BEAST_422:EYE +"CREATURE:FORGOTTEN_BEAST_422:BRAIN +!CREATURE:FORGOTTEN_BEAST_422:LUNG +"CREATURE:FORGOTTEN_BEAST_422:HEART +"CREATURE:FORGOTTEN_BEAST_422:LIVER + CREATURE:FORGOTTEN_BEAST_422:GUT +$CREATURE:FORGOTTEN_BEAST_422:STOMACH +$CREATURE:FORGOTTEN_BEAST_422:GIZZARD +%CREATURE:FORGOTTEN_BEAST_422:PANCREAS +#CREATURE:FORGOTTEN_BEAST_422:SPLEEN +#CREATURE:FORGOTTEN_BEAST_422:KIDNEY +#CREATURE:FORGOTTEN_BEAST_423:MUSCLE + CREATURE:FORGOTTEN_BEAST_423:EYE +"CREATURE:FORGOTTEN_BEAST_423:BRAIN +!CREATURE:FORGOTTEN_BEAST_423:LUNG +"CREATURE:FORGOTTEN_BEAST_423:HEART +"CREATURE:FORGOTTEN_BEAST_423:LIVER + CREATURE:FORGOTTEN_BEAST_423:GUT +$CREATURE:FORGOTTEN_BEAST_423:STOMACH +$CREATURE:FORGOTTEN_BEAST_423:GIZZARD +%CREATURE:FORGOTTEN_BEAST_423:PANCREAS +#CREATURE:FORGOTTEN_BEAST_423:SPLEEN +#CREATURE:FORGOTTEN_BEAST_423:KIDNEY +#CREATURE:FORGOTTEN_BEAST_424:MUSCLE + CREATURE:FORGOTTEN_BEAST_424:EYE +"CREATURE:FORGOTTEN_BEAST_424:BRAIN +!CREATURE:FORGOTTEN_BEAST_424:LUNG +"CREATURE:FORGOTTEN_BEAST_424:HEART +"CREATURE:FORGOTTEN_BEAST_424:LIVER + CREATURE:FORGOTTEN_BEAST_424:GUT +$CREATURE:FORGOTTEN_BEAST_424:STOMACH +$CREATURE:FORGOTTEN_BEAST_424:GIZZARD +%CREATURE:FORGOTTEN_BEAST_424:PANCREAS +#CREATURE:FORGOTTEN_BEAST_424:SPLEEN +#CREATURE:FORGOTTEN_BEAST_424:KIDNEY +#CREATURE:FORGOTTEN_BEAST_427:MUSCLE + CREATURE:FORGOTTEN_BEAST_427:EYE +"CREATURE:FORGOTTEN_BEAST_427:BRAIN +!CREATURE:FORGOTTEN_BEAST_427:LUNG +"CREATURE:FORGOTTEN_BEAST_427:HEART +"CREATURE:FORGOTTEN_BEAST_427:LIVER + CREATURE:FORGOTTEN_BEAST_427:GUT +$CREATURE:FORGOTTEN_BEAST_427:STOMACH +$CREATURE:FORGOTTEN_BEAST_427:GIZZARD +%CREATURE:FORGOTTEN_BEAST_427:PANCREAS +#CREATURE:FORGOTTEN_BEAST_427:SPLEEN +#CREATURE:FORGOTTEN_BEAST_427:KIDNEY +#CREATURE:FORGOTTEN_BEAST_429:MUSCLE + CREATURE:FORGOTTEN_BEAST_429:EYE +"CREATURE:FORGOTTEN_BEAST_429:BRAIN +!CREATURE:FORGOTTEN_BEAST_429:LUNG +"CREATURE:FORGOTTEN_BEAST_429:HEART +"CREATURE:FORGOTTEN_BEAST_429:LIVER + CREATURE:FORGOTTEN_BEAST_429:GUT +$CREATURE:FORGOTTEN_BEAST_429:STOMACH +$CREATURE:FORGOTTEN_BEAST_429:GIZZARD +%CREATURE:FORGOTTEN_BEAST_429:PANCREAS +#CREATURE:FORGOTTEN_BEAST_429:SPLEEN +#CREATURE:FORGOTTEN_BEAST_429:KIDNEY +#CREATURE:FORGOTTEN_BEAST_430:MUSCLE + CREATURE:FORGOTTEN_BEAST_430:EYE +"CREATURE:FORGOTTEN_BEAST_430:BRAIN +!CREATURE:FORGOTTEN_BEAST_430:LUNG +"CREATURE:FORGOTTEN_BEAST_430:HEART +"CREATURE:FORGOTTEN_BEAST_430:LIVER + CREATURE:FORGOTTEN_BEAST_430:GUT +$CREATURE:FORGOTTEN_BEAST_430:STOMACH +$CREATURE:FORGOTTEN_BEAST_430:GIZZARD +%CREATURE:FORGOTTEN_BEAST_430:PANCREAS +#CREATURE:FORGOTTEN_BEAST_430:SPLEEN +#CREATURE:FORGOTTEN_BEAST_430:KIDNEY +#CREATURE:FORGOTTEN_BEAST_432:MUSCLE + CREATURE:FORGOTTEN_BEAST_432:EYE +"CREATURE:FORGOTTEN_BEAST_432:BRAIN +!CREATURE:FORGOTTEN_BEAST_432:LUNG +"CREATURE:FORGOTTEN_BEAST_432:HEART +"CREATURE:FORGOTTEN_BEAST_432:LIVER + CREATURE:FORGOTTEN_BEAST_432:GUT +$CREATURE:FORGOTTEN_BEAST_432:STOMACH +$CREATURE:FORGOTTEN_BEAST_432:GIZZARD +%CREATURE:FORGOTTEN_BEAST_432:PANCREAS +#CREATURE:FORGOTTEN_BEAST_432:SPLEEN +#CREATURE:FORGOTTEN_BEAST_432:KIDNEY +#CREATURE:FORGOTTEN_BEAST_433:MUSCLE + CREATURE:FORGOTTEN_BEAST_433:EYE +"CREATURE:FORGOTTEN_BEAST_433:BRAIN +!CREATURE:FORGOTTEN_BEAST_433:LUNG +"CREATURE:FORGOTTEN_BEAST_433:HEART +"CREATURE:FORGOTTEN_BEAST_433:LIVER + CREATURE:FORGOTTEN_BEAST_433:GUT +$CREATURE:FORGOTTEN_BEAST_433:STOMACH +$CREATURE:FORGOTTEN_BEAST_433:GIZZARD +%CREATURE:FORGOTTEN_BEAST_433:PANCREAS +#CREATURE:FORGOTTEN_BEAST_433:SPLEEN +#CREATURE:FORGOTTEN_BEAST_433:KIDNEY +#CREATURE:FORGOTTEN_BEAST_434:MUSCLE + CREATURE:FORGOTTEN_BEAST_434:EYE +"CREATURE:FORGOTTEN_BEAST_434:BRAIN +!CREATURE:FORGOTTEN_BEAST_434:LUNG +"CREATURE:FORGOTTEN_BEAST_434:HEART +"CREATURE:FORGOTTEN_BEAST_434:LIVER + CREATURE:FORGOTTEN_BEAST_434:GUT +$CREATURE:FORGOTTEN_BEAST_434:STOMACH +$CREATURE:FORGOTTEN_BEAST_434:GIZZARD +%CREATURE:FORGOTTEN_BEAST_434:PANCREAS +#CREATURE:FORGOTTEN_BEAST_434:SPLEEN +#CREATURE:FORGOTTEN_BEAST_434:KIDNEY +#CREATURE:FORGOTTEN_BEAST_435:MUSCLE + CREATURE:FORGOTTEN_BEAST_435:EYE +"CREATURE:FORGOTTEN_BEAST_435:BRAIN +!CREATURE:FORGOTTEN_BEAST_435:LUNG +"CREATURE:FORGOTTEN_BEAST_435:HEART +"CREATURE:FORGOTTEN_BEAST_435:LIVER + CREATURE:FORGOTTEN_BEAST_435:GUT +$CREATURE:FORGOTTEN_BEAST_435:STOMACH +$CREATURE:FORGOTTEN_BEAST_435:GIZZARD +%CREATURE:FORGOTTEN_BEAST_435:PANCREAS +#CREATURE:FORGOTTEN_BEAST_435:SPLEEN +#CREATURE:FORGOTTEN_BEAST_435:KIDNEY +#CREATURE:FORGOTTEN_BEAST_436:MUSCLE + CREATURE:FORGOTTEN_BEAST_436:EYE +"CREATURE:FORGOTTEN_BEAST_436:BRAIN +!CREATURE:FORGOTTEN_BEAST_436:LUNG +"CREATURE:FORGOTTEN_BEAST_436:HEART +"CREATURE:FORGOTTEN_BEAST_436:LIVER + CREATURE:FORGOTTEN_BEAST_436:GUT +$CREATURE:FORGOTTEN_BEAST_436:STOMACH +$CREATURE:FORGOTTEN_BEAST_436:GIZZARD +%CREATURE:FORGOTTEN_BEAST_436:PANCREAS +#CREATURE:FORGOTTEN_BEAST_436:SPLEEN +#CREATURE:FORGOTTEN_BEAST_436:KIDNEY +#CREATURE:FORGOTTEN_BEAST_437:MUSCLE + CREATURE:FORGOTTEN_BEAST_437:EYE +"CREATURE:FORGOTTEN_BEAST_437:BRAIN +!CREATURE:FORGOTTEN_BEAST_437:LUNG +"CREATURE:FORGOTTEN_BEAST_437:HEART +"CREATURE:FORGOTTEN_BEAST_437:LIVER + CREATURE:FORGOTTEN_BEAST_437:GUT +$CREATURE:FORGOTTEN_BEAST_437:STOMACH +$CREATURE:FORGOTTEN_BEAST_437:GIZZARD +%CREATURE:FORGOTTEN_BEAST_437:PANCREAS +#CREATURE:FORGOTTEN_BEAST_437:SPLEEN +#CREATURE:FORGOTTEN_BEAST_437:KIDNEY +#CREATURE:FORGOTTEN_BEAST_438:MUSCLE + CREATURE:FORGOTTEN_BEAST_438:EYE +"CREATURE:FORGOTTEN_BEAST_438:BRAIN +!CREATURE:FORGOTTEN_BEAST_438:LUNG +"CREATURE:FORGOTTEN_BEAST_438:HEART +"CREATURE:FORGOTTEN_BEAST_438:LIVER + CREATURE:FORGOTTEN_BEAST_438:GUT +$CREATURE:FORGOTTEN_BEAST_438:STOMACH +$CREATURE:FORGOTTEN_BEAST_438:GIZZARD +%CREATURE:FORGOTTEN_BEAST_438:PANCREAS +#CREATURE:FORGOTTEN_BEAST_438:SPLEEN +#CREATURE:FORGOTTEN_BEAST_438:KIDNEY +#CREATURE:FORGOTTEN_BEAST_440:MUSCLE + CREATURE:FORGOTTEN_BEAST_440:EYE +"CREATURE:FORGOTTEN_BEAST_440:BRAIN +!CREATURE:FORGOTTEN_BEAST_440:LUNG +"CREATURE:FORGOTTEN_BEAST_440:HEART +"CREATURE:FORGOTTEN_BEAST_440:LIVER + CREATURE:FORGOTTEN_BEAST_440:GUT +$CREATURE:FORGOTTEN_BEAST_440:STOMACH +$CREATURE:FORGOTTEN_BEAST_440:GIZZARD +%CREATURE:FORGOTTEN_BEAST_440:PANCREAS +#CREATURE:FORGOTTEN_BEAST_440:SPLEEN +#CREATURE:FORGOTTEN_BEAST_440:KIDNEY +#CREATURE:FORGOTTEN_BEAST_441:MUSCLE + CREATURE:FORGOTTEN_BEAST_441:EYE +"CREATURE:FORGOTTEN_BEAST_441:BRAIN +!CREATURE:FORGOTTEN_BEAST_441:LUNG +"CREATURE:FORGOTTEN_BEAST_441:HEART +"CREATURE:FORGOTTEN_BEAST_441:LIVER + CREATURE:FORGOTTEN_BEAST_441:GUT +$CREATURE:FORGOTTEN_BEAST_441:STOMACH +$CREATURE:FORGOTTEN_BEAST_441:GIZZARD +%CREATURE:FORGOTTEN_BEAST_441:PANCREAS +#CREATURE:FORGOTTEN_BEAST_441:SPLEEN +#CREATURE:FORGOTTEN_BEAST_441:KIDNEY +#CREATURE:FORGOTTEN_BEAST_442:MUSCLE + CREATURE:FORGOTTEN_BEAST_442:EYE +"CREATURE:FORGOTTEN_BEAST_442:BRAIN +!CREATURE:FORGOTTEN_BEAST_442:LUNG +"CREATURE:FORGOTTEN_BEAST_442:HEART +"CREATURE:FORGOTTEN_BEAST_442:LIVER + CREATURE:FORGOTTEN_BEAST_442:GUT +$CREATURE:FORGOTTEN_BEAST_442:STOMACH +$CREATURE:FORGOTTEN_BEAST_442:GIZZARD +%CREATURE:FORGOTTEN_BEAST_442:PANCREAS +#CREATURE:FORGOTTEN_BEAST_442:SPLEEN +#CREATURE:FORGOTTEN_BEAST_442:KIDNEY +#CREATURE:FORGOTTEN_BEAST_444:MUSCLE + CREATURE:FORGOTTEN_BEAST_444:EYE +"CREATURE:FORGOTTEN_BEAST_444:BRAIN +!CREATURE:FORGOTTEN_BEAST_444:LUNG +"CREATURE:FORGOTTEN_BEAST_444:HEART +"CREATURE:FORGOTTEN_BEAST_444:LIVER + CREATURE:FORGOTTEN_BEAST_444:GUT +$CREATURE:FORGOTTEN_BEAST_444:STOMACH +$CREATURE:FORGOTTEN_BEAST_444:GIZZARD +%CREATURE:FORGOTTEN_BEAST_444:PANCREAS +#CREATURE:FORGOTTEN_BEAST_444:SPLEEN +#CREATURE:FORGOTTEN_BEAST_444:KIDNEY +#CREATURE:FORGOTTEN_BEAST_446:MUSCLE + CREATURE:FORGOTTEN_BEAST_446:EYE +"CREATURE:FORGOTTEN_BEAST_446:BRAIN +!CREATURE:FORGOTTEN_BEAST_446:LUNG +"CREATURE:FORGOTTEN_BEAST_446:HEART +"CREATURE:FORGOTTEN_BEAST_446:LIVER + CREATURE:FORGOTTEN_BEAST_446:GUT +$CREATURE:FORGOTTEN_BEAST_446:STOMACH +$CREATURE:FORGOTTEN_BEAST_446:GIZZARD +%CREATURE:FORGOTTEN_BEAST_446:PANCREAS +#CREATURE:FORGOTTEN_BEAST_446:SPLEEN +#CREATURE:FORGOTTEN_BEAST_446:KIDNEY +#CREATURE:FORGOTTEN_BEAST_447:MUSCLE + CREATURE:FORGOTTEN_BEAST_447:EYE +"CREATURE:FORGOTTEN_BEAST_447:BRAIN +!CREATURE:FORGOTTEN_BEAST_447:LUNG +"CREATURE:FORGOTTEN_BEAST_447:HEART +"CREATURE:FORGOTTEN_BEAST_447:LIVER + CREATURE:FORGOTTEN_BEAST_447:GUT +$CREATURE:FORGOTTEN_BEAST_447:STOMACH +$CREATURE:FORGOTTEN_BEAST_447:GIZZARD +%CREATURE:FORGOTTEN_BEAST_447:PANCREAS +#CREATURE:FORGOTTEN_BEAST_447:SPLEEN +#CREATURE:FORGOTTEN_BEAST_447:KIDNEY +#CREATURE:FORGOTTEN_BEAST_448:MUSCLE + CREATURE:FORGOTTEN_BEAST_448:EYE +"CREATURE:FORGOTTEN_BEAST_448:BRAIN +!CREATURE:FORGOTTEN_BEAST_448:LUNG +"CREATURE:FORGOTTEN_BEAST_448:HEART +"CREATURE:FORGOTTEN_BEAST_448:LIVER + CREATURE:FORGOTTEN_BEAST_448:GUT +$CREATURE:FORGOTTEN_BEAST_448:STOMACH +$CREATURE:FORGOTTEN_BEAST_448:GIZZARD +%CREATURE:FORGOTTEN_BEAST_448:PANCREAS +#CREATURE:FORGOTTEN_BEAST_448:SPLEEN +#CREATURE:FORGOTTEN_BEAST_448:KIDNEY +#CREATURE:FORGOTTEN_BEAST_449:MUSCLE + CREATURE:FORGOTTEN_BEAST_449:EYE +"CREATURE:FORGOTTEN_BEAST_449:BRAIN +!CREATURE:FORGOTTEN_BEAST_449:LUNG +"CREATURE:FORGOTTEN_BEAST_449:HEART +"CREATURE:FORGOTTEN_BEAST_449:LIVER + CREATURE:FORGOTTEN_BEAST_449:GUT +$CREATURE:FORGOTTEN_BEAST_449:STOMACH +$CREATURE:FORGOTTEN_BEAST_449:GIZZARD +%CREATURE:FORGOTTEN_BEAST_449:PANCREAS +#CREATURE:FORGOTTEN_BEAST_449:SPLEEN +#CREATURE:FORGOTTEN_BEAST_449:KIDNEY +#CREATURE:FORGOTTEN_BEAST_450:MUSCLE + CREATURE:FORGOTTEN_BEAST_450:EYE +"CREATURE:FORGOTTEN_BEAST_450:BRAIN +!CREATURE:FORGOTTEN_BEAST_450:LUNG +"CREATURE:FORGOTTEN_BEAST_450:HEART +"CREATURE:FORGOTTEN_BEAST_450:LIVER + CREATURE:FORGOTTEN_BEAST_450:GUT +$CREATURE:FORGOTTEN_BEAST_450:STOMACH +$CREATURE:FORGOTTEN_BEAST_450:GIZZARD +%CREATURE:FORGOTTEN_BEAST_450:PANCREAS +#CREATURE:FORGOTTEN_BEAST_450:SPLEEN +#CREATURE:FORGOTTEN_BEAST_450:KIDNEY +#CREATURE:FORGOTTEN_BEAST_451:MUSCLE + CREATURE:FORGOTTEN_BEAST_451:EYE +"CREATURE:FORGOTTEN_BEAST_451:BRAIN +!CREATURE:FORGOTTEN_BEAST_451:LUNG +"CREATURE:FORGOTTEN_BEAST_451:HEART +"CREATURE:FORGOTTEN_BEAST_451:LIVER + CREATURE:FORGOTTEN_BEAST_451:GUT +$CREATURE:FORGOTTEN_BEAST_451:STOMACH +$CREATURE:FORGOTTEN_BEAST_451:GIZZARD +%CREATURE:FORGOTTEN_BEAST_451:PANCREAS +#CREATURE:FORGOTTEN_BEAST_451:SPLEEN +#CREATURE:FORGOTTEN_BEAST_451:KIDNEY +#CREATURE:FORGOTTEN_BEAST_453:MUSCLE + CREATURE:FORGOTTEN_BEAST_453:EYE +"CREATURE:FORGOTTEN_BEAST_453:BRAIN +!CREATURE:FORGOTTEN_BEAST_453:LUNG +"CREATURE:FORGOTTEN_BEAST_453:HEART +"CREATURE:FORGOTTEN_BEAST_453:LIVER + CREATURE:FORGOTTEN_BEAST_453:GUT +$CREATURE:FORGOTTEN_BEAST_453:STOMACH +$CREATURE:FORGOTTEN_BEAST_453:GIZZARD +%CREATURE:FORGOTTEN_BEAST_453:PANCREAS +#CREATURE:FORGOTTEN_BEAST_453:SPLEEN +#CREATURE:FORGOTTEN_BEAST_453:KIDNEY +#CREATURE:FORGOTTEN_BEAST_454:MUSCLE + CREATURE:FORGOTTEN_BEAST_454:EYE +"CREATURE:FORGOTTEN_BEAST_454:BRAIN +!CREATURE:FORGOTTEN_BEAST_454:LUNG +"CREATURE:FORGOTTEN_BEAST_454:HEART +"CREATURE:FORGOTTEN_BEAST_454:LIVER + CREATURE:FORGOTTEN_BEAST_454:GUT +$CREATURE:FORGOTTEN_BEAST_454:STOMACH +$CREATURE:FORGOTTEN_BEAST_454:GIZZARD +%CREATURE:FORGOTTEN_BEAST_454:PANCREAS +#CREATURE:FORGOTTEN_BEAST_454:SPLEEN +#CREATURE:FORGOTTEN_BEAST_454:KIDNEY +#CREATURE:FORGOTTEN_BEAST_455:MUSCLE + CREATURE:FORGOTTEN_BEAST_455:EYE +"CREATURE:FORGOTTEN_BEAST_455:BRAIN +!CREATURE:FORGOTTEN_BEAST_455:LUNG +"CREATURE:FORGOTTEN_BEAST_455:HEART +"CREATURE:FORGOTTEN_BEAST_455:LIVER + CREATURE:FORGOTTEN_BEAST_455:GUT +$CREATURE:FORGOTTEN_BEAST_455:STOMACH +$CREATURE:FORGOTTEN_BEAST_455:GIZZARD +%CREATURE:FORGOTTEN_BEAST_455:PANCREAS +#CREATURE:FORGOTTEN_BEAST_455:SPLEEN +#CREATURE:FORGOTTEN_BEAST_455:KIDNEY +#CREATURE:FORGOTTEN_BEAST_457:MUSCLE + CREATURE:FORGOTTEN_BEAST_457:EYE +"CREATURE:FORGOTTEN_BEAST_457:BRAIN +!CREATURE:FORGOTTEN_BEAST_457:LUNG +"CREATURE:FORGOTTEN_BEAST_457:HEART +"CREATURE:FORGOTTEN_BEAST_457:LIVER + CREATURE:FORGOTTEN_BEAST_457:GUT +$CREATURE:FORGOTTEN_BEAST_457:STOMACH +$CREATURE:FORGOTTEN_BEAST_457:GIZZARD +%CREATURE:FORGOTTEN_BEAST_457:PANCREAS +#CREATURE:FORGOTTEN_BEAST_457:SPLEEN +#CREATURE:FORGOTTEN_BEAST_457:KIDNEY +#CREATURE:FORGOTTEN_BEAST_459:MUSCLE + CREATURE:FORGOTTEN_BEAST_459:EYE +"CREATURE:FORGOTTEN_BEAST_459:BRAIN +!CREATURE:FORGOTTEN_BEAST_459:LUNG +"CREATURE:FORGOTTEN_BEAST_459:HEART +"CREATURE:FORGOTTEN_BEAST_459:LIVER + CREATURE:FORGOTTEN_BEAST_459:GUT +$CREATURE:FORGOTTEN_BEAST_459:STOMACH +$CREATURE:FORGOTTEN_BEAST_459:GIZZARD +%CREATURE:FORGOTTEN_BEAST_459:PANCREAS +#CREATURE:FORGOTTEN_BEAST_459:SPLEEN +#CREATURE:FORGOTTEN_BEAST_459:KIDNEY +#CREATURE:FORGOTTEN_BEAST_461:MUSCLE + CREATURE:FORGOTTEN_BEAST_461:EYE +"CREATURE:FORGOTTEN_BEAST_461:BRAIN +!CREATURE:FORGOTTEN_BEAST_461:LUNG +"CREATURE:FORGOTTEN_BEAST_461:HEART +"CREATURE:FORGOTTEN_BEAST_461:LIVER + CREATURE:FORGOTTEN_BEAST_461:GUT +$CREATURE:FORGOTTEN_BEAST_461:STOMACH +$CREATURE:FORGOTTEN_BEAST_461:GIZZARD +%CREATURE:FORGOTTEN_BEAST_461:PANCREAS +#CREATURE:FORGOTTEN_BEAST_461:SPLEEN +#CREATURE:FORGOTTEN_BEAST_461:KIDNEY +#CREATURE:FORGOTTEN_BEAST_462:MUSCLE + CREATURE:FORGOTTEN_BEAST_462:EYE +"CREATURE:FORGOTTEN_BEAST_462:BRAIN +!CREATURE:FORGOTTEN_BEAST_462:LUNG +"CREATURE:FORGOTTEN_BEAST_462:HEART +"CREATURE:FORGOTTEN_BEAST_462:LIVER + CREATURE:FORGOTTEN_BEAST_462:GUT +$CREATURE:FORGOTTEN_BEAST_462:STOMACH +$CREATURE:FORGOTTEN_BEAST_462:GIZZARD +%CREATURE:FORGOTTEN_BEAST_462:PANCREAS +#CREATURE:FORGOTTEN_BEAST_462:SPLEEN +#CREATURE:FORGOTTEN_BEAST_462:KIDNEY +#CREATURE:FORGOTTEN_BEAST_463:MUSCLE + CREATURE:FORGOTTEN_BEAST_463:EYE +"CREATURE:FORGOTTEN_BEAST_463:BRAIN +!CREATURE:FORGOTTEN_BEAST_463:LUNG +"CREATURE:FORGOTTEN_BEAST_463:HEART +"CREATURE:FORGOTTEN_BEAST_463:LIVER + CREATURE:FORGOTTEN_BEAST_463:GUT +$CREATURE:FORGOTTEN_BEAST_463:STOMACH +$CREATURE:FORGOTTEN_BEAST_463:GIZZARD +%CREATURE:FORGOTTEN_BEAST_463:PANCREAS +#CREATURE:FORGOTTEN_BEAST_463:SPLEEN +#CREATURE:FORGOTTEN_BEAST_463:KIDNEY +#CREATURE:FORGOTTEN_BEAST_465:MUSCLE + CREATURE:FORGOTTEN_BEAST_465:EYE +"CREATURE:FORGOTTEN_BEAST_465:BRAIN +!CREATURE:FORGOTTEN_BEAST_465:LUNG +"CREATURE:FORGOTTEN_BEAST_465:HEART +"CREATURE:FORGOTTEN_BEAST_465:LIVER + CREATURE:FORGOTTEN_BEAST_465:GUT +$CREATURE:FORGOTTEN_BEAST_465:STOMACH +$CREATURE:FORGOTTEN_BEAST_465:GIZZARD +%CREATURE:FORGOTTEN_BEAST_465:PANCREAS +#CREATURE:FORGOTTEN_BEAST_465:SPLEEN +#CREATURE:FORGOTTEN_BEAST_465:KIDNEY +#CREATURE:FORGOTTEN_BEAST_466:MUSCLE + CREATURE:FORGOTTEN_BEAST_466:EYE +"CREATURE:FORGOTTEN_BEAST_466:BRAIN +!CREATURE:FORGOTTEN_BEAST_466:LUNG +"CREATURE:FORGOTTEN_BEAST_466:HEART +"CREATURE:FORGOTTEN_BEAST_466:LIVER + CREATURE:FORGOTTEN_BEAST_466:GUT +$CREATURE:FORGOTTEN_BEAST_466:STOMACH +$CREATURE:FORGOTTEN_BEAST_466:GIZZARD +%CREATURE:FORGOTTEN_BEAST_466:PANCREAS +#CREATURE:FORGOTTEN_BEAST_466:SPLEEN +#CREATURE:FORGOTTEN_BEAST_466:KIDNEY +#CREATURE:FORGOTTEN_BEAST_468:MUSCLE + CREATURE:FORGOTTEN_BEAST_468:EYE +"CREATURE:FORGOTTEN_BEAST_468:BRAIN +!CREATURE:FORGOTTEN_BEAST_468:LUNG +"CREATURE:FORGOTTEN_BEAST_468:HEART +"CREATURE:FORGOTTEN_BEAST_468:LIVER + CREATURE:FORGOTTEN_BEAST_468:GUT +$CREATURE:FORGOTTEN_BEAST_468:STOMACH +$CREATURE:FORGOTTEN_BEAST_468:GIZZARD +%CREATURE:FORGOTTEN_BEAST_468:PANCREAS +#CREATURE:FORGOTTEN_BEAST_468:SPLEEN +#CREATURE:FORGOTTEN_BEAST_468:KIDNEY +#CREATURE:FORGOTTEN_BEAST_469:MUSCLE + CREATURE:FORGOTTEN_BEAST_469:EYE +"CREATURE:FORGOTTEN_BEAST_469:BRAIN +!CREATURE:FORGOTTEN_BEAST_469:LUNG +"CREATURE:FORGOTTEN_BEAST_469:HEART +"CREATURE:FORGOTTEN_BEAST_469:LIVER + CREATURE:FORGOTTEN_BEAST_469:GUT +$CREATURE:FORGOTTEN_BEAST_469:STOMACH +$CREATURE:FORGOTTEN_BEAST_469:GIZZARD +%CREATURE:FORGOTTEN_BEAST_469:PANCREAS +#CREATURE:FORGOTTEN_BEAST_469:SPLEEN +#CREATURE:FORGOTTEN_BEAST_469:KIDNEY +#CREATURE:FORGOTTEN_BEAST_470:MUSCLE + CREATURE:FORGOTTEN_BEAST_470:EYE +"CREATURE:FORGOTTEN_BEAST_470:BRAIN +!CREATURE:FORGOTTEN_BEAST_470:LUNG +"CREATURE:FORGOTTEN_BEAST_470:HEART +"CREATURE:FORGOTTEN_BEAST_470:LIVER + CREATURE:FORGOTTEN_BEAST_470:GUT +$CREATURE:FORGOTTEN_BEAST_470:STOMACH +$CREATURE:FORGOTTEN_BEAST_470:GIZZARD +%CREATURE:FORGOTTEN_BEAST_470:PANCREAS +#CREATURE:FORGOTTEN_BEAST_470:SPLEEN +#CREATURE:FORGOTTEN_BEAST_470:KIDNEY +#CREATURE:FORGOTTEN_BEAST_471:MUSCLE + CREATURE:FORGOTTEN_BEAST_471:EYE +"CREATURE:FORGOTTEN_BEAST_471:BRAIN +!CREATURE:FORGOTTEN_BEAST_471:LUNG +"CREATURE:FORGOTTEN_BEAST_471:HEART +"CREATURE:FORGOTTEN_BEAST_471:LIVER + CREATURE:FORGOTTEN_BEAST_471:GUT +$CREATURE:FORGOTTEN_BEAST_471:STOMACH +$CREATURE:FORGOTTEN_BEAST_471:GIZZARD +%CREATURE:FORGOTTEN_BEAST_471:PANCREAS +#CREATURE:FORGOTTEN_BEAST_471:SPLEEN +#CREATURE:FORGOTTEN_BEAST_471:KIDNEY +#CREATURE:FORGOTTEN_BEAST_472:MUSCLE + CREATURE:FORGOTTEN_BEAST_472:EYE +"CREATURE:FORGOTTEN_BEAST_472:BRAIN +!CREATURE:FORGOTTEN_BEAST_472:LUNG +"CREATURE:FORGOTTEN_BEAST_472:HEART +"CREATURE:FORGOTTEN_BEAST_472:LIVER + CREATURE:FORGOTTEN_BEAST_472:GUT +$CREATURE:FORGOTTEN_BEAST_472:STOMACH +$CREATURE:FORGOTTEN_BEAST_472:GIZZARD +%CREATURE:FORGOTTEN_BEAST_472:PANCREAS +#CREATURE:FORGOTTEN_BEAST_472:SPLEEN +#CREATURE:FORGOTTEN_BEAST_472:KIDNEY +#CREATURE:FORGOTTEN_BEAST_474:MUSCLE + CREATURE:FORGOTTEN_BEAST_474:EYE +"CREATURE:FORGOTTEN_BEAST_474:BRAIN +!CREATURE:FORGOTTEN_BEAST_474:LUNG +"CREATURE:FORGOTTEN_BEAST_474:HEART +"CREATURE:FORGOTTEN_BEAST_474:LIVER + CREATURE:FORGOTTEN_BEAST_474:GUT +$CREATURE:FORGOTTEN_BEAST_474:STOMACH +$CREATURE:FORGOTTEN_BEAST_474:GIZZARD +%CREATURE:FORGOTTEN_BEAST_474:PANCREAS +#CREATURE:FORGOTTEN_BEAST_474:SPLEEN +#CREATURE:FORGOTTEN_BEAST_474:KIDNEY +#CREATURE:FORGOTTEN_BEAST_475:MUSCLE + CREATURE:FORGOTTEN_BEAST_475:EYE +"CREATURE:FORGOTTEN_BEAST_475:BRAIN +!CREATURE:FORGOTTEN_BEAST_475:LUNG +"CREATURE:FORGOTTEN_BEAST_475:HEART +"CREATURE:FORGOTTEN_BEAST_475:LIVER + CREATURE:FORGOTTEN_BEAST_475:GUT +$CREATURE:FORGOTTEN_BEAST_475:STOMACH +$CREATURE:FORGOTTEN_BEAST_475:GIZZARD +%CREATURE:FORGOTTEN_BEAST_475:PANCREAS +#CREATURE:FORGOTTEN_BEAST_475:SPLEEN +#CREATURE:FORGOTTEN_BEAST_475:KIDNEY +#CREATURE:FORGOTTEN_BEAST_476:MUSCLE + CREATURE:FORGOTTEN_BEAST_476:EYE +"CREATURE:FORGOTTEN_BEAST_476:BRAIN +!CREATURE:FORGOTTEN_BEAST_476:LUNG +"CREATURE:FORGOTTEN_BEAST_476:HEART +"CREATURE:FORGOTTEN_BEAST_476:LIVER + CREATURE:FORGOTTEN_BEAST_476:GUT +$CREATURE:FORGOTTEN_BEAST_476:STOMACH +$CREATURE:FORGOTTEN_BEAST_476:GIZZARD +%CREATURE:FORGOTTEN_BEAST_476:PANCREAS +#CREATURE:FORGOTTEN_BEAST_476:SPLEEN +#CREATURE:FORGOTTEN_BEAST_476:KIDNEY +#CREATURE:FORGOTTEN_BEAST_478:MUSCLE + CREATURE:FORGOTTEN_BEAST_478:EYE +"CREATURE:FORGOTTEN_BEAST_478:BRAIN +!CREATURE:FORGOTTEN_BEAST_478:LUNG +"CREATURE:FORGOTTEN_BEAST_478:HEART +"CREATURE:FORGOTTEN_BEAST_478:LIVER + CREATURE:FORGOTTEN_BEAST_478:GUT +$CREATURE:FORGOTTEN_BEAST_478:STOMACH +$CREATURE:FORGOTTEN_BEAST_478:GIZZARD +%CREATURE:FORGOTTEN_BEAST_478:PANCREAS +#CREATURE:FORGOTTEN_BEAST_478:SPLEEN +#CREATURE:FORGOTTEN_BEAST_478:KIDNEY +#CREATURE:FORGOTTEN_BEAST_479:MUSCLE + CREATURE:FORGOTTEN_BEAST_479:EYE +"CREATURE:FORGOTTEN_BEAST_479:BRAIN +!CREATURE:FORGOTTEN_BEAST_479:LUNG +"CREATURE:FORGOTTEN_BEAST_479:HEART +"CREATURE:FORGOTTEN_BEAST_479:LIVER + CREATURE:FORGOTTEN_BEAST_479:GUT +$CREATURE:FORGOTTEN_BEAST_479:STOMACH +$CREATURE:FORGOTTEN_BEAST_479:GIZZARD +%CREATURE:FORGOTTEN_BEAST_479:PANCREAS +#CREATURE:FORGOTTEN_BEAST_479:SPLEEN +#CREATURE:FORGOTTEN_BEAST_479:KIDNEY +#CREATURE:FORGOTTEN_BEAST_480:MUSCLE + CREATURE:FORGOTTEN_BEAST_480:EYE +"CREATURE:FORGOTTEN_BEAST_480:BRAIN +!CREATURE:FORGOTTEN_BEAST_480:LUNG +"CREATURE:FORGOTTEN_BEAST_480:HEART +"CREATURE:FORGOTTEN_BEAST_480:LIVER + CREATURE:FORGOTTEN_BEAST_480:GUT +$CREATURE:FORGOTTEN_BEAST_480:STOMACH +$CREATURE:FORGOTTEN_BEAST_480:GIZZARD +%CREATURE:FORGOTTEN_BEAST_480:PANCREAS +#CREATURE:FORGOTTEN_BEAST_480:SPLEEN +#CREATURE:FORGOTTEN_BEAST_480:KIDNEY +#CREATURE:FORGOTTEN_BEAST_481:MUSCLE + CREATURE:FORGOTTEN_BEAST_481:EYE +"CREATURE:FORGOTTEN_BEAST_481:BRAIN +!CREATURE:FORGOTTEN_BEAST_481:LUNG +"CREATURE:FORGOTTEN_BEAST_481:HEART +"CREATURE:FORGOTTEN_BEAST_481:LIVER + CREATURE:FORGOTTEN_BEAST_481:GUT +$CREATURE:FORGOTTEN_BEAST_481:STOMACH +$CREATURE:FORGOTTEN_BEAST_481:GIZZARD +%CREATURE:FORGOTTEN_BEAST_481:PANCREAS +#CREATURE:FORGOTTEN_BEAST_481:SPLEEN +#CREATURE:FORGOTTEN_BEAST_481:KIDNEY +#CREATURE:FORGOTTEN_BEAST_483:MUSCLE + CREATURE:FORGOTTEN_BEAST_483:EYE +"CREATURE:FORGOTTEN_BEAST_483:BRAIN +!CREATURE:FORGOTTEN_BEAST_483:LUNG +"CREATURE:FORGOTTEN_BEAST_483:HEART +"CREATURE:FORGOTTEN_BEAST_483:LIVER + CREATURE:FORGOTTEN_BEAST_483:GUT +$CREATURE:FORGOTTEN_BEAST_483:STOMACH +$CREATURE:FORGOTTEN_BEAST_483:GIZZARD +%CREATURE:FORGOTTEN_BEAST_483:PANCREAS +#CREATURE:FORGOTTEN_BEAST_483:SPLEEN +#CREATURE:FORGOTTEN_BEAST_483:KIDNEY +#CREATURE:FORGOTTEN_BEAST_486:MUSCLE + CREATURE:FORGOTTEN_BEAST_486:EYE +"CREATURE:FORGOTTEN_BEAST_486:BRAIN +!CREATURE:FORGOTTEN_BEAST_486:LUNG +"CREATURE:FORGOTTEN_BEAST_486:HEART +"CREATURE:FORGOTTEN_BEAST_486:LIVER + CREATURE:FORGOTTEN_BEAST_486:GUT +$CREATURE:FORGOTTEN_BEAST_486:STOMACH +$CREATURE:FORGOTTEN_BEAST_486:GIZZARD +%CREATURE:FORGOTTEN_BEAST_486:PANCREAS +#CREATURE:FORGOTTEN_BEAST_486:SPLEEN +#CREATURE:FORGOTTEN_BEAST_486:KIDNEY +#CREATURE:FORGOTTEN_BEAST_487:MUSCLE + CREATURE:FORGOTTEN_BEAST_487:EYE +"CREATURE:FORGOTTEN_BEAST_487:BRAIN +!CREATURE:FORGOTTEN_BEAST_487:LUNG +"CREATURE:FORGOTTEN_BEAST_487:HEART +"CREATURE:FORGOTTEN_BEAST_487:LIVER + CREATURE:FORGOTTEN_BEAST_487:GUT +$CREATURE:FORGOTTEN_BEAST_487:STOMACH +$CREATURE:FORGOTTEN_BEAST_487:GIZZARD +%CREATURE:FORGOTTEN_BEAST_487:PANCREAS +#CREATURE:FORGOTTEN_BEAST_487:SPLEEN +#CREATURE:FORGOTTEN_BEAST_487:KIDNEY +#CREATURE:FORGOTTEN_BEAST_489:MUSCLE + CREATURE:FORGOTTEN_BEAST_489:EYE +"CREATURE:FORGOTTEN_BEAST_489:BRAIN +!CREATURE:FORGOTTEN_BEAST_489:LUNG +"CREATURE:FORGOTTEN_BEAST_489:HEART +"CREATURE:FORGOTTEN_BEAST_489:LIVER + CREATURE:FORGOTTEN_BEAST_489:GUT +$CREATURE:FORGOTTEN_BEAST_489:STOMACH +$CREATURE:FORGOTTEN_BEAST_489:GIZZARD +%CREATURE:FORGOTTEN_BEAST_489:PANCREAS +#CREATURE:FORGOTTEN_BEAST_489:SPLEEN +#CREATURE:FORGOTTEN_BEAST_489:KIDNEY +#CREATURE:FORGOTTEN_BEAST_492:MUSCLE + CREATURE:FORGOTTEN_BEAST_492:EYE +"CREATURE:FORGOTTEN_BEAST_492:BRAIN +!CREATURE:FORGOTTEN_BEAST_492:LUNG +"CREATURE:FORGOTTEN_BEAST_492:HEART +"CREATURE:FORGOTTEN_BEAST_492:LIVER + CREATURE:FORGOTTEN_BEAST_492:GUT +$CREATURE:FORGOTTEN_BEAST_492:STOMACH +$CREATURE:FORGOTTEN_BEAST_492:GIZZARD +%CREATURE:FORGOTTEN_BEAST_492:PANCREAS +#CREATURE:FORGOTTEN_BEAST_492:SPLEEN +#CREATURE:FORGOTTEN_BEAST_492:KIDNEY +#CREATURE:FORGOTTEN_BEAST_494:MUSCLE + CREATURE:FORGOTTEN_BEAST_494:EYE +"CREATURE:FORGOTTEN_BEAST_494:BRAIN +!CREATURE:FORGOTTEN_BEAST_494:LUNG +"CREATURE:FORGOTTEN_BEAST_494:HEART +"CREATURE:FORGOTTEN_BEAST_494:LIVER + CREATURE:FORGOTTEN_BEAST_494:GUT +$CREATURE:FORGOTTEN_BEAST_494:STOMACH +$CREATURE:FORGOTTEN_BEAST_494:GIZZARD +%CREATURE:FORGOTTEN_BEAST_494:PANCREAS +#CREATURE:FORGOTTEN_BEAST_494:SPLEEN +#CREATURE:FORGOTTEN_BEAST_494:KIDNEY +#CREATURE:FORGOTTEN_BEAST_495:MUSCLE + CREATURE:FORGOTTEN_BEAST_495:EYE +"CREATURE:FORGOTTEN_BEAST_495:BRAIN +!CREATURE:FORGOTTEN_BEAST_495:LUNG +"CREATURE:FORGOTTEN_BEAST_495:HEART +"CREATURE:FORGOTTEN_BEAST_495:LIVER + CREATURE:FORGOTTEN_BEAST_495:GUT +$CREATURE:FORGOTTEN_BEAST_495:STOMACH +$CREATURE:FORGOTTEN_BEAST_495:GIZZARD +%CREATURE:FORGOTTEN_BEAST_495:PANCREAS +#CREATURE:FORGOTTEN_BEAST_495:SPLEEN +#CREATURE:FORGOTTEN_BEAST_495:KIDNEY +#CREATURE:FORGOTTEN_BEAST_496:MUSCLE + CREATURE:FORGOTTEN_BEAST_496:EYE +"CREATURE:FORGOTTEN_BEAST_496:BRAIN +!CREATURE:FORGOTTEN_BEAST_496:LUNG +"CREATURE:FORGOTTEN_BEAST_496:HEART +"CREATURE:FORGOTTEN_BEAST_496:LIVER + CREATURE:FORGOTTEN_BEAST_496:GUT +$CREATURE:FORGOTTEN_BEAST_496:STOMACH +$CREATURE:FORGOTTEN_BEAST_496:GIZZARD +%CREATURE:FORGOTTEN_BEAST_496:PANCREAS +#CREATURE:FORGOTTEN_BEAST_496:SPLEEN +#CREATURE:FORGOTTEN_BEAST_496:KIDNEY +#CREATURE:FORGOTTEN_BEAST_497:MUSCLE + CREATURE:FORGOTTEN_BEAST_497:EYE +"CREATURE:FORGOTTEN_BEAST_497:BRAIN +!CREATURE:FORGOTTEN_BEAST_497:LUNG +"CREATURE:FORGOTTEN_BEAST_497:HEART +"CREATURE:FORGOTTEN_BEAST_497:LIVER + CREATURE:FORGOTTEN_BEAST_497:GUT +$CREATURE:FORGOTTEN_BEAST_497:STOMACH +$CREATURE:FORGOTTEN_BEAST_497:GIZZARD +%CREATURE:FORGOTTEN_BEAST_497:PANCREAS +#CREATURE:FORGOTTEN_BEAST_497:SPLEEN +#CREATURE:FORGOTTEN_BEAST_497:KIDNEY +#CREATURE:FORGOTTEN_BEAST_498:MUSCLE + CREATURE:FORGOTTEN_BEAST_498:EYE +"CREATURE:FORGOTTEN_BEAST_498:BRAIN +!CREATURE:FORGOTTEN_BEAST_498:LUNG +"CREATURE:FORGOTTEN_BEAST_498:HEART +"CREATURE:FORGOTTEN_BEAST_498:LIVER + CREATURE:FORGOTTEN_BEAST_498:GUT +$CREATURE:FORGOTTEN_BEAST_498:STOMACH +$CREATURE:FORGOTTEN_BEAST_498:GIZZARD +%CREATURE:FORGOTTEN_BEAST_498:PANCREAS +#CREATURE:FORGOTTEN_BEAST_498:SPLEEN +#CREATURE:FORGOTTEN_BEAST_498:KIDNEY +#CREATURE:FORGOTTEN_BEAST_499:MUSCLE + CREATURE:FORGOTTEN_BEAST_499:EYE +"CREATURE:FORGOTTEN_BEAST_499:BRAIN +!CREATURE:FORGOTTEN_BEAST_499:LUNG +"CREATURE:FORGOTTEN_BEAST_499:HEART +"CREATURE:FORGOTTEN_BEAST_499:LIVER + CREATURE:FORGOTTEN_BEAST_499:GUT +$CREATURE:FORGOTTEN_BEAST_499:STOMACH +$CREATURE:FORGOTTEN_BEAST_499:GIZZARD +%CREATURE:FORGOTTEN_BEAST_499:PANCREAS +#CREATURE:FORGOTTEN_BEAST_499:SPLEEN +#CREATURE:FORGOTTEN_BEAST_499:KIDNEY +#CREATURE:FORGOTTEN_BEAST_501:MUSCLE + CREATURE:FORGOTTEN_BEAST_501:EYE +"CREATURE:FORGOTTEN_BEAST_501:BRAIN +!CREATURE:FORGOTTEN_BEAST_501:LUNG +"CREATURE:FORGOTTEN_BEAST_501:HEART +"CREATURE:FORGOTTEN_BEAST_501:LIVER + CREATURE:FORGOTTEN_BEAST_501:GUT +$CREATURE:FORGOTTEN_BEAST_501:STOMACH +$CREATURE:FORGOTTEN_BEAST_501:GIZZARD +%CREATURE:FORGOTTEN_BEAST_501:PANCREAS +#CREATURE:FORGOTTEN_BEAST_501:SPLEEN +#CREATURE:FORGOTTEN_BEAST_501:KIDNEY +#CREATURE:FORGOTTEN_BEAST_503:MUSCLE + CREATURE:FORGOTTEN_BEAST_503:EYE +"CREATURE:FORGOTTEN_BEAST_503:BRAIN +!CREATURE:FORGOTTEN_BEAST_503:LUNG +"CREATURE:FORGOTTEN_BEAST_503:HEART +"CREATURE:FORGOTTEN_BEAST_503:LIVER + CREATURE:FORGOTTEN_BEAST_503:GUT +$CREATURE:FORGOTTEN_BEAST_503:STOMACH +$CREATURE:FORGOTTEN_BEAST_503:GIZZARD +%CREATURE:FORGOTTEN_BEAST_503:PANCREAS +#CREATURE:FORGOTTEN_BEAST_503:SPLEEN +#CREATURE:FORGOTTEN_BEAST_503:KIDNEY +#CREATURE:FORGOTTEN_BEAST_504:MUSCLE + CREATURE:FORGOTTEN_BEAST_504:EYE +"CREATURE:FORGOTTEN_BEAST_504:BRAIN +!CREATURE:FORGOTTEN_BEAST_504:LUNG +"CREATURE:FORGOTTEN_BEAST_504:HEART +"CREATURE:FORGOTTEN_BEAST_504:LIVER + CREATURE:FORGOTTEN_BEAST_504:GUT +$CREATURE:FORGOTTEN_BEAST_504:STOMACH +$CREATURE:FORGOTTEN_BEAST_504:GIZZARD +%CREATURE:FORGOTTEN_BEAST_504:PANCREAS +#CREATURE:FORGOTTEN_BEAST_504:SPLEEN +#CREATURE:FORGOTTEN_BEAST_504:KIDNEY +#CREATURE:FORGOTTEN_BEAST_505:MUSCLE + CREATURE:FORGOTTEN_BEAST_505:EYE +"CREATURE:FORGOTTEN_BEAST_505:BRAIN +!CREATURE:FORGOTTEN_BEAST_505:LUNG +"CREATURE:FORGOTTEN_BEAST_505:HEART +"CREATURE:FORGOTTEN_BEAST_505:LIVER + CREATURE:FORGOTTEN_BEAST_505:GUT +$CREATURE:FORGOTTEN_BEAST_505:STOMACH +$CREATURE:FORGOTTEN_BEAST_505:GIZZARD +%CREATURE:FORGOTTEN_BEAST_505:PANCREAS +#CREATURE:FORGOTTEN_BEAST_505:SPLEEN +#CREATURE:FORGOTTEN_BEAST_505:KIDNEY +#CREATURE:FORGOTTEN_BEAST_507:MUSCLE + CREATURE:FORGOTTEN_BEAST_507:EYE +"CREATURE:FORGOTTEN_BEAST_507:BRAIN +!CREATURE:FORGOTTEN_BEAST_507:LUNG +"CREATURE:FORGOTTEN_BEAST_507:HEART +"CREATURE:FORGOTTEN_BEAST_507:LIVER + CREATURE:FORGOTTEN_BEAST_507:GUT +$CREATURE:FORGOTTEN_BEAST_507:STOMACH +$CREATURE:FORGOTTEN_BEAST_507:GIZZARD +%CREATURE:FORGOTTEN_BEAST_507:PANCREAS +#CREATURE:FORGOTTEN_BEAST_507:SPLEEN +#CREATURE:FORGOTTEN_BEAST_507:KIDNEY +#CREATURE:FORGOTTEN_BEAST_508:MUSCLE + CREATURE:FORGOTTEN_BEAST_508:EYE +"CREATURE:FORGOTTEN_BEAST_508:BRAIN +!CREATURE:FORGOTTEN_BEAST_508:LUNG +"CREATURE:FORGOTTEN_BEAST_508:HEART +"CREATURE:FORGOTTEN_BEAST_508:LIVER + CREATURE:FORGOTTEN_BEAST_508:GUT +$CREATURE:FORGOTTEN_BEAST_508:STOMACH +$CREATURE:FORGOTTEN_BEAST_508:GIZZARD +%CREATURE:FORGOTTEN_BEAST_508:PANCREAS +#CREATURE:FORGOTTEN_BEAST_508:SPLEEN +#CREATURE:FORGOTTEN_BEAST_508:KIDNEY +#CREATURE:FORGOTTEN_BEAST_510:MUSCLE + CREATURE:FORGOTTEN_BEAST_510:EYE +"CREATURE:FORGOTTEN_BEAST_510:BRAIN +!CREATURE:FORGOTTEN_BEAST_510:LUNG +"CREATURE:FORGOTTEN_BEAST_510:HEART +"CREATURE:FORGOTTEN_BEAST_510:LIVER + CREATURE:FORGOTTEN_BEAST_510:GUT +$CREATURE:FORGOTTEN_BEAST_510:STOMACH +$CREATURE:FORGOTTEN_BEAST_510:GIZZARD +%CREATURE:FORGOTTEN_BEAST_510:PANCREAS +#CREATURE:FORGOTTEN_BEAST_510:SPLEEN +#CREATURE:FORGOTTEN_BEAST_510:KIDNEY +#CREATURE:FORGOTTEN_BEAST_512:MUSCLE + CREATURE:FORGOTTEN_BEAST_512:EYE +"CREATURE:FORGOTTEN_BEAST_512:BRAIN +!CREATURE:FORGOTTEN_BEAST_512:LUNG +"CREATURE:FORGOTTEN_BEAST_512:HEART +"CREATURE:FORGOTTEN_BEAST_512:LIVER + CREATURE:FORGOTTEN_BEAST_512:GUT +$CREATURE:FORGOTTEN_BEAST_512:STOMACH +$CREATURE:FORGOTTEN_BEAST_512:GIZZARD +%CREATURE:FORGOTTEN_BEAST_512:PANCREAS +#CREATURE:FORGOTTEN_BEAST_512:SPLEEN +#CREATURE:FORGOTTEN_BEAST_512:KIDNEY +#CREATURE:FORGOTTEN_BEAST_513:MUSCLE + CREATURE:FORGOTTEN_BEAST_513:EYE +"CREATURE:FORGOTTEN_BEAST_513:BRAIN +!CREATURE:FORGOTTEN_BEAST_513:LUNG +"CREATURE:FORGOTTEN_BEAST_513:HEART +"CREATURE:FORGOTTEN_BEAST_513:LIVER + CREATURE:FORGOTTEN_BEAST_513:GUT +$CREATURE:FORGOTTEN_BEAST_513:STOMACH +$CREATURE:FORGOTTEN_BEAST_513:GIZZARD +%CREATURE:FORGOTTEN_BEAST_513:PANCREAS +#CREATURE:FORGOTTEN_BEAST_513:SPLEEN +#CREATURE:FORGOTTEN_BEAST_513:KIDNEY +#CREATURE:FORGOTTEN_BEAST_514:MUSCLE + CREATURE:FORGOTTEN_BEAST_514:EYE +"CREATURE:FORGOTTEN_BEAST_514:BRAIN +!CREATURE:FORGOTTEN_BEAST_514:LUNG +"CREATURE:FORGOTTEN_BEAST_514:HEART +"CREATURE:FORGOTTEN_BEAST_514:LIVER + CREATURE:FORGOTTEN_BEAST_514:GUT +$CREATURE:FORGOTTEN_BEAST_514:STOMACH +$CREATURE:FORGOTTEN_BEAST_514:GIZZARD +%CREATURE:FORGOTTEN_BEAST_514:PANCREAS +#CREATURE:FORGOTTEN_BEAST_514:SPLEEN +#CREATURE:FORGOTTEN_BEAST_514:KIDNEY +#CREATURE:FORGOTTEN_BEAST_515:MUSCLE + CREATURE:FORGOTTEN_BEAST_515:EYE +"CREATURE:FORGOTTEN_BEAST_515:BRAIN +!CREATURE:FORGOTTEN_BEAST_515:LUNG +"CREATURE:FORGOTTEN_BEAST_515:HEART +"CREATURE:FORGOTTEN_BEAST_515:LIVER + CREATURE:FORGOTTEN_BEAST_515:GUT +$CREATURE:FORGOTTEN_BEAST_515:STOMACH +$CREATURE:FORGOTTEN_BEAST_515:GIZZARD +%CREATURE:FORGOTTEN_BEAST_515:PANCREAS +#CREATURE:FORGOTTEN_BEAST_515:SPLEEN +#CREATURE:FORGOTTEN_BEAST_515:KIDNEY +#CREATURE:FORGOTTEN_BEAST_516:MUSCLE + CREATURE:FORGOTTEN_BEAST_516:EYE +"CREATURE:FORGOTTEN_BEAST_516:BRAIN +!CREATURE:FORGOTTEN_BEAST_516:LUNG +"CREATURE:FORGOTTEN_BEAST_516:HEART +"CREATURE:FORGOTTEN_BEAST_516:LIVER + CREATURE:FORGOTTEN_BEAST_516:GUT +$CREATURE:FORGOTTEN_BEAST_516:STOMACH +$CREATURE:FORGOTTEN_BEAST_516:GIZZARD +%CREATURE:FORGOTTEN_BEAST_516:PANCREAS +#CREATURE:FORGOTTEN_BEAST_516:SPLEEN +#CREATURE:FORGOTTEN_BEAST_516:KIDNEY +#CREATURE:FORGOTTEN_BEAST_517:MUSCLE + CREATURE:FORGOTTEN_BEAST_517:EYE +"CREATURE:FORGOTTEN_BEAST_517:BRAIN +!CREATURE:FORGOTTEN_BEAST_517:LUNG +"CREATURE:FORGOTTEN_BEAST_517:HEART +"CREATURE:FORGOTTEN_BEAST_517:LIVER + CREATURE:FORGOTTEN_BEAST_517:GUT +$CREATURE:FORGOTTEN_BEAST_517:STOMACH +$CREATURE:FORGOTTEN_BEAST_517:GIZZARD +%CREATURE:FORGOTTEN_BEAST_517:PANCREAS +#CREATURE:FORGOTTEN_BEAST_517:SPLEEN +#CREATURE:FORGOTTEN_BEAST_517:KIDNEY +#CREATURE:FORGOTTEN_BEAST_518:MUSCLE + CREATURE:FORGOTTEN_BEAST_518:EYE +"CREATURE:FORGOTTEN_BEAST_518:BRAIN +!CREATURE:FORGOTTEN_BEAST_518:LUNG +"CREATURE:FORGOTTEN_BEAST_518:HEART +"CREATURE:FORGOTTEN_BEAST_518:LIVER + CREATURE:FORGOTTEN_BEAST_518:GUT +$CREATURE:FORGOTTEN_BEAST_518:STOMACH +$CREATURE:FORGOTTEN_BEAST_518:GIZZARD +%CREATURE:FORGOTTEN_BEAST_518:PANCREAS +#CREATURE:FORGOTTEN_BEAST_518:SPLEEN +#CREATURE:FORGOTTEN_BEAST_518:KIDNEY +#CREATURE:FORGOTTEN_BEAST_519:MUSCLE + CREATURE:FORGOTTEN_BEAST_519:EYE +"CREATURE:FORGOTTEN_BEAST_519:BRAIN +!CREATURE:FORGOTTEN_BEAST_519:LUNG +"CREATURE:FORGOTTEN_BEAST_519:HEART +"CREATURE:FORGOTTEN_BEAST_519:LIVER + CREATURE:FORGOTTEN_BEAST_519:GUT +$CREATURE:FORGOTTEN_BEAST_519:STOMACH +$CREATURE:FORGOTTEN_BEAST_519:GIZZARD +%CREATURE:FORGOTTEN_BEAST_519:PANCREAS +#CREATURE:FORGOTTEN_BEAST_519:SPLEEN +#CREATURE:FORGOTTEN_BEAST_519:KIDNEY +#CREATURE:FORGOTTEN_BEAST_520:MUSCLE + CREATURE:FORGOTTEN_BEAST_520:EYE +"CREATURE:FORGOTTEN_BEAST_520:BRAIN +!CREATURE:FORGOTTEN_BEAST_520:LUNG +"CREATURE:FORGOTTEN_BEAST_520:HEART +"CREATURE:FORGOTTEN_BEAST_520:LIVER + CREATURE:FORGOTTEN_BEAST_520:GUT +$CREATURE:FORGOTTEN_BEAST_520:STOMACH +$CREATURE:FORGOTTEN_BEAST_520:GIZZARD +%CREATURE:FORGOTTEN_BEAST_520:PANCREAS +#CREATURE:FORGOTTEN_BEAST_520:SPLEEN +#CREATURE:FORGOTTEN_BEAST_520:KIDNEY +#CREATURE:FORGOTTEN_BEAST_521:MUSCLE + CREATURE:FORGOTTEN_BEAST_521:EYE +"CREATURE:FORGOTTEN_BEAST_521:BRAIN +!CREATURE:FORGOTTEN_BEAST_521:LUNG +"CREATURE:FORGOTTEN_BEAST_521:HEART +"CREATURE:FORGOTTEN_BEAST_521:LIVER + CREATURE:FORGOTTEN_BEAST_521:GUT +$CREATURE:FORGOTTEN_BEAST_521:STOMACH +$CREATURE:FORGOTTEN_BEAST_521:GIZZARD +%CREATURE:FORGOTTEN_BEAST_521:PANCREAS +#CREATURE:FORGOTTEN_BEAST_521:SPLEEN +#CREATURE:FORGOTTEN_BEAST_521:KIDNEY +#CREATURE:FORGOTTEN_BEAST_522:MUSCLE + CREATURE:FORGOTTEN_BEAST_522:EYE +"CREATURE:FORGOTTEN_BEAST_522:BRAIN +!CREATURE:FORGOTTEN_BEAST_522:LUNG +"CREATURE:FORGOTTEN_BEAST_522:HEART +"CREATURE:FORGOTTEN_BEAST_522:LIVER + CREATURE:FORGOTTEN_BEAST_522:GUT +$CREATURE:FORGOTTEN_BEAST_522:STOMACH +$CREATURE:FORGOTTEN_BEAST_522:GIZZARD +%CREATURE:FORGOTTEN_BEAST_522:PANCREAS +#CREATURE:FORGOTTEN_BEAST_522:SPLEEN +#CREATURE:FORGOTTEN_BEAST_522:KIDNEY +#CREATURE:FORGOTTEN_BEAST_523:MUSCLE + CREATURE:FORGOTTEN_BEAST_523:EYE +"CREATURE:FORGOTTEN_BEAST_523:BRAIN +!CREATURE:FORGOTTEN_BEAST_523:LUNG +"CREATURE:FORGOTTEN_BEAST_523:HEART +"CREATURE:FORGOTTEN_BEAST_523:LIVER + CREATURE:FORGOTTEN_BEAST_523:GUT +$CREATURE:FORGOTTEN_BEAST_523:STOMACH +$CREATURE:FORGOTTEN_BEAST_523:GIZZARD +%CREATURE:FORGOTTEN_BEAST_523:PANCREAS +#CREATURE:FORGOTTEN_BEAST_523:SPLEEN +#CREATURE:FORGOTTEN_BEAST_523:KIDNEY +#CREATURE:FORGOTTEN_BEAST_525:MUSCLE + CREATURE:FORGOTTEN_BEAST_525:EYE +"CREATURE:FORGOTTEN_BEAST_525:BRAIN +!CREATURE:FORGOTTEN_BEAST_525:LUNG +"CREATURE:FORGOTTEN_BEAST_525:HEART +"CREATURE:FORGOTTEN_BEAST_525:LIVER + CREATURE:FORGOTTEN_BEAST_525:GUT +$CREATURE:FORGOTTEN_BEAST_525:STOMACH +$CREATURE:FORGOTTEN_BEAST_525:GIZZARD +%CREATURE:FORGOTTEN_BEAST_525:PANCREAS +#CREATURE:FORGOTTEN_BEAST_525:SPLEEN +#CREATURE:FORGOTTEN_BEAST_525:KIDNEY +#CREATURE:FORGOTTEN_BEAST_526:MUSCLE + CREATURE:FORGOTTEN_BEAST_526:EYE +"CREATURE:FORGOTTEN_BEAST_526:BRAIN +!CREATURE:FORGOTTEN_BEAST_526:LUNG +"CREATURE:FORGOTTEN_BEAST_526:HEART +"CREATURE:FORGOTTEN_BEAST_526:LIVER + CREATURE:FORGOTTEN_BEAST_526:GUT +$CREATURE:FORGOTTEN_BEAST_526:STOMACH +$CREATURE:FORGOTTEN_BEAST_526:GIZZARD +%CREATURE:FORGOTTEN_BEAST_526:PANCREAS +#CREATURE:FORGOTTEN_BEAST_526:SPLEEN +#CREATURE:FORGOTTEN_BEAST_526:KIDNEY +#CREATURE:FORGOTTEN_BEAST_527:MUSCLE + CREATURE:FORGOTTEN_BEAST_527:EYE +"CREATURE:FORGOTTEN_BEAST_527:BRAIN +!CREATURE:FORGOTTEN_BEAST_527:LUNG +"CREATURE:FORGOTTEN_BEAST_527:HEART +"CREATURE:FORGOTTEN_BEAST_527:LIVER + CREATURE:FORGOTTEN_BEAST_527:GUT +$CREATURE:FORGOTTEN_BEAST_527:STOMACH +$CREATURE:FORGOTTEN_BEAST_527:GIZZARD +%CREATURE:FORGOTTEN_BEAST_527:PANCREAS +#CREATURE:FORGOTTEN_BEAST_527:SPLEEN +#CREATURE:FORGOTTEN_BEAST_527:KIDNEY +#CREATURE:FORGOTTEN_BEAST_528:MUSCLE + CREATURE:FORGOTTEN_BEAST_528:EYE +"CREATURE:FORGOTTEN_BEAST_528:BRAIN +!CREATURE:FORGOTTEN_BEAST_528:LUNG +"CREATURE:FORGOTTEN_BEAST_528:HEART +"CREATURE:FORGOTTEN_BEAST_528:LIVER + CREATURE:FORGOTTEN_BEAST_528:GUT +$CREATURE:FORGOTTEN_BEAST_528:STOMACH +$CREATURE:FORGOTTEN_BEAST_528:GIZZARD +%CREATURE:FORGOTTEN_BEAST_528:PANCREAS +#CREATURE:FORGOTTEN_BEAST_528:SPLEEN +#CREATURE:FORGOTTEN_BEAST_528:KIDNEY +#CREATURE:FORGOTTEN_BEAST_529:MUSCLE + CREATURE:FORGOTTEN_BEAST_529:EYE +"CREATURE:FORGOTTEN_BEAST_529:BRAIN +!CREATURE:FORGOTTEN_BEAST_529:LUNG +"CREATURE:FORGOTTEN_BEAST_529:HEART +"CREATURE:FORGOTTEN_BEAST_529:LIVER + CREATURE:FORGOTTEN_BEAST_529:GUT +$CREATURE:FORGOTTEN_BEAST_529:STOMACH +$CREATURE:FORGOTTEN_BEAST_529:GIZZARD +%CREATURE:FORGOTTEN_BEAST_529:PANCREAS +#CREATURE:FORGOTTEN_BEAST_529:SPLEEN +#CREATURE:FORGOTTEN_BEAST_529:KIDNEY +#CREATURE:FORGOTTEN_BEAST_530:MUSCLE + CREATURE:FORGOTTEN_BEAST_530:EYE +"CREATURE:FORGOTTEN_BEAST_530:BRAIN +!CREATURE:FORGOTTEN_BEAST_530:LUNG +"CREATURE:FORGOTTEN_BEAST_530:HEART +"CREATURE:FORGOTTEN_BEAST_530:LIVER + CREATURE:FORGOTTEN_BEAST_530:GUT +$CREATURE:FORGOTTEN_BEAST_530:STOMACH +$CREATURE:FORGOTTEN_BEAST_530:GIZZARD +%CREATURE:FORGOTTEN_BEAST_530:PANCREAS +#CREATURE:FORGOTTEN_BEAST_530:SPLEEN +#CREATURE:FORGOTTEN_BEAST_530:KIDNEY +#CREATURE:FORGOTTEN_BEAST_531:MUSCLE + CREATURE:FORGOTTEN_BEAST_531:EYE +"CREATURE:FORGOTTEN_BEAST_531:BRAIN +!CREATURE:FORGOTTEN_BEAST_531:LUNG +"CREATURE:FORGOTTEN_BEAST_531:HEART +"CREATURE:FORGOTTEN_BEAST_531:LIVER + CREATURE:FORGOTTEN_BEAST_531:GUT +$CREATURE:FORGOTTEN_BEAST_531:STOMACH +$CREATURE:FORGOTTEN_BEAST_531:GIZZARD +%CREATURE:FORGOTTEN_BEAST_531:PANCREAS +#CREATURE:FORGOTTEN_BEAST_531:SPLEEN +#CREATURE:FORGOTTEN_BEAST_531:KIDNEY +#CREATURE:FORGOTTEN_BEAST_532:MUSCLE + CREATURE:FORGOTTEN_BEAST_532:EYE +"CREATURE:FORGOTTEN_BEAST_532:BRAIN +!CREATURE:FORGOTTEN_BEAST_532:LUNG +"CREATURE:FORGOTTEN_BEAST_532:HEART +"CREATURE:FORGOTTEN_BEAST_532:LIVER + CREATURE:FORGOTTEN_BEAST_532:GUT +$CREATURE:FORGOTTEN_BEAST_532:STOMACH +$CREATURE:FORGOTTEN_BEAST_532:GIZZARD +%CREATURE:FORGOTTEN_BEAST_532:PANCREAS +#CREATURE:FORGOTTEN_BEAST_532:SPLEEN +#CREATURE:FORGOTTEN_BEAST_532:KIDNEY +#CREATURE:FORGOTTEN_BEAST_533:MUSCLE + CREATURE:FORGOTTEN_BEAST_533:EYE +"CREATURE:FORGOTTEN_BEAST_533:BRAIN +!CREATURE:FORGOTTEN_BEAST_533:LUNG +"CREATURE:FORGOTTEN_BEAST_533:HEART +"CREATURE:FORGOTTEN_BEAST_533:LIVER + CREATURE:FORGOTTEN_BEAST_533:GUT +$CREATURE:FORGOTTEN_BEAST_533:STOMACH +$CREATURE:FORGOTTEN_BEAST_533:GIZZARD +%CREATURE:FORGOTTEN_BEAST_533:PANCREAS +#CREATURE:FORGOTTEN_BEAST_533:SPLEEN +#CREATURE:FORGOTTEN_BEAST_533:KIDNEY +#CREATURE:FORGOTTEN_BEAST_534:MUSCLE + CREATURE:FORGOTTEN_BEAST_534:EYE +"CREATURE:FORGOTTEN_BEAST_534:BRAIN +!CREATURE:FORGOTTEN_BEAST_534:LUNG +"CREATURE:FORGOTTEN_BEAST_534:HEART +"CREATURE:FORGOTTEN_BEAST_534:LIVER + CREATURE:FORGOTTEN_BEAST_534:GUT +$CREATURE:FORGOTTEN_BEAST_534:STOMACH +$CREATURE:FORGOTTEN_BEAST_534:GIZZARD +%CREATURE:FORGOTTEN_BEAST_534:PANCREAS +#CREATURE:FORGOTTEN_BEAST_534:SPLEEN +#CREATURE:FORGOTTEN_BEAST_534:KIDNEY +#CREATURE:FORGOTTEN_BEAST_535:MUSCLE + CREATURE:FORGOTTEN_BEAST_535:EYE +"CREATURE:FORGOTTEN_BEAST_535:BRAIN +!CREATURE:FORGOTTEN_BEAST_535:LUNG +"CREATURE:FORGOTTEN_BEAST_535:HEART +"CREATURE:FORGOTTEN_BEAST_535:LIVER + CREATURE:FORGOTTEN_BEAST_535:GUT +$CREATURE:FORGOTTEN_BEAST_535:STOMACH +$CREATURE:FORGOTTEN_BEAST_535:GIZZARD +%CREATURE:FORGOTTEN_BEAST_535:PANCREAS +#CREATURE:FORGOTTEN_BEAST_535:SPLEEN +#CREATURE:FORGOTTEN_BEAST_535:KIDNEY +#CREATURE:FORGOTTEN_BEAST_536:MUSCLE + CREATURE:FORGOTTEN_BEAST_536:EYE +"CREATURE:FORGOTTEN_BEAST_536:BRAIN +!CREATURE:FORGOTTEN_BEAST_536:LUNG +"CREATURE:FORGOTTEN_BEAST_536:HEART +"CREATURE:FORGOTTEN_BEAST_536:LIVER + CREATURE:FORGOTTEN_BEAST_536:GUT +$CREATURE:FORGOTTEN_BEAST_536:STOMACH +$CREATURE:FORGOTTEN_BEAST_536:GIZZARD +%CREATURE:FORGOTTEN_BEAST_536:PANCREAS +#CREATURE:FORGOTTEN_BEAST_536:SPLEEN +#CREATURE:FORGOTTEN_BEAST_536:KIDNEY +#CREATURE:FORGOTTEN_BEAST_539:MUSCLE + CREATURE:FORGOTTEN_BEAST_539:EYE +"CREATURE:FORGOTTEN_BEAST_539:BRAIN +!CREATURE:FORGOTTEN_BEAST_539:LUNG +"CREATURE:FORGOTTEN_BEAST_539:HEART +"CREATURE:FORGOTTEN_BEAST_539:LIVER + CREATURE:FORGOTTEN_BEAST_539:GUT +$CREATURE:FORGOTTEN_BEAST_539:STOMACH +$CREATURE:FORGOTTEN_BEAST_539:GIZZARD +%CREATURE:FORGOTTEN_BEAST_539:PANCREAS +#CREATURE:FORGOTTEN_BEAST_539:SPLEEN +#CREATURE:FORGOTTEN_BEAST_539:KIDNEY +#CREATURE:FORGOTTEN_BEAST_540:MUSCLE + CREATURE:FORGOTTEN_BEAST_540:EYE +"CREATURE:FORGOTTEN_BEAST_540:BRAIN +!CREATURE:FORGOTTEN_BEAST_540:LUNG +"CREATURE:FORGOTTEN_BEAST_540:HEART +"CREATURE:FORGOTTEN_BEAST_540:LIVER + CREATURE:FORGOTTEN_BEAST_540:GUT +$CREATURE:FORGOTTEN_BEAST_540:STOMACH +$CREATURE:FORGOTTEN_BEAST_540:GIZZARD +%CREATURE:FORGOTTEN_BEAST_540:PANCREAS +#CREATURE:FORGOTTEN_BEAST_540:SPLEEN +#CREATURE:FORGOTTEN_BEAST_540:KIDNEY +#CREATURE:FORGOTTEN_BEAST_541:MUSCLE + CREATURE:FORGOTTEN_BEAST_541:EYE +"CREATURE:FORGOTTEN_BEAST_541:BRAIN +!CREATURE:FORGOTTEN_BEAST_541:LUNG +"CREATURE:FORGOTTEN_BEAST_541:HEART +"CREATURE:FORGOTTEN_BEAST_541:LIVER + CREATURE:FORGOTTEN_BEAST_541:GUT +$CREATURE:FORGOTTEN_BEAST_541:STOMACH +$CREATURE:FORGOTTEN_BEAST_541:GIZZARD +%CREATURE:FORGOTTEN_BEAST_541:PANCREAS +#CREATURE:FORGOTTEN_BEAST_541:SPLEEN +#CREATURE:FORGOTTEN_BEAST_541:KIDNEY +#CREATURE:FORGOTTEN_BEAST_543:MUSCLE + CREATURE:FORGOTTEN_BEAST_543:EYE +"CREATURE:FORGOTTEN_BEAST_543:BRAIN +!CREATURE:FORGOTTEN_BEAST_543:LUNG +"CREATURE:FORGOTTEN_BEAST_543:HEART +"CREATURE:FORGOTTEN_BEAST_543:LIVER + CREATURE:FORGOTTEN_BEAST_543:GUT +$CREATURE:FORGOTTEN_BEAST_543:STOMACH +$CREATURE:FORGOTTEN_BEAST_543:GIZZARD +%CREATURE:FORGOTTEN_BEAST_543:PANCREAS +#CREATURE:FORGOTTEN_BEAST_543:SPLEEN +#CREATURE:FORGOTTEN_BEAST_543:KIDNEY +#CREATURE:FORGOTTEN_BEAST_545:MUSCLE + CREATURE:FORGOTTEN_BEAST_545:EYE +"CREATURE:FORGOTTEN_BEAST_545:BRAIN +!CREATURE:FORGOTTEN_BEAST_545:LUNG +"CREATURE:FORGOTTEN_BEAST_545:HEART +"CREATURE:FORGOTTEN_BEAST_545:LIVER + CREATURE:FORGOTTEN_BEAST_545:GUT +$CREATURE:FORGOTTEN_BEAST_545:STOMACH +$CREATURE:FORGOTTEN_BEAST_545:GIZZARD +%CREATURE:FORGOTTEN_BEAST_545:PANCREAS +#CREATURE:FORGOTTEN_BEAST_545:SPLEEN +#CREATURE:FORGOTTEN_BEAST_545:KIDNEY +#CREATURE:FORGOTTEN_BEAST_546:MUSCLE + CREATURE:FORGOTTEN_BEAST_546:EYE +"CREATURE:FORGOTTEN_BEAST_546:BRAIN +!CREATURE:FORGOTTEN_BEAST_546:LUNG +"CREATURE:FORGOTTEN_BEAST_546:HEART +"CREATURE:FORGOTTEN_BEAST_546:LIVER + CREATURE:FORGOTTEN_BEAST_546:GUT +$CREATURE:FORGOTTEN_BEAST_546:STOMACH +$CREATURE:FORGOTTEN_BEAST_546:GIZZARD +%CREATURE:FORGOTTEN_BEAST_546:PANCREAS +#CREATURE:FORGOTTEN_BEAST_546:SPLEEN +#CREATURE:FORGOTTEN_BEAST_546:KIDNEY +#CREATURE:FORGOTTEN_BEAST_547:MUSCLE + CREATURE:FORGOTTEN_BEAST_547:EYE +"CREATURE:FORGOTTEN_BEAST_547:BRAIN +!CREATURE:FORGOTTEN_BEAST_547:LUNG +"CREATURE:FORGOTTEN_BEAST_547:HEART +"CREATURE:FORGOTTEN_BEAST_547:LIVER + CREATURE:FORGOTTEN_BEAST_547:GUT +$CREATURE:FORGOTTEN_BEAST_547:STOMACH +$CREATURE:FORGOTTEN_BEAST_547:GIZZARD +%CREATURE:FORGOTTEN_BEAST_547:PANCREAS +#CREATURE:FORGOTTEN_BEAST_547:SPLEEN +#CREATURE:FORGOTTEN_BEAST_547:KIDNEY +#CREATURE:FORGOTTEN_BEAST_548:MUSCLE + CREATURE:FORGOTTEN_BEAST_548:EYE +"CREATURE:FORGOTTEN_BEAST_548:BRAIN +!CREATURE:FORGOTTEN_BEAST_548:LUNG +"CREATURE:FORGOTTEN_BEAST_548:HEART +"CREATURE:FORGOTTEN_BEAST_548:LIVER + CREATURE:FORGOTTEN_BEAST_548:GUT +$CREATURE:FORGOTTEN_BEAST_548:STOMACH +$CREATURE:FORGOTTEN_BEAST_548:GIZZARD +%CREATURE:FORGOTTEN_BEAST_548:PANCREAS +#CREATURE:FORGOTTEN_BEAST_548:SPLEEN +#CREATURE:FORGOTTEN_BEAST_548:KIDNEY +#CREATURE:FORGOTTEN_BEAST_549:MUSCLE + CREATURE:FORGOTTEN_BEAST_549:EYE +"CREATURE:FORGOTTEN_BEAST_549:BRAIN +!CREATURE:FORGOTTEN_BEAST_549:LUNG +"CREATURE:FORGOTTEN_BEAST_549:HEART +"CREATURE:FORGOTTEN_BEAST_549:LIVER + CREATURE:FORGOTTEN_BEAST_549:GUT +$CREATURE:FORGOTTEN_BEAST_549:STOMACH +$CREATURE:FORGOTTEN_BEAST_549:GIZZARD +%CREATURE:FORGOTTEN_BEAST_549:PANCREAS +#CREATURE:FORGOTTEN_BEAST_549:SPLEEN +#CREATURE:FORGOTTEN_BEAST_549:KIDNEY +#CREATURE:FORGOTTEN_BEAST_550:MUSCLE + CREATURE:FORGOTTEN_BEAST_550:EYE +"CREATURE:FORGOTTEN_BEAST_550:BRAIN +!CREATURE:FORGOTTEN_BEAST_550:LUNG +"CREATURE:FORGOTTEN_BEAST_550:HEART +"CREATURE:FORGOTTEN_BEAST_550:LIVER + CREATURE:FORGOTTEN_BEAST_550:GUT +$CREATURE:FORGOTTEN_BEAST_550:STOMACH +$CREATURE:FORGOTTEN_BEAST_550:GIZZARD +%CREATURE:FORGOTTEN_BEAST_550:PANCREAS +#CREATURE:FORGOTTEN_BEAST_550:SPLEEN +#CREATURE:FORGOTTEN_BEAST_550:KIDNEY +#CREATURE:FORGOTTEN_BEAST_551:MUSCLE + CREATURE:FORGOTTEN_BEAST_551:EYE +"CREATURE:FORGOTTEN_BEAST_551:BRAIN +!CREATURE:FORGOTTEN_BEAST_551:LUNG +"CREATURE:FORGOTTEN_BEAST_551:HEART +"CREATURE:FORGOTTEN_BEAST_551:LIVER + CREATURE:FORGOTTEN_BEAST_551:GUT +$CREATURE:FORGOTTEN_BEAST_551:STOMACH +$CREATURE:FORGOTTEN_BEAST_551:GIZZARD +%CREATURE:FORGOTTEN_BEAST_551:PANCREAS +#CREATURE:FORGOTTEN_BEAST_551:SPLEEN +#CREATURE:FORGOTTEN_BEAST_551:KIDNEY +#CREATURE:FORGOTTEN_BEAST_554:MUSCLE + CREATURE:FORGOTTEN_BEAST_554:EYE +"CREATURE:FORGOTTEN_BEAST_554:BRAIN +!CREATURE:FORGOTTEN_BEAST_554:LUNG +"CREATURE:FORGOTTEN_BEAST_554:HEART +"CREATURE:FORGOTTEN_BEAST_554:LIVER + CREATURE:FORGOTTEN_BEAST_554:GUT +$CREATURE:FORGOTTEN_BEAST_554:STOMACH +$CREATURE:FORGOTTEN_BEAST_554:GIZZARD +%CREATURE:FORGOTTEN_BEAST_554:PANCREAS +#CREATURE:FORGOTTEN_BEAST_554:SPLEEN +#CREATURE:FORGOTTEN_BEAST_554:KIDNEY +#CREATURE:FORGOTTEN_BEAST_555:MUSCLE + CREATURE:FORGOTTEN_BEAST_555:EYE +"CREATURE:FORGOTTEN_BEAST_555:BRAIN +!CREATURE:FORGOTTEN_BEAST_555:LUNG +"CREATURE:FORGOTTEN_BEAST_555:HEART +"CREATURE:FORGOTTEN_BEAST_555:LIVER + CREATURE:FORGOTTEN_BEAST_555:GUT +$CREATURE:FORGOTTEN_BEAST_555:STOMACH +$CREATURE:FORGOTTEN_BEAST_555:GIZZARD +%CREATURE:FORGOTTEN_BEAST_555:PANCREAS +#CREATURE:FORGOTTEN_BEAST_555:SPLEEN +#CREATURE:FORGOTTEN_BEAST_555:KIDNEY +#CREATURE:FORGOTTEN_BEAST_556:MUSCLE + CREATURE:FORGOTTEN_BEAST_556:EYE +"CREATURE:FORGOTTEN_BEAST_556:BRAIN +!CREATURE:FORGOTTEN_BEAST_556:LUNG +"CREATURE:FORGOTTEN_BEAST_556:HEART +"CREATURE:FORGOTTEN_BEAST_556:LIVER + CREATURE:FORGOTTEN_BEAST_556:GUT +$CREATURE:FORGOTTEN_BEAST_556:STOMACH +$CREATURE:FORGOTTEN_BEAST_556:GIZZARD +%CREATURE:FORGOTTEN_BEAST_556:PANCREAS +#CREATURE:FORGOTTEN_BEAST_556:SPLEEN +#CREATURE:FORGOTTEN_BEAST_556:KIDNEY +#CREATURE:FORGOTTEN_BEAST_557:MUSCLE + CREATURE:FORGOTTEN_BEAST_557:EYE +"CREATURE:FORGOTTEN_BEAST_557:BRAIN +!CREATURE:FORGOTTEN_BEAST_557:LUNG +"CREATURE:FORGOTTEN_BEAST_557:HEART +"CREATURE:FORGOTTEN_BEAST_557:LIVER + CREATURE:FORGOTTEN_BEAST_557:GUT +$CREATURE:FORGOTTEN_BEAST_557:STOMACH +$CREATURE:FORGOTTEN_BEAST_557:GIZZARD +%CREATURE:FORGOTTEN_BEAST_557:PANCREAS +#CREATURE:FORGOTTEN_BEAST_557:SPLEEN +#CREATURE:FORGOTTEN_BEAST_557:KIDNEY +#CREATURE:FORGOTTEN_BEAST_561:MUSCLE + CREATURE:FORGOTTEN_BEAST_561:EYE +"CREATURE:FORGOTTEN_BEAST_561:BRAIN +!CREATURE:FORGOTTEN_BEAST_561:LUNG +"CREATURE:FORGOTTEN_BEAST_561:HEART +"CREATURE:FORGOTTEN_BEAST_561:LIVER + CREATURE:FORGOTTEN_BEAST_561:GUT +$CREATURE:FORGOTTEN_BEAST_561:STOMACH +$CREATURE:FORGOTTEN_BEAST_561:GIZZARD +%CREATURE:FORGOTTEN_BEAST_561:PANCREAS +#CREATURE:FORGOTTEN_BEAST_561:SPLEEN +#CREATURE:FORGOTTEN_BEAST_561:KIDNEY +#CREATURE:FORGOTTEN_BEAST_562:MUSCLE + CREATURE:FORGOTTEN_BEAST_562:EYE +"CREATURE:FORGOTTEN_BEAST_562:BRAIN +!CREATURE:FORGOTTEN_BEAST_562:LUNG +"CREATURE:FORGOTTEN_BEAST_562:HEART +"CREATURE:FORGOTTEN_BEAST_562:LIVER + CREATURE:FORGOTTEN_BEAST_562:GUT +$CREATURE:FORGOTTEN_BEAST_562:STOMACH +$CREATURE:FORGOTTEN_BEAST_562:GIZZARD +%CREATURE:FORGOTTEN_BEAST_562:PANCREAS +#CREATURE:FORGOTTEN_BEAST_562:SPLEEN +#CREATURE:FORGOTTEN_BEAST_562:KIDNEY +#CREATURE:FORGOTTEN_BEAST_564:MUSCLE + CREATURE:FORGOTTEN_BEAST_564:EYE +"CREATURE:FORGOTTEN_BEAST_564:BRAIN +!CREATURE:FORGOTTEN_BEAST_564:LUNG +"CREATURE:FORGOTTEN_BEAST_564:HEART +"CREATURE:FORGOTTEN_BEAST_564:LIVER + CREATURE:FORGOTTEN_BEAST_564:GUT +$CREATURE:FORGOTTEN_BEAST_564:STOMACH +$CREATURE:FORGOTTEN_BEAST_564:GIZZARD +%CREATURE:FORGOTTEN_BEAST_564:PANCREAS +#CREATURE:FORGOTTEN_BEAST_564:SPLEEN +#CREATURE:FORGOTTEN_BEAST_564:KIDNEY +#CREATURE:FORGOTTEN_BEAST_569:MUSCLE + CREATURE:FORGOTTEN_BEAST_569:EYE +"CREATURE:FORGOTTEN_BEAST_569:BRAIN +!CREATURE:FORGOTTEN_BEAST_569:LUNG +"CREATURE:FORGOTTEN_BEAST_569:HEART +"CREATURE:FORGOTTEN_BEAST_569:LIVER + CREATURE:FORGOTTEN_BEAST_569:GUT +$CREATURE:FORGOTTEN_BEAST_569:STOMACH +$CREATURE:FORGOTTEN_BEAST_569:GIZZARD +%CREATURE:FORGOTTEN_BEAST_569:PANCREAS +#CREATURE:FORGOTTEN_BEAST_569:SPLEEN +#CREATURE:FORGOTTEN_BEAST_569:KIDNEY +#CREATURE:FORGOTTEN_BEAST_570:MUSCLE + CREATURE:FORGOTTEN_BEAST_570:EYE +"CREATURE:FORGOTTEN_BEAST_570:BRAIN +!CREATURE:FORGOTTEN_BEAST_570:LUNG +"CREATURE:FORGOTTEN_BEAST_570:HEART +"CREATURE:FORGOTTEN_BEAST_570:LIVER + CREATURE:FORGOTTEN_BEAST_570:GUT +$CREATURE:FORGOTTEN_BEAST_570:STOMACH +$CREATURE:FORGOTTEN_BEAST_570:GIZZARD +%CREATURE:FORGOTTEN_BEAST_570:PANCREAS +#CREATURE:FORGOTTEN_BEAST_570:SPLEEN +#CREATURE:FORGOTTEN_BEAST_570:KIDNEY +#CREATURE:FORGOTTEN_BEAST_571:MUSCLE + CREATURE:FORGOTTEN_BEAST_571:EYE +"CREATURE:FORGOTTEN_BEAST_571:BRAIN +!CREATURE:FORGOTTEN_BEAST_571:LUNG +"CREATURE:FORGOTTEN_BEAST_571:HEART +"CREATURE:FORGOTTEN_BEAST_571:LIVER + CREATURE:FORGOTTEN_BEAST_571:GUT +$CREATURE:FORGOTTEN_BEAST_571:STOMACH +$CREATURE:FORGOTTEN_BEAST_571:GIZZARD +%CREATURE:FORGOTTEN_BEAST_571:PANCREAS +#CREATURE:FORGOTTEN_BEAST_571:SPLEEN +#CREATURE:FORGOTTEN_BEAST_571:KIDNEY +#CREATURE:FORGOTTEN_BEAST_572:MUSCLE + CREATURE:FORGOTTEN_BEAST_572:EYE +"CREATURE:FORGOTTEN_BEAST_572:BRAIN +!CREATURE:FORGOTTEN_BEAST_572:LUNG +"CREATURE:FORGOTTEN_BEAST_572:HEART +"CREATURE:FORGOTTEN_BEAST_572:LIVER + CREATURE:FORGOTTEN_BEAST_572:GUT +$CREATURE:FORGOTTEN_BEAST_572:STOMACH +$CREATURE:FORGOTTEN_BEAST_572:GIZZARD +%CREATURE:FORGOTTEN_BEAST_572:PANCREAS +#CREATURE:FORGOTTEN_BEAST_572:SPLEEN +#CREATURE:FORGOTTEN_BEAST_572:KIDNEY +#CREATURE:FORGOTTEN_BEAST_574:MUSCLE + CREATURE:FORGOTTEN_BEAST_574:EYE +"CREATURE:FORGOTTEN_BEAST_574:BRAIN +!CREATURE:FORGOTTEN_BEAST_574:LUNG +"CREATURE:FORGOTTEN_BEAST_574:HEART +"CREATURE:FORGOTTEN_BEAST_574:LIVER + CREATURE:FORGOTTEN_BEAST_574:GUT +$CREATURE:FORGOTTEN_BEAST_574:STOMACH +$CREATURE:FORGOTTEN_BEAST_574:GIZZARD +%CREATURE:FORGOTTEN_BEAST_574:PANCREAS +#CREATURE:FORGOTTEN_BEAST_574:SPLEEN +#CREATURE:FORGOTTEN_BEAST_574:KIDNEY +#CREATURE:FORGOTTEN_BEAST_576:MUSCLE + CREATURE:FORGOTTEN_BEAST_576:EYE +"CREATURE:FORGOTTEN_BEAST_576:BRAIN +!CREATURE:FORGOTTEN_BEAST_576:LUNG +"CREATURE:FORGOTTEN_BEAST_576:HEART +"CREATURE:FORGOTTEN_BEAST_576:LIVER + CREATURE:FORGOTTEN_BEAST_576:GUT +$CREATURE:FORGOTTEN_BEAST_576:STOMACH +$CREATURE:FORGOTTEN_BEAST_576:GIZZARD +%CREATURE:FORGOTTEN_BEAST_576:PANCREAS +#CREATURE:FORGOTTEN_BEAST_576:SPLEEN +#CREATURE:FORGOTTEN_BEAST_576:KIDNEY +#CREATURE:FORGOTTEN_BEAST_578:MUSCLE + CREATURE:FORGOTTEN_BEAST_578:EYE +"CREATURE:FORGOTTEN_BEAST_578:BRAIN +!CREATURE:FORGOTTEN_BEAST_578:LUNG +"CREATURE:FORGOTTEN_BEAST_578:HEART +"CREATURE:FORGOTTEN_BEAST_578:LIVER + CREATURE:FORGOTTEN_BEAST_578:GUT +$CREATURE:FORGOTTEN_BEAST_578:STOMACH +$CREATURE:FORGOTTEN_BEAST_578:GIZZARD +%CREATURE:FORGOTTEN_BEAST_578:PANCREAS +#CREATURE:FORGOTTEN_BEAST_578:SPLEEN +#CREATURE:FORGOTTEN_BEAST_578:KIDNEY +#CREATURE:FORGOTTEN_BEAST_579:MUSCLE + CREATURE:FORGOTTEN_BEAST_579:EYE +"CREATURE:FORGOTTEN_BEAST_579:BRAIN +!CREATURE:FORGOTTEN_BEAST_579:LUNG +"CREATURE:FORGOTTEN_BEAST_579:HEART +"CREATURE:FORGOTTEN_BEAST_579:LIVER + CREATURE:FORGOTTEN_BEAST_579:GUT +$CREATURE:FORGOTTEN_BEAST_579:STOMACH +$CREATURE:FORGOTTEN_BEAST_579:GIZZARD +%CREATURE:FORGOTTEN_BEAST_579:PANCREAS +#CREATURE:FORGOTTEN_BEAST_579:SPLEEN +#CREATURE:FORGOTTEN_BEAST_579:KIDNEY +#CREATURE:FORGOTTEN_BEAST_580:MUSCLE + CREATURE:FORGOTTEN_BEAST_580:EYE +"CREATURE:FORGOTTEN_BEAST_580:BRAIN +!CREATURE:FORGOTTEN_BEAST_580:LUNG +"CREATURE:FORGOTTEN_BEAST_580:HEART +"CREATURE:FORGOTTEN_BEAST_580:LIVER + CREATURE:FORGOTTEN_BEAST_580:GUT +$CREATURE:FORGOTTEN_BEAST_580:STOMACH +$CREATURE:FORGOTTEN_BEAST_580:GIZZARD +%CREATURE:FORGOTTEN_BEAST_580:PANCREAS +#CREATURE:FORGOTTEN_BEAST_580:SPLEEN +#CREATURE:FORGOTTEN_BEAST_580:KIDNEY +#CREATURE:FORGOTTEN_BEAST_581:MUSCLE + CREATURE:FORGOTTEN_BEAST_581:EYE +"CREATURE:FORGOTTEN_BEAST_581:BRAIN +!CREATURE:FORGOTTEN_BEAST_581:LUNG +"CREATURE:FORGOTTEN_BEAST_581:HEART +"CREATURE:FORGOTTEN_BEAST_581:LIVER + CREATURE:FORGOTTEN_BEAST_581:GUT +$CREATURE:FORGOTTEN_BEAST_581:STOMACH +$CREATURE:FORGOTTEN_BEAST_581:GIZZARD +%CREATURE:FORGOTTEN_BEAST_581:PANCREAS +#CREATURE:FORGOTTEN_BEAST_581:SPLEEN +#CREATURE:FORGOTTEN_BEAST_581:KIDNEY +#CREATURE:FORGOTTEN_BEAST_583:MUSCLE + CREATURE:FORGOTTEN_BEAST_583:EYE +"CREATURE:FORGOTTEN_BEAST_583:BRAIN +!CREATURE:FORGOTTEN_BEAST_583:LUNG +"CREATURE:FORGOTTEN_BEAST_583:HEART +"CREATURE:FORGOTTEN_BEAST_583:LIVER + CREATURE:FORGOTTEN_BEAST_583:GUT +$CREATURE:FORGOTTEN_BEAST_583:STOMACH +$CREATURE:FORGOTTEN_BEAST_583:GIZZARD +%CREATURE:FORGOTTEN_BEAST_583:PANCREAS +#CREATURE:FORGOTTEN_BEAST_583:SPLEEN +#CREATURE:FORGOTTEN_BEAST_583:KIDNEY +#CREATURE:FORGOTTEN_BEAST_584:MUSCLE + CREATURE:FORGOTTEN_BEAST_584:EYE +"CREATURE:FORGOTTEN_BEAST_584:BRAIN +!CREATURE:FORGOTTEN_BEAST_584:LUNG +"CREATURE:FORGOTTEN_BEAST_584:HEART +"CREATURE:FORGOTTEN_BEAST_584:LIVER + CREATURE:FORGOTTEN_BEAST_584:GUT +$CREATURE:FORGOTTEN_BEAST_584:STOMACH +$CREATURE:FORGOTTEN_BEAST_584:GIZZARD +%CREATURE:FORGOTTEN_BEAST_584:PANCREAS +#CREATURE:FORGOTTEN_BEAST_584:SPLEEN +#CREATURE:FORGOTTEN_BEAST_584:KIDNEY +#CREATURE:FORGOTTEN_BEAST_586:MUSCLE + CREATURE:FORGOTTEN_BEAST_586:EYE +"CREATURE:FORGOTTEN_BEAST_586:BRAIN +!CREATURE:FORGOTTEN_BEAST_586:LUNG +"CREATURE:FORGOTTEN_BEAST_586:HEART +"CREATURE:FORGOTTEN_BEAST_586:LIVER + CREATURE:FORGOTTEN_BEAST_586:GUT +$CREATURE:FORGOTTEN_BEAST_586:STOMACH +$CREATURE:FORGOTTEN_BEAST_586:GIZZARD +%CREATURE:FORGOTTEN_BEAST_586:PANCREAS +#CREATURE:FORGOTTEN_BEAST_586:SPLEEN +#CREATURE:FORGOTTEN_BEAST_586:KIDNEY +#CREATURE:FORGOTTEN_BEAST_588:MUSCLE + CREATURE:FORGOTTEN_BEAST_588:EYE +"CREATURE:FORGOTTEN_BEAST_588:BRAIN +!CREATURE:FORGOTTEN_BEAST_588:LUNG +"CREATURE:FORGOTTEN_BEAST_588:HEART +"CREATURE:FORGOTTEN_BEAST_588:LIVER + CREATURE:FORGOTTEN_BEAST_588:GUT +$CREATURE:FORGOTTEN_BEAST_588:STOMACH +$CREATURE:FORGOTTEN_BEAST_588:GIZZARD +%CREATURE:FORGOTTEN_BEAST_588:PANCREAS +#CREATURE:FORGOTTEN_BEAST_588:SPLEEN +#CREATURE:FORGOTTEN_BEAST_588:KIDNEY +#CREATURE:FORGOTTEN_BEAST_589:MUSCLE + CREATURE:FORGOTTEN_BEAST_589:EYE +"CREATURE:FORGOTTEN_BEAST_589:BRAIN +!CREATURE:FORGOTTEN_BEAST_589:LUNG +"CREATURE:FORGOTTEN_BEAST_589:HEART +"CREATURE:FORGOTTEN_BEAST_589:LIVER + CREATURE:FORGOTTEN_BEAST_589:GUT +$CREATURE:FORGOTTEN_BEAST_589:STOMACH +$CREATURE:FORGOTTEN_BEAST_589:GIZZARD +%CREATURE:FORGOTTEN_BEAST_589:PANCREAS +#CREATURE:FORGOTTEN_BEAST_589:SPLEEN +#CREATURE:FORGOTTEN_BEAST_589:KIDNEY +#CREATURE:FORGOTTEN_BEAST_590:MUSCLE + CREATURE:FORGOTTEN_BEAST_590:EYE +"CREATURE:FORGOTTEN_BEAST_590:BRAIN +!CREATURE:FORGOTTEN_BEAST_590:LUNG +"CREATURE:FORGOTTEN_BEAST_590:HEART +"CREATURE:FORGOTTEN_BEAST_590:LIVER + CREATURE:FORGOTTEN_BEAST_590:GUT +$CREATURE:FORGOTTEN_BEAST_590:STOMACH +$CREATURE:FORGOTTEN_BEAST_590:GIZZARD +%CREATURE:FORGOTTEN_BEAST_590:PANCREAS +#CREATURE:FORGOTTEN_BEAST_590:SPLEEN +#CREATURE:FORGOTTEN_BEAST_590:KIDNEY +#CREATURE:FORGOTTEN_BEAST_592:MUSCLE + CREATURE:FORGOTTEN_BEAST_592:EYE +"CREATURE:FORGOTTEN_BEAST_592:BRAIN +!CREATURE:FORGOTTEN_BEAST_592:LUNG +"CREATURE:FORGOTTEN_BEAST_592:HEART +"CREATURE:FORGOTTEN_BEAST_592:LIVER + CREATURE:FORGOTTEN_BEAST_592:GUT +$CREATURE:FORGOTTEN_BEAST_592:STOMACH +$CREATURE:FORGOTTEN_BEAST_592:GIZZARD +%CREATURE:FORGOTTEN_BEAST_592:PANCREAS +#CREATURE:FORGOTTEN_BEAST_592:SPLEEN +#CREATURE:FORGOTTEN_BEAST_592:KIDNEY +#CREATURE:FORGOTTEN_BEAST_593:MUSCLE + CREATURE:FORGOTTEN_BEAST_593:EYE +"CREATURE:FORGOTTEN_BEAST_593:BRAIN +!CREATURE:FORGOTTEN_BEAST_593:LUNG +"CREATURE:FORGOTTEN_BEAST_593:HEART +"CREATURE:FORGOTTEN_BEAST_593:LIVER + CREATURE:FORGOTTEN_BEAST_593:GUT +$CREATURE:FORGOTTEN_BEAST_593:STOMACH +$CREATURE:FORGOTTEN_BEAST_593:GIZZARD +%CREATURE:FORGOTTEN_BEAST_593:PANCREAS +#CREATURE:FORGOTTEN_BEAST_593:SPLEEN +#CREATURE:FORGOTTEN_BEAST_593:KIDNEY +#CREATURE:FORGOTTEN_BEAST_594:MUSCLE + CREATURE:FORGOTTEN_BEAST_594:EYE +"CREATURE:FORGOTTEN_BEAST_594:BRAIN +!CREATURE:FORGOTTEN_BEAST_594:LUNG +"CREATURE:FORGOTTEN_BEAST_594:HEART +"CREATURE:FORGOTTEN_BEAST_594:LIVER + CREATURE:FORGOTTEN_BEAST_594:GUT +$CREATURE:FORGOTTEN_BEAST_594:STOMACH +$CREATURE:FORGOTTEN_BEAST_594:GIZZARD +%CREATURE:FORGOTTEN_BEAST_594:PANCREAS +#CREATURE:FORGOTTEN_BEAST_594:SPLEEN +#CREATURE:FORGOTTEN_BEAST_594:KIDNEY +#CREATURE:FORGOTTEN_BEAST_595:MUSCLE + CREATURE:FORGOTTEN_BEAST_595:EYE +"CREATURE:FORGOTTEN_BEAST_595:BRAIN +!CREATURE:FORGOTTEN_BEAST_595:LUNG +"CREATURE:FORGOTTEN_BEAST_595:HEART +"CREATURE:FORGOTTEN_BEAST_595:LIVER + CREATURE:FORGOTTEN_BEAST_595:GUT +$CREATURE:FORGOTTEN_BEAST_595:STOMACH +$CREATURE:FORGOTTEN_BEAST_595:GIZZARD +%CREATURE:FORGOTTEN_BEAST_595:PANCREAS +#CREATURE:FORGOTTEN_BEAST_595:SPLEEN +#CREATURE:FORGOTTEN_BEAST_595:KIDNEY +#CREATURE:FORGOTTEN_BEAST_596:MUSCLE + CREATURE:FORGOTTEN_BEAST_596:EYE +"CREATURE:FORGOTTEN_BEAST_596:BRAIN +!CREATURE:FORGOTTEN_BEAST_596:LUNG +"CREATURE:FORGOTTEN_BEAST_596:HEART +"CREATURE:FORGOTTEN_BEAST_596:LIVER + CREATURE:FORGOTTEN_BEAST_596:GUT +$CREATURE:FORGOTTEN_BEAST_596:STOMACH +$CREATURE:FORGOTTEN_BEAST_596:GIZZARD +%CREATURE:FORGOTTEN_BEAST_596:PANCREAS +#CREATURE:FORGOTTEN_BEAST_596:SPLEEN +#CREATURE:FORGOTTEN_BEAST_596:KIDNEY +#CREATURE:FORGOTTEN_BEAST_597:MUSCLE + CREATURE:FORGOTTEN_BEAST_597:EYE +"CREATURE:FORGOTTEN_BEAST_597:BRAIN +!CREATURE:FORGOTTEN_BEAST_597:LUNG +"CREATURE:FORGOTTEN_BEAST_597:HEART +"CREATURE:FORGOTTEN_BEAST_597:LIVER + CREATURE:FORGOTTEN_BEAST_597:GUT +$CREATURE:FORGOTTEN_BEAST_597:STOMACH +$CREATURE:FORGOTTEN_BEAST_597:GIZZARD +%CREATURE:FORGOTTEN_BEAST_597:PANCREAS +#CREATURE:FORGOTTEN_BEAST_597:SPLEEN +#CREATURE:FORGOTTEN_BEAST_597:KIDNEY +#CREATURE:FORGOTTEN_BEAST_599:MUSCLE + CREATURE:FORGOTTEN_BEAST_599:EYE +"CREATURE:FORGOTTEN_BEAST_599:BRAIN +!CREATURE:FORGOTTEN_BEAST_599:LUNG +"CREATURE:FORGOTTEN_BEAST_599:HEART +"CREATURE:FORGOTTEN_BEAST_599:LIVER + CREATURE:FORGOTTEN_BEAST_599:GUT +$CREATURE:FORGOTTEN_BEAST_599:STOMACH +$CREATURE:FORGOTTEN_BEAST_599:GIZZARD +%CREATURE:FORGOTTEN_BEAST_599:PANCREAS +#CREATURE:FORGOTTEN_BEAST_599:SPLEEN +#CREATURE:FORGOTTEN_BEAST_599:KIDNEY +#CREATURE:FORGOTTEN_BEAST_600:MUSCLE + CREATURE:FORGOTTEN_BEAST_600:EYE +"CREATURE:FORGOTTEN_BEAST_600:BRAIN +!CREATURE:FORGOTTEN_BEAST_600:LUNG +"CREATURE:FORGOTTEN_BEAST_600:HEART +"CREATURE:FORGOTTEN_BEAST_600:LIVER + CREATURE:FORGOTTEN_BEAST_600:GUT +$CREATURE:FORGOTTEN_BEAST_600:STOMACH +$CREATURE:FORGOTTEN_BEAST_600:GIZZARD +%CREATURE:FORGOTTEN_BEAST_600:PANCREAS +#CREATURE:FORGOTTEN_BEAST_600:SPLEEN +#CREATURE:FORGOTTEN_BEAST_600:KIDNEY +#CREATURE:FORGOTTEN_BEAST_601:MUSCLE + CREATURE:FORGOTTEN_BEAST_601:EYE +"CREATURE:FORGOTTEN_BEAST_601:BRAIN +!CREATURE:FORGOTTEN_BEAST_601:LUNG +"CREATURE:FORGOTTEN_BEAST_601:HEART +"CREATURE:FORGOTTEN_BEAST_601:LIVER + CREATURE:FORGOTTEN_BEAST_601:GUT +$CREATURE:FORGOTTEN_BEAST_601:STOMACH +$CREATURE:FORGOTTEN_BEAST_601:GIZZARD +%CREATURE:FORGOTTEN_BEAST_601:PANCREAS +#CREATURE:FORGOTTEN_BEAST_601:SPLEEN +#CREATURE:FORGOTTEN_BEAST_601:KIDNEY +#CREATURE:FORGOTTEN_BEAST_603:MUSCLE + CREATURE:FORGOTTEN_BEAST_603:EYE +"CREATURE:FORGOTTEN_BEAST_603:BRAIN +!CREATURE:FORGOTTEN_BEAST_603:LUNG +"CREATURE:FORGOTTEN_BEAST_603:HEART +"CREATURE:FORGOTTEN_BEAST_603:LIVER + CREATURE:FORGOTTEN_BEAST_603:GUT +$CREATURE:FORGOTTEN_BEAST_603:STOMACH +$CREATURE:FORGOTTEN_BEAST_603:GIZZARD +%CREATURE:FORGOTTEN_BEAST_603:PANCREAS +#CREATURE:FORGOTTEN_BEAST_603:SPLEEN +#CREATURE:FORGOTTEN_BEAST_603:KIDNEY +#CREATURE:FORGOTTEN_BEAST_605:MUSCLE + CREATURE:FORGOTTEN_BEAST_605:EYE +"CREATURE:FORGOTTEN_BEAST_605:BRAIN +!CREATURE:FORGOTTEN_BEAST_605:LUNG +"CREATURE:FORGOTTEN_BEAST_605:HEART +"CREATURE:FORGOTTEN_BEAST_605:LIVER + CREATURE:FORGOTTEN_BEAST_605:GUT +$CREATURE:FORGOTTEN_BEAST_605:STOMACH +$CREATURE:FORGOTTEN_BEAST_605:GIZZARD +%CREATURE:FORGOTTEN_BEAST_605:PANCREAS +#CREATURE:FORGOTTEN_BEAST_605:SPLEEN +#CREATURE:FORGOTTEN_BEAST_605:KIDNEY +#CREATURE:FORGOTTEN_BEAST_607:MUSCLE + CREATURE:FORGOTTEN_BEAST_607:EYE +"CREATURE:FORGOTTEN_BEAST_607:BRAIN +!CREATURE:FORGOTTEN_BEAST_607:LUNG +"CREATURE:FORGOTTEN_BEAST_607:HEART +"CREATURE:FORGOTTEN_BEAST_607:LIVER + CREATURE:FORGOTTEN_BEAST_607:GUT +$CREATURE:FORGOTTEN_BEAST_607:STOMACH +$CREATURE:FORGOTTEN_BEAST_607:GIZZARD +%CREATURE:FORGOTTEN_BEAST_607:PANCREAS +#CREATURE:FORGOTTEN_BEAST_607:SPLEEN +#CREATURE:FORGOTTEN_BEAST_607:KIDNEY +#CREATURE:FORGOTTEN_BEAST_608:MUSCLE + CREATURE:FORGOTTEN_BEAST_608:EYE +"CREATURE:FORGOTTEN_BEAST_608:BRAIN +!CREATURE:FORGOTTEN_BEAST_608:LUNG +"CREATURE:FORGOTTEN_BEAST_608:HEART +"CREATURE:FORGOTTEN_BEAST_608:LIVER + CREATURE:FORGOTTEN_BEAST_608:GUT +$CREATURE:FORGOTTEN_BEAST_608:STOMACH +$CREATURE:FORGOTTEN_BEAST_608:GIZZARD +%CREATURE:FORGOTTEN_BEAST_608:PANCREAS +#CREATURE:FORGOTTEN_BEAST_608:SPLEEN +#CREATURE:FORGOTTEN_BEAST_608:KIDNEY +#CREATURE:FORGOTTEN_BEAST_609:MUSCLE + CREATURE:FORGOTTEN_BEAST_609:EYE +"CREATURE:FORGOTTEN_BEAST_609:BRAIN +!CREATURE:FORGOTTEN_BEAST_609:LUNG +"CREATURE:FORGOTTEN_BEAST_609:HEART +"CREATURE:FORGOTTEN_BEAST_609:LIVER + CREATURE:FORGOTTEN_BEAST_609:GUT +$CREATURE:FORGOTTEN_BEAST_609:STOMACH +$CREATURE:FORGOTTEN_BEAST_609:GIZZARD +%CREATURE:FORGOTTEN_BEAST_609:PANCREAS +#CREATURE:FORGOTTEN_BEAST_609:SPLEEN +#CREATURE:FORGOTTEN_BEAST_609:KIDNEY +#CREATURE:FORGOTTEN_BEAST_610:MUSCLE + CREATURE:FORGOTTEN_BEAST_610:EYE +"CREATURE:FORGOTTEN_BEAST_610:BRAIN +!CREATURE:FORGOTTEN_BEAST_610:LUNG +"CREATURE:FORGOTTEN_BEAST_610:HEART +"CREATURE:FORGOTTEN_BEAST_610:LIVER + CREATURE:FORGOTTEN_BEAST_610:GUT +$CREATURE:FORGOTTEN_BEAST_610:STOMACH +$CREATURE:FORGOTTEN_BEAST_610:GIZZARD +%CREATURE:FORGOTTEN_BEAST_610:PANCREAS +#CREATURE:FORGOTTEN_BEAST_610:SPLEEN +#CREATURE:FORGOTTEN_BEAST_610:KIDNEY +#CREATURE:FORGOTTEN_BEAST_611:MUSCLE + CREATURE:FORGOTTEN_BEAST_611:EYE +"CREATURE:FORGOTTEN_BEAST_611:BRAIN +!CREATURE:FORGOTTEN_BEAST_611:LUNG +"CREATURE:FORGOTTEN_BEAST_611:HEART +"CREATURE:FORGOTTEN_BEAST_611:LIVER + CREATURE:FORGOTTEN_BEAST_611:GUT +$CREATURE:FORGOTTEN_BEAST_611:STOMACH +$CREATURE:FORGOTTEN_BEAST_611:GIZZARD +%CREATURE:FORGOTTEN_BEAST_611:PANCREAS +#CREATURE:FORGOTTEN_BEAST_611:SPLEEN +#CREATURE:FORGOTTEN_BEAST_611:KIDNEY +#CREATURE:FORGOTTEN_BEAST_612:MUSCLE + CREATURE:FORGOTTEN_BEAST_612:EYE +"CREATURE:FORGOTTEN_BEAST_612:BRAIN +!CREATURE:FORGOTTEN_BEAST_612:LUNG +"CREATURE:FORGOTTEN_BEAST_612:HEART +"CREATURE:FORGOTTEN_BEAST_612:LIVER + CREATURE:FORGOTTEN_BEAST_612:GUT +$CREATURE:FORGOTTEN_BEAST_612:STOMACH +$CREATURE:FORGOTTEN_BEAST_612:GIZZARD +%CREATURE:FORGOTTEN_BEAST_612:PANCREAS +#CREATURE:FORGOTTEN_BEAST_612:SPLEEN +#CREATURE:FORGOTTEN_BEAST_612:KIDNEY +#CREATURE:FORGOTTEN_BEAST_613:MUSCLE + CREATURE:FORGOTTEN_BEAST_613:EYE +"CREATURE:FORGOTTEN_BEAST_613:BRAIN +!CREATURE:FORGOTTEN_BEAST_613:LUNG +"CREATURE:FORGOTTEN_BEAST_613:HEART +"CREATURE:FORGOTTEN_BEAST_613:LIVER + CREATURE:FORGOTTEN_BEAST_613:GUT +$CREATURE:FORGOTTEN_BEAST_613:STOMACH +$CREATURE:FORGOTTEN_BEAST_613:GIZZARD +%CREATURE:FORGOTTEN_BEAST_613:PANCREAS +#CREATURE:FORGOTTEN_BEAST_613:SPLEEN +#CREATURE:FORGOTTEN_BEAST_613:KIDNEY +#CREATURE:FORGOTTEN_BEAST_614:MUSCLE + CREATURE:FORGOTTEN_BEAST_614:EYE +"CREATURE:FORGOTTEN_BEAST_614:BRAIN +!CREATURE:FORGOTTEN_BEAST_614:LUNG +"CREATURE:FORGOTTEN_BEAST_614:HEART +"CREATURE:FORGOTTEN_BEAST_614:LIVER + CREATURE:FORGOTTEN_BEAST_614:GUT +$CREATURE:FORGOTTEN_BEAST_614:STOMACH +$CREATURE:FORGOTTEN_BEAST_614:GIZZARD +%CREATURE:FORGOTTEN_BEAST_614:PANCREAS +#CREATURE:FORGOTTEN_BEAST_614:SPLEEN +#CREATURE:FORGOTTEN_BEAST_614:KIDNEY +#CREATURE:FORGOTTEN_BEAST_616:MUSCLE + CREATURE:FORGOTTEN_BEAST_616:EYE +"CREATURE:FORGOTTEN_BEAST_616:BRAIN +!CREATURE:FORGOTTEN_BEAST_616:LUNG +"CREATURE:FORGOTTEN_BEAST_616:HEART +"CREATURE:FORGOTTEN_BEAST_616:LIVER + CREATURE:FORGOTTEN_BEAST_616:GUT +$CREATURE:FORGOTTEN_BEAST_616:STOMACH +$CREATURE:FORGOTTEN_BEAST_616:GIZZARD +%CREATURE:FORGOTTEN_BEAST_616:PANCREAS +#CREATURE:FORGOTTEN_BEAST_616:SPLEEN +#CREATURE:FORGOTTEN_BEAST_616:KIDNEY +#CREATURE:FORGOTTEN_BEAST_619:MUSCLE + CREATURE:FORGOTTEN_BEAST_619:EYE +"CREATURE:FORGOTTEN_BEAST_619:BRAIN +!CREATURE:FORGOTTEN_BEAST_619:LUNG +"CREATURE:FORGOTTEN_BEAST_619:HEART +"CREATURE:FORGOTTEN_BEAST_619:LIVER + CREATURE:FORGOTTEN_BEAST_619:GUT +$CREATURE:FORGOTTEN_BEAST_619:STOMACH +$CREATURE:FORGOTTEN_BEAST_619:GIZZARD +%CREATURE:FORGOTTEN_BEAST_619:PANCREAS +#CREATURE:FORGOTTEN_BEAST_619:SPLEEN +#CREATURE:FORGOTTEN_BEAST_619:KIDNEY +#CREATURE:FORGOTTEN_BEAST_620:MUSCLE + CREATURE:FORGOTTEN_BEAST_620:EYE +"CREATURE:FORGOTTEN_BEAST_620:BRAIN +!CREATURE:FORGOTTEN_BEAST_620:LUNG +"CREATURE:FORGOTTEN_BEAST_620:HEART +"CREATURE:FORGOTTEN_BEAST_620:LIVER + CREATURE:FORGOTTEN_BEAST_620:GUT +$CREATURE:FORGOTTEN_BEAST_620:STOMACH +$CREATURE:FORGOTTEN_BEAST_620:GIZZARD +%CREATURE:FORGOTTEN_BEAST_620:PANCREAS +#CREATURE:FORGOTTEN_BEAST_620:SPLEEN +#CREATURE:FORGOTTEN_BEAST_620:KIDNEY +#CREATURE:FORGOTTEN_BEAST_621:MUSCLE + CREATURE:FORGOTTEN_BEAST_621:EYE +"CREATURE:FORGOTTEN_BEAST_621:BRAIN +!CREATURE:FORGOTTEN_BEAST_621:LUNG +"CREATURE:FORGOTTEN_BEAST_621:HEART +"CREATURE:FORGOTTEN_BEAST_621:LIVER + CREATURE:FORGOTTEN_BEAST_621:GUT +$CREATURE:FORGOTTEN_BEAST_621:STOMACH +$CREATURE:FORGOTTEN_BEAST_621:GIZZARD +%CREATURE:FORGOTTEN_BEAST_621:PANCREAS +#CREATURE:FORGOTTEN_BEAST_621:SPLEEN +#CREATURE:FORGOTTEN_BEAST_621:KIDNEY +#CREATURE:FORGOTTEN_BEAST_623:MUSCLE + CREATURE:FORGOTTEN_BEAST_623:EYE +"CREATURE:FORGOTTEN_BEAST_623:BRAIN +!CREATURE:FORGOTTEN_BEAST_623:LUNG +"CREATURE:FORGOTTEN_BEAST_623:HEART +"CREATURE:FORGOTTEN_BEAST_623:LIVER + CREATURE:FORGOTTEN_BEAST_623:GUT +$CREATURE:FORGOTTEN_BEAST_623:STOMACH +$CREATURE:FORGOTTEN_BEAST_623:GIZZARD +%CREATURE:FORGOTTEN_BEAST_623:PANCREAS +#CREATURE:FORGOTTEN_BEAST_623:SPLEEN +#CREATURE:FORGOTTEN_BEAST_623:KIDNEY +#CREATURE:FORGOTTEN_BEAST_624:MUSCLE + CREATURE:FORGOTTEN_BEAST_624:EYE +"CREATURE:FORGOTTEN_BEAST_624:BRAIN +!CREATURE:FORGOTTEN_BEAST_624:LUNG +"CREATURE:FORGOTTEN_BEAST_624:HEART +"CREATURE:FORGOTTEN_BEAST_624:LIVER + CREATURE:FORGOTTEN_BEAST_624:GUT +$CREATURE:FORGOTTEN_BEAST_624:STOMACH +$CREATURE:FORGOTTEN_BEAST_624:GIZZARD +%CREATURE:FORGOTTEN_BEAST_624:PANCREAS +#CREATURE:FORGOTTEN_BEAST_624:SPLEEN +#CREATURE:FORGOTTEN_BEAST_624:KIDNEY +#CREATURE:FORGOTTEN_BEAST_625:MUSCLE + CREATURE:FORGOTTEN_BEAST_625:EYE +"CREATURE:FORGOTTEN_BEAST_625:BRAIN +!CREATURE:FORGOTTEN_BEAST_625:LUNG +"CREATURE:FORGOTTEN_BEAST_625:HEART +"CREATURE:FORGOTTEN_BEAST_625:LIVER + CREATURE:FORGOTTEN_BEAST_625:GUT +$CREATURE:FORGOTTEN_BEAST_625:STOMACH +$CREATURE:FORGOTTEN_BEAST_625:GIZZARD +%CREATURE:FORGOTTEN_BEAST_625:PANCREAS +#CREATURE:FORGOTTEN_BEAST_625:SPLEEN +#CREATURE:FORGOTTEN_BEAST_625:KIDNEY +#CREATURE:FORGOTTEN_BEAST_626:MUSCLE + CREATURE:FORGOTTEN_BEAST_626:EYE +"CREATURE:FORGOTTEN_BEAST_626:BRAIN +!CREATURE:FORGOTTEN_BEAST_626:LUNG +"CREATURE:FORGOTTEN_BEAST_626:HEART +"CREATURE:FORGOTTEN_BEAST_626:LIVER + CREATURE:FORGOTTEN_BEAST_626:GUT +$CREATURE:FORGOTTEN_BEAST_626:STOMACH +$CREATURE:FORGOTTEN_BEAST_626:GIZZARD +%CREATURE:FORGOTTEN_BEAST_626:PANCREAS +#CREATURE:FORGOTTEN_BEAST_626:SPLEEN +#CREATURE:FORGOTTEN_BEAST_626:KIDNEY +#CREATURE:FORGOTTEN_BEAST_627:MUSCLE + CREATURE:FORGOTTEN_BEAST_627:EYE +"CREATURE:FORGOTTEN_BEAST_627:BRAIN +!CREATURE:FORGOTTEN_BEAST_627:LUNG +"CREATURE:FORGOTTEN_BEAST_627:HEART +"CREATURE:FORGOTTEN_BEAST_627:LIVER + CREATURE:FORGOTTEN_BEAST_627:GUT +$CREATURE:FORGOTTEN_BEAST_627:STOMACH +$CREATURE:FORGOTTEN_BEAST_627:GIZZARD +%CREATURE:FORGOTTEN_BEAST_627:PANCREAS +#CREATURE:FORGOTTEN_BEAST_627:SPLEEN +#CREATURE:FORGOTTEN_BEAST_627:KIDNEY +#CREATURE:FORGOTTEN_BEAST_628:MUSCLE + CREATURE:FORGOTTEN_BEAST_628:EYE +"CREATURE:FORGOTTEN_BEAST_628:BRAIN +!CREATURE:FORGOTTEN_BEAST_628:LUNG +"CREATURE:FORGOTTEN_BEAST_628:HEART +"CREATURE:FORGOTTEN_BEAST_628:LIVER + CREATURE:FORGOTTEN_BEAST_628:GUT +$CREATURE:FORGOTTEN_BEAST_628:STOMACH +$CREATURE:FORGOTTEN_BEAST_628:GIZZARD +%CREATURE:FORGOTTEN_BEAST_628:PANCREAS +#CREATURE:FORGOTTEN_BEAST_628:SPLEEN +#CREATURE:FORGOTTEN_BEAST_628:KIDNEY +#CREATURE:FORGOTTEN_BEAST_629:MUSCLE + CREATURE:FORGOTTEN_BEAST_629:EYE +"CREATURE:FORGOTTEN_BEAST_629:BRAIN +!CREATURE:FORGOTTEN_BEAST_629:LUNG +"CREATURE:FORGOTTEN_BEAST_629:HEART +"CREATURE:FORGOTTEN_BEAST_629:LIVER + CREATURE:FORGOTTEN_BEAST_629:GUT +$CREATURE:FORGOTTEN_BEAST_629:STOMACH +$CREATURE:FORGOTTEN_BEAST_629:GIZZARD +%CREATURE:FORGOTTEN_BEAST_629:PANCREAS +#CREATURE:FORGOTTEN_BEAST_629:SPLEEN +#CREATURE:FORGOTTEN_BEAST_629:KIDNEY +#CREATURE:FORGOTTEN_BEAST_630:MUSCLE + CREATURE:FORGOTTEN_BEAST_630:EYE +"CREATURE:FORGOTTEN_BEAST_630:BRAIN +!CREATURE:FORGOTTEN_BEAST_630:LUNG +"CREATURE:FORGOTTEN_BEAST_630:HEART +"CREATURE:FORGOTTEN_BEAST_630:LIVER + CREATURE:FORGOTTEN_BEAST_630:GUT +$CREATURE:FORGOTTEN_BEAST_630:STOMACH +$CREATURE:FORGOTTEN_BEAST_630:GIZZARD +%CREATURE:FORGOTTEN_BEAST_630:PANCREAS +#CREATURE:FORGOTTEN_BEAST_630:SPLEEN +#CREATURE:FORGOTTEN_BEAST_630:KIDNEY +#CREATURE:FORGOTTEN_BEAST_631:MUSCLE + CREATURE:FORGOTTEN_BEAST_631:EYE +"CREATURE:FORGOTTEN_BEAST_631:BRAIN +!CREATURE:FORGOTTEN_BEAST_631:LUNG +"CREATURE:FORGOTTEN_BEAST_631:HEART +"CREATURE:FORGOTTEN_BEAST_631:LIVER + CREATURE:FORGOTTEN_BEAST_631:GUT +$CREATURE:FORGOTTEN_BEAST_631:STOMACH +$CREATURE:FORGOTTEN_BEAST_631:GIZZARD +%CREATURE:FORGOTTEN_BEAST_631:PANCREAS +#CREATURE:FORGOTTEN_BEAST_631:SPLEEN +#CREATURE:FORGOTTEN_BEAST_631:KIDNEY +#CREATURE:FORGOTTEN_BEAST_632:MUSCLE + CREATURE:FORGOTTEN_BEAST_632:EYE +"CREATURE:FORGOTTEN_BEAST_632:BRAIN +!CREATURE:FORGOTTEN_BEAST_632:LUNG +"CREATURE:FORGOTTEN_BEAST_632:HEART +"CREATURE:FORGOTTEN_BEAST_632:LIVER + CREATURE:FORGOTTEN_BEAST_632:GUT +$CREATURE:FORGOTTEN_BEAST_632:STOMACH +$CREATURE:FORGOTTEN_BEAST_632:GIZZARD +%CREATURE:FORGOTTEN_BEAST_632:PANCREAS +#CREATURE:FORGOTTEN_BEAST_632:SPLEEN +#CREATURE:FORGOTTEN_BEAST_632:KIDNEY +#CREATURE:FORGOTTEN_BEAST_633:MUSCLE + CREATURE:FORGOTTEN_BEAST_633:EYE +"CREATURE:FORGOTTEN_BEAST_633:BRAIN +!CREATURE:FORGOTTEN_BEAST_633:LUNG +"CREATURE:FORGOTTEN_BEAST_633:HEART +"CREATURE:FORGOTTEN_BEAST_633:LIVER + CREATURE:FORGOTTEN_BEAST_633:GUT +$CREATURE:FORGOTTEN_BEAST_633:STOMACH +$CREATURE:FORGOTTEN_BEAST_633:GIZZARD +%CREATURE:FORGOTTEN_BEAST_633:PANCREAS +#CREATURE:FORGOTTEN_BEAST_633:SPLEEN +#CREATURE:FORGOTTEN_BEAST_633:KIDNEY +#CREATURE:FORGOTTEN_BEAST_634:MUSCLE + CREATURE:FORGOTTEN_BEAST_634:EYE +"CREATURE:FORGOTTEN_BEAST_634:BRAIN +!CREATURE:FORGOTTEN_BEAST_634:LUNG +"CREATURE:FORGOTTEN_BEAST_634:HEART +"CREATURE:FORGOTTEN_BEAST_634:LIVER + CREATURE:FORGOTTEN_BEAST_634:GUT +$CREATURE:FORGOTTEN_BEAST_634:STOMACH +$CREATURE:FORGOTTEN_BEAST_634:GIZZARD +%CREATURE:FORGOTTEN_BEAST_634:PANCREAS +#CREATURE:FORGOTTEN_BEAST_634:SPLEEN +#CREATURE:FORGOTTEN_BEAST_634:KIDNEY +#CREATURE:FORGOTTEN_BEAST_635:MUSCLE + CREATURE:FORGOTTEN_BEAST_635:EYE +"CREATURE:FORGOTTEN_BEAST_635:BRAIN +!CREATURE:FORGOTTEN_BEAST_635:LUNG +"CREATURE:FORGOTTEN_BEAST_635:HEART +"CREATURE:FORGOTTEN_BEAST_635:LIVER + CREATURE:FORGOTTEN_BEAST_635:GUT +$CREATURE:FORGOTTEN_BEAST_635:STOMACH +$CREATURE:FORGOTTEN_BEAST_635:GIZZARD +%CREATURE:FORGOTTEN_BEAST_635:PANCREAS +#CREATURE:FORGOTTEN_BEAST_635:SPLEEN +#CREATURE:FORGOTTEN_BEAST_635:KIDNEY +#CREATURE:FORGOTTEN_BEAST_636:MUSCLE + CREATURE:FORGOTTEN_BEAST_636:EYE +"CREATURE:FORGOTTEN_BEAST_636:BRAIN +!CREATURE:FORGOTTEN_BEAST_636:LUNG +"CREATURE:FORGOTTEN_BEAST_636:HEART +"CREATURE:FORGOTTEN_BEAST_636:LIVER + CREATURE:FORGOTTEN_BEAST_636:GUT +$CREATURE:FORGOTTEN_BEAST_636:STOMACH +$CREATURE:FORGOTTEN_BEAST_636:GIZZARD +%CREATURE:FORGOTTEN_BEAST_636:PANCREAS +#CREATURE:FORGOTTEN_BEAST_636:SPLEEN +#CREATURE:FORGOTTEN_BEAST_636:KIDNEY +#CREATURE:FORGOTTEN_BEAST_637:MUSCLE + CREATURE:FORGOTTEN_BEAST_637:EYE +"CREATURE:FORGOTTEN_BEAST_637:BRAIN +!CREATURE:FORGOTTEN_BEAST_637:LUNG +"CREATURE:FORGOTTEN_BEAST_637:HEART +"CREATURE:FORGOTTEN_BEAST_637:LIVER + CREATURE:FORGOTTEN_BEAST_637:GUT +$CREATURE:FORGOTTEN_BEAST_637:STOMACH +$CREATURE:FORGOTTEN_BEAST_637:GIZZARD +%CREATURE:FORGOTTEN_BEAST_637:PANCREAS +#CREATURE:FORGOTTEN_BEAST_637:SPLEEN +#CREATURE:FORGOTTEN_BEAST_637:KIDNEY +#CREATURE:FORGOTTEN_BEAST_639:MUSCLE + CREATURE:FORGOTTEN_BEAST_639:EYE +"CREATURE:FORGOTTEN_BEAST_639:BRAIN +!CREATURE:FORGOTTEN_BEAST_639:LUNG +"CREATURE:FORGOTTEN_BEAST_639:HEART +"CREATURE:FORGOTTEN_BEAST_639:LIVER + CREATURE:FORGOTTEN_BEAST_639:GUT +$CREATURE:FORGOTTEN_BEAST_639:STOMACH +$CREATURE:FORGOTTEN_BEAST_639:GIZZARD +%CREATURE:FORGOTTEN_BEAST_639:PANCREAS +#CREATURE:FORGOTTEN_BEAST_639:SPLEEN +#CREATURE:FORGOTTEN_BEAST_639:KIDNEY +#CREATURE:FORGOTTEN_BEAST_640:MUSCLE + CREATURE:FORGOTTEN_BEAST_640:EYE +"CREATURE:FORGOTTEN_BEAST_640:BRAIN +!CREATURE:FORGOTTEN_BEAST_640:LUNG +"CREATURE:FORGOTTEN_BEAST_640:HEART +"CREATURE:FORGOTTEN_BEAST_640:LIVER + CREATURE:FORGOTTEN_BEAST_640:GUT +$CREATURE:FORGOTTEN_BEAST_640:STOMACH +$CREATURE:FORGOTTEN_BEAST_640:GIZZARD +%CREATURE:FORGOTTEN_BEAST_640:PANCREAS +#CREATURE:FORGOTTEN_BEAST_640:SPLEEN +#CREATURE:FORGOTTEN_BEAST_640:KIDNEY +#CREATURE:FORGOTTEN_BEAST_643:MUSCLE + CREATURE:FORGOTTEN_BEAST_643:EYE +"CREATURE:FORGOTTEN_BEAST_643:BRAIN +!CREATURE:FORGOTTEN_BEAST_643:LUNG +"CREATURE:FORGOTTEN_BEAST_643:HEART +"CREATURE:FORGOTTEN_BEAST_643:LIVER + CREATURE:FORGOTTEN_BEAST_643:GUT +$CREATURE:FORGOTTEN_BEAST_643:STOMACH +$CREATURE:FORGOTTEN_BEAST_643:GIZZARD +%CREATURE:FORGOTTEN_BEAST_643:PANCREAS +#CREATURE:FORGOTTEN_BEAST_643:SPLEEN +#CREATURE:FORGOTTEN_BEAST_643:KIDNEY +#CREATURE:FORGOTTEN_BEAST_644:MUSCLE + CREATURE:FORGOTTEN_BEAST_644:EYE +"CREATURE:FORGOTTEN_BEAST_644:BRAIN +!CREATURE:FORGOTTEN_BEAST_644:LUNG +"CREATURE:FORGOTTEN_BEAST_644:HEART +"CREATURE:FORGOTTEN_BEAST_644:LIVER + CREATURE:FORGOTTEN_BEAST_644:GUT +$CREATURE:FORGOTTEN_BEAST_644:STOMACH +$CREATURE:FORGOTTEN_BEAST_644:GIZZARD +%CREATURE:FORGOTTEN_BEAST_644:PANCREAS +#CREATURE:FORGOTTEN_BEAST_644:SPLEEN +#CREATURE:FORGOTTEN_BEAST_644:KIDNEY +#CREATURE:FORGOTTEN_BEAST_645:MUSCLE + CREATURE:FORGOTTEN_BEAST_645:EYE +"CREATURE:FORGOTTEN_BEAST_645:BRAIN +!CREATURE:FORGOTTEN_BEAST_645:LUNG +"CREATURE:FORGOTTEN_BEAST_645:HEART +"CREATURE:FORGOTTEN_BEAST_645:LIVER + CREATURE:FORGOTTEN_BEAST_645:GUT +$CREATURE:FORGOTTEN_BEAST_645:STOMACH +$CREATURE:FORGOTTEN_BEAST_645:GIZZARD +%CREATURE:FORGOTTEN_BEAST_645:PANCREAS +#CREATURE:FORGOTTEN_BEAST_645:SPLEEN +#CREATURE:FORGOTTEN_BEAST_645:KIDNEY +#CREATURE:FORGOTTEN_BEAST_646:MUSCLE + CREATURE:FORGOTTEN_BEAST_646:EYE +"CREATURE:FORGOTTEN_BEAST_646:BRAIN +!CREATURE:FORGOTTEN_BEAST_646:LUNG +"CREATURE:FORGOTTEN_BEAST_646:HEART +"CREATURE:FORGOTTEN_BEAST_646:LIVER + CREATURE:FORGOTTEN_BEAST_646:GUT +$CREATURE:FORGOTTEN_BEAST_646:STOMACH +$CREATURE:FORGOTTEN_BEAST_646:GIZZARD +%CREATURE:FORGOTTEN_BEAST_646:PANCREAS +#CREATURE:FORGOTTEN_BEAST_646:SPLEEN +#CREATURE:FORGOTTEN_BEAST_646:KIDNEY +#CREATURE:FORGOTTEN_BEAST_647:MUSCLE + CREATURE:FORGOTTEN_BEAST_647:EYE +"CREATURE:FORGOTTEN_BEAST_647:BRAIN +!CREATURE:FORGOTTEN_BEAST_647:LUNG +"CREATURE:FORGOTTEN_BEAST_647:HEART +"CREATURE:FORGOTTEN_BEAST_647:LIVER + CREATURE:FORGOTTEN_BEAST_647:GUT +$CREATURE:FORGOTTEN_BEAST_647:STOMACH +$CREATURE:FORGOTTEN_BEAST_647:GIZZARD +%CREATURE:FORGOTTEN_BEAST_647:PANCREAS +#CREATURE:FORGOTTEN_BEAST_647:SPLEEN +#CREATURE:FORGOTTEN_BEAST_647:KIDNEY +#CREATURE:FORGOTTEN_BEAST_649:MUSCLE + CREATURE:FORGOTTEN_BEAST_649:EYE +"CREATURE:FORGOTTEN_BEAST_649:BRAIN +!CREATURE:FORGOTTEN_BEAST_649:LUNG +"CREATURE:FORGOTTEN_BEAST_649:HEART +"CREATURE:FORGOTTEN_BEAST_649:LIVER + CREATURE:FORGOTTEN_BEAST_649:GUT +$CREATURE:FORGOTTEN_BEAST_649:STOMACH +$CREATURE:FORGOTTEN_BEAST_649:GIZZARD +%CREATURE:FORGOTTEN_BEAST_649:PANCREAS +#CREATURE:FORGOTTEN_BEAST_649:SPLEEN +#CREATURE:FORGOTTEN_BEAST_649:KIDNEY +#CREATURE:FORGOTTEN_BEAST_650:MUSCLE + CREATURE:FORGOTTEN_BEAST_650:EYE +"CREATURE:FORGOTTEN_BEAST_650:BRAIN +!CREATURE:FORGOTTEN_BEAST_650:LUNG +"CREATURE:FORGOTTEN_BEAST_650:HEART +"CREATURE:FORGOTTEN_BEAST_650:LIVER + CREATURE:FORGOTTEN_BEAST_650:GUT +$CREATURE:FORGOTTEN_BEAST_650:STOMACH +$CREATURE:FORGOTTEN_BEAST_650:GIZZARD +%CREATURE:FORGOTTEN_BEAST_650:PANCREAS +#CREATURE:FORGOTTEN_BEAST_650:SPLEEN +#CREATURE:FORGOTTEN_BEAST_650:KIDNEY +#CREATURE:FORGOTTEN_BEAST_651:MUSCLE + CREATURE:FORGOTTEN_BEAST_651:EYE +"CREATURE:FORGOTTEN_BEAST_651:BRAIN +!CREATURE:FORGOTTEN_BEAST_651:LUNG +"CREATURE:FORGOTTEN_BEAST_651:HEART +"CREATURE:FORGOTTEN_BEAST_651:LIVER + CREATURE:FORGOTTEN_BEAST_651:GUT +$CREATURE:FORGOTTEN_BEAST_651:STOMACH +$CREATURE:FORGOTTEN_BEAST_651:GIZZARD +%CREATURE:FORGOTTEN_BEAST_651:PANCREAS +#CREATURE:FORGOTTEN_BEAST_651:SPLEEN +#CREATURE:FORGOTTEN_BEAST_651:KIDNEY +#CREATURE:FORGOTTEN_BEAST_652:MUSCLE + CREATURE:FORGOTTEN_BEAST_652:EYE +"CREATURE:FORGOTTEN_BEAST_652:BRAIN +!CREATURE:FORGOTTEN_BEAST_652:LUNG +"CREATURE:FORGOTTEN_BEAST_652:HEART +"CREATURE:FORGOTTEN_BEAST_652:LIVER + CREATURE:FORGOTTEN_BEAST_652:GUT +$CREATURE:FORGOTTEN_BEAST_652:STOMACH +$CREATURE:FORGOTTEN_BEAST_652:GIZZARD +%CREATURE:FORGOTTEN_BEAST_652:PANCREAS +#CREATURE:FORGOTTEN_BEAST_652:SPLEEN +#CREATURE:FORGOTTEN_BEAST_652:KIDNEY +#CREATURE:FORGOTTEN_BEAST_656:MUSCLE + CREATURE:FORGOTTEN_BEAST_656:EYE +"CREATURE:FORGOTTEN_BEAST_656:BRAIN +!CREATURE:FORGOTTEN_BEAST_656:LUNG +"CREATURE:FORGOTTEN_BEAST_656:HEART +"CREATURE:FORGOTTEN_BEAST_656:LIVER + CREATURE:FORGOTTEN_BEAST_656:GUT +$CREATURE:FORGOTTEN_BEAST_656:STOMACH +$CREATURE:FORGOTTEN_BEAST_656:GIZZARD +%CREATURE:FORGOTTEN_BEAST_656:PANCREAS +#CREATURE:FORGOTTEN_BEAST_656:SPLEEN +#CREATURE:FORGOTTEN_BEAST_656:KIDNEY +#CREATURE:FORGOTTEN_BEAST_658:MUSCLE + CREATURE:FORGOTTEN_BEAST_658:EYE +"CREATURE:FORGOTTEN_BEAST_658:BRAIN +!CREATURE:FORGOTTEN_BEAST_658:LUNG +"CREATURE:FORGOTTEN_BEAST_658:HEART +"CREATURE:FORGOTTEN_BEAST_658:LIVER + CREATURE:FORGOTTEN_BEAST_658:GUT +$CREATURE:FORGOTTEN_BEAST_658:STOMACH +$CREATURE:FORGOTTEN_BEAST_658:GIZZARD +%CREATURE:FORGOTTEN_BEAST_658:PANCREAS +#CREATURE:FORGOTTEN_BEAST_658:SPLEEN +#CREATURE:FORGOTTEN_BEAST_658:KIDNEY +#CREATURE:FORGOTTEN_BEAST_659:MUSCLE + CREATURE:FORGOTTEN_BEAST_659:EYE +"CREATURE:FORGOTTEN_BEAST_659:BRAIN +!CREATURE:FORGOTTEN_BEAST_659:LUNG +"CREATURE:FORGOTTEN_BEAST_659:HEART +"CREATURE:FORGOTTEN_BEAST_659:LIVER + CREATURE:FORGOTTEN_BEAST_659:GUT +$CREATURE:FORGOTTEN_BEAST_659:STOMACH +$CREATURE:FORGOTTEN_BEAST_659:GIZZARD +%CREATURE:FORGOTTEN_BEAST_659:PANCREAS +#CREATURE:FORGOTTEN_BEAST_659:SPLEEN +#CREATURE:FORGOTTEN_BEAST_659:KIDNEY +#CREATURE:FORGOTTEN_BEAST_661:MUSCLE + CREATURE:FORGOTTEN_BEAST_661:EYE +"CREATURE:FORGOTTEN_BEAST_661:BRAIN +!CREATURE:FORGOTTEN_BEAST_661:LUNG +"CREATURE:FORGOTTEN_BEAST_661:HEART +"CREATURE:FORGOTTEN_BEAST_661:LIVER + CREATURE:FORGOTTEN_BEAST_661:GUT +$CREATURE:FORGOTTEN_BEAST_661:STOMACH +$CREATURE:FORGOTTEN_BEAST_661:GIZZARD +%CREATURE:FORGOTTEN_BEAST_661:PANCREAS +#CREATURE:FORGOTTEN_BEAST_661:SPLEEN +#CREATURE:FORGOTTEN_BEAST_661:KIDNEY +#CREATURE:FORGOTTEN_BEAST_663:MUSCLE + CREATURE:FORGOTTEN_BEAST_663:EYE +"CREATURE:FORGOTTEN_BEAST_663:BRAIN +!CREATURE:FORGOTTEN_BEAST_663:LUNG +"CREATURE:FORGOTTEN_BEAST_663:HEART +"CREATURE:FORGOTTEN_BEAST_663:LIVER + CREATURE:FORGOTTEN_BEAST_663:GUT +$CREATURE:FORGOTTEN_BEAST_663:STOMACH +$CREATURE:FORGOTTEN_BEAST_663:GIZZARD +%CREATURE:FORGOTTEN_BEAST_663:PANCREAS +#CREATURE:FORGOTTEN_BEAST_663:SPLEEN +#CREATURE:FORGOTTEN_BEAST_663:KIDNEY +#CREATURE:FORGOTTEN_BEAST_664:MUSCLE + CREATURE:FORGOTTEN_BEAST_664:EYE +"CREATURE:FORGOTTEN_BEAST_664:BRAIN +!CREATURE:FORGOTTEN_BEAST_664:LUNG +"CREATURE:FORGOTTEN_BEAST_664:HEART +"CREATURE:FORGOTTEN_BEAST_664:LIVER + CREATURE:FORGOTTEN_BEAST_664:GUT +$CREATURE:FORGOTTEN_BEAST_664:STOMACH +$CREATURE:FORGOTTEN_BEAST_664:GIZZARD +%CREATURE:FORGOTTEN_BEAST_664:PANCREAS +#CREATURE:FORGOTTEN_BEAST_664:SPLEEN +#CREATURE:FORGOTTEN_BEAST_664:KIDNEY +#CREATURE:FORGOTTEN_BEAST_666:MUSCLE + CREATURE:FORGOTTEN_BEAST_666:EYE +"CREATURE:FORGOTTEN_BEAST_666:BRAIN +!CREATURE:FORGOTTEN_BEAST_666:LUNG +"CREATURE:FORGOTTEN_BEAST_666:HEART +"CREATURE:FORGOTTEN_BEAST_666:LIVER + CREATURE:FORGOTTEN_BEAST_666:GUT +$CREATURE:FORGOTTEN_BEAST_666:STOMACH +$CREATURE:FORGOTTEN_BEAST_666:GIZZARD +%CREATURE:FORGOTTEN_BEAST_666:PANCREAS +#CREATURE:FORGOTTEN_BEAST_666:SPLEEN +#CREATURE:FORGOTTEN_BEAST_666:KIDNEY +#CREATURE:FORGOTTEN_BEAST_667:MUSCLE + CREATURE:FORGOTTEN_BEAST_667:EYE +"CREATURE:FORGOTTEN_BEAST_667:BRAIN +!CREATURE:FORGOTTEN_BEAST_667:LUNG +"CREATURE:FORGOTTEN_BEAST_667:HEART +"CREATURE:FORGOTTEN_BEAST_667:LIVER + CREATURE:FORGOTTEN_BEAST_667:GUT +$CREATURE:FORGOTTEN_BEAST_667:STOMACH +$CREATURE:FORGOTTEN_BEAST_667:GIZZARD +%CREATURE:FORGOTTEN_BEAST_667:PANCREAS +#CREATURE:FORGOTTEN_BEAST_667:SPLEEN +#CREATURE:FORGOTTEN_BEAST_667:KIDNEY +#CREATURE:FORGOTTEN_BEAST_669:MUSCLE + CREATURE:FORGOTTEN_BEAST_669:EYE +"CREATURE:FORGOTTEN_BEAST_669:BRAIN +!CREATURE:FORGOTTEN_BEAST_669:LUNG +"CREATURE:FORGOTTEN_BEAST_669:HEART +"CREATURE:FORGOTTEN_BEAST_669:LIVER + CREATURE:FORGOTTEN_BEAST_669:GUT +$CREATURE:FORGOTTEN_BEAST_669:STOMACH +$CREATURE:FORGOTTEN_BEAST_669:GIZZARD +%CREATURE:FORGOTTEN_BEAST_669:PANCREAS +#CREATURE:FORGOTTEN_BEAST_669:SPLEEN +#CREATURE:FORGOTTEN_BEAST_669:KIDNEY +#CREATURE:FORGOTTEN_BEAST_670:MUSCLE + CREATURE:FORGOTTEN_BEAST_670:EYE +"CREATURE:FORGOTTEN_BEAST_670:BRAIN +!CREATURE:FORGOTTEN_BEAST_670:LUNG +"CREATURE:FORGOTTEN_BEAST_670:HEART +"CREATURE:FORGOTTEN_BEAST_670:LIVER + CREATURE:FORGOTTEN_BEAST_670:GUT +$CREATURE:FORGOTTEN_BEAST_670:STOMACH +$CREATURE:FORGOTTEN_BEAST_670:GIZZARD +%CREATURE:FORGOTTEN_BEAST_670:PANCREAS +#CREATURE:FORGOTTEN_BEAST_670:SPLEEN +#CREATURE:FORGOTTEN_BEAST_670:KIDNEY +#CREATURE:FORGOTTEN_BEAST_671:MUSCLE + CREATURE:FORGOTTEN_BEAST_671:EYE +"CREATURE:FORGOTTEN_BEAST_671:BRAIN +!CREATURE:FORGOTTEN_BEAST_671:LUNG +"CREATURE:FORGOTTEN_BEAST_671:HEART +"CREATURE:FORGOTTEN_BEAST_671:LIVER + CREATURE:FORGOTTEN_BEAST_671:GUT +$CREATURE:FORGOTTEN_BEAST_671:STOMACH +$CREATURE:FORGOTTEN_BEAST_671:GIZZARD +%CREATURE:FORGOTTEN_BEAST_671:PANCREAS +#CREATURE:FORGOTTEN_BEAST_671:SPLEEN +#CREATURE:FORGOTTEN_BEAST_671:KIDNEY +#CREATURE:FORGOTTEN_BEAST_674:MUSCLE + CREATURE:FORGOTTEN_BEAST_674:EYE +"CREATURE:FORGOTTEN_BEAST_674:BRAIN +!CREATURE:FORGOTTEN_BEAST_674:LUNG +"CREATURE:FORGOTTEN_BEAST_674:HEART +"CREATURE:FORGOTTEN_BEAST_674:LIVER + CREATURE:FORGOTTEN_BEAST_674:GUT +$CREATURE:FORGOTTEN_BEAST_674:STOMACH +$CREATURE:FORGOTTEN_BEAST_674:GIZZARD +%CREATURE:FORGOTTEN_BEAST_674:PANCREAS +#CREATURE:FORGOTTEN_BEAST_674:SPLEEN +#CREATURE:FORGOTTEN_BEAST_674:KIDNEY +#CREATURE:FORGOTTEN_BEAST_675:MUSCLE + CREATURE:FORGOTTEN_BEAST_675:EYE +"CREATURE:FORGOTTEN_BEAST_675:BRAIN +!CREATURE:FORGOTTEN_BEAST_675:LUNG +"CREATURE:FORGOTTEN_BEAST_675:HEART +"CREATURE:FORGOTTEN_BEAST_675:LIVER + CREATURE:FORGOTTEN_BEAST_675:GUT +$CREATURE:FORGOTTEN_BEAST_675:STOMACH +$CREATURE:FORGOTTEN_BEAST_675:GIZZARD +%CREATURE:FORGOTTEN_BEAST_675:PANCREAS +#CREATURE:FORGOTTEN_BEAST_675:SPLEEN +#CREATURE:FORGOTTEN_BEAST_675:KIDNEY +#CREATURE:FORGOTTEN_BEAST_678:MUSCLE + CREATURE:FORGOTTEN_BEAST_678:EYE +"CREATURE:FORGOTTEN_BEAST_678:BRAIN +!CREATURE:FORGOTTEN_BEAST_678:LUNG +"CREATURE:FORGOTTEN_BEAST_678:HEART +"CREATURE:FORGOTTEN_BEAST_678:LIVER + CREATURE:FORGOTTEN_BEAST_678:GUT +$CREATURE:FORGOTTEN_BEAST_678:STOMACH +$CREATURE:FORGOTTEN_BEAST_678:GIZZARD +%CREATURE:FORGOTTEN_BEAST_678:PANCREAS +#CREATURE:FORGOTTEN_BEAST_678:SPLEEN +#CREATURE:FORGOTTEN_BEAST_678:KIDNEY +#CREATURE:FORGOTTEN_BEAST_680:MUSCLE + CREATURE:FORGOTTEN_BEAST_680:EYE +"CREATURE:FORGOTTEN_BEAST_680:BRAIN +!CREATURE:FORGOTTEN_BEAST_680:LUNG +"CREATURE:FORGOTTEN_BEAST_680:HEART +"CREATURE:FORGOTTEN_BEAST_680:LIVER + CREATURE:FORGOTTEN_BEAST_680:GUT +$CREATURE:FORGOTTEN_BEAST_680:STOMACH +$CREATURE:FORGOTTEN_BEAST_680:GIZZARD +%CREATURE:FORGOTTEN_BEAST_680:PANCREAS +#CREATURE:FORGOTTEN_BEAST_680:SPLEEN +#CREATURE:FORGOTTEN_BEAST_680:KIDNEY +#CREATURE:FORGOTTEN_BEAST_681:MUSCLE + CREATURE:FORGOTTEN_BEAST_681:EYE +"CREATURE:FORGOTTEN_BEAST_681:BRAIN +!CREATURE:FORGOTTEN_BEAST_681:LUNG +"CREATURE:FORGOTTEN_BEAST_681:HEART +"CREATURE:FORGOTTEN_BEAST_681:LIVER + CREATURE:FORGOTTEN_BEAST_681:GUT +$CREATURE:FORGOTTEN_BEAST_681:STOMACH +$CREATURE:FORGOTTEN_BEAST_681:GIZZARD +%CREATURE:FORGOTTEN_BEAST_681:PANCREAS +#CREATURE:FORGOTTEN_BEAST_681:SPLEEN +#CREATURE:FORGOTTEN_BEAST_681:KIDNEY +#CREATURE:FORGOTTEN_BEAST_682:MUSCLE + CREATURE:FORGOTTEN_BEAST_682:EYE +"CREATURE:FORGOTTEN_BEAST_682:BRAIN +!CREATURE:FORGOTTEN_BEAST_682:LUNG +"CREATURE:FORGOTTEN_BEAST_682:HEART +"CREATURE:FORGOTTEN_BEAST_682:LIVER + CREATURE:FORGOTTEN_BEAST_682:GUT +$CREATURE:FORGOTTEN_BEAST_682:STOMACH +$CREATURE:FORGOTTEN_BEAST_682:GIZZARD +%CREATURE:FORGOTTEN_BEAST_682:PANCREAS +#CREATURE:FORGOTTEN_BEAST_682:SPLEEN +#CREATURE:FORGOTTEN_BEAST_682:KIDNEY +#CREATURE:FORGOTTEN_BEAST_684:MUSCLE + CREATURE:FORGOTTEN_BEAST_684:EYE +"CREATURE:FORGOTTEN_BEAST_684:BRAIN +!CREATURE:FORGOTTEN_BEAST_684:LUNG +"CREATURE:FORGOTTEN_BEAST_684:HEART +"CREATURE:FORGOTTEN_BEAST_684:LIVER + CREATURE:FORGOTTEN_BEAST_684:GUT +$CREATURE:FORGOTTEN_BEAST_684:STOMACH +$CREATURE:FORGOTTEN_BEAST_684:GIZZARD +%CREATURE:FORGOTTEN_BEAST_684:PANCREAS +#CREATURE:FORGOTTEN_BEAST_684:SPLEEN +#CREATURE:FORGOTTEN_BEAST_684:KIDNEY +#CREATURE:FORGOTTEN_BEAST_685:MUSCLE + CREATURE:FORGOTTEN_BEAST_685:EYE +"CREATURE:FORGOTTEN_BEAST_685:BRAIN +!CREATURE:FORGOTTEN_BEAST_685:LUNG +"CREATURE:FORGOTTEN_BEAST_685:HEART +"CREATURE:FORGOTTEN_BEAST_685:LIVER + CREATURE:FORGOTTEN_BEAST_685:GUT +$CREATURE:FORGOTTEN_BEAST_685:STOMACH +$CREATURE:FORGOTTEN_BEAST_685:GIZZARD +%CREATURE:FORGOTTEN_BEAST_685:PANCREAS +#CREATURE:FORGOTTEN_BEAST_685:SPLEEN +#CREATURE:FORGOTTEN_BEAST_685:KIDNEY +#CREATURE:FORGOTTEN_BEAST_686:MUSCLE + CREATURE:FORGOTTEN_BEAST_686:EYE +"CREATURE:FORGOTTEN_BEAST_686:BRAIN +!CREATURE:FORGOTTEN_BEAST_686:LUNG +"CREATURE:FORGOTTEN_BEAST_686:HEART +"CREATURE:FORGOTTEN_BEAST_686:LIVER + CREATURE:FORGOTTEN_BEAST_686:GUT +$CREATURE:FORGOTTEN_BEAST_686:STOMACH +$CREATURE:FORGOTTEN_BEAST_686:GIZZARD +%CREATURE:FORGOTTEN_BEAST_686:PANCREAS +#CREATURE:FORGOTTEN_BEAST_686:SPLEEN +#CREATURE:FORGOTTEN_BEAST_686:KIDNEY +#CREATURE:FORGOTTEN_BEAST_687:MUSCLE + CREATURE:FORGOTTEN_BEAST_687:EYE +"CREATURE:FORGOTTEN_BEAST_687:BRAIN +!CREATURE:FORGOTTEN_BEAST_687:LUNG +"CREATURE:FORGOTTEN_BEAST_687:HEART +"CREATURE:FORGOTTEN_BEAST_687:LIVER + CREATURE:FORGOTTEN_BEAST_687:GUT +$CREATURE:FORGOTTEN_BEAST_687:STOMACH +$CREATURE:FORGOTTEN_BEAST_687:GIZZARD +%CREATURE:FORGOTTEN_BEAST_687:PANCREAS +#CREATURE:FORGOTTEN_BEAST_687:SPLEEN +#CREATURE:FORGOTTEN_BEAST_687:KIDNEY +#CREATURE:FORGOTTEN_BEAST_688:MUSCLE + CREATURE:FORGOTTEN_BEAST_688:EYE +"CREATURE:FORGOTTEN_BEAST_688:BRAIN +!CREATURE:FORGOTTEN_BEAST_688:LUNG +"CREATURE:FORGOTTEN_BEAST_688:HEART +"CREATURE:FORGOTTEN_BEAST_688:LIVER + CREATURE:FORGOTTEN_BEAST_688:GUT +$CREATURE:FORGOTTEN_BEAST_688:STOMACH +$CREATURE:FORGOTTEN_BEAST_688:GIZZARD +%CREATURE:FORGOTTEN_BEAST_688:PANCREAS +#CREATURE:FORGOTTEN_BEAST_688:SPLEEN +#CREATURE:FORGOTTEN_BEAST_688:KIDNEY +#CREATURE:FORGOTTEN_BEAST_689:MUSCLE + CREATURE:FORGOTTEN_BEAST_689:EYE +"CREATURE:FORGOTTEN_BEAST_689:BRAIN +!CREATURE:FORGOTTEN_BEAST_689:LUNG +"CREATURE:FORGOTTEN_BEAST_689:HEART +"CREATURE:FORGOTTEN_BEAST_689:LIVER + CREATURE:FORGOTTEN_BEAST_689:GUT +$CREATURE:FORGOTTEN_BEAST_689:STOMACH +$CREATURE:FORGOTTEN_BEAST_689:GIZZARD +%CREATURE:FORGOTTEN_BEAST_689:PANCREAS +#CREATURE:FORGOTTEN_BEAST_689:SPLEEN +#CREATURE:FORGOTTEN_BEAST_689:KIDNEY +#CREATURE:FORGOTTEN_BEAST_691:MUSCLE + CREATURE:FORGOTTEN_BEAST_691:EYE +"CREATURE:FORGOTTEN_BEAST_691:BRAIN +!CREATURE:FORGOTTEN_BEAST_691:LUNG +"CREATURE:FORGOTTEN_BEAST_691:HEART +"CREATURE:FORGOTTEN_BEAST_691:LIVER + CREATURE:FORGOTTEN_BEAST_691:GUT +$CREATURE:FORGOTTEN_BEAST_691:STOMACH +$CREATURE:FORGOTTEN_BEAST_691:GIZZARD +%CREATURE:FORGOTTEN_BEAST_691:PANCREAS +#CREATURE:FORGOTTEN_BEAST_691:SPLEEN +#CREATURE:FORGOTTEN_BEAST_691:KIDNEY +#CREATURE:FORGOTTEN_BEAST_692:MUSCLE + CREATURE:FORGOTTEN_BEAST_692:EYE +"CREATURE:FORGOTTEN_BEAST_692:BRAIN +!CREATURE:FORGOTTEN_BEAST_692:LUNG +"CREATURE:FORGOTTEN_BEAST_692:HEART +"CREATURE:FORGOTTEN_BEAST_692:LIVER + CREATURE:FORGOTTEN_BEAST_692:GUT +$CREATURE:FORGOTTEN_BEAST_692:STOMACH +$CREATURE:FORGOTTEN_BEAST_692:GIZZARD +%CREATURE:FORGOTTEN_BEAST_692:PANCREAS +#CREATURE:FORGOTTEN_BEAST_692:SPLEEN +#CREATURE:FORGOTTEN_BEAST_692:KIDNEY +#CREATURE:FORGOTTEN_BEAST_695:MUSCLE + CREATURE:FORGOTTEN_BEAST_695:EYE +"CREATURE:FORGOTTEN_BEAST_695:BRAIN +!CREATURE:FORGOTTEN_BEAST_695:LUNG +"CREATURE:FORGOTTEN_BEAST_695:HEART +"CREATURE:FORGOTTEN_BEAST_695:LIVER + CREATURE:FORGOTTEN_BEAST_695:GUT +$CREATURE:FORGOTTEN_BEAST_695:STOMACH +$CREATURE:FORGOTTEN_BEAST_695:GIZZARD +%CREATURE:FORGOTTEN_BEAST_695:PANCREAS +#CREATURE:FORGOTTEN_BEAST_695:SPLEEN +#CREATURE:FORGOTTEN_BEAST_695:KIDNEY +#CREATURE:FORGOTTEN_BEAST_696:MUSCLE + CREATURE:FORGOTTEN_BEAST_696:EYE +"CREATURE:FORGOTTEN_BEAST_696:BRAIN +!CREATURE:FORGOTTEN_BEAST_696:LUNG +"CREATURE:FORGOTTEN_BEAST_696:HEART +"CREATURE:FORGOTTEN_BEAST_696:LIVER + CREATURE:FORGOTTEN_BEAST_696:GUT +$CREATURE:FORGOTTEN_BEAST_696:STOMACH +$CREATURE:FORGOTTEN_BEAST_696:GIZZARD +%CREATURE:FORGOTTEN_BEAST_696:PANCREAS +#CREATURE:FORGOTTEN_BEAST_696:SPLEEN +#CREATURE:FORGOTTEN_BEAST_696:KIDNEY +#CREATURE:FORGOTTEN_BEAST_697:MUSCLE + CREATURE:FORGOTTEN_BEAST_697:EYE +"CREATURE:FORGOTTEN_BEAST_697:BRAIN +!CREATURE:FORGOTTEN_BEAST_697:LUNG +"CREATURE:FORGOTTEN_BEAST_697:HEART +"CREATURE:FORGOTTEN_BEAST_697:LIVER + CREATURE:FORGOTTEN_BEAST_697:GUT +$CREATURE:FORGOTTEN_BEAST_697:STOMACH +$CREATURE:FORGOTTEN_BEAST_697:GIZZARD +%CREATURE:FORGOTTEN_BEAST_697:PANCREAS +#CREATURE:FORGOTTEN_BEAST_697:SPLEEN +#CREATURE:FORGOTTEN_BEAST_697:KIDNEY +#CREATURE:FORGOTTEN_BEAST_698:MUSCLE + CREATURE:FORGOTTEN_BEAST_698:EYE +"CREATURE:FORGOTTEN_BEAST_698:BRAIN +!CREATURE:FORGOTTEN_BEAST_698:LUNG +"CREATURE:FORGOTTEN_BEAST_698:HEART +"CREATURE:FORGOTTEN_BEAST_698:LIVER + CREATURE:FORGOTTEN_BEAST_698:GUT +$CREATURE:FORGOTTEN_BEAST_698:STOMACH +$CREATURE:FORGOTTEN_BEAST_698:GIZZARD +%CREATURE:FORGOTTEN_BEAST_698:PANCREAS +#CREATURE:FORGOTTEN_BEAST_698:SPLEEN +#CREATURE:FORGOTTEN_BEAST_698:KIDNEY +#CREATURE:FORGOTTEN_BEAST_699:MUSCLE + CREATURE:FORGOTTEN_BEAST_699:EYE +"CREATURE:FORGOTTEN_BEAST_699:BRAIN +!CREATURE:FORGOTTEN_BEAST_699:LUNG +"CREATURE:FORGOTTEN_BEAST_699:HEART +"CREATURE:FORGOTTEN_BEAST_699:LIVER + CREATURE:FORGOTTEN_BEAST_699:GUT +$CREATURE:FORGOTTEN_BEAST_699:STOMACH +$CREATURE:FORGOTTEN_BEAST_699:GIZZARD +%CREATURE:FORGOTTEN_BEAST_699:PANCREAS +#CREATURE:FORGOTTEN_BEAST_699:SPLEEN +#CREATURE:FORGOTTEN_BEAST_699:KIDNEY +#CREATURE:FORGOTTEN_BEAST_700:MUSCLE + CREATURE:FORGOTTEN_BEAST_700:EYE +"CREATURE:FORGOTTEN_BEAST_700:BRAIN +!CREATURE:FORGOTTEN_BEAST_700:LUNG +"CREATURE:FORGOTTEN_BEAST_700:HEART +"CREATURE:FORGOTTEN_BEAST_700:LIVER + CREATURE:FORGOTTEN_BEAST_700:GUT +$CREATURE:FORGOTTEN_BEAST_700:STOMACH +$CREATURE:FORGOTTEN_BEAST_700:GIZZARD +%CREATURE:FORGOTTEN_BEAST_700:PANCREAS +#CREATURE:FORGOTTEN_BEAST_700:SPLEEN +#CREATURE:FORGOTTEN_BEAST_700:KIDNEY +#CREATURE:FORGOTTEN_BEAST_701:MUSCLE + CREATURE:FORGOTTEN_BEAST_701:EYE +"CREATURE:FORGOTTEN_BEAST_701:BRAIN +!CREATURE:FORGOTTEN_BEAST_701:LUNG +"CREATURE:FORGOTTEN_BEAST_701:HEART +"CREATURE:FORGOTTEN_BEAST_701:LIVER + CREATURE:FORGOTTEN_BEAST_701:GUT +$CREATURE:FORGOTTEN_BEAST_701:STOMACH +$CREATURE:FORGOTTEN_BEAST_701:GIZZARD +%CREATURE:FORGOTTEN_BEAST_701:PANCREAS +#CREATURE:FORGOTTEN_BEAST_701:SPLEEN +#CREATURE:FORGOTTEN_BEAST_701:KIDNEY +#CREATURE:FORGOTTEN_BEAST_702:MUSCLE + CREATURE:FORGOTTEN_BEAST_702:EYE +"CREATURE:FORGOTTEN_BEAST_702:BRAIN +!CREATURE:FORGOTTEN_BEAST_702:LUNG +"CREATURE:FORGOTTEN_BEAST_702:HEART +"CREATURE:FORGOTTEN_BEAST_702:LIVER + CREATURE:FORGOTTEN_BEAST_702:GUT +$CREATURE:FORGOTTEN_BEAST_702:STOMACH +$CREATURE:FORGOTTEN_BEAST_702:GIZZARD +%CREATURE:FORGOTTEN_BEAST_702:PANCREAS +#CREATURE:FORGOTTEN_BEAST_702:SPLEEN +#CREATURE:FORGOTTEN_BEAST_702:KIDNEY +#CREATURE:FORGOTTEN_BEAST_704:MUSCLE + CREATURE:FORGOTTEN_BEAST_704:EYE +"CREATURE:FORGOTTEN_BEAST_704:BRAIN +!CREATURE:FORGOTTEN_BEAST_704:LUNG +"CREATURE:FORGOTTEN_BEAST_704:HEART +"CREATURE:FORGOTTEN_BEAST_704:LIVER + CREATURE:FORGOTTEN_BEAST_704:GUT +$CREATURE:FORGOTTEN_BEAST_704:STOMACH +$CREATURE:FORGOTTEN_BEAST_704:GIZZARD +%CREATURE:FORGOTTEN_BEAST_704:PANCREAS +#CREATURE:FORGOTTEN_BEAST_704:SPLEEN +#CREATURE:FORGOTTEN_BEAST_704:KIDNEY +#CREATURE:FORGOTTEN_BEAST_706:MUSCLE + CREATURE:FORGOTTEN_BEAST_706:EYE +"CREATURE:FORGOTTEN_BEAST_706:BRAIN +!CREATURE:FORGOTTEN_BEAST_706:LUNG +"CREATURE:FORGOTTEN_BEAST_706:HEART +"CREATURE:FORGOTTEN_BEAST_706:LIVER + CREATURE:FORGOTTEN_BEAST_706:GUT +$CREATURE:FORGOTTEN_BEAST_706:STOMACH +$CREATURE:FORGOTTEN_BEAST_706:GIZZARD +%CREATURE:FORGOTTEN_BEAST_706:PANCREAS +#CREATURE:FORGOTTEN_BEAST_706:SPLEEN +#CREATURE:FORGOTTEN_BEAST_706:KIDNEY +#CREATURE:FORGOTTEN_BEAST_708:MUSCLE + CREATURE:FORGOTTEN_BEAST_708:EYE +"CREATURE:FORGOTTEN_BEAST_708:BRAIN +!CREATURE:FORGOTTEN_BEAST_708:LUNG +"CREATURE:FORGOTTEN_BEAST_708:HEART +"CREATURE:FORGOTTEN_BEAST_708:LIVER + CREATURE:FORGOTTEN_BEAST_708:GUT +$CREATURE:FORGOTTEN_BEAST_708:STOMACH +$CREATURE:FORGOTTEN_BEAST_708:GIZZARD +%CREATURE:FORGOTTEN_BEAST_708:PANCREAS +#CREATURE:FORGOTTEN_BEAST_708:SPLEEN +#CREATURE:FORGOTTEN_BEAST_708:KIDNEY +#CREATURE:FORGOTTEN_BEAST_709:MUSCLE + CREATURE:FORGOTTEN_BEAST_709:EYE +"CREATURE:FORGOTTEN_BEAST_709:BRAIN +!CREATURE:FORGOTTEN_BEAST_709:LUNG +"CREATURE:FORGOTTEN_BEAST_709:HEART +"CREATURE:FORGOTTEN_BEAST_709:LIVER + CREATURE:FORGOTTEN_BEAST_709:GUT +$CREATURE:FORGOTTEN_BEAST_709:STOMACH +$CREATURE:FORGOTTEN_BEAST_709:GIZZARD +%CREATURE:FORGOTTEN_BEAST_709:PANCREAS +#CREATURE:FORGOTTEN_BEAST_709:SPLEEN +#CREATURE:FORGOTTEN_BEAST_709:KIDNEY +#CREATURE:FORGOTTEN_BEAST_711:MUSCLE + CREATURE:FORGOTTEN_BEAST_711:EYE +"CREATURE:FORGOTTEN_BEAST_711:BRAIN +!CREATURE:FORGOTTEN_BEAST_711:LUNG +"CREATURE:FORGOTTEN_BEAST_711:HEART +"CREATURE:FORGOTTEN_BEAST_711:LIVER + CREATURE:FORGOTTEN_BEAST_711:GUT +$CREATURE:FORGOTTEN_BEAST_711:STOMACH +$CREATURE:FORGOTTEN_BEAST_711:GIZZARD +%CREATURE:FORGOTTEN_BEAST_711:PANCREAS +#CREATURE:FORGOTTEN_BEAST_711:SPLEEN +#CREATURE:FORGOTTEN_BEAST_711:KIDNEY +#CREATURE:FORGOTTEN_BEAST_712:MUSCLE + CREATURE:FORGOTTEN_BEAST_712:EYE +"CREATURE:FORGOTTEN_BEAST_712:BRAIN +!CREATURE:FORGOTTEN_BEAST_712:LUNG +"CREATURE:FORGOTTEN_BEAST_712:HEART +"CREATURE:FORGOTTEN_BEAST_712:LIVER + CREATURE:FORGOTTEN_BEAST_712:GUT +$CREATURE:FORGOTTEN_BEAST_712:STOMACH +$CREATURE:FORGOTTEN_BEAST_712:GIZZARD +%CREATURE:FORGOTTEN_BEAST_712:PANCREAS +#CREATURE:FORGOTTEN_BEAST_712:SPLEEN +#CREATURE:FORGOTTEN_BEAST_712:KIDNEY +#CREATURE:FORGOTTEN_BEAST_713:MUSCLE + CREATURE:FORGOTTEN_BEAST_713:EYE +"CREATURE:FORGOTTEN_BEAST_713:BRAIN +!CREATURE:FORGOTTEN_BEAST_713:LUNG +"CREATURE:FORGOTTEN_BEAST_713:HEART +"CREATURE:FORGOTTEN_BEAST_713:LIVER + CREATURE:FORGOTTEN_BEAST_713:GUT +$CREATURE:FORGOTTEN_BEAST_713:STOMACH +$CREATURE:FORGOTTEN_BEAST_713:GIZZARD +%CREATURE:FORGOTTEN_BEAST_713:PANCREAS +#CREATURE:FORGOTTEN_BEAST_713:SPLEEN +#CREATURE:FORGOTTEN_BEAST_713:KIDNEY +#CREATURE:FORGOTTEN_BEAST_714:MUSCLE + CREATURE:FORGOTTEN_BEAST_714:EYE +"CREATURE:FORGOTTEN_BEAST_714:BRAIN +!CREATURE:FORGOTTEN_BEAST_714:LUNG +"CREATURE:FORGOTTEN_BEAST_714:HEART +"CREATURE:FORGOTTEN_BEAST_714:LIVER + CREATURE:FORGOTTEN_BEAST_714:GUT +$CREATURE:FORGOTTEN_BEAST_714:STOMACH +$CREATURE:FORGOTTEN_BEAST_714:GIZZARD +%CREATURE:FORGOTTEN_BEAST_714:PANCREAS +#CREATURE:FORGOTTEN_BEAST_714:SPLEEN +#CREATURE:FORGOTTEN_BEAST_714:KIDNEY +#CREATURE:FORGOTTEN_BEAST_715:MUSCLE + CREATURE:FORGOTTEN_BEAST_715:EYE +"CREATURE:FORGOTTEN_BEAST_715:BRAIN +!CREATURE:FORGOTTEN_BEAST_715:LUNG +"CREATURE:FORGOTTEN_BEAST_715:HEART +"CREATURE:FORGOTTEN_BEAST_715:LIVER + CREATURE:FORGOTTEN_BEAST_715:GUT +$CREATURE:FORGOTTEN_BEAST_715:STOMACH +$CREATURE:FORGOTTEN_BEAST_715:GIZZARD +%CREATURE:FORGOTTEN_BEAST_715:PANCREAS +#CREATURE:FORGOTTEN_BEAST_715:SPLEEN +#CREATURE:FORGOTTEN_BEAST_715:KIDNEY +#CREATURE:FORGOTTEN_BEAST_717:MUSCLE + CREATURE:FORGOTTEN_BEAST_717:EYE +"CREATURE:FORGOTTEN_BEAST_717:BRAIN +!CREATURE:FORGOTTEN_BEAST_717:LUNG +"CREATURE:FORGOTTEN_BEAST_717:HEART +"CREATURE:FORGOTTEN_BEAST_717:LIVER + CREATURE:FORGOTTEN_BEAST_717:GUT +$CREATURE:FORGOTTEN_BEAST_717:STOMACH +$CREATURE:FORGOTTEN_BEAST_717:GIZZARD +%CREATURE:FORGOTTEN_BEAST_717:PANCREAS +#CREATURE:FORGOTTEN_BEAST_717:SPLEEN +#CREATURE:FORGOTTEN_BEAST_717:KIDNEY +#CREATURE:FORGOTTEN_BEAST_718:MUSCLE + CREATURE:FORGOTTEN_BEAST_718:EYE +"CREATURE:FORGOTTEN_BEAST_718:BRAIN +!CREATURE:FORGOTTEN_BEAST_718:LUNG +"CREATURE:FORGOTTEN_BEAST_718:HEART +"CREATURE:FORGOTTEN_BEAST_718:LIVER + CREATURE:FORGOTTEN_BEAST_718:GUT +$CREATURE:FORGOTTEN_BEAST_718:STOMACH +$CREATURE:FORGOTTEN_BEAST_718:GIZZARD +%CREATURE:FORGOTTEN_BEAST_718:PANCREAS +#CREATURE:FORGOTTEN_BEAST_718:SPLEEN +#CREATURE:FORGOTTEN_BEAST_718:KIDNEY +#CREATURE:FORGOTTEN_BEAST_719:MUSCLE + CREATURE:FORGOTTEN_BEAST_719:EYE +"CREATURE:FORGOTTEN_BEAST_719:BRAIN +!CREATURE:FORGOTTEN_BEAST_719:LUNG +"CREATURE:FORGOTTEN_BEAST_719:HEART +"CREATURE:FORGOTTEN_BEAST_719:LIVER + CREATURE:FORGOTTEN_BEAST_719:GUT +$CREATURE:FORGOTTEN_BEAST_719:STOMACH +$CREATURE:FORGOTTEN_BEAST_719:GIZZARD +%CREATURE:FORGOTTEN_BEAST_719:PANCREAS +#CREATURE:FORGOTTEN_BEAST_719:SPLEEN +#CREATURE:FORGOTTEN_BEAST_719:KIDNEY +#CREATURE:FORGOTTEN_BEAST_720:MUSCLE + CREATURE:FORGOTTEN_BEAST_720:EYE +"CREATURE:FORGOTTEN_BEAST_720:BRAIN +!CREATURE:FORGOTTEN_BEAST_720:LUNG +"CREATURE:FORGOTTEN_BEAST_720:HEART +"CREATURE:FORGOTTEN_BEAST_720:LIVER + CREATURE:FORGOTTEN_BEAST_720:GUT +$CREATURE:FORGOTTEN_BEAST_720:STOMACH +$CREATURE:FORGOTTEN_BEAST_720:GIZZARD +%CREATURE:FORGOTTEN_BEAST_720:PANCREAS +#CREATURE:FORGOTTEN_BEAST_720:SPLEEN +#CREATURE:FORGOTTEN_BEAST_720:KIDNEY +#CREATURE:FORGOTTEN_BEAST_721:MUSCLE + CREATURE:FORGOTTEN_BEAST_721:EYE +"CREATURE:FORGOTTEN_BEAST_721:BRAIN +!CREATURE:FORGOTTEN_BEAST_721:LUNG +"CREATURE:FORGOTTEN_BEAST_721:HEART +"CREATURE:FORGOTTEN_BEAST_721:LIVER + CREATURE:FORGOTTEN_BEAST_721:GUT +$CREATURE:FORGOTTEN_BEAST_721:STOMACH +$CREATURE:FORGOTTEN_BEAST_721:GIZZARD +%CREATURE:FORGOTTEN_BEAST_721:PANCREAS +#CREATURE:FORGOTTEN_BEAST_721:SPLEEN +#CREATURE:FORGOTTEN_BEAST_721:KIDNEY +#CREATURE:FORGOTTEN_BEAST_722:MUSCLE + CREATURE:FORGOTTEN_BEAST_722:EYE +"CREATURE:FORGOTTEN_BEAST_722:BRAIN +!CREATURE:FORGOTTEN_BEAST_722:LUNG +"CREATURE:FORGOTTEN_BEAST_722:HEART +"CREATURE:FORGOTTEN_BEAST_722:LIVER + CREATURE:FORGOTTEN_BEAST_722:GUT +$CREATURE:FORGOTTEN_BEAST_722:STOMACH +$CREATURE:FORGOTTEN_BEAST_722:GIZZARD +%CREATURE:FORGOTTEN_BEAST_722:PANCREAS +#CREATURE:FORGOTTEN_BEAST_722:SPLEEN +#CREATURE:FORGOTTEN_BEAST_722:KIDNEY +#CREATURE:FORGOTTEN_BEAST_723:MUSCLE + CREATURE:FORGOTTEN_BEAST_723:EYE +"CREATURE:FORGOTTEN_BEAST_723:BRAIN +!CREATURE:FORGOTTEN_BEAST_723:LUNG +"CREATURE:FORGOTTEN_BEAST_723:HEART +"CREATURE:FORGOTTEN_BEAST_723:LIVER + CREATURE:FORGOTTEN_BEAST_723:GUT +$CREATURE:FORGOTTEN_BEAST_723:STOMACH +$CREATURE:FORGOTTEN_BEAST_723:GIZZARD +%CREATURE:FORGOTTEN_BEAST_723:PANCREAS +#CREATURE:FORGOTTEN_BEAST_723:SPLEEN +#CREATURE:FORGOTTEN_BEAST_723:KIDNEY +#CREATURE:FORGOTTEN_BEAST_724:MUSCLE + CREATURE:FORGOTTEN_BEAST_724:EYE +"CREATURE:FORGOTTEN_BEAST_724:BRAIN +!CREATURE:FORGOTTEN_BEAST_724:LUNG +"CREATURE:FORGOTTEN_BEAST_724:HEART +"CREATURE:FORGOTTEN_BEAST_724:LIVER + CREATURE:FORGOTTEN_BEAST_724:GUT +$CREATURE:FORGOTTEN_BEAST_724:STOMACH +$CREATURE:FORGOTTEN_BEAST_724:GIZZARD +%CREATURE:FORGOTTEN_BEAST_724:PANCREAS +#CREATURE:FORGOTTEN_BEAST_724:SPLEEN +#CREATURE:FORGOTTEN_BEAST_724:KIDNEY +#CREATURE:FORGOTTEN_BEAST_726:MUSCLE + CREATURE:FORGOTTEN_BEAST_726:EYE +"CREATURE:FORGOTTEN_BEAST_726:BRAIN +!CREATURE:FORGOTTEN_BEAST_726:LUNG +"CREATURE:FORGOTTEN_BEAST_726:HEART +"CREATURE:FORGOTTEN_BEAST_726:LIVER + CREATURE:FORGOTTEN_BEAST_726:GUT +$CREATURE:FORGOTTEN_BEAST_726:STOMACH +$CREATURE:FORGOTTEN_BEAST_726:GIZZARD +%CREATURE:FORGOTTEN_BEAST_726:PANCREAS +#CREATURE:FORGOTTEN_BEAST_726:SPLEEN +#CREATURE:FORGOTTEN_BEAST_726:KIDNEY +#CREATURE:FORGOTTEN_BEAST_730:MUSCLE + CREATURE:FORGOTTEN_BEAST_730:EYE +"CREATURE:FORGOTTEN_BEAST_730:BRAIN +!CREATURE:FORGOTTEN_BEAST_730:LUNG +"CREATURE:FORGOTTEN_BEAST_730:HEART +"CREATURE:FORGOTTEN_BEAST_730:LIVER + CREATURE:FORGOTTEN_BEAST_730:GUT +$CREATURE:FORGOTTEN_BEAST_730:STOMACH +$CREATURE:FORGOTTEN_BEAST_730:GIZZARD +%CREATURE:FORGOTTEN_BEAST_730:PANCREAS +#CREATURE:FORGOTTEN_BEAST_730:SPLEEN +#CREATURE:FORGOTTEN_BEAST_730:KIDNEY +#CREATURE:FORGOTTEN_BEAST_731:MUSCLE + CREATURE:FORGOTTEN_BEAST_731:EYE +"CREATURE:FORGOTTEN_BEAST_731:BRAIN +!CREATURE:FORGOTTEN_BEAST_731:LUNG +"CREATURE:FORGOTTEN_BEAST_731:HEART +"CREATURE:FORGOTTEN_BEAST_731:LIVER + CREATURE:FORGOTTEN_BEAST_731:GUT +$CREATURE:FORGOTTEN_BEAST_731:STOMACH +$CREATURE:FORGOTTEN_BEAST_731:GIZZARD +%CREATURE:FORGOTTEN_BEAST_731:PANCREAS +#CREATURE:FORGOTTEN_BEAST_731:SPLEEN +#CREATURE:FORGOTTEN_BEAST_731:KIDNEY +#CREATURE:FORGOTTEN_BEAST_734:MUSCLE + CREATURE:FORGOTTEN_BEAST_734:EYE +"CREATURE:FORGOTTEN_BEAST_734:BRAIN +!CREATURE:FORGOTTEN_BEAST_734:LUNG +"CREATURE:FORGOTTEN_BEAST_734:HEART +"CREATURE:FORGOTTEN_BEAST_734:LIVER + CREATURE:FORGOTTEN_BEAST_734:GUT +$CREATURE:FORGOTTEN_BEAST_734:STOMACH +$CREATURE:FORGOTTEN_BEAST_734:GIZZARD +%CREATURE:FORGOTTEN_BEAST_734:PANCREAS +#CREATURE:FORGOTTEN_BEAST_734:SPLEEN +#CREATURE:FORGOTTEN_BEAST_734:KIDNEY +#CREATURE:FORGOTTEN_BEAST_735:MUSCLE + CREATURE:FORGOTTEN_BEAST_735:EYE +"CREATURE:FORGOTTEN_BEAST_735:BRAIN +!CREATURE:FORGOTTEN_BEAST_735:LUNG +"CREATURE:FORGOTTEN_BEAST_735:HEART +"CREATURE:FORGOTTEN_BEAST_735:LIVER + CREATURE:FORGOTTEN_BEAST_735:GUT +$CREATURE:FORGOTTEN_BEAST_735:STOMACH +$CREATURE:FORGOTTEN_BEAST_735:GIZZARD +%CREATURE:FORGOTTEN_BEAST_735:PANCREAS +#CREATURE:FORGOTTEN_BEAST_735:SPLEEN +#CREATURE:FORGOTTEN_BEAST_735:KIDNEY +#CREATURE:FORGOTTEN_BEAST_737:MUSCLE + CREATURE:FORGOTTEN_BEAST_737:EYE +"CREATURE:FORGOTTEN_BEAST_737:BRAIN +!CREATURE:FORGOTTEN_BEAST_737:LUNG +"CREATURE:FORGOTTEN_BEAST_737:HEART +"CREATURE:FORGOTTEN_BEAST_737:LIVER + CREATURE:FORGOTTEN_BEAST_737:GUT +$CREATURE:FORGOTTEN_BEAST_737:STOMACH +$CREATURE:FORGOTTEN_BEAST_737:GIZZARD +%CREATURE:FORGOTTEN_BEAST_737:PANCREAS +#CREATURE:FORGOTTEN_BEAST_737:SPLEEN +#CREATURE:FORGOTTEN_BEAST_737:KIDNEY +#CREATURE:FORGOTTEN_BEAST_738:MUSCLE + CREATURE:FORGOTTEN_BEAST_738:EYE +"CREATURE:FORGOTTEN_BEAST_738:BRAIN +!CREATURE:FORGOTTEN_BEAST_738:LUNG +"CREATURE:FORGOTTEN_BEAST_738:HEART +"CREATURE:FORGOTTEN_BEAST_738:LIVER + CREATURE:FORGOTTEN_BEAST_738:GUT +$CREATURE:FORGOTTEN_BEAST_738:STOMACH +$CREATURE:FORGOTTEN_BEAST_738:GIZZARD +%CREATURE:FORGOTTEN_BEAST_738:PANCREAS +#CREATURE:FORGOTTEN_BEAST_738:SPLEEN +#CREATURE:FORGOTTEN_BEAST_738:KIDNEY +#CREATURE:FORGOTTEN_BEAST_739:MUSCLE + CREATURE:FORGOTTEN_BEAST_739:EYE +"CREATURE:FORGOTTEN_BEAST_739:BRAIN +!CREATURE:FORGOTTEN_BEAST_739:LUNG +"CREATURE:FORGOTTEN_BEAST_739:HEART +"CREATURE:FORGOTTEN_BEAST_739:LIVER + CREATURE:FORGOTTEN_BEAST_739:GUT +$CREATURE:FORGOTTEN_BEAST_739:STOMACH +$CREATURE:FORGOTTEN_BEAST_739:GIZZARD +%CREATURE:FORGOTTEN_BEAST_739:PANCREAS +#CREATURE:FORGOTTEN_BEAST_739:SPLEEN +#CREATURE:FORGOTTEN_BEAST_739:KIDNEY +#CREATURE:FORGOTTEN_BEAST_740:MUSCLE + CREATURE:FORGOTTEN_BEAST_740:EYE +"CREATURE:FORGOTTEN_BEAST_740:BRAIN +!CREATURE:FORGOTTEN_BEAST_740:LUNG +"CREATURE:FORGOTTEN_BEAST_740:HEART +"CREATURE:FORGOTTEN_BEAST_740:LIVER + CREATURE:FORGOTTEN_BEAST_740:GUT +$CREATURE:FORGOTTEN_BEAST_740:STOMACH +$CREATURE:FORGOTTEN_BEAST_740:GIZZARD +%CREATURE:FORGOTTEN_BEAST_740:PANCREAS +#CREATURE:FORGOTTEN_BEAST_740:SPLEEN +#CREATURE:FORGOTTEN_BEAST_740:KIDNEY +#CREATURE:FORGOTTEN_BEAST_741:MUSCLE + CREATURE:FORGOTTEN_BEAST_741:EYE +"CREATURE:FORGOTTEN_BEAST_741:BRAIN +!CREATURE:FORGOTTEN_BEAST_741:LUNG +"CREATURE:FORGOTTEN_BEAST_741:HEART +"CREATURE:FORGOTTEN_BEAST_741:LIVER + CREATURE:FORGOTTEN_BEAST_741:GUT +$CREATURE:FORGOTTEN_BEAST_741:STOMACH +$CREATURE:FORGOTTEN_BEAST_741:GIZZARD +%CREATURE:FORGOTTEN_BEAST_741:PANCREAS +#CREATURE:FORGOTTEN_BEAST_741:SPLEEN +#CREATURE:FORGOTTEN_BEAST_741:KIDNEY +#CREATURE:FORGOTTEN_BEAST_742:MUSCLE + CREATURE:FORGOTTEN_BEAST_742:EYE +"CREATURE:FORGOTTEN_BEAST_742:BRAIN +!CREATURE:FORGOTTEN_BEAST_742:LUNG +"CREATURE:FORGOTTEN_BEAST_742:HEART +"CREATURE:FORGOTTEN_BEAST_742:LIVER + CREATURE:FORGOTTEN_BEAST_742:GUT +$CREATURE:FORGOTTEN_BEAST_742:STOMACH +$CREATURE:FORGOTTEN_BEAST_742:GIZZARD +%CREATURE:FORGOTTEN_BEAST_742:PANCREAS +#CREATURE:FORGOTTEN_BEAST_742:SPLEEN +#CREATURE:FORGOTTEN_BEAST_742:KIDNEY +#CREATURE:FORGOTTEN_BEAST_743:MUSCLE + CREATURE:FORGOTTEN_BEAST_743:EYE +"CREATURE:FORGOTTEN_BEAST_743:BRAIN +!CREATURE:FORGOTTEN_BEAST_743:LUNG +"CREATURE:FORGOTTEN_BEAST_743:HEART +"CREATURE:FORGOTTEN_BEAST_743:LIVER + CREATURE:FORGOTTEN_BEAST_743:GUT +$CREATURE:FORGOTTEN_BEAST_743:STOMACH +$CREATURE:FORGOTTEN_BEAST_743:GIZZARD +%CREATURE:FORGOTTEN_BEAST_743:PANCREAS +#CREATURE:FORGOTTEN_BEAST_743:SPLEEN +#CREATURE:FORGOTTEN_BEAST_743:KIDNEY +#CREATURE:FORGOTTEN_BEAST_744:MUSCLE + CREATURE:FORGOTTEN_BEAST_744:EYE +"CREATURE:FORGOTTEN_BEAST_744:BRAIN +!CREATURE:FORGOTTEN_BEAST_744:LUNG +"CREATURE:FORGOTTEN_BEAST_744:HEART +"CREATURE:FORGOTTEN_BEAST_744:LIVER + CREATURE:FORGOTTEN_BEAST_744:GUT +$CREATURE:FORGOTTEN_BEAST_744:STOMACH +$CREATURE:FORGOTTEN_BEAST_744:GIZZARD +%CREATURE:FORGOTTEN_BEAST_744:PANCREAS +#CREATURE:FORGOTTEN_BEAST_744:SPLEEN +#CREATURE:FORGOTTEN_BEAST_744:KIDNEY +#CREATURE:FORGOTTEN_BEAST_746:MUSCLE + CREATURE:FORGOTTEN_BEAST_746:EYE +"CREATURE:FORGOTTEN_BEAST_746:BRAIN +!CREATURE:FORGOTTEN_BEAST_746:LUNG +"CREATURE:FORGOTTEN_BEAST_746:HEART +"CREATURE:FORGOTTEN_BEAST_746:LIVER + CREATURE:FORGOTTEN_BEAST_746:GUT +$CREATURE:FORGOTTEN_BEAST_746:STOMACH +$CREATURE:FORGOTTEN_BEAST_746:GIZZARD +%CREATURE:FORGOTTEN_BEAST_746:PANCREAS +#CREATURE:FORGOTTEN_BEAST_746:SPLEEN +#CREATURE:FORGOTTEN_BEAST_746:KIDNEY +#CREATURE:FORGOTTEN_BEAST_747:MUSCLE + CREATURE:FORGOTTEN_BEAST_747:EYE +"CREATURE:FORGOTTEN_BEAST_747:BRAIN +!CREATURE:FORGOTTEN_BEAST_747:LUNG +"CREATURE:FORGOTTEN_BEAST_747:HEART +"CREATURE:FORGOTTEN_BEAST_747:LIVER + CREATURE:FORGOTTEN_BEAST_747:GUT +$CREATURE:FORGOTTEN_BEAST_747:STOMACH +$CREATURE:FORGOTTEN_BEAST_747:GIZZARD +%CREATURE:FORGOTTEN_BEAST_747:PANCREAS +#CREATURE:FORGOTTEN_BEAST_747:SPLEEN +#CREATURE:FORGOTTEN_BEAST_747:KIDNEY +#CREATURE:FORGOTTEN_BEAST_749:MUSCLE + CREATURE:FORGOTTEN_BEAST_749:EYE +"CREATURE:FORGOTTEN_BEAST_749:BRAIN +!CREATURE:FORGOTTEN_BEAST_749:LUNG +"CREATURE:FORGOTTEN_BEAST_749:HEART +"CREATURE:FORGOTTEN_BEAST_749:LIVER + CREATURE:FORGOTTEN_BEAST_749:GUT +$CREATURE:FORGOTTEN_BEAST_749:STOMACH +$CREATURE:FORGOTTEN_BEAST_749:GIZZARD +%CREATURE:FORGOTTEN_BEAST_749:PANCREAS +#CREATURE:FORGOTTEN_BEAST_749:SPLEEN +#CREATURE:FORGOTTEN_BEAST_749:KIDNEY +#CREATURE:FORGOTTEN_BEAST_750:MUSCLE + CREATURE:FORGOTTEN_BEAST_750:EYE +"CREATURE:FORGOTTEN_BEAST_750:BRAIN +!CREATURE:FORGOTTEN_BEAST_750:LUNG +"CREATURE:FORGOTTEN_BEAST_750:HEART +"CREATURE:FORGOTTEN_BEAST_750:LIVER + CREATURE:FORGOTTEN_BEAST_750:GUT +$CREATURE:FORGOTTEN_BEAST_750:STOMACH +$CREATURE:FORGOTTEN_BEAST_750:GIZZARD +%CREATURE:FORGOTTEN_BEAST_750:PANCREAS +#CREATURE:FORGOTTEN_BEAST_750:SPLEEN +#CREATURE:FORGOTTEN_BEAST_750:KIDNEY +#CREATURE:FORGOTTEN_BEAST_751:MUSCLE + CREATURE:FORGOTTEN_BEAST_751:EYE +"CREATURE:FORGOTTEN_BEAST_751:BRAIN +!CREATURE:FORGOTTEN_BEAST_751:LUNG +"CREATURE:FORGOTTEN_BEAST_751:HEART +"CREATURE:FORGOTTEN_BEAST_751:LIVER + CREATURE:FORGOTTEN_BEAST_751:GUT +$CREATURE:FORGOTTEN_BEAST_751:STOMACH +$CREATURE:FORGOTTEN_BEAST_751:GIZZARD +%CREATURE:FORGOTTEN_BEAST_751:PANCREAS +#CREATURE:FORGOTTEN_BEAST_751:SPLEEN +#CREATURE:FORGOTTEN_BEAST_751:KIDNEY +#CREATURE:FORGOTTEN_BEAST_752:MUSCLE + CREATURE:FORGOTTEN_BEAST_752:EYE +"CREATURE:FORGOTTEN_BEAST_752:BRAIN +!CREATURE:FORGOTTEN_BEAST_752:LUNG +"CREATURE:FORGOTTEN_BEAST_752:HEART +"CREATURE:FORGOTTEN_BEAST_752:LIVER + CREATURE:FORGOTTEN_BEAST_752:GUT +$CREATURE:FORGOTTEN_BEAST_752:STOMACH +$CREATURE:FORGOTTEN_BEAST_752:GIZZARD +%CREATURE:FORGOTTEN_BEAST_752:PANCREAS +#CREATURE:FORGOTTEN_BEAST_752:SPLEEN +#CREATURE:FORGOTTEN_BEAST_752:KIDNEY +#CREATURE:FORGOTTEN_BEAST_753:MUSCLE + CREATURE:FORGOTTEN_BEAST_753:EYE +"CREATURE:FORGOTTEN_BEAST_753:BRAIN +!CREATURE:FORGOTTEN_BEAST_753:LUNG +"CREATURE:FORGOTTEN_BEAST_753:HEART +"CREATURE:FORGOTTEN_BEAST_753:LIVER + CREATURE:FORGOTTEN_BEAST_753:GUT +$CREATURE:FORGOTTEN_BEAST_753:STOMACH +$CREATURE:FORGOTTEN_BEAST_753:GIZZARD +%CREATURE:FORGOTTEN_BEAST_753:PANCREAS +#CREATURE:FORGOTTEN_BEAST_753:SPLEEN +#CREATURE:FORGOTTEN_BEAST_753:KIDNEY +#CREATURE:FORGOTTEN_BEAST_754:MUSCLE + CREATURE:FORGOTTEN_BEAST_754:EYE +"CREATURE:FORGOTTEN_BEAST_754:BRAIN +!CREATURE:FORGOTTEN_BEAST_754:LUNG +"CREATURE:FORGOTTEN_BEAST_754:HEART +"CREATURE:FORGOTTEN_BEAST_754:LIVER + CREATURE:FORGOTTEN_BEAST_754:GUT +$CREATURE:FORGOTTEN_BEAST_754:STOMACH +$CREATURE:FORGOTTEN_BEAST_754:GIZZARD +%CREATURE:FORGOTTEN_BEAST_754:PANCREAS +#CREATURE:FORGOTTEN_BEAST_754:SPLEEN +#CREATURE:FORGOTTEN_BEAST_754:KIDNEY +#CREATURE:FORGOTTEN_BEAST_755:MUSCLE + CREATURE:FORGOTTEN_BEAST_755:EYE +"CREATURE:FORGOTTEN_BEAST_755:BRAIN +!CREATURE:FORGOTTEN_BEAST_755:LUNG +"CREATURE:FORGOTTEN_BEAST_755:HEART +"CREATURE:FORGOTTEN_BEAST_755:LIVER + CREATURE:FORGOTTEN_BEAST_755:GUT +$CREATURE:FORGOTTEN_BEAST_755:STOMACH +$CREATURE:FORGOTTEN_BEAST_755:GIZZARD +%CREATURE:FORGOTTEN_BEAST_755:PANCREAS +#CREATURE:FORGOTTEN_BEAST_755:SPLEEN +#CREATURE:FORGOTTEN_BEAST_755:KIDNEY +#CREATURE:FORGOTTEN_BEAST_756:MUSCLE + CREATURE:FORGOTTEN_BEAST_756:EYE +"CREATURE:FORGOTTEN_BEAST_756:BRAIN +!CREATURE:FORGOTTEN_BEAST_756:LUNG +"CREATURE:FORGOTTEN_BEAST_756:HEART +"CREATURE:FORGOTTEN_BEAST_756:LIVER + CREATURE:FORGOTTEN_BEAST_756:GUT +$CREATURE:FORGOTTEN_BEAST_756:STOMACH +$CREATURE:FORGOTTEN_BEAST_756:GIZZARD +%CREATURE:FORGOTTEN_BEAST_756:PANCREAS +#CREATURE:FORGOTTEN_BEAST_756:SPLEEN +#CREATURE:FORGOTTEN_BEAST_756:KIDNEY +#CREATURE:FORGOTTEN_BEAST_757:MUSCLE + CREATURE:FORGOTTEN_BEAST_757:EYE +"CREATURE:FORGOTTEN_BEAST_757:BRAIN +!CREATURE:FORGOTTEN_BEAST_757:LUNG +"CREATURE:FORGOTTEN_BEAST_757:HEART +"CREATURE:FORGOTTEN_BEAST_757:LIVER + CREATURE:FORGOTTEN_BEAST_757:GUT +$CREATURE:FORGOTTEN_BEAST_757:STOMACH +$CREATURE:FORGOTTEN_BEAST_757:GIZZARD +%CREATURE:FORGOTTEN_BEAST_757:PANCREAS +#CREATURE:FORGOTTEN_BEAST_757:SPLEEN +#CREATURE:FORGOTTEN_BEAST_757:KIDNEY +#CREATURE:FORGOTTEN_BEAST_758:MUSCLE + CREATURE:FORGOTTEN_BEAST_758:EYE +"CREATURE:FORGOTTEN_BEAST_758:BRAIN +!CREATURE:FORGOTTEN_BEAST_758:LUNG +"CREATURE:FORGOTTEN_BEAST_758:HEART +"CREATURE:FORGOTTEN_BEAST_758:LIVER + CREATURE:FORGOTTEN_BEAST_758:GUT +$CREATURE:FORGOTTEN_BEAST_758:STOMACH +$CREATURE:FORGOTTEN_BEAST_758:GIZZARD +%CREATURE:FORGOTTEN_BEAST_758:PANCREAS +#CREATURE:FORGOTTEN_BEAST_758:SPLEEN +#CREATURE:FORGOTTEN_BEAST_758:KIDNEY +#CREATURE:FORGOTTEN_BEAST_759:MUSCLE + CREATURE:FORGOTTEN_BEAST_759:EYE +"CREATURE:FORGOTTEN_BEAST_759:BRAIN +!CREATURE:FORGOTTEN_BEAST_759:LUNG +"CREATURE:FORGOTTEN_BEAST_759:HEART +"CREATURE:FORGOTTEN_BEAST_759:LIVER + CREATURE:FORGOTTEN_BEAST_759:GUT +$CREATURE:FORGOTTEN_BEAST_759:STOMACH +$CREATURE:FORGOTTEN_BEAST_759:GIZZARD +%CREATURE:FORGOTTEN_BEAST_759:PANCREAS +#CREATURE:FORGOTTEN_BEAST_759:SPLEEN +#CREATURE:FORGOTTEN_BEAST_759:KIDNEY +#CREATURE:FORGOTTEN_BEAST_760:MUSCLE + CREATURE:FORGOTTEN_BEAST_760:EYE +"CREATURE:FORGOTTEN_BEAST_760:BRAIN +!CREATURE:FORGOTTEN_BEAST_760:LUNG +"CREATURE:FORGOTTEN_BEAST_760:HEART +"CREATURE:FORGOTTEN_BEAST_760:LIVER + CREATURE:FORGOTTEN_BEAST_760:GUT +$CREATURE:FORGOTTEN_BEAST_760:STOMACH +$CREATURE:FORGOTTEN_BEAST_760:GIZZARD +%CREATURE:FORGOTTEN_BEAST_760:PANCREAS +#CREATURE:FORGOTTEN_BEAST_760:SPLEEN +#CREATURE:FORGOTTEN_BEAST_760:KIDNEY +#CREATURE:FORGOTTEN_BEAST_761:MUSCLE + CREATURE:FORGOTTEN_BEAST_761:EYE +"CREATURE:FORGOTTEN_BEAST_761:BRAIN +!CREATURE:FORGOTTEN_BEAST_761:LUNG +"CREATURE:FORGOTTEN_BEAST_761:HEART +"CREATURE:FORGOTTEN_BEAST_761:LIVER + CREATURE:FORGOTTEN_BEAST_761:GUT +$CREATURE:FORGOTTEN_BEAST_761:STOMACH +$CREATURE:FORGOTTEN_BEAST_761:GIZZARD +%CREATURE:FORGOTTEN_BEAST_761:PANCREAS +#CREATURE:FORGOTTEN_BEAST_761:SPLEEN +#CREATURE:FORGOTTEN_BEAST_761:KIDNEY +#CREATURE:FORGOTTEN_BEAST_762:MUSCLE + CREATURE:FORGOTTEN_BEAST_762:EYE +"CREATURE:FORGOTTEN_BEAST_762:BRAIN +!CREATURE:FORGOTTEN_BEAST_762:LUNG +"CREATURE:FORGOTTEN_BEAST_762:HEART +"CREATURE:FORGOTTEN_BEAST_762:LIVER + CREATURE:FORGOTTEN_BEAST_762:GUT +$CREATURE:FORGOTTEN_BEAST_762:STOMACH +$CREATURE:FORGOTTEN_BEAST_762:GIZZARD +%CREATURE:FORGOTTEN_BEAST_762:PANCREAS +#CREATURE:FORGOTTEN_BEAST_762:SPLEEN +#CREATURE:FORGOTTEN_BEAST_762:KIDNEY +#CREATURE:FORGOTTEN_BEAST_764:MUSCLE + CREATURE:FORGOTTEN_BEAST_764:EYE +"CREATURE:FORGOTTEN_BEAST_764:BRAIN +!CREATURE:FORGOTTEN_BEAST_764:LUNG +"CREATURE:FORGOTTEN_BEAST_764:HEART +"CREATURE:FORGOTTEN_BEAST_764:LIVER + CREATURE:FORGOTTEN_BEAST_764:GUT +$CREATURE:FORGOTTEN_BEAST_764:STOMACH +$CREATURE:FORGOTTEN_BEAST_764:GIZZARD +%CREATURE:FORGOTTEN_BEAST_764:PANCREAS +#CREATURE:FORGOTTEN_BEAST_764:SPLEEN +#CREATURE:FORGOTTEN_BEAST_764:KIDNEY +#CREATURE:FORGOTTEN_BEAST_767:MUSCLE + CREATURE:FORGOTTEN_BEAST_767:EYE +"CREATURE:FORGOTTEN_BEAST_767:BRAIN +!CREATURE:FORGOTTEN_BEAST_767:LUNG +"CREATURE:FORGOTTEN_BEAST_767:HEART +"CREATURE:FORGOTTEN_BEAST_767:LIVER + CREATURE:FORGOTTEN_BEAST_767:GUT +$CREATURE:FORGOTTEN_BEAST_767:STOMACH +$CREATURE:FORGOTTEN_BEAST_767:GIZZARD +%CREATURE:FORGOTTEN_BEAST_767:PANCREAS +#CREATURE:FORGOTTEN_BEAST_767:SPLEEN +#CREATURE:FORGOTTEN_BEAST_767:KIDNEY +#CREATURE:FORGOTTEN_BEAST_768:MUSCLE + CREATURE:FORGOTTEN_BEAST_768:EYE +"CREATURE:FORGOTTEN_BEAST_768:BRAIN +!CREATURE:FORGOTTEN_BEAST_768:LUNG +"CREATURE:FORGOTTEN_BEAST_768:HEART +"CREATURE:FORGOTTEN_BEAST_768:LIVER + CREATURE:FORGOTTEN_BEAST_768:GUT +$CREATURE:FORGOTTEN_BEAST_768:STOMACH +$CREATURE:FORGOTTEN_BEAST_768:GIZZARD +%CREATURE:FORGOTTEN_BEAST_768:PANCREAS +#CREATURE:FORGOTTEN_BEAST_768:SPLEEN +#CREATURE:FORGOTTEN_BEAST_768:KIDNEY +#CREATURE:FORGOTTEN_BEAST_769:MUSCLE + CREATURE:FORGOTTEN_BEAST_769:EYE +"CREATURE:FORGOTTEN_BEAST_769:BRAIN +!CREATURE:FORGOTTEN_BEAST_769:LUNG +"CREATURE:FORGOTTEN_BEAST_769:HEART +"CREATURE:FORGOTTEN_BEAST_769:LIVER + CREATURE:FORGOTTEN_BEAST_769:GUT +$CREATURE:FORGOTTEN_BEAST_769:STOMACH +$CREATURE:FORGOTTEN_BEAST_769:GIZZARD +%CREATURE:FORGOTTEN_BEAST_769:PANCREAS +#CREATURE:FORGOTTEN_BEAST_769:SPLEEN +#CREATURE:FORGOTTEN_BEAST_769:KIDNEY +#CREATURE:FORGOTTEN_BEAST_770:MUSCLE + CREATURE:FORGOTTEN_BEAST_770:EYE +"CREATURE:FORGOTTEN_BEAST_770:BRAIN +!CREATURE:FORGOTTEN_BEAST_770:LUNG +"CREATURE:FORGOTTEN_BEAST_770:HEART +"CREATURE:FORGOTTEN_BEAST_770:LIVER + CREATURE:FORGOTTEN_BEAST_770:GUT +$CREATURE:FORGOTTEN_BEAST_770:STOMACH +$CREATURE:FORGOTTEN_BEAST_770:GIZZARD +%CREATURE:FORGOTTEN_BEAST_770:PANCREAS +#CREATURE:FORGOTTEN_BEAST_770:SPLEEN +#CREATURE:FORGOTTEN_BEAST_770:KIDNEY +#CREATURE:FORGOTTEN_BEAST_773:MUSCLE + CREATURE:FORGOTTEN_BEAST_773:EYE +"CREATURE:FORGOTTEN_BEAST_773:BRAIN +!CREATURE:FORGOTTEN_BEAST_773:LUNG +"CREATURE:FORGOTTEN_BEAST_773:HEART +"CREATURE:FORGOTTEN_BEAST_773:LIVER + CREATURE:FORGOTTEN_BEAST_773:GUT +$CREATURE:FORGOTTEN_BEAST_773:STOMACH +$CREATURE:FORGOTTEN_BEAST_773:GIZZARD +%CREATURE:FORGOTTEN_BEAST_773:PANCREAS +#CREATURE:FORGOTTEN_BEAST_773:SPLEEN +#CREATURE:FORGOTTEN_BEAST_773:KIDNEY +#CREATURE:FORGOTTEN_BEAST_774:MUSCLE + CREATURE:FORGOTTEN_BEAST_774:EYE +"CREATURE:FORGOTTEN_BEAST_774:BRAIN +!CREATURE:FORGOTTEN_BEAST_774:LUNG +"CREATURE:FORGOTTEN_BEAST_774:HEART +"CREATURE:FORGOTTEN_BEAST_774:LIVER + CREATURE:FORGOTTEN_BEAST_774:GUT +$CREATURE:FORGOTTEN_BEAST_774:STOMACH +$CREATURE:FORGOTTEN_BEAST_774:GIZZARD +%CREATURE:FORGOTTEN_BEAST_774:PANCREAS +#CREATURE:FORGOTTEN_BEAST_774:SPLEEN +#CREATURE:FORGOTTEN_BEAST_774:KIDNEY +#CREATURE:FORGOTTEN_BEAST_776:MUSCLE + CREATURE:FORGOTTEN_BEAST_776:EYE +"CREATURE:FORGOTTEN_BEAST_776:BRAIN +!CREATURE:FORGOTTEN_BEAST_776:LUNG +"CREATURE:FORGOTTEN_BEAST_776:HEART +"CREATURE:FORGOTTEN_BEAST_776:LIVER + CREATURE:FORGOTTEN_BEAST_776:GUT +$CREATURE:FORGOTTEN_BEAST_776:STOMACH +$CREATURE:FORGOTTEN_BEAST_776:GIZZARD +%CREATURE:FORGOTTEN_BEAST_776:PANCREAS +#CREATURE:FORGOTTEN_BEAST_776:SPLEEN +#CREATURE:FORGOTTEN_BEAST_776:KIDNEY +#CREATURE:FORGOTTEN_BEAST_777:MUSCLE + CREATURE:FORGOTTEN_BEAST_777:EYE +"CREATURE:FORGOTTEN_BEAST_777:BRAIN +!CREATURE:FORGOTTEN_BEAST_777:LUNG +"CREATURE:FORGOTTEN_BEAST_777:HEART +"CREATURE:FORGOTTEN_BEAST_777:LIVER + CREATURE:FORGOTTEN_BEAST_777:GUT +$CREATURE:FORGOTTEN_BEAST_777:STOMACH +$CREATURE:FORGOTTEN_BEAST_777:GIZZARD +%CREATURE:FORGOTTEN_BEAST_777:PANCREAS +#CREATURE:FORGOTTEN_BEAST_777:SPLEEN +#CREATURE:FORGOTTEN_BEAST_777:KIDNEY +#CREATURE:FORGOTTEN_BEAST_778:MUSCLE + CREATURE:FORGOTTEN_BEAST_778:EYE +"CREATURE:FORGOTTEN_BEAST_778:BRAIN +!CREATURE:FORGOTTEN_BEAST_778:LUNG +"CREATURE:FORGOTTEN_BEAST_778:HEART +"CREATURE:FORGOTTEN_BEAST_778:LIVER + CREATURE:FORGOTTEN_BEAST_778:GUT +$CREATURE:FORGOTTEN_BEAST_778:STOMACH +$CREATURE:FORGOTTEN_BEAST_778:GIZZARD +%CREATURE:FORGOTTEN_BEAST_778:PANCREAS +#CREATURE:FORGOTTEN_BEAST_778:SPLEEN +#CREATURE:FORGOTTEN_BEAST_778:KIDNEY +#CREATURE:FORGOTTEN_BEAST_779:MUSCLE + CREATURE:FORGOTTEN_BEAST_779:EYE +"CREATURE:FORGOTTEN_BEAST_779:BRAIN +!CREATURE:FORGOTTEN_BEAST_779:LUNG +"CREATURE:FORGOTTEN_BEAST_779:HEART +"CREATURE:FORGOTTEN_BEAST_779:LIVER + CREATURE:FORGOTTEN_BEAST_779:GUT +$CREATURE:FORGOTTEN_BEAST_779:STOMACH +$CREATURE:FORGOTTEN_BEAST_779:GIZZARD +%CREATURE:FORGOTTEN_BEAST_779:PANCREAS +#CREATURE:FORGOTTEN_BEAST_779:SPLEEN +#CREATURE:FORGOTTEN_BEAST_779:KIDNEY +#CREATURE:FORGOTTEN_BEAST_780:MUSCLE + CREATURE:FORGOTTEN_BEAST_780:EYE +"CREATURE:FORGOTTEN_BEAST_780:BRAIN +!CREATURE:FORGOTTEN_BEAST_780:LUNG +"CREATURE:FORGOTTEN_BEAST_780:HEART +"CREATURE:FORGOTTEN_BEAST_780:LIVER + CREATURE:FORGOTTEN_BEAST_780:GUT +$CREATURE:FORGOTTEN_BEAST_780:STOMACH +$CREATURE:FORGOTTEN_BEAST_780:GIZZARD +%CREATURE:FORGOTTEN_BEAST_780:PANCREAS +#CREATURE:FORGOTTEN_BEAST_780:SPLEEN +#CREATURE:FORGOTTEN_BEAST_780:KIDNEY +#CREATURE:FORGOTTEN_BEAST_781:MUSCLE + CREATURE:FORGOTTEN_BEAST_781:EYE +"CREATURE:FORGOTTEN_BEAST_781:BRAIN +!CREATURE:FORGOTTEN_BEAST_781:LUNG +"CREATURE:FORGOTTEN_BEAST_781:HEART +"CREATURE:FORGOTTEN_BEAST_781:LIVER + CREATURE:FORGOTTEN_BEAST_781:GUT +$CREATURE:FORGOTTEN_BEAST_781:STOMACH +$CREATURE:FORGOTTEN_BEAST_781:GIZZARD +%CREATURE:FORGOTTEN_BEAST_781:PANCREAS +#CREATURE:FORGOTTEN_BEAST_781:SPLEEN +#CREATURE:FORGOTTEN_BEAST_781:KIDNEY +#CREATURE:FORGOTTEN_BEAST_782:MUSCLE + CREATURE:FORGOTTEN_BEAST_782:EYE +"CREATURE:FORGOTTEN_BEAST_782:BRAIN +!CREATURE:FORGOTTEN_BEAST_782:LUNG +"CREATURE:FORGOTTEN_BEAST_782:HEART +"CREATURE:FORGOTTEN_BEAST_782:LIVER + CREATURE:FORGOTTEN_BEAST_782:GUT +$CREATURE:FORGOTTEN_BEAST_782:STOMACH +$CREATURE:FORGOTTEN_BEAST_782:GIZZARD +%CREATURE:FORGOTTEN_BEAST_782:PANCREAS +#CREATURE:FORGOTTEN_BEAST_782:SPLEEN +#CREATURE:FORGOTTEN_BEAST_782:KIDNEY +#CREATURE:FORGOTTEN_BEAST_783:MUSCLE + CREATURE:FORGOTTEN_BEAST_783:EYE +"CREATURE:FORGOTTEN_BEAST_783:BRAIN +!CREATURE:FORGOTTEN_BEAST_783:LUNG +"CREATURE:FORGOTTEN_BEAST_783:HEART +"CREATURE:FORGOTTEN_BEAST_783:LIVER + CREATURE:FORGOTTEN_BEAST_783:GUT +$CREATURE:FORGOTTEN_BEAST_783:STOMACH +$CREATURE:FORGOTTEN_BEAST_783:GIZZARD +%CREATURE:FORGOTTEN_BEAST_783:PANCREAS +#CREATURE:FORGOTTEN_BEAST_783:SPLEEN +#CREATURE:FORGOTTEN_BEAST_783:KIDNEY +#CREATURE:FORGOTTEN_BEAST_784:MUSCLE + CREATURE:FORGOTTEN_BEAST_784:EYE +"CREATURE:FORGOTTEN_BEAST_784:BRAIN +!CREATURE:FORGOTTEN_BEAST_784:LUNG +"CREATURE:FORGOTTEN_BEAST_784:HEART +"CREATURE:FORGOTTEN_BEAST_784:LIVER + CREATURE:FORGOTTEN_BEAST_784:GUT +$CREATURE:FORGOTTEN_BEAST_784:STOMACH +$CREATURE:FORGOTTEN_BEAST_784:GIZZARD +%CREATURE:FORGOTTEN_BEAST_784:PANCREAS +#CREATURE:FORGOTTEN_BEAST_784:SPLEEN +#CREATURE:FORGOTTEN_BEAST_784:KIDNEY +#CREATURE:FORGOTTEN_BEAST_785:MUSCLE + CREATURE:FORGOTTEN_BEAST_785:EYE +"CREATURE:FORGOTTEN_BEAST_785:BRAIN +!CREATURE:FORGOTTEN_BEAST_785:LUNG +"CREATURE:FORGOTTEN_BEAST_785:HEART +"CREATURE:FORGOTTEN_BEAST_785:LIVER + CREATURE:FORGOTTEN_BEAST_785:GUT +$CREATURE:FORGOTTEN_BEAST_785:STOMACH +$CREATURE:FORGOTTEN_BEAST_785:GIZZARD +%CREATURE:FORGOTTEN_BEAST_785:PANCREAS +#CREATURE:FORGOTTEN_BEAST_785:SPLEEN +#CREATURE:FORGOTTEN_BEAST_785:KIDNEY +#CREATURE:FORGOTTEN_BEAST_786:MUSCLE + CREATURE:FORGOTTEN_BEAST_786:EYE +"CREATURE:FORGOTTEN_BEAST_786:BRAIN +!CREATURE:FORGOTTEN_BEAST_786:LUNG +"CREATURE:FORGOTTEN_BEAST_786:HEART +"CREATURE:FORGOTTEN_BEAST_786:LIVER + CREATURE:FORGOTTEN_BEAST_786:GUT +$CREATURE:FORGOTTEN_BEAST_786:STOMACH +$CREATURE:FORGOTTEN_BEAST_786:GIZZARD +%CREATURE:FORGOTTEN_BEAST_786:PANCREAS +#CREATURE:FORGOTTEN_BEAST_786:SPLEEN +#CREATURE:FORGOTTEN_BEAST_786:KIDNEY +#CREATURE:FORGOTTEN_BEAST_787:MUSCLE + CREATURE:FORGOTTEN_BEAST_787:EYE +"CREATURE:FORGOTTEN_BEAST_787:BRAIN +!CREATURE:FORGOTTEN_BEAST_787:LUNG +"CREATURE:FORGOTTEN_BEAST_787:HEART +"CREATURE:FORGOTTEN_BEAST_787:LIVER + CREATURE:FORGOTTEN_BEAST_787:GUT +$CREATURE:FORGOTTEN_BEAST_787:STOMACH +$CREATURE:FORGOTTEN_BEAST_787:GIZZARD +%CREATURE:FORGOTTEN_BEAST_787:PANCREAS +#CREATURE:FORGOTTEN_BEAST_787:SPLEEN +#CREATURE:FORGOTTEN_BEAST_787:KIDNEY +#CREATURE:FORGOTTEN_BEAST_789:MUSCLE + CREATURE:FORGOTTEN_BEAST_789:EYE +"CREATURE:FORGOTTEN_BEAST_789:BRAIN +!CREATURE:FORGOTTEN_BEAST_789:LUNG +"CREATURE:FORGOTTEN_BEAST_789:HEART +"CREATURE:FORGOTTEN_BEAST_789:LIVER + CREATURE:FORGOTTEN_BEAST_789:GUT +$CREATURE:FORGOTTEN_BEAST_789:STOMACH +$CREATURE:FORGOTTEN_BEAST_789:GIZZARD +%CREATURE:FORGOTTEN_BEAST_789:PANCREAS +#CREATURE:FORGOTTEN_BEAST_789:SPLEEN +#CREATURE:FORGOTTEN_BEAST_789:KIDNEY +#CREATURE:FORGOTTEN_BEAST_792:MUSCLE + CREATURE:FORGOTTEN_BEAST_792:EYE +"CREATURE:FORGOTTEN_BEAST_792:BRAIN +!CREATURE:FORGOTTEN_BEAST_792:LUNG +"CREATURE:FORGOTTEN_BEAST_792:HEART +"CREATURE:FORGOTTEN_BEAST_792:LIVER + CREATURE:FORGOTTEN_BEAST_792:GUT +$CREATURE:FORGOTTEN_BEAST_792:STOMACH +$CREATURE:FORGOTTEN_BEAST_792:GIZZARD +%CREATURE:FORGOTTEN_BEAST_792:PANCREAS +#CREATURE:FORGOTTEN_BEAST_792:SPLEEN +#CREATURE:FORGOTTEN_BEAST_792:KIDNEY +#CREATURE:FORGOTTEN_BEAST_793:MUSCLE + CREATURE:FORGOTTEN_BEAST_793:EYE +"CREATURE:FORGOTTEN_BEAST_793:BRAIN +!CREATURE:FORGOTTEN_BEAST_793:LUNG +"CREATURE:FORGOTTEN_BEAST_793:HEART +"CREATURE:FORGOTTEN_BEAST_793:LIVER + CREATURE:FORGOTTEN_BEAST_793:GUT +$CREATURE:FORGOTTEN_BEAST_793:STOMACH +$CREATURE:FORGOTTEN_BEAST_793:GIZZARD +%CREATURE:FORGOTTEN_BEAST_793:PANCREAS +#CREATURE:FORGOTTEN_BEAST_793:SPLEEN +#CREATURE:FORGOTTEN_BEAST_793:KIDNEY +#CREATURE:FORGOTTEN_BEAST_794:MUSCLE + CREATURE:FORGOTTEN_BEAST_794:EYE +"CREATURE:FORGOTTEN_BEAST_794:BRAIN +!CREATURE:FORGOTTEN_BEAST_794:LUNG +"CREATURE:FORGOTTEN_BEAST_794:HEART +"CREATURE:FORGOTTEN_BEAST_794:LIVER + CREATURE:FORGOTTEN_BEAST_794:GUT +$CREATURE:FORGOTTEN_BEAST_794:STOMACH +$CREATURE:FORGOTTEN_BEAST_794:GIZZARD +%CREATURE:FORGOTTEN_BEAST_794:PANCREAS +#CREATURE:FORGOTTEN_BEAST_794:SPLEEN +#CREATURE:FORGOTTEN_BEAST_794:KIDNEY +#CREATURE:FORGOTTEN_BEAST_795:MUSCLE + CREATURE:FORGOTTEN_BEAST_795:EYE +"CREATURE:FORGOTTEN_BEAST_795:BRAIN +!CREATURE:FORGOTTEN_BEAST_795:LUNG +"CREATURE:FORGOTTEN_BEAST_795:HEART +"CREATURE:FORGOTTEN_BEAST_795:LIVER + CREATURE:FORGOTTEN_BEAST_795:GUT +$CREATURE:FORGOTTEN_BEAST_795:STOMACH +$CREATURE:FORGOTTEN_BEAST_795:GIZZARD +%CREATURE:FORGOTTEN_BEAST_795:PANCREAS +#CREATURE:FORGOTTEN_BEAST_795:SPLEEN +#CREATURE:FORGOTTEN_BEAST_795:KIDNEY +#CREATURE:FORGOTTEN_BEAST_796:MUSCLE + CREATURE:FORGOTTEN_BEAST_796:EYE +"CREATURE:FORGOTTEN_BEAST_796:BRAIN +!CREATURE:FORGOTTEN_BEAST_796:LUNG +"CREATURE:FORGOTTEN_BEAST_796:HEART +"CREATURE:FORGOTTEN_BEAST_796:LIVER + CREATURE:FORGOTTEN_BEAST_796:GUT +$CREATURE:FORGOTTEN_BEAST_796:STOMACH +$CREATURE:FORGOTTEN_BEAST_796:GIZZARD +%CREATURE:FORGOTTEN_BEAST_796:PANCREAS +#CREATURE:FORGOTTEN_BEAST_796:SPLEEN +#CREATURE:FORGOTTEN_BEAST_796:KIDNEY +#CREATURE:FORGOTTEN_BEAST_799:MUSCLE + CREATURE:FORGOTTEN_BEAST_799:EYE +"CREATURE:FORGOTTEN_BEAST_799:BRAIN +!CREATURE:FORGOTTEN_BEAST_799:LUNG +"CREATURE:FORGOTTEN_BEAST_799:HEART +"CREATURE:FORGOTTEN_BEAST_799:LIVER + CREATURE:FORGOTTEN_BEAST_799:GUT +$CREATURE:FORGOTTEN_BEAST_799:STOMACH +$CREATURE:FORGOTTEN_BEAST_799:GIZZARD +%CREATURE:FORGOTTEN_BEAST_799:PANCREAS +#CREATURE:FORGOTTEN_BEAST_799:SPLEEN +#CREATURE:FORGOTTEN_BEAST_799:KIDNEY +#CREATURE:FORGOTTEN_BEAST_800:MUSCLE + CREATURE:FORGOTTEN_BEAST_800:EYE +"CREATURE:FORGOTTEN_BEAST_800:BRAIN +!CREATURE:FORGOTTEN_BEAST_800:LUNG +"CREATURE:FORGOTTEN_BEAST_800:HEART +"CREATURE:FORGOTTEN_BEAST_800:LIVER + CREATURE:FORGOTTEN_BEAST_800:GUT +$CREATURE:FORGOTTEN_BEAST_800:STOMACH +$CREATURE:FORGOTTEN_BEAST_800:GIZZARD +%CREATURE:FORGOTTEN_BEAST_800:PANCREAS +#CREATURE:FORGOTTEN_BEAST_800:SPLEEN +#CREATURE:FORGOTTEN_BEAST_800:KIDNEY +#CREATURE:FORGOTTEN_BEAST_801:MUSCLE + CREATURE:FORGOTTEN_BEAST_801:EYE +"CREATURE:FORGOTTEN_BEAST_801:BRAIN +!CREATURE:FORGOTTEN_BEAST_801:LUNG +"CREATURE:FORGOTTEN_BEAST_801:HEART +"CREATURE:FORGOTTEN_BEAST_801:LIVER + CREATURE:FORGOTTEN_BEAST_801:GUT +$CREATURE:FORGOTTEN_BEAST_801:STOMACH +$CREATURE:FORGOTTEN_BEAST_801:GIZZARD +%CREATURE:FORGOTTEN_BEAST_801:PANCREAS +#CREATURE:FORGOTTEN_BEAST_801:SPLEEN +#CREATURE:FORGOTTEN_BEAST_801:KIDNEY +#CREATURE:FORGOTTEN_BEAST_802:MUSCLE + CREATURE:FORGOTTEN_BEAST_802:EYE +"CREATURE:FORGOTTEN_BEAST_802:BRAIN +!CREATURE:FORGOTTEN_BEAST_802:LUNG +"CREATURE:FORGOTTEN_BEAST_802:HEART +"CREATURE:FORGOTTEN_BEAST_802:LIVER + CREATURE:FORGOTTEN_BEAST_802:GUT +$CREATURE:FORGOTTEN_BEAST_802:STOMACH +$CREATURE:FORGOTTEN_BEAST_802:GIZZARD +%CREATURE:FORGOTTEN_BEAST_802:PANCREAS +#CREATURE:FORGOTTEN_BEAST_802:SPLEEN +#CREATURE:FORGOTTEN_BEAST_802:KIDNEY +#CREATURE:FORGOTTEN_BEAST_803:MUSCLE + CREATURE:FORGOTTEN_BEAST_803:EYE +"CREATURE:FORGOTTEN_BEAST_803:BRAIN +!CREATURE:FORGOTTEN_BEAST_803:LUNG +"CREATURE:FORGOTTEN_BEAST_803:HEART +"CREATURE:FORGOTTEN_BEAST_803:LIVER + CREATURE:FORGOTTEN_BEAST_803:GUT +$CREATURE:FORGOTTEN_BEAST_803:STOMACH +$CREATURE:FORGOTTEN_BEAST_803:GIZZARD +%CREATURE:FORGOTTEN_BEAST_803:PANCREAS +#CREATURE:FORGOTTEN_BEAST_803:SPLEEN +#CREATURE:FORGOTTEN_BEAST_803:KIDNEY +#CREATURE:FORGOTTEN_BEAST_804:MUSCLE + CREATURE:FORGOTTEN_BEAST_804:EYE +"CREATURE:FORGOTTEN_BEAST_804:BRAIN +!CREATURE:FORGOTTEN_BEAST_804:LUNG +"CREATURE:FORGOTTEN_BEAST_804:HEART +"CREATURE:FORGOTTEN_BEAST_804:LIVER + CREATURE:FORGOTTEN_BEAST_804:GUT +$CREATURE:FORGOTTEN_BEAST_804:STOMACH +$CREATURE:FORGOTTEN_BEAST_804:GIZZARD +%CREATURE:FORGOTTEN_BEAST_804:PANCREAS +#CREATURE:FORGOTTEN_BEAST_804:SPLEEN +#CREATURE:FORGOTTEN_BEAST_804:KIDNEY +#CREATURE:FORGOTTEN_BEAST_806:MUSCLE + CREATURE:FORGOTTEN_BEAST_806:EYE +"CREATURE:FORGOTTEN_BEAST_806:BRAIN +!CREATURE:FORGOTTEN_BEAST_806:LUNG +"CREATURE:FORGOTTEN_BEAST_806:HEART +"CREATURE:FORGOTTEN_BEAST_806:LIVER + CREATURE:FORGOTTEN_BEAST_806:GUT +$CREATURE:FORGOTTEN_BEAST_806:STOMACH +$CREATURE:FORGOTTEN_BEAST_806:GIZZARD +%CREATURE:FORGOTTEN_BEAST_806:PANCREAS +#CREATURE:FORGOTTEN_BEAST_806:SPLEEN +#CREATURE:FORGOTTEN_BEAST_806:KIDNEY +#CREATURE:FORGOTTEN_BEAST_807:MUSCLE + CREATURE:FORGOTTEN_BEAST_807:EYE +"CREATURE:FORGOTTEN_BEAST_807:BRAIN +!CREATURE:FORGOTTEN_BEAST_807:LUNG +"CREATURE:FORGOTTEN_BEAST_807:HEART +"CREATURE:FORGOTTEN_BEAST_807:LIVER + CREATURE:FORGOTTEN_BEAST_807:GUT +$CREATURE:FORGOTTEN_BEAST_807:STOMACH +$CREATURE:FORGOTTEN_BEAST_807:GIZZARD +%CREATURE:FORGOTTEN_BEAST_807:PANCREAS +#CREATURE:FORGOTTEN_BEAST_807:SPLEEN +#CREATURE:FORGOTTEN_BEAST_807:KIDNEY +#CREATURE:FORGOTTEN_BEAST_809:MUSCLE + CREATURE:FORGOTTEN_BEAST_809:EYE +"CREATURE:FORGOTTEN_BEAST_809:BRAIN +!CREATURE:FORGOTTEN_BEAST_809:LUNG +"CREATURE:FORGOTTEN_BEAST_809:HEART +"CREATURE:FORGOTTEN_BEAST_809:LIVER + CREATURE:FORGOTTEN_BEAST_809:GUT +$CREATURE:FORGOTTEN_BEAST_809:STOMACH +$CREATURE:FORGOTTEN_BEAST_809:GIZZARD +%CREATURE:FORGOTTEN_BEAST_809:PANCREAS +#CREATURE:FORGOTTEN_BEAST_809:SPLEEN +#CREATURE:FORGOTTEN_BEAST_809:KIDNEY +#CREATURE:FORGOTTEN_BEAST_810:MUSCLE + CREATURE:FORGOTTEN_BEAST_810:EYE +"CREATURE:FORGOTTEN_BEAST_810:BRAIN +!CREATURE:FORGOTTEN_BEAST_810:LUNG +"CREATURE:FORGOTTEN_BEAST_810:HEART +"CREATURE:FORGOTTEN_BEAST_810:LIVER + CREATURE:FORGOTTEN_BEAST_810:GUT +$CREATURE:FORGOTTEN_BEAST_810:STOMACH +$CREATURE:FORGOTTEN_BEAST_810:GIZZARD +%CREATURE:FORGOTTEN_BEAST_810:PANCREAS +#CREATURE:FORGOTTEN_BEAST_810:SPLEEN +#CREATURE:FORGOTTEN_BEAST_810:KIDNEY +#CREATURE:FORGOTTEN_BEAST_811:MUSCLE + CREATURE:FORGOTTEN_BEAST_811:EYE +"CREATURE:FORGOTTEN_BEAST_811:BRAIN +!CREATURE:FORGOTTEN_BEAST_811:LUNG +"CREATURE:FORGOTTEN_BEAST_811:HEART +"CREATURE:FORGOTTEN_BEAST_811:LIVER + CREATURE:FORGOTTEN_BEAST_811:GUT +$CREATURE:FORGOTTEN_BEAST_811:STOMACH +$CREATURE:FORGOTTEN_BEAST_811:GIZZARD +%CREATURE:FORGOTTEN_BEAST_811:PANCREAS +#CREATURE:FORGOTTEN_BEAST_811:SPLEEN +#CREATURE:FORGOTTEN_BEAST_811:KIDNEY +#CREATURE:FORGOTTEN_BEAST_812:MUSCLE + CREATURE:FORGOTTEN_BEAST_812:EYE +"CREATURE:FORGOTTEN_BEAST_812:BRAIN +!CREATURE:FORGOTTEN_BEAST_812:LUNG +"CREATURE:FORGOTTEN_BEAST_812:HEART +"CREATURE:FORGOTTEN_BEAST_812:LIVER + CREATURE:FORGOTTEN_BEAST_812:GUT +$CREATURE:FORGOTTEN_BEAST_812:STOMACH +$CREATURE:FORGOTTEN_BEAST_812:GIZZARD +%CREATURE:FORGOTTEN_BEAST_812:PANCREAS +#CREATURE:FORGOTTEN_BEAST_812:SPLEEN +#CREATURE:FORGOTTEN_BEAST_812:KIDNEY +#CREATURE:FORGOTTEN_BEAST_815:MUSCLE + CREATURE:FORGOTTEN_BEAST_815:EYE +"CREATURE:FORGOTTEN_BEAST_815:BRAIN +!CREATURE:FORGOTTEN_BEAST_815:LUNG +"CREATURE:FORGOTTEN_BEAST_815:HEART +"CREATURE:FORGOTTEN_BEAST_815:LIVER + CREATURE:FORGOTTEN_BEAST_815:GUT +$CREATURE:FORGOTTEN_BEAST_815:STOMACH +$CREATURE:FORGOTTEN_BEAST_815:GIZZARD +%CREATURE:FORGOTTEN_BEAST_815:PANCREAS +#CREATURE:FORGOTTEN_BEAST_815:SPLEEN +#CREATURE:FORGOTTEN_BEAST_815:KIDNEY +#CREATURE:FORGOTTEN_BEAST_817:MUSCLE + CREATURE:FORGOTTEN_BEAST_817:EYE +"CREATURE:FORGOTTEN_BEAST_817:BRAIN +!CREATURE:FORGOTTEN_BEAST_817:LUNG +"CREATURE:FORGOTTEN_BEAST_817:HEART +"CREATURE:FORGOTTEN_BEAST_817:LIVER + CREATURE:FORGOTTEN_BEAST_817:GUT +$CREATURE:FORGOTTEN_BEAST_817:STOMACH +$CREATURE:FORGOTTEN_BEAST_817:GIZZARD +%CREATURE:FORGOTTEN_BEAST_817:PANCREAS +#CREATURE:FORGOTTEN_BEAST_817:SPLEEN +#CREATURE:FORGOTTEN_BEAST_817:KIDNEY +#CREATURE:FORGOTTEN_BEAST_818:MUSCLE + CREATURE:FORGOTTEN_BEAST_818:EYE +"CREATURE:FORGOTTEN_BEAST_818:BRAIN +!CREATURE:FORGOTTEN_BEAST_818:LUNG +"CREATURE:FORGOTTEN_BEAST_818:HEART +"CREATURE:FORGOTTEN_BEAST_818:LIVER + CREATURE:FORGOTTEN_BEAST_818:GUT +$CREATURE:FORGOTTEN_BEAST_818:STOMACH +$CREATURE:FORGOTTEN_BEAST_818:GIZZARD +%CREATURE:FORGOTTEN_BEAST_818:PANCREAS +#CREATURE:FORGOTTEN_BEAST_818:SPLEEN +#CREATURE:FORGOTTEN_BEAST_818:KIDNEY +#CREATURE:FORGOTTEN_BEAST_819:MUSCLE + CREATURE:FORGOTTEN_BEAST_819:EYE +"CREATURE:FORGOTTEN_BEAST_819:BRAIN +!CREATURE:FORGOTTEN_BEAST_819:LUNG +"CREATURE:FORGOTTEN_BEAST_819:HEART +"CREATURE:FORGOTTEN_BEAST_819:LIVER + CREATURE:FORGOTTEN_BEAST_819:GUT +$CREATURE:FORGOTTEN_BEAST_819:STOMACH +$CREATURE:FORGOTTEN_BEAST_819:GIZZARD +%CREATURE:FORGOTTEN_BEAST_819:PANCREAS +#CREATURE:FORGOTTEN_BEAST_819:SPLEEN +#CREATURE:FORGOTTEN_BEAST_819:KIDNEY +#CREATURE:FORGOTTEN_BEAST_820:MUSCLE + CREATURE:FORGOTTEN_BEAST_820:EYE +"CREATURE:FORGOTTEN_BEAST_820:BRAIN +!CREATURE:FORGOTTEN_BEAST_820:LUNG +"CREATURE:FORGOTTEN_BEAST_820:HEART +"CREATURE:FORGOTTEN_BEAST_820:LIVER + CREATURE:FORGOTTEN_BEAST_820:GUT +$CREATURE:FORGOTTEN_BEAST_820:STOMACH +$CREATURE:FORGOTTEN_BEAST_820:GIZZARD +%CREATURE:FORGOTTEN_BEAST_820:PANCREAS +#CREATURE:FORGOTTEN_BEAST_820:SPLEEN +#CREATURE:FORGOTTEN_BEAST_820:KIDNEY +#CREATURE:FORGOTTEN_BEAST_821:MUSCLE + CREATURE:FORGOTTEN_BEAST_821:EYE +"CREATURE:FORGOTTEN_BEAST_821:BRAIN +!CREATURE:FORGOTTEN_BEAST_821:LUNG +"CREATURE:FORGOTTEN_BEAST_821:HEART +"CREATURE:FORGOTTEN_BEAST_821:LIVER + CREATURE:FORGOTTEN_BEAST_821:GUT +$CREATURE:FORGOTTEN_BEAST_821:STOMACH +$CREATURE:FORGOTTEN_BEAST_821:GIZZARD +%CREATURE:FORGOTTEN_BEAST_821:PANCREAS +#CREATURE:FORGOTTEN_BEAST_821:SPLEEN +#CREATURE:FORGOTTEN_BEAST_821:KIDNEY +#CREATURE:FORGOTTEN_BEAST_822:MUSCLE + CREATURE:FORGOTTEN_BEAST_822:EYE +"CREATURE:FORGOTTEN_BEAST_822:BRAIN +!CREATURE:FORGOTTEN_BEAST_822:LUNG +"CREATURE:FORGOTTEN_BEAST_822:HEART +"CREATURE:FORGOTTEN_BEAST_822:LIVER + CREATURE:FORGOTTEN_BEAST_822:GUT +$CREATURE:FORGOTTEN_BEAST_822:STOMACH +$CREATURE:FORGOTTEN_BEAST_822:GIZZARD +%CREATURE:FORGOTTEN_BEAST_822:PANCREAS +#CREATURE:FORGOTTEN_BEAST_822:SPLEEN +#CREATURE:FORGOTTEN_BEAST_822:KIDNEY +#CREATURE:FORGOTTEN_BEAST_824:MUSCLE + CREATURE:FORGOTTEN_BEAST_824:EYE +"CREATURE:FORGOTTEN_BEAST_824:BRAIN +!CREATURE:FORGOTTEN_BEAST_824:LUNG +"CREATURE:FORGOTTEN_BEAST_824:HEART +"CREATURE:FORGOTTEN_BEAST_824:LIVER + CREATURE:FORGOTTEN_BEAST_824:GUT +$CREATURE:FORGOTTEN_BEAST_824:STOMACH +$CREATURE:FORGOTTEN_BEAST_824:GIZZARD +%CREATURE:FORGOTTEN_BEAST_824:PANCREAS +#CREATURE:FORGOTTEN_BEAST_824:SPLEEN +#CREATURE:FORGOTTEN_BEAST_824:KIDNEY +#CREATURE:FORGOTTEN_BEAST_825:MUSCLE + CREATURE:FORGOTTEN_BEAST_825:EYE +"CREATURE:FORGOTTEN_BEAST_825:BRAIN +!CREATURE:FORGOTTEN_BEAST_825:LUNG +"CREATURE:FORGOTTEN_BEAST_825:HEART +"CREATURE:FORGOTTEN_BEAST_825:LIVER + CREATURE:FORGOTTEN_BEAST_825:GUT +$CREATURE:FORGOTTEN_BEAST_825:STOMACH +$CREATURE:FORGOTTEN_BEAST_825:GIZZARD +%CREATURE:FORGOTTEN_BEAST_825:PANCREAS +#CREATURE:FORGOTTEN_BEAST_825:SPLEEN +#CREATURE:FORGOTTEN_BEAST_825:KIDNEY +#CREATURE:FORGOTTEN_BEAST_826:MUSCLE + CREATURE:FORGOTTEN_BEAST_826:EYE +"CREATURE:FORGOTTEN_BEAST_826:BRAIN +!CREATURE:FORGOTTEN_BEAST_826:LUNG +"CREATURE:FORGOTTEN_BEAST_826:HEART +"CREATURE:FORGOTTEN_BEAST_826:LIVER + CREATURE:FORGOTTEN_BEAST_826:GUT +$CREATURE:FORGOTTEN_BEAST_826:STOMACH +$CREATURE:FORGOTTEN_BEAST_826:GIZZARD +%CREATURE:FORGOTTEN_BEAST_826:PANCREAS +#CREATURE:FORGOTTEN_BEAST_826:SPLEEN +#CREATURE:FORGOTTEN_BEAST_826:KIDNEY +#CREATURE:FORGOTTEN_BEAST_827:MUSCLE + CREATURE:FORGOTTEN_BEAST_827:EYE +"CREATURE:FORGOTTEN_BEAST_827:BRAIN +!CREATURE:FORGOTTEN_BEAST_827:LUNG +"CREATURE:FORGOTTEN_BEAST_827:HEART +"CREATURE:FORGOTTEN_BEAST_827:LIVER + CREATURE:FORGOTTEN_BEAST_827:GUT +$CREATURE:FORGOTTEN_BEAST_827:STOMACH +$CREATURE:FORGOTTEN_BEAST_827:GIZZARD +%CREATURE:FORGOTTEN_BEAST_827:PANCREAS +#CREATURE:FORGOTTEN_BEAST_827:SPLEEN +#CREATURE:FORGOTTEN_BEAST_827:KIDNEY +#CREATURE:FORGOTTEN_BEAST_828:MUSCLE + CREATURE:FORGOTTEN_BEAST_828:EYE +"CREATURE:FORGOTTEN_BEAST_828:BRAIN +!CREATURE:FORGOTTEN_BEAST_828:LUNG +"CREATURE:FORGOTTEN_BEAST_828:HEART +"CREATURE:FORGOTTEN_BEAST_828:LIVER + CREATURE:FORGOTTEN_BEAST_828:GUT +$CREATURE:FORGOTTEN_BEAST_828:STOMACH +$CREATURE:FORGOTTEN_BEAST_828:GIZZARD +%CREATURE:FORGOTTEN_BEAST_828:PANCREAS +#CREATURE:FORGOTTEN_BEAST_828:SPLEEN +#CREATURE:FORGOTTEN_BEAST_828:KIDNEY +#CREATURE:FORGOTTEN_BEAST_831:MUSCLE + CREATURE:FORGOTTEN_BEAST_831:EYE +"CREATURE:FORGOTTEN_BEAST_831:BRAIN +!CREATURE:FORGOTTEN_BEAST_831:LUNG +"CREATURE:FORGOTTEN_BEAST_831:HEART +"CREATURE:FORGOTTEN_BEAST_831:LIVER + CREATURE:FORGOTTEN_BEAST_831:GUT +$CREATURE:FORGOTTEN_BEAST_831:STOMACH +$CREATURE:FORGOTTEN_BEAST_831:GIZZARD +%CREATURE:FORGOTTEN_BEAST_831:PANCREAS +#CREATURE:FORGOTTEN_BEAST_831:SPLEEN +#CREATURE:FORGOTTEN_BEAST_831:KIDNEY +#CREATURE:FORGOTTEN_BEAST_833:MUSCLE + CREATURE:FORGOTTEN_BEAST_833:EYE +"CREATURE:FORGOTTEN_BEAST_833:BRAIN +!CREATURE:FORGOTTEN_BEAST_833:LUNG +"CREATURE:FORGOTTEN_BEAST_833:HEART +"CREATURE:FORGOTTEN_BEAST_833:LIVER + CREATURE:FORGOTTEN_BEAST_833:GUT +$CREATURE:FORGOTTEN_BEAST_833:STOMACH +$CREATURE:FORGOTTEN_BEAST_833:GIZZARD +%CREATURE:FORGOTTEN_BEAST_833:PANCREAS +#CREATURE:FORGOTTEN_BEAST_833:SPLEEN +#CREATURE:FORGOTTEN_BEAST_833:KIDNEY +#CREATURE:FORGOTTEN_BEAST_835:MUSCLE + CREATURE:FORGOTTEN_BEAST_835:EYE +"CREATURE:FORGOTTEN_BEAST_835:BRAIN +!CREATURE:FORGOTTEN_BEAST_835:LUNG +"CREATURE:FORGOTTEN_BEAST_835:HEART +"CREATURE:FORGOTTEN_BEAST_835:LIVER + CREATURE:FORGOTTEN_BEAST_835:GUT +$CREATURE:FORGOTTEN_BEAST_835:STOMACH +$CREATURE:FORGOTTEN_BEAST_835:GIZZARD +%CREATURE:FORGOTTEN_BEAST_835:PANCREAS +#CREATURE:FORGOTTEN_BEAST_835:SPLEEN +#CREATURE:FORGOTTEN_BEAST_835:KIDNEY +#CREATURE:FORGOTTEN_BEAST_837:MUSCLE + CREATURE:FORGOTTEN_BEAST_837:EYE +"CREATURE:FORGOTTEN_BEAST_837:BRAIN +!CREATURE:FORGOTTEN_BEAST_837:LUNG +"CREATURE:FORGOTTEN_BEAST_837:HEART +"CREATURE:FORGOTTEN_BEAST_837:LIVER + CREATURE:FORGOTTEN_BEAST_837:GUT +$CREATURE:FORGOTTEN_BEAST_837:STOMACH +$CREATURE:FORGOTTEN_BEAST_837:GIZZARD +%CREATURE:FORGOTTEN_BEAST_837:PANCREAS +#CREATURE:FORGOTTEN_BEAST_837:SPLEEN +#CREATURE:FORGOTTEN_BEAST_837:KIDNEY +#CREATURE:FORGOTTEN_BEAST_838:MUSCLE + CREATURE:FORGOTTEN_BEAST_838:EYE +"CREATURE:FORGOTTEN_BEAST_838:BRAIN +!CREATURE:FORGOTTEN_BEAST_838:LUNG +"CREATURE:FORGOTTEN_BEAST_838:HEART +"CREATURE:FORGOTTEN_BEAST_838:LIVER + CREATURE:FORGOTTEN_BEAST_838:GUT +$CREATURE:FORGOTTEN_BEAST_838:STOMACH +$CREATURE:FORGOTTEN_BEAST_838:GIZZARD +%CREATURE:FORGOTTEN_BEAST_838:PANCREAS +#CREATURE:FORGOTTEN_BEAST_838:SPLEEN +#CREATURE:FORGOTTEN_BEAST_838:KIDNEY +#CREATURE:FORGOTTEN_BEAST_842:MUSCLE + CREATURE:FORGOTTEN_BEAST_842:EYE +"CREATURE:FORGOTTEN_BEAST_842:BRAIN +!CREATURE:FORGOTTEN_BEAST_842:LUNG +"CREATURE:FORGOTTEN_BEAST_842:HEART +"CREATURE:FORGOTTEN_BEAST_842:LIVER + CREATURE:FORGOTTEN_BEAST_842:GUT +$CREATURE:FORGOTTEN_BEAST_842:STOMACH +$CREATURE:FORGOTTEN_BEAST_842:GIZZARD +%CREATURE:FORGOTTEN_BEAST_842:PANCREAS +#CREATURE:FORGOTTEN_BEAST_842:SPLEEN +#CREATURE:FORGOTTEN_BEAST_842:KIDNEY +#CREATURE:FORGOTTEN_BEAST_843:MUSCLE + CREATURE:FORGOTTEN_BEAST_843:EYE +"CREATURE:FORGOTTEN_BEAST_843:BRAIN +!CREATURE:FORGOTTEN_BEAST_843:LUNG +"CREATURE:FORGOTTEN_BEAST_843:HEART +"CREATURE:FORGOTTEN_BEAST_843:LIVER + CREATURE:FORGOTTEN_BEAST_843:GUT +$CREATURE:FORGOTTEN_BEAST_843:STOMACH +$CREATURE:FORGOTTEN_BEAST_843:GIZZARD +%CREATURE:FORGOTTEN_BEAST_843:PANCREAS +#CREATURE:FORGOTTEN_BEAST_843:SPLEEN +#CREATURE:FORGOTTEN_BEAST_843:KIDNEY +#CREATURE:FORGOTTEN_BEAST_844:MUSCLE + CREATURE:FORGOTTEN_BEAST_844:EYE +"CREATURE:FORGOTTEN_BEAST_844:BRAIN +!CREATURE:FORGOTTEN_BEAST_844:LUNG +"CREATURE:FORGOTTEN_BEAST_844:HEART +"CREATURE:FORGOTTEN_BEAST_844:LIVER + CREATURE:FORGOTTEN_BEAST_844:GUT +$CREATURE:FORGOTTEN_BEAST_844:STOMACH +$CREATURE:FORGOTTEN_BEAST_844:GIZZARD +%CREATURE:FORGOTTEN_BEAST_844:PANCREAS +#CREATURE:FORGOTTEN_BEAST_844:SPLEEN +#CREATURE:FORGOTTEN_BEAST_844:KIDNEY +#CREATURE:FORGOTTEN_BEAST_845:MUSCLE + CREATURE:FORGOTTEN_BEAST_845:EYE +"CREATURE:FORGOTTEN_BEAST_845:BRAIN +!CREATURE:FORGOTTEN_BEAST_845:LUNG +"CREATURE:FORGOTTEN_BEAST_845:HEART +"CREATURE:FORGOTTEN_BEAST_845:LIVER + CREATURE:FORGOTTEN_BEAST_845:GUT +$CREATURE:FORGOTTEN_BEAST_845:STOMACH +$CREATURE:FORGOTTEN_BEAST_845:GIZZARD +%CREATURE:FORGOTTEN_BEAST_845:PANCREAS +#CREATURE:FORGOTTEN_BEAST_845:SPLEEN +#CREATURE:FORGOTTEN_BEAST_845:KIDNEY +#CREATURE:FORGOTTEN_BEAST_846:MUSCLE + CREATURE:FORGOTTEN_BEAST_846:EYE +"CREATURE:FORGOTTEN_BEAST_846:BRAIN +!CREATURE:FORGOTTEN_BEAST_846:LUNG +"CREATURE:FORGOTTEN_BEAST_846:HEART +"CREATURE:FORGOTTEN_BEAST_846:LIVER + CREATURE:FORGOTTEN_BEAST_846:GUT +$CREATURE:FORGOTTEN_BEAST_846:STOMACH +$CREATURE:FORGOTTEN_BEAST_846:GIZZARD +%CREATURE:FORGOTTEN_BEAST_846:PANCREAS +#CREATURE:FORGOTTEN_BEAST_846:SPLEEN +#CREATURE:FORGOTTEN_BEAST_846:KIDNEY +#CREATURE:FORGOTTEN_BEAST_847:MUSCLE + CREATURE:FORGOTTEN_BEAST_847:EYE +"CREATURE:FORGOTTEN_BEAST_847:BRAIN +!CREATURE:FORGOTTEN_BEAST_847:LUNG +"CREATURE:FORGOTTEN_BEAST_847:HEART +"CREATURE:FORGOTTEN_BEAST_847:LIVER + CREATURE:FORGOTTEN_BEAST_847:GUT +$CREATURE:FORGOTTEN_BEAST_847:STOMACH +$CREATURE:FORGOTTEN_BEAST_847:GIZZARD +%CREATURE:FORGOTTEN_BEAST_847:PANCREAS +#CREATURE:FORGOTTEN_BEAST_847:SPLEEN +#CREATURE:FORGOTTEN_BEAST_847:KIDNEY +#CREATURE:FORGOTTEN_BEAST_848:MUSCLE + CREATURE:FORGOTTEN_BEAST_848:EYE +"CREATURE:FORGOTTEN_BEAST_848:BRAIN +!CREATURE:FORGOTTEN_BEAST_848:LUNG +"CREATURE:FORGOTTEN_BEAST_848:HEART +"CREATURE:FORGOTTEN_BEAST_848:LIVER + CREATURE:FORGOTTEN_BEAST_848:GUT +$CREATURE:FORGOTTEN_BEAST_848:STOMACH +$CREATURE:FORGOTTEN_BEAST_848:GIZZARD +%CREATURE:FORGOTTEN_BEAST_848:PANCREAS +#CREATURE:FORGOTTEN_BEAST_848:SPLEEN +#CREATURE:FORGOTTEN_BEAST_848:KIDNEY +#CREATURE:FORGOTTEN_BEAST_849:MUSCLE + CREATURE:FORGOTTEN_BEAST_849:EYE +"CREATURE:FORGOTTEN_BEAST_849:BRAIN +!CREATURE:FORGOTTEN_BEAST_849:LUNG +"CREATURE:FORGOTTEN_BEAST_849:HEART +"CREATURE:FORGOTTEN_BEAST_849:LIVER + CREATURE:FORGOTTEN_BEAST_849:GUT +$CREATURE:FORGOTTEN_BEAST_849:STOMACH +$CREATURE:FORGOTTEN_BEAST_849:GIZZARD +%CREATURE:FORGOTTEN_BEAST_849:PANCREAS +#CREATURE:FORGOTTEN_BEAST_849:SPLEEN +#CREATURE:FORGOTTEN_BEAST_849:KIDNEY +#CREATURE:FORGOTTEN_BEAST_851:MUSCLE + CREATURE:FORGOTTEN_BEAST_851:EYE +"CREATURE:FORGOTTEN_BEAST_851:BRAIN +!CREATURE:FORGOTTEN_BEAST_851:LUNG +"CREATURE:FORGOTTEN_BEAST_851:HEART +"CREATURE:FORGOTTEN_BEAST_851:LIVER + CREATURE:FORGOTTEN_BEAST_851:GUT +$CREATURE:FORGOTTEN_BEAST_851:STOMACH +$CREATURE:FORGOTTEN_BEAST_851:GIZZARD +%CREATURE:FORGOTTEN_BEAST_851:PANCREAS +#CREATURE:FORGOTTEN_BEAST_851:SPLEEN +#CREATURE:FORGOTTEN_BEAST_851:KIDNEY +#CREATURE:FORGOTTEN_BEAST_853:MUSCLE + CREATURE:FORGOTTEN_BEAST_853:EYE +"CREATURE:FORGOTTEN_BEAST_853:BRAIN +!CREATURE:FORGOTTEN_BEAST_853:LUNG +"CREATURE:FORGOTTEN_BEAST_853:HEART +"CREATURE:FORGOTTEN_BEAST_853:LIVER + CREATURE:FORGOTTEN_BEAST_853:GUT +$CREATURE:FORGOTTEN_BEAST_853:STOMACH +$CREATURE:FORGOTTEN_BEAST_853:GIZZARD +%CREATURE:FORGOTTEN_BEAST_853:PANCREAS +#CREATURE:FORGOTTEN_BEAST_853:SPLEEN +#CREATURE:FORGOTTEN_BEAST_853:KIDNEY +#CREATURE:FORGOTTEN_BEAST_854:MUSCLE + CREATURE:FORGOTTEN_BEAST_854:EYE +"CREATURE:FORGOTTEN_BEAST_854:BRAIN +!CREATURE:FORGOTTEN_BEAST_854:LUNG +"CREATURE:FORGOTTEN_BEAST_854:HEART +"CREATURE:FORGOTTEN_BEAST_854:LIVER + CREATURE:FORGOTTEN_BEAST_854:GUT +$CREATURE:FORGOTTEN_BEAST_854:STOMACH +$CREATURE:FORGOTTEN_BEAST_854:GIZZARD +%CREATURE:FORGOTTEN_BEAST_854:PANCREAS +#CREATURE:FORGOTTEN_BEAST_854:SPLEEN +#CREATURE:FORGOTTEN_BEAST_854:KIDNEY +#CREATURE:FORGOTTEN_BEAST_855:MUSCLE + CREATURE:FORGOTTEN_BEAST_855:EYE +"CREATURE:FORGOTTEN_BEAST_855:BRAIN +!CREATURE:FORGOTTEN_BEAST_855:LUNG +"CREATURE:FORGOTTEN_BEAST_855:HEART +"CREATURE:FORGOTTEN_BEAST_855:LIVER + CREATURE:FORGOTTEN_BEAST_855:GUT +$CREATURE:FORGOTTEN_BEAST_855:STOMACH +$CREATURE:FORGOTTEN_BEAST_855:GIZZARD +%CREATURE:FORGOTTEN_BEAST_855:PANCREAS +#CREATURE:FORGOTTEN_BEAST_855:SPLEEN +#CREATURE:FORGOTTEN_BEAST_855:KIDNEY +#CREATURE:FORGOTTEN_BEAST_857:MUSCLE + CREATURE:FORGOTTEN_BEAST_857:EYE +"CREATURE:FORGOTTEN_BEAST_857:BRAIN +!CREATURE:FORGOTTEN_BEAST_857:LUNG +"CREATURE:FORGOTTEN_BEAST_857:HEART +"CREATURE:FORGOTTEN_BEAST_857:LIVER + CREATURE:FORGOTTEN_BEAST_857:GUT +$CREATURE:FORGOTTEN_BEAST_857:STOMACH +$CREATURE:FORGOTTEN_BEAST_857:GIZZARD +%CREATURE:FORGOTTEN_BEAST_857:PANCREAS +#CREATURE:FORGOTTEN_BEAST_857:SPLEEN +#CREATURE:FORGOTTEN_BEAST_857:KIDNEY +#CREATURE:FORGOTTEN_BEAST_858:MUSCLE + CREATURE:FORGOTTEN_BEAST_858:EYE +"CREATURE:FORGOTTEN_BEAST_858:BRAIN +!CREATURE:FORGOTTEN_BEAST_858:LUNG +"CREATURE:FORGOTTEN_BEAST_858:HEART +"CREATURE:FORGOTTEN_BEAST_858:LIVER + CREATURE:FORGOTTEN_BEAST_858:GUT +$CREATURE:FORGOTTEN_BEAST_858:STOMACH +$CREATURE:FORGOTTEN_BEAST_858:GIZZARD +%CREATURE:FORGOTTEN_BEAST_858:PANCREAS +#CREATURE:FORGOTTEN_BEAST_858:SPLEEN +#CREATURE:FORGOTTEN_BEAST_858:KIDNEY +#CREATURE:FORGOTTEN_BEAST_859:MUSCLE + CREATURE:FORGOTTEN_BEAST_859:EYE +"CREATURE:FORGOTTEN_BEAST_859:BRAIN +!CREATURE:FORGOTTEN_BEAST_859:LUNG +"CREATURE:FORGOTTEN_BEAST_859:HEART +"CREATURE:FORGOTTEN_BEAST_859:LIVER + CREATURE:FORGOTTEN_BEAST_859:GUT +$CREATURE:FORGOTTEN_BEAST_859:STOMACH +$CREATURE:FORGOTTEN_BEAST_859:GIZZARD +%CREATURE:FORGOTTEN_BEAST_859:PANCREAS +#CREATURE:FORGOTTEN_BEAST_859:SPLEEN +#CREATURE:FORGOTTEN_BEAST_859:KIDNEY +#CREATURE:FORGOTTEN_BEAST_860:MUSCLE + CREATURE:FORGOTTEN_BEAST_860:EYE +"CREATURE:FORGOTTEN_BEAST_860:BRAIN +!CREATURE:FORGOTTEN_BEAST_860:LUNG +"CREATURE:FORGOTTEN_BEAST_860:HEART +"CREATURE:FORGOTTEN_BEAST_860:LIVER + CREATURE:FORGOTTEN_BEAST_860:GUT +$CREATURE:FORGOTTEN_BEAST_860:STOMACH +$CREATURE:FORGOTTEN_BEAST_860:GIZZARD +%CREATURE:FORGOTTEN_BEAST_860:PANCREAS +#CREATURE:FORGOTTEN_BEAST_860:SPLEEN +#CREATURE:FORGOTTEN_BEAST_860:KIDNEY +#CREATURE:FORGOTTEN_BEAST_861:MUSCLE + CREATURE:FORGOTTEN_BEAST_861:EYE +"CREATURE:FORGOTTEN_BEAST_861:BRAIN +!CREATURE:FORGOTTEN_BEAST_861:LUNG +"CREATURE:FORGOTTEN_BEAST_861:HEART +"CREATURE:FORGOTTEN_BEAST_861:LIVER + CREATURE:FORGOTTEN_BEAST_861:GUT +$CREATURE:FORGOTTEN_BEAST_861:STOMACH +$CREATURE:FORGOTTEN_BEAST_861:GIZZARD +%CREATURE:FORGOTTEN_BEAST_861:PANCREAS +#CREATURE:FORGOTTEN_BEAST_861:SPLEEN +#CREATURE:FORGOTTEN_BEAST_861:KIDNEY +#CREATURE:FORGOTTEN_BEAST_862:MUSCLE + CREATURE:FORGOTTEN_BEAST_862:EYE +"CREATURE:FORGOTTEN_BEAST_862:BRAIN +!CREATURE:FORGOTTEN_BEAST_862:LUNG +"CREATURE:FORGOTTEN_BEAST_862:HEART +"CREATURE:FORGOTTEN_BEAST_862:LIVER + CREATURE:FORGOTTEN_BEAST_862:GUT +$CREATURE:FORGOTTEN_BEAST_862:STOMACH +$CREATURE:FORGOTTEN_BEAST_862:GIZZARD +%CREATURE:FORGOTTEN_BEAST_862:PANCREAS +#CREATURE:FORGOTTEN_BEAST_862:SPLEEN +#CREATURE:FORGOTTEN_BEAST_862:KIDNEY +#CREATURE:FORGOTTEN_BEAST_865:MUSCLE + CREATURE:FORGOTTEN_BEAST_865:EYE +"CREATURE:FORGOTTEN_BEAST_865:BRAIN +!CREATURE:FORGOTTEN_BEAST_865:LUNG +"CREATURE:FORGOTTEN_BEAST_865:HEART +"CREATURE:FORGOTTEN_BEAST_865:LIVER + CREATURE:FORGOTTEN_BEAST_865:GUT +$CREATURE:FORGOTTEN_BEAST_865:STOMACH +$CREATURE:FORGOTTEN_BEAST_865:GIZZARD +%CREATURE:FORGOTTEN_BEAST_865:PANCREAS +#CREATURE:FORGOTTEN_BEAST_865:SPLEEN +#CREATURE:FORGOTTEN_BEAST_865:KIDNEY +#CREATURE:FORGOTTEN_BEAST_866:MUSCLE + CREATURE:FORGOTTEN_BEAST_866:EYE +"CREATURE:FORGOTTEN_BEAST_866:BRAIN +!CREATURE:FORGOTTEN_BEAST_866:LUNG +"CREATURE:FORGOTTEN_BEAST_866:HEART +"CREATURE:FORGOTTEN_BEAST_866:LIVER + CREATURE:FORGOTTEN_BEAST_866:GUT +$CREATURE:FORGOTTEN_BEAST_866:STOMACH +$CREATURE:FORGOTTEN_BEAST_866:GIZZARD +%CREATURE:FORGOTTEN_BEAST_866:PANCREAS +#CREATURE:FORGOTTEN_BEAST_866:SPLEEN +#CREATURE:FORGOTTEN_BEAST_866:KIDNEY +#CREATURE:FORGOTTEN_BEAST_867:MUSCLE + CREATURE:FORGOTTEN_BEAST_867:EYE +"CREATURE:FORGOTTEN_BEAST_867:BRAIN +!CREATURE:FORGOTTEN_BEAST_867:LUNG +"CREATURE:FORGOTTEN_BEAST_867:HEART +"CREATURE:FORGOTTEN_BEAST_867:LIVER + CREATURE:FORGOTTEN_BEAST_867:GUT +$CREATURE:FORGOTTEN_BEAST_867:STOMACH +$CREATURE:FORGOTTEN_BEAST_867:GIZZARD +%CREATURE:FORGOTTEN_BEAST_867:PANCREAS +#CREATURE:FORGOTTEN_BEAST_867:SPLEEN +#CREATURE:FORGOTTEN_BEAST_867:KIDNEY +CREATURE:TITAN_1:MUSCLE +CREATURE:TITAN_1:EYE +CREATURE:TITAN_1:BRAIN +CREATURE:TITAN_1:LUNG +CREATURE:TITAN_1:HEART +CREATURE:TITAN_1:LIVER +CREATURE:TITAN_1:GUT +CREATURE:TITAN_1:STOMACH +CREATURE:TITAN_1:GIZZARD +CREATURE:TITAN_1:PANCREAS +CREATURE:TITAN_1:SPLEEN +CREATURE:TITAN_1:KIDNEY +CREATURE:TITAN_3:MUSCLE +CREATURE:TITAN_3:EYE +CREATURE:TITAN_3:BRAIN +CREATURE:TITAN_3:LUNG +CREATURE:TITAN_3:HEART +CREATURE:TITAN_3:LIVER +CREATURE:TITAN_3:GUT +CREATURE:TITAN_3:STOMACH +CREATURE:TITAN_3:GIZZARD +CREATURE:TITAN_3:PANCREAS +CREATURE:TITAN_3:SPLEEN +CREATURE:TITAN_3:KIDNEY +CREATURE:TITAN_4:MUSCLE +CREATURE:TITAN_4:EYE +CREATURE:TITAN_4:BRAIN +CREATURE:TITAN_4:LUNG +CREATURE:TITAN_4:HEART +CREATURE:TITAN_4:LIVER +CREATURE:TITAN_4:GUT +CREATURE:TITAN_4:STOMACH +CREATURE:TITAN_4:GIZZARD +CREATURE:TITAN_4:PANCREAS +CREATURE:TITAN_4:SPLEEN +CREATURE:TITAN_4:KIDNEY +CREATURE:TITAN_6:MUSCLE +CREATURE:TITAN_6:EYE +CREATURE:TITAN_6:BRAIN +CREATURE:TITAN_6:LUNG +CREATURE:TITAN_6:HEART +CREATURE:TITAN_6:LIVER +CREATURE:TITAN_6:GUT +CREATURE:TITAN_6:STOMACH +CREATURE:TITAN_6:GIZZARD +CREATURE:TITAN_6:PANCREAS +CREATURE:TITAN_6:SPLEEN +CREATURE:TITAN_6:KIDNEY +CREATURE:TITAN_7:MUSCLE +CREATURE:TITAN_7:EYE +CREATURE:TITAN_7:BRAIN +CREATURE:TITAN_7:LUNG +CREATURE:TITAN_7:HEART +CREATURE:TITAN_7:LIVER +CREATURE:TITAN_7:GUT +CREATURE:TITAN_7:STOMACH +CREATURE:TITAN_7:GIZZARD +CREATURE:TITAN_7:PANCREAS +CREATURE:TITAN_7:SPLEEN +CREATURE:TITAN_7:KIDNEY +CREATURE:TITAN_8:MUSCLE +CREATURE:TITAN_8:EYE +CREATURE:TITAN_8:BRAIN +CREATURE:TITAN_8:LUNG +CREATURE:TITAN_8:HEART +CREATURE:TITAN_8:LIVER +CREATURE:TITAN_8:GUT +CREATURE:TITAN_8:STOMACH +CREATURE:TITAN_8:GIZZARD +CREATURE:TITAN_8:PANCREAS +CREATURE:TITAN_8:SPLEEN +CREATURE:TITAN_8:KIDNEY +CREATURE:TITAN_9:MUSCLE +CREATURE:TITAN_9:EYE +CREATURE:TITAN_9:BRAIN +CREATURE:TITAN_9:LUNG +CREATURE:TITAN_9:HEART +CREATURE:TITAN_9:LIVER +CREATURE:TITAN_9:GUT +CREATURE:TITAN_9:STOMACH +CREATURE:TITAN_9:GIZZARD +CREATURE:TITAN_9:PANCREAS +CREATURE:TITAN_9:SPLEEN +CREATURE:TITAN_9:KIDNEY +CREATURE:TITAN_10:MUSCLE +CREATURE:TITAN_10:EYE +CREATURE:TITAN_10:BRAIN +CREATURE:TITAN_10:LUNG +CREATURE:TITAN_10:HEART +CREATURE:TITAN_10:LIVER +CREATURE:TITAN_10:GUT +CREATURE:TITAN_10:STOMACH +CREATURE:TITAN_10:GIZZARD +CREATURE:TITAN_10:PANCREAS +CREATURE:TITAN_10:SPLEEN +CREATURE:TITAN_10:KIDNEY +CREATURE:TITAN_11:MUSCLE +CREATURE:TITAN_11:EYE +CREATURE:TITAN_11:BRAIN +CREATURE:TITAN_11:LUNG +CREATURE:TITAN_11:HEART +CREATURE:TITAN_11:LIVER +CREATURE:TITAN_11:GUT +CREATURE:TITAN_11:STOMACH +CREATURE:TITAN_11:GIZZARD +CREATURE:TITAN_11:PANCREAS +CREATURE:TITAN_11:SPLEEN +CREATURE:TITAN_11:KIDNEY +CREATURE:TITAN_12:MUSCLE +CREATURE:TITAN_12:EYE +CREATURE:TITAN_12:BRAIN +CREATURE:TITAN_12:LUNG +CREATURE:TITAN_12:HEART +CREATURE:TITAN_12:LIVER +CREATURE:TITAN_12:GUT +CREATURE:TITAN_12:STOMACH +CREATURE:TITAN_12:GIZZARD +CREATURE:TITAN_12:PANCREAS +CREATURE:TITAN_12:SPLEEN +CREATURE:TITAN_12:KIDNEY +CREATURE:TITAN_13:MUSCLE +CREATURE:TITAN_13:EYE +CREATURE:TITAN_13:BRAIN +CREATURE:TITAN_13:LUNG +CREATURE:TITAN_13:HEART +CREATURE:TITAN_13:LIVER +CREATURE:TITAN_13:GUT +CREATURE:TITAN_13:STOMACH +CREATURE:TITAN_13:GIZZARD +CREATURE:TITAN_13:PANCREAS +CREATURE:TITAN_13:SPLEEN +CREATURE:TITAN_13:KIDNEY +CREATURE:TITAN_14:MUSCLE +CREATURE:TITAN_14:EYE +CREATURE:TITAN_14:BRAIN +CREATURE:TITAN_14:LUNG +CREATURE:TITAN_14:HEART +CREATURE:TITAN_14:LIVER +CREATURE:TITAN_14:GUT +CREATURE:TITAN_14:STOMACH +CREATURE:TITAN_14:GIZZARD +CREATURE:TITAN_14:PANCREAS +CREATURE:TITAN_14:SPLEEN +CREATURE:TITAN_14:KIDNEY +CREATURE:TITAN_15:MUSCLE +CREATURE:TITAN_15:EYE +CREATURE:TITAN_15:BRAIN +CREATURE:TITAN_15:LUNG +CREATURE:TITAN_15:HEART +CREATURE:TITAN_15:LIVER +CREATURE:TITAN_15:GUT +CREATURE:TITAN_15:STOMACH +CREATURE:TITAN_15:GIZZARD +CREATURE:TITAN_15:PANCREAS +CREATURE:TITAN_15:SPLEEN +CREATURE:TITAN_15:KIDNEY +CREATURE:TITAN_18:MUSCLE +CREATURE:TITAN_18:EYE +CREATURE:TITAN_18:BRAIN +CREATURE:TITAN_18:LUNG +CREATURE:TITAN_18:HEART +CREATURE:TITAN_18:LIVER +CREATURE:TITAN_18:GUT +CREATURE:TITAN_18:STOMACH +CREATURE:TITAN_18:GIZZARD +CREATURE:TITAN_18:PANCREAS +CREATURE:TITAN_18:SPLEEN +CREATURE:TITAN_18:KIDNEY +CREATURE:TITAN_19:MUSCLE +CREATURE:TITAN_19:EYE +CREATURE:TITAN_19:BRAIN +CREATURE:TITAN_19:LUNG +CREATURE:TITAN_19:HEART +CREATURE:TITAN_19:LIVER +CREATURE:TITAN_19:GUT +CREATURE:TITAN_19:STOMACH +CREATURE:TITAN_19:GIZZARD +CREATURE:TITAN_19:PANCREAS +CREATURE:TITAN_19:SPLEEN +CREATURE:TITAN_19:KIDNEY +CREATURE:TITAN_21:MUSCLE +CREATURE:TITAN_21:EYE +CREATURE:TITAN_21:BRAIN +CREATURE:TITAN_21:LUNG +CREATURE:TITAN_21:HEART +CREATURE:TITAN_21:LIVER +CREATURE:TITAN_21:GUT +CREATURE:TITAN_21:STOMACH +CREATURE:TITAN_21:GIZZARD +CREATURE:TITAN_21:PANCREAS +CREATURE:TITAN_21:SPLEEN +CREATURE:TITAN_21:KIDNEY +CREATURE:TITAN_23:MUSCLE +CREATURE:TITAN_23:EYE +CREATURE:TITAN_23:BRAIN +CREATURE:TITAN_23:LUNG +CREATURE:TITAN_23:HEART +CREATURE:TITAN_23:LIVER +CREATURE:TITAN_23:GUT +CREATURE:TITAN_23:STOMACH +CREATURE:TITAN_23:GIZZARD +CREATURE:TITAN_23:PANCREAS +CREATURE:TITAN_23:SPLEEN +CREATURE:TITAN_23:KIDNEY +CREATURE:TITAN_24:MUSCLE +CREATURE:TITAN_24:EYE +CREATURE:TITAN_24:BRAIN +CREATURE:TITAN_24:LUNG +CREATURE:TITAN_24:HEART +CREATURE:TITAN_24:LIVER +CREATURE:TITAN_24:GUT +CREATURE:TITAN_24:STOMACH +CREATURE:TITAN_24:GIZZARD +CREATURE:TITAN_24:PANCREAS +CREATURE:TITAN_24:SPLEEN +CREATURE:TITAN_24:KIDNEY +CREATURE:TITAN_25:MUSCLE +CREATURE:TITAN_25:EYE +CREATURE:TITAN_25:BRAIN +CREATURE:TITAN_25:LUNG +CREATURE:TITAN_25:HEART +CREATURE:TITAN_25:LIVER +CREATURE:TITAN_25:GUT +CREATURE:TITAN_25:STOMACH +CREATURE:TITAN_25:GIZZARD +CREATURE:TITAN_25:PANCREAS +CREATURE:TITAN_25:SPLEEN +CREATURE:TITAN_25:KIDNEY +CREATURE:TITAN_26:MUSCLE +CREATURE:TITAN_26:EYE +CREATURE:TITAN_26:BRAIN +CREATURE:TITAN_26:LUNG +CREATURE:TITAN_26:HEART +CREATURE:TITAN_26:LIVER +CREATURE:TITAN_26:GUT +CREATURE:TITAN_26:STOMACH +CREATURE:TITAN_26:GIZZARD +CREATURE:TITAN_26:PANCREAS +CREATURE:TITAN_26:SPLEEN +CREATURE:TITAN_26:KIDNEY +CREATURE:TITAN_27:MUSCLE +CREATURE:TITAN_27:EYE +CREATURE:TITAN_27:BRAIN +CREATURE:TITAN_27:LUNG +CREATURE:TITAN_27:HEART +CREATURE:TITAN_27:LIVER +CREATURE:TITAN_27:GUT +CREATURE:TITAN_27:STOMACH +CREATURE:TITAN_27:GIZZARD +CREATURE:TITAN_27:PANCREAS +CREATURE:TITAN_27:SPLEEN +CREATURE:TITAN_27:KIDNEY +CREATURE:TITAN_28:MUSCLE +CREATURE:TITAN_28:EYE +CREATURE:TITAN_28:BRAIN +CREATURE:TITAN_28:LUNG +CREATURE:TITAN_28:HEART +CREATURE:TITAN_28:LIVER +CREATURE:TITAN_28:GUT +CREATURE:TITAN_28:STOMACH +CREATURE:TITAN_28:GIZZARD +CREATURE:TITAN_28:PANCREAS +CREATURE:TITAN_28:SPLEEN +CREATURE:TITAN_28:KIDNEY +CREATURE:TITAN_29:MUSCLE +CREATURE:TITAN_29:EYE +CREATURE:TITAN_29:BRAIN +CREATURE:TITAN_29:LUNG +CREATURE:TITAN_29:HEART +CREATURE:TITAN_29:LIVER +CREATURE:TITAN_29:GUT +CREATURE:TITAN_29:STOMACH +CREATURE:TITAN_29:GIZZARD +CREATURE:TITAN_29:PANCREAS +CREATURE:TITAN_29:SPLEEN +CREATURE:TITAN_29:KIDNEY +CREATURE:TITAN_30:MUSCLE +CREATURE:TITAN_30:EYE +CREATURE:TITAN_30:BRAIN +CREATURE:TITAN_30:LUNG +CREATURE:TITAN_30:HEART +CREATURE:TITAN_30:LIVER +CREATURE:TITAN_30:GUT +CREATURE:TITAN_30:STOMACH +CREATURE:TITAN_30:GIZZARD +CREATURE:TITAN_30:PANCREAS +CREATURE:TITAN_30:SPLEEN +CREATURE:TITAN_30:KIDNEY +CREATURE:TITAN_32:MUSCLE +CREATURE:TITAN_32:EYE +CREATURE:TITAN_32:BRAIN +CREATURE:TITAN_32:LUNG +CREATURE:TITAN_32:HEART +CREATURE:TITAN_32:LIVER +CREATURE:TITAN_32:GUT +CREATURE:TITAN_32:STOMACH +CREATURE:TITAN_32:GIZZARD +CREATURE:TITAN_32:PANCREAS +CREATURE:TITAN_32:SPLEEN +CREATURE:TITAN_32:KIDNEY +CREATURE:TITAN_33:MUSCLE +CREATURE:TITAN_33:EYE +CREATURE:TITAN_33:BRAIN +CREATURE:TITAN_33:LUNG +CREATURE:TITAN_33:HEART +CREATURE:TITAN_33:LIVER +CREATURE:TITAN_33:GUT +CREATURE:TITAN_33:STOMACH +CREATURE:TITAN_33:GIZZARD +CREATURE:TITAN_33:PANCREAS +CREATURE:TITAN_33:SPLEEN +CREATURE:TITAN_33:KIDNEY +CREATURE:DEMON_7:MUSCLE +CREATURE:DEMON_7:EYE +CREATURE:DEMON_7:BRAIN +CREATURE:DEMON_7:LUNG +CREATURE:DEMON_7:HEART +CREATURE:DEMON_7:LIVER +CREATURE:DEMON_7:GUT +CREATURE:DEMON_7:STOMACH +CREATURE:DEMON_7:GIZZARD +CREATURE:DEMON_7:PANCREAS +CREATURE:DEMON_7:SPLEEN +CREATURE:DEMON_7:KIDNEY +CREATURE:DEMON_8:MUSCLE +CREATURE:DEMON_8:EYE +CREATURE:DEMON_8:BRAIN +CREATURE:DEMON_8:LUNG +CREATURE:DEMON_8:HEART +CREATURE:DEMON_8:LIVER +CREATURE:DEMON_8:GUT +CREATURE:DEMON_8:STOMACH +CREATURE:DEMON_8:GIZZARD +CREATURE:DEMON_8:PANCREAS +CREATURE:DEMON_8:SPLEEN +CREATURE:DEMON_8:KIDNEY +CREATURE:DEMON_9:MUSCLE +CREATURE:DEMON_9:EYE +CREATURE:DEMON_9:BRAIN +CREATURE:DEMON_9:LUNG +CREATURE:DEMON_9:HEART +CREATURE:DEMON_9:LIVER +CREATURE:DEMON_9:GUT +CREATURE:DEMON_9:STOMACH +CREATURE:DEMON_9:GIZZARD +CREATURE:DEMON_9:PANCREAS +CREATURE:DEMON_9:SPLEEN +CREATURE:DEMON_9:KIDNEY +CREATURE:DEMON_10:MUSCLE +CREATURE:DEMON_10:EYE +CREATURE:DEMON_10:BRAIN +CREATURE:DEMON_10:LUNG +CREATURE:DEMON_10:HEART +CREATURE:DEMON_10:LIVER +CREATURE:DEMON_10:GUT +CREATURE:DEMON_10:STOMACH +CREATURE:DEMON_10:GIZZARD +CREATURE:DEMON_10:PANCREAS +CREATURE:DEMON_10:SPLEEN +CREATURE:DEMON_10:KIDNEY +CREATURE:DEMON_11:MUSCLE +CREATURE:DEMON_11:EYE +CREATURE:DEMON_11:BRAIN +CREATURE:DEMON_11:LUNG +CREATURE:DEMON_11:HEART +CREATURE:DEMON_11:LIVER +CREATURE:DEMON_11:GUT +CREATURE:DEMON_11:STOMACH +CREATURE:DEMON_11:GIZZARD +CREATURE:DEMON_11:PANCREAS +CREATURE:DEMON_11:SPLEEN +CREATURE:DEMON_11:KIDNEY +CREATURE:DEMON_12:MUSCLE +CREATURE:DEMON_12:EYE +CREATURE:DEMON_12:BRAIN +CREATURE:DEMON_12:LUNG +CREATURE:DEMON_12:HEART +CREATURE:DEMON_12:LIVER +CREATURE:DEMON_12:GUT +CREATURE:DEMON_12:STOMACH +CREATURE:DEMON_12:GIZZARD +CREATURE:DEMON_12:PANCREAS +CREATURE:DEMON_12:SPLEEN +CREATURE:DEMON_12:KIDNEY +CREATURE:DEMON_13:MUSCLE +CREATURE:DEMON_13:EYE +CREATURE:DEMON_13:BRAIN +CREATURE:DEMON_13:LUNG +CREATURE:DEMON_13:HEART +CREATURE:DEMON_13:LIVER +CREATURE:DEMON_13:GUT +CREATURE:DEMON_13:STOMACH +CREATURE:DEMON_13:GIZZARD +CREATURE:DEMON_13:PANCREAS +CREATURE:DEMON_13:SPLEEN +CREATURE:DEMON_13:KIDNEY +CREATURE:DEMON_14:MUSCLE +CREATURE:DEMON_14:EYE +CREATURE:DEMON_14:BRAIN +CREATURE:DEMON_14:LUNG +CREATURE:DEMON_14:HEART +CREATURE:DEMON_14:LIVER +CREATURE:DEMON_14:GUT +CREATURE:DEMON_14:STOMACH +CREATURE:DEMON_14:GIZZARD +CREATURE:DEMON_14:PANCREAS +CREATURE:DEMON_14:SPLEEN +CREATURE:DEMON_14:KIDNEY +CREATURE:DEMON_15:MUSCLE +CREATURE:DEMON_15:EYE +CREATURE:DEMON_15:BRAIN +CREATURE:DEMON_15:LUNG +CREATURE:DEMON_15:HEART +CREATURE:DEMON_15:LIVER +CREATURE:DEMON_15:GUT +CREATURE:DEMON_15:STOMACH +CREATURE:DEMON_15:GIZZARD +CREATURE:DEMON_15:PANCREAS +CREATURE:DEMON_15:SPLEEN +CREATURE:DEMON_15:KIDNEY +CREATURE:DEMON_16:MUSCLE +CREATURE:DEMON_16:EYE +CREATURE:DEMON_16:BRAIN +CREATURE:DEMON_16:LUNG +CREATURE:DEMON_16:HEART +CREATURE:DEMON_16:LIVER +CREATURE:DEMON_16:GUT +CREATURE:DEMON_16:STOMACH +CREATURE:DEMON_16:GIZZARD +CREATURE:DEMON_16:PANCREAS +CREATURE:DEMON_16:SPLEEN +CREATURE:DEMON_16:KIDNEY +CREATURE:DEMON_17:MUSCLE +CREATURE:DEMON_17:EYE +CREATURE:DEMON_17:BRAIN +CREATURE:DEMON_17:LUNG +CREATURE:DEMON_17:HEART +CREATURE:DEMON_17:LIVER +CREATURE:DEMON_17:GUT +CREATURE:DEMON_17:STOMACH +CREATURE:DEMON_17:GIZZARD +CREATURE:DEMON_17:PANCREAS +CREATURE:DEMON_17:SPLEEN +CREATURE:DEMON_17:KIDNEY +CREATURE:DEMON_18:MUSCLE +CREATURE:DEMON_18:EYE +CREATURE:DEMON_18:BRAIN +CREATURE:DEMON_18:LUNG +CREATURE:DEMON_18:HEART +CREATURE:DEMON_18:LIVER +CREATURE:DEMON_18:GUT +CREATURE:DEMON_18:STOMACH +CREATURE:DEMON_18:GIZZARD +CREATURE:DEMON_18:PANCREAS +CREATURE:DEMON_18:SPLEEN +CREATURE:DEMON_18:KIDNEY +CREATURE:DEMON_19:MUSCLE +CREATURE:DEMON_19:EYE +CREATURE:DEMON_19:BRAIN +CREATURE:DEMON_19:LUNG +CREATURE:DEMON_19:HEART +CREATURE:DEMON_19:LIVER +CREATURE:DEMON_19:GUT +CREATURE:DEMON_19:STOMACH +CREATURE:DEMON_19:GIZZARD +CREATURE:DEMON_19:PANCREAS +CREATURE:DEMON_19:SPLEEN +CREATURE:DEMON_19:KIDNEY +CREATURE:DEMON_20:MUSCLE +CREATURE:DEMON_20:EYE +CREATURE:DEMON_20:BRAIN +CREATURE:DEMON_20:LUNG +CREATURE:DEMON_20:HEART +CREATURE:DEMON_20:LIVER +CREATURE:DEMON_20:GUT +CREATURE:DEMON_20:STOMACH +CREATURE:DEMON_20:GIZZARD +CREATURE:DEMON_20:PANCREAS +CREATURE:DEMON_20:SPLEEN +CREATURE:DEMON_20:KIDNEY +CREATURE:DEMON_21:MUSCLE +CREATURE:DEMON_21:EYE +CREATURE:DEMON_21:BRAIN +CREATURE:DEMON_21:LUNG +CREATURE:DEMON_21:HEART +CREATURE:DEMON_21:LIVER +CREATURE:DEMON_21:GUT +CREATURE:DEMON_21:STOMACH +CREATURE:DEMON_21:GIZZARD +CREATURE:DEMON_21:PANCREAS +CREATURE:DEMON_21:SPLEEN +CREATURE:DEMON_21:KIDNEY +CREATURE:DEMON_22:MUSCLE +CREATURE:DEMON_22:EYE +CREATURE:DEMON_22:BRAIN +CREATURE:DEMON_22:LUNG +CREATURE:DEMON_22:HEART +CREATURE:DEMON_22:LIVER +CREATURE:DEMON_22:GUT +CREATURE:DEMON_22:STOMACH +CREATURE:DEMON_22:GIZZARD +CREATURE:DEMON_22:PANCREAS +CREATURE:DEMON_22:SPLEEN +CREATURE:DEMON_22:KIDNEY +CREATURE:DEMON_23:MUSCLE +CREATURE:DEMON_23:EYE +CREATURE:DEMON_23:BRAIN +CREATURE:DEMON_23:LUNG +CREATURE:DEMON_23:HEART +CREATURE:DEMON_23:LIVER +CREATURE:DEMON_23:GUT +CREATURE:DEMON_23:STOMACH +CREATURE:DEMON_23:GIZZARD +CREATURE:DEMON_23:PANCREAS +CREATURE:DEMON_23:SPLEEN +CREATURE:DEMON_23:KIDNEY +CREATURE:DEMON_24:MUSCLE +CREATURE:DEMON_24:EYE +CREATURE:DEMON_24:BRAIN +CREATURE:DEMON_24:LUNG +CREATURE:DEMON_24:HEART +CREATURE:DEMON_24:LIVER +CREATURE:DEMON_24:GUT +CREATURE:DEMON_24:STOMACH +CREATURE:DEMON_24:GIZZARD +CREATURE:DEMON_24:PANCREAS +CREATURE:DEMON_24:SPLEEN +CREATURE:DEMON_24:KIDNEY +CREATURE:DEMON_25:MUSCLE +CREATURE:DEMON_25:EYE +CREATURE:DEMON_25:BRAIN +CREATURE:DEMON_25:LUNG +CREATURE:DEMON_25:HEART +CREATURE:DEMON_25:LIVER +CREATURE:DEMON_25:GUT +CREATURE:DEMON_25:STOMACH +CREATURE:DEMON_25:GIZZARD +CREATURE:DEMON_25:PANCREAS +CREATURE:DEMON_25:SPLEEN +CREATURE:DEMON_25:KIDNEY +CREATURE:DEMON_26:MUSCLE +CREATURE:DEMON_26:EYE +CREATURE:DEMON_26:BRAIN +CREATURE:DEMON_26:LUNG +CREATURE:DEMON_26:HEART +CREATURE:DEMON_26:LIVER +CREATURE:DEMON_26:GUT +CREATURE:DEMON_26:STOMACH +CREATURE:DEMON_26:GIZZARD +CREATURE:DEMON_26:PANCREAS +CREATURE:DEMON_26:SPLEEN +CREATURE:DEMON_26:KIDNEY +CREATURE:DEMON_27:MUSCLE +CREATURE:DEMON_27:EYE +CREATURE:DEMON_27:BRAIN +CREATURE:DEMON_27:LUNG +CREATURE:DEMON_27:HEART +CREATURE:DEMON_27:LIVER +CREATURE:DEMON_27:GUT +CREATURE:DEMON_27:STOMACH +CREATURE:DEMON_27:GIZZARD +CREATURE:DEMON_27:PANCREAS +CREATURE:DEMON_27:SPLEEN +CREATURE:DEMON_27:KIDNEY +CREATURE:DEMON_28:MUSCLE +CREATURE:DEMON_28:EYE +CREATURE:DEMON_28:BRAIN +CREATURE:DEMON_28:LUNG +CREATURE:DEMON_28:HEART +CREATURE:DEMON_28:LIVER +CREATURE:DEMON_28:GUT +CREATURE:DEMON_28:STOMACH +CREATURE:DEMON_28:GIZZARD +CREATURE:DEMON_28:PANCREAS +CREATURE:DEMON_28:SPLEEN +CREATURE:DEMON_28:KIDNEY +CREATURE:DEMON_29:MUSCLE +CREATURE:DEMON_29:EYE +CREATURE:DEMON_29:BRAIN +CREATURE:DEMON_29:LUNG +CREATURE:DEMON_29:HEART +CREATURE:DEMON_29:LIVER +CREATURE:DEMON_29:GUT +CREATURE:DEMON_29:STOMACH +CREATURE:DEMON_29:GIZZARD +CREATURE:DEMON_29:PANCREAS +CREATURE:DEMON_29:SPLEEN +CREATURE:DEMON_29:KIDNEY +CREATURE:DEMON_31:MUSCLE +CREATURE:DEMON_31:EYE +CREATURE:DEMON_31:BRAIN +CREATURE:DEMON_31:LUNG +CREATURE:DEMON_31:HEART +CREATURE:DEMON_31:LIVER +CREATURE:DEMON_31:GUT +CREATURE:DEMON_31:STOMACH +CREATURE:DEMON_31:GIZZARD +CREATURE:DEMON_31:PANCREAS +CREATURE:DEMON_31:SPLEEN +CREATURE:DEMON_31:KIDNEY +CREATURE:DEMON_32:MUSCLE +CREATURE:DEMON_32:EYE +CREATURE:DEMON_32:BRAIN +CREATURE:DEMON_32:LUNG +CREATURE:DEMON_32:HEART +CREATURE:DEMON_32:LIVER +CREATURE:DEMON_32:GUT +CREATURE:DEMON_32:STOMACH +CREATURE:DEMON_32:GIZZARD +CREATURE:DEMON_32:PANCREAS +CREATURE:DEMON_32:SPLEEN +CREATURE:DEMON_32:KIDNEY +CREATURE:DEMON_38:MUSCLE +CREATURE:DEMON_38:EYE +CREATURE:DEMON_38:BRAIN +CREATURE:DEMON_38:LUNG +CREATURE:DEMON_38:HEART +CREATURE:DEMON_38:LIVER +CREATURE:DEMON_38:GUT +CREATURE:DEMON_38:STOMACH +CREATURE:DEMON_38:GIZZARD +CREATURE:DEMON_38:PANCREAS +CREATURE:DEMON_38:SPLEEN +CREATURE:DEMON_38:KIDNEY +CREATURE:DEMON_39:MUSCLE +CREATURE:DEMON_39:EYE +CREATURE:DEMON_39:BRAIN +CREATURE:DEMON_39:LUNG +CREATURE:DEMON_39:HEART +CREATURE:DEMON_39:LIVER +CREATURE:DEMON_39:GUT +CREATURE:DEMON_39:STOMACH +CREATURE:DEMON_39:GIZZARD +CREATURE:DEMON_39:PANCREAS +CREATURE:DEMON_39:SPLEEN +CREATURE:DEMON_39:KIDNEY +CREATURE:DEMON_40:MUSCLE +CREATURE:DEMON_40:EYE +CREATURE:DEMON_40:BRAIN +CREATURE:DEMON_40:LUNG +CREATURE:DEMON_40:HEART +CREATURE:DEMON_40:LIVER +CREATURE:DEMON_40:GUT +CREATURE:DEMON_40:STOMACH +CREATURE:DEMON_40:GIZZARD +CREATURE:DEMON_40:PANCREAS +CREATURE:DEMON_40:SPLEEN +CREATURE:DEMON_40:KIDNEY +CREATURE:DEMON_41:MUSCLE +CREATURE:DEMON_41:EYE +CREATURE:DEMON_41:BRAIN +CREATURE:DEMON_41:LUNG +CREATURE:DEMON_41:HEART +CREATURE:DEMON_41:LIVER +CREATURE:DEMON_41:GUT +CREATURE:DEMON_41:STOMACH +CREATURE:DEMON_41:GIZZARD +CREATURE:DEMON_41:PANCREAS +CREATURE:DEMON_41:SPLEEN +CREATURE:DEMON_41:KIDNEY +CREATURE:DEMON_42:MUSCLE +CREATURE:DEMON_42:EYE +CREATURE:DEMON_42:BRAIN +CREATURE:DEMON_42:LUNG +CREATURE:DEMON_42:HEART +CREATURE:DEMON_42:LIVER +CREATURE:DEMON_42:GUT +CREATURE:DEMON_42:STOMACH +CREATURE:DEMON_42:GIZZARD +CREATURE:DEMON_42:PANCREAS +CREATURE:DEMON_42:SPLEEN +CREATURE:DEMON_42:KIDNEY +CREATURE:DEMON_43:MUSCLE +CREATURE:DEMON_43:EYE +CREATURE:DEMON_43:BRAIN +CREATURE:DEMON_43:LUNG +CREATURE:DEMON_43:HEART +CREATURE:DEMON_43:LIVER +CREATURE:DEMON_43:GUT +CREATURE:DEMON_43:STOMACH +CREATURE:DEMON_43:GIZZARD +CREATURE:DEMON_43:PANCREAS +CREATURE:DEMON_43:SPLEEN +CREATURE:DEMON_43:KIDNEY +CREATURE:DEMON_44:MUSCLE +CREATURE:DEMON_44:EYE +CREATURE:DEMON_44:BRAIN +CREATURE:DEMON_44:LUNG +CREATURE:DEMON_44:HEART +CREATURE:DEMON_44:LIVER +CREATURE:DEMON_44:GUT +CREATURE:DEMON_44:STOMACH +CREATURE:DEMON_44:GIZZARD +CREATURE:DEMON_44:PANCREAS +CREATURE:DEMON_44:SPLEEN +CREATURE:DEMON_44:KIDNEY +CREATURE:DEMON_45:MUSCLE +CREATURE:DEMON_45:EYE +CREATURE:DEMON_45:BRAIN +CREATURE:DEMON_45:LUNG +CREATURE:DEMON_45:HEART +CREATURE:DEMON_45:LIVER +CREATURE:DEMON_45:GUT +CREATURE:DEMON_45:STOMACH +CREATURE:DEMON_45:GIZZARD +CREATURE:DEMON_45:PANCREAS +CREATURE:DEMON_45:SPLEEN +CREATURE:DEMON_45:KIDNEY +CREATURE:DEMON_47:MUSCLE +CREATURE:DEMON_47:EYE +CREATURE:DEMON_47:BRAIN +CREATURE:DEMON_47:LUNG +CREATURE:DEMON_47:HEART +CREATURE:DEMON_47:LIVER +CREATURE:DEMON_47:GUT +CREATURE:DEMON_47:STOMACH +CREATURE:DEMON_47:GIZZARD +CREATURE:DEMON_47:PANCREAS +CREATURE:DEMON_47:SPLEEN +CREATURE:DEMON_47:KIDNEY +CREATURE:DEMON_48:MUSCLE +CREATURE:DEMON_48:EYE +CREATURE:DEMON_48:BRAIN +CREATURE:DEMON_48:LUNG +CREATURE:DEMON_48:HEART +CREATURE:DEMON_48:LIVER +CREATURE:DEMON_48:GUT +CREATURE:DEMON_48:STOMACH +CREATURE:DEMON_48:GIZZARD +CREATURE:DEMON_48:PANCREAS +CREATURE:DEMON_48:SPLEEN +CREATURE:DEMON_48:KIDNEY +CREATURE:DEMON_49:MUSCLE +CREATURE:DEMON_49:EYE +CREATURE:DEMON_49:BRAIN +CREATURE:DEMON_49:LUNG +CREATURE:DEMON_49:HEART +CREATURE:DEMON_49:LIVER +CREATURE:DEMON_49:GUT +CREATURE:DEMON_49:STOMACH +CREATURE:DEMON_49:GIZZARD +CREATURE:DEMON_49:PANCREAS +CREATURE:DEMON_49:SPLEEN +CREATURE:DEMON_49:KIDNEY +CREATURE:DEMON_52:MUSCLE +CREATURE:DEMON_52:EYE +CREATURE:DEMON_52:BRAIN +CREATURE:DEMON_52:LUNG +CREATURE:DEMON_52:HEART +CREATURE:DEMON_52:LIVER +CREATURE:DEMON_52:GUT +CREATURE:DEMON_52:STOMACH +CREATURE:DEMON_52:GIZZARD +CREATURE:DEMON_52:PANCREAS +CREATURE:DEMON_52:SPLEEN +CREATURE:DEMON_52:KIDNEY + CREATURE:NIGHT_CREATURE_1:MUSCLE +CREATURE:NIGHT_CREATURE_1:EYE +CREATURE:NIGHT_CREATURE_1:BRAIN +CREATURE:NIGHT_CREATURE_1:LUNG +CREATURE:NIGHT_CREATURE_1:HEART +CREATURE:NIGHT_CREATURE_1:LIVER +CREATURE:NIGHT_CREATURE_1:GUT +!CREATURE:NIGHT_CREATURE_1:STOMACH +!CREATURE:NIGHT_CREATURE_1:GIZZARD +"CREATURE:NIGHT_CREATURE_1:PANCREAS + CREATURE:NIGHT_CREATURE_1:SPLEEN + CREATURE:NIGHT_CREATURE_1:KIDNEY + CREATURE:NIGHT_CREATURE_2:MUSCLE +CREATURE:NIGHT_CREATURE_2:EYE +CREATURE:NIGHT_CREATURE_2:BRAIN +CREATURE:NIGHT_CREATURE_2:LUNG +CREATURE:NIGHT_CREATURE_2:HEART +CREATURE:NIGHT_CREATURE_2:LIVER +CREATURE:NIGHT_CREATURE_2:GUT +!CREATURE:NIGHT_CREATURE_2:STOMACH +!CREATURE:NIGHT_CREATURE_2:GIZZARD +"CREATURE:NIGHT_CREATURE_2:PANCREAS + CREATURE:NIGHT_CREATURE_2:SPLEEN + CREATURE:NIGHT_CREATURE_2:KIDNEY + CREATURE:NIGHT_CREATURE_3:MUSCLE +CREATURE:NIGHT_CREATURE_3:EYE +CREATURE:NIGHT_CREATURE_3:BRAIN +CREATURE:NIGHT_CREATURE_3:LUNG +CREATURE:NIGHT_CREATURE_3:HEART +CREATURE:NIGHT_CREATURE_3:LIVER +CREATURE:NIGHT_CREATURE_3:GUT +!CREATURE:NIGHT_CREATURE_3:STOMACH +!CREATURE:NIGHT_CREATURE_3:GIZZARD +"CREATURE:NIGHT_CREATURE_3:PANCREAS + CREATURE:NIGHT_CREATURE_3:SPLEEN + CREATURE:NIGHT_CREATURE_3:KIDNEY + CREATURE:NIGHT_CREATURE_4:MUSCLE +CREATURE:NIGHT_CREATURE_4:EYE +CREATURE:NIGHT_CREATURE_4:BRAIN +CREATURE:NIGHT_CREATURE_4:LUNG +CREATURE:NIGHT_CREATURE_4:HEART +CREATURE:NIGHT_CREATURE_4:LIVER +CREATURE:NIGHT_CREATURE_4:GUT +!CREATURE:NIGHT_CREATURE_4:STOMACH +!CREATURE:NIGHT_CREATURE_4:GIZZARD +"CREATURE:NIGHT_CREATURE_4:PANCREAS + CREATURE:NIGHT_CREATURE_4:SPLEEN + CREATURE:NIGHT_CREATURE_4:KIDNEY + CREATURE:NIGHT_CREATURE_5:MUSCLE +CREATURE:NIGHT_CREATURE_5:EYE +CREATURE:NIGHT_CREATURE_5:BRAIN +CREATURE:NIGHT_CREATURE_5:LUNG +CREATURE:NIGHT_CREATURE_5:HEART +CREATURE:NIGHT_CREATURE_5:LIVER +CREATURE:NIGHT_CREATURE_5:GUT +!CREATURE:NIGHT_CREATURE_5:STOMACH +!CREATURE:NIGHT_CREATURE_5:GIZZARD +"CREATURE:NIGHT_CREATURE_5:PANCREAS + CREATURE:NIGHT_CREATURE_5:SPLEEN + CREATURE:NIGHT_CREATURE_5:KIDNEY + CREATURE:NIGHT_CREATURE_6:MUSCLE +CREATURE:NIGHT_CREATURE_6:EYE +CREATURE:NIGHT_CREATURE_6:BRAIN +CREATURE:NIGHT_CREATURE_6:LUNG +CREATURE:NIGHT_CREATURE_6:HEART +CREATURE:NIGHT_CREATURE_6:LIVER +CREATURE:NIGHT_CREATURE_6:GUT +!CREATURE:NIGHT_CREATURE_6:STOMACH +!CREATURE:NIGHT_CREATURE_6:GIZZARD +"CREATURE:NIGHT_CREATURE_6:PANCREAS + CREATURE:NIGHT_CREATURE_6:SPLEEN + CREATURE:NIGHT_CREATURE_6:KIDNEY + CREATURE:NIGHT_CREATURE_7:MUSCLE +CREATURE:NIGHT_CREATURE_7:EYE +CREATURE:NIGHT_CREATURE_7:BRAIN +CREATURE:NIGHT_CREATURE_7:LUNG +CREATURE:NIGHT_CREATURE_7:HEART +CREATURE:NIGHT_CREATURE_7:LIVER +CREATURE:NIGHT_CREATURE_7:GUT +!CREATURE:NIGHT_CREATURE_7:STOMACH +!CREATURE:NIGHT_CREATURE_7:GIZZARD +"CREATURE:NIGHT_CREATURE_7:PANCREAS + CREATURE:NIGHT_CREATURE_7:SPLEEN + CREATURE:NIGHT_CREATURE_7:KIDNEY + CREATURE:NIGHT_CREATURE_8:MUSCLE +CREATURE:NIGHT_CREATURE_8:EYE +CREATURE:NIGHT_CREATURE_8:BRAIN +CREATURE:NIGHT_CREATURE_8:LUNG +CREATURE:NIGHT_CREATURE_8:HEART +CREATURE:NIGHT_CREATURE_8:LIVER +CREATURE:NIGHT_CREATURE_8:GUT +!CREATURE:NIGHT_CREATURE_8:STOMACH +!CREATURE:NIGHT_CREATURE_8:GIZZARD +"CREATURE:NIGHT_CREATURE_8:PANCREAS + CREATURE:NIGHT_CREATURE_8:SPLEEN + CREATURE:NIGHT_CREATURE_8:KIDNEY + CREATURE:NIGHT_CREATURE_9:MUSCLE +CREATURE:NIGHT_CREATURE_9:EYE +CREATURE:NIGHT_CREATURE_9:BRAIN +CREATURE:NIGHT_CREATURE_9:LUNG +CREATURE:NIGHT_CREATURE_9:HEART +CREATURE:NIGHT_CREATURE_9:LIVER +CREATURE:NIGHT_CREATURE_9:GUT +!CREATURE:NIGHT_CREATURE_9:STOMACH +!CREATURE:NIGHT_CREATURE_9:GIZZARD +"CREATURE:NIGHT_CREATURE_9:PANCREAS + CREATURE:NIGHT_CREATURE_9:SPLEEN + CREATURE:NIGHT_CREATURE_9:KIDNEY +!CREATURE:NIGHT_CREATURE_10:MUSCLE +CREATURE:NIGHT_CREATURE_10:EYE + CREATURE:NIGHT_CREATURE_10:BRAIN +CREATURE:NIGHT_CREATURE_10:LUNG + CREATURE:NIGHT_CREATURE_10:HEART + CREATURE:NIGHT_CREATURE_10:LIVER +CREATURE:NIGHT_CREATURE_10:GUT +"CREATURE:NIGHT_CREATURE_10:STOMACH +"CREATURE:NIGHT_CREATURE_10:GIZZARD +#CREATURE:NIGHT_CREATURE_10:PANCREAS +!CREATURE:NIGHT_CREATURE_10:SPLEEN +!CREATURE:NIGHT_CREATURE_10:KIDNEY +!CREATURE:NIGHT_CREATURE_11:MUSCLE +CREATURE:NIGHT_CREATURE_11:EYE + CREATURE:NIGHT_CREATURE_11:BRAIN +CREATURE:NIGHT_CREATURE_11:LUNG + CREATURE:NIGHT_CREATURE_11:HEART + CREATURE:NIGHT_CREATURE_11:LIVER +CREATURE:NIGHT_CREATURE_11:GUT +"CREATURE:NIGHT_CREATURE_11:STOMACH +"CREATURE:NIGHT_CREATURE_11:GIZZARD +#CREATURE:NIGHT_CREATURE_11:PANCREAS +!CREATURE:NIGHT_CREATURE_11:SPLEEN +!CREATURE:NIGHT_CREATURE_11:KIDNEY +!CREATURE:NIGHT_CREATURE_12:MUSCLE +CREATURE:NIGHT_CREATURE_12:EYE + CREATURE:NIGHT_CREATURE_12:BRAIN +CREATURE:NIGHT_CREATURE_12:LUNG + CREATURE:NIGHT_CREATURE_12:HEART + CREATURE:NIGHT_CREATURE_12:LIVER +CREATURE:NIGHT_CREATURE_12:GUT +"CREATURE:NIGHT_CREATURE_12:STOMACH +"CREATURE:NIGHT_CREATURE_12:GIZZARD +#CREATURE:NIGHT_CREATURE_12:PANCREAS +!CREATURE:NIGHT_CREATURE_12:SPLEEN +!CREATURE:NIGHT_CREATURE_12:KIDNEY +!CREATURE:NIGHT_CREATURE_13:MUSCLE +CREATURE:NIGHT_CREATURE_13:EYE + CREATURE:NIGHT_CREATURE_13:BRAIN +CREATURE:NIGHT_CREATURE_13:LUNG + CREATURE:NIGHT_CREATURE_13:HEART + CREATURE:NIGHT_CREATURE_13:LIVER +CREATURE:NIGHT_CREATURE_13:GUT +"CREATURE:NIGHT_CREATURE_13:STOMACH +"CREATURE:NIGHT_CREATURE_13:GIZZARD +#CREATURE:NIGHT_CREATURE_13:PANCREAS +!CREATURE:NIGHT_CREATURE_13:SPLEEN +!CREATURE:NIGHT_CREATURE_13:KIDNEY +!CREATURE:NIGHT_CREATURE_14:MUSCLE +CREATURE:NIGHT_CREATURE_14:EYE + CREATURE:NIGHT_CREATURE_14:BRAIN +CREATURE:NIGHT_CREATURE_14:LUNG + CREATURE:NIGHT_CREATURE_14:HEART + CREATURE:NIGHT_CREATURE_14:LIVER +CREATURE:NIGHT_CREATURE_14:GUT +"CREATURE:NIGHT_CREATURE_14:STOMACH +"CREATURE:NIGHT_CREATURE_14:GIZZARD +#CREATURE:NIGHT_CREATURE_14:PANCREAS +!CREATURE:NIGHT_CREATURE_14:SPLEEN +!CREATURE:NIGHT_CREATURE_14:KIDNEY +!CREATURE:NIGHT_CREATURE_15:MUSCLE +CREATURE:NIGHT_CREATURE_15:EYE + CREATURE:NIGHT_CREATURE_15:BRAIN +CREATURE:NIGHT_CREATURE_15:LUNG + CREATURE:NIGHT_CREATURE_15:HEART + CREATURE:NIGHT_CREATURE_15:LIVER +CREATURE:NIGHT_CREATURE_15:GUT +"CREATURE:NIGHT_CREATURE_15:STOMACH +"CREATURE:NIGHT_CREATURE_15:GIZZARD +#CREATURE:NIGHT_CREATURE_15:PANCREAS +!CREATURE:NIGHT_CREATURE_15:SPLEEN +!CREATURE:NIGHT_CREATURE_15:KIDNEY +!CREATURE:NIGHT_CREATURE_16:MUSCLE +CREATURE:NIGHT_CREATURE_16:EYE + CREATURE:NIGHT_CREATURE_16:BRAIN +CREATURE:NIGHT_CREATURE_16:LUNG + CREATURE:NIGHT_CREATURE_16:HEART + CREATURE:NIGHT_CREATURE_16:LIVER +CREATURE:NIGHT_CREATURE_16:GUT +"CREATURE:NIGHT_CREATURE_16:STOMACH +"CREATURE:NIGHT_CREATURE_16:GIZZARD +#CREATURE:NIGHT_CREATURE_16:PANCREAS +!CREATURE:NIGHT_CREATURE_16:SPLEEN +!CREATURE:NIGHT_CREATURE_16:KIDNEY +!CREATURE:NIGHT_CREATURE_17:MUSCLE +CREATURE:NIGHT_CREATURE_17:EYE + CREATURE:NIGHT_CREATURE_17:BRAIN +CREATURE:NIGHT_CREATURE_17:LUNG + CREATURE:NIGHT_CREATURE_17:HEART + CREATURE:NIGHT_CREATURE_17:LIVER +CREATURE:NIGHT_CREATURE_17:GUT +"CREATURE:NIGHT_CREATURE_17:STOMACH +"CREATURE:NIGHT_CREATURE_17:GIZZARD +#CREATURE:NIGHT_CREATURE_17:PANCREAS +!CREATURE:NIGHT_CREATURE_17:SPLEEN +!CREATURE:NIGHT_CREATURE_17:KIDNEY +!CREATURE:NIGHT_CREATURE_18:MUSCLE +CREATURE:NIGHT_CREATURE_18:EYE + CREATURE:NIGHT_CREATURE_18:BRAIN +CREATURE:NIGHT_CREATURE_18:LUNG + CREATURE:NIGHT_CREATURE_18:HEART + CREATURE:NIGHT_CREATURE_18:LIVER +CREATURE:NIGHT_CREATURE_18:GUT +"CREATURE:NIGHT_CREATURE_18:STOMACH +"CREATURE:NIGHT_CREATURE_18:GIZZARD +#CREATURE:NIGHT_CREATURE_18:PANCREAS +!CREATURE:NIGHT_CREATURE_18:SPLEEN +!CREATURE:NIGHT_CREATURE_18:KIDNEY +!CREATURE:NIGHT_CREATURE_19:MUSCLE +CREATURE:NIGHT_CREATURE_19:EYE + CREATURE:NIGHT_CREATURE_19:BRAIN +CREATURE:NIGHT_CREATURE_19:LUNG + CREATURE:NIGHT_CREATURE_19:HEART + CREATURE:NIGHT_CREATURE_19:LIVER +CREATURE:NIGHT_CREATURE_19:GUT +"CREATURE:NIGHT_CREATURE_19:STOMACH +"CREATURE:NIGHT_CREATURE_19:GIZZARD +#CREATURE:NIGHT_CREATURE_19:PANCREAS +!CREATURE:NIGHT_CREATURE_19:SPLEEN +!CREATURE:NIGHT_CREATURE_19:KIDNEY +!CREATURE:NIGHT_CREATURE_20:MUSCLE +CREATURE:NIGHT_CREATURE_20:EYE + CREATURE:NIGHT_CREATURE_20:BRAIN +CREATURE:NIGHT_CREATURE_20:LUNG + CREATURE:NIGHT_CREATURE_20:HEART + CREATURE:NIGHT_CREATURE_20:LIVER +CREATURE:NIGHT_CREATURE_20:GUT +"CREATURE:NIGHT_CREATURE_20:STOMACH +"CREATURE:NIGHT_CREATURE_20:GIZZARD +#CREATURE:NIGHT_CREATURE_20:PANCREAS +!CREATURE:NIGHT_CREATURE_20:SPLEEN +!CREATURE:NIGHT_CREATURE_20:KIDNEY +!CREATURE:NIGHT_CREATURE_21:MUSCLE +CREATURE:NIGHT_CREATURE_21:EYE + CREATURE:NIGHT_CREATURE_21:BRAIN +CREATURE:NIGHT_CREATURE_21:LUNG + CREATURE:NIGHT_CREATURE_21:HEART + CREATURE:NIGHT_CREATURE_21:LIVER +CREATURE:NIGHT_CREATURE_21:GUT +"CREATURE:NIGHT_CREATURE_21:STOMACH +"CREATURE:NIGHT_CREATURE_21:GIZZARD +#CREATURE:NIGHT_CREATURE_21:PANCREAS +!CREATURE:NIGHT_CREATURE_21:SPLEEN +!CREATURE:NIGHT_CREATURE_21:KIDNEY +!CREATURE:NIGHT_CREATURE_22:MUSCLE +CREATURE:NIGHT_CREATURE_22:EYE + CREATURE:NIGHT_CREATURE_22:BRAIN +CREATURE:NIGHT_CREATURE_22:LUNG + CREATURE:NIGHT_CREATURE_22:HEART + CREATURE:NIGHT_CREATURE_22:LIVER +CREATURE:NIGHT_CREATURE_22:GUT +"CREATURE:NIGHT_CREATURE_22:STOMACH +"CREATURE:NIGHT_CREATURE_22:GIZZARD +#CREATURE:NIGHT_CREATURE_22:PANCREAS +!CREATURE:NIGHT_CREATURE_22:SPLEEN +!CREATURE:NIGHT_CREATURE_22:KIDNEY +!CREATURE:NIGHT_CREATURE_23:MUSCLE +CREATURE:NIGHT_CREATURE_23:EYE + CREATURE:NIGHT_CREATURE_23:BRAIN +CREATURE:NIGHT_CREATURE_23:LUNG + CREATURE:NIGHT_CREATURE_23:HEART + CREATURE:NIGHT_CREATURE_23:LIVER +CREATURE:NIGHT_CREATURE_23:GUT +"CREATURE:NIGHT_CREATURE_23:STOMACH +"CREATURE:NIGHT_CREATURE_23:GIZZARD +#CREATURE:NIGHT_CREATURE_23:PANCREAS +!CREATURE:NIGHT_CREATURE_23:SPLEEN +!CREATURE:NIGHT_CREATURE_23:KIDNEY +!CREATURE:NIGHT_CREATURE_24:MUSCLE +CREATURE:NIGHT_CREATURE_24:EYE + CREATURE:NIGHT_CREATURE_24:BRAIN +CREATURE:NIGHT_CREATURE_24:LUNG + CREATURE:NIGHT_CREATURE_24:HEART + CREATURE:NIGHT_CREATURE_24:LIVER +CREATURE:NIGHT_CREATURE_24:GUT +"CREATURE:NIGHT_CREATURE_24:STOMACH +"CREATURE:NIGHT_CREATURE_24:GIZZARD +#CREATURE:NIGHT_CREATURE_24:PANCREAS +!CREATURE:NIGHT_CREATURE_24:SPLEEN +!CREATURE:NIGHT_CREATURE_24:KIDNEY +!CREATURE:NIGHT_CREATURE_25:MUSCLE +CREATURE:NIGHT_CREATURE_25:EYE + CREATURE:NIGHT_CREATURE_25:BRAIN +CREATURE:NIGHT_CREATURE_25:LUNG + CREATURE:NIGHT_CREATURE_25:HEART + CREATURE:NIGHT_CREATURE_25:LIVER +CREATURE:NIGHT_CREATURE_25:GUT +"CREATURE:NIGHT_CREATURE_25:STOMACH +"CREATURE:NIGHT_CREATURE_25:GIZZARD +#CREATURE:NIGHT_CREATURE_25:PANCREAS +!CREATURE:NIGHT_CREATURE_25:SPLEEN +!CREATURE:NIGHT_CREATURE_25:KIDNEY +!CREATURE:NIGHT_CREATURE_26:MUSCLE +CREATURE:NIGHT_CREATURE_26:EYE + CREATURE:NIGHT_CREATURE_26:BRAIN +CREATURE:NIGHT_CREATURE_26:LUNG + CREATURE:NIGHT_CREATURE_26:HEART + CREATURE:NIGHT_CREATURE_26:LIVER +CREATURE:NIGHT_CREATURE_26:GUT +"CREATURE:NIGHT_CREATURE_26:STOMACH +"CREATURE:NIGHT_CREATURE_26:GIZZARD +#CREATURE:NIGHT_CREATURE_26:PANCREAS +!CREATURE:NIGHT_CREATURE_26:SPLEEN +!CREATURE:NIGHT_CREATURE_26:KIDNEY +!CREATURE:NIGHT_CREATURE_27:MUSCLE +CREATURE:NIGHT_CREATURE_27:EYE + CREATURE:NIGHT_CREATURE_27:BRAIN +CREATURE:NIGHT_CREATURE_27:LUNG + CREATURE:NIGHT_CREATURE_27:HEART + CREATURE:NIGHT_CREATURE_27:LIVER +CREATURE:NIGHT_CREATURE_27:GUT +"CREATURE:NIGHT_CREATURE_27:STOMACH +"CREATURE:NIGHT_CREATURE_27:GIZZARD +#CREATURE:NIGHT_CREATURE_27:PANCREAS +!CREATURE:NIGHT_CREATURE_27:SPLEEN +!CREATURE:NIGHT_CREATURE_27:KIDNEY +!CREATURE:NIGHT_CREATURE_28:MUSCLE +CREATURE:NIGHT_CREATURE_28:EYE + CREATURE:NIGHT_CREATURE_28:BRAIN +CREATURE:NIGHT_CREATURE_28:LUNG + CREATURE:NIGHT_CREATURE_28:HEART + CREATURE:NIGHT_CREATURE_28:LIVER +CREATURE:NIGHT_CREATURE_28:GUT +"CREATURE:NIGHT_CREATURE_28:STOMACH +"CREATURE:NIGHT_CREATURE_28:GIZZARD +#CREATURE:NIGHT_CREATURE_28:PANCREAS +!CREATURE:NIGHT_CREATURE_28:SPLEEN +!CREATURE:NIGHT_CREATURE_28:KIDNEY +!CREATURE:NIGHT_CREATURE_29:MUSCLE +CREATURE:NIGHT_CREATURE_29:EYE + CREATURE:NIGHT_CREATURE_29:BRAIN +CREATURE:NIGHT_CREATURE_29:LUNG + CREATURE:NIGHT_CREATURE_29:HEART + CREATURE:NIGHT_CREATURE_29:LIVER +CREATURE:NIGHT_CREATURE_29:GUT +"CREATURE:NIGHT_CREATURE_29:STOMACH +"CREATURE:NIGHT_CREATURE_29:GIZZARD +#CREATURE:NIGHT_CREATURE_29:PANCREAS +!CREATURE:NIGHT_CREATURE_29:SPLEEN +!CREATURE:NIGHT_CREATURE_29:KIDNEY +!CREATURE:NIGHT_CREATURE_30:MUSCLE +CREATURE:NIGHT_CREATURE_30:EYE + CREATURE:NIGHT_CREATURE_30:BRAIN +CREATURE:NIGHT_CREATURE_30:LUNG + CREATURE:NIGHT_CREATURE_30:HEART + CREATURE:NIGHT_CREATURE_30:LIVER +CREATURE:NIGHT_CREATURE_30:GUT +"CREATURE:NIGHT_CREATURE_30:STOMACH +"CREATURE:NIGHT_CREATURE_30:GIZZARD +#CREATURE:NIGHT_CREATURE_30:PANCREAS +!CREATURE:NIGHT_CREATURE_30:SPLEEN +!CREATURE:NIGHT_CREATURE_30:KIDNEY +!CREATURE:NIGHT_CREATURE_31:MUSCLE +CREATURE:NIGHT_CREATURE_31:EYE + CREATURE:NIGHT_CREATURE_31:BRAIN +CREATURE:NIGHT_CREATURE_31:LUNG + CREATURE:NIGHT_CREATURE_31:HEART + CREATURE:NIGHT_CREATURE_31:LIVER +CREATURE:NIGHT_CREATURE_31:GUT +"CREATURE:NIGHT_CREATURE_31:STOMACH +"CREATURE:NIGHT_CREATURE_31:GIZZARD +#CREATURE:NIGHT_CREATURE_31:PANCREAS +!CREATURE:NIGHT_CREATURE_31:SPLEEN +!CREATURE:NIGHT_CREATURE_31:KIDNEY +!CREATURE:NIGHT_CREATURE_32:MUSCLE +CREATURE:NIGHT_CREATURE_32:EYE + CREATURE:NIGHT_CREATURE_32:BRAIN +CREATURE:NIGHT_CREATURE_32:LUNG + CREATURE:NIGHT_CREATURE_32:HEART + CREATURE:NIGHT_CREATURE_32:LIVER +CREATURE:NIGHT_CREATURE_32:GUT +"CREATURE:NIGHT_CREATURE_32:STOMACH +"CREATURE:NIGHT_CREATURE_32:GIZZARD +#CREATURE:NIGHT_CREATURE_32:PANCREAS +!CREATURE:NIGHT_CREATURE_32:SPLEEN +!CREATURE:NIGHT_CREATURE_32:KIDNEY +!CREATURE:NIGHT_CREATURE_33:MUSCLE +CREATURE:NIGHT_CREATURE_33:EYE + CREATURE:NIGHT_CREATURE_33:BRAIN +CREATURE:NIGHT_CREATURE_33:LUNG + CREATURE:NIGHT_CREATURE_33:HEART + CREATURE:NIGHT_CREATURE_33:LIVER +CREATURE:NIGHT_CREATURE_33:GUT +"CREATURE:NIGHT_CREATURE_33:STOMACH +"CREATURE:NIGHT_CREATURE_33:GIZZARD +#CREATURE:NIGHT_CREATURE_33:PANCREAS +!CREATURE:NIGHT_CREATURE_33:SPLEEN +!CREATURE:NIGHT_CREATURE_33:KIDNEY +!CREATURE:NIGHT_CREATURE_34:MUSCLE +CREATURE:NIGHT_CREATURE_34:EYE + CREATURE:NIGHT_CREATURE_34:BRAIN +CREATURE:NIGHT_CREATURE_34:LUNG + CREATURE:NIGHT_CREATURE_34:HEART + CREATURE:NIGHT_CREATURE_34:LIVER +CREATURE:NIGHT_CREATURE_34:GUT +"CREATURE:NIGHT_CREATURE_34:STOMACH +"CREATURE:NIGHT_CREATURE_34:GIZZARD +#CREATURE:NIGHT_CREATURE_34:PANCREAS +!CREATURE:NIGHT_CREATURE_34:SPLEEN +!CREATURE:NIGHT_CREATURE_34:KIDNEY +!CREATURE:NIGHT_CREATURE_35:MUSCLE +CREATURE:NIGHT_CREATURE_35:EYE + CREATURE:NIGHT_CREATURE_35:BRAIN +CREATURE:NIGHT_CREATURE_35:LUNG + CREATURE:NIGHT_CREATURE_35:HEART + CREATURE:NIGHT_CREATURE_35:LIVER +CREATURE:NIGHT_CREATURE_35:GUT +"CREATURE:NIGHT_CREATURE_35:STOMACH +"CREATURE:NIGHT_CREATURE_35:GIZZARD +#CREATURE:NIGHT_CREATURE_35:PANCREAS +!CREATURE:NIGHT_CREATURE_35:SPLEEN +!CREATURE:NIGHT_CREATURE_35:KIDNEY +!CREATURE:NIGHT_CREATURE_36:MUSCLE +CREATURE:NIGHT_CREATURE_36:EYE + CREATURE:NIGHT_CREATURE_36:BRAIN +CREATURE:NIGHT_CREATURE_36:LUNG + CREATURE:NIGHT_CREATURE_36:HEART + CREATURE:NIGHT_CREATURE_36:LIVER +CREATURE:NIGHT_CREATURE_36:GUT +"CREATURE:NIGHT_CREATURE_36:STOMACH +"CREATURE:NIGHT_CREATURE_36:GIZZARD +#CREATURE:NIGHT_CREATURE_36:PANCREAS +!CREATURE:NIGHT_CREATURE_36:SPLEEN +!CREATURE:NIGHT_CREATURE_36:KIDNEY +!CREATURE:NIGHT_CREATURE_37:MUSCLE +CREATURE:NIGHT_CREATURE_37:EYE + CREATURE:NIGHT_CREATURE_37:BRAIN +CREATURE:NIGHT_CREATURE_37:LUNG + CREATURE:NIGHT_CREATURE_37:HEART + CREATURE:NIGHT_CREATURE_37:LIVER +CREATURE:NIGHT_CREATURE_37:GUT +"CREATURE:NIGHT_CREATURE_37:STOMACH +"CREATURE:NIGHT_CREATURE_37:GIZZARD +#CREATURE:NIGHT_CREATURE_37:PANCREAS +!CREATURE:NIGHT_CREATURE_37:SPLEEN +!CREATURE:NIGHT_CREATURE_37:KIDNEY +!CREATURE:NIGHT_CREATURE_38:MUSCLE +CREATURE:NIGHT_CREATURE_38:EYE + CREATURE:NIGHT_CREATURE_38:BRAIN +CREATURE:NIGHT_CREATURE_38:LUNG + CREATURE:NIGHT_CREATURE_38:HEART + CREATURE:NIGHT_CREATURE_38:LIVER +CREATURE:NIGHT_CREATURE_38:GUT +"CREATURE:NIGHT_CREATURE_38:STOMACH +"CREATURE:NIGHT_CREATURE_38:GIZZARD +#CREATURE:NIGHT_CREATURE_38:PANCREAS +!CREATURE:NIGHT_CREATURE_38:SPLEEN +!CREATURE:NIGHT_CREATURE_38:KIDNEY +!CREATURE:NIGHT_CREATURE_39:MUSCLE +CREATURE:NIGHT_CREATURE_39:EYE + CREATURE:NIGHT_CREATURE_39:BRAIN +CREATURE:NIGHT_CREATURE_39:LUNG + CREATURE:NIGHT_CREATURE_39:HEART + CREATURE:NIGHT_CREATURE_39:LIVER +CREATURE:NIGHT_CREATURE_39:GUT +"CREATURE:NIGHT_CREATURE_39:STOMACH +"CREATURE:NIGHT_CREATURE_39:GIZZARD +#CREATURE:NIGHT_CREATURE_39:PANCREAS +!CREATURE:NIGHT_CREATURE_39:SPLEEN +!CREATURE:NIGHT_CREATURE_39:KIDNEY +!CREATURE:NIGHT_CREATURE_40:MUSCLE +CREATURE:NIGHT_CREATURE_40:EYE + CREATURE:NIGHT_CREATURE_40:BRAIN +CREATURE:NIGHT_CREATURE_40:LUNG + CREATURE:NIGHT_CREATURE_40:HEART + CREATURE:NIGHT_CREATURE_40:LIVER +CREATURE:NIGHT_CREATURE_40:GUT +"CREATURE:NIGHT_CREATURE_40:STOMACH +"CREATURE:NIGHT_CREATURE_40:GIZZARD +#CREATURE:NIGHT_CREATURE_40:PANCREAS +!CREATURE:NIGHT_CREATURE_40:SPLEEN +!CREATURE:NIGHT_CREATURE_40:KIDNEY +!CREATURE:NIGHT_CREATURE_41:MUSCLE +CREATURE:NIGHT_CREATURE_41:EYE + CREATURE:NIGHT_CREATURE_41:BRAIN +CREATURE:NIGHT_CREATURE_41:LUNG + CREATURE:NIGHT_CREATURE_41:HEART + CREATURE:NIGHT_CREATURE_41:LIVER +CREATURE:NIGHT_CREATURE_41:GUT +"CREATURE:NIGHT_CREATURE_41:STOMACH +"CREATURE:NIGHT_CREATURE_41:GIZZARD +#CREATURE:NIGHT_CREATURE_41:PANCREAS +!CREATURE:NIGHT_CREATURE_41:SPLEEN +!CREATURE:NIGHT_CREATURE_41:KIDNEY +!CREATURE:NIGHT_CREATURE_42:MUSCLE +CREATURE:NIGHT_CREATURE_42:EYE + CREATURE:NIGHT_CREATURE_42:BRAIN +CREATURE:NIGHT_CREATURE_42:LUNG + CREATURE:NIGHT_CREATURE_42:HEART + CREATURE:NIGHT_CREATURE_42:LIVER +CREATURE:NIGHT_CREATURE_42:GUT +"CREATURE:NIGHT_CREATURE_42:STOMACH +"CREATURE:NIGHT_CREATURE_42:GIZZARD +#CREATURE:NIGHT_CREATURE_42:PANCREAS +!CREATURE:NIGHT_CREATURE_42:SPLEEN +!CREATURE:NIGHT_CREATURE_42:KIDNEY +!CREATURE:NIGHT_CREATURE_43:MUSCLE +CREATURE:NIGHT_CREATURE_43:EYE + CREATURE:NIGHT_CREATURE_43:BRAIN +CREATURE:NIGHT_CREATURE_43:LUNG + CREATURE:NIGHT_CREATURE_43:HEART + CREATURE:NIGHT_CREATURE_43:LIVER +CREATURE:NIGHT_CREATURE_43:GUT +"CREATURE:NIGHT_CREATURE_43:STOMACH +"CREATURE:NIGHT_CREATURE_43:GIZZARD +#CREATURE:NIGHT_CREATURE_43:PANCREAS +!CREATURE:NIGHT_CREATURE_43:SPLEEN +!CREATURE:NIGHT_CREATURE_43:KIDNEY +!CREATURE:NIGHT_CREATURE_44:MUSCLE +CREATURE:NIGHT_CREATURE_44:EYE + CREATURE:NIGHT_CREATURE_44:BRAIN +CREATURE:NIGHT_CREATURE_44:LUNG + CREATURE:NIGHT_CREATURE_44:HEART + CREATURE:NIGHT_CREATURE_44:LIVER +CREATURE:NIGHT_CREATURE_44:GUT +"CREATURE:NIGHT_CREATURE_44:STOMACH +"CREATURE:NIGHT_CREATURE_44:GIZZARD +#CREATURE:NIGHT_CREATURE_44:PANCREAS +!CREATURE:NIGHT_CREATURE_44:SPLEEN +!CREATURE:NIGHT_CREATURE_44:KIDNEY +!CREATURE:NIGHT_CREATURE_45:MUSCLE +CREATURE:NIGHT_CREATURE_45:EYE + CREATURE:NIGHT_CREATURE_45:BRAIN +CREATURE:NIGHT_CREATURE_45:LUNG + CREATURE:NIGHT_CREATURE_45:HEART + CREATURE:NIGHT_CREATURE_45:LIVER +CREATURE:NIGHT_CREATURE_45:GUT +"CREATURE:NIGHT_CREATURE_45:STOMACH +"CREATURE:NIGHT_CREATURE_45:GIZZARD +#CREATURE:NIGHT_CREATURE_45:PANCREAS +!CREATURE:NIGHT_CREATURE_45:SPLEEN +!CREATURE:NIGHT_CREATURE_45:KIDNEY +!CREATURE:NIGHT_CREATURE_46:MUSCLE +CREATURE:NIGHT_CREATURE_46:EYE + CREATURE:NIGHT_CREATURE_46:BRAIN +CREATURE:NIGHT_CREATURE_46:LUNG + CREATURE:NIGHT_CREATURE_46:HEART + CREATURE:NIGHT_CREATURE_46:LIVER +CREATURE:NIGHT_CREATURE_46:GUT +"CREATURE:NIGHT_CREATURE_46:STOMACH +"CREATURE:NIGHT_CREATURE_46:GIZZARD +#CREATURE:NIGHT_CREATURE_46:PANCREAS +!CREATURE:NIGHT_CREATURE_46:SPLEEN +!CREATURE:NIGHT_CREATURE_46:KIDNEY +!CREATURE:NIGHT_CREATURE_47:MUSCLE +CREATURE:NIGHT_CREATURE_47:EYE + CREATURE:NIGHT_CREATURE_47:BRAIN +CREATURE:NIGHT_CREATURE_47:LUNG + CREATURE:NIGHT_CREATURE_47:HEART + CREATURE:NIGHT_CREATURE_47:LIVER +CREATURE:NIGHT_CREATURE_47:GUT +"CREATURE:NIGHT_CREATURE_47:STOMACH +"CREATURE:NIGHT_CREATURE_47:GIZZARD +#CREATURE:NIGHT_CREATURE_47:PANCREAS +!CREATURE:NIGHT_CREATURE_47:SPLEEN +!CREATURE:NIGHT_CREATURE_47:KIDNEY +!CREATURE:NIGHT_CREATURE_48:MUSCLE +CREATURE:NIGHT_CREATURE_48:EYE + CREATURE:NIGHT_CREATURE_48:BRAIN +CREATURE:NIGHT_CREATURE_48:LUNG + CREATURE:NIGHT_CREATURE_48:HEART + CREATURE:NIGHT_CREATURE_48:LIVER +CREATURE:NIGHT_CREATURE_48:GUT +"CREATURE:NIGHT_CREATURE_48:STOMACH +"CREATURE:NIGHT_CREATURE_48:GIZZARD +#CREATURE:NIGHT_CREATURE_48:PANCREAS +!CREATURE:NIGHT_CREATURE_48:SPLEEN +!CREATURE:NIGHT_CREATURE_48:KIDNEY +!CREATURE:NIGHT_CREATURE_49:MUSCLE +CREATURE:NIGHT_CREATURE_49:EYE + CREATURE:NIGHT_CREATURE_49:BRAIN +CREATURE:NIGHT_CREATURE_49:LUNG + CREATURE:NIGHT_CREATURE_49:HEART + CREATURE:NIGHT_CREATURE_49:LIVER +CREATURE:NIGHT_CREATURE_49:GUT +"CREATURE:NIGHT_CREATURE_49:STOMACH +"CREATURE:NIGHT_CREATURE_49:GIZZARD +#CREATURE:NIGHT_CREATURE_49:PANCREAS +!CREATURE:NIGHT_CREATURE_49:SPLEEN +!CREATURE:NIGHT_CREATURE_49:KIDNEY +!CREATURE:NIGHT_CREATURE_50:MUSCLE +CREATURE:NIGHT_CREATURE_50:EYE + CREATURE:NIGHT_CREATURE_50:BRAIN +CREATURE:NIGHT_CREATURE_50:LUNG + CREATURE:NIGHT_CREATURE_50:HEART + CREATURE:NIGHT_CREATURE_50:LIVER +CREATURE:NIGHT_CREATURE_50:GUT +"CREATURE:NIGHT_CREATURE_50:STOMACH +"CREATURE:NIGHT_CREATURE_50:GIZZARD +#CREATURE:NIGHT_CREATURE_50:PANCREAS +!CREATURE:NIGHT_CREATURE_50:SPLEEN +!CREATURE:NIGHT_CREATURE_50:KIDNEY +!CREATURE:NIGHT_CREATURE_51:MUSCLE +CREATURE:NIGHT_CREATURE_51:EYE + CREATURE:NIGHT_CREATURE_51:BRAIN +CREATURE:NIGHT_CREATURE_51:LUNG + CREATURE:NIGHT_CREATURE_51:HEART + CREATURE:NIGHT_CREATURE_51:LIVER +CREATURE:NIGHT_CREATURE_51:GUT +"CREATURE:NIGHT_CREATURE_51:STOMACH +"CREATURE:NIGHT_CREATURE_51:GIZZARD +#CREATURE:NIGHT_CREATURE_51:PANCREAS +!CREATURE:NIGHT_CREATURE_51:SPLEEN +!CREATURE:NIGHT_CREATURE_51:KIDNEY +!CREATURE:NIGHT_CREATURE_52:MUSCLE +CREATURE:NIGHT_CREATURE_52:EYE + CREATURE:NIGHT_CREATURE_52:BRAIN +CREATURE:NIGHT_CREATURE_52:LUNG + CREATURE:NIGHT_CREATURE_52:HEART + CREATURE:NIGHT_CREATURE_52:LIVER +CREATURE:NIGHT_CREATURE_52:GUT +"CREATURE:NIGHT_CREATURE_52:STOMACH +"CREATURE:NIGHT_CREATURE_52:GIZZARD +#CREATURE:NIGHT_CREATURE_52:PANCREAS +!CREATURE:NIGHT_CREATURE_52:SPLEEN +!CREATURE:NIGHT_CREATURE_52:KIDNEY +!CREATURE:NIGHT_CREATURE_53:MUSCLE +CREATURE:NIGHT_CREATURE_53:EYE + CREATURE:NIGHT_CREATURE_53:BRAIN +CREATURE:NIGHT_CREATURE_53:LUNG + CREATURE:NIGHT_CREATURE_53:HEART + CREATURE:NIGHT_CREATURE_53:LIVER +CREATURE:NIGHT_CREATURE_53:GUT +"CREATURE:NIGHT_CREATURE_53:STOMACH +"CREATURE:NIGHT_CREATURE_53:GIZZARD +#CREATURE:NIGHT_CREATURE_53:PANCREAS +!CREATURE:NIGHT_CREATURE_53:SPLEEN +!CREATURE:NIGHT_CREATURE_53:KIDNEY +!CREATURE:NIGHT_CREATURE_54:MUSCLE +CREATURE:NIGHT_CREATURE_54:EYE + CREATURE:NIGHT_CREATURE_54:BRAIN +CREATURE:NIGHT_CREATURE_54:LUNG + CREATURE:NIGHT_CREATURE_54:HEART + CREATURE:NIGHT_CREATURE_54:LIVER +CREATURE:NIGHT_CREATURE_54:GUT +"CREATURE:NIGHT_CREATURE_54:STOMACH +"CREATURE:NIGHT_CREATURE_54:GIZZARD +#CREATURE:NIGHT_CREATURE_54:PANCREAS +!CREATURE:NIGHT_CREATURE_54:SPLEEN +!CREATURE:NIGHT_CREATURE_54:KIDNEY +!CREATURE:NIGHT_CREATURE_55:MUSCLE +CREATURE:NIGHT_CREATURE_55:EYE + CREATURE:NIGHT_CREATURE_55:BRAIN +CREATURE:NIGHT_CREATURE_55:LUNG + CREATURE:NIGHT_CREATURE_55:HEART + CREATURE:NIGHT_CREATURE_55:LIVER +CREATURE:NIGHT_CREATURE_55:GUT +"CREATURE:NIGHT_CREATURE_55:STOMACH +"CREATURE:NIGHT_CREATURE_55:GIZZARD +#CREATURE:NIGHT_CREATURE_55:PANCREAS +!CREATURE:NIGHT_CREATURE_55:SPLEEN +!CREATURE:NIGHT_CREATURE_55:KIDNEY +!CREATURE:NIGHT_CREATURE_56:MUSCLE +CREATURE:NIGHT_CREATURE_56:EYE + CREATURE:NIGHT_CREATURE_56:BRAIN +CREATURE:NIGHT_CREATURE_56:LUNG + CREATURE:NIGHT_CREATURE_56:HEART + CREATURE:NIGHT_CREATURE_56:LIVER +CREATURE:NIGHT_CREATURE_56:GUT +"CREATURE:NIGHT_CREATURE_56:STOMACH +"CREATURE:NIGHT_CREATURE_56:GIZZARD +#CREATURE:NIGHT_CREATURE_56:PANCREAS +!CREATURE:NIGHT_CREATURE_56:SPLEEN +!CREATURE:NIGHT_CREATURE_56:KIDNEY +!CREATURE:NIGHT_CREATURE_57:MUSCLE +CREATURE:NIGHT_CREATURE_57:EYE + CREATURE:NIGHT_CREATURE_57:BRAIN +CREATURE:NIGHT_CREATURE_57:LUNG + CREATURE:NIGHT_CREATURE_57:HEART + CREATURE:NIGHT_CREATURE_57:LIVER +CREATURE:NIGHT_CREATURE_57:GUT +"CREATURE:NIGHT_CREATURE_57:STOMACH +"CREATURE:NIGHT_CREATURE_57:GIZZARD +#CREATURE:NIGHT_CREATURE_57:PANCREAS +!CREATURE:NIGHT_CREATURE_57:SPLEEN +!CREATURE:NIGHT_CREATURE_57:KIDNEY +!CREATURE:NIGHT_CREATURE_58:MUSCLE +CREATURE:NIGHT_CREATURE_58:EYE + CREATURE:NIGHT_CREATURE_58:BRAIN +CREATURE:NIGHT_CREATURE_58:LUNG + CREATURE:NIGHT_CREATURE_58:HEART + CREATURE:NIGHT_CREATURE_58:LIVER +CREATURE:NIGHT_CREATURE_58:GUT +"CREATURE:NIGHT_CREATURE_58:STOMACH +"CREATURE:NIGHT_CREATURE_58:GIZZARD +#CREATURE:NIGHT_CREATURE_58:PANCREAS +!CREATURE:NIGHT_CREATURE_58:SPLEEN +!CREATURE:NIGHT_CREATURE_58:KIDNEY +!CREATURE:NIGHT_CREATURE_59:MUSCLE +CREATURE:NIGHT_CREATURE_59:EYE + CREATURE:NIGHT_CREATURE_59:BRAIN +CREATURE:NIGHT_CREATURE_59:LUNG + CREATURE:NIGHT_CREATURE_59:HEART + CREATURE:NIGHT_CREATURE_59:LIVER +CREATURE:NIGHT_CREATURE_59:GUT +"CREATURE:NIGHT_CREATURE_59:STOMACH +"CREATURE:NIGHT_CREATURE_59:GIZZARD +#CREATURE:NIGHT_CREATURE_59:PANCREAS +!CREATURE:NIGHT_CREATURE_59:SPLEEN +!CREATURE:NIGHT_CREATURE_59:KIDNEY +!CREATURE:NIGHT_CREATURE_60:MUSCLE +CREATURE:NIGHT_CREATURE_60:EYE + CREATURE:NIGHT_CREATURE_60:BRAIN +CREATURE:NIGHT_CREATURE_60:LUNG + CREATURE:NIGHT_CREATURE_60:HEART + CREATURE:NIGHT_CREATURE_60:LIVER +CREATURE:NIGHT_CREATURE_60:GUT +"CREATURE:NIGHT_CREATURE_60:STOMACH +"CREATURE:NIGHT_CREATURE_60:GIZZARD +#CREATURE:NIGHT_CREATURE_60:PANCREAS +!CREATURE:NIGHT_CREATURE_60:SPLEEN +!CREATURE:NIGHT_CREATURE_60:KIDNEY +!CREATURE:NIGHT_CREATURE_61:MUSCLE +CREATURE:NIGHT_CREATURE_61:EYE + CREATURE:NIGHT_CREATURE_61:BRAIN +CREATURE:NIGHT_CREATURE_61:LUNG + CREATURE:NIGHT_CREATURE_61:HEART + CREATURE:NIGHT_CREATURE_61:LIVER +CREATURE:NIGHT_CREATURE_61:GUT +"CREATURE:NIGHT_CREATURE_61:STOMACH +"CREATURE:NIGHT_CREATURE_61:GIZZARD +#CREATURE:NIGHT_CREATURE_61:PANCREAS +!CREATURE:NIGHT_CREATURE_61:SPLEEN +!CREATURE:NIGHT_CREATURE_61:KIDNEY +!CREATURE:NIGHT_CREATURE_62:MUSCLE +CREATURE:NIGHT_CREATURE_62:EYE + CREATURE:NIGHT_CREATURE_62:BRAIN +CREATURE:NIGHT_CREATURE_62:LUNG + CREATURE:NIGHT_CREATURE_62:HEART + CREATURE:NIGHT_CREATURE_62:LIVER +CREATURE:NIGHT_CREATURE_62:GUT +"CREATURE:NIGHT_CREATURE_62:STOMACH +"CREATURE:NIGHT_CREATURE_62:GIZZARD +#CREATURE:NIGHT_CREATURE_62:PANCREAS +!CREATURE:NIGHT_CREATURE_62:SPLEEN +!CREATURE:NIGHT_CREATURE_62:KIDNEY +!CREATURE:NIGHT_CREATURE_63:MUSCLE +CREATURE:NIGHT_CREATURE_63:EYE + CREATURE:NIGHT_CREATURE_63:BRAIN +CREATURE:NIGHT_CREATURE_63:LUNG + CREATURE:NIGHT_CREATURE_63:HEART + CREATURE:NIGHT_CREATURE_63:LIVER +CREATURE:NIGHT_CREATURE_63:GUT +"CREATURE:NIGHT_CREATURE_63:STOMACH +"CREATURE:NIGHT_CREATURE_63:GIZZARD +#CREATURE:NIGHT_CREATURE_63:PANCREAS +!CREATURE:NIGHT_CREATURE_63:SPLEEN +!CREATURE:NIGHT_CREATURE_63:KIDNEY +!CREATURE:NIGHT_CREATURE_64:MUSCLE +CREATURE:NIGHT_CREATURE_64:EYE + CREATURE:NIGHT_CREATURE_64:BRAIN +CREATURE:NIGHT_CREATURE_64:LUNG + CREATURE:NIGHT_CREATURE_64:HEART + CREATURE:NIGHT_CREATURE_64:LIVER +CREATURE:NIGHT_CREATURE_64:GUT +"CREATURE:NIGHT_CREATURE_64:STOMACH +"CREATURE:NIGHT_CREATURE_64:GIZZARD +#CREATURE:NIGHT_CREATURE_64:PANCREAS +!CREATURE:NIGHT_CREATURE_64:SPLEEN +!CREATURE:NIGHT_CREATURE_64:KIDNEY +!CREATURE:NIGHT_CREATURE_65:MUSCLE +CREATURE:NIGHT_CREATURE_65:EYE + CREATURE:NIGHT_CREATURE_65:BRAIN +CREATURE:NIGHT_CREATURE_65:LUNG + CREATURE:NIGHT_CREATURE_65:HEART + CREATURE:NIGHT_CREATURE_65:LIVER +CREATURE:NIGHT_CREATURE_65:GUT +"CREATURE:NIGHT_CREATURE_65:STOMACH +"CREATURE:NIGHT_CREATURE_65:GIZZARD +#CREATURE:NIGHT_CREATURE_65:PANCREAS +!CREATURE:NIGHT_CREATURE_65:SPLEEN +!CREATURE:NIGHT_CREATURE_65:KIDNEY +!CREATURE:NIGHT_CREATURE_66:MUSCLE +CREATURE:NIGHT_CREATURE_66:EYE + CREATURE:NIGHT_CREATURE_66:BRAIN +CREATURE:NIGHT_CREATURE_66:LUNG + CREATURE:NIGHT_CREATURE_66:HEART + CREATURE:NIGHT_CREATURE_66:LIVER +CREATURE:NIGHT_CREATURE_66:GUT +"CREATURE:NIGHT_CREATURE_66:STOMACH +"CREATURE:NIGHT_CREATURE_66:GIZZARD +#CREATURE:NIGHT_CREATURE_66:PANCREAS +!CREATURE:NIGHT_CREATURE_66:SPLEEN +!CREATURE:NIGHT_CREATURE_66:KIDNEY +!CREATURE:NIGHT_CREATURE_67:MUSCLE +CREATURE:NIGHT_CREATURE_67:EYE + CREATURE:NIGHT_CREATURE_67:BRAIN +CREATURE:NIGHT_CREATURE_67:LUNG + CREATURE:NIGHT_CREATURE_67:HEART + CREATURE:NIGHT_CREATURE_67:LIVER +CREATURE:NIGHT_CREATURE_67:GUT +"CREATURE:NIGHT_CREATURE_67:STOMACH +"CREATURE:NIGHT_CREATURE_67:GIZZARD +#CREATURE:NIGHT_CREATURE_67:PANCREAS +!CREATURE:NIGHT_CREATURE_67:SPLEEN +!CREATURE:NIGHT_CREATURE_67:KIDNEY +!CREATURE:NIGHT_CREATURE_68:MUSCLE +CREATURE:NIGHT_CREATURE_68:EYE + CREATURE:NIGHT_CREATURE_68:BRAIN +CREATURE:NIGHT_CREATURE_68:LUNG + CREATURE:NIGHT_CREATURE_68:HEART + CREATURE:NIGHT_CREATURE_68:LIVER +CREATURE:NIGHT_CREATURE_68:GUT +"CREATURE:NIGHT_CREATURE_68:STOMACH +"CREATURE:NIGHT_CREATURE_68:GIZZARD +#CREATURE:NIGHT_CREATURE_68:PANCREAS +!CREATURE:NIGHT_CREATURE_68:SPLEEN +!CREATURE:NIGHT_CREATURE_68:KIDNEY +!CREATURE:NIGHT_CREATURE_69:MUSCLE +CREATURE:NIGHT_CREATURE_69:EYE + CREATURE:NIGHT_CREATURE_69:BRAIN +CREATURE:NIGHT_CREATURE_69:LUNG + CREATURE:NIGHT_CREATURE_69:HEART + CREATURE:NIGHT_CREATURE_69:LIVER +CREATURE:NIGHT_CREATURE_69:GUT +"CREATURE:NIGHT_CREATURE_69:STOMACH +"CREATURE:NIGHT_CREATURE_69:GIZZARD +#CREATURE:NIGHT_CREATURE_69:PANCREAS +!CREATURE:NIGHT_CREATURE_69:SPLEEN +!CREATURE:NIGHT_CREATURE_69:KIDNEY +!CREATURE:NIGHT_CREATURE_70:MUSCLE +CREATURE:NIGHT_CREATURE_70:EYE + CREATURE:NIGHT_CREATURE_70:BRAIN +CREATURE:NIGHT_CREATURE_70:LUNG + CREATURE:NIGHT_CREATURE_70:HEART + CREATURE:NIGHT_CREATURE_70:LIVER +CREATURE:NIGHT_CREATURE_70:GUT +"CREATURE:NIGHT_CREATURE_70:STOMACH +"CREATURE:NIGHT_CREATURE_70:GIZZARD +#CREATURE:NIGHT_CREATURE_70:PANCREAS +!CREATURE:NIGHT_CREATURE_70:SPLEEN +!CREATURE:NIGHT_CREATURE_70:KIDNEY +!CREATURE:NIGHT_CREATURE_71:MUSCLE +CREATURE:NIGHT_CREATURE_71:EYE + CREATURE:NIGHT_CREATURE_71:BRAIN +CREATURE:NIGHT_CREATURE_71:LUNG + CREATURE:NIGHT_CREATURE_71:HEART + CREATURE:NIGHT_CREATURE_71:LIVER +CREATURE:NIGHT_CREATURE_71:GUT +"CREATURE:NIGHT_CREATURE_71:STOMACH +"CREATURE:NIGHT_CREATURE_71:GIZZARD +#CREATURE:NIGHT_CREATURE_71:PANCREAS +!CREATURE:NIGHT_CREATURE_71:SPLEEN +!CREATURE:NIGHT_CREATURE_71:KIDNEY +!CREATURE:NIGHT_CREATURE_72:MUSCLE +CREATURE:NIGHT_CREATURE_72:EYE + CREATURE:NIGHT_CREATURE_72:BRAIN +CREATURE:NIGHT_CREATURE_72:LUNG + CREATURE:NIGHT_CREATURE_72:HEART + CREATURE:NIGHT_CREATURE_72:LIVER +CREATURE:NIGHT_CREATURE_72:GUT +"CREATURE:NIGHT_CREATURE_72:STOMACH +"CREATURE:NIGHT_CREATURE_72:GIZZARD +#CREATURE:NIGHT_CREATURE_72:PANCREAS +!CREATURE:NIGHT_CREATURE_72:SPLEEN +!CREATURE:NIGHT_CREATURE_72:KIDNEY +!CREATURE:NIGHT_CREATURE_73:MUSCLE +CREATURE:NIGHT_CREATURE_73:EYE + CREATURE:NIGHT_CREATURE_73:BRAIN +CREATURE:NIGHT_CREATURE_73:LUNG + CREATURE:NIGHT_CREATURE_73:HEART + CREATURE:NIGHT_CREATURE_73:LIVER +CREATURE:NIGHT_CREATURE_73:GUT +"CREATURE:NIGHT_CREATURE_73:STOMACH +"CREATURE:NIGHT_CREATURE_73:GIZZARD +#CREATURE:NIGHT_CREATURE_73:PANCREAS +!CREATURE:NIGHT_CREATURE_73:SPLEEN +!CREATURE:NIGHT_CREATURE_73:KIDNEY +!CREATURE:NIGHT_CREATURE_74:MUSCLE +CREATURE:NIGHT_CREATURE_74:EYE + CREATURE:NIGHT_CREATURE_74:BRAIN +CREATURE:NIGHT_CREATURE_74:LUNG + CREATURE:NIGHT_CREATURE_74:HEART + CREATURE:NIGHT_CREATURE_74:LIVER +CREATURE:NIGHT_CREATURE_74:GUT +"CREATURE:NIGHT_CREATURE_74:STOMACH +"CREATURE:NIGHT_CREATURE_74:GIZZARD +#CREATURE:NIGHT_CREATURE_74:PANCREAS +!CREATURE:NIGHT_CREATURE_74:SPLEEN +!CREATURE:NIGHT_CREATURE_74:KIDNEY +!CREATURE:NIGHT_CREATURE_75:MUSCLE +CREATURE:NIGHT_CREATURE_75:EYE + CREATURE:NIGHT_CREATURE_75:BRAIN +CREATURE:NIGHT_CREATURE_75:LUNG + CREATURE:NIGHT_CREATURE_75:HEART + CREATURE:NIGHT_CREATURE_75:LIVER +CREATURE:NIGHT_CREATURE_75:GUT +"CREATURE:NIGHT_CREATURE_75:STOMACH +"CREATURE:NIGHT_CREATURE_75:GIZZARD +#CREATURE:NIGHT_CREATURE_75:PANCREAS +!CREATURE:NIGHT_CREATURE_75:SPLEEN +!CREATURE:NIGHT_CREATURE_75:KIDNEY +!CREATURE:NIGHT_CREATURE_76:MUSCLE +CREATURE:NIGHT_CREATURE_76:EYE + CREATURE:NIGHT_CREATURE_76:BRAIN +CREATURE:NIGHT_CREATURE_76:LUNG + CREATURE:NIGHT_CREATURE_76:HEART + CREATURE:NIGHT_CREATURE_76:LIVER +CREATURE:NIGHT_CREATURE_76:GUT +"CREATURE:NIGHT_CREATURE_76:STOMACH +"CREATURE:NIGHT_CREATURE_76:GIZZARD +#CREATURE:NIGHT_CREATURE_76:PANCREAS +!CREATURE:NIGHT_CREATURE_76:SPLEEN +!CREATURE:NIGHT_CREATURE_76:KIDNEY +!CREATURE:NIGHT_CREATURE_77:MUSCLE +CREATURE:NIGHT_CREATURE_77:EYE + CREATURE:NIGHT_CREATURE_77:BRAIN +CREATURE:NIGHT_CREATURE_77:LUNG + CREATURE:NIGHT_CREATURE_77:HEART + CREATURE:NIGHT_CREATURE_77:LIVER +CREATURE:NIGHT_CREATURE_77:GUT +"CREATURE:NIGHT_CREATURE_77:STOMACH +"CREATURE:NIGHT_CREATURE_77:GIZZARD +#CREATURE:NIGHT_CREATURE_77:PANCREAS +!CREATURE:NIGHT_CREATURE_77:SPLEEN +!CREATURE:NIGHT_CREATURE_77:KIDNEY +!CREATURE:NIGHT_CREATURE_78:MUSCLE +CREATURE:NIGHT_CREATURE_78:EYE + CREATURE:NIGHT_CREATURE_78:BRAIN +CREATURE:NIGHT_CREATURE_78:LUNG + CREATURE:NIGHT_CREATURE_78:HEART + CREATURE:NIGHT_CREATURE_78:LIVER +CREATURE:NIGHT_CREATURE_78:GUT +"CREATURE:NIGHT_CREATURE_78:STOMACH +"CREATURE:NIGHT_CREATURE_78:GIZZARD +#CREATURE:NIGHT_CREATURE_78:PANCREAS +!CREATURE:NIGHT_CREATURE_78:SPLEEN +!CREATURE:NIGHT_CREATURE_78:KIDNEY +!CREATURE:NIGHT_CREATURE_79:MUSCLE +CREATURE:NIGHT_CREATURE_79:EYE + CREATURE:NIGHT_CREATURE_79:BRAIN +CREATURE:NIGHT_CREATURE_79:LUNG + CREATURE:NIGHT_CREATURE_79:HEART + CREATURE:NIGHT_CREATURE_79:LIVER +CREATURE:NIGHT_CREATURE_79:GUT +"CREATURE:NIGHT_CREATURE_79:STOMACH +"CREATURE:NIGHT_CREATURE_79:GIZZARD +#CREATURE:NIGHT_CREATURE_79:PANCREAS +!CREATURE:NIGHT_CREATURE_79:SPLEEN +!CREATURE:NIGHT_CREATURE_79:KIDNEY +!CREATURE:NIGHT_CREATURE_80:MUSCLE +CREATURE:NIGHT_CREATURE_80:EYE + CREATURE:NIGHT_CREATURE_80:BRAIN +CREATURE:NIGHT_CREATURE_80:LUNG + CREATURE:NIGHT_CREATURE_80:HEART + CREATURE:NIGHT_CREATURE_80:LIVER +CREATURE:NIGHT_CREATURE_80:GUT +"CREATURE:NIGHT_CREATURE_80:STOMACH +"CREATURE:NIGHT_CREATURE_80:GIZZARD +#CREATURE:NIGHT_CREATURE_80:PANCREAS +!CREATURE:NIGHT_CREATURE_80:SPLEEN +!CREATURE:NIGHT_CREATURE_80:KIDNEY +!CREATURE:NIGHT_CREATURE_81:MUSCLE +CREATURE:NIGHT_CREATURE_81:EYE + CREATURE:NIGHT_CREATURE_81:BRAIN +CREATURE:NIGHT_CREATURE_81:LUNG + CREATURE:NIGHT_CREATURE_81:HEART + CREATURE:NIGHT_CREATURE_81:LIVER +CREATURE:NIGHT_CREATURE_81:GUT +"CREATURE:NIGHT_CREATURE_81:STOMACH +"CREATURE:NIGHT_CREATURE_81:GIZZARD +#CREATURE:NIGHT_CREATURE_81:PANCREAS +!CREATURE:NIGHT_CREATURE_81:SPLEEN +!CREATURE:NIGHT_CREATURE_81:KIDNEY +!CREATURE:NIGHT_CREATURE_82:MUSCLE +CREATURE:NIGHT_CREATURE_82:EYE + CREATURE:NIGHT_CREATURE_82:BRAIN +CREATURE:NIGHT_CREATURE_82:LUNG + CREATURE:NIGHT_CREATURE_82:HEART + CREATURE:NIGHT_CREATURE_82:LIVER +CREATURE:NIGHT_CREATURE_82:GUT +"CREATURE:NIGHT_CREATURE_82:STOMACH +"CREATURE:NIGHT_CREATURE_82:GIZZARD +#CREATURE:NIGHT_CREATURE_82:PANCREAS +!CREATURE:NIGHT_CREATURE_82:SPLEEN +!CREATURE:NIGHT_CREATURE_82:KIDNEY +!CREATURE:NIGHT_CREATURE_83:MUSCLE +CREATURE:NIGHT_CREATURE_83:EYE + CREATURE:NIGHT_CREATURE_83:BRAIN +CREATURE:NIGHT_CREATURE_83:LUNG + CREATURE:NIGHT_CREATURE_83:HEART + CREATURE:NIGHT_CREATURE_83:LIVER +CREATURE:NIGHT_CREATURE_83:GUT +"CREATURE:NIGHT_CREATURE_83:STOMACH +"CREATURE:NIGHT_CREATURE_83:GIZZARD +#CREATURE:NIGHT_CREATURE_83:PANCREAS +!CREATURE:NIGHT_CREATURE_83:SPLEEN +!CREATURE:NIGHT_CREATURE_83:KIDNEY +!CREATURE:NIGHT_CREATURE_84:MUSCLE +CREATURE:NIGHT_CREATURE_84:EYE + CREATURE:NIGHT_CREATURE_84:BRAIN +CREATURE:NIGHT_CREATURE_84:LUNG + CREATURE:NIGHT_CREATURE_84:HEART + CREATURE:NIGHT_CREATURE_84:LIVER +CREATURE:NIGHT_CREATURE_84:GUT +"CREATURE:NIGHT_CREATURE_84:STOMACH +"CREATURE:NIGHT_CREATURE_84:GIZZARD +#CREATURE:NIGHT_CREATURE_84:PANCREAS +!CREATURE:NIGHT_CREATURE_84:SPLEEN +!CREATURE:NIGHT_CREATURE_84:KIDNEY +!CREATURE:NIGHT_CREATURE_85:MUSCLE +CREATURE:NIGHT_CREATURE_85:EYE + CREATURE:NIGHT_CREATURE_85:BRAIN +CREATURE:NIGHT_CREATURE_85:LUNG + CREATURE:NIGHT_CREATURE_85:HEART + CREATURE:NIGHT_CREATURE_85:LIVER +CREATURE:NIGHT_CREATURE_85:GUT +"CREATURE:NIGHT_CREATURE_85:STOMACH +"CREATURE:NIGHT_CREATURE_85:GIZZARD +#CREATURE:NIGHT_CREATURE_85:PANCREAS +!CREATURE:NIGHT_CREATURE_85:SPLEEN +!CREATURE:NIGHT_CREATURE_85:KIDNEY +!CREATURE:NIGHT_CREATURE_86:MUSCLE +CREATURE:NIGHT_CREATURE_86:EYE + CREATURE:NIGHT_CREATURE_86:BRAIN +CREATURE:NIGHT_CREATURE_86:LUNG + CREATURE:NIGHT_CREATURE_86:HEART + CREATURE:NIGHT_CREATURE_86:LIVER +CREATURE:NIGHT_CREATURE_86:GUT +"CREATURE:NIGHT_CREATURE_86:STOMACH +"CREATURE:NIGHT_CREATURE_86:GIZZARD +#CREATURE:NIGHT_CREATURE_86:PANCREAS +!CREATURE:NIGHT_CREATURE_86:SPLEEN +!CREATURE:NIGHT_CREATURE_86:KIDNEY +!CREATURE:NIGHT_CREATURE_87:MUSCLE +CREATURE:NIGHT_CREATURE_87:EYE + CREATURE:NIGHT_CREATURE_87:BRAIN +CREATURE:NIGHT_CREATURE_87:LUNG + CREATURE:NIGHT_CREATURE_87:HEART + CREATURE:NIGHT_CREATURE_87:LIVER +CREATURE:NIGHT_CREATURE_87:GUT +"CREATURE:NIGHT_CREATURE_87:STOMACH +"CREATURE:NIGHT_CREATURE_87:GIZZARD +#CREATURE:NIGHT_CREATURE_87:PANCREAS +!CREATURE:NIGHT_CREATURE_87:SPLEEN +!CREATURE:NIGHT_CREATURE_87:KIDNEY +!CREATURE:NIGHT_CREATURE_88:MUSCLE +CREATURE:NIGHT_CREATURE_88:EYE + CREATURE:NIGHT_CREATURE_88:BRAIN +CREATURE:NIGHT_CREATURE_88:LUNG + CREATURE:NIGHT_CREATURE_88:HEART + CREATURE:NIGHT_CREATURE_88:LIVER +CREATURE:NIGHT_CREATURE_88:GUT +"CREATURE:NIGHT_CREATURE_88:STOMACH +"CREATURE:NIGHT_CREATURE_88:GIZZARD +#CREATURE:NIGHT_CREATURE_88:PANCREAS +!CREATURE:NIGHT_CREATURE_88:SPLEEN +!CREATURE:NIGHT_CREATURE_88:KIDNEY +!CREATURE:NIGHT_CREATURE_89:MUSCLE +CREATURE:NIGHT_CREATURE_89:EYE + CREATURE:NIGHT_CREATURE_89:BRAIN +CREATURE:NIGHT_CREATURE_89:LUNG + CREATURE:NIGHT_CREATURE_89:HEART + CREATURE:NIGHT_CREATURE_89:LIVER +CREATURE:NIGHT_CREATURE_89:GUT +"CREATURE:NIGHT_CREATURE_89:STOMACH +"CREATURE:NIGHT_CREATURE_89:GIZZARD +#CREATURE:NIGHT_CREATURE_89:PANCREAS +!CREATURE:NIGHT_CREATURE_89:SPLEEN +!CREATURE:NIGHT_CREATURE_89:KIDNEY +!CREATURE:NIGHT_CREATURE_90:MUSCLE +CREATURE:NIGHT_CREATURE_90:EYE + CREATURE:NIGHT_CREATURE_90:BRAIN +CREATURE:NIGHT_CREATURE_90:LUNG + CREATURE:NIGHT_CREATURE_90:HEART + CREATURE:NIGHT_CREATURE_90:LIVER +CREATURE:NIGHT_CREATURE_90:GUT +"CREATURE:NIGHT_CREATURE_90:STOMACH +"CREATURE:NIGHT_CREATURE_90:GIZZARD +#CREATURE:NIGHT_CREATURE_90:PANCREAS +!CREATURE:NIGHT_CREATURE_90:SPLEEN +!CREATURE:NIGHT_CREATURE_90:KIDNEY +!CREATURE:NIGHT_CREATURE_91:MUSCLE +CREATURE:NIGHT_CREATURE_91:EYE + CREATURE:NIGHT_CREATURE_91:BRAIN +CREATURE:NIGHT_CREATURE_91:LUNG + CREATURE:NIGHT_CREATURE_91:HEART + CREATURE:NIGHT_CREATURE_91:LIVER +CREATURE:NIGHT_CREATURE_91:GUT +"CREATURE:NIGHT_CREATURE_91:STOMACH +"CREATURE:NIGHT_CREATURE_91:GIZZARD +#CREATURE:NIGHT_CREATURE_91:PANCREAS +!CREATURE:NIGHT_CREATURE_91:SPLEEN +!CREATURE:NIGHT_CREATURE_91:KIDNEY +!CREATURE:NIGHT_CREATURE_92:MUSCLE +CREATURE:NIGHT_CREATURE_92:EYE + CREATURE:NIGHT_CREATURE_92:BRAIN +CREATURE:NIGHT_CREATURE_92:LUNG + CREATURE:NIGHT_CREATURE_92:HEART + CREATURE:NIGHT_CREATURE_92:LIVER +CREATURE:NIGHT_CREATURE_92:GUT +"CREATURE:NIGHT_CREATURE_92:STOMACH +"CREATURE:NIGHT_CREATURE_92:GIZZARD +#CREATURE:NIGHT_CREATURE_92:PANCREAS +!CREATURE:NIGHT_CREATURE_92:SPLEEN +!CREATURE:NIGHT_CREATURE_92:KIDNEY +!CREATURE:NIGHT_CREATURE_93:MUSCLE +CREATURE:NIGHT_CREATURE_93:EYE + CREATURE:NIGHT_CREATURE_93:BRAIN +CREATURE:NIGHT_CREATURE_93:LUNG + CREATURE:NIGHT_CREATURE_93:HEART + CREATURE:NIGHT_CREATURE_93:LIVER +CREATURE:NIGHT_CREATURE_93:GUT +"CREATURE:NIGHT_CREATURE_93:STOMACH +"CREATURE:NIGHT_CREATURE_93:GIZZARD +#CREATURE:NIGHT_CREATURE_93:PANCREAS +!CREATURE:NIGHT_CREATURE_93:SPLEEN +!CREATURE:NIGHT_CREATURE_93:KIDNEY +!CREATURE:NIGHT_CREATURE_94:MUSCLE +CREATURE:NIGHT_CREATURE_94:EYE + CREATURE:NIGHT_CREATURE_94:BRAIN +CREATURE:NIGHT_CREATURE_94:LUNG + CREATURE:NIGHT_CREATURE_94:HEART + CREATURE:NIGHT_CREATURE_94:LIVER +CREATURE:NIGHT_CREATURE_94:GUT +"CREATURE:NIGHT_CREATURE_94:STOMACH +"CREATURE:NIGHT_CREATURE_94:GIZZARD +#CREATURE:NIGHT_CREATURE_94:PANCREAS +!CREATURE:NIGHT_CREATURE_94:SPLEEN +!CREATURE:NIGHT_CREATURE_94:KIDNEY +!CREATURE:NIGHT_CREATURE_95:MUSCLE +CREATURE:NIGHT_CREATURE_95:EYE + CREATURE:NIGHT_CREATURE_95:BRAIN +CREATURE:NIGHT_CREATURE_95:LUNG + CREATURE:NIGHT_CREATURE_95:HEART + CREATURE:NIGHT_CREATURE_95:LIVER +CREATURE:NIGHT_CREATURE_95:GUT +"CREATURE:NIGHT_CREATURE_95:STOMACH +"CREATURE:NIGHT_CREATURE_95:GIZZARD +#CREATURE:NIGHT_CREATURE_95:PANCREAS +!CREATURE:NIGHT_CREATURE_95:SPLEEN +!CREATURE:NIGHT_CREATURE_95:KIDNEY +!CREATURE:NIGHT_CREATURE_96:MUSCLE +CREATURE:NIGHT_CREATURE_96:EYE + CREATURE:NIGHT_CREATURE_96:BRAIN +CREATURE:NIGHT_CREATURE_96:LUNG + CREATURE:NIGHT_CREATURE_96:HEART + CREATURE:NIGHT_CREATURE_96:LIVER +CREATURE:NIGHT_CREATURE_96:GUT +"CREATURE:NIGHT_CREATURE_96:STOMACH +"CREATURE:NIGHT_CREATURE_96:GIZZARD +#CREATURE:NIGHT_CREATURE_96:PANCREAS +!CREATURE:NIGHT_CREATURE_96:SPLEEN +!CREATURE:NIGHT_CREATURE_96:KIDNEY +!CREATURE:NIGHT_CREATURE_97:MUSCLE +CREATURE:NIGHT_CREATURE_97:EYE + CREATURE:NIGHT_CREATURE_97:BRAIN +CREATURE:NIGHT_CREATURE_97:LUNG + CREATURE:NIGHT_CREATURE_97:HEART + CREATURE:NIGHT_CREATURE_97:LIVER +CREATURE:NIGHT_CREATURE_97:GUT +"CREATURE:NIGHT_CREATURE_97:STOMACH +"CREATURE:NIGHT_CREATURE_97:GIZZARD +#CREATURE:NIGHT_CREATURE_97:PANCREAS +!CREATURE:NIGHT_CREATURE_97:SPLEEN +!CREATURE:NIGHT_CREATURE_97:KIDNEY +!CREATURE:NIGHT_CREATURE_98:MUSCLE +CREATURE:NIGHT_CREATURE_98:EYE + CREATURE:NIGHT_CREATURE_98:BRAIN +CREATURE:NIGHT_CREATURE_98:LUNG + CREATURE:NIGHT_CREATURE_98:HEART + CREATURE:NIGHT_CREATURE_98:LIVER +CREATURE:NIGHT_CREATURE_98:GUT +"CREATURE:NIGHT_CREATURE_98:STOMACH +"CREATURE:NIGHT_CREATURE_98:GIZZARD +#CREATURE:NIGHT_CREATURE_98:PANCREAS +!CREATURE:NIGHT_CREATURE_98:SPLEEN +!CREATURE:NIGHT_CREATURE_98:KIDNEY +!CREATURE:NIGHT_CREATURE_99:MUSCLE +CREATURE:NIGHT_CREATURE_99:EYE + CREATURE:NIGHT_CREATURE_99:BRAIN +CREATURE:NIGHT_CREATURE_99:LUNG + CREATURE:NIGHT_CREATURE_99:HEART + CREATURE:NIGHT_CREATURE_99:LIVER +CREATURE:NIGHT_CREATURE_99:GUT +"CREATURE:NIGHT_CREATURE_99:STOMACH +"CREATURE:NIGHT_CREATURE_99:GIZZARD +#CREATURE:NIGHT_CREATURE_99:PANCREAS +!CREATURE:NIGHT_CREATURE_99:SPLEEN +!CREATURE:NIGHT_CREATURE_99:KIDNEY +"CREATURE:NIGHT_CREATURE_100:MUSCLE +CREATURE:NIGHT_CREATURE_100:EYE +!CREATURE:NIGHT_CREATURE_100:BRAIN + CREATURE:NIGHT_CREATURE_100:LUNG +!CREATURE:NIGHT_CREATURE_100:HEART +!CREATURE:NIGHT_CREATURE_100:LIVER +CREATURE:NIGHT_CREATURE_100:GUT +#CREATURE:NIGHT_CREATURE_100:STOMACH +#CREATURE:NIGHT_CREATURE_100:GIZZARD +$CREATURE:NIGHT_CREATURE_100:PANCREAS +"CREATURE:NIGHT_CREATURE_100:SPLEEN +"CREATURE:NIGHT_CREATURE_100:KIDNEY +"CREATURE:NIGHT_CREATURE_101:MUSCLE +CREATURE:NIGHT_CREATURE_101:EYE +!CREATURE:NIGHT_CREATURE_101:BRAIN + CREATURE:NIGHT_CREATURE_101:LUNG +!CREATURE:NIGHT_CREATURE_101:HEART +!CREATURE:NIGHT_CREATURE_101:LIVER +CREATURE:NIGHT_CREATURE_101:GUT +#CREATURE:NIGHT_CREATURE_101:STOMACH +#CREATURE:NIGHT_CREATURE_101:GIZZARD +$CREATURE:NIGHT_CREATURE_101:PANCREAS +"CREATURE:NIGHT_CREATURE_101:SPLEEN +"CREATURE:NIGHT_CREATURE_101:KIDNEY +"CREATURE:NIGHT_CREATURE_102:MUSCLE +CREATURE:NIGHT_CREATURE_102:EYE +!CREATURE:NIGHT_CREATURE_102:BRAIN + CREATURE:NIGHT_CREATURE_102:LUNG +!CREATURE:NIGHT_CREATURE_102:HEART +!CREATURE:NIGHT_CREATURE_102:LIVER +CREATURE:NIGHT_CREATURE_102:GUT +#CREATURE:NIGHT_CREATURE_102:STOMACH +#CREATURE:NIGHT_CREATURE_102:GIZZARD +$CREATURE:NIGHT_CREATURE_102:PANCREAS +"CREATURE:NIGHT_CREATURE_102:SPLEEN +"CREATURE:NIGHT_CREATURE_102:KIDNEY +"CREATURE:NIGHT_CREATURE_103:MUSCLE +CREATURE:NIGHT_CREATURE_103:EYE +!CREATURE:NIGHT_CREATURE_103:BRAIN + CREATURE:NIGHT_CREATURE_103:LUNG +!CREATURE:NIGHT_CREATURE_103:HEART +!CREATURE:NIGHT_CREATURE_103:LIVER +CREATURE:NIGHT_CREATURE_103:GUT +#CREATURE:NIGHT_CREATURE_103:STOMACH +#CREATURE:NIGHT_CREATURE_103:GIZZARD +$CREATURE:NIGHT_CREATURE_103:PANCREAS +"CREATURE:NIGHT_CREATURE_103:SPLEEN +"CREATURE:NIGHT_CREATURE_103:KIDNEY +"CREATURE:NIGHT_CREATURE_104:MUSCLE +CREATURE:NIGHT_CREATURE_104:EYE +!CREATURE:NIGHT_CREATURE_104:BRAIN + CREATURE:NIGHT_CREATURE_104:LUNG +!CREATURE:NIGHT_CREATURE_104:HEART +!CREATURE:NIGHT_CREATURE_104:LIVER +CREATURE:NIGHT_CREATURE_104:GUT +#CREATURE:NIGHT_CREATURE_104:STOMACH +#CREATURE:NIGHT_CREATURE_104:GIZZARD +$CREATURE:NIGHT_CREATURE_104:PANCREAS +"CREATURE:NIGHT_CREATURE_104:SPLEEN +"CREATURE:NIGHT_CREATURE_104:KIDNEY +CREATURE:HF1248 DIVINE_1:MUSCLE +CREATURE:HF1248 DIVINE_1:EYE +CREATURE:HF1248 DIVINE_1:BRAIN +CREATURE:HF1248 DIVINE_1:LUNG +CREATURE:HF1248 DIVINE_1:HEART +CREATURE:HF1248 DIVINE_1:LIVER +CREATURE:HF1248 DIVINE_1:GUT + CREATURE:HF1248 DIVINE_1:STOMACH + CREATURE:HF1248 DIVINE_1:GIZZARD +!CREATURE:HF1248 DIVINE_1:PANCREAS +CREATURE:HF1248 DIVINE_1:SPLEEN +CREATURE:HF1248 DIVINE_1:KIDNEY +CREATURE:HF1248 DIVINE_2:MUSCLE +CREATURE:HF1248 DIVINE_2:EYE +CREATURE:HF1248 DIVINE_2:BRAIN +CREATURE:HF1248 DIVINE_2:LUNG +CREATURE:HF1248 DIVINE_2:HEART +CREATURE:HF1248 DIVINE_2:LIVER +CREATURE:HF1248 DIVINE_2:GUT + CREATURE:HF1248 DIVINE_2:STOMACH + CREATURE:HF1248 DIVINE_2:GIZZARD +!CREATURE:HF1248 DIVINE_2:PANCREAS +CREATURE:HF1248 DIVINE_2:SPLEEN +CREATURE:HF1248 DIVINE_2:KIDNEY +CREATURE:HF1248 DIVINE_3:MUSCLE +CREATURE:HF1248 DIVINE_3:EYE +CREATURE:HF1248 DIVINE_3:BRAIN +CREATURE:HF1248 DIVINE_3:LUNG +CREATURE:HF1248 DIVINE_3:HEART +CREATURE:HF1248 DIVINE_3:LIVER +CREATURE:HF1248 DIVINE_3:GUT + CREATURE:HF1248 DIVINE_3:STOMACH + CREATURE:HF1248 DIVINE_3:GIZZARD +!CREATURE:HF1248 DIVINE_3:PANCREAS +CREATURE:HF1248 DIVINE_3:SPLEEN +CREATURE:HF1248 DIVINE_3:KIDNEY +CREATURE:HF1108 DIVINE_2:MUSCLE +CREATURE:HF1108 DIVINE_2:EYE +CREATURE:HF1108 DIVINE_2:BRAIN +CREATURE:HF1108 DIVINE_2:LUNG +CREATURE:HF1108 DIVINE_2:HEART +CREATURE:HF1108 DIVINE_2:LIVER +CREATURE:HF1108 DIVINE_2:GUT + CREATURE:HF1108 DIVINE_2:STOMACH + CREATURE:HF1108 DIVINE_2:GIZZARD +!CREATURE:HF1108 DIVINE_2:PANCREAS +CREATURE:HF1108 DIVINE_2:SPLEEN +CREATURE:HF1108 DIVINE_2:KIDNEY +CREATURE:HF1249 DIVINE_1:MUSCLE +CREATURE:HF1249 DIVINE_1:EYE +CREATURE:HF1249 DIVINE_1:BRAIN +CREATURE:HF1249 DIVINE_1:LUNG +CREATURE:HF1249 DIVINE_1:HEART +CREATURE:HF1249 DIVINE_1:LIVER +CREATURE:HF1249 DIVINE_1:GUT + CREATURE:HF1249 DIVINE_1:STOMACH + CREATURE:HF1249 DIVINE_1:GIZZARD +!CREATURE:HF1249 DIVINE_1:PANCREAS +CREATURE:HF1249 DIVINE_1:SPLEEN +CREATURE:HF1249 DIVINE_1:KIDNEY +CREATURE:HF1249 DIVINE_3:MUSCLE +CREATURE:HF1249 DIVINE_3:EYE +CREATURE:HF1249 DIVINE_3:BRAIN +CREATURE:HF1249 DIVINE_3:LUNG +CREATURE:HF1249 DIVINE_3:HEART +CREATURE:HF1249 DIVINE_3:LIVER +CREATURE:HF1249 DIVINE_3:GUT + CREATURE:HF1249 DIVINE_3:STOMACH + CREATURE:HF1249 DIVINE_3:GIZZARD +!CREATURE:HF1249 DIVINE_3:PANCREAS +CREATURE:HF1249 DIVINE_3:SPLEEN +CREATURE:HF1249 DIVINE_3:KIDNEY +CREATURE:HF1345 DIVINE_3:MUSCLE +CREATURE:HF1345 DIVINE_3:EYE +CREATURE:HF1345 DIVINE_3:BRAIN +CREATURE:HF1345 DIVINE_3:LUNG +CREATURE:HF1345 DIVINE_3:HEART +CREATURE:HF1345 DIVINE_3:LIVER +CREATURE:HF1345 DIVINE_3:GUT + CREATURE:HF1345 DIVINE_3:STOMACH + CREATURE:HF1345 DIVINE_3:GIZZARD +!CREATURE:HF1345 DIVINE_3:PANCREAS +CREATURE:HF1345 DIVINE_3:SPLEEN +CREATURE:HF1345 DIVINE_3:KIDNEYCUTTLEFISH:FEMALECUTTLEFISH:MALENAUTILUS:FEMALE NAUTILUS:MALEMOGHOPPER:FEMALEMOGHOPPER:MALEPOND_TURTLE:FEMALEPOND_TURTLE:MALEMUSSEL:DEFAULTOYSTER:DEFAULTFISH_SALMON:FEMALEFISH_SALMON:MALEFISH_CLOWNFISH:FEMALEFISH_CLOWNFISH:MALEFISH_HAGFISH:FEMALEFISH_HAGFISH:MALEFISH_LAMPREY_BROOK:FEMALEFISH_LAMPREY_BROOK:MALEFISH_RAY_BAT:FEMALEFISH_RAY_BAT:MALEFISH_RAY_THORNBACK:FEMALEFISH_RAY_THORNBACK:MALEFISH_RATFISH_SPOTTED:FEMALEFISH_RATFISH_SPOTTED:MALEFISH_HERRING:FEMALEFISH_HERRING:MALEFISH_SHAD:FEMALEFISH_SHAD:MALEFISH_ANCHOVY:FEMALEFISH_ANCHOVY:MALEFISH_TROUT_STEELHEAD:FEMALEFISH_TROUT_STEELHEAD:MALEFISH_HAKE:FEMALEFISH_HAKE:MALEFISH_SEAHORSE:FEMALEFISH_SEAHORSE:MALEFISH_GLASSEYE:FEMALEFISH_GLASSEYE:MALE FISH_PUFFER_WHITE_SPOTTED:FEMALEFISH_PUFFER_WHITE_SPOTTED:MALEFISH_SOLE:FEMALEFISH_SOLE:MALEFISH_FLOUNDER:FEMALEFISH_FLOUNDER:MALEFISH_MACKEREL:FEMALEFISH_MACKEREL:MALEJELLYFISH_SEA_NETTLE:DEFAULT SQUID:FEMALE +SQUID:MALEFISH_LUNGFISH:FEMALEFISH_LUNGFISH:MALEFISH_LOACH_CLOWN:FEMALEFISH_LOACH_CLOWN:MALEFISH_BULLHEAD_BROWN:FEMALEFISH_BULLHEAD_BROWN:MALEFISH_BULLHEAD_YELLOW:FEMALEFISH_BULLHEAD_YELLOW:MALEFISH_BULLHEAD_BLACK:FEMALEFISH_BULLHEAD_BLACK:MALEFISH_KNIFEFISH_BANDED:FEMALEFISH_KNIFEFISH_BANDED:MALEFISH_CHAR:FEMALEFISH_CHAR:MALEFISH_TROUT_RAINBOW:FEMALEFISH_TROUT_RAINBOW:MALEFISH_MOLLY_SAILFIN:FEMALEFISH_MOLLY_SAILFIN:MALEFISH_GUPPY:FEMALEFISH_GUPPY:MALEFISH_PERCH:FEMALEFISH_PERCH:MALEFISH_CAVE:FEMALEFISH_CAVE:MALELOBSTER_CAVE:FEMALELOBSTER_CAVE:MALEBIRD_BLUEJAY:FEMALEBLUEJAY_MAN:FEMALEGIANT_BLUEJAY:FEMALEBIRD_CARDINAL:FEMALECARDINAL_MAN:FEMALEGIANT_CARDINAL:FEMALEBIRD_GRACKLE:FEMALEGRACKLE_MAN:FEMALEGIANT_GRACKLE:FEMALEBIRD_ORIOLE:FEMALEORIOLE_MAN:FEMALEGIANT_ORIOLE:FEMALEBIRD_RW_BLACKBIRD:FEMALERW_BLACKBIRD_MAN:FEMALEGIANT_RW_BLACKBIRD:FEMALEBIRD_PENGUIN:FEMALEBIRD_PENGUIN_LITTLE:FEMALEBIRD_PENGUIN_EMPEROR:FEMALEPENGUIN MAN:FEMALEBIRD_PENGUIN_GIANT:FEMALEBIRD_FALCON_PEREGRINE:FEMALEPEREGRINE FALCON MAN:FEMALEGIANT PEREGRINE FALCON:FEMALEBIRD_KIWI:FEMALEKIWI MAN:FEMALEBIRD_KIWI_GIANT:FEMALEBIRD_OSTRICH:FEMALEOSTRICH MAN:FEMALEBIRD_OSTRICH_GIANT:FEMALEBIRD_CROW:FEMALECROW_MAN:FEMALEGIANT_CROW:FEMALEBIRD_RAVEN:FEMALERAVEN_MAN:FEMALEGIANT_RAVEN:FEMALEBIRD_CASSOWARY:FEMALECASSOWARY_MAN:FEMALEGIANT_CASSOWARY:FEMALEBIRD_KEA:FEMALEKEA_MAN:FEMALEGIANT_KEA:FEMALEBIRD_OWL_SNOWY:FEMALESNOWY_OWL_MAN:FEMALEGIANT_SNOWY_OWL:FEMALESPARROW:FEMALESPARROW_MAN:FEMALEGIANT_SPARROW:FEMALEBIRD_STORK_WHITE:FEMALEWHITE_STORK_MAN:FEMALEGIANT_WHITE_STORK:FEMALEBIRD_LOON:FEMALELOON_MAN:FEMALEGIANT_LOON:FEMALEBIRD_OWL_BARN:FEMALEBARN_OWL_MAN:FEMALEGIANT_BARN_OWL:FEMALEBIRD_PARAKEET:FEMALEPARAKEET_MAN:FEMALEGIANT_PARAKEET:FEMALEBIRD_KAKAPO:FEMALEKAKAPO_MAN:FEMALEGIANT_KAKAPO:FEMALEBIRD_PARROT_GREY:FEMALEGREY_PARROT_MAN:FEMALEGIANT_GREY_PARROT:FEMALEBIRD_PUFFIN:FEMALEPUFFIN_MAN:FEMALEGIANT_PUFFIN:FEMALEBIRD_SWAN:FEMALESWAN_MAN:FEMALEGIANT_SWAN:FEMALEBIRD_LORIKEET:FEMALELORIKEET_MAN:FEMALEGIANT_LORIKEET:FEMALEBIRD_WREN:FEMALEWREN_MAN:FEMALEGIANT_WREN:FEMALEBIRD_OSPREY:FEMALEOSPREY_MAN:FEMALEGIANT_OSPREY:FEMALEBIRD_EMU:FEMALEEMU_MAN:FEMALEGIANT_EMU:FEMALEBIRD_COCKATIEL:FEMALECOCKATIEL_MAN:FEMALEGIANT_COCKATIEL:FEMALE BIRD_LOVEBIRD_PEACH-FACED:FEMALEPEACH-FACED_LOVEBIRD_MAN:FEMALE!GIANT_PEACH-FACED_LOVEBIRD:FEMALEBIRD_MAGPIE:FEMALEMAGPIE_MAN:FEMALEGIANT_MAGPIE:FEMALEBIRD_KESTREL:FEMALEKESTREL_MAN:FEMALEGIANT_KESTREL:FEMALEBIRD_ALBATROSS:FEMALEALBATROSS_MAN:FEMALEGIANT_ALBATROSS:FEMALEBIRD_OWL_GREAT_HORNED:FEMALEGREAT_HORNED_OWL_MAN:FEMALEGIANT_GREAT_HORNED_OWL:FEMALEBIRD_EAGLE:FEMALEEAGLE_MAN:FEMALEGIANT_EAGLE:FEMALEBIRD_HORNBILL:FEMALEHORNBILL_MAN:FEMALEGIANT_HORNBILL:FEMALEBIRD_LOVEBIRD_MASKED:FEMALEMASKED_LOVEBIRD_MAN:FEMALEGIANT_MASKED_LOVEBIRD:FEMALEBIRD_BUSHTIT:FEMALEBUSHTIT_MAN:FEMALEGIANT_BUSHTIT:FEMALEDESERT TORTOISE:FEMALEDESERT_TORTOISE_MAN:FEMALEGIANT_DESERT_TORTOISE:FEMALEGILA_MONSTER:FEMALEGILA_MONSTER_MAN:FEMALEGIANT_GILA_MONSTER:FEMALEBIRD_CHICKEN:FEMALEBIRD_DUCK:FEMALEBIRD_GOOSE:FEMALEBIRD_GUINEAFOWL:FEMALEBIRD_PEAFOWL_BLUE:FEMALEBIRD_TURKEY:FEMALEPLATYPUS:FEMALEPLATYPUS MAN:FEMALEPLATYPUS, GIANT:FEMALEALLIGATOR:FEMALEALLIGATOR_MAN:FEMALEGIANT_ALLIGATOR:FEMALEBIRD_BUZZARD:FEMALEBUZZARD_MAN:FEMALEGIANT_BUZZARD:FEMALECROCODILE_SALTWATER:FEMALECROCODILE_SALTWATER_MAN:FEMALE GIANT_CROCODILE_SALTWATER:FEMALEBIRD_VULTURE:FEMALEVULTURE_MAN:FEMALEGIANT_VULTURE:FEMALEGIANT TORTOISE:FEMALEGIANT TORTOISE MAN:FEMALEGIGANTIC TORTOISE:FEMALECRUNDLE:FEMALEELK_BIRD:FEMALEHELMET_SNAKE:FEMALEJABBERER:FEMALECAVE_DRAGON:FEMALELIZARD_RHINO_TWO_LEGGED:FEMALE SKINK:FEMALESKINK_MAN:FEMALEGIANT_SKINK:FEMALECHAMELEON:FEMALECHAMELEON_MAN:FEMALEGIANT_CHAMELEON:FEMALE ANOLE:FEMALEANOLE_MAN:FEMALEGIANT_ANOLE:FEMALE IGUANA:FEMALEIGUANA_MAN:FEMALEGIANT_IGUANA:FEMALESNAPPING TURTLE:FEMALE ALLIGATOR SNAPPING TURTLE:FEMALESNAPPING_TURTLE_MAN:FEMALEGIANT_SNAPPING_TURTLE:FEMALEPOND_TURTLE:FEMALEPOND_TURTLE_MAN:FEMALEGIANT_POND_TURTLE:FEMALE KOBOLD:FEMALE DRAGON:FEMALEBEAK_DOG:FEMALESEA_SERPENT:FEMALEBIRD_ROC:FEMALECROCODILE_CAVE:FEMALEBIRD_SWALLOW_CAVE:FEMALECAVE_SWALLOW_MAN:FEMALEBIRD_SWALLOW_CAVE_GIANT:FEMALEREPTILE_MAN:FEMALESERPENT_MAN:FEMALE ADDER:FEMALEADDER_MAN:FEMALEGIANT_ADDER:FEMALEECHIDNA:FEMALEECHIDNA_MAN:FEMALEGIANT_ECHIDNA:FEMALEKINGSNAKE:FEMALEKINGSNAKE_MAN:FEMALEGIANT_KINGSNAKE:FEMALEMONITOR_LIZARD:FEMALEMONITOR_LIZARD_MAN:FEMALEGIANT_MONITOR_LIZARD:FEMALEKING_COBRA:FEMALEKING_COBRA_MAN:FEMALEGIANT_KING_COBRA:FEMALEBLACK_MAMBA:FEMALEBLACK_MAMBA_MAN:FEMALEGIANT_BLACK_MAMBA:FEMALEBUSHMASTER:FEMALEBUSHMASTER_MAN:FEMALEGIANT_BUSHMASTER:FEMALE PYTHON:FEMALEPYTHON_MAN:FEMALEGIANT_PYTHON:FEMALENAGA:FEMALE_TWONAGA:FEMALE_FOURNAGA:FEMALE_SIX"#PLANT:SINGLE-GRAIN_WHEAT:STRUCTURAL" PLANT:TWO-GRAIN_WHEAT:STRUCTURAL"PLANT:SOFT_WHEAT:STRUCTURAL"PLANT:HARD_WHEAT:STRUCTURAL"PLANT:SPELT:STRUCTURAL"PLANT:BARLEY:STRUCTURAL"PLANT:BUCKWHEAT:STRUCTURAL"PLANT:OATS:STRUCTURAL"PLANT:ALFALFA:STRUCTURAL"PLANT:RYE:STRUCTURAL"PLANT:SORGHUM:STRUCTURAL"PLANT:RICE:STRUCTURAL"PLANT:MAIZE:STRUCTURAL"PLANT:QUINOA:STRUCTURAL"PLANT:KANIWA:STRUCTURAL"PLANT:BITTER_VETCH:STRUCTURAL"!PLANT:PENDANT_AMARANTH:STRUCTURAL"PLANT:BLOOD_AMARANTH:STRUCTURAL" PLANT:PURPLE_AMARANTH:STRUCTURAL"PLANT:RED_SPINACH:STRUCTURAL"'PLANT:ELEPHANT-HEAD_AMARANTH:STRUCTURAL"PLANT:PEARL_MILLET:STRUCTURAL"PLANT:WHITE_MILLET:STRUCTURAL"PLANT:FINGER_MILLET:STRUCTURAL"PLANT:FOXTAIL_MILLET:STRUCTURAL"PLANT:FONIO:STRUCTURAL"PLANT:TEFF:STRUCTURAL"PLANT:FLAX:STRUCTURAL"PLANT:JUTE:STRUCTURAL"PLANT:HEMP:STRUCTURAL"PLANT:COTTON:STRUCTURAL"PLANT:RAMIE:STRUCTURAL"PLANT:KENAF:STRUCTURAL"PLANT:PAPYRUS_SEDGE:STRUCTURAL"PLANT:ARTICHOKE:STRUCTURAL"PLANT:ASPARAGUS:STRUCTURAL""PLANT:BAMBARA_GROUNDNUT:STRUCTURAL"PLANT:STRING_BEAN:STRUCTURAL"PLANT:BROAD_BEAN:STRUCTURAL"PLANT:BEET:STRUCTURAL"PLANT:BITTER_MELON:STRUCTURAL"PLANT:CABBAGE:STRUCTURAL"PLANT:CAPER:STRUCTURAL"PLANT:WILD_CARROT:STRUCTURAL"PLANT:CASSAVA:STRUCTURAL"PLANT:CELERY:STRUCTURAL"PLANT:CHICKPEA:STRUCTURAL"PLANT:CHICORY:STRUCTURAL"PLANT:COWPEA:STRUCTURAL"PLANT:CUCUMBER:STRUCTURAL"PLANT:EGGPLANT:STRUCTURAL"PLANT:GARDEN_CRESS:STRUCTURAL"PLANT:GARLIC:STRUCTURAL"PLANT:HORNED_MELON:STRUCTURAL"PLANT:LEEK:STRUCTURAL"PLANT:LENTIL:STRUCTURAL"PLANT:LETTUCE:STRUCTURAL"PLANT:MUNG_BEAN:STRUCTURAL"PLANT:MUSKMELON:STRUCTURAL"PLANT:ONION:STRUCTURAL"PLANT:PARSNIP:STRUCTURAL"PLANT:PEA:STRUCTURAL"PLANT:PEANUT:STRUCTURAL"PLANT:PEPPER:STRUCTURAL"PLANT:POTATO:STRUCTURAL"PLANT:RADISH:STRUCTURAL"PLANT:RED_BEAN:STRUCTURAL"PLANT:RHUBARB:STRUCTURAL"PLANT:SOYBEAN:STRUCTURAL"PLANT:SPINACH:STRUCTURAL"PLANT:SQUASH:STRUCTURAL"PLANT:SWEET_POTATO:STRUCTURAL"PLANT:TARO:STRUCTURAL"PLANT:TOMATO:STRUCTURAL"PLANT:TOMATILLO:STRUCTURAL"PLANT:TURNIP:STRUCTURAL"PLANT:URAD_BEAN:STRUCTURAL"PLANT:WATERMELON:STRUCTURAL"PLANT:WINTER_MELON:STRUCTURAL"PLANT:LESSER_YAM:STRUCTURAL"PLANT:LONG_YAM:STRUCTURAL"PLANT:PURPLE_YAM:STRUCTURAL"PLANT:WHITE_YAM:STRUCTURAL"PLANT:PASSION_FRUIT:STRUCTURAL"PLANT:GRAPE:STRUCTURAL"PLANT:CRANBERRY:STRUCTURAL"PLANT:BILBERRY:STRUCTURAL"PLANT:BLUEBERRY:STRUCTURAL"PLANT:BLACKBERRY:STRUCTURAL"PLANT:RASPBERRY:STRUCTURAL"PLANT:PINEAPPLE:STRUCTURAL"PLANT:MEADOW-GRASS:STRUCTURAL"PLANT:HAIR GRASS:STRUCTURAL"PLANT:BENTGRASS:STRUCTURAL"PLANT:RYEGRASS:STRUCTURAL"PLANT:FESCUE GRASS:STRUCTURAL"PLANT:REEDGRASS:STRUCTURAL"PLANT:KNOTGRASS:STRUCTURAL"PLANT:ZOYSIA:STRUCTURAL""PLANT:DOG'S TOOTH GRASS:STRUCTURAL"PLANT:DALLISGRASS:STRUCTURAL"PLANT:CARPETGRASS:STRUCTURAL"PLANT:SATINTAIL:STRUCTURAL"PLANT:GRAMA:STRUCTURAL"PLANT:DROPSEED GRASS:STRUCTURAL"PLANT:NEEDLE GRASS:STRUCTURAL"$PLANT:BABY TOES SUCCULENT:STRUCTURAL"PLANT:PEBBLE PLANTS:STRUCTURAL"PLANT:BLUE SEDGE:STRUCTURAL"PLANT:FIELD SEDGE:STRUCTURAL""PLANT:PURPLE MOOR GRASS:STRUCTURAL"PLANT:VELVET GRASS:STRUCTURAL"PLANT:MEADOWSWEET:STRUCTURAL"PLANT:RUSH:STRUCTURAL"PLANT:MARSH THISTLE:STRUCTURAL"PLANT:COMMON REED:STRUCTURAL"PLANT:CATTAIL:STRUCTURAL"PLANT:SAWGRASS:STRUCTURAL"PLANT:COTTONGRASS:STRUCTURAL"'PLANT:WHITE MOUNTAIN HEATHER:STRUCTURAL"PLANT:MOUNTAIN AVENS:STRUCTURAL"PLANT:CLOUDBERRY:STRUCTURAL"PLANT:WORMY TENDRILS:STRUCTURAL"PLANT:EYEBALL:STRUCTURAL"PLANT:BUBBLE BULBS:STRUCTURAL"PLANT:DOWNY GRASS:STRUCTURAL"PLANT:CAVE MOSS:STRUCTURAL"PLANT:FLOOR FUNGI:STRUCTURAL"PLANT:UNDERLICHEN:STRUCTURAL"PLANT:BAMBOO, ARROW:STRUCTURAL"PLANT:BAMBOO, GOLDEN:STRUCTURAL"PLANT:BAMBOO, HEDGE:STRUCTURAL"PLANT:ABACA:STRUCTURAL"PLANT:BANANA:STRUCTURAL"PLANT:CARAMBOLA:STRUCTURAL"PLANT:CASHEW:STRUCTURAL"PLANT:COFFEE:STRUCTURAL"PLANT:DURIAN:STRUCTURAL"PLANT:GUAVA:STRUCTURAL"PLANT:PAPAYA:STRUCTURAL"PLANT:PARADISE_NUT:STRUCTURAL"PLANT:RAMBUTAN:STRUCTURAL"PLANT:TEA:STRUCTURAL"PLANT:AVOCADO:STRUCTURAL"PLANT:LIME:STRUCTURAL"PLANT:POMELO:STRUCTURAL"PLANT:CITRON:STRUCTURAL"PLANT:ORANGE:STRUCTURAL"PLANT:BITTER_ORANGE:STRUCTURAL"PLANT:FINGER_LIME:STRUCTURAL"PLANT:ROUND_LIME:STRUCTURAL"PLANT:DESERT_LIME:STRUCTURAL"PLANT:KUMQUAT:STRUCTURAL"PLANT:CUSTARD-APPLE:STRUCTURAL"PLANT:DATE_PALM:STRUCTURAL"PLANT:LYCHEE:STRUCTURAL"PLANT:MACADAMIA:STRUCTURAL"PLANT:OLIVE:STRUCTURAL"PLANT:POMEGRANATE:STRUCTURAL"PLANT:ALMOND:STRUCTURAL"PLANT:APPLE:STRUCTURAL"PLANT:APRICOT:STRUCTURAL"PLANT:BAYBERRY:STRUCTURAL"PLANT:CHERRY:STRUCTURAL"PLANT:GINKGO:STRUCTURAL"PLANT:HAZEL:STRUCTURAL"PLANT:PEACH:STRUCTURAL"PLANT:PEAR:STRUCTURAL"PLANT:PECAN:STRUCTURAL"PLANT:PERSIMMON:STRUCTURAL"PLANT:PLUM:STRUCTURAL"PLANT:SAND_PEAR:STRUCTURAL"PLANT:WALNUT:STRUCTURAL"&PLANT:MUSHROOM_HELMET_PLUMP:STRUCTURAL"PLANT:GRASS_TAIL_PIG:STRUCTURAL"!PLANT:GRASS_WHEAT_CAVE:STRUCTURAL"PLANT:POD_SWEET:STRUCTURAL"PLANT:BUSH_QUARRY:STRUCTURAL"PLANT:ROOT_MUCK:STRUCTURAL"PLANT:TUBER_BLOATED:STRUCTURAL"PLANT:BULB_KOBOLD:STRUCTURAL" PLANT:BERRIES_PRICKLE:STRUCTURAL"PLANT:BERRIES_STRAW:STRUCTURAL"PLANT:GRASS_LONGLAND:STRUCTURAL"PLANT:HERB_VALLEY:STRUCTURAL"PLANT:WEED_RAT:STRUCTURAL"PLANT:BERRIES_FISHER:STRUCTURAL"PLANT:REED_ROPE:STRUCTURAL"$PLANT:MUSHROOM_CUP_DIMPLE:STRUCTURAL"PLANT:WEED_BLADE:STRUCTURAL"PLANT:ROOT_HIDE:STRUCTURAL"PLANT:SLIVER_BARB:STRUCTURAL"PLANT:BERRY_SUN:STRUCTURAL"PLANT:VINE_WHIP:STRUCTURAL"PLANT:MANGROVE:STRUCTURAL"PLANT:SAGUARO:STRUCTURAL"PLANT:PINE:STRUCTURAL"PLANT:CEDAR:STRUCTURAL"PLANT:OAK:STRUCTURAL"PLANT:MAHOGANY:STRUCTURAL"PLANT:ACACIA:STRUCTURAL"PLANT:KAPOK:STRUCTURAL"PLANT:MAPLE:STRUCTURAL"PLANT:WILLOW:STRUCTURAL"PLANT:TOWER_CAP:STRUCTURAL"PLANT:BLACK_CAP:STRUCTURAL"PLANT:NETHER_CAP:STRUCTURAL"PLANT:GOBLIN_CAP:STRUCTURAL"PLANT:FUNGIWOOD:STRUCTURAL"PLANT:TUNNEL_TUBE:STRUCTURAL"PLANT:SPORE_TREE:STRUCTURAL"PLANT:BLOOD_THORN:STRUCTURAL"PLANT:GLUMPRONG:STRUCTURAL"PLANT:FEATHER:STRUCTURAL"PLANT:HIGHWOOD:STRUCTURAL"PLANT:LARCH:STRUCTURAL"PLANT:CHESTNUT:STRUCTURAL"PLANT:ALDER:STRUCTURAL"PLANT:BIRCH:STRUCTURAL"PLANT:ASH:STRUCTURAL"PLANT:CANDLENUT:STRUCTURAL"PLANT:MANGO:STRUCTURAL"PLANT:RUBBER:STRUCTURAL"PLANT:CACAO:STRUCTURAL"PLANT:PALM:STRUCTURAL*PLANT:SINGLE-GRAIN_WHEAT:DRINK*PLANT:TWO-GRAIN_WHEAT:DRINK*PLANT:SOFT_WHEAT:DRINK*PLANT:HARD_WHEAT:DRINK*PLANT:SPELT:DRINK*PLANT:BARLEY:DRINK*PLANT:BUCKWHEAT:DRINK*PLANT:RYE:DRINK*PLANT:SORGHUM:DRINK*PLANT:RICE:DRINK*PLANT:MAIZE:DRINK*PLANT:QUINOA:DRINK*PLANT:KANIWA:DRINK*PLANT:PENDANT_AMARANTH:DRINK*PLANT:BLOOD_AMARANTH:DRINK*PLANT:PURPLE_AMARANTH:DRINK*PLANT:PEARL_MILLET:DRINK*PLANT:WHITE_MILLET:DRINK*PLANT:FINGER_MILLET:DRINK*PLANT:FOXTAIL_MILLET:DRINK*PLANT:FONIO:DRINK*PLANT:TEFF:DRINK*PLANT:ARTICHOKE:DRINK*PLANT:BEET:DRINK*PLANT:WILD_CARROT:DRINK*PLANT:CASSAVA:DRINK*PLANT:PARSNIP:DRINK*PLANT:POTATO:DRINK*PLANT:RADISH:DRINK*PLANT:SWEET_POTATO:DRINK*PLANT:TOMATO:DRINK*PLANT:TOMATILLO:DRINK*PLANT:TURNIP:DRINK*PLANT:PASSION_FRUIT:DRINK*PLANT:GRAPE:DRINK*PLANT:CRANBERRY:DRINK*PLANT:BILBERRY:DRINK*PLANT:BLUEBERRY:DRINK*PLANT:BLACKBERRY:DRINK*PLANT:RASPBERRY:DRINK*PLANT:PINEAPPLE:DRINK*PLANT:BANANA:DRINK*PLANT:CARAMBOLA:DRINK*PLANT:DURIAN:DRINK*PLANT:GUAVA:DRINK*PLANT:PAPAYA:DRINK*PLANT:RAMBUTAN:DRINK*PLANT:CUSTARD-APPLE:DRINK*PLANT:DATE_PALM:DRINK*PLANT:LYCHEE:DRINK*PLANT:POMEGRANATE:DRINK*PLANT:APPLE:DRINK*PLANT:APRICOT:DRINK*PLANT:BAYBERRY:DRINK*PLANT:CHERRY:DRINK*PLANT:PEACH:DRINK*PLANT:PEAR:DRINK*PLANT:PERSIMMON:DRINK*PLANT:PLUM:DRINK*PLANT:SAND_PEAR:DRINK*!PLANT:MUSHROOM_HELMET_PLUMP:DRINK*PLANT:GRASS_TAIL_PIG:DRINK*PLANT:GRASS_WHEAT_CAVE:DRINK*PLANT:POD_SWEET:DRINK*PLANT:ROOT_MUCK:DRINK*PLANT:TUBER_BLOATED:DRINK*PLANT:BERRIES_PRICKLE:DRINK*PLANT:BERRIES_STRAW:DRINK*PLANT:GRASS_LONGLAND:DRINK*PLANT:WEED_RAT:DRINK*PLANT:BERRIES_FISHER:DRINK*PLANT:REED_ROPE:DRINK*PLANT:SLIVER_BARB:DRINK*PLANT:BERRY_SUN:DRINK*PLANT:VINE_WHIP:DRINK*PLANT:MANGO:DRINK2CREATURE:HONEY_BEE:MEAD2CREATURE:BUMBLEBEE:MEADBCREATURE:DONKEY:CHEESEBCREATURE:HORSE:CHEESEBCREATURE:COW:CHEESEBCREATURE:SHEEP:CHEESEBCREATURE:PIG:CHEESEBCREATURE:GOAT:CHEESEBCREATURE:WATER_BUFFALO:CHEESEBCREATURE:REINDEER:CHEESEBCREATURE:YAK:CHEESEBCREATURE:LLAMA:CHEESEBCREATURE:ALPACA:CHEESEBCREATURE:CAMEL_1_HUMP:CHEESEB CREATURE:CAMEL_1_HUMP_MAN:CHEESEB"CREATURE:GIANT_CAMEL_1_HUMP:CHEESEBCREATURE:CAMEL_2_HUMP:CHEESEB CREATURE:CAMEL_2_HUMP_MAN:CHEESEB"CREATURE:GIANT_CAMEL_2_HUMP:CHEESEBCREATURE:MAGGOT_PURRING:CHEESEBCREATURE:KANGAROO:CHEESEBCREATURE:KANGAROO_MAN:CHEESEBCREATURE:GIANT_KANGAROO:CHEESEBCREATURE:TAPIR:CHEESEBCREATURE:TAPIR_MAN:CHEESEBCREATURE:GIANT_TAPIR:CHEESEJPLANT:SINGLE-GRAIN_WHEAT:SEEDJPLANT:TWO-GRAIN_WHEAT:SEEDJPLANT:SOFT_WHEAT:SEEDJPLANT:HARD_WHEAT:SEEDJPLANT:SPELT:SEEDJPLANT:BARLEY:SEEDJPLANT:BUCKWHEAT:SEEDJPLANT:OATS:SEEDJPLANT:ALFALFA:SEEDJPLANT:RYE:SEEDJPLANT:SORGHUM:SEEDJPLANT:RICE:SEEDJPLANT:MAIZE:SEEDJPLANT:QUINOA:SEEDJPLANT:KANIWA:SEEDJPLANT:BITTER_VETCH:SEEDJPLANT:PENDANT_AMARANTH:SEEDJPLANT:BLOOD_AMARANTH:SEEDJPLANT:PURPLE_AMARANTH:SEEDJPLANT:RED_SPINACH:SEEDJ!PLANT:ELEPHANT-HEAD_AMARANTH:SEEDJPLANT:PEARL_MILLET:SEEDJPLANT:WHITE_MILLET:SEEDJPLANT:FINGER_MILLET:SEEDJPLANT:FOXTAIL_MILLET:SEEDJPLANT:FONIO:SEEDJPLANT:TEFF:SEEDJPLANT:FLAX:SEEDJPLANT:JUTE:SEEDJPLANT:HEMP:SEEDJPLANT:COTTON:SEEDJPLANT:RAMIE:SEEDJPLANT:KENAF:SEEDJPLANT:PAPYRUS_SEDGE:SEEDJPLANT:ARTICHOKE:SEEDJPLANT:ASPARAGUS:SEEDJPLANT:BAMBARA_GROUNDNUT:SEEDJPLANT:STRING_BEAN:SEEDJPLANT:BROAD_BEAN:SEEDJPLANT:BEET:SEEDJPLANT:BITTER_MELON:SEEDJPLANT:CABBAGE:SEEDJPLANT:CAPER:SEEDJPLANT:WILD_CARROT:SEEDJPLANT:CASSAVA:SEEDJPLANT:CELERY:SEEDJPLANT:CHICKPEA:SEEDJPLANT:CHICORY:SEEDJPLANT:COWPEA:SEEDJPLANT:CUCUMBER:SEEDJPLANT:EGGPLANT:SEEDJPLANT:GARDEN_CRESS:SEEDJPLANT:GARLIC:SEEDJPLANT:HORNED_MELON:SEEDJPLANT:LEEK:SEEDJPLANT:LENTIL:SEEDJPLANT:LETTUCE:SEEDJPLANT:MUNG_BEAN:SEEDJPLANT:MUSKMELON:SEEDJPLANT:ONION:SEEDJPLANT:PARSNIP:SEEDJPLANT:PEA:SEEDJPLANT:PEANUT:SEEDJPLANT:PEPPER:SEEDJPLANT:POTATO:SEEDJPLANT:RADISH:SEEDJPLANT:RED_BEAN:SEEDJPLANT:RHUBARB:SEEDJPLANT:SOYBEAN:SEEDJPLANT:SPINACH:SEEDJPLANT:SQUASH:SEEDJPLANT:SWEET_POTATO:SEEDJPLANT:TARO:SEEDJPLANT:TOMATO:SEEDJPLANT:TOMATILLO:SEEDJPLANT:TURNIP:SEEDJPLANT:URAD_BEAN:SEEDJPLANT:WATERMELON:SEEDJPLANT:WINTER_MELON:SEEDJPLANT:LESSER_YAM:SEEDJPLANT:LONG_YAM:SEEDJPLANT:PURPLE_YAM:SEEDJPLANT:WHITE_YAM:SEEDJPLANT:PASSION_FRUIT:SEEDJPLANT:GRAPE:SEEDJPLANT:CRANBERRY:SEEDJPLANT:BILBERRY:SEEDJPLANT:BLUEBERRY:SEEDJPLANT:BLACKBERRY:SEEDJPLANT:RASPBERRY:SEEDJPLANT:PINEAPPLE:SEEDJPLANT:ABACA:SEEDJPLANT:BANANA:SEEDJPLANT:CARAMBOLA:SEEDJPLANT:CASHEW:SEEDJPLANT:COFFEE:SEEDJPLANT:DURIAN:SEEDJPLANT:GUAVA:SEEDJPLANT:PAPAYA:SEEDJPLANT:PARADISE_NUT:SEEDJPLANT:RAMBUTAN:SEEDJPLANT:TEA:SEEDJPLANT:AVOCADO:SEEDJPLANT:LIME:SEEDJPLANT:POMELO:SEEDJPLANT:CITRON:SEEDJPLANT:ORANGE:SEEDJPLANT:BITTER_ORANGE:SEEDJPLANT:FINGER_LIME:SEEDJPLANT:ROUND_LIME:SEEDJPLANT:DESERT_LIME:SEEDJPLANT:KUMQUAT:SEEDJPLANT:CUSTARD-APPLE:SEEDJPLANT:DATE_PALM:SEEDJPLANT:LYCHEE:SEEDJPLANT:MACADAMIA:SEEDJPLANT:OLIVE:SEEDJPLANT:POMEGRANATE:SEEDJPLANT:ALMOND:SEEDJPLANT:APPLE:SEEDJPLANT:APRICOT:SEEDJPLANT:BAYBERRY:SEEDJPLANT:CHERRY:SEEDJPLANT:GINKGO:SEEDJPLANT:HAZEL:SEEDJPLANT:PEACH:SEEDJPLANT:PEAR:SEEDJPLANT:PECAN:SEEDJPLANT:PERSIMMON:SEEDJPLANT:PLUM:SEEDJPLANT:SAND_PEAR:SEEDJPLANT:WALNUT:SEEDJ PLANT:MUSHROOM_HELMET_PLUMP:SEEDJPLANT:GRASS_TAIL_PIG:SEEDJPLANT:GRASS_WHEAT_CAVE:SEEDJPLANT:POD_SWEET:SEEDJPLANT:BUSH_QUARRY:SEEDJPLANT:ROOT_MUCK:SEEDJPLANT:TUBER_BLOATED:SEEDJPLANT:BERRIES_PRICKLE:SEEDJPLANT:BERRIES_STRAW:SEEDJPLANT:GRASS_LONGLAND:SEEDJPLANT:WEED_RAT:SEEDJPLANT:BERRIES_FISHER:SEEDJPLANT:REED_ROPE:SEEDJPLANT:MUSHROOM_CUP_DIMPLE:SEEDJPLANT:WEED_BLADE:SEEDJPLANT:ROOT_HIDE:SEEDJPLANT:SLIVER_BARB:SEEDJPLANT:BERRY_SUN:SEEDJPLANT:VINE_WHIP:SEEDJPLANT:OAK:SEEDJPLANT:ACACIA:SEEDJPLANT:CHESTNUT:SEEDJPLANT:CANDLENUT:SEEDJPLANT:MANGO:SEEDJPLANT:CACAO:SEEDRPLANT:BITTER_VETCH:LEAFRPLANT:BLOOD_AMARANTH:LEAFRPLANT:PURPLE_AMARANTH:LEAFRPLANT:RED_SPINACH:LEAFR!PLANT:ELEPHANT-HEAD_AMARANTH:LEAFRPLANT:ARTICHOKE:HEARTRPLANT:BITTER_MELON:LEAFRPLANT:BITTER_MELON:FRUITRPLANT:CAPER:LEAFRPLANT:CAPER:BUDRPLANT:CAPER:FRUITRPLANT:CUCUMBER:FRUITRPLANT:EGGPLANT:FRUITRPLANT:GARDEN_CRESS:LEAFRPLANT:GARLIC:BULBRPLANT:HORNED_MELON:FRUITRPLANT:LETTUCE:LEAFRPLANT:MUSKMELON:FRUITRPLANT:ONION:BULBRPLANT:PEPPER:FRUITRPLANT:SPINACH:LEAFRPLANT:SQUASH:FRUITRPLANT:TOMATO:FRUITRPLANT:TOMATILLO:FRUITRPLANT:WATERMELON:FRUITRPLANT:WINTER_MELON:FRUITRPLANT:PASSION_FRUIT:FRUITRPLANT:GRAPE:FRUITRPLANT:CRANBERRY:FRUITRPLANT:BILBERRY:FRUITRPLANT:BLUEBERRY:FRUITRPLANT:BLACKBERRY:FRUITRPLANT:RASPBERRY:FRUITRPLANT:PINEAPPLE:FRUITRPLANT:BANANA:FRUITRPLANT:CARAMBOLA:FRUITRPLANT:CASHEW:FRUITRPLANT:COFFEE:FRUITRPLANT:DURIAN:FRUITRPLANT:GUAVA:FRUITRPLANT:PAPAYA:FRUITRPLANT:PARADISE_NUT:FRUITRPLANT:RAMBUTAN:FRUITRPLANT:AVOCADO:FRUITRPLANT:LIME:FRUITRPLANT:POMELO:FRUITRPLANT:CITRON:FRUITRPLANT:ORANGE:FRUITRPLANT:BITTER_ORANGE:FRUITRPLANT:FINGER_LIME:FRUITRPLANT:ROUND_LIME:FRUITRPLANT:DESERT_LIME:FRUITRPLANT:KUMQUAT:FRUITRPLANT:CUSTARD-APPLE:FRUITRPLANT:DATE_PALM:FLOWERRPLANT:DATE_PALM:FRUITRPLANT:LYCHEE:FRUITRPLANT:OLIVE:FRUITRPLANT:POMEGRANATE:FRUITRPLANT:APPLE:FRUITRPLANT:APRICOT:FRUITRPLANT:BAYBERRY:FRUITRPLANT:CHERRY:FRUITRPLANT:PEACH:FRUITRPLANT:PEAR:FRUITRPLANT:PERSIMMON:FRUITRPLANT:PLUM:FRUITRPLANT:SAND_PEAR:FRUITRPLANT:BUSH_QUARRY:LEAFRPLANT:BERRIES_STRAW:FRUITRPLANT:SAGUARO:FRUITRPLANT:FEATHER:EGGRPLANT:MANGO:FRUITRPLANT:PALM:NUTZPLANT:SINGLE-GRAIN_WHEAT:MILLZPLANT:TWO-GRAIN_WHEAT:MILLZPLANT:SOFT_WHEAT:MILLZPLANT:HARD_WHEAT:MILLZPLANT:SPELT:MILLZPLANT:BARLEY:MILLZPLANT:BUCKWHEAT:MILLZPLANT:OATS:MILLZPLANT:RYE:MILLZPLANT:SORGHUM:MILLZPLANT:RICE:MILLZPLANT:MAIZE:MILLZPLANT:QUINOA:MILLZPLANT:KANIWA:MILLZPLANT:PENDANT_AMARANTH:MILLZPLANT:BLOOD_AMARANTH:MILLZPLANT:PURPLE_AMARANTH:MILLZPLANT:PEARL_MILLET:MILLZPLANT:WHITE_MILLET:MILLZPLANT:FINGER_MILLET:MILLZPLANT:FOXTAIL_MILLET:MILLZPLANT:FONIO:MILLZPLANT:TEFF:MILLZPLANT:FLAX:MILLZPLANT:HEMP:MILLZPLANT:GRASS_WHEAT_CAVE:MILLZPLANT:POD_SWEET:MILLZPLANT:GRASS_LONGLAND:MILLZPLANT:MUSHROOM_CUP_DIMPLE:MILLZPLANT:WEED_BLADE:MILLZPLANT:ROOT_HIDE:MILLZPLANT:SLIVER_BARB:MILLZPLANT:VINE_WHIP:MILLjCREATURE:TOAD:FATjCREATURE:TOAD:TALLOWjCREATURE:TOAD_MAN:FATjCREATURE:TOAD_MAN:TALLOWjCREATURE:GIANT_TOAD:FATjCREATURE:GIANT_TOAD:TALLOWjCREATURE:WORM:FATjCREATURE:WORM:TALLOWjCREATURE:WORM_MAN:FATjCREATURE:WORM_MAN:TALLOWjCREATURE:BIRD_BLUEJAY:FATjCREATURE:BIRD_BLUEJAY:TALLOWjCREATURE:BLUEJAY_MAN:FATjCREATURE:BLUEJAY_MAN:TALLOWjCREATURE:GIANT_BLUEJAY:FATjCREATURE:GIANT_BLUEJAY:TALLOWjCREATURE:BIRD_CARDINAL:FATjCREATURE:BIRD_CARDINAL:TALLOWjCREATURE:CARDINAL_MAN:FATjCREATURE:CARDINAL_MAN:TALLOWjCREATURE:GIANT_CARDINAL:FATjCREATURE:GIANT_CARDINAL:TALLOWjCREATURE:BIRD_GRACKLE:FATjCREATURE:BIRD_GRACKLE:TALLOWjCREATURE:GRACKLE_MAN:FATjCREATURE:GRACKLE_MAN:TALLOWjCREATURE:GIANT_GRACKLE:FATjCREATURE:GIANT_GRACKLE:TALLOWjCREATURE:BIRD_ORIOLE:FATjCREATURE:BIRD_ORIOLE:TALLOWjCREATURE:ORIOLE_MAN:FATjCREATURE:ORIOLE_MAN:TALLOWjCREATURE:GIANT_ORIOLE:FATjCREATURE:GIANT_ORIOLE:TALLOWjCREATURE:BIRD_RW_BLACKBIRD:FATj!CREATURE:BIRD_RW_BLACKBIRD:TALLOWjCREATURE:RW_BLACKBIRD_MAN:FATj CREATURE:RW_BLACKBIRD_MAN:TALLOWjCREATURE:GIANT_RW_BLACKBIRD:FATj"CREATURE:GIANT_RW_BLACKBIRD:TALLOWjCREATURE:BIRD_PENGUIN:FATjCREATURE:BIRD_PENGUIN:TALLOWj CREATURE:BIRD_PENGUIN_LITTLE:FATj#CREATURE:BIRD_PENGUIN_LITTLE:TALLOWj!CREATURE:BIRD_PENGUIN_EMPEROR:FATj$CREATURE:BIRD_PENGUIN_EMPEROR:TALLOWjCREATURE:PENGUIN MAN:FATjCREATURE:PENGUIN MAN:TALLOWjCREATURE:BIRD_PENGUIN_GIANT:FATj"CREATURE:BIRD_PENGUIN_GIANT:TALLOWj"CREATURE:BIRD_FALCON_PEREGRINE:FATj%CREATURE:BIRD_FALCON_PEREGRINE:TALLOWj!CREATURE:PEREGRINE FALCON MAN:FATj$CREATURE:PEREGRINE FALCON MAN:TALLOWj#CREATURE:GIANT PEREGRINE FALCON:FATj&CREATURE:GIANT PEREGRINE FALCON:TALLOWjCREATURE:BIRD_KIWI:FATjCREATURE:BIRD_KIWI:TALLOWjCREATURE:KIWI MAN:FATjCREATURE:KIWI MAN:TALLOWjCREATURE:BIRD_KIWI_GIANT:FATjCREATURE:BIRD_KIWI_GIANT:TALLOWjCREATURE:BIRD_OSTRICH:FATjCREATURE:BIRD_OSTRICH:TALLOWjCREATURE:OSTRICH MAN:FATjCREATURE:OSTRICH MAN:TALLOWjCREATURE:BIRD_OSTRICH_GIANT:FATj"CREATURE:BIRD_OSTRICH_GIANT:TALLOWjCREATURE:BIRD_CROW:FATjCREATURE:BIRD_CROW:TALLOWjCREATURE:CROW_MAN:FATjCREATURE:CROW_MAN:TALLOWjCREATURE:GIANT_CROW:FATjCREATURE:GIANT_CROW:TALLOWjCREATURE:BIRD_RAVEN:FATjCREATURE:BIRD_RAVEN:TALLOWjCREATURE:RAVEN_MAN:FATjCREATURE:RAVEN_MAN:TALLOWjCREATURE:GIANT_RAVEN:FATjCREATURE:GIANT_RAVEN:TALLOWjCREATURE:BIRD_CASSOWARY:FATjCREATURE:BIRD_CASSOWARY:TALLOWjCREATURE:CASSOWARY_MAN:FATjCREATURE:CASSOWARY_MAN:TALLOWjCREATURE:GIANT_CASSOWARY:FATjCREATURE:GIANT_CASSOWARY:TALLOWjCREATURE:BIRD_KEA:FATjCREATURE:BIRD_KEA:TALLOWjCREATURE:KEA_MAN:FATjCREATURE:KEA_MAN:TALLOWjCREATURE:GIANT_KEA:FATjCREATURE:GIANT_KEA:TALLOWjCREATURE:BIRD_OWL_SNOWY:FATjCREATURE:BIRD_OWL_SNOWY:TALLOWjCREATURE:SNOWY_OWL_MAN:FATjCREATURE:SNOWY_OWL_MAN:TALLOWjCREATURE:GIANT_SNOWY_OWL:FATjCREATURE:GIANT_SNOWY_OWL:TALLOWjCREATURE:SPARROW:FATjCREATURE:SPARROW:TALLOWjCREATURE:SPARROW_MAN:FATjCREATURE:SPARROW_MAN:TALLOWjCREATURE:GIANT_SPARROW:FATjCREATURE:GIANT_SPARROW:TALLOWjCREATURE:BIRD_STORK_WHITE:FATj CREATURE:BIRD_STORK_WHITE:TALLOWjCREATURE:WHITE_STORK_MAN:FATjCREATURE:WHITE_STORK_MAN:TALLOWjCREATURE:GIANT_WHITE_STORK:FATj!CREATURE:GIANT_WHITE_STORK:TALLOWjCREATURE:BIRD_LOON:FATjCREATURE:BIRD_LOON:TALLOWjCREATURE:LOON_MAN:FATjCREATURE:LOON_MAN:TALLOWjCREATURE:GIANT_LOON:FATjCREATURE:GIANT_LOON:TALLOWjCREATURE:BIRD_OWL_BARN:FATjCREATURE:BIRD_OWL_BARN:TALLOWjCREATURE:BARN_OWL_MAN:FATjCREATURE:BARN_OWL_MAN:TALLOWjCREATURE:GIANT_BARN_OWL:FATjCREATURE:GIANT_BARN_OWL:TALLOWjCREATURE:BIRD_PARAKEET:FATjCREATURE:BIRD_PARAKEET:TALLOWjCREATURE:PARAKEET_MAN:FATjCREATURE:PARAKEET_MAN:TALLOWjCREATURE:GIANT_PARAKEET:FATjCREATURE:GIANT_PARAKEET:TALLOWjCREATURE:BIRD_KAKAPO:FATjCREATURE:BIRD_KAKAPO:TALLOWjCREATURE:KAKAPO_MAN:FATjCREATURE:KAKAPO_MAN:TALLOWjCREATURE:GIANT_KAKAPO:FATjCREATURE:GIANT_KAKAPO:TALLOWjCREATURE:BIRD_PARROT_GREY:FATj CREATURE:BIRD_PARROT_GREY:TALLOWjCREATURE:GREY_PARROT_MAN:FATjCREATURE:GREY_PARROT_MAN:TALLOWjCREATURE:GIANT_GREY_PARROT:FATj!CREATURE:GIANT_GREY_PARROT:TALLOWjCREATURE:BIRD_PUFFIN:FATjCREATURE:BIRD_PUFFIN:TALLOWjCREATURE:PUFFIN_MAN:FATjCREATURE:PUFFIN_MAN:TALLOWjCREATURE:GIANT_PUFFIN:FATjCREATURE:GIANT_PUFFIN:TALLOWjCREATURE:BIRD_SWAN:FATjCREATURE:BIRD_SWAN:TALLOWjCREATURE:SWAN_MAN:FATjCREATURE:SWAN_MAN:TALLOWjCREATURE:GIANT_SWAN:FATjCREATURE:GIANT_SWAN:TALLOWjCREATURE:BIRD_LORIKEET:FATjCREATURE:BIRD_LORIKEET:TALLOWjCREATURE:LORIKEET_MAN:FATjCREATURE:LORIKEET_MAN:TALLOWjCREATURE:GIANT_LORIKEET:FATjCREATURE:GIANT_LORIKEET:TALLOWjCREATURE:BIRD_WREN:FATjCREATURE:BIRD_WREN:TALLOWjCREATURE:WREN_MAN:FATjCREATURE:WREN_MAN:TALLOWjCREATURE:GIANT_WREN:FATjCREATURE:GIANT_WREN:TALLOWjCREATURE:BIRD_OSPREY:FATjCREATURE:BIRD_OSPREY:TALLOWjCREATURE:OSPREY_MAN:FATjCREATURE:OSPREY_MAN:TALLOWjCREATURE:GIANT_OSPREY:FATjCREATURE:GIANT_OSPREY:TALLOWjCREATURE:BIRD_EMU:FATjCREATURE:BIRD_EMU:TALLOWjCREATURE:EMU_MAN:FATjCREATURE:EMU_MAN:TALLOWjCREATURE:GIANT_EMU:FATjCREATURE:GIANT_EMU:TALLOWjCREATURE:BIRD_COCKATIEL:FATjCREATURE:BIRD_COCKATIEL:TALLOWjCREATURE:COCKATIEL_MAN:FATjCREATURE:COCKATIEL_MAN:TALLOWjCREATURE:GIANT_COCKATIEL:FATjCREATURE:GIANT_COCKATIEL:TALLOWj&CREATURE:BIRD_LOVEBIRD_PEACH-FACED:FATj)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:TALLOWj%CREATURE:PEACH-FACED_LOVEBIRD_MAN:FATj(CREATURE:PEACH-FACED_LOVEBIRD_MAN:TALLOWj'CREATURE:GIANT_PEACH-FACED_LOVEBIRD:FATj*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:TALLOWjCREATURE:BIRD_MAGPIE:FATjCREATURE:BIRD_MAGPIE:TALLOWjCREATURE:MAGPIE_MAN:FATjCREATURE:MAGPIE_MAN:TALLOWjCREATURE:GIANT_MAGPIE:FATjCREATURE:GIANT_MAGPIE:TALLOWjCREATURE:BIRD_KESTREL:FATjCREATURE:BIRD_KESTREL:TALLOWjCREATURE:KESTREL_MAN:FATjCREATURE:KESTREL_MAN:TALLOWjCREATURE:GIANT_KESTREL:FATjCREATURE:GIANT_KESTREL:TALLOWjCREATURE:BIRD_ALBATROSS:FATjCREATURE:BIRD_ALBATROSS:TALLOWjCREATURE:ALBATROSS_MAN:FATjCREATURE:ALBATROSS_MAN:TALLOWjCREATURE:GIANT_ALBATROSS:FATjCREATURE:GIANT_ALBATROSS:TALLOWj"CREATURE:BIRD_OWL_GREAT_HORNED:FATj%CREATURE:BIRD_OWL_GREAT_HORNED:TALLOWj!CREATURE:GREAT_HORNED_OWL_MAN:FATj$CREATURE:GREAT_HORNED_OWL_MAN:TALLOWj#CREATURE:GIANT_GREAT_HORNED_OWL:FATj&CREATURE:GIANT_GREAT_HORNED_OWL:TALLOWjCREATURE:BIRD_EAGLE:FATjCREATURE:BIRD_EAGLE:TALLOWjCREATURE:EAGLE_MAN:FATjCREATURE:EAGLE_MAN:TALLOWjCREATURE:GIANT_EAGLE:FATjCREATURE:GIANT_EAGLE:TALLOWjCREATURE:BIRD_HORNBILL:FATjCREATURE:BIRD_HORNBILL:TALLOWjCREATURE:HORNBILL_MAN:FATjCREATURE:HORNBILL_MAN:TALLOWjCREATURE:GIANT_HORNBILL:FATjCREATURE:GIANT_HORNBILL:TALLOWj!CREATURE:BIRD_LOVEBIRD_MASKED:FATj$CREATURE:BIRD_LOVEBIRD_MASKED:TALLOWj CREATURE:MASKED_LOVEBIRD_MAN:FATj#CREATURE:MASKED_LOVEBIRD_MAN:TALLOWj"CREATURE:GIANT_MASKED_LOVEBIRD:FATj%CREATURE:GIANT_MASKED_LOVEBIRD:TALLOWjCREATURE:BIRD_BUSHTIT:FATjCREATURE:BIRD_BUSHTIT:TALLOWjCREATURE:BUSHTIT_MAN:FATjCREATURE:BUSHTIT_MAN:TALLOWjCREATURE:GIANT_BUSHTIT:FATjCREATURE:GIANT_BUSHTIT:TALLOWjCREATURE:DAMSELFLY:FATjCREATURE:DAMSELFLY:TALLOWjCREATURE:DAMSELFLY_MAN:FATjCREATURE:DAMSELFLY_MAN:TALLOWjCREATURE:GIANT_DAMSELFLY:FATjCREATURE:GIANT_DAMSELFLY:TALLOWjCREATURE:MOTH:FATjCREATURE:MOTH:TALLOWjCREATURE:MOTH_MAN:FATjCREATURE:MOTH_MAN:TALLOWjCREATURE:GIANT_MOTH:FATjCREATURE:GIANT_MOTH:TALLOWjCREATURE:GRASSHOPPER:FATjCREATURE:GRASSHOPPER:TALLOWjCREATURE:GRASSHOPPER_MAN:FATjCREATURE:GRASSHOPPER_MAN:TALLOWjCREATURE:GIANT_GRASSHOPPER:FATj!CREATURE:GIANT_GRASSHOPPER:TALLOWjCREATURE:BARK_SCORPION:FATjCREATURE:BARK_SCORPION:TALLOWjCREATURE:BARK_SCORPION_MAN:FATj!CREATURE:BARK_SCORPION_MAN:TALLOWj CREATURE:GIANT_BARK_SCORPION:FATj#CREATURE:GIANT_BARK_SCORPION:TALLOWjCREATURE:MANTIS:FATjCREATURE:MANTIS:TALLOWjCREATURE:MANTIS_MAN:FATjCREATURE:MANTIS_MAN:TALLOWjCREATURE:GIANT_MANTIS:FATjCREATURE:GIANT_MANTIS:TALLOWjCREATURE:TICK:FATjCREATURE:TICK:TALLOWjCREATURE:TICK_MAN:FATjCREATURE:TICK_MAN:TALLOWjCREATURE:GIANT_TICK:FATjCREATURE:GIANT_TICK:TALLOWjCREATURE:LOUSE:FATjCREATURE:LOUSE:TALLOWjCREATURE:LOUSE_MAN:FATjCREATURE:LOUSE_MAN:TALLOWjCREATURE:GIANT_LOUSE:FATjCREATURE:GIANT_LOUSE:TALLOWjCREATURE:THRIPS:FATjCREATURE:THRIPS:TALLOWjCREATURE:THRIPS_MAN:FATjCREATURE:THRIPS_MAN:TALLOWjCREATURE:GIANT_THRIPS:FATjCREATURE:GIANT_THRIPS:TALLOWjCREATURE:SLUG:FATjCREATURE:SLUG:TALLOWjCREATURE:SLUG_MAN:FATjCREATURE:SLUG_MAN:TALLOWjCREATURE:GIANT_SLUG:FATjCREATURE:GIANT_SLUG:TALLOWjCREATURE:MOSQUITO:FATjCREATURE:MOSQUITO:TALLOWjCREATURE:MOSQUITO_MAN:FATjCREATURE:MOSQUITO_MAN:TALLOWjCREATURE:GIANT_MOSQUITO:FATjCREATURE:GIANT_MOSQUITO:TALLOWjCREATURE:SPIDER_JUMPING:FATjCREATURE:SPIDER_JUMPING:TALLOWjCREATURE:JUMPING_SPIDER_MAN:FATj"CREATURE:JUMPING_SPIDER_MAN:TALLOWj!CREATURE:GIANT_JUMPING_SPIDER:FATj$CREATURE:GIANT_JUMPING_SPIDER:TALLOWjCREATURE:TERMITE:FATjCREATURE:TERMITE:TALLOWjCREATURE:MOON_SNAIL:FATjCREATURE:MOON_SNAIL:TALLOWjCREATURE:MOON_SNAIL_MAN:FATjCREATURE:MOON_SNAIL_MAN:TALLOWjCREATURE:GIANT_MOON_SNAIL:FATj CREATURE:GIANT_MOON_SNAIL:TALLOWj!CREATURE:SPIDER_BROWN_RECLUSE:FATj$CREATURE:SPIDER_BROWN_RECLUSE:TALLOWj%CREATURE:BROWN_RECLUSE_SPIDER_MAN:FATj(CREATURE:BROWN_RECLUSE_SPIDER_MAN:TALLOWj'CREATURE:GIANT_BROWN_RECLUSE_SPIDER:FATj*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:TALLOWjCREATURE:SNAIL:FATjCREATURE:SNAIL:TALLOWjCREATURE:SNAIL_MAN:FATjCREATURE:SNAIL_MAN:TALLOWjCREATURE:GIANT_SNAIL:FATjCREATURE:GIANT_SNAIL:TALLOWjCREATURE:GECKO_LEOPARD:FATjCREATURE:GECKO_LEOPARD:TALLOWjCREATURE:LEOPARD_GECKO_MAN:FATj!CREATURE:LEOPARD_GECKO_MAN:TALLOWj CREATURE:GIANT_LEOPARD_GECKO:FATj#CREATURE:GIANT_LEOPARD_GECKO:TALLOWjCREATURE:DESERT TORTOISE:FATjCREATURE:DESERT TORTOISE:TALLOWj CREATURE:DESERT_TORTOISE_MAN:FATj#CREATURE:DESERT_TORTOISE_MAN:TALLOWj"CREATURE:GIANT_DESERT_TORTOISE:FATj%CREATURE:GIANT_DESERT_TORTOISE:TALLOWjCREATURE:GILA_MONSTER:FATjCREATURE:GILA_MONSTER:TALLOWjCREATURE:GILA_MONSTER_MAN:FATj CREATURE:GILA_MONSTER_MAN:TALLOWjCREATURE:GIANT_GILA_MONSTER:FATj"CREATURE:GIANT_GILA_MONSTER:TALLOWjCREATURE:DOG:FATjCREATURE:DOG:TALLOWjCREATURE:CAT:FATjCREATURE:CAT:TALLOWjCREATURE:MULE:FATjCREATURE:MULE:TALLOWjCREATURE:DONKEY:FATjCREATURE:DONKEY:TALLOWjCREATURE:HORSE:FATjCREATURE:HORSE:TALLOWjCREATURE:COW:FATjCREATURE:COW:TALLOWjCREATURE:SHEEP:FATjCREATURE:SHEEP:TALLOWjCREATURE:PIG:FATjCREATURE:PIG:TALLOWjCREATURE:GOAT:FATjCREATURE:GOAT:TALLOWjCREATURE:BIRD_CHICKEN:FATjCREATURE:BIRD_CHICKEN:TALLOWjCREATURE:CAVY:FATjCREATURE:CAVY:TALLOWjCREATURE:BIRD_DUCK:FATjCREATURE:BIRD_DUCK:TALLOWjCREATURE:WATER_BUFFALO:FATjCREATURE:WATER_BUFFALO:TALLOWjCREATURE:REINDEER:FATjCREATURE:REINDEER:TALLOWjCREATURE:BIRD_GOOSE:FATjCREATURE:BIRD_GOOSE:TALLOWjCREATURE:YAK:FATjCREATURE:YAK:TALLOWjCREATURE:LLAMA:FATjCREATURE:LLAMA:TALLOWjCREATURE:ALPACA:FATjCREATURE:ALPACA:TALLOWjCREATURE:BIRD_GUINEAFOWL:FATjCREATURE:BIRD_GUINEAFOWL:TALLOWjCREATURE:BIRD_PEAFOWL_BLUE:FATj!CREATURE:BIRD_PEAFOWL_BLUE:TALLOWjCREATURE:BIRD_TURKEY:FATjCREATURE:BIRD_TURKEY:TALLOWjCREATURE:RABBIT:FATjCREATURE:RABBIT:TALLOWjCREATURE:FLY:FATjCREATURE:FLY:TALLOWjCREATURE:FLY_MAN:FATjCREATURE:FLY_MAN:TALLOWjCREATURE:GIANT_FLY:FATjCREATURE:GIANT_FLY:TALLOWjCREATURE:ROACH_LARGE:FATjCREATURE:ROACH_LARGE:TALLOWjCREATURE:ROACH_MAN:FATjCREATURE:ROACH_MAN:TALLOWjCREATURE:GIANT_ROACH:FATjCREATURE:GIANT_ROACH:TALLOWjCREATURE:BEETLE:FATjCREATURE:BEETLE:TALLOWjCREATURE:BEETLE_MAN:FATjCREATURE:BEETLE_MAN:TALLOWjCREATURE:GIANT_BEETLE:FATjCREATURE:GIANT_BEETLE:TALLOWjCREATURE:ANT:FATjCREATURE:ANT:TALLOWjCREATURE:BUTTERFLY_MONARCH:FATj!CREATURE:BUTTERFLY_MONARCH:TALLOWj"CREATURE:BUTTERFLY_MONARCH_MAN:FATj%CREATURE:BUTTERFLY_MONARCH_MAN:TALLOWj$CREATURE:GIANT_BUTTERFLY_MONARCH:FATj'CREATURE:GIANT_BUTTERFLY_MONARCH:TALLOWjCREATURE:FIREFLY:FATjCREATURE:FIREFLY:TALLOWjCREATURE:FIREFLY_MAN:FATjCREATURE:FIREFLY_MAN:TALLOWjCREATURE:GIANT_FIREFLY:FATjCREATURE:GIANT_FIREFLY:TALLOWjCREATURE:DRAGONFLY:FATjCREATURE:DRAGONFLY:TALLOWjCREATURE:DRAGONFLY_MAN:FATjCREATURE:DRAGONFLY_MAN:TALLOWjCREATURE:GIANT_DRAGONFLY:FATjCREATURE:GIANT_DRAGONFLY:TALLOWjCREATURE:HONEY_BEE:WAXjCREATURE:HONEY_BEE:FATjCREATURE:HONEY_BEE:TALLOWjCREATURE:BUMBLEBEE:WAXjCREATURE:BUMBLEBEE:FATjCREATURE:BUMBLEBEE:TALLOWjCREATURE:GOAT_MOUNTAIN:FATjCREATURE:GOAT_MOUNTAIN:TALLOWjCREATURE:GOAT_MOUNTAIN_MAN:FATj!CREATURE:GOAT_MOUNTAIN_MAN:TALLOWj CREATURE:GIANT_GOAT_MOUNTAIN:FATj#CREATURE:GIANT_GOAT_MOUNTAIN:TALLOWjCREATURE:MARMOT_HOARY:FATjCREATURE:MARMOT_HOARY:TALLOWjCREATURE:MARMOT_HOARY_MAN:FATj CREATURE:MARMOT_HOARY_MAN:TALLOWjCREATURE:GIANT_MARMOT_HOARY:FATj"CREATURE:GIANT_MARMOT_HOARY:TALLOWjCREATURE:GNOME_MOUNTAIN:FATjCREATURE:GNOME_MOUNTAIN:TALLOWjCREATURE:GNOME_DARK:FATjCREATURE:GNOME_DARK:TALLOWjCREATURE:WALRUS:FATjCREATURE:WALRUS:TALLOWjCREATURE:WALRUS_MAN:FATjCREATURE:WALRUS_MAN:TALLOWjCREATURE:GIANT_WALRUS:FATjCREATURE:GIANT_WALRUS:TALLOWjCREATURE:FISH_LAMPREY_SEA:FATj CREATURE:FISH_LAMPREY_SEA:TALLOWjCREATURE:SHARK_GREAT_WHITE:FATj!CREATURE:SHARK_GREAT_WHITE:TALLOWjCREATURE:SHARK_FRILL:FATjCREATURE:SHARK_FRILL:TALLOWj CREATURE:SHARK_SPINY_DOGFISH:FATj#CREATURE:SHARK_SPINY_DOGFISH:TALLOWj$CREATURE:SHARK_WOBBEGONG_SPOTTED:FATj'CREATURE:SHARK_WOBBEGONG_SPOTTED:TALLOWjCREATURE:SHARK_WHALE:FATjCREATURE:SHARK_WHALE:TALLOWjCREATURE:SHARK_BASKING:FATjCREATURE:SHARK_BASKING:TALLOWjCREATURE:SHARK_NURSE:FATjCREATURE:SHARK_NURSE:TALLOWj CREATURE:SHARK_MAKO_SHORTFIN:FATj#CREATURE:SHARK_MAKO_SHORTFIN:TALLOWjCREATURE:SHARK_MAKO_LONGFIN:FATj"CREATURE:SHARK_MAKO_LONGFIN:TALLOWjCREATURE:SHARK_TIGER:FATjCREATURE:SHARK_TIGER:TALLOWjCREATURE:SHARK_BULL:FATjCREATURE:SHARK_BULL:TALLOWj CREATURE:SHARK_REEF_BLACKTIP:FATj#CREATURE:SHARK_REEF_BLACKTIP:TALLOWj CREATURE:SHARK_REEF_WHITETIP:FATj#CREATURE:SHARK_REEF_WHITETIP:TALLOWjCREATURE:SHARK_BLUE:FATjCREATURE:SHARK_BLUE:TALLOWjCREATURE:SHARK_HAMMERHEAD:FATj CREATURE:SHARK_HAMMERHEAD:TALLOWjCREATURE:SHARK_ANGEL:FATjCREATURE:SHARK_ANGEL:TALLOWjCREATURE:FISH_SKATE_COMMON:FATj!CREATURE:FISH_SKATE_COMMON:TALLOWjCREATURE:FISH_RAY_MANTA:FATjCREATURE:FISH_RAY_MANTA:TALLOWjCREATURE:FISH_STINGRAY:FATjCREATURE:FISH_STINGRAY:TALLOWjCREATURE:FISH_COELACANTH:FATjCREATURE:FISH_COELACANTH:TALLOWjCREATURE:FISH_STURGEON:FATjCREATURE:FISH_STURGEON:TALLOWjCREATURE:FISH_CONGER_EEL:FATjCREATURE:FISH_CONGER_EEL:TALLOWjCREATURE:FISH_MILKFISH:FATjCREATURE:FISH_MILKFISH:TALLOWjCREATURE:FISH_COD:FATjCREATURE:FISH_COD:TALLOWjCREATURE:FISH_OPAH:FATjCREATURE:FISH_OPAH:TALLOWjCREATURE:FISH_GROUPER_GIANT:FATj"CREATURE:FISH_GROUPER_GIANT:TALLOWjCREATURE:FISH_BLUEFISH:FATjCREATURE:FISH_BLUEFISH:TALLOWjCREATURE:FISH_SUNFISH_OCEAN:FATj"CREATURE:FISH_SUNFISH_OCEAN:TALLOWjCREATURE:FISH_SWORDFISH:FATjCREATURE:FISH_SWORDFISH:TALLOWjCREATURE:FISH_MARLIN:FATjCREATURE:FISH_MARLIN:TALLOWjCREATURE:FISH_HALIBUT:FATjCREATURE:FISH_HALIBUT:TALLOWj!CREATURE:FISH_BARRACUDA_GREAT:FATj$CREATURE:FISH_BARRACUDA_GREAT:TALLOWjCREATURE:FISH_TUNA_BLUEFIN:FATj!CREATURE:FISH_TUNA_BLUEFIN:TALLOWjCREATURE:NARWHAL:FATjCREATURE:NARWHAL:TALLOWjCREATURE:NARWHAL MAN:FATjCREATURE:NARWHAL MAN:TALLOWjCREATURE:NARWHAL, GIANT:FATjCREATURE:NARWHAL, GIANT:TALLOWjCREATURE:HIPPO:FATjCREATURE:HIPPO:TALLOWjCREATURE:HIPPO_MAN:FATjCREATURE:HIPPO_MAN:TALLOWjCREATURE:GIANT_HIPPO:FATjCREATURE:GIANT_HIPPO:TALLOWjCREATURE:FISH_GAR_LONGNOSE:FATj!CREATURE:FISH_GAR_LONGNOSE:TALLOWjCREATURE:FISH_CARP:FATjCREATURE:FISH_CARP:TALLOWjCREATURE:FISH_TIGERFISH:FATjCREATURE:FISH_TIGERFISH:TALLOWjCREATURE:FISH_PIKE:FATjCREATURE:FISH_PIKE:TALLOWjCREATURE:PLATYPUS:FATjCREATURE:PLATYPUS:TALLOWjCREATURE:PLATYPUS MAN:FATjCREATURE:PLATYPUS MAN:TALLOWjCREATURE:PLATYPUS, GIANT:FATjCREATURE:PLATYPUS, GIANT:TALLOWjCREATURE:BEAR_GRIZZLY:FATjCREATURE:BEAR_GRIZZLY:TALLOWjCREATURE:BEAR_GRIZZLY_MAN:FATj CREATURE:BEAR_GRIZZLY_MAN:TALLOWjCREATURE:GIANT_BEAR_GRIZZLY:FATj"CREATURE:GIANT_BEAR_GRIZZLY:TALLOWjCREATURE:BEAR_BLACK:FATjCREATURE:BEAR_BLACK:TALLOWjCREATURE:BEAR_BLACK_MAN:FATjCREATURE:BEAR_BLACK_MAN:TALLOWjCREATURE:GIANT_BEAR_BLACK:FATj CREATURE:GIANT_BEAR_BLACK:TALLOWjCREATURE:DEER:FATjCREATURE:DEER:TALLOWjCREATURE:DEER_MAN:FATjCREATURE:DEER_MAN:TALLOWjCREATURE:GIANT_DEER:FATjCREATURE:GIANT_DEER:TALLOWjCREATURE:FOX:FATjCREATURE:FOX:TALLOWjCREATURE:FOX_MAN:FATjCREATURE:FOX_MAN:TALLOWjCREATURE:GIANT_FOX:FATjCREATURE:GIANT_FOX:TALLOWjCREATURE:RACCOON:FATjCREATURE:RACCOON:TALLOWjCREATURE:RACCOON_MAN:FATjCREATURE:RACCOON_MAN:TALLOWjCREATURE:GIANT_RACCOON:FATjCREATURE:GIANT_RACCOON:TALLOWjCREATURE:MACAQUE_RHESUS:FATjCREATURE:MACAQUE_RHESUS:TALLOWjCREATURE:MACAQUE_RHESUS_MAN:FATj"CREATURE:MACAQUE_RHESUS_MAN:TALLOWj!CREATURE:GIANT_MACAQUE_RHESUS:FATj$CREATURE:GIANT_MACAQUE_RHESUS:TALLOWjCREATURE:COUGAR:FATjCREATURE:COUGAR:TALLOWjCREATURE:COUGAR_MAN:FATjCREATURE:COUGAR_MAN:TALLOWjCREATURE:GIANT_COUGAR:FATjCREATURE:GIANT_COUGAR:TALLOWjCREATURE:WOLF:FATjCREATURE:WOLF:TALLOWjCREATURE:WOLF_MAN:FATjCREATURE:WOLF_MAN:TALLOWjCREATURE:GIANT_WOLF:FATjCREATURE:GIANT_WOLF:TALLOWjCREATURE:GROUNDHOG:FATjCREATURE:GROUNDHOG:TALLOWjCREATURE:GROUNDHOG_MAN:FATjCREATURE:GROUNDHOG_MAN:TALLOWjCREATURE:GIANT_GROUNDHOG:FATjCREATURE:GIANT_GROUNDHOG:TALLOWjCREATURE:ALLIGATOR:FATjCREATURE:ALLIGATOR:TALLOWjCREATURE:ALLIGATOR_MAN:FATjCREATURE:ALLIGATOR_MAN:TALLOWjCREATURE:GIANT_ALLIGATOR:FATjCREATURE:GIANT_ALLIGATOR:TALLOWjCREATURE:BIRD_BUZZARD:FATjCREATURE:BIRD_BUZZARD:TALLOWjCREATURE:BUZZARD_MAN:FATjCREATURE:BUZZARD_MAN:TALLOWjCREATURE:GIANT_BUZZARD:FATjCREATURE:GIANT_BUZZARD:TALLOWjCREATURE:PANDA:FATjCREATURE:PANDA:TALLOWjCREATURE:PANDA, GIGANTIC:FATjCREATURE:PANDA, GIGANTIC:TALLOWjCREATURE:PANDA MAN:FATjCREATURE:PANDA MAN:TALLOWjCREATURE:CAPYBARA:FATjCREATURE:CAPYBARA:TALLOWjCREATURE:CAPYBARA, GIANT:FATjCREATURE:CAPYBARA, GIANT:TALLOWjCREATURE:CAPYBARA MAN:FATjCREATURE:CAPYBARA MAN:TALLOWjCREATURE:BADGER:FATjCREATURE:BADGER:TALLOWjCREATURE:BADGER MAN:FATjCREATURE:BADGER MAN:TALLOWjCREATURE:BADGER, GIANT:FATjCREATURE:BADGER, GIANT:TALLOWjCREATURE:MOOSE:FATjCREATURE:MOOSE:TALLOWjCREATURE:MOOSE MAN:FATjCREATURE:MOOSE MAN:TALLOWjCREATURE:MOOSE, GIANT:FATjCREATURE:MOOSE, GIANT:TALLOWjCREATURE:RED PANDA:FATjCREATURE:RED PANDA:TALLOWjCREATURE:RED PANDA MAN:FATjCREATURE:RED PANDA MAN:TALLOWjCREATURE:RED PANDA, GIANT:FATj CREATURE:RED PANDA, GIANT:TALLOWjCREATURE:ELEPHANT:FATjCREATURE:ELEPHANT:TALLOWjCREATURE:ELEPHANT_MAN:FATjCREATURE:ELEPHANT_MAN:TALLOWjCREATURE:GIANT_ELEPHANT:FATjCREATURE:GIANT_ELEPHANT:TALLOWjCREATURE:WARTHOG:FATjCREATURE:WARTHOG:TALLOWjCREATURE:WARTHOG_MAN:FATjCREATURE:WARTHOG_MAN:TALLOWjCREATURE:GIANT_WARTHOG:FATjCREATURE:GIANT_WARTHOG:TALLOWjCREATURE:LION:FATjCREATURE:LION:TALLOWjCREATURE:LION_MAN:FATjCREATURE:LION_MAN:TALLOWjCREATURE:GIANT_LION:FATjCREATURE:GIANT_LION:TALLOWjCREATURE:LEOPARD:FATjCREATURE:LEOPARD:TALLOWjCREATURE:LEOPARD_MAN:FATjCREATURE:LEOPARD_MAN:TALLOWjCREATURE:GIANT_LEOPARD:FATjCREATURE:GIANT_LEOPARD:TALLOWjCREATURE:JAGUAR:FATjCREATURE:JAGUAR:TALLOWjCREATURE:JAGUAR_MAN:FATjCREATURE:JAGUAR_MAN:TALLOWjCREATURE:GIANT_JAGUAR:FATjCREATURE:GIANT_JAGUAR:TALLOWjCREATURE:TIGER:FATjCREATURE:TIGER:TALLOWjCREATURE:TIGER_MAN:FATjCREATURE:TIGER_MAN:TALLOWjCREATURE:GIANT_TIGER:FATjCREATURE:GIANT_TIGER:TALLOWjCREATURE:CHEETAH:FATjCREATURE:CHEETAH:TALLOWjCREATURE:CHEETAH_MAN:FATjCREATURE:CHEETAH_MAN:TALLOWjCREATURE:GIANT_CHEETAH:FATjCREATURE:GIANT_CHEETAH:TALLOWjCREATURE:GAZELLE:FATjCREATURE:GAZELLE:TALLOWjCREATURE:GAZELLE_MAN:FATjCREATURE:GAZELLE_MAN:TALLOWjCREATURE:GIANT_GAZELLE:FATjCREATURE:GIANT_GAZELLE:TALLOWjCREATURE:MANDRILL:FATjCREATURE:MANDRILL:TALLOWjCREATURE:MANDRILL_MAN:FATjCREATURE:MANDRILL_MAN:TALLOWjCREATURE:GIANT_MANDRILL:FATjCREATURE:GIANT_MANDRILL:TALLOWjCREATURE:CHIMPANZEE:FATjCREATURE:CHIMPANZEE:TALLOWjCREATURE:BONOBO:FATjCREATURE:BONOBO:TALLOWjCREATURE:GORILLA:FATjCREATURE:GORILLA:TALLOWjCREATURE:ORANGUTAN:FATjCREATURE:ORANGUTAN:TALLOWjCREATURE:GIBBON_SIAMANG:FATjCREATURE:GIBBON_SIAMANG:TALLOWj CREATURE:GIBBON_WHITE_HANDED:FATj#CREATURE:GIBBON_WHITE_HANDED:TALLOWj CREATURE:GIBBON_BLACK_HANDED:FATj#CREATURE:GIBBON_BLACK_HANDED:TALLOWjCREATURE:GIBBON_GRAY:FATjCREATURE:GIBBON_GRAY:TALLOWjCREATURE:GIBBON_SILVERY:FATjCREATURE:GIBBON_SILVERY:TALLOWjCREATURE:GIBBON_PILEATED:FATjCREATURE:GIBBON_PILEATED:TALLOWjCREATURE:GIBBON_BILOU:FATjCREATURE:GIBBON_BILOU:TALLOWj CREATURE:GIBBON_WHITE_BROWED:FATj#CREATURE:GIBBON_WHITE_BROWED:TALLOWj!CREATURE:GIBBON_BLACK_CRESTED:FATj$CREATURE:GIBBON_BLACK_CRESTED:TALLOWjCREATURE:CAMEL_1_HUMP:FATjCREATURE:CAMEL_1_HUMP:TALLOWjCREATURE:CAMEL_1_HUMP_MAN:FATj CREATURE:CAMEL_1_HUMP_MAN:TALLOWjCREATURE:GIANT_CAMEL_1_HUMP:FATj"CREATURE:GIANT_CAMEL_1_HUMP:TALLOWjCREATURE:CAMEL_2_HUMP:FATjCREATURE:CAMEL_2_HUMP:TALLOWjCREATURE:CAMEL_2_HUMP_MAN:FATj CREATURE:CAMEL_2_HUMP_MAN:TALLOWjCREATURE:GIANT_CAMEL_2_HUMP:FATj"CREATURE:GIANT_CAMEL_2_HUMP:TALLOWj CREATURE:CROCODILE_SALTWATER:FATj#CREATURE:CROCODILE_SALTWATER:TALLOWj$CREATURE:CROCODILE_SALTWATER_MAN:FATj'CREATURE:CROCODILE_SALTWATER_MAN:TALLOWj&CREATURE:GIANT_CROCODILE_SALTWATER:FATj)CREATURE:GIANT_CROCODILE_SALTWATER:TALLOWjCREATURE:BIRD_VULTURE:FATjCREATURE:BIRD_VULTURE:TALLOWjCREATURE:VULTURE_MAN:FATjCREATURE:VULTURE_MAN:TALLOWjCREATURE:GIANT_VULTURE:FATjCREATURE:GIANT_VULTURE:TALLOWjCREATURE:RHINOCEROS:FATjCREATURE:RHINOCEROS:TALLOWjCREATURE:RHINOCEROS_MAN:FATjCREATURE:RHINOCEROS_MAN:TALLOWjCREATURE:GIANT_RHINOCEROS:FATj CREATURE:GIANT_RHINOCEROS:TALLOWjCREATURE:GIRAFFE:FATjCREATURE:GIRAFFE:TALLOWjCREATURE:GIRAFFE_MAN:FATjCREATURE:GIRAFFE_MAN:TALLOWjCREATURE:GIANT_GIRAFFE:FATjCREATURE:GIANT_GIRAFFE:TALLOWjCREATURE:HONEY BADGER:FATjCREATURE:HONEY BADGER:TALLOWjCREATURE:HONEY BADGER MAN:FATj CREATURE:HONEY BADGER MAN:TALLOWj CREATURE:HONEY BADGER, GIANT:FATj#CREATURE:HONEY BADGER, GIANT:TALLOWjCREATURE:GIANT TORTOISE:FATjCREATURE:GIANT TORTOISE:TALLOWjCREATURE:GIANT TORTOISE MAN:FATj"CREATURE:GIANT TORTOISE MAN:TALLOWjCREATURE:GIGANTIC TORTOISE:FATj!CREATURE:GIGANTIC TORTOISE:TALLOWjCREATURE:ARMADILLO:FATjCREATURE:ARMADILLO:TALLOWjCREATURE:ARMADILLO MAN:FATjCREATURE:ARMADILLO MAN:TALLOWjCREATURE:ARMADILLO, GIANT:FATj CREATURE:ARMADILLO, GIANT:TALLOWjCREATURE:MUSKOX:FATjCREATURE:MUSKOX:TALLOWjCREATURE:MUSKOX_MAN:FATjCREATURE:MUSKOX_MAN:TALLOWjCREATURE:GIANT_MUSKOX:FATjCREATURE:GIANT_MUSKOX:TALLOWjCREATURE:ELK:FATjCREATURE:ELK:TALLOWjCREATURE:ELK_MAN:FATjCREATURE:ELK_MAN:TALLOWjCREATURE:GIANT_ELK:FATjCREATURE:GIANT_ELK:TALLOWjCREATURE:BEAR_POLAR:FATjCREATURE:BEAR_POLAR:TALLOWjCREATURE:BEAR_POLAR_MAN:FATjCREATURE:BEAR_POLAR_MAN:TALLOWjCREATURE:GIANT_BEAR_POLAR:FATj CREATURE:GIANT_BEAR_POLAR:TALLOWjCREATURE:WOLVERINE:FATjCREATURE:WOLVERINE:TALLOWjCREATURE:WOLVERINE_MAN:FATjCREATURE:WOLVERINE_MAN:TALLOWjCREATURE:GIANT_WOLVERINE:FATjCREATURE:GIANT_WOLVERINE:TALLOWjCREATURE:CHINCHILLA:FATjCREATURE:CHINCHILLA:TALLOWjCREATURE:CHINCHILLA_MAN:FATjCREATURE:CHINCHILLA_MAN:TALLOWjCREATURE:GIANT_CHINCHILLA:FATj CREATURE:GIANT_CHINCHILLA:TALLOWjCREATURE:FLOATING_GUTS:FATjCREATURE:FLOATING_GUTS:TALLOWjCREATURE:DRUNIAN:FATjCREATURE:DRUNIAN:TALLOWjCREATURE:CREEPING_EYE:FATjCREATURE:CREEPING_EYE:TALLOWj#CREATURE:VORACIOUS_CAVE_CRAWLER:FATj&CREATURE:VORACIOUS_CAVE_CRAWLER:TALLOWjCREATURE:BLIND_CAVE_OGRE:FATjCREATURE:BLIND_CAVE_OGRE:TALLOWjCREATURE:CAP_HOPPER:FATjCREATURE:CAP_HOPPER:TALLOWjCREATURE:MAGMA_CRAB:FATjCREATURE:MAGMA_CRAB:TALLOWjCREATURE:CRUNDLE:FATjCREATURE:CRUNDLE:TALLOWjCREATURE:HUNGRY_HEAD:FATjCREATURE:HUNGRY_HEAD:TALLOWjCREATURE:ELK_BIRD:FATjCREATURE:ELK_BIRD:TALLOWjCREATURE:HELMET_SNAKE:FATjCREATURE:HELMET_SNAKE:TALLOWjCREATURE:GREEN_DEVOURER:FATjCREATURE:GREEN_DEVOURER:TALLOWjCREATURE:RUTHERER:FATjCREATURE:RUTHERER:TALLOWjCREATURE:CREEPY_CRAWLER:FATjCREATURE:CREEPY_CRAWLER:TALLOWjCREATURE:DRALTHA:FATjCREATURE:DRALTHA:TALLOWjCREATURE:GIANT_EARTHWORM:FATjCREATURE:GIANT_EARTHWORM:TALLOWjCREATURE:BUGBAT:FATjCREATURE:BUGBAT:TALLOWjCREATURE:MANERA:FATjCREATURE:MANERA:TALLOWjCREATURE:MOLEMARIAN:FATjCREATURE:MOLEMARIAN:TALLOWjCREATURE:JABBERER:FATjCREATURE:JABBERER:TALLOWjCREATURE:POND_GRABBER:FATjCREATURE:POND_GRABBER:TALLOWjCREATURE:BLIND_CAVE_BEAR:FATjCREATURE:BLIND_CAVE_BEAR:TALLOWjCREATURE:CAVE_DRAGON:FATjCREATURE:CAVE_DRAGON:TALLOWjCREATURE:REACHER:FATjCREATURE:REACHER:TALLOWjCREATURE:GORLAK:FATjCREATURE:GORLAK:TALLOWjCREATURE:OCTOPUS:FATjCREATURE:OCTOPUS:TALLOWjCREATURE:OCTOPUS_MAN:FATjCREATURE:OCTOPUS_MAN:TALLOWjCREATURE:GIANT_OCTOPUS:FATjCREATURE:GIANT_OCTOPUS:TALLOWjCREATURE:CRAB:FATjCREATURE:CRAB:TALLOWjCREATURE:CRAB_MAN:FATjCREATURE:CRAB_MAN:TALLOWjCREATURE:GIANT_CRAB:FATjCREATURE:GIANT_CRAB:TALLOWjCREATURE:LEOPARD_SEAL:FATjCREATURE:LEOPARD_SEAL:TALLOWjCREATURE:LEOPARD_SEAL_MAN:FATj CREATURE:LEOPARD_SEAL_MAN:TALLOWjCREATURE:GIANT_LEOPARD_SEAL:FATj"CREATURE:GIANT_LEOPARD_SEAL:TALLOWjCREATURE:CUTTLEFISH:FATjCREATURE:CUTTLEFISH:TALLOWjCREATURE:CUTTLEFISH_MAN:FATjCREATURE:CUTTLEFISH_MAN:TALLOWjCREATURE:GIANT_CUTTLEFISH:FATj CREATURE:GIANT_CUTTLEFISH:TALLOWjCREATURE:ORCA:FATjCREATURE:ORCA:TALLOWjCREATURE:ORCA_MAN:FATjCREATURE:ORCA_MAN:TALLOWjCREATURE:GIANT_ORCA:FATjCREATURE:GIANT_ORCA:TALLOWjCREATURE:HORSESHOE_CRAB:FATjCREATURE:HORSESHOE_CRAB:TALLOWjCREATURE:HORSESHOE_CRAB_MAN:FATj"CREATURE:HORSESHOE_CRAB_MAN:TALLOWj!CREATURE:GIANT_HORSESHOE_CRAB:FATj$CREATURE:GIANT_HORSESHOE_CRAB:TALLOWjCREATURE:SPERM_WHALE:FATjCREATURE:SPERM_WHALE:TALLOWjCREATURE:SPERM_WHALE_MAN:FATjCREATURE:SPERM_WHALE_MAN:TALLOWjCREATURE:GIANT_SPERM_WHALE:FATj!CREATURE:GIANT_SPERM_WHALE:TALLOWjCREATURE:ELEPHANT_SEAL:FATjCREATURE:ELEPHANT_SEAL:TALLOWjCREATURE:ELEPHANT_SEAL_MAN:FATj!CREATURE:ELEPHANT_SEAL_MAN:TALLOWj CREATURE:GIANT_ELEPHANT_SEAL:FATj#CREATURE:GIANT_ELEPHANT_SEAL:TALLOWjCREATURE:HARP_SEAL:FATjCREATURE:HARP_SEAL:TALLOWjCREATURE:HARP_SEAL_MAN:FATjCREATURE:HARP_SEAL_MAN:TALLOWjCREATURE:GIANT_HARP_SEAL:FATjCREATURE:GIANT_HARP_SEAL:TALLOWjCREATURE:NAUTILUS:FATjCREATURE:NAUTILUS:TALLOWjCREATURE:NAUTILUS_MAN:FATjCREATURE:NAUTILUS_MAN:TALLOWjCREATURE:GIANT_NAUTILUS:FATjCREATURE:GIANT_NAUTILUS:TALLOWjCREATURE:FOXSQUIRREL:FATjCREATURE:FOXSQUIRREL:TALLOWjCREATURE:MOGHOPPER:FATjCREATURE:MOGHOPPER:TALLOWjCREATURE:RAT_DEMON:FATjCREATURE:RAT_DEMON:TALLOWjCREATURE:WAMBLER_FLUFFY:TALLOWjCREATURE:WAMBLER_FLUFFY:FATj$CREATURE:LIZARD_RHINO_TWO_LEGGED:FATj'CREATURE:LIZARD_RHINO_TWO_LEGGED:TALLOWjCREATURE:WORM_KNUCKLE:FATjCREATURE:WORM_KNUCKLE:TALLOWjCREATURE:SPIDER_PHANTOM:FATjCREATURE:SPIDER_PHANTOM:TALLOWjCREATURE:FLY_ACORN:FATjCREATURE:FLY_ACORN:TALLOWjCREATURE:GNAT_BLOOD:FATjCREATURE:GNAT_BLOOD:TALLOWjCREATURE:LIZARD:FATjCREATURE:LIZARD:TALLOWjCREATURE:LIZARD_MAN:FATjCREATURE:LIZARD_MAN:TALLOWjCREATURE:GIANT_LIZARD:FATjCREATURE:GIANT_LIZARD:TALLOWjCREATURE:SKINK:FATjCREATURE:SKINK:TALLOWjCREATURE:SKINK_MAN:FATjCREATURE:SKINK_MAN:TALLOWjCREATURE:GIANT_SKINK:FATjCREATURE:GIANT_SKINK:TALLOWjCREATURE:CHAMELEON:FATjCREATURE:CHAMELEON:TALLOWjCREATURE:CHAMELEON_MAN:FATjCREATURE:CHAMELEON_MAN:TALLOWjCREATURE:GIANT_CHAMELEON:FATjCREATURE:GIANT_CHAMELEON:TALLOWjCREATURE:ANOLE:FATjCREATURE:ANOLE:TALLOWjCREATURE:ANOLE_MAN:FATjCREATURE:ANOLE_MAN:TALLOWjCREATURE:GIANT_ANOLE:FATjCREATURE:GIANT_ANOLE:TALLOWjCREATURE:IGUANA:FATjCREATURE:IGUANA:TALLOWjCREATURE:IGUANA_MAN:FATjCREATURE:IGUANA_MAN:TALLOWjCREATURE:GIANT_IGUANA:FATjCREATURE:GIANT_IGUANA:TALLOWjCREATURE:RIVER OTTER:FATjCREATURE:RIVER OTTER:TALLOWjCREATURE:SEA OTTER:FATjCREATURE:SEA OTTER:TALLOWjCREATURE:OTTER_MAN:FATjCREATURE:OTTER_MAN:TALLOWjCREATURE:GIANT_OTTER:FATjCREATURE:GIANT_OTTER:TALLOWjCREATURE:SNAPPING TURTLE:FATjCREATURE:SNAPPING TURTLE:TALLOWj&CREATURE:ALLIGATOR SNAPPING TURTLE:FATj)CREATURE:ALLIGATOR SNAPPING TURTLE:TALLOWj CREATURE:SNAPPING_TURTLE_MAN:FATj#CREATURE:SNAPPING_TURTLE_MAN:TALLOWj"CREATURE:GIANT_SNAPPING_TURTLE:FATj%CREATURE:GIANT_SNAPPING_TURTLE:TALLOWjCREATURE:BEAVER:FATjCREATURE:BEAVER:TALLOWjCREATURE:BEAVER_MAN:FATjCREATURE:BEAVER_MAN:TALLOWjCREATURE:GIANT_BEAVER:FATjCREATURE:GIANT_BEAVER:TALLOWjCREATURE:LEECH:FATjCREATURE:LEECH:TALLOWjCREATURE:LEECH_MAN:FATjCREATURE:LEECH_MAN:TALLOWjCREATURE:GIANT_LEECH:FATjCREATURE:GIANT_LEECH:TALLOWjCREATURE:AXOLOTL:FATjCREATURE:AXOLOTL:TALLOWjCREATURE:AXOLOTL_MAN:FATjCREATURE:AXOLOTL_MAN:TALLOWjCREATURE:GIANT_AXOLOTL:FATjCREATURE:GIANT_AXOLOTL:TALLOWjCREATURE:MINK:FATjCREATURE:MINK:TALLOWjCREATURE:MINK_MAN:FATjCREATURE:MINK_MAN:TALLOWjCREATURE:GIANT_MINK:FATjCREATURE:GIANT_MINK:TALLOWjCREATURE:POND_TURTLE:FATjCREATURE:POND_TURTLE:TALLOWjCREATURE:POND_TURTLE_MAN:FATjCREATURE:POND_TURTLE_MAN:TALLOWjCREATURE:GIANT_POND_TURTLE:FATj!CREATURE:GIANT_POND_TURTLE:TALLOWjCREATURE:RAT:FATjCREATURE:RAT:TALLOWjCREATURE:RAT_MAN:FATjCREATURE:RAT_MAN:TALLOWjCREATURE:SQUIRREL_GRAY:FATjCREATURE:SQUIRREL_GRAY:TALLOWjCREATURE:SQUIRREL_GRAY_MAN:FATj!CREATURE:SQUIRREL_GRAY_MAN:TALLOWj CREATURE:GIANT_SQUIRREL_GRAY:FATj#CREATURE:GIANT_SQUIRREL_GRAY:TALLOWjCREATURE:SQUIRREL_RED:FATjCREATURE:SQUIRREL_RED:TALLOWjCREATURE:SQUIRREL_RED_MAN:FATj CREATURE:SQUIRREL_RED_MAN:TALLOWjCREATURE:GIANT_SQUIRREL_RED:FATj"CREATURE:GIANT_SQUIRREL_RED:TALLOWjCREATURE:CHIPMUNK:FATjCREATURE:CHIPMUNK:TALLOWjCREATURE:CHIPMUNK_MAN:FATjCREATURE:CHIPMUNK_MAN:TALLOWjCREATURE:GIANT_CHIPMUNK:FATjCREATURE:GIANT_CHIPMUNK:TALLOWjCREATURE:HAMSTER:FATjCREATURE:HAMSTER:TALLOWjCREATURE:HAMSTER_MAN:FATjCREATURE:HAMSTER_MAN:TALLOWjCREATURE:GIANT_HAMSTER:FATjCREATURE:GIANT_HAMSTER:TALLOWjCREATURE:HEDGEHOG:FATjCREATURE:HEDGEHOG:TALLOWjCREATURE:HEDGEHOG_MAN:FATjCREATURE:HEDGEHOG_MAN:TALLOWjCREATURE:GIANT_HEDGEHOG:FATjCREATURE:GIANT_HEDGEHOG:TALLOWjCREATURE:SQUIRREL_FLYING:FATjCREATURE:SQUIRREL_FLYING:TALLOWj CREATURE:FLYING_SQUIRREL_MAN:FATj#CREATURE:FLYING_SQUIRREL_MAN:TALLOWj"CREATURE:GIANT_FLYING_SQUIRREL:FATj%CREATURE:GIANT_FLYING_SQUIRREL:TALLOWjCREATURE:MUSSEL:FATjCREATURE:MUSSEL:TALLOWjCREATURE:OYSTER:FATjCREATURE:OYSTER:TALLOWjCREATURE:FISH_SALMON:FATjCREATURE:FISH_SALMON:TALLOWjCREATURE:FISH_CLOWNFISH:FATjCREATURE:FISH_CLOWNFISH:TALLOWjCREATURE:FISH_HAGFISH:FATjCREATURE:FISH_HAGFISH:TALLOWjCREATURE:FISH_LAMPREY_BROOK:FATj"CREATURE:FISH_LAMPREY_BROOK:TALLOWjCREATURE:FISH_RAY_BAT:FATjCREATURE:FISH_RAY_BAT:TALLOWjCREATURE:FISH_RAY_THORNBACK:FATj"CREATURE:FISH_RAY_THORNBACK:TALLOWj!CREATURE:FISH_RATFISH_SPOTTED:FATj$CREATURE:FISH_RATFISH_SPOTTED:TALLOWjCREATURE:FISH_HERRING:FATjCREATURE:FISH_HERRING:TALLOWjCREATURE:FISH_SHAD:FATjCREATURE:FISH_SHAD:TALLOWjCREATURE:FISH_ANCHOVY:FATjCREATURE:FISH_ANCHOVY:TALLOWj!CREATURE:FISH_TROUT_STEELHEAD:FATj$CREATURE:FISH_TROUT_STEELHEAD:TALLOWjCREATURE:FISH_HAKE:FATjCREATURE:FISH_HAKE:TALLOWjCREATURE:FISH_SEAHORSE:FATjCREATURE:FISH_SEAHORSE:TALLOWjCREATURE:FISH_GLASSEYE:FATjCREATURE:FISH_GLASSEYE:TALLOWj&CREATURE:FISH_PUFFER_WHITE_SPOTTED:FATj)CREATURE:FISH_PUFFER_WHITE_SPOTTED:TALLOWjCREATURE:FISH_SOLE:FATjCREATURE:FISH_SOLE:TALLOWjCREATURE:FISH_FLOUNDER:FATjCREATURE:FISH_FLOUNDER:TALLOWjCREATURE:FISH_MACKEREL:FATjCREATURE:FISH_MACKEREL:TALLOWj!CREATURE:JELLYFISH_SEA_NETTLE:FATj$CREATURE:JELLYFISH_SEA_NETTLE:TALLOWjCREATURE:SQUID:FATjCREATURE:SQUID:TALLOWjCREATURE:SQUID MAN:FATjCREATURE:SQUID MAN:TALLOWjCREATURE:GIGANTIC SQUID:FATjCREATURE:GIGANTIC SQUID:TALLOWjCREATURE:FISH_LUNGFISH:FATjCREATURE:FISH_LUNGFISH:TALLOWjCREATURE:FISH_LOACH_CLOWN:FATj CREATURE:FISH_LOACH_CLOWN:TALLOWj CREATURE:FISH_BULLHEAD_BROWN:FATj#CREATURE:FISH_BULLHEAD_BROWN:TALLOWj!CREATURE:FISH_BULLHEAD_YELLOW:FATj$CREATURE:FISH_BULLHEAD_YELLOW:TALLOWj CREATURE:FISH_BULLHEAD_BLACK:FATj#CREATURE:FISH_BULLHEAD_BLACK:TALLOWj"CREATURE:FISH_KNIFEFISH_BANDED:FATj%CREATURE:FISH_KNIFEFISH_BANDED:TALLOWjCREATURE:FISH_CHAR:FATjCREATURE:FISH_CHAR:TALLOWjCREATURE:FISH_TROUT_RAINBOW:FATj"CREATURE:FISH_TROUT_RAINBOW:TALLOWjCREATURE:FISH_MOLLY_SAILFIN:FATj"CREATURE:FISH_MOLLY_SAILFIN:TALLOWjCREATURE:FISH_GUPPY:FATjCREATURE:FISH_GUPPY:TALLOWjCREATURE:FISH_PERCH:FATjCREATURE:FISH_PERCH:TALLOWjCREATURE:DWARF:FATjCREATURE:DWARF:TALLOWjCREATURE:HUMAN:FATjCREATURE:HUMAN:TALLOWjCREATURE:ELF:FATjCREATURE:ELF:TALLOWjCREATURE:GOBLIN:FATjCREATURE:GOBLIN:TALLOWjCREATURE:KOBOLD:FATjCREATURE:KOBOLD:TALLOWjCREATURE:GREMLIN:FATjCREATURE:GREMLIN:TALLOWjCREATURE:TROLL:FATjCREATURE:TROLL:TALLOWjCREATURE:OGRE:FATjCREATURE:OGRE:TALLOWjCREATURE:UNICORN:FATjCREATURE:UNICORN:TALLOWjCREATURE:DRAGON:FATjCREATURE:DRAGON:TALLOWjCREATURE:SATYR:FATjCREATURE:SATYR:TALLOWjCREATURE:GIANT:FATjCREATURE:GIANT:TALLOWjCREATURE:CYCLOPS:FATjCREATURE:CYCLOPS:TALLOWjCREATURE:ETTIN:FATjCREATURE:ETTIN:TALLOWjCREATURE:MINOTAUR:FATjCREATURE:MINOTAUR:TALLOWjCREATURE:YETI:FATjCREATURE:YETI:TALLOWjCREATURE:SASQUATCH:FATjCREATURE:SASQUATCH:TALLOWjCREATURE:BLIZZARD_MAN:FATjCREATURE:BLIZZARD_MAN:TALLOWjCREATURE:WOLF_ICE:FATjCREATURE:WOLF_ICE:TALLOWjCREATURE:FAIRY:FATjCREATURE:FAIRY:TALLOWjCREATURE:PIXIE:FATjCREATURE:PIXIE:TALLOWjCREATURE:BEAK_DOG:FATjCREATURE:BEAK_DOG:TALLOWjCREATURE:GRIMELING:FATjCREATURE:GRIMELING:TALLOWjCREATURE:BLENDEC_FOUL:FATjCREATURE:BLENDEC_FOUL:TALLOWjCREATURE:STRANGLER:FATjCREATURE:STRANGLER:TALLOWjCREATURE:NIGHTWING:FATjCREATURE:NIGHTWING:TALLOWjCREATURE:HARPY:FATjCREATURE:HARPY:TALLOWjCREATURE:HYDRA:FATjCREATURE:HYDRA:TALLOWjCREATURE:MERPERSON:FATjCREATURE:MERPERSON:TALLOWjCREATURE:SEA_SERPENT:FATjCREATURE:SEA_SERPENT:TALLOWjCREATURE:SEA_MONSTER:FATjCREATURE:SEA_MONSTER:TALLOWjCREATURE:BIRD_ROC:FATjCREATURE:BIRD_ROC:TALLOWjCREATURE:CROCODILE_CAVE:FATjCREATURE:CROCODILE_CAVE:TALLOWjCREATURE:TOAD_GIANT_CAVE:FATjCREATURE:TOAD_GIANT_CAVE:TALLOWjCREATURE:OLM_GIANT:FATjCREATURE:OLM_GIANT:TALLOWjCREATURE:BAT_GIANT:FATjCREATURE:BAT_GIANT:TALLOWjCREATURE:RAT_GIANT:FATjCREATURE:RAT_GIANT:TALLOWjCREATURE:RAT_LARGE:FATjCREATURE:RAT_LARGE:TALLOWjCREATURE:MOLE_DOG_NAKED:FATjCREATURE:MOLE_DOG_NAKED:TALLOWjCREATURE:TROGLODYTE:FATjCREATURE:TROGLODYTE:TALLOWjCREATURE:MOLE_GIANT:FATjCREATURE:MOLE_GIANT:TALLOWjCREATURE:IMP_FIRE:FATjCREATURE:IMP_FIRE:TALLOWjCREATURE:SPIDER_CAVE_GIANT:FATj!CREATURE:SPIDER_CAVE_GIANT:TALLOWjCREATURE:SPIDER_CAVE:FATjCREATURE:SPIDER_CAVE:TALLOWjCREATURE:FISH_CAVE:FATjCREATURE:FISH_CAVE:TALLOWjCREATURE:CAVE_FISH_MAN:FATjCREATURE:CAVE_FISH_MAN:TALLOWjCREATURE:LOBSTER_CAVE:FATjCREATURE:LOBSTER_CAVE:TALLOWjCREATURE:OLM:FATjCREATURE:OLM:TALLOWjCREATURE:OLM_MAN:FATjCREATURE:OLM_MAN:TALLOWjCREATURE:BAT:FATjCREATURE:BAT:TALLOWjCREATURE:BAT_MAN:FATjCREATURE:BAT_MAN:TALLOWjCREATURE:MAGGOT_PURRING:FATjCREATURE:MAGGOT_PURRING:TALLOWjCREATURE:BIRD_SWALLOW_CAVE:FATj!CREATURE:BIRD_SWALLOW_CAVE:TALLOWjCREATURE:CAVE_SWALLOW_MAN:FATj CREATURE:CAVE_SWALLOW_MAN:TALLOWj$CREATURE:BIRD_SWALLOW_CAVE_GIANT:FATj'CREATURE:BIRD_SWALLOW_CAVE_GIANT:TALLOWjCREATURE:AMPHIBIAN_MAN:FATjCREATURE:AMPHIBIAN_MAN:TALLOWjCREATURE:REPTILE_MAN:FATjCREATURE:REPTILE_MAN:TALLOWjCREATURE:SERPENT_MAN:FATjCREATURE:SERPENT_MAN:TALLOWjCREATURE:ANT_MAN:FATjCREATURE:ANT_MAN:TALLOWjCREATURE:RODENT MAN:FATjCREATURE:RODENT MAN:TALLOWjCREATURE:WILD_BOAR:FATjCREATURE:WILD_BOAR:TALLOWjCREATURE:WILD_BOAR_MAN:FATjCREATURE:WILD_BOAR_MAN:TALLOWjCREATURE:GIANT_WILD_BOAR:FATjCREATURE:GIANT_WILD_BOAR:TALLOWjCREATURE:COYOTE:FATjCREATURE:COYOTE:TALLOWjCREATURE:COYOTE_MAN:FATjCREATURE:COYOTE_MAN:TALLOWjCREATURE:GIANT_COYOTE:FATjCREATURE:GIANT_COYOTE:TALLOWjCREATURE:KANGAROO:FATjCREATURE:KANGAROO:TALLOWjCREATURE:KANGAROO_MAN:FATjCREATURE:KANGAROO_MAN:TALLOWjCREATURE:GIANT_KANGAROO:FATjCREATURE:GIANT_KANGAROO:TALLOWjCREATURE:KOALA:FATjCREATURE:KOALA:TALLOWjCREATURE:KOALA_MAN:FATjCREATURE:KOALA_MAN:TALLOWjCREATURE:GIANT_KOALA:FATjCREATURE:GIANT_KOALA:TALLOWjCREATURE:ADDER:FATjCREATURE:ADDER:TALLOWjCREATURE:ADDER_MAN:FATjCREATURE:ADDER_MAN:TALLOWjCREATURE:GIANT_ADDER:FATjCREATURE:GIANT_ADDER:TALLOWjCREATURE:ECHIDNA:FATjCREATURE:ECHIDNA:TALLOWjCREATURE:ECHIDNA_MAN:FATjCREATURE:ECHIDNA_MAN:TALLOWjCREATURE:GIANT_ECHIDNA:FATjCREATURE:GIANT_ECHIDNA:TALLOWjCREATURE:PORCUPINE:FATjCREATURE:PORCUPINE:TALLOWjCREATURE:PORCUPINE_MAN:FATjCREATURE:PORCUPINE_MAN:TALLOWjCREATURE:GIANT_PORCUPINE:FATjCREATURE:GIANT_PORCUPINE:TALLOWjCREATURE:KINGSNAKE:FATjCREATURE:KINGSNAKE:TALLOWjCREATURE:KINGSNAKE_MAN:FATjCREATURE:KINGSNAKE_MAN:TALLOWjCREATURE:GIANT_KINGSNAKE:FATjCREATURE:GIANT_KINGSNAKE:TALLOWjCREATURE:GRAY_LANGUR:FATjCREATURE:GRAY_LANGUR:TALLOWjCREATURE:GRAY_LANGUR_MAN:FATjCREATURE:GRAY_LANGUR_MAN:TALLOWjCREATURE:GIANT_GRAY_LANGUR:FATj!CREATURE:GIANT_GRAY_LANGUR:TALLOWjCREATURE:BOBCAT:FATjCREATURE:BOBCAT:TALLOWjCREATURE:BOBCAT_MAN:FATjCREATURE:BOBCAT_MAN:TALLOWjCREATURE:GIANT_BOBCAT:FATjCREATURE:GIANT_BOBCAT:TALLOWjCREATURE:SKUNK:FATjCREATURE:SKUNK:TALLOWjCREATURE:SKUNK_MAN:FATjCREATURE:SKUNK_MAN:TALLOWjCREATURE:GIANT_SKUNK:FATjCREATURE:GIANT_SKUNK:TALLOWjCREATURE:GREEN_TREE_FROG:FATjCREATURE:GREEN_TREE_FROG:TALLOWj CREATURE:GREEN_TREE_FROG_MAN:FATj#CREATURE:GREEN_TREE_FROG_MAN:TALLOWj"CREATURE:GIANT_GREEN_TREE_FROG:FATj%CREATURE:GIANT_GREEN_TREE_FROG:TALLOWjCREATURE:HARE:FATjCREATURE:HARE:TALLOWjCREATURE:HARE_MAN:FATjCREATURE:HARE_MAN:TALLOWjCREATURE:GIANT_HARE:FATjCREATURE:GIANT_HARE:TALLOWjCREATURE:RATTLESNAKE:FATjCREATURE:RATTLESNAKE:TALLOWjCREATURE:RATTLESNAKE_MAN:FATjCREATURE:RATTLESNAKE_MAN:TALLOWjCREATURE:GIANT_RATTLESNAKE:FATj!CREATURE:GIANT_RATTLESNAKE:TALLOWjCREATURE:WEASEL:FATjCREATURE:WEASEL:TALLOWjCREATURE:WEASEL_MAN:FATjCREATURE:WEASEL_MAN:TALLOWjCREATURE:GIANT_WEASEL:FATjCREATURE:GIANT_WEASEL:TALLOWjCREATURE:COPPERHEAD_SNAKE:FATj CREATURE:COPPERHEAD_SNAKE:TALLOWj!CREATURE:COPPERHEAD_SNAKE_MAN:FATj$CREATURE:COPPERHEAD_SNAKE_MAN:TALLOWj#CREATURE:GIANT_COPPERHEAD_SNAKE:FATj&CREATURE:GIANT_COPPERHEAD_SNAKE:TALLOWjCREATURE:IBEX:FATjCREATURE:IBEX:TALLOWjCREATURE:IBEX_MAN:FATjCREATURE:IBEX_MAN:TALLOWjCREATURE:GIANT_IBEX:FATjCREATURE:GIANT_IBEX:TALLOWjCREATURE:WOMBAT:FATjCREATURE:WOMBAT:TALLOWjCREATURE:WOMBAT_MAN:FATjCREATURE:WOMBAT_MAN:TALLOWjCREATURE:GIANT_WOMBAT:FATjCREATURE:GIANT_WOMBAT:TALLOWjCREATURE:DINGO:FATjCREATURE:DINGO:TALLOWjCREATURE:DINGO_MAN:FATjCREATURE:DINGO_MAN:TALLOWjCREATURE:GIANT_DINGO:FATjCREATURE:GIANT_DINGO:TALLOWjCREATURE:COATI:FATjCREATURE:COATI:TALLOWjCREATURE:COATI_MAN:FATjCREATURE:COATI_MAN:TALLOWjCREATURE:GIANT_COATI:FATjCREATURE:GIANT_COATI:TALLOWjCREATURE:OPOSSUM:FATjCREATURE:OPOSSUM:TALLOWjCREATURE:OPOSSUM_MAN:FATjCREATURE:OPOSSUM_MAN:TALLOWjCREATURE:GIANT_OPOSSUM:FATjCREATURE:GIANT_OPOSSUM:TALLOWjCREATURE:MONGOOSE:FATjCREATURE:MONGOOSE:TALLOWjCREATURE:MONGOOSE_MAN:FATjCREATURE:MONGOOSE_MAN:TALLOWjCREATURE:GIANT_MONGOOSE:FATjCREATURE:GIANT_MONGOOSE:TALLOWjCREATURE:HYENA:FATjCREATURE:HYENA:TALLOWjCREATURE:HYENA_MAN:FATjCREATURE:HYENA_MAN:TALLOWjCREATURE:GIANT_HYENA:FATjCREATURE:GIANT_HYENA:TALLOWjCREATURE:ANACONDA:FATjCREATURE:ANACONDA:TALLOWjCREATURE:ANACONDA_MAN:FATjCREATURE:ANACONDA_MAN:TALLOWjCREATURE:GIANT_ANACONDA:FATjCREATURE:GIANT_ANACONDA:TALLOWjCREATURE:MONITOR_LIZARD:FATjCREATURE:MONITOR_LIZARD:TALLOWjCREATURE:MONITOR_LIZARD_MAN:FATj"CREATURE:MONITOR_LIZARD_MAN:TALLOWj!CREATURE:GIANT_MONITOR_LIZARD:FATj$CREATURE:GIANT_MONITOR_LIZARD:TALLOWjCREATURE:KING_COBRA:FATjCREATURE:KING_COBRA:TALLOWjCREATURE:KING_COBRA_MAN:FATjCREATURE:KING_COBRA_MAN:TALLOWjCREATURE:GIANT_KING_COBRA:FATj CREATURE:GIANT_KING_COBRA:TALLOWjCREATURE:OCELOT:FATjCREATURE:OCELOT:TALLOWjCREATURE:OCELOT_MAN:FATjCREATURE:OCELOT_MAN:TALLOWjCREATURE:GIANT_OCELOT:FATjCREATURE:GIANT_OCELOT:TALLOWjCREATURE:JACKAL:FATjCREATURE:JACKAL:TALLOWjCREATURE:JACKAL_MAN:FATjCREATURE:JACKAL_MAN:TALLOWjCREATURE:GIANT_JACKAL:FATjCREATURE:GIANT_JACKAL:TALLOWjCREATURE:CAPUCHIN:FATjCREATURE:CAPUCHIN:TALLOWjCREATURE:CAPUCHIN_MAN:FATjCREATURE:CAPUCHIN_MAN:TALLOWjCREATURE:GIANT_CAPUCHIN:FATjCREATURE:GIANT_CAPUCHIN:TALLOWjCREATURE:SLOTH:FATjCREATURE:SLOTH:TALLOWjCREATURE:SLOTH_MAN:FATjCREATURE:SLOTH_MAN:TALLOWjCREATURE:GIANT_SLOTH:FATjCREATURE:GIANT_SLOTH:TALLOWjCREATURE:SPIDER_MONKEY:FATjCREATURE:SPIDER_MONKEY:TALLOWjCREATURE:SPIDER_MONKEY_MAN:FATj!CREATURE:SPIDER_MONKEY_MAN:TALLOWj CREATURE:GIANT_SPIDER_MONKEY:FATj#CREATURE:GIANT_SPIDER_MONKEY:TALLOWjCREATURE:PANGOLIN:FATjCREATURE:PANGOLIN:TALLOWjCREATURE:PANGOLIN_MAN:FATjCREATURE:PANGOLIN_MAN:TALLOWjCREATURE:GIANT_PANGOLIN:FATjCREATURE:GIANT_PANGOLIN:TALLOWjCREATURE:BLACK_MAMBA:FATjCREATURE:BLACK_MAMBA:TALLOWjCREATURE:BLACK_MAMBA_MAN:FATjCREATURE:BLACK_MAMBA_MAN:TALLOWjCREATURE:GIANT_BLACK_MAMBA:FATj!CREATURE:GIANT_BLACK_MAMBA:TALLOWjCREATURE:BEAR_SLOTH:FATjCREATURE:BEAR_SLOTH:TALLOWjCREATURE:SLOTH_BEAR_MAN:FATjCREATURE:SLOTH_BEAR_MAN:TALLOWjCREATURE:GIANT_SLOTH_BEAR:FATj CREATURE:GIANT_SLOTH_BEAR:TALLOWjCREATURE:AYE-AYE:FATjCREATURE:AYE-AYE:TALLOWjCREATURE:AYE-AYE_MAN:FATjCREATURE:AYE-AYE_MAN:TALLOWjCREATURE:GIANT_AYE-AYE:FATjCREATURE:GIANT_AYE-AYE:TALLOWjCREATURE:BUSHMASTER:FATjCREATURE:BUSHMASTER:TALLOWjCREATURE:BUSHMASTER_MAN:FATjCREATURE:BUSHMASTER_MAN:TALLOWjCREATURE:GIANT_BUSHMASTER:FATj CREATURE:GIANT_BUSHMASTER:TALLOWjCREATURE:PYTHON:FATjCREATURE:PYTHON:TALLOWjCREATURE:PYTHON_MAN:FATjCREATURE:PYTHON_MAN:TALLOWjCREATURE:GIANT_PYTHON:FATjCREATURE:GIANT_PYTHON:TALLOWjCREATURE:TAPIR:FATjCREATURE:TAPIR:TALLOWjCREATURE:TAPIR_MAN:FATjCREATURE:TAPIR_MAN:TALLOWjCREATURE:GIANT_TAPIR:FATjCREATURE:GIANT_TAPIR:TALLOWjCREATURE:IMPALA:FATjCREATURE:IMPALA:TALLOWjCREATURE:IMPALA_MAN:FATjCREATURE:IMPALA_MAN:TALLOWjCREATURE:GIANT_IMPALA:FATjCREATURE:GIANT_IMPALA:TALLOWjCREATURE:AARDVARK:FATjCREATURE:AARDVARK:TALLOWjCREATURE:AARDVARK_MAN:FATjCREATURE:AARDVARK_MAN:TALLOWjCREATURE:GIANT_AARDVARK:FATjCREATURE:GIANT_AARDVARK:TALLOWjCREATURE:LION_TAMARIN:FATjCREATURE:LION_TAMARIN:TALLOWjCREATURE:LION_TAMARIN_MAN:FATj CREATURE:LION_TAMARIN_MAN:TALLOWjCREATURE:GIANT_LION_TAMARIN:FATj"CREATURE:GIANT_LION_TAMARIN:TALLOWjCREATURE:STOAT:FATjCREATURE:STOAT:TALLOWjCREATURE:STOAT_MAN:FATjCREATURE:STOAT_MAN:TALLOWjCREATURE:GIANT_STOAT:FATjCREATURE:GIANT_STOAT:TALLOWjCREATURE:LYNX:FATjCREATURE:LYNX:TALLOWjCREATURE:LYNX_MAN:FATjCREATURE:LYNX_MAN:TALLOWjCREATURE:GIANT_LYNX:FATjCREATURE:GIANT_LYNX:TALLOWjCREATURE:GNOLL:FATjCREATURE:GNOLL:TALLOWjCREATURE:NAGA:FATjCREATURE:NAGA:TALLOWjCREATURE:FORGOTTEN_BEAST_2:FATj!CREATURE:FORGOTTEN_BEAST_2:TALLOWjCREATURE:FORGOTTEN_BEAST_4:FATj!CREATURE:FORGOTTEN_BEAST_4:TALLOWjCREATURE:FORGOTTEN_BEAST_5:FATj!CREATURE:FORGOTTEN_BEAST_5:TALLOWjCREATURE:FORGOTTEN_BEAST_6:FATj!CREATURE:FORGOTTEN_BEAST_6:TALLOWjCREATURE:FORGOTTEN_BEAST_7:FATj!CREATURE:FORGOTTEN_BEAST_7:TALLOWjCREATURE:FORGOTTEN_BEAST_10:FATj"CREATURE:FORGOTTEN_BEAST_10:TALLOWjCREATURE:FORGOTTEN_BEAST_12:FATj"CREATURE:FORGOTTEN_BEAST_12:TALLOWjCREATURE:FORGOTTEN_BEAST_13:FATj"CREATURE:FORGOTTEN_BEAST_13:TALLOWjCREATURE:FORGOTTEN_BEAST_16:FATj"CREATURE:FORGOTTEN_BEAST_16:TALLOWjCREATURE:FORGOTTEN_BEAST_17:FATj"CREATURE:FORGOTTEN_BEAST_17:TALLOWjCREATURE:FORGOTTEN_BEAST_18:FATj"CREATURE:FORGOTTEN_BEAST_18:TALLOWjCREATURE:FORGOTTEN_BEAST_19:FATj"CREATURE:FORGOTTEN_BEAST_19:TALLOWjCREATURE:FORGOTTEN_BEAST_20:FATj"CREATURE:FORGOTTEN_BEAST_20:TALLOWjCREATURE:FORGOTTEN_BEAST_22:FATj"CREATURE:FORGOTTEN_BEAST_22:TALLOWjCREATURE:FORGOTTEN_BEAST_23:FATj"CREATURE:FORGOTTEN_BEAST_23:TALLOWjCREATURE:FORGOTTEN_BEAST_24:FATj"CREATURE:FORGOTTEN_BEAST_24:TALLOWjCREATURE:FORGOTTEN_BEAST_25:FATj"CREATURE:FORGOTTEN_BEAST_25:TALLOWjCREATURE:FORGOTTEN_BEAST_26:FATj"CREATURE:FORGOTTEN_BEAST_26:TALLOWjCREATURE:FORGOTTEN_BEAST_27:FATj"CREATURE:FORGOTTEN_BEAST_27:TALLOWjCREATURE:FORGOTTEN_BEAST_28:FATj"CREATURE:FORGOTTEN_BEAST_28:TALLOWjCREATURE:FORGOTTEN_BEAST_29:FATj"CREATURE:FORGOTTEN_BEAST_29:TALLOWjCREATURE:FORGOTTEN_BEAST_32:FATj"CREATURE:FORGOTTEN_BEAST_32:TALLOWjCREATURE:FORGOTTEN_BEAST_33:FATj"CREATURE:FORGOTTEN_BEAST_33:TALLOWjCREATURE:FORGOTTEN_BEAST_34:FATj"CREATURE:FORGOTTEN_BEAST_34:TALLOWjCREATURE:FORGOTTEN_BEAST_35:FATj"CREATURE:FORGOTTEN_BEAST_35:TALLOWjCREATURE:FORGOTTEN_BEAST_36:FATj"CREATURE:FORGOTTEN_BEAST_36:TALLOWjCREATURE:FORGOTTEN_BEAST_39:FATj"CREATURE:FORGOTTEN_BEAST_39:TALLOWjCREATURE:FORGOTTEN_BEAST_41:FATj"CREATURE:FORGOTTEN_BEAST_41:TALLOWjCREATURE:FORGOTTEN_BEAST_42:FATj"CREATURE:FORGOTTEN_BEAST_42:TALLOWjCREATURE:FORGOTTEN_BEAST_43:FATj"CREATURE:FORGOTTEN_BEAST_43:TALLOWjCREATURE:FORGOTTEN_BEAST_44:FATj"CREATURE:FORGOTTEN_BEAST_44:TALLOWjCREATURE:FORGOTTEN_BEAST_45:FATj"CREATURE:FORGOTTEN_BEAST_45:TALLOWjCREATURE:FORGOTTEN_BEAST_47:FATj"CREATURE:FORGOTTEN_BEAST_47:TALLOWjCREATURE:FORGOTTEN_BEAST_50:FATj"CREATURE:FORGOTTEN_BEAST_50:TALLOWjCREATURE:FORGOTTEN_BEAST_52:FATj"CREATURE:FORGOTTEN_BEAST_52:TALLOWjCREATURE:FORGOTTEN_BEAST_53:FATj"CREATURE:FORGOTTEN_BEAST_53:TALLOWjCREATURE:FORGOTTEN_BEAST_55:FATj"CREATURE:FORGOTTEN_BEAST_55:TALLOWjCREATURE:FORGOTTEN_BEAST_56:FATj"CREATURE:FORGOTTEN_BEAST_56:TALLOWjCREATURE:FORGOTTEN_BEAST_58:FATj"CREATURE:FORGOTTEN_BEAST_58:TALLOWjCREATURE:FORGOTTEN_BEAST_59:FATj"CREATURE:FORGOTTEN_BEAST_59:TALLOWjCREATURE:FORGOTTEN_BEAST_60:FATj"CREATURE:FORGOTTEN_BEAST_60:TALLOWjCREATURE:FORGOTTEN_BEAST_61:FATj"CREATURE:FORGOTTEN_BEAST_61:TALLOWjCREATURE:FORGOTTEN_BEAST_64:FATj"CREATURE:FORGOTTEN_BEAST_64:TALLOWjCREATURE:FORGOTTEN_BEAST_66:FATj"CREATURE:FORGOTTEN_BEAST_66:TALLOWjCREATURE:FORGOTTEN_BEAST_67:FATj"CREATURE:FORGOTTEN_BEAST_67:TALLOWjCREATURE:FORGOTTEN_BEAST_69:FATj"CREATURE:FORGOTTEN_BEAST_69:TALLOWjCREATURE:FORGOTTEN_BEAST_71:FATj"CREATURE:FORGOTTEN_BEAST_71:TALLOWjCREATURE:FORGOTTEN_BEAST_72:FATj"CREATURE:FORGOTTEN_BEAST_72:TALLOWjCREATURE:FORGOTTEN_BEAST_73:FATj"CREATURE:FORGOTTEN_BEAST_73:TALLOWjCREATURE:FORGOTTEN_BEAST_74:FATj"CREATURE:FORGOTTEN_BEAST_74:TALLOWjCREATURE:FORGOTTEN_BEAST_75:FATj"CREATURE:FORGOTTEN_BEAST_75:TALLOWjCREATURE:FORGOTTEN_BEAST_78:FATj"CREATURE:FORGOTTEN_BEAST_78:TALLOWjCREATURE:FORGOTTEN_BEAST_80:FATj"CREATURE:FORGOTTEN_BEAST_80:TALLOWjCREATURE:FORGOTTEN_BEAST_81:FATj"CREATURE:FORGOTTEN_BEAST_81:TALLOWjCREATURE:FORGOTTEN_BEAST_82:FATj"CREATURE:FORGOTTEN_BEAST_82:TALLOWjCREATURE:FORGOTTEN_BEAST_83:FATj"CREATURE:FORGOTTEN_BEAST_83:TALLOWjCREATURE:FORGOTTEN_BEAST_84:FATj"CREATURE:FORGOTTEN_BEAST_84:TALLOWjCREATURE:FORGOTTEN_BEAST_86:FATj"CREATURE:FORGOTTEN_BEAST_86:TALLOWjCREATURE:FORGOTTEN_BEAST_87:FATj"CREATURE:FORGOTTEN_BEAST_87:TALLOWjCREATURE:FORGOTTEN_BEAST_89:FATj"CREATURE:FORGOTTEN_BEAST_89:TALLOWjCREATURE:FORGOTTEN_BEAST_90:FATj"CREATURE:FORGOTTEN_BEAST_90:TALLOWjCREATURE:FORGOTTEN_BEAST_92:FATj"CREATURE:FORGOTTEN_BEAST_92:TALLOWjCREATURE:FORGOTTEN_BEAST_94:FATj"CREATURE:FORGOTTEN_BEAST_94:TALLOWjCREATURE:FORGOTTEN_BEAST_95:FATj"CREATURE:FORGOTTEN_BEAST_95:TALLOWjCREATURE:FORGOTTEN_BEAST_96:FATj"CREATURE:FORGOTTEN_BEAST_96:TALLOWjCREATURE:FORGOTTEN_BEAST_97:FATj"CREATURE:FORGOTTEN_BEAST_97:TALLOWjCREATURE:FORGOTTEN_BEAST_98:FATj"CREATURE:FORGOTTEN_BEAST_98:TALLOWj CREATURE:FORGOTTEN_BEAST_100:FATj#CREATURE:FORGOTTEN_BEAST_100:TALLOWj CREATURE:FORGOTTEN_BEAST_105:FATj#CREATURE:FORGOTTEN_BEAST_105:TALLOWj CREATURE:FORGOTTEN_BEAST_106:FATj#CREATURE:FORGOTTEN_BEAST_106:TALLOWj CREATURE:FORGOTTEN_BEAST_107:FATj#CREATURE:FORGOTTEN_BEAST_107:TALLOWj CREATURE:FORGOTTEN_BEAST_108:FATj#CREATURE:FORGOTTEN_BEAST_108:TALLOWj CREATURE:FORGOTTEN_BEAST_109:FATj#CREATURE:FORGOTTEN_BEAST_109:TALLOWj CREATURE:FORGOTTEN_BEAST_111:FATj#CREATURE:FORGOTTEN_BEAST_111:TALLOWj CREATURE:FORGOTTEN_BEAST_112:FATj#CREATURE:FORGOTTEN_BEAST_112:TALLOWj CREATURE:FORGOTTEN_BEAST_113:FATj#CREATURE:FORGOTTEN_BEAST_113:TALLOWj CREATURE:FORGOTTEN_BEAST_114:FATj#CREATURE:FORGOTTEN_BEAST_114:TALLOWj CREATURE:FORGOTTEN_BEAST_115:FATj#CREATURE:FORGOTTEN_BEAST_115:TALLOWj CREATURE:FORGOTTEN_BEAST_116:FATj#CREATURE:FORGOTTEN_BEAST_116:TALLOWj CREATURE:FORGOTTEN_BEAST_117:FATj#CREATURE:FORGOTTEN_BEAST_117:TALLOWj CREATURE:FORGOTTEN_BEAST_118:FATj#CREATURE:FORGOTTEN_BEAST_118:TALLOWj CREATURE:FORGOTTEN_BEAST_119:FATj#CREATURE:FORGOTTEN_BEAST_119:TALLOWj CREATURE:FORGOTTEN_BEAST_120:FATj#CREATURE:FORGOTTEN_BEAST_120:TALLOWj CREATURE:FORGOTTEN_BEAST_122:FATj#CREATURE:FORGOTTEN_BEAST_122:TALLOWj CREATURE:FORGOTTEN_BEAST_123:FATj#CREATURE:FORGOTTEN_BEAST_123:TALLOWj CREATURE:FORGOTTEN_BEAST_124:FATj#CREATURE:FORGOTTEN_BEAST_124:TALLOWj CREATURE:FORGOTTEN_BEAST_125:FATj#CREATURE:FORGOTTEN_BEAST_125:TALLOWj CREATURE:FORGOTTEN_BEAST_127:FATj#CREATURE:FORGOTTEN_BEAST_127:TALLOWj CREATURE:FORGOTTEN_BEAST_128:FATj#CREATURE:FORGOTTEN_BEAST_128:TALLOWj CREATURE:FORGOTTEN_BEAST_130:FATj#CREATURE:FORGOTTEN_BEAST_130:TALLOWj CREATURE:FORGOTTEN_BEAST_131:FATj#CREATURE:FORGOTTEN_BEAST_131:TALLOWj CREATURE:FORGOTTEN_BEAST_132:FATj#CREATURE:FORGOTTEN_BEAST_132:TALLOWj CREATURE:FORGOTTEN_BEAST_133:FATj#CREATURE:FORGOTTEN_BEAST_133:TALLOWj CREATURE:FORGOTTEN_BEAST_134:FATj#CREATURE:FORGOTTEN_BEAST_134:TALLOWj CREATURE:FORGOTTEN_BEAST_135:FATj#CREATURE:FORGOTTEN_BEAST_135:TALLOWj CREATURE:FORGOTTEN_BEAST_137:FATj#CREATURE:FORGOTTEN_BEAST_137:TALLOWj CREATURE:FORGOTTEN_BEAST_138:FATj#CREATURE:FORGOTTEN_BEAST_138:TALLOWj CREATURE:FORGOTTEN_BEAST_139:FATj#CREATURE:FORGOTTEN_BEAST_139:TALLOWj CREATURE:FORGOTTEN_BEAST_141:FATj#CREATURE:FORGOTTEN_BEAST_141:TALLOWj CREATURE:FORGOTTEN_BEAST_142:FATj#CREATURE:FORGOTTEN_BEAST_142:TALLOWj CREATURE:FORGOTTEN_BEAST_144:FATj#CREATURE:FORGOTTEN_BEAST_144:TALLOWj CREATURE:FORGOTTEN_BEAST_146:FATj#CREATURE:FORGOTTEN_BEAST_146:TALLOWj CREATURE:FORGOTTEN_BEAST_148:FATj#CREATURE:FORGOTTEN_BEAST_148:TALLOWj CREATURE:FORGOTTEN_BEAST_149:FATj#CREATURE:FORGOTTEN_BEAST_149:TALLOWj CREATURE:FORGOTTEN_BEAST_150:FATj#CREATURE:FORGOTTEN_BEAST_150:TALLOWj CREATURE:FORGOTTEN_BEAST_152:FATj#CREATURE:FORGOTTEN_BEAST_152:TALLOWj CREATURE:FORGOTTEN_BEAST_154:FATj#CREATURE:FORGOTTEN_BEAST_154:TALLOWj CREATURE:FORGOTTEN_BEAST_156:FATj#CREATURE:FORGOTTEN_BEAST_156:TALLOWj CREATURE:FORGOTTEN_BEAST_157:FATj#CREATURE:FORGOTTEN_BEAST_157:TALLOWj CREATURE:FORGOTTEN_BEAST_158:FATj#CREATURE:FORGOTTEN_BEAST_158:TALLOWj CREATURE:FORGOTTEN_BEAST_159:FATj#CREATURE:FORGOTTEN_BEAST_159:TALLOWj CREATURE:FORGOTTEN_BEAST_161:FATj#CREATURE:FORGOTTEN_BEAST_161:TALLOWj CREATURE:FORGOTTEN_BEAST_162:FATj#CREATURE:FORGOTTEN_BEAST_162:TALLOWj CREATURE:FORGOTTEN_BEAST_163:FATj#CREATURE:FORGOTTEN_BEAST_163:TALLOWj CREATURE:FORGOTTEN_BEAST_165:FATj#CREATURE:FORGOTTEN_BEAST_165:TALLOWj CREATURE:FORGOTTEN_BEAST_167:FATj#CREATURE:FORGOTTEN_BEAST_167:TALLOWj CREATURE:FORGOTTEN_BEAST_168:FATj#CREATURE:FORGOTTEN_BEAST_168:TALLOWj CREATURE:FORGOTTEN_BEAST_169:FATj#CREATURE:FORGOTTEN_BEAST_169:TALLOWj CREATURE:FORGOTTEN_BEAST_170:FATj#CREATURE:FORGOTTEN_BEAST_170:TALLOWj CREATURE:FORGOTTEN_BEAST_171:FATj#CREATURE:FORGOTTEN_BEAST_171:TALLOWj CREATURE:FORGOTTEN_BEAST_172:FATj#CREATURE:FORGOTTEN_BEAST_172:TALLOWj CREATURE:FORGOTTEN_BEAST_173:FATj#CREATURE:FORGOTTEN_BEAST_173:TALLOWj CREATURE:FORGOTTEN_BEAST_176:FATj#CREATURE:FORGOTTEN_BEAST_176:TALLOWj CREATURE:FORGOTTEN_BEAST_177:FATj#CREATURE:FORGOTTEN_BEAST_177:TALLOWj CREATURE:FORGOTTEN_BEAST_178:FATj#CREATURE:FORGOTTEN_BEAST_178:TALLOWj CREATURE:FORGOTTEN_BEAST_179:FATj#CREATURE:FORGOTTEN_BEAST_179:TALLOWj CREATURE:FORGOTTEN_BEAST_180:FATj#CREATURE:FORGOTTEN_BEAST_180:TALLOWj CREATURE:FORGOTTEN_BEAST_181:FATj#CREATURE:FORGOTTEN_BEAST_181:TALLOWj CREATURE:FORGOTTEN_BEAST_182:FATj#CREATURE:FORGOTTEN_BEAST_182:TALLOWj CREATURE:FORGOTTEN_BEAST_183:FATj#CREATURE:FORGOTTEN_BEAST_183:TALLOWj CREATURE:FORGOTTEN_BEAST_184:FATj#CREATURE:FORGOTTEN_BEAST_184:TALLOWj CREATURE:FORGOTTEN_BEAST_185:FATj#CREATURE:FORGOTTEN_BEAST_185:TALLOWj CREATURE:FORGOTTEN_BEAST_186:FATj#CREATURE:FORGOTTEN_BEAST_186:TALLOWj CREATURE:FORGOTTEN_BEAST_188:FATj#CREATURE:FORGOTTEN_BEAST_188:TALLOWj CREATURE:FORGOTTEN_BEAST_189:FATj#CREATURE:FORGOTTEN_BEAST_189:TALLOWj CREATURE:FORGOTTEN_BEAST_190:FATj#CREATURE:FORGOTTEN_BEAST_190:TALLOWj CREATURE:FORGOTTEN_BEAST_191:FATj#CREATURE:FORGOTTEN_BEAST_191:TALLOWj CREATURE:FORGOTTEN_BEAST_193:FATj#CREATURE:FORGOTTEN_BEAST_193:TALLOWj CREATURE:FORGOTTEN_BEAST_194:FATj#CREATURE:FORGOTTEN_BEAST_194:TALLOWj CREATURE:FORGOTTEN_BEAST_195:FATj#CREATURE:FORGOTTEN_BEAST_195:TALLOWj CREATURE:FORGOTTEN_BEAST_196:FATj#CREATURE:FORGOTTEN_BEAST_196:TALLOWj CREATURE:FORGOTTEN_BEAST_197:FATj#CREATURE:FORGOTTEN_BEAST_197:TALLOWj CREATURE:FORGOTTEN_BEAST_199:FATj#CREATURE:FORGOTTEN_BEAST_199:TALLOWj CREATURE:FORGOTTEN_BEAST_200:FATj#CREATURE:FORGOTTEN_BEAST_200:TALLOWj CREATURE:FORGOTTEN_BEAST_201:FATj#CREATURE:FORGOTTEN_BEAST_201:TALLOWj CREATURE:FORGOTTEN_BEAST_204:FATj#CREATURE:FORGOTTEN_BEAST_204:TALLOWj CREATURE:FORGOTTEN_BEAST_205:FATj#CREATURE:FORGOTTEN_BEAST_205:TALLOWj CREATURE:FORGOTTEN_BEAST_206:FATj#CREATURE:FORGOTTEN_BEAST_206:TALLOWj CREATURE:FORGOTTEN_BEAST_207:FATj#CREATURE:FORGOTTEN_BEAST_207:TALLOWj CREATURE:FORGOTTEN_BEAST_208:FATj#CREATURE:FORGOTTEN_BEAST_208:TALLOWj CREATURE:FORGOTTEN_BEAST_209:FATj#CREATURE:FORGOTTEN_BEAST_209:TALLOWj CREATURE:FORGOTTEN_BEAST_210:FATj#CREATURE:FORGOTTEN_BEAST_210:TALLOWj CREATURE:FORGOTTEN_BEAST_211:FATj#CREATURE:FORGOTTEN_BEAST_211:TALLOWj CREATURE:FORGOTTEN_BEAST_212:FATj#CREATURE:FORGOTTEN_BEAST_212:TALLOWj CREATURE:FORGOTTEN_BEAST_213:FATj#CREATURE:FORGOTTEN_BEAST_213:TALLOWj CREATURE:FORGOTTEN_BEAST_214:FATj#CREATURE:FORGOTTEN_BEAST_214:TALLOWj CREATURE:FORGOTTEN_BEAST_215:FATj#CREATURE:FORGOTTEN_BEAST_215:TALLOWj CREATURE:FORGOTTEN_BEAST_216:FATj#CREATURE:FORGOTTEN_BEAST_216:TALLOWj CREATURE:FORGOTTEN_BEAST_217:FATj#CREATURE:FORGOTTEN_BEAST_217:TALLOWj CREATURE:FORGOTTEN_BEAST_218:FATj#CREATURE:FORGOTTEN_BEAST_218:TALLOWj CREATURE:FORGOTTEN_BEAST_220:FATj#CREATURE:FORGOTTEN_BEAST_220:TALLOWj CREATURE:FORGOTTEN_BEAST_221:FATj#CREATURE:FORGOTTEN_BEAST_221:TALLOWj CREATURE:FORGOTTEN_BEAST_224:FATj#CREATURE:FORGOTTEN_BEAST_224:TALLOWj CREATURE:FORGOTTEN_BEAST_225:FATj#CREATURE:FORGOTTEN_BEAST_225:TALLOWj CREATURE:FORGOTTEN_BEAST_228:FATj#CREATURE:FORGOTTEN_BEAST_228:TALLOWj CREATURE:FORGOTTEN_BEAST_231:FATj#CREATURE:FORGOTTEN_BEAST_231:TALLOWj CREATURE:FORGOTTEN_BEAST_232:FATj#CREATURE:FORGOTTEN_BEAST_232:TALLOWj CREATURE:FORGOTTEN_BEAST_235:FATj#CREATURE:FORGOTTEN_BEAST_235:TALLOWj CREATURE:FORGOTTEN_BEAST_236:FATj#CREATURE:FORGOTTEN_BEAST_236:TALLOWj CREATURE:FORGOTTEN_BEAST_237:FATj#CREATURE:FORGOTTEN_BEAST_237:TALLOWj CREATURE:FORGOTTEN_BEAST_239:FATj#CREATURE:FORGOTTEN_BEAST_239:TALLOWj CREATURE:FORGOTTEN_BEAST_240:FATj#CREATURE:FORGOTTEN_BEAST_240:TALLOWj CREATURE:FORGOTTEN_BEAST_242:FATj#CREATURE:FORGOTTEN_BEAST_242:TALLOWj CREATURE:FORGOTTEN_BEAST_243:FATj#CREATURE:FORGOTTEN_BEAST_243:TALLOWj CREATURE:FORGOTTEN_BEAST_244:FATj#CREATURE:FORGOTTEN_BEAST_244:TALLOWj CREATURE:FORGOTTEN_BEAST_245:FATj#CREATURE:FORGOTTEN_BEAST_245:TALLOWj CREATURE:FORGOTTEN_BEAST_246:FATj#CREATURE:FORGOTTEN_BEAST_246:TALLOWj CREATURE:FORGOTTEN_BEAST_247:FATj#CREATURE:FORGOTTEN_BEAST_247:TALLOWj CREATURE:FORGOTTEN_BEAST_248:FATj#CREATURE:FORGOTTEN_BEAST_248:TALLOWj CREATURE:FORGOTTEN_BEAST_249:FATj#CREATURE:FORGOTTEN_BEAST_249:TALLOWj CREATURE:FORGOTTEN_BEAST_253:FATj#CREATURE:FORGOTTEN_BEAST_253:TALLOWj CREATURE:FORGOTTEN_BEAST_255:FATj#CREATURE:FORGOTTEN_BEAST_255:TALLOWj CREATURE:FORGOTTEN_BEAST_256:FATj#CREATURE:FORGOTTEN_BEAST_256:TALLOWj CREATURE:FORGOTTEN_BEAST_259:FATj#CREATURE:FORGOTTEN_BEAST_259:TALLOWj CREATURE:FORGOTTEN_BEAST_260:FATj#CREATURE:FORGOTTEN_BEAST_260:TALLOWj CREATURE:FORGOTTEN_BEAST_263:FATj#CREATURE:FORGOTTEN_BEAST_263:TALLOWj CREATURE:FORGOTTEN_BEAST_264:FATj#CREATURE:FORGOTTEN_BEAST_264:TALLOWj CREATURE:FORGOTTEN_BEAST_265:FATj#CREATURE:FORGOTTEN_BEAST_265:TALLOWj CREATURE:FORGOTTEN_BEAST_266:FATj#CREATURE:FORGOTTEN_BEAST_266:TALLOWj CREATURE:FORGOTTEN_BEAST_268:FATj#CREATURE:FORGOTTEN_BEAST_268:TALLOWj CREATURE:FORGOTTEN_BEAST_269:FATj#CREATURE:FORGOTTEN_BEAST_269:TALLOWj CREATURE:FORGOTTEN_BEAST_270:FATj#CREATURE:FORGOTTEN_BEAST_270:TALLOWj CREATURE:FORGOTTEN_BEAST_271:FATj#CREATURE:FORGOTTEN_BEAST_271:TALLOWj CREATURE:FORGOTTEN_BEAST_272:FATj#CREATURE:FORGOTTEN_BEAST_272:TALLOWj CREATURE:FORGOTTEN_BEAST_273:FATj#CREATURE:FORGOTTEN_BEAST_273:TALLOWj CREATURE:FORGOTTEN_BEAST_274:FATj#CREATURE:FORGOTTEN_BEAST_274:TALLOWj CREATURE:FORGOTTEN_BEAST_275:FATj#CREATURE:FORGOTTEN_BEAST_275:TALLOWj CREATURE:FORGOTTEN_BEAST_276:FATj#CREATURE:FORGOTTEN_BEAST_276:TALLOWj CREATURE:FORGOTTEN_BEAST_277:FATj#CREATURE:FORGOTTEN_BEAST_277:TALLOWj CREATURE:FORGOTTEN_BEAST_279:FATj#CREATURE:FORGOTTEN_BEAST_279:TALLOWj CREATURE:FORGOTTEN_BEAST_280:FATj#CREATURE:FORGOTTEN_BEAST_280:TALLOWj CREATURE:FORGOTTEN_BEAST_281:FATj#CREATURE:FORGOTTEN_BEAST_281:TALLOWj CREATURE:FORGOTTEN_BEAST_282:FATj#CREATURE:FORGOTTEN_BEAST_282:TALLOWj CREATURE:FORGOTTEN_BEAST_283:FATj#CREATURE:FORGOTTEN_BEAST_283:TALLOWj CREATURE:FORGOTTEN_BEAST_285:FATj#CREATURE:FORGOTTEN_BEAST_285:TALLOWj CREATURE:FORGOTTEN_BEAST_286:FATj#CREATURE:FORGOTTEN_BEAST_286:TALLOWj CREATURE:FORGOTTEN_BEAST_287:FATj#CREATURE:FORGOTTEN_BEAST_287:TALLOWj CREATURE:FORGOTTEN_BEAST_288:FATj#CREATURE:FORGOTTEN_BEAST_288:TALLOWj CREATURE:FORGOTTEN_BEAST_289:FATj#CREATURE:FORGOTTEN_BEAST_289:TALLOWj CREATURE:FORGOTTEN_BEAST_290:FATj#CREATURE:FORGOTTEN_BEAST_290:TALLOWj CREATURE:FORGOTTEN_BEAST_293:FATj#CREATURE:FORGOTTEN_BEAST_293:TALLOWj CREATURE:FORGOTTEN_BEAST_295:FATj#CREATURE:FORGOTTEN_BEAST_295:TALLOWj CREATURE:FORGOTTEN_BEAST_297:FATj#CREATURE:FORGOTTEN_BEAST_297:TALLOWj CREATURE:FORGOTTEN_BEAST_300:FATj#CREATURE:FORGOTTEN_BEAST_300:TALLOWj CREATURE:FORGOTTEN_BEAST_302:FATj#CREATURE:FORGOTTEN_BEAST_302:TALLOWj CREATURE:FORGOTTEN_BEAST_306:FATj#CREATURE:FORGOTTEN_BEAST_306:TALLOWj CREATURE:FORGOTTEN_BEAST_307:FATj#CREATURE:FORGOTTEN_BEAST_307:TALLOWj CREATURE:FORGOTTEN_BEAST_310:FATj#CREATURE:FORGOTTEN_BEAST_310:TALLOWj CREATURE:FORGOTTEN_BEAST_311:FATj#CREATURE:FORGOTTEN_BEAST_311:TALLOWj CREATURE:FORGOTTEN_BEAST_312:FATj#CREATURE:FORGOTTEN_BEAST_312:TALLOWj CREATURE:FORGOTTEN_BEAST_314:FATj#CREATURE:FORGOTTEN_BEAST_314:TALLOWj CREATURE:FORGOTTEN_BEAST_316:FATj#CREATURE:FORGOTTEN_BEAST_316:TALLOWj CREATURE:FORGOTTEN_BEAST_317:FATj#CREATURE:FORGOTTEN_BEAST_317:TALLOWj CREATURE:FORGOTTEN_BEAST_318:FATj#CREATURE:FORGOTTEN_BEAST_318:TALLOWj CREATURE:FORGOTTEN_BEAST_320:FATj#CREATURE:FORGOTTEN_BEAST_320:TALLOWj CREATURE:FORGOTTEN_BEAST_321:FATj#CREATURE:FORGOTTEN_BEAST_321:TALLOWj CREATURE:FORGOTTEN_BEAST_322:FATj#CREATURE:FORGOTTEN_BEAST_322:TALLOWj CREATURE:FORGOTTEN_BEAST_323:FATj#CREATURE:FORGOTTEN_BEAST_323:TALLOWj CREATURE:FORGOTTEN_BEAST_324:FATj#CREATURE:FORGOTTEN_BEAST_324:TALLOWj CREATURE:FORGOTTEN_BEAST_325:FATj#CREATURE:FORGOTTEN_BEAST_325:TALLOWj CREATURE:FORGOTTEN_BEAST_326:FATj#CREATURE:FORGOTTEN_BEAST_326:TALLOWj CREATURE:FORGOTTEN_BEAST_327:FATj#CREATURE:FORGOTTEN_BEAST_327:TALLOWj CREATURE:FORGOTTEN_BEAST_328:FATj#CREATURE:FORGOTTEN_BEAST_328:TALLOWj CREATURE:FORGOTTEN_BEAST_329:FATj#CREATURE:FORGOTTEN_BEAST_329:TALLOWj CREATURE:FORGOTTEN_BEAST_330:FATj#CREATURE:FORGOTTEN_BEAST_330:TALLOWj CREATURE:FORGOTTEN_BEAST_332:FATj#CREATURE:FORGOTTEN_BEAST_332:TALLOWj CREATURE:FORGOTTEN_BEAST_333:FATj#CREATURE:FORGOTTEN_BEAST_333:TALLOWj CREATURE:FORGOTTEN_BEAST_335:FATj#CREATURE:FORGOTTEN_BEAST_335:TALLOWj CREATURE:FORGOTTEN_BEAST_336:FATj#CREATURE:FORGOTTEN_BEAST_336:TALLOWj CREATURE:FORGOTTEN_BEAST_337:FATj#CREATURE:FORGOTTEN_BEAST_337:TALLOWj CREATURE:FORGOTTEN_BEAST_338:FATj#CREATURE:FORGOTTEN_BEAST_338:TALLOWj CREATURE:FORGOTTEN_BEAST_339:FATj#CREATURE:FORGOTTEN_BEAST_339:TALLOWj CREATURE:FORGOTTEN_BEAST_341:FATj#CREATURE:FORGOTTEN_BEAST_341:TALLOWj CREATURE:FORGOTTEN_BEAST_342:FATj#CREATURE:FORGOTTEN_BEAST_342:TALLOWj CREATURE:FORGOTTEN_BEAST_343:FATj#CREATURE:FORGOTTEN_BEAST_343:TALLOWj CREATURE:FORGOTTEN_BEAST_344:FATj#CREATURE:FORGOTTEN_BEAST_344:TALLOWj CREATURE:FORGOTTEN_BEAST_347:FATj#CREATURE:FORGOTTEN_BEAST_347:TALLOWj CREATURE:FORGOTTEN_BEAST_348:FATj#CREATURE:FORGOTTEN_BEAST_348:TALLOWj CREATURE:FORGOTTEN_BEAST_349:FATj#CREATURE:FORGOTTEN_BEAST_349:TALLOWj CREATURE:FORGOTTEN_BEAST_351:FATj#CREATURE:FORGOTTEN_BEAST_351:TALLOWj CREATURE:FORGOTTEN_BEAST_352:FATj#CREATURE:FORGOTTEN_BEAST_352:TALLOWj CREATURE:FORGOTTEN_BEAST_353:FATj#CREATURE:FORGOTTEN_BEAST_353:TALLOWj CREATURE:FORGOTTEN_BEAST_354:FATj#CREATURE:FORGOTTEN_BEAST_354:TALLOWj CREATURE:FORGOTTEN_BEAST_355:FATj#CREATURE:FORGOTTEN_BEAST_355:TALLOWj CREATURE:FORGOTTEN_BEAST_356:FATj#CREATURE:FORGOTTEN_BEAST_356:TALLOWj CREATURE:FORGOTTEN_BEAST_357:FATj#CREATURE:FORGOTTEN_BEAST_357:TALLOWj CREATURE:FORGOTTEN_BEAST_358:FATj#CREATURE:FORGOTTEN_BEAST_358:TALLOWj CREATURE:FORGOTTEN_BEAST_359:FATj#CREATURE:FORGOTTEN_BEAST_359:TALLOWj CREATURE:FORGOTTEN_BEAST_361:FATj#CREATURE:FORGOTTEN_BEAST_361:TALLOWj CREATURE:FORGOTTEN_BEAST_363:FATj#CREATURE:FORGOTTEN_BEAST_363:TALLOWj CREATURE:FORGOTTEN_BEAST_364:FATj#CREATURE:FORGOTTEN_BEAST_364:TALLOWj CREATURE:FORGOTTEN_BEAST_365:FATj#CREATURE:FORGOTTEN_BEAST_365:TALLOWj CREATURE:FORGOTTEN_BEAST_366:FATj#CREATURE:FORGOTTEN_BEAST_366:TALLOWj CREATURE:FORGOTTEN_BEAST_367:FATj#CREATURE:FORGOTTEN_BEAST_367:TALLOWj CREATURE:FORGOTTEN_BEAST_368:FATj#CREATURE:FORGOTTEN_BEAST_368:TALLOWj CREATURE:FORGOTTEN_BEAST_370:FATj#CREATURE:FORGOTTEN_BEAST_370:TALLOWj CREATURE:FORGOTTEN_BEAST_372:FATj#CREATURE:FORGOTTEN_BEAST_372:TALLOWj CREATURE:FORGOTTEN_BEAST_374:FATj#CREATURE:FORGOTTEN_BEAST_374:TALLOWj CREATURE:FORGOTTEN_BEAST_375:FATj#CREATURE:FORGOTTEN_BEAST_375:TALLOWj CREATURE:FORGOTTEN_BEAST_377:FATj#CREATURE:FORGOTTEN_BEAST_377:TALLOWj CREATURE:FORGOTTEN_BEAST_378:FATj#CREATURE:FORGOTTEN_BEAST_378:TALLOWj CREATURE:FORGOTTEN_BEAST_379:FATj#CREATURE:FORGOTTEN_BEAST_379:TALLOWj CREATURE:FORGOTTEN_BEAST_380:FATj#CREATURE:FORGOTTEN_BEAST_380:TALLOWj CREATURE:FORGOTTEN_BEAST_381:FATj#CREATURE:FORGOTTEN_BEAST_381:TALLOWj CREATURE:FORGOTTEN_BEAST_382:FATj#CREATURE:FORGOTTEN_BEAST_382:TALLOWj CREATURE:FORGOTTEN_BEAST_383:FATj#CREATURE:FORGOTTEN_BEAST_383:TALLOWj CREATURE:FORGOTTEN_BEAST_384:FATj#CREATURE:FORGOTTEN_BEAST_384:TALLOWj CREATURE:FORGOTTEN_BEAST_385:FATj#CREATURE:FORGOTTEN_BEAST_385:TALLOWj CREATURE:FORGOTTEN_BEAST_386:FATj#CREATURE:FORGOTTEN_BEAST_386:TALLOWj CREATURE:FORGOTTEN_BEAST_387:FATj#CREATURE:FORGOTTEN_BEAST_387:TALLOWj CREATURE:FORGOTTEN_BEAST_388:FATj#CREATURE:FORGOTTEN_BEAST_388:TALLOWj CREATURE:FORGOTTEN_BEAST_389:FATj#CREATURE:FORGOTTEN_BEAST_389:TALLOWj CREATURE:FORGOTTEN_BEAST_390:FATj#CREATURE:FORGOTTEN_BEAST_390:TALLOWj CREATURE:FORGOTTEN_BEAST_391:FATj#CREATURE:FORGOTTEN_BEAST_391:TALLOWj CREATURE:FORGOTTEN_BEAST_392:FATj#CREATURE:FORGOTTEN_BEAST_392:TALLOWj CREATURE:FORGOTTEN_BEAST_393:FATj#CREATURE:FORGOTTEN_BEAST_393:TALLOWj CREATURE:FORGOTTEN_BEAST_396:FATj#CREATURE:FORGOTTEN_BEAST_396:TALLOWj CREATURE:FORGOTTEN_BEAST_397:FATj#CREATURE:FORGOTTEN_BEAST_397:TALLOWj CREATURE:FORGOTTEN_BEAST_398:FATj#CREATURE:FORGOTTEN_BEAST_398:TALLOWj CREATURE:FORGOTTEN_BEAST_400:FATj#CREATURE:FORGOTTEN_BEAST_400:TALLOWj CREATURE:FORGOTTEN_BEAST_403:FATj#CREATURE:FORGOTTEN_BEAST_403:TALLOWj CREATURE:FORGOTTEN_BEAST_404:FATj#CREATURE:FORGOTTEN_BEAST_404:TALLOWj CREATURE:FORGOTTEN_BEAST_405:FATj#CREATURE:FORGOTTEN_BEAST_405:TALLOWj CREATURE:FORGOTTEN_BEAST_406:FATj#CREATURE:FORGOTTEN_BEAST_406:TALLOWj CREATURE:FORGOTTEN_BEAST_407:FATj#CREATURE:FORGOTTEN_BEAST_407:TALLOWj CREATURE:FORGOTTEN_BEAST_408:FATj#CREATURE:FORGOTTEN_BEAST_408:TALLOWj CREATURE:FORGOTTEN_BEAST_409:FATj#CREATURE:FORGOTTEN_BEAST_409:TALLOWj CREATURE:FORGOTTEN_BEAST_410:FATj#CREATURE:FORGOTTEN_BEAST_410:TALLOWj CREATURE:FORGOTTEN_BEAST_411:FATj#CREATURE:FORGOTTEN_BEAST_411:TALLOWj CREATURE:FORGOTTEN_BEAST_412:FATj#CREATURE:FORGOTTEN_BEAST_412:TALLOWj CREATURE:FORGOTTEN_BEAST_413:FATj#CREATURE:FORGOTTEN_BEAST_413:TALLOWj CREATURE:FORGOTTEN_BEAST_414:FATj#CREATURE:FORGOTTEN_BEAST_414:TALLOWj CREATURE:FORGOTTEN_BEAST_416:FATj#CREATURE:FORGOTTEN_BEAST_416:TALLOWj CREATURE:FORGOTTEN_BEAST_417:FATj#CREATURE:FORGOTTEN_BEAST_417:TALLOWj CREATURE:FORGOTTEN_BEAST_418:FATj#CREATURE:FORGOTTEN_BEAST_418:TALLOWj CREATURE:FORGOTTEN_BEAST_420:FATj#CREATURE:FORGOTTEN_BEAST_420:TALLOWj CREATURE:FORGOTTEN_BEAST_421:FATj#CREATURE:FORGOTTEN_BEAST_421:TALLOWj CREATURE:FORGOTTEN_BEAST_422:FATj#CREATURE:FORGOTTEN_BEAST_422:TALLOWj CREATURE:FORGOTTEN_BEAST_423:FATj#CREATURE:FORGOTTEN_BEAST_423:TALLOWj CREATURE:FORGOTTEN_BEAST_424:FATj#CREATURE:FORGOTTEN_BEAST_424:TALLOWj CREATURE:FORGOTTEN_BEAST_427:FATj#CREATURE:FORGOTTEN_BEAST_427:TALLOWj CREATURE:FORGOTTEN_BEAST_429:FATj#CREATURE:FORGOTTEN_BEAST_429:TALLOWj CREATURE:FORGOTTEN_BEAST_430:FATj#CREATURE:FORGOTTEN_BEAST_430:TALLOWj CREATURE:FORGOTTEN_BEAST_432:FATj#CREATURE:FORGOTTEN_BEAST_432:TALLOWj CREATURE:FORGOTTEN_BEAST_433:FATj#CREATURE:FORGOTTEN_BEAST_433:TALLOWj CREATURE:FORGOTTEN_BEAST_434:FATj#CREATURE:FORGOTTEN_BEAST_434:TALLOWj CREATURE:FORGOTTEN_BEAST_435:FATj#CREATURE:FORGOTTEN_BEAST_435:TALLOWj CREATURE:FORGOTTEN_BEAST_436:FATj#CREATURE:FORGOTTEN_BEAST_436:TALLOWj CREATURE:FORGOTTEN_BEAST_437:FATj#CREATURE:FORGOTTEN_BEAST_437:TALLOWj CREATURE:FORGOTTEN_BEAST_438:FATj#CREATURE:FORGOTTEN_BEAST_438:TALLOWj CREATURE:FORGOTTEN_BEAST_440:FATj#CREATURE:FORGOTTEN_BEAST_440:TALLOWj CREATURE:FORGOTTEN_BEAST_441:FATj#CREATURE:FORGOTTEN_BEAST_441:TALLOWj CREATURE:FORGOTTEN_BEAST_442:FATj#CREATURE:FORGOTTEN_BEAST_442:TALLOWj CREATURE:FORGOTTEN_BEAST_444:FATj#CREATURE:FORGOTTEN_BEAST_444:TALLOWj CREATURE:FORGOTTEN_BEAST_446:FATj#CREATURE:FORGOTTEN_BEAST_446:TALLOWj CREATURE:FORGOTTEN_BEAST_447:FATj#CREATURE:FORGOTTEN_BEAST_447:TALLOWj CREATURE:FORGOTTEN_BEAST_448:FATj#CREATURE:FORGOTTEN_BEAST_448:TALLOWj CREATURE:FORGOTTEN_BEAST_449:FATj#CREATURE:FORGOTTEN_BEAST_449:TALLOWj CREATURE:FORGOTTEN_BEAST_450:FATj#CREATURE:FORGOTTEN_BEAST_450:TALLOWj CREATURE:FORGOTTEN_BEAST_451:FATj#CREATURE:FORGOTTEN_BEAST_451:TALLOWj CREATURE:FORGOTTEN_BEAST_453:FATj#CREATURE:FORGOTTEN_BEAST_453:TALLOWj CREATURE:FORGOTTEN_BEAST_454:FATj#CREATURE:FORGOTTEN_BEAST_454:TALLOWj CREATURE:FORGOTTEN_BEAST_455:FATj#CREATURE:FORGOTTEN_BEAST_455:TALLOWj CREATURE:FORGOTTEN_BEAST_457:FATj#CREATURE:FORGOTTEN_BEAST_457:TALLOWj CREATURE:FORGOTTEN_BEAST_459:FATj#CREATURE:FORGOTTEN_BEAST_459:TALLOWj CREATURE:FORGOTTEN_BEAST_461:FATj#CREATURE:FORGOTTEN_BEAST_461:TALLOWj CREATURE:FORGOTTEN_BEAST_462:FATj#CREATURE:FORGOTTEN_BEAST_462:TALLOWj CREATURE:FORGOTTEN_BEAST_463:FATj#CREATURE:FORGOTTEN_BEAST_463:TALLOWj CREATURE:FORGOTTEN_BEAST_465:FATj#CREATURE:FORGOTTEN_BEAST_465:TALLOWj CREATURE:FORGOTTEN_BEAST_466:FATj#CREATURE:FORGOTTEN_BEAST_466:TALLOWj CREATURE:FORGOTTEN_BEAST_468:FATj#CREATURE:FORGOTTEN_BEAST_468:TALLOWj CREATURE:FORGOTTEN_BEAST_469:FATj#CREATURE:FORGOTTEN_BEAST_469:TALLOWj CREATURE:FORGOTTEN_BEAST_470:FATj#CREATURE:FORGOTTEN_BEAST_470:TALLOWj CREATURE:FORGOTTEN_BEAST_471:FATj#CREATURE:FORGOTTEN_BEAST_471:TALLOWj CREATURE:FORGOTTEN_BEAST_472:FATj#CREATURE:FORGOTTEN_BEAST_472:TALLOWj CREATURE:FORGOTTEN_BEAST_474:FATj#CREATURE:FORGOTTEN_BEAST_474:TALLOWj CREATURE:FORGOTTEN_BEAST_475:FATj#CREATURE:FORGOTTEN_BEAST_475:TALLOWj CREATURE:FORGOTTEN_BEAST_476:FATj#CREATURE:FORGOTTEN_BEAST_476:TALLOWj CREATURE:FORGOTTEN_BEAST_478:FATj#CREATURE:FORGOTTEN_BEAST_478:TALLOWj CREATURE:FORGOTTEN_BEAST_479:FATj#CREATURE:FORGOTTEN_BEAST_479:TALLOWj CREATURE:FORGOTTEN_BEAST_480:FATj#CREATURE:FORGOTTEN_BEAST_480:TALLOWj CREATURE:FORGOTTEN_BEAST_481:FATj#CREATURE:FORGOTTEN_BEAST_481:TALLOWj CREATURE:FORGOTTEN_BEAST_483:FATj#CREATURE:FORGOTTEN_BEAST_483:TALLOWj CREATURE:FORGOTTEN_BEAST_486:FATj#CREATURE:FORGOTTEN_BEAST_486:TALLOWj CREATURE:FORGOTTEN_BEAST_487:FATj#CREATURE:FORGOTTEN_BEAST_487:TALLOWj CREATURE:FORGOTTEN_BEAST_489:FATj#CREATURE:FORGOTTEN_BEAST_489:TALLOWj CREATURE:FORGOTTEN_BEAST_492:FATj#CREATURE:FORGOTTEN_BEAST_492:TALLOWj CREATURE:FORGOTTEN_BEAST_494:FATj#CREATURE:FORGOTTEN_BEAST_494:TALLOWj CREATURE:FORGOTTEN_BEAST_495:FATj#CREATURE:FORGOTTEN_BEAST_495:TALLOWj CREATURE:FORGOTTEN_BEAST_496:FATj#CREATURE:FORGOTTEN_BEAST_496:TALLOWj CREATURE:FORGOTTEN_BEAST_497:FATj#CREATURE:FORGOTTEN_BEAST_497:TALLOWj CREATURE:FORGOTTEN_BEAST_498:FATj#CREATURE:FORGOTTEN_BEAST_498:TALLOWj CREATURE:FORGOTTEN_BEAST_499:FATj#CREATURE:FORGOTTEN_BEAST_499:TALLOWj CREATURE:FORGOTTEN_BEAST_501:FATj#CREATURE:FORGOTTEN_BEAST_501:TALLOWj CREATURE:FORGOTTEN_BEAST_503:FATj#CREATURE:FORGOTTEN_BEAST_503:TALLOWj CREATURE:FORGOTTEN_BEAST_504:FATj#CREATURE:FORGOTTEN_BEAST_504:TALLOWj CREATURE:FORGOTTEN_BEAST_505:FATj#CREATURE:FORGOTTEN_BEAST_505:TALLOWj CREATURE:FORGOTTEN_BEAST_507:FATj#CREATURE:FORGOTTEN_BEAST_507:TALLOWj CREATURE:FORGOTTEN_BEAST_508:FATj#CREATURE:FORGOTTEN_BEAST_508:TALLOWj CREATURE:FORGOTTEN_BEAST_510:FATj#CREATURE:FORGOTTEN_BEAST_510:TALLOWj CREATURE:FORGOTTEN_BEAST_512:FATj#CREATURE:FORGOTTEN_BEAST_512:TALLOWj CREATURE:FORGOTTEN_BEAST_513:FATj#CREATURE:FORGOTTEN_BEAST_513:TALLOWj CREATURE:FORGOTTEN_BEAST_514:FATj#CREATURE:FORGOTTEN_BEAST_514:TALLOWj CREATURE:FORGOTTEN_BEAST_515:FATj#CREATURE:FORGOTTEN_BEAST_515:TALLOWj CREATURE:FORGOTTEN_BEAST_516:FATj#CREATURE:FORGOTTEN_BEAST_516:TALLOWj CREATURE:FORGOTTEN_BEAST_517:FATj#CREATURE:FORGOTTEN_BEAST_517:TALLOWj CREATURE:FORGOTTEN_BEAST_518:FATj#CREATURE:FORGOTTEN_BEAST_518:TALLOWj CREATURE:FORGOTTEN_BEAST_519:FATj#CREATURE:FORGOTTEN_BEAST_519:TALLOWj CREATURE:FORGOTTEN_BEAST_520:FATj#CREATURE:FORGOTTEN_BEAST_520:TALLOWj CREATURE:FORGOTTEN_BEAST_521:FATj#CREATURE:FORGOTTEN_BEAST_521:TALLOWj CREATURE:FORGOTTEN_BEAST_522:FATj#CREATURE:FORGOTTEN_BEAST_522:TALLOWj CREATURE:FORGOTTEN_BEAST_523:FATj#CREATURE:FORGOTTEN_BEAST_523:TALLOWj CREATURE:FORGOTTEN_BEAST_525:FATj#CREATURE:FORGOTTEN_BEAST_525:TALLOWj CREATURE:FORGOTTEN_BEAST_526:FATj#CREATURE:FORGOTTEN_BEAST_526:TALLOWj CREATURE:FORGOTTEN_BEAST_527:FATj#CREATURE:FORGOTTEN_BEAST_527:TALLOWj CREATURE:FORGOTTEN_BEAST_528:FATj#CREATURE:FORGOTTEN_BEAST_528:TALLOWj CREATURE:FORGOTTEN_BEAST_529:FATj#CREATURE:FORGOTTEN_BEAST_529:TALLOWj CREATURE:FORGOTTEN_BEAST_530:FATj#CREATURE:FORGOTTEN_BEAST_530:TALLOWj CREATURE:FORGOTTEN_BEAST_531:FATj#CREATURE:FORGOTTEN_BEAST_531:TALLOWj CREATURE:FORGOTTEN_BEAST_532:FATj#CREATURE:FORGOTTEN_BEAST_532:TALLOWj CREATURE:FORGOTTEN_BEAST_533:FATj#CREATURE:FORGOTTEN_BEAST_533:TALLOWj CREATURE:FORGOTTEN_BEAST_534:FATj#CREATURE:FORGOTTEN_BEAST_534:TALLOWj CREATURE:FORGOTTEN_BEAST_535:FATj#CREATURE:FORGOTTEN_BEAST_535:TALLOWj CREATURE:FORGOTTEN_BEAST_536:FATj#CREATURE:FORGOTTEN_BEAST_536:TALLOWj CREATURE:FORGOTTEN_BEAST_539:FATj#CREATURE:FORGOTTEN_BEAST_539:TALLOWj CREATURE:FORGOTTEN_BEAST_540:FATj#CREATURE:FORGOTTEN_BEAST_540:TALLOWj CREATURE:FORGOTTEN_BEAST_541:FATj#CREATURE:FORGOTTEN_BEAST_541:TALLOWj CREATURE:FORGOTTEN_BEAST_543:FATj#CREATURE:FORGOTTEN_BEAST_543:TALLOWj CREATURE:FORGOTTEN_BEAST_545:FATj#CREATURE:FORGOTTEN_BEAST_545:TALLOWj CREATURE:FORGOTTEN_BEAST_546:FATj#CREATURE:FORGOTTEN_BEAST_546:TALLOWj CREATURE:FORGOTTEN_BEAST_547:FATj#CREATURE:FORGOTTEN_BEAST_547:TALLOWj CREATURE:FORGOTTEN_BEAST_548:FATj#CREATURE:FORGOTTEN_BEAST_548:TALLOWj CREATURE:FORGOTTEN_BEAST_549:FATj#CREATURE:FORGOTTEN_BEAST_549:TALLOWj CREATURE:FORGOTTEN_BEAST_550:FATj#CREATURE:FORGOTTEN_BEAST_550:TALLOWj CREATURE:FORGOTTEN_BEAST_551:FATj#CREATURE:FORGOTTEN_BEAST_551:TALLOWj CREATURE:FORGOTTEN_BEAST_554:FATj#CREATURE:FORGOTTEN_BEAST_554:TALLOWj CREATURE:FORGOTTEN_BEAST_555:FATj#CREATURE:FORGOTTEN_BEAST_555:TALLOWj CREATURE:FORGOTTEN_BEAST_556:FATj#CREATURE:FORGOTTEN_BEAST_556:TALLOWj CREATURE:FORGOTTEN_BEAST_557:FATj#CREATURE:FORGOTTEN_BEAST_557:TALLOWj CREATURE:FORGOTTEN_BEAST_561:FATj#CREATURE:FORGOTTEN_BEAST_561:TALLOWj CREATURE:FORGOTTEN_BEAST_562:FATj#CREATURE:FORGOTTEN_BEAST_562:TALLOWj CREATURE:FORGOTTEN_BEAST_564:FATj#CREATURE:FORGOTTEN_BEAST_564:TALLOWj CREATURE:FORGOTTEN_BEAST_569:FATj#CREATURE:FORGOTTEN_BEAST_569:TALLOWj CREATURE:FORGOTTEN_BEAST_570:FATj#CREATURE:FORGOTTEN_BEAST_570:TALLOWj CREATURE:FORGOTTEN_BEAST_571:FATj#CREATURE:FORGOTTEN_BEAST_571:TALLOWj CREATURE:FORGOTTEN_BEAST_572:FATj#CREATURE:FORGOTTEN_BEAST_572:TALLOWj CREATURE:FORGOTTEN_BEAST_574:FATj#CREATURE:FORGOTTEN_BEAST_574:TALLOWj CREATURE:FORGOTTEN_BEAST_576:FATj#CREATURE:FORGOTTEN_BEAST_576:TALLOWj CREATURE:FORGOTTEN_BEAST_578:FATj#CREATURE:FORGOTTEN_BEAST_578:TALLOWj CREATURE:FORGOTTEN_BEAST_579:FATj#CREATURE:FORGOTTEN_BEAST_579:TALLOWj CREATURE:FORGOTTEN_BEAST_580:FATj#CREATURE:FORGOTTEN_BEAST_580:TALLOWj CREATURE:FORGOTTEN_BEAST_581:FATj#CREATURE:FORGOTTEN_BEAST_581:TALLOWj CREATURE:FORGOTTEN_BEAST_583:FATj#CREATURE:FORGOTTEN_BEAST_583:TALLOWj CREATURE:FORGOTTEN_BEAST_584:FATj#CREATURE:FORGOTTEN_BEAST_584:TALLOWj CREATURE:FORGOTTEN_BEAST_586:FATj#CREATURE:FORGOTTEN_BEAST_586:TALLOWj CREATURE:FORGOTTEN_BEAST_588:FATj#CREATURE:FORGOTTEN_BEAST_588:TALLOWj CREATURE:FORGOTTEN_BEAST_589:FATj#CREATURE:FORGOTTEN_BEAST_589:TALLOWj CREATURE:FORGOTTEN_BEAST_590:FATj#CREATURE:FORGOTTEN_BEAST_590:TALLOWj CREATURE:FORGOTTEN_BEAST_592:FATj#CREATURE:FORGOTTEN_BEAST_592:TALLOWj CREATURE:FORGOTTEN_BEAST_593:FATj#CREATURE:FORGOTTEN_BEAST_593:TALLOWj CREATURE:FORGOTTEN_BEAST_594:FATj#CREATURE:FORGOTTEN_BEAST_594:TALLOWj CREATURE:FORGOTTEN_BEAST_595:FATj#CREATURE:FORGOTTEN_BEAST_595:TALLOWj CREATURE:FORGOTTEN_BEAST_596:FATj#CREATURE:FORGOTTEN_BEAST_596:TALLOWj CREATURE:FORGOTTEN_BEAST_597:FATj#CREATURE:FORGOTTEN_BEAST_597:TALLOWj CREATURE:FORGOTTEN_BEAST_599:FATj#CREATURE:FORGOTTEN_BEAST_599:TALLOWj CREATURE:FORGOTTEN_BEAST_600:FATj#CREATURE:FORGOTTEN_BEAST_600:TALLOWj CREATURE:FORGOTTEN_BEAST_601:FATj#CREATURE:FORGOTTEN_BEAST_601:TALLOWj CREATURE:FORGOTTEN_BEAST_603:FATj#CREATURE:FORGOTTEN_BEAST_603:TALLOWj CREATURE:FORGOTTEN_BEAST_605:FATj#CREATURE:FORGOTTEN_BEAST_605:TALLOWj CREATURE:FORGOTTEN_BEAST_607:FATj#CREATURE:FORGOTTEN_BEAST_607:TALLOWj CREATURE:FORGOTTEN_BEAST_608:FATj#CREATURE:FORGOTTEN_BEAST_608:TALLOWj CREATURE:FORGOTTEN_BEAST_609:FATj#CREATURE:FORGOTTEN_BEAST_609:TALLOWj CREATURE:FORGOTTEN_BEAST_610:FATj#CREATURE:FORGOTTEN_BEAST_610:TALLOWj CREATURE:FORGOTTEN_BEAST_611:FATj#CREATURE:FORGOTTEN_BEAST_611:TALLOWj CREATURE:FORGOTTEN_BEAST_612:FATj#CREATURE:FORGOTTEN_BEAST_612:TALLOWj CREATURE:FORGOTTEN_BEAST_613:FATj#CREATURE:FORGOTTEN_BEAST_613:TALLOWj CREATURE:FORGOTTEN_BEAST_614:FATj#CREATURE:FORGOTTEN_BEAST_614:TALLOWj CREATURE:FORGOTTEN_BEAST_616:FATj#CREATURE:FORGOTTEN_BEAST_616:TALLOWj CREATURE:FORGOTTEN_BEAST_619:FATj#CREATURE:FORGOTTEN_BEAST_619:TALLOWj CREATURE:FORGOTTEN_BEAST_620:FATj#CREATURE:FORGOTTEN_BEAST_620:TALLOWj CREATURE:FORGOTTEN_BEAST_621:FATj#CREATURE:FORGOTTEN_BEAST_621:TALLOWj CREATURE:FORGOTTEN_BEAST_623:FATj#CREATURE:FORGOTTEN_BEAST_623:TALLOWj CREATURE:FORGOTTEN_BEAST_624:FATj#CREATURE:FORGOTTEN_BEAST_624:TALLOWj CREATURE:FORGOTTEN_BEAST_625:FATj#CREATURE:FORGOTTEN_BEAST_625:TALLOWj CREATURE:FORGOTTEN_BEAST_626:FATj#CREATURE:FORGOTTEN_BEAST_626:TALLOWj CREATURE:FORGOTTEN_BEAST_627:FATj#CREATURE:FORGOTTEN_BEAST_627:TALLOWj CREATURE:FORGOTTEN_BEAST_628:FATj#CREATURE:FORGOTTEN_BEAST_628:TALLOWj CREATURE:FORGOTTEN_BEAST_629:FATj#CREATURE:FORGOTTEN_BEAST_629:TALLOWj CREATURE:FORGOTTEN_BEAST_630:FATj#CREATURE:FORGOTTEN_BEAST_630:TALLOWj CREATURE:FORGOTTEN_BEAST_631:FATj#CREATURE:FORGOTTEN_BEAST_631:TALLOWj CREATURE:FORGOTTEN_BEAST_632:FATj#CREATURE:FORGOTTEN_BEAST_632:TALLOWj CREATURE:FORGOTTEN_BEAST_633:FATj#CREATURE:FORGOTTEN_BEAST_633:TALLOWj CREATURE:FORGOTTEN_BEAST_634:FATj#CREATURE:FORGOTTEN_BEAST_634:TALLOWj CREATURE:FORGOTTEN_BEAST_635:FATj#CREATURE:FORGOTTEN_BEAST_635:TALLOWj CREATURE:FORGOTTEN_BEAST_636:FATj#CREATURE:FORGOTTEN_BEAST_636:TALLOWj CREATURE:FORGOTTEN_BEAST_637:FATj#CREATURE:FORGOTTEN_BEAST_637:TALLOWj CREATURE:FORGOTTEN_BEAST_639:FATj#CREATURE:FORGOTTEN_BEAST_639:TALLOWj CREATURE:FORGOTTEN_BEAST_640:FATj#CREATURE:FORGOTTEN_BEAST_640:TALLOWj CREATURE:FORGOTTEN_BEAST_643:FATj#CREATURE:FORGOTTEN_BEAST_643:TALLOWj CREATURE:FORGOTTEN_BEAST_644:FATj#CREATURE:FORGOTTEN_BEAST_644:TALLOWj CREATURE:FORGOTTEN_BEAST_645:FATj#CREATURE:FORGOTTEN_BEAST_645:TALLOWj CREATURE:FORGOTTEN_BEAST_646:FATj#CREATURE:FORGOTTEN_BEAST_646:TALLOWj CREATURE:FORGOTTEN_BEAST_647:FATj#CREATURE:FORGOTTEN_BEAST_647:TALLOWj CREATURE:FORGOTTEN_BEAST_649:FATj#CREATURE:FORGOTTEN_BEAST_649:TALLOWj CREATURE:FORGOTTEN_BEAST_650:FATj#CREATURE:FORGOTTEN_BEAST_650:TALLOWj CREATURE:FORGOTTEN_BEAST_651:FATj#CREATURE:FORGOTTEN_BEAST_651:TALLOWj CREATURE:FORGOTTEN_BEAST_652:FATj#CREATURE:FORGOTTEN_BEAST_652:TALLOWj CREATURE:FORGOTTEN_BEAST_656:FATj#CREATURE:FORGOTTEN_BEAST_656:TALLOWj CREATURE:FORGOTTEN_BEAST_658:FATj#CREATURE:FORGOTTEN_BEAST_658:TALLOWj CREATURE:FORGOTTEN_BEAST_659:FATj#CREATURE:FORGOTTEN_BEAST_659:TALLOWj CREATURE:FORGOTTEN_BEAST_661:FATj#CREATURE:FORGOTTEN_BEAST_661:TALLOWj CREATURE:FORGOTTEN_BEAST_663:FATj#CREATURE:FORGOTTEN_BEAST_663:TALLOWj CREATURE:FORGOTTEN_BEAST_664:FATj#CREATURE:FORGOTTEN_BEAST_664:TALLOWj CREATURE:FORGOTTEN_BEAST_666:FATj#CREATURE:FORGOTTEN_BEAST_666:TALLOWj CREATURE:FORGOTTEN_BEAST_667:FATj#CREATURE:FORGOTTEN_BEAST_667:TALLOWj CREATURE:FORGOTTEN_BEAST_669:FATj#CREATURE:FORGOTTEN_BEAST_669:TALLOWj CREATURE:FORGOTTEN_BEAST_670:FATj#CREATURE:FORGOTTEN_BEAST_670:TALLOWj CREATURE:FORGOTTEN_BEAST_671:FATj#CREATURE:FORGOTTEN_BEAST_671:TALLOWj CREATURE:FORGOTTEN_BEAST_674:FATj#CREATURE:FORGOTTEN_BEAST_674:TALLOWj CREATURE:FORGOTTEN_BEAST_675:FATj#CREATURE:FORGOTTEN_BEAST_675:TALLOWj CREATURE:FORGOTTEN_BEAST_678:FATj#CREATURE:FORGOTTEN_BEAST_678:TALLOWj CREATURE:FORGOTTEN_BEAST_680:FATj#CREATURE:FORGOTTEN_BEAST_680:TALLOWj CREATURE:FORGOTTEN_BEAST_681:FATj#CREATURE:FORGOTTEN_BEAST_681:TALLOWj CREATURE:FORGOTTEN_BEAST_682:FATj#CREATURE:FORGOTTEN_BEAST_682:TALLOWj CREATURE:FORGOTTEN_BEAST_684:FATj#CREATURE:FORGOTTEN_BEAST_684:TALLOWj CREATURE:FORGOTTEN_BEAST_685:FATj#CREATURE:FORGOTTEN_BEAST_685:TALLOWj CREATURE:FORGOTTEN_BEAST_686:FATj#CREATURE:FORGOTTEN_BEAST_686:TALLOWj CREATURE:FORGOTTEN_BEAST_687:FATj#CREATURE:FORGOTTEN_BEAST_687:TALLOWj CREATURE:FORGOTTEN_BEAST_688:FATj#CREATURE:FORGOTTEN_BEAST_688:TALLOWj CREATURE:FORGOTTEN_BEAST_689:FATj#CREATURE:FORGOTTEN_BEAST_689:TALLOWj CREATURE:FORGOTTEN_BEAST_691:FATj#CREATURE:FORGOTTEN_BEAST_691:TALLOWj CREATURE:FORGOTTEN_BEAST_692:FATj#CREATURE:FORGOTTEN_BEAST_692:TALLOWj CREATURE:FORGOTTEN_BEAST_695:FATj#CREATURE:FORGOTTEN_BEAST_695:TALLOWj CREATURE:FORGOTTEN_BEAST_696:FATj#CREATURE:FORGOTTEN_BEAST_696:TALLOWj CREATURE:FORGOTTEN_BEAST_697:FATj#CREATURE:FORGOTTEN_BEAST_697:TALLOWj CREATURE:FORGOTTEN_BEAST_698:FATj#CREATURE:FORGOTTEN_BEAST_698:TALLOWj CREATURE:FORGOTTEN_BEAST_699:FATj#CREATURE:FORGOTTEN_BEAST_699:TALLOWj CREATURE:FORGOTTEN_BEAST_700:FATj#CREATURE:FORGOTTEN_BEAST_700:TALLOWj CREATURE:FORGOTTEN_BEAST_701:FATj#CREATURE:FORGOTTEN_BEAST_701:TALLOWj CREATURE:FORGOTTEN_BEAST_702:FATj#CREATURE:FORGOTTEN_BEAST_702:TALLOWj CREATURE:FORGOTTEN_BEAST_704:FATj#CREATURE:FORGOTTEN_BEAST_704:TALLOWj CREATURE:FORGOTTEN_BEAST_706:FATj#CREATURE:FORGOTTEN_BEAST_706:TALLOWj CREATURE:FORGOTTEN_BEAST_708:FATj#CREATURE:FORGOTTEN_BEAST_708:TALLOWj CREATURE:FORGOTTEN_BEAST_709:FATj#CREATURE:FORGOTTEN_BEAST_709:TALLOWj CREATURE:FORGOTTEN_BEAST_711:FATj#CREATURE:FORGOTTEN_BEAST_711:TALLOWj CREATURE:FORGOTTEN_BEAST_712:FATj#CREATURE:FORGOTTEN_BEAST_712:TALLOWj CREATURE:FORGOTTEN_BEAST_713:FATj#CREATURE:FORGOTTEN_BEAST_713:TALLOWj CREATURE:FORGOTTEN_BEAST_714:FATj#CREATURE:FORGOTTEN_BEAST_714:TALLOWj CREATURE:FORGOTTEN_BEAST_715:FATj#CREATURE:FORGOTTEN_BEAST_715:TALLOWj CREATURE:FORGOTTEN_BEAST_717:FATj#CREATURE:FORGOTTEN_BEAST_717:TALLOWj CREATURE:FORGOTTEN_BEAST_718:FATj#CREATURE:FORGOTTEN_BEAST_718:TALLOWj CREATURE:FORGOTTEN_BEAST_719:FATj#CREATURE:FORGOTTEN_BEAST_719:TALLOWj CREATURE:FORGOTTEN_BEAST_720:FATj#CREATURE:FORGOTTEN_BEAST_720:TALLOWj CREATURE:FORGOTTEN_BEAST_721:FATj#CREATURE:FORGOTTEN_BEAST_721:TALLOWj CREATURE:FORGOTTEN_BEAST_722:FATj#CREATURE:FORGOTTEN_BEAST_722:TALLOWj CREATURE:FORGOTTEN_BEAST_723:FATj#CREATURE:FORGOTTEN_BEAST_723:TALLOWj CREATURE:FORGOTTEN_BEAST_724:FATj#CREATURE:FORGOTTEN_BEAST_724:TALLOWj CREATURE:FORGOTTEN_BEAST_726:FATj#CREATURE:FORGOTTEN_BEAST_726:TALLOWj CREATURE:FORGOTTEN_BEAST_730:FATj#CREATURE:FORGOTTEN_BEAST_730:TALLOWj CREATURE:FORGOTTEN_BEAST_731:FATj#CREATURE:FORGOTTEN_BEAST_731:TALLOWj CREATURE:FORGOTTEN_BEAST_734:FATj#CREATURE:FORGOTTEN_BEAST_734:TALLOWj CREATURE:FORGOTTEN_BEAST_735:FATj#CREATURE:FORGOTTEN_BEAST_735:TALLOWj CREATURE:FORGOTTEN_BEAST_737:FATj#CREATURE:FORGOTTEN_BEAST_737:TALLOWj CREATURE:FORGOTTEN_BEAST_738:FATj#CREATURE:FORGOTTEN_BEAST_738:TALLOWj CREATURE:FORGOTTEN_BEAST_739:FATj#CREATURE:FORGOTTEN_BEAST_739:TALLOWj CREATURE:FORGOTTEN_BEAST_740:FATj#CREATURE:FORGOTTEN_BEAST_740:TALLOWj CREATURE:FORGOTTEN_BEAST_741:FATj#CREATURE:FORGOTTEN_BEAST_741:TALLOWj CREATURE:FORGOTTEN_BEAST_742:FATj#CREATURE:FORGOTTEN_BEAST_742:TALLOWj CREATURE:FORGOTTEN_BEAST_743:FATj#CREATURE:FORGOTTEN_BEAST_743:TALLOWj CREATURE:FORGOTTEN_BEAST_744:FATj#CREATURE:FORGOTTEN_BEAST_744:TALLOWj CREATURE:FORGOTTEN_BEAST_746:FATj#CREATURE:FORGOTTEN_BEAST_746:TALLOWj CREATURE:FORGOTTEN_BEAST_747:FATj#CREATURE:FORGOTTEN_BEAST_747:TALLOWj CREATURE:FORGOTTEN_BEAST_749:FATj#CREATURE:FORGOTTEN_BEAST_749:TALLOWj CREATURE:FORGOTTEN_BEAST_750:FATj#CREATURE:FORGOTTEN_BEAST_750:TALLOWj CREATURE:FORGOTTEN_BEAST_751:FATj#CREATURE:FORGOTTEN_BEAST_751:TALLOWj CREATURE:FORGOTTEN_BEAST_752:FATj#CREATURE:FORGOTTEN_BEAST_752:TALLOWj CREATURE:FORGOTTEN_BEAST_753:FATj#CREATURE:FORGOTTEN_BEAST_753:TALLOWj CREATURE:FORGOTTEN_BEAST_754:FATj#CREATURE:FORGOTTEN_BEAST_754:TALLOWj CREATURE:FORGOTTEN_BEAST_755:FATj#CREATURE:FORGOTTEN_BEAST_755:TALLOWj CREATURE:FORGOTTEN_BEAST_756:FATj#CREATURE:FORGOTTEN_BEAST_756:TALLOWj CREATURE:FORGOTTEN_BEAST_757:FATj#CREATURE:FORGOTTEN_BEAST_757:TALLOWj CREATURE:FORGOTTEN_BEAST_758:FATj#CREATURE:FORGOTTEN_BEAST_758:TALLOWj CREATURE:FORGOTTEN_BEAST_759:FATj#CREATURE:FORGOTTEN_BEAST_759:TALLOWj CREATURE:FORGOTTEN_BEAST_760:FATj#CREATURE:FORGOTTEN_BEAST_760:TALLOWj CREATURE:FORGOTTEN_BEAST_761:FATj#CREATURE:FORGOTTEN_BEAST_761:TALLOWj CREATURE:FORGOTTEN_BEAST_762:FATj#CREATURE:FORGOTTEN_BEAST_762:TALLOWj CREATURE:FORGOTTEN_BEAST_764:FATj#CREATURE:FORGOTTEN_BEAST_764:TALLOWj CREATURE:FORGOTTEN_BEAST_767:FATj#CREATURE:FORGOTTEN_BEAST_767:TALLOWj CREATURE:FORGOTTEN_BEAST_768:FATj#CREATURE:FORGOTTEN_BEAST_768:TALLOWj CREATURE:FORGOTTEN_BEAST_769:FATj#CREATURE:FORGOTTEN_BEAST_769:TALLOWj CREATURE:FORGOTTEN_BEAST_770:FATj#CREATURE:FORGOTTEN_BEAST_770:TALLOWj CREATURE:FORGOTTEN_BEAST_773:FATj#CREATURE:FORGOTTEN_BEAST_773:TALLOWj CREATURE:FORGOTTEN_BEAST_774:FATj#CREATURE:FORGOTTEN_BEAST_774:TALLOWj CREATURE:FORGOTTEN_BEAST_776:FATj#CREATURE:FORGOTTEN_BEAST_776:TALLOWj CREATURE:FORGOTTEN_BEAST_777:FATj#CREATURE:FORGOTTEN_BEAST_777:TALLOWj CREATURE:FORGOTTEN_BEAST_778:FATj#CREATURE:FORGOTTEN_BEAST_778:TALLOWj CREATURE:FORGOTTEN_BEAST_779:FATj#CREATURE:FORGOTTEN_BEAST_779:TALLOWj CREATURE:FORGOTTEN_BEAST_780:FATj#CREATURE:FORGOTTEN_BEAST_780:TALLOWj CREATURE:FORGOTTEN_BEAST_781:FATj#CREATURE:FORGOTTEN_BEAST_781:TALLOWj CREATURE:FORGOTTEN_BEAST_782:FATj#CREATURE:FORGOTTEN_BEAST_782:TALLOWj CREATURE:FORGOTTEN_BEAST_783:FATj#CREATURE:FORGOTTEN_BEAST_783:TALLOWj CREATURE:FORGOTTEN_BEAST_784:FATj#CREATURE:FORGOTTEN_BEAST_784:TALLOWj CREATURE:FORGOTTEN_BEAST_785:FATj#CREATURE:FORGOTTEN_BEAST_785:TALLOWj CREATURE:FORGOTTEN_BEAST_786:FATj#CREATURE:FORGOTTEN_BEAST_786:TALLOWj CREATURE:FORGOTTEN_BEAST_787:FATj#CREATURE:FORGOTTEN_BEAST_787:TALLOWj CREATURE:FORGOTTEN_BEAST_789:FATj#CREATURE:FORGOTTEN_BEAST_789:TALLOWj CREATURE:FORGOTTEN_BEAST_792:FATj#CREATURE:FORGOTTEN_BEAST_792:TALLOWj CREATURE:FORGOTTEN_BEAST_793:FATj#CREATURE:FORGOTTEN_BEAST_793:TALLOWj CREATURE:FORGOTTEN_BEAST_794:FATj#CREATURE:FORGOTTEN_BEAST_794:TALLOWj CREATURE:FORGOTTEN_BEAST_795:FATj#CREATURE:FORGOTTEN_BEAST_795:TALLOWj CREATURE:FORGOTTEN_BEAST_796:FATj#CREATURE:FORGOTTEN_BEAST_796:TALLOWj CREATURE:FORGOTTEN_BEAST_799:FATj#CREATURE:FORGOTTEN_BEAST_799:TALLOWj CREATURE:FORGOTTEN_BEAST_800:FATj#CREATURE:FORGOTTEN_BEAST_800:TALLOWj CREATURE:FORGOTTEN_BEAST_801:FATj#CREATURE:FORGOTTEN_BEAST_801:TALLOWj CREATURE:FORGOTTEN_BEAST_802:FATj#CREATURE:FORGOTTEN_BEAST_802:TALLOWj CREATURE:FORGOTTEN_BEAST_803:FATj#CREATURE:FORGOTTEN_BEAST_803:TALLOWj CREATURE:FORGOTTEN_BEAST_804:FATj#CREATURE:FORGOTTEN_BEAST_804:TALLOWj CREATURE:FORGOTTEN_BEAST_806:FATj#CREATURE:FORGOTTEN_BEAST_806:TALLOWj CREATURE:FORGOTTEN_BEAST_807:FATj#CREATURE:FORGOTTEN_BEAST_807:TALLOWj CREATURE:FORGOTTEN_BEAST_809:FATj#CREATURE:FORGOTTEN_BEAST_809:TALLOWj CREATURE:FORGOTTEN_BEAST_810:FATj#CREATURE:FORGOTTEN_BEAST_810:TALLOWj CREATURE:FORGOTTEN_BEAST_811:FATj#CREATURE:FORGOTTEN_BEAST_811:TALLOWj CREATURE:FORGOTTEN_BEAST_812:FATj#CREATURE:FORGOTTEN_BEAST_812:TALLOWj CREATURE:FORGOTTEN_BEAST_815:FATj#CREATURE:FORGOTTEN_BEAST_815:TALLOWj CREATURE:FORGOTTEN_BEAST_817:FATj#CREATURE:FORGOTTEN_BEAST_817:TALLOWj CREATURE:FORGOTTEN_BEAST_818:FATj#CREATURE:FORGOTTEN_BEAST_818:TALLOWj CREATURE:FORGOTTEN_BEAST_819:FATj#CREATURE:FORGOTTEN_BEAST_819:TALLOWj CREATURE:FORGOTTEN_BEAST_820:FATj#CREATURE:FORGOTTEN_BEAST_820:TALLOWj CREATURE:FORGOTTEN_BEAST_821:FATj#CREATURE:FORGOTTEN_BEAST_821:TALLOWj CREATURE:FORGOTTEN_BEAST_822:FATj#CREATURE:FORGOTTEN_BEAST_822:TALLOWj CREATURE:FORGOTTEN_BEAST_824:FATj#CREATURE:FORGOTTEN_BEAST_824:TALLOWj CREATURE:FORGOTTEN_BEAST_825:FATj#CREATURE:FORGOTTEN_BEAST_825:TALLOWj CREATURE:FORGOTTEN_BEAST_826:FATj#CREATURE:FORGOTTEN_BEAST_826:TALLOWj CREATURE:FORGOTTEN_BEAST_827:FATj#CREATURE:FORGOTTEN_BEAST_827:TALLOWj CREATURE:FORGOTTEN_BEAST_828:FATj#CREATURE:FORGOTTEN_BEAST_828:TALLOWj CREATURE:FORGOTTEN_BEAST_831:FATj#CREATURE:FORGOTTEN_BEAST_831:TALLOWj CREATURE:FORGOTTEN_BEAST_833:FATj#CREATURE:FORGOTTEN_BEAST_833:TALLOWj CREATURE:FORGOTTEN_BEAST_835:FATj#CREATURE:FORGOTTEN_BEAST_835:TALLOWj CREATURE:FORGOTTEN_BEAST_837:FATj#CREATURE:FORGOTTEN_BEAST_837:TALLOWj CREATURE:FORGOTTEN_BEAST_838:FATj#CREATURE:FORGOTTEN_BEAST_838:TALLOWj CREATURE:FORGOTTEN_BEAST_842:FATj#CREATURE:FORGOTTEN_BEAST_842:TALLOWj CREATURE:FORGOTTEN_BEAST_843:FATj#CREATURE:FORGOTTEN_BEAST_843:TALLOWj CREATURE:FORGOTTEN_BEAST_844:FATj#CREATURE:FORGOTTEN_BEAST_844:TALLOWj CREATURE:FORGOTTEN_BEAST_845:FATj#CREATURE:FORGOTTEN_BEAST_845:TALLOWj CREATURE:FORGOTTEN_BEAST_846:FATj#CREATURE:FORGOTTEN_BEAST_846:TALLOWj CREATURE:FORGOTTEN_BEAST_847:FATj#CREATURE:FORGOTTEN_BEAST_847:TALLOWj CREATURE:FORGOTTEN_BEAST_848:FATj#CREATURE:FORGOTTEN_BEAST_848:TALLOWj CREATURE:FORGOTTEN_BEAST_849:FATj#CREATURE:FORGOTTEN_BEAST_849:TALLOWj CREATURE:FORGOTTEN_BEAST_851:FATj#CREATURE:FORGOTTEN_BEAST_851:TALLOWj CREATURE:FORGOTTEN_BEAST_853:FATj#CREATURE:FORGOTTEN_BEAST_853:TALLOWj CREATURE:FORGOTTEN_BEAST_854:FATj#CREATURE:FORGOTTEN_BEAST_854:TALLOWj CREATURE:FORGOTTEN_BEAST_855:FATj#CREATURE:FORGOTTEN_BEAST_855:TALLOWj CREATURE:FORGOTTEN_BEAST_857:FATj#CREATURE:FORGOTTEN_BEAST_857:TALLOWj CREATURE:FORGOTTEN_BEAST_858:FATj#CREATURE:FORGOTTEN_BEAST_858:TALLOWj CREATURE:FORGOTTEN_BEAST_859:FATj#CREATURE:FORGOTTEN_BEAST_859:TALLOWj CREATURE:FORGOTTEN_BEAST_860:FATj#CREATURE:FORGOTTEN_BEAST_860:TALLOWj CREATURE:FORGOTTEN_BEAST_861:FATj#CREATURE:FORGOTTEN_BEAST_861:TALLOWj CREATURE:FORGOTTEN_BEAST_862:FATj#CREATURE:FORGOTTEN_BEAST_862:TALLOWj CREATURE:FORGOTTEN_BEAST_865:FATj#CREATURE:FORGOTTEN_BEAST_865:TALLOWj CREATURE:FORGOTTEN_BEAST_866:FATj#CREATURE:FORGOTTEN_BEAST_866:TALLOWj CREATURE:FORGOTTEN_BEAST_867:FATj#CREATURE:FORGOTTEN_BEAST_867:TALLOWjCREATURE:TITAN_1:FATjCREATURE:TITAN_1:TALLOWjCREATURE:TITAN_3:FATjCREATURE:TITAN_3:TALLOWjCREATURE:TITAN_4:FATjCREATURE:TITAN_4:TALLOWjCREATURE:TITAN_6:FATjCREATURE:TITAN_6:TALLOWjCREATURE:TITAN_7:FATjCREATURE:TITAN_7:TALLOWjCREATURE:TITAN_8:FATjCREATURE:TITAN_8:TALLOWjCREATURE:TITAN_9:FATjCREATURE:TITAN_9:TALLOWjCREATURE:TITAN_10:FATjCREATURE:TITAN_10:TALLOWjCREATURE:TITAN_11:FATjCREATURE:TITAN_11:TALLOWjCREATURE:TITAN_12:FATjCREATURE:TITAN_12:TALLOWjCREATURE:TITAN_13:FATjCREATURE:TITAN_13:TALLOWjCREATURE:TITAN_14:FATjCREATURE:TITAN_14:TALLOWjCREATURE:TITAN_15:FATjCREATURE:TITAN_15:TALLOWjCREATURE:TITAN_18:FATjCREATURE:TITAN_18:TALLOWjCREATURE:TITAN_19:FATjCREATURE:TITAN_19:TALLOWjCREATURE:TITAN_21:FATjCREATURE:TITAN_21:TALLOWjCREATURE:TITAN_23:FATjCREATURE:TITAN_23:TALLOWjCREATURE:TITAN_24:FATjCREATURE:TITAN_24:TALLOWjCREATURE:TITAN_25:FATjCREATURE:TITAN_25:TALLOWjCREATURE:TITAN_26:FATjCREATURE:TITAN_26:TALLOWjCREATURE:TITAN_27:FATjCREATURE:TITAN_27:TALLOWjCREATURE:TITAN_28:FATjCREATURE:TITAN_28:TALLOWjCREATURE:TITAN_29:FATjCREATURE:TITAN_29:TALLOWjCREATURE:TITAN_30:FATjCREATURE:TITAN_30:TALLOWjCREATURE:TITAN_32:FATjCREATURE:TITAN_32:TALLOWjCREATURE:TITAN_33:FATjCREATURE:TITAN_33:TALLOWjCREATURE:DEMON_7:FATjCREATURE:DEMON_7:TALLOWjCREATURE:DEMON_8:FATjCREATURE:DEMON_8:TALLOWjCREATURE:DEMON_9:FATjCREATURE:DEMON_9:TALLOWjCREATURE:DEMON_10:FATjCREATURE:DEMON_10:TALLOWjCREATURE:DEMON_11:FATjCREATURE:DEMON_11:TALLOWjCREATURE:DEMON_12:FATjCREATURE:DEMON_12:TALLOWjCREATURE:DEMON_13:FATjCREATURE:DEMON_13:TALLOWjCREATURE:DEMON_14:FATjCREATURE:DEMON_14:TALLOWjCREATURE:DEMON_15:FATjCREATURE:DEMON_15:TALLOWjCREATURE:DEMON_16:FATjCREATURE:DEMON_16:TALLOWjCREATURE:DEMON_17:FATjCREATURE:DEMON_17:TALLOWjCREATURE:DEMON_18:FATjCREATURE:DEMON_18:TALLOWjCREATURE:DEMON_19:FATjCREATURE:DEMON_19:TALLOWjCREATURE:DEMON_20:FATjCREATURE:DEMON_20:TALLOWjCREATURE:DEMON_21:FATjCREATURE:DEMON_21:TALLOWjCREATURE:DEMON_22:FATjCREATURE:DEMON_22:TALLOWjCREATURE:DEMON_23:FATjCREATURE:DEMON_23:TALLOWjCREATURE:DEMON_24:FATjCREATURE:DEMON_24:TALLOWjCREATURE:DEMON_25:FATjCREATURE:DEMON_25:TALLOWjCREATURE:DEMON_26:FATjCREATURE:DEMON_26:TALLOWjCREATURE:DEMON_27:FATjCREATURE:DEMON_27:TALLOWjCREATURE:DEMON_28:FATjCREATURE:DEMON_28:TALLOWjCREATURE:DEMON_29:FATjCREATURE:DEMON_29:TALLOWjCREATURE:DEMON_31:FATjCREATURE:DEMON_31:TALLOWjCREATURE:DEMON_32:FATjCREATURE:DEMON_32:TALLOWjCREATURE:DEMON_38:FATjCREATURE:DEMON_38:TALLOWjCREATURE:DEMON_39:FATjCREATURE:DEMON_39:TALLOWjCREATURE:DEMON_40:FATjCREATURE:DEMON_40:TALLOWjCREATURE:DEMON_41:FATjCREATURE:DEMON_41:TALLOWjCREATURE:DEMON_42:FATjCREATURE:DEMON_42:TALLOWjCREATURE:DEMON_43:FATjCREATURE:DEMON_43:TALLOWjCREATURE:DEMON_44:FATjCREATURE:DEMON_44:TALLOWjCREATURE:DEMON_45:FATjCREATURE:DEMON_45:TALLOWjCREATURE:DEMON_47:FATjCREATURE:DEMON_47:TALLOWjCREATURE:DEMON_48:FATjCREATURE:DEMON_48:TALLOWjCREATURE:DEMON_49:FATjCREATURE:DEMON_49:TALLOWjCREATURE:DEMON_52:FATjCREATURE:DEMON_52:TALLOWjCREATURE:NIGHT_CREATURE_1:FATj CREATURE:NIGHT_CREATURE_1:TALLOWjCREATURE:NIGHT_CREATURE_2:FATj CREATURE:NIGHT_CREATURE_2:TALLOWjCREATURE:NIGHT_CREATURE_3:FATj CREATURE:NIGHT_CREATURE_3:TALLOWjCREATURE:NIGHT_CREATURE_4:FATj CREATURE:NIGHT_CREATURE_4:TALLOWjCREATURE:NIGHT_CREATURE_5:FATj CREATURE:NIGHT_CREATURE_5:TALLOWjCREATURE:NIGHT_CREATURE_6:FATj CREATURE:NIGHT_CREATURE_6:TALLOWjCREATURE:NIGHT_CREATURE_7:FATj CREATURE:NIGHT_CREATURE_7:TALLOWjCREATURE:NIGHT_CREATURE_8:FATj CREATURE:NIGHT_CREATURE_8:TALLOWjCREATURE:NIGHT_CREATURE_9:FATj CREATURE:NIGHT_CREATURE_9:TALLOWjCREATURE:NIGHT_CREATURE_10:FATj!CREATURE:NIGHT_CREATURE_10:TALLOWjCREATURE:NIGHT_CREATURE_11:FATj!CREATURE:NIGHT_CREATURE_11:TALLOWjCREATURE:NIGHT_CREATURE_12:FATj!CREATURE:NIGHT_CREATURE_12:TALLOWjCREATURE:NIGHT_CREATURE_13:FATj!CREATURE:NIGHT_CREATURE_13:TALLOWjCREATURE:NIGHT_CREATURE_14:FATj!CREATURE:NIGHT_CREATURE_14:TALLOWjCREATURE:NIGHT_CREATURE_15:FATj!CREATURE:NIGHT_CREATURE_15:TALLOWjCREATURE:NIGHT_CREATURE_16:FATj!CREATURE:NIGHT_CREATURE_16:TALLOWjCREATURE:NIGHT_CREATURE_17:FATj!CREATURE:NIGHT_CREATURE_17:TALLOWjCREATURE:NIGHT_CREATURE_18:FATj!CREATURE:NIGHT_CREATURE_18:TALLOWjCREATURE:NIGHT_CREATURE_19:FATj!CREATURE:NIGHT_CREATURE_19:TALLOWjCREATURE:NIGHT_CREATURE_20:FATj!CREATURE:NIGHT_CREATURE_20:TALLOWjCREATURE:NIGHT_CREATURE_21:FATj!CREATURE:NIGHT_CREATURE_21:TALLOWjCREATURE:NIGHT_CREATURE_22:FATj!CREATURE:NIGHT_CREATURE_22:TALLOWjCREATURE:NIGHT_CREATURE_23:FATj!CREATURE:NIGHT_CREATURE_23:TALLOWjCREATURE:NIGHT_CREATURE_24:FATj!CREATURE:NIGHT_CREATURE_24:TALLOWjCREATURE:NIGHT_CREATURE_25:FATj!CREATURE:NIGHT_CREATURE_25:TALLOWjCREATURE:NIGHT_CREATURE_26:FATj!CREATURE:NIGHT_CREATURE_26:TALLOWjCREATURE:NIGHT_CREATURE_27:FATj!CREATURE:NIGHT_CREATURE_27:TALLOWjCREATURE:NIGHT_CREATURE_28:FATj!CREATURE:NIGHT_CREATURE_28:TALLOWjCREATURE:NIGHT_CREATURE_29:FATj!CREATURE:NIGHT_CREATURE_29:TALLOWjCREATURE:NIGHT_CREATURE_30:FATj!CREATURE:NIGHT_CREATURE_30:TALLOWjCREATURE:NIGHT_CREATURE_31:FATj!CREATURE:NIGHT_CREATURE_31:TALLOWjCREATURE:NIGHT_CREATURE_32:FATj!CREATURE:NIGHT_CREATURE_32:TALLOWjCREATURE:NIGHT_CREATURE_33:FATj!CREATURE:NIGHT_CREATURE_33:TALLOWjCREATURE:NIGHT_CREATURE_34:FATj!CREATURE:NIGHT_CREATURE_34:TALLOWjCREATURE:NIGHT_CREATURE_35:FATj!CREATURE:NIGHT_CREATURE_35:TALLOWjCREATURE:NIGHT_CREATURE_36:FATj!CREATURE:NIGHT_CREATURE_36:TALLOWjCREATURE:NIGHT_CREATURE_37:FATj!CREATURE:NIGHT_CREATURE_37:TALLOWjCREATURE:NIGHT_CREATURE_38:FATj!CREATURE:NIGHT_CREATURE_38:TALLOWjCREATURE:NIGHT_CREATURE_39:FATj!CREATURE:NIGHT_CREATURE_39:TALLOWjCREATURE:NIGHT_CREATURE_40:FATj!CREATURE:NIGHT_CREATURE_40:TALLOWjCREATURE:NIGHT_CREATURE_41:FATj!CREATURE:NIGHT_CREATURE_41:TALLOWjCREATURE:NIGHT_CREATURE_42:FATj!CREATURE:NIGHT_CREATURE_42:TALLOWjCREATURE:NIGHT_CREATURE_43:FATj!CREATURE:NIGHT_CREATURE_43:TALLOWjCREATURE:NIGHT_CREATURE_44:FATj!CREATURE:NIGHT_CREATURE_44:TALLOWjCREATURE:NIGHT_CREATURE_45:FATj!CREATURE:NIGHT_CREATURE_45:TALLOWjCREATURE:NIGHT_CREATURE_46:FATj!CREATURE:NIGHT_CREATURE_46:TALLOWjCREATURE:NIGHT_CREATURE_47:FATj!CREATURE:NIGHT_CREATURE_47:TALLOWjCREATURE:NIGHT_CREATURE_48:FATj!CREATURE:NIGHT_CREATURE_48:TALLOWjCREATURE:NIGHT_CREATURE_49:FATj!CREATURE:NIGHT_CREATURE_49:TALLOWjCREATURE:NIGHT_CREATURE_50:FATj!CREATURE:NIGHT_CREATURE_50:TALLOWjCREATURE:NIGHT_CREATURE_51:FATj!CREATURE:NIGHT_CREATURE_51:TALLOWjCREATURE:NIGHT_CREATURE_52:FATj!CREATURE:NIGHT_CREATURE_52:TALLOWjCREATURE:NIGHT_CREATURE_53:FATj!CREATURE:NIGHT_CREATURE_53:TALLOWjCREATURE:NIGHT_CREATURE_54:FATj!CREATURE:NIGHT_CREATURE_54:TALLOWjCREATURE:NIGHT_CREATURE_55:FATj!CREATURE:NIGHT_CREATURE_55:TALLOWjCREATURE:NIGHT_CREATURE_56:FATj!CREATURE:NIGHT_CREATURE_56:TALLOWjCREATURE:NIGHT_CREATURE_57:FATj!CREATURE:NIGHT_CREATURE_57:TALLOWjCREATURE:NIGHT_CREATURE_58:FATj!CREATURE:NIGHT_CREATURE_58:TALLOWjCREATURE:NIGHT_CREATURE_59:FATj!CREATURE:NIGHT_CREATURE_59:TALLOWjCREATURE:NIGHT_CREATURE_60:FATj!CREATURE:NIGHT_CREATURE_60:TALLOWjCREATURE:NIGHT_CREATURE_61:FATj!CREATURE:NIGHT_CREATURE_61:TALLOWjCREATURE:NIGHT_CREATURE_62:FATj!CREATURE:NIGHT_CREATURE_62:TALLOWjCREATURE:NIGHT_CREATURE_63:FATj!CREATURE:NIGHT_CREATURE_63:TALLOWjCREATURE:NIGHT_CREATURE_64:FATj!CREATURE:NIGHT_CREATURE_64:TALLOWjCREATURE:NIGHT_CREATURE_65:FATj!CREATURE:NIGHT_CREATURE_65:TALLOWjCREATURE:NIGHT_CREATURE_66:FATj!CREATURE:NIGHT_CREATURE_66:TALLOWjCREATURE:NIGHT_CREATURE_67:FATj!CREATURE:NIGHT_CREATURE_67:TALLOWjCREATURE:NIGHT_CREATURE_68:FATj!CREATURE:NIGHT_CREATURE_68:TALLOWjCREATURE:NIGHT_CREATURE_69:FATj!CREATURE:NIGHT_CREATURE_69:TALLOWjCREATURE:NIGHT_CREATURE_70:FATj!CREATURE:NIGHT_CREATURE_70:TALLOWjCREATURE:NIGHT_CREATURE_71:FATj!CREATURE:NIGHT_CREATURE_71:TALLOWjCREATURE:NIGHT_CREATURE_72:FATj!CREATURE:NIGHT_CREATURE_72:TALLOWjCREATURE:NIGHT_CREATURE_73:FATj!CREATURE:NIGHT_CREATURE_73:TALLOWjCREATURE:NIGHT_CREATURE_74:FATj!CREATURE:NIGHT_CREATURE_74:TALLOWjCREATURE:NIGHT_CREATURE_75:FATj!CREATURE:NIGHT_CREATURE_75:TALLOWjCREATURE:NIGHT_CREATURE_76:FATj!CREATURE:NIGHT_CREATURE_76:TALLOWjCREATURE:NIGHT_CREATURE_77:FATj!CREATURE:NIGHT_CREATURE_77:TALLOWjCREATURE:NIGHT_CREATURE_78:FATj!CREATURE:NIGHT_CREATURE_78:TALLOWjCREATURE:NIGHT_CREATURE_79:FATj!CREATURE:NIGHT_CREATURE_79:TALLOWjCREATURE:NIGHT_CREATURE_80:FATj!CREATURE:NIGHT_CREATURE_80:TALLOWjCREATURE:NIGHT_CREATURE_81:FATj!CREATURE:NIGHT_CREATURE_81:TALLOWjCREATURE:NIGHT_CREATURE_82:FATj!CREATURE:NIGHT_CREATURE_82:TALLOWjCREATURE:NIGHT_CREATURE_83:FATj!CREATURE:NIGHT_CREATURE_83:TALLOWjCREATURE:NIGHT_CREATURE_84:FATj!CREATURE:NIGHT_CREATURE_84:TALLOWjCREATURE:NIGHT_CREATURE_85:FATj!CREATURE:NIGHT_CREATURE_85:TALLOWjCREATURE:NIGHT_CREATURE_86:FATj!CREATURE:NIGHT_CREATURE_86:TALLOWjCREATURE:NIGHT_CREATURE_87:FATj!CREATURE:NIGHT_CREATURE_87:TALLOWjCREATURE:NIGHT_CREATURE_88:FATj!CREATURE:NIGHT_CREATURE_88:TALLOWjCREATURE:NIGHT_CREATURE_89:FATj!CREATURE:NIGHT_CREATURE_89:TALLOWjCREATURE:NIGHT_CREATURE_90:FATj!CREATURE:NIGHT_CREATURE_90:TALLOWjCREATURE:NIGHT_CREATURE_91:FATj!CREATURE:NIGHT_CREATURE_91:TALLOWjCREATURE:NIGHT_CREATURE_92:FATj!CREATURE:NIGHT_CREATURE_92:TALLOWjCREATURE:NIGHT_CREATURE_93:FATj!CREATURE:NIGHT_CREATURE_93:TALLOWjCREATURE:NIGHT_CREATURE_94:FATj!CREATURE:NIGHT_CREATURE_94:TALLOWjCREATURE:NIGHT_CREATURE_95:FATj!CREATURE:NIGHT_CREATURE_95:TALLOWjCREATURE:NIGHT_CREATURE_96:FATj!CREATURE:NIGHT_CREATURE_96:TALLOWjCREATURE:NIGHT_CREATURE_97:FATj!CREATURE:NIGHT_CREATURE_97:TALLOWjCREATURE:NIGHT_CREATURE_98:FATj!CREATURE:NIGHT_CREATURE_98:TALLOWjCREATURE:NIGHT_CREATURE_99:FATj!CREATURE:NIGHT_CREATURE_99:TALLOWjCREATURE:NIGHT_CREATURE_100:FATj"CREATURE:NIGHT_CREATURE_100:TALLOWjCREATURE:NIGHT_CREATURE_101:FATj"CREATURE:NIGHT_CREATURE_101:TALLOWjCREATURE:NIGHT_CREATURE_102:FATj"CREATURE:NIGHT_CREATURE_102:TALLOWjCREATURE:NIGHT_CREATURE_103:FATj"CREATURE:NIGHT_CREATURE_103:TALLOWjCREATURE:NIGHT_CREATURE_104:FATj"CREATURE:NIGHT_CREATURE_104:TALLOWjCREATURE:HF1248 DIVINE_1:FATjCREATURE:HF1248 DIVINE_1:TALLOWjCREATURE:HF1248 DIVINE_2:FATjCREATURE:HF1248 DIVINE_2:TALLOWjCREATURE:HF1248 DIVINE_3:FATjCREATURE:HF1248 DIVINE_3:TALLOWjCREATURE:HF1108 DIVINE_2:FATjCREATURE:HF1108 DIVINE_2:TALLOWjCREATURE:HF1249 DIVINE_1:FATjCREATURE:HF1249 DIVINE_1:TALLOWjCREATURE:HF1249 DIVINE_3:FATjCREATURE:HF1249 DIVINE_3:TALLOWjCREATURE:HF1345 DIVINE_3:FATjCREATURE:HF1345 DIVINE_3:TALLOWrPLANT:FLAX:SEEDrPLANT:FLAX:THREADrPLANT:JUTE:THREADrPLANT:HEMP:SEEDrPLANT:HEMP:THREADrPLANT:COTTON:SEEDrPLANT:COTTON:THREADrPLANT:RAMIE:THREADrPLANT:KENAF:SEEDrPLANT:KENAF:THREADrPLANT:GRASS_TAIL_PIG:THREADrPLANT:BUSH_QUARRY:SEEDrPLANT:REED_ROPE:THREADzPLANT:FLAX:SEEDzPLANT:HEMP:SEEDzPLANT:COTTON:SEEDzPLANT:KENAF:SEEDzPLANT:OLIVE:FRUITzPLANT:BUSH_QUARRY:SEEDzCREATURE:HONEY_BEE:WAX‚PLANT:FLAX:OIL‚PLANT:HEMP:OIL‚PLANT:COTTON:OIL‚PLANT:KENAF:OIL‚PLANT:OLIVE:OIL‚PLANT:POD_SWEET:EXTRACT‚PLANT:BUSH_QUARRY:OIL‚PLANT:BULB_KOBOLD:EXTRACT‚PLANT:HERB_VALLEY:EXTRACTŠCREATURE:BARK_SCORPION:VENOMŠ CREATURE:BARK_SCORPION_MAN:VENOMŠ"CREATURE:GIANT_BARK_SCORPION:VENOMŠ#CREATURE:SPIDER_BROWN_RECLUSE:VENOMŠ'CREATURE:BROWN_RECLUSE_SPIDER_MAN:VENOMŠ)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:VENOMŠCREATURE:GILA_MONSTER:VENOMŠCREATURE:GILA_MONSTER_MAN:VENOMŠ!CREATURE:GIANT_GILA_MONSTER:VENOMŠCREATURE:DONKEY:MILKŠCREATURE:HORSE:MILKŠCREATURE:COW:MILKŠCREATURE:SHEEP:MILKŠCREATURE:PIG:MILKŠCREATURE:GOAT:MILKŠCREATURE:WATER_BUFFALO:MILKŠCREATURE:REINDEER:MILKŠCREATURE:YAK:MILKŠCREATURE:LLAMA:MILKŠCREATURE:ALPACA:MILKŠCREATURE:HONEY_BEE:ROYAL_JELLYŠCREATURE:HONEY_BEE:HONEYŠCREATURE:HONEY_BEE:VENOMŠCREATURE:BUMBLEBEE:ROYAL_JELLYŠCREATURE:BUMBLEBEE:HONEYŠCREATURE:BUMBLEBEE:VENOMŠCREATURE:PLATYPUS:VENOMŠCREATURE:PLATYPUS MAN:VENOMŠCREATURE:PLATYPUS, GIANT:VENOMŠCREATURE:CAMEL_1_HUMP:MILKŠCREATURE:CAMEL_1_HUMP_MAN:MILKŠ CREATURE:GIANT_CAMEL_1_HUMP:MILKŠCREATURE:CAMEL_2_HUMP:MILKŠCREATURE:CAMEL_2_HUMP_MAN:MILKŠ CREATURE:GIANT_CAMEL_2_HUMP:MILKŠCREATURE:HELMET_SNAKE:VENOMŠCREATURE:CAVE_FLOATER:POD_JUICEŠCREATURE:CAVE_BLOB:FLUIDŠCREATURE:OCTOPUS:INKŠCREATURE:OCTOPUS_MAN:INKŠCREATURE:GIANT_OCTOPUS:INKŠCREATURE:CUTTLEFISH:INKŠCREATURE:CUTTLEFISH_MAN:INKŠCREATURE:GIANT_CUTTLEFISH:INKŠCREATURE:MOGHOPPER:MOG_JUICEŠCREATURE:SPIDER_PHANTOM:VENOMŠCREATURE:SQUID:INKŠCREATURE:SQUID MAN:INKŠCREATURE:GIGANTIC SQUID:INKŠ!CREATURE:SPIDER_CAVE_GIANT:POISONŠCREATURE:SPIDER_CAVE:VENOMŠCREATURE:MAGGOT_PURRING:MILKŠCREATURE:ELEMENTMAN_IRON:GASŠCREATURE:SERPENT_MAN:VENOMŠCREATURE:KANGAROO:MILKŠCREATURE:KANGAROO_MAN:MILKŠCREATURE:GIANT_KANGAROO:MILKŠCREATURE:ADDER:VENOMŠCREATURE:ADDER_MAN:VENOMŠCREATURE:GIANT_ADDER:VENOMŠCREATURE:RATTLESNAKE:VENOMŠCREATURE:RATTLESNAKE_MAN:VENOMŠ CREATURE:GIANT_RATTLESNAKE:VENOMŠCREATURE:COPPERHEAD_SNAKE:VENOMŠ#CREATURE:COPPERHEAD_SNAKE_MAN:VENOMŠ%CREATURE:GIANT_COPPERHEAD_SNAKE:VENOMŠCREATURE:KING_COBRA:VENOMŠCREATURE:KING_COBRA_MAN:VENOMŠCREATURE:GIANT_KING_COBRA:VENOMŠCREATURE:BLACK_MAMBA:VENOMŠCREATURE:BLACK_MAMBA_MAN:VENOMŠ CREATURE:GIANT_BLACK_MAMBA:VENOMŠCREATURE:BUSHMASTER:VENOMŠCREATURE:BUSHMASTER_MAN:VENOMŠCREATURE:GIANT_BUSHMASTER:VENOMŠCREATURE:TAPIR:MILKŠCREATURE:TAPIR_MAN:MILKŠCREATURE:GIANT_TAPIR:MILKŠ!CREATURE:FORGOTTEN_BEAST_2:POISONŠ!CREATURE:FORGOTTEN_BEAST_4:POISONŠ!CREATURE:FORGOTTEN_BEAST_5:POISONŠ!CREATURE:FORGOTTEN_BEAST_7:POISONŠ!CREATURE:FORGOTTEN_BEAST_8:POISONŠ!CREATURE:FORGOTTEN_BEAST_9:POISONŠ"CREATURE:FORGOTTEN_BEAST_11:POISONŠ"CREATURE:FORGOTTEN_BEAST_13:POISONŠ"CREATURE:FORGOTTEN_BEAST_15:POISONŠ"CREATURE:FORGOTTEN_BEAST_16:POISONŠ"CREATURE:FORGOTTEN_BEAST_18:POISONŠ"CREATURE:FORGOTTEN_BEAST_19:POISONŠ"CREATURE:FORGOTTEN_BEAST_20:POISONŠ"CREATURE:FORGOTTEN_BEAST_21:POISONŠ"CREATURE:FORGOTTEN_BEAST_22:POISONŠ"CREATURE:FORGOTTEN_BEAST_24:POISONŠ"CREATURE:FORGOTTEN_BEAST_25:POISONŠ"CREATURE:FORGOTTEN_BEAST_27:POISONŠ"CREATURE:FORGOTTEN_BEAST_28:POISONŠ"CREATURE:FORGOTTEN_BEAST_33:POISONŠ"CREATURE:FORGOTTEN_BEAST_36:POISONŠ"CREATURE:FORGOTTEN_BEAST_37:POISONŠ"CREATURE:FORGOTTEN_BEAST_39:POISONŠ"CREATURE:FORGOTTEN_BEAST_40:POISONŠ"CREATURE:FORGOTTEN_BEAST_41:POISONŠ"CREATURE:FORGOTTEN_BEAST_43:POISONŠ"CREATURE:FORGOTTEN_BEAST_44:POISONŠ"CREATURE:FORGOTTEN_BEAST_45:POISONŠ"CREATURE:FORGOTTEN_BEAST_46:POISONŠ"CREATURE:FORGOTTEN_BEAST_48:POISONŠ"CREATURE:FORGOTTEN_BEAST_49:POISONŠ"CREATURE:FORGOTTEN_BEAST_50:POISONŠ"CREATURE:FORGOTTEN_BEAST_51:POISONŠ"CREATURE:FORGOTTEN_BEAST_56:POISONŠ"CREATURE:FORGOTTEN_BEAST_59:POISONŠ"CREATURE:FORGOTTEN_BEAST_61:POISONŠ"CREATURE:FORGOTTEN_BEAST_62:POISONŠ"CREATURE:FORGOTTEN_BEAST_64:POISONŠ"CREATURE:FORGOTTEN_BEAST_66:POISONŠ"CREATURE:FORGOTTEN_BEAST_67:POISONŠ"CREATURE:FORGOTTEN_BEAST_69:POISONŠ"CREATURE:FORGOTTEN_BEAST_70:POISONŠ"CREATURE:FORGOTTEN_BEAST_72:POISONŠ"CREATURE:FORGOTTEN_BEAST_73:POISONŠ"CREATURE:FORGOTTEN_BEAST_75:POISONŠ"CREATURE:FORGOTTEN_BEAST_77:POISONŠ"CREATURE:FORGOTTEN_BEAST_78:POISONŠ"CREATURE:FORGOTTEN_BEAST_80:POISONŠ"CREATURE:FORGOTTEN_BEAST_82:POISONŠ"CREATURE:FORGOTTEN_BEAST_84:POISONŠ"CREATURE:FORGOTTEN_BEAST_88:POISONŠ"CREATURE:FORGOTTEN_BEAST_89:POISONŠ"CREATURE:FORGOTTEN_BEAST_90:POISONŠ"CREATURE:FORGOTTEN_BEAST_92:POISONŠ"CREATURE:FORGOTTEN_BEAST_93:POISONŠ"CREATURE:FORGOTTEN_BEAST_94:POISONŠ"CREATURE:FORGOTTEN_BEAST_95:POISONŠ"CREATURE:FORGOTTEN_BEAST_97:POISONŠ"CREATURE:FORGOTTEN_BEAST_98:POISONŠ#CREATURE:FORGOTTEN_BEAST_100:POISONŠ#CREATURE:FORGOTTEN_BEAST_104:POISONŠ#CREATURE:FORGOTTEN_BEAST_105:POISONŠ#CREATURE:FORGOTTEN_BEAST_106:POISONŠ#CREATURE:FORGOTTEN_BEAST_107:POISONŠ#CREATURE:FORGOTTEN_BEAST_108:POISONŠ#CREATURE:FORGOTTEN_BEAST_110:POISONŠ#CREATURE:FORGOTTEN_BEAST_114:POISONŠ#CREATURE:FORGOTTEN_BEAST_115:POISONŠ#CREATURE:FORGOTTEN_BEAST_116:POISONŠ#CREATURE:FORGOTTEN_BEAST_117:POISONŠ#CREATURE:FORGOTTEN_BEAST_118:POISONŠ#CREATURE:FORGOTTEN_BEAST_120:POISONŠ#CREATURE:FORGOTTEN_BEAST_122:POISONŠ#CREATURE:FORGOTTEN_BEAST_123:POISONŠ#CREATURE:FORGOTTEN_BEAST_124:POISONŠ#CREATURE:FORGOTTEN_BEAST_126:POISONŠ#CREATURE:FORGOTTEN_BEAST_127:POISONŠ#CREATURE:FORGOTTEN_BEAST_130:POISONŠ#CREATURE:FORGOTTEN_BEAST_133:POISONŠ#CREATURE:FORGOTTEN_BEAST_134:POISONŠ#CREATURE:FORGOTTEN_BEAST_135:POISONŠ#CREATURE:FORGOTTEN_BEAST_136:POISONŠ#CREATURE:FORGOTTEN_BEAST_138:POISONŠ#CREATURE:FORGOTTEN_BEAST_139:POISONŠ#CREATURE:FORGOTTEN_BEAST_141:POISONŠ#CREATURE:FORGOTTEN_BEAST_143:POISONŠ#CREATURE:FORGOTTEN_BEAST_145:POISONŠ#CREATURE:FORGOTTEN_BEAST_147:POISONŠ#CREATURE:FORGOTTEN_BEAST_148:POISONŠ#CREATURE:FORGOTTEN_BEAST_151:POISONŠ#CREATURE:FORGOTTEN_BEAST_154:POISONŠ#CREATURE:FORGOTTEN_BEAST_155:POISONŠ#CREATURE:FORGOTTEN_BEAST_156:POISONŠ#CREATURE:FORGOTTEN_BEAST_157:POISONŠ#CREATURE:FORGOTTEN_BEAST_158:POISONŠ#CREATURE:FORGOTTEN_BEAST_160:POISONŠ#CREATURE:FORGOTTEN_BEAST_163:POISONŠ#CREATURE:FORGOTTEN_BEAST_165:POISONŠ#CREATURE:FORGOTTEN_BEAST_167:POISONŠ#CREATURE:FORGOTTEN_BEAST_170:POISONŠ#CREATURE:FORGOTTEN_BEAST_171:POISONŠ#CREATURE:FORGOTTEN_BEAST_172:POISONŠ#CREATURE:FORGOTTEN_BEAST_175:POISONŠ#CREATURE:FORGOTTEN_BEAST_176:POISONŠ#CREATURE:FORGOTTEN_BEAST_178:POISONŠ#CREATURE:FORGOTTEN_BEAST_179:POISONŠ#CREATURE:FORGOTTEN_BEAST_182:POISONŠ#CREATURE:FORGOTTEN_BEAST_183:POISONŠ#CREATURE:FORGOTTEN_BEAST_184:POISONŠ#CREATURE:FORGOTTEN_BEAST_185:POISONŠ#CREATURE:FORGOTTEN_BEAST_187:POISONŠ#CREATURE:FORGOTTEN_BEAST_188:POISONŠ#CREATURE:FORGOTTEN_BEAST_189:POISONŠ#CREATURE:FORGOTTEN_BEAST_190:POISONŠ#CREATURE:FORGOTTEN_BEAST_191:POISONŠ#CREATURE:FORGOTTEN_BEAST_192:POISONŠ#CREATURE:FORGOTTEN_BEAST_193:POISONŠ#CREATURE:FORGOTTEN_BEAST_194:POISONŠ#CREATURE:FORGOTTEN_BEAST_196:POISONŠ#CREATURE:FORGOTTEN_BEAST_197:POISONŠ#CREATURE:FORGOTTEN_BEAST_198:POISONŠ#CREATURE:FORGOTTEN_BEAST_199:POISONŠ#CREATURE:FORGOTTEN_BEAST_201:POISONŠ#CREATURE:FORGOTTEN_BEAST_203:POISONŠ#CREATURE:FORGOTTEN_BEAST_206:POISONŠ#CREATURE:FORGOTTEN_BEAST_207:POISONŠ#CREATURE:FORGOTTEN_BEAST_208:POISONŠ#CREATURE:FORGOTTEN_BEAST_210:POISONŠ#CREATURE:FORGOTTEN_BEAST_212:POISONŠ#CREATURE:FORGOTTEN_BEAST_213:POISONŠ#CREATURE:FORGOTTEN_BEAST_215:POISONŠ#CREATURE:FORGOTTEN_BEAST_216:POISONŠ#CREATURE:FORGOTTEN_BEAST_217:POISONŠ#CREATURE:FORGOTTEN_BEAST_218:POISONŠ#CREATURE:FORGOTTEN_BEAST_219:POISONŠ#CREATURE:FORGOTTEN_BEAST_220:POISONŠ#CREATURE:FORGOTTEN_BEAST_221:POISONŠ#CREATURE:FORGOTTEN_BEAST_223:POISONŠ#CREATURE:FORGOTTEN_BEAST_225:POISONŠ#CREATURE:FORGOTTEN_BEAST_227:POISONŠ#CREATURE:FORGOTTEN_BEAST_228:POISONŠ#CREATURE:FORGOTTEN_BEAST_230:POISONŠ#CREATURE:FORGOTTEN_BEAST_232:POISONŠ#CREATURE:FORGOTTEN_BEAST_233:POISONŠ#CREATURE:FORGOTTEN_BEAST_234:POISONŠ#CREATURE:FORGOTTEN_BEAST_236:POISONŠ#CREATURE:FORGOTTEN_BEAST_237:POISONŠ#CREATURE:FORGOTTEN_BEAST_240:POISONŠ#CREATURE:FORGOTTEN_BEAST_243:POISONŠ#CREATURE:FORGOTTEN_BEAST_244:POISONŠ#CREATURE:FORGOTTEN_BEAST_245:POISONŠ#CREATURE:FORGOTTEN_BEAST_247:POISONŠ#CREATURE:FORGOTTEN_BEAST_249:POISONŠ#CREATURE:FORGOTTEN_BEAST_252:POISONŠ#CREATURE:FORGOTTEN_BEAST_253:POISONŠ#CREATURE:FORGOTTEN_BEAST_254:POISONŠ#CREATURE:FORGOTTEN_BEAST_255:POISONŠ#CREATURE:FORGOTTEN_BEAST_256:POISONŠ#CREATURE:FORGOTTEN_BEAST_257:POISONŠ#CREATURE:FORGOTTEN_BEAST_258:POISONŠ#CREATURE:FORGOTTEN_BEAST_259:POISONŠ#CREATURE:FORGOTTEN_BEAST_260:POISONŠ#CREATURE:FORGOTTEN_BEAST_263:POISONŠ#CREATURE:FORGOTTEN_BEAST_264:POISONŠ#CREATURE:FORGOTTEN_BEAST_265:POISONŠ#CREATURE:FORGOTTEN_BEAST_266:POISONŠ#CREATURE:FORGOTTEN_BEAST_268:POISONŠ#CREATURE:FORGOTTEN_BEAST_269:POISONŠ#CREATURE:FORGOTTEN_BEAST_270:POISONŠ#CREATURE:FORGOTTEN_BEAST_271:POISONŠ#CREATURE:FORGOTTEN_BEAST_272:POISONŠ#CREATURE:FORGOTTEN_BEAST_273:POISONŠ#CREATURE:FORGOTTEN_BEAST_274:POISONŠ#CREATURE:FORGOTTEN_BEAST_275:POISONŠ#CREATURE:FORGOTTEN_BEAST_276:POISONŠ#CREATURE:FORGOTTEN_BEAST_278:POISONŠ#CREATURE:FORGOTTEN_BEAST_279:POISONŠ#CREATURE:FORGOTTEN_BEAST_283:POISONŠ#CREATURE:FORGOTTEN_BEAST_286:POISONŠ#CREATURE:FORGOTTEN_BEAST_288:POISONŠ#CREATURE:FORGOTTEN_BEAST_291:POISONŠ#CREATURE:FORGOTTEN_BEAST_295:POISONŠ#CREATURE:FORGOTTEN_BEAST_296:POISONŠ#CREATURE:FORGOTTEN_BEAST_297:POISONŠ#CREATURE:FORGOTTEN_BEAST_299:POISONŠ#CREATURE:FORGOTTEN_BEAST_300:POISONŠ#CREATURE:FORGOTTEN_BEAST_301:POISONŠ#CREATURE:FORGOTTEN_BEAST_302:POISONŠ#CREATURE:FORGOTTEN_BEAST_303:POISONŠ#CREATURE:FORGOTTEN_BEAST_305:POISONŠ#CREATURE:FORGOTTEN_BEAST_306:POISONŠ#CREATURE:FORGOTTEN_BEAST_307:POISONŠ#CREATURE:FORGOTTEN_BEAST_310:POISONŠ#CREATURE:FORGOTTEN_BEAST_312:POISONŠ#CREATURE:FORGOTTEN_BEAST_314:POISONŠ#CREATURE:FORGOTTEN_BEAST_315:POISONŠ#CREATURE:FORGOTTEN_BEAST_316:POISONŠ#CREATURE:FORGOTTEN_BEAST_319:POISONŠ#CREATURE:FORGOTTEN_BEAST_321:POISONŠ#CREATURE:FORGOTTEN_BEAST_322:POISONŠ#CREATURE:FORGOTTEN_BEAST_323:POISONŠ#CREATURE:FORGOTTEN_BEAST_324:POISONŠ#CREATURE:FORGOTTEN_BEAST_325:POISONŠ#CREATURE:FORGOTTEN_BEAST_327:POISONŠ#CREATURE:FORGOTTEN_BEAST_328:POISONŠ#CREATURE:FORGOTTEN_BEAST_329:POISONŠ#CREATURE:FORGOTTEN_BEAST_332:POISONŠ#CREATURE:FORGOTTEN_BEAST_334:POISONŠ#CREATURE:FORGOTTEN_BEAST_335:POISONŠ#CREATURE:FORGOTTEN_BEAST_336:POISONŠ#CREATURE:FORGOTTEN_BEAST_337:POISONŠ#CREATURE:FORGOTTEN_BEAST_338:POISONŠ#CREATURE:FORGOTTEN_BEAST_339:POISONŠ#CREATURE:FORGOTTEN_BEAST_341:POISONŠ#CREATURE:FORGOTTEN_BEAST_342:POISONŠ#CREATURE:FORGOTTEN_BEAST_343:POISONŠ#CREATURE:FORGOTTEN_BEAST_344:POISONŠ#CREATURE:FORGOTTEN_BEAST_348:POISONŠ#CREATURE:FORGOTTEN_BEAST_349:POISONŠ#CREATURE:FORGOTTEN_BEAST_351:POISONŠ#CREATURE:FORGOTTEN_BEAST_352:POISONŠ#CREATURE:FORGOTTEN_BEAST_353:POISONŠ#CREATURE:FORGOTTEN_BEAST_354:POISONŠ#CREATURE:FORGOTTEN_BEAST_355:POISONŠ#CREATURE:FORGOTTEN_BEAST_356:POISONŠ#CREATURE:FORGOTTEN_BEAST_357:POISONŠ#CREATURE:FORGOTTEN_BEAST_358:POISONŠ#CREATURE:FORGOTTEN_BEAST_359:POISONŠ#CREATURE:FORGOTTEN_BEAST_361:POISONŠ#CREATURE:FORGOTTEN_BEAST_363:POISONŠ#CREATURE:FORGOTTEN_BEAST_364:POISONŠ#CREATURE:FORGOTTEN_BEAST_365:POISONŠ#CREATURE:FORGOTTEN_BEAST_366:POISONŠ#CREATURE:FORGOTTEN_BEAST_367:POISONŠ#CREATURE:FORGOTTEN_BEAST_368:POISONŠ#CREATURE:FORGOTTEN_BEAST_369:POISONŠ#CREATURE:FORGOTTEN_BEAST_370:POISONŠ#CREATURE:FORGOTTEN_BEAST_375:POISONŠ#CREATURE:FORGOTTEN_BEAST_377:POISONŠ#CREATURE:FORGOTTEN_BEAST_378:POISONŠ#CREATURE:FORGOTTEN_BEAST_382:POISONŠ#CREATURE:FORGOTTEN_BEAST_385:POISONŠ#CREATURE:FORGOTTEN_BEAST_386:POISONŠ#CREATURE:FORGOTTEN_BEAST_387:POISONŠ#CREATURE:FORGOTTEN_BEAST_388:POISONŠ#CREATURE:FORGOTTEN_BEAST_389:POISONŠ#CREATURE:FORGOTTEN_BEAST_390:POISONŠ#CREATURE:FORGOTTEN_BEAST_391:POISONŠ#CREATURE:FORGOTTEN_BEAST_392:POISONŠ#CREATURE:FORGOTTEN_BEAST_394:POISONŠ#CREATURE:FORGOTTEN_BEAST_395:POISONŠ#CREATURE:FORGOTTEN_BEAST_398:POISONŠ#CREATURE:FORGOTTEN_BEAST_399:POISONŠ#CREATURE:FORGOTTEN_BEAST_400:POISONŠ#CREATURE:FORGOTTEN_BEAST_402:POISONŠ#CREATURE:FORGOTTEN_BEAST_405:POISONŠ#CREATURE:FORGOTTEN_BEAST_408:POISONŠ#CREATURE:FORGOTTEN_BEAST_409:POISONŠ#CREATURE:FORGOTTEN_BEAST_410:POISONŠ#CREATURE:FORGOTTEN_BEAST_412:POISONŠ#CREATURE:FORGOTTEN_BEAST_413:POISONŠ#CREATURE:FORGOTTEN_BEAST_414:POISONŠ#CREATURE:FORGOTTEN_BEAST_415:POISONŠ#CREATURE:FORGOTTEN_BEAST_416:POISONŠ#CREATURE:FORGOTTEN_BEAST_417:POISONŠ#CREATURE:FORGOTTEN_BEAST_418:POISONŠ#CREATURE:FORGOTTEN_BEAST_419:POISONŠ#CREATURE:FORGOTTEN_BEAST_420:POISONŠ#CREATURE:FORGOTTEN_BEAST_421:POISONŠ#CREATURE:FORGOTTEN_BEAST_422:POISONŠ#CREATURE:FORGOTTEN_BEAST_423:POISONŠ#CREATURE:FORGOTTEN_BEAST_425:POISONŠ#CREATURE:FORGOTTEN_BEAST_431:POISONŠ#CREATURE:FORGOTTEN_BEAST_433:POISONŠ#CREATURE:FORGOTTEN_BEAST_434:POISONŠ#CREATURE:FORGOTTEN_BEAST_436:POISONŠ#CREATURE:FORGOTTEN_BEAST_437:POISONŠ#CREATURE:FORGOTTEN_BEAST_439:POISONŠ#CREATURE:FORGOTTEN_BEAST_441:POISONŠ#CREATURE:FORGOTTEN_BEAST_443:POISONŠ#CREATURE:FORGOTTEN_BEAST_444:POISONŠ#CREATURE:FORGOTTEN_BEAST_445:POISONŠ#CREATURE:FORGOTTEN_BEAST_447:POISONŠ#CREATURE:FORGOTTEN_BEAST_449:POISONŠ#CREATURE:FORGOTTEN_BEAST_450:POISONŠ#CREATURE:FORGOTTEN_BEAST_451:POISONŠ#CREATURE:FORGOTTEN_BEAST_452:POISONŠ#CREATURE:FORGOTTEN_BEAST_453:POISONŠ#CREATURE:FORGOTTEN_BEAST_454:POISONŠ#CREATURE:FORGOTTEN_BEAST_455:POISONŠ#CREATURE:FORGOTTEN_BEAST_459:POISONŠ#CREATURE:FORGOTTEN_BEAST_461:POISONŠ#CREATURE:FORGOTTEN_BEAST_462:POISONŠ#CREATURE:FORGOTTEN_BEAST_463:POISONŠ#CREATURE:FORGOTTEN_BEAST_464:POISONŠ#CREATURE:FORGOTTEN_BEAST_469:POISONŠ#CREATURE:FORGOTTEN_BEAST_470:POISONŠ#CREATURE:FORGOTTEN_BEAST_474:POISONŠ#CREATURE:FORGOTTEN_BEAST_475:POISONŠ#CREATURE:FORGOTTEN_BEAST_476:POISONŠ#CREATURE:FORGOTTEN_BEAST_477:POISONŠ#CREATURE:FORGOTTEN_BEAST_478:POISONŠ#CREATURE:FORGOTTEN_BEAST_480:POISONŠ#CREATURE:FORGOTTEN_BEAST_481:POISONŠ#CREATURE:FORGOTTEN_BEAST_482:POISONŠ#CREATURE:FORGOTTEN_BEAST_483:POISONŠ#CREATURE:FORGOTTEN_BEAST_484:POISONŠ#CREATURE:FORGOTTEN_BEAST_485:POISONŠ#CREATURE:FORGOTTEN_BEAST_487:POISONŠ#CREATURE:FORGOTTEN_BEAST_489:POISONŠ#CREATURE:FORGOTTEN_BEAST_492:POISONŠ#CREATURE:FORGOTTEN_BEAST_494:POISONŠ#CREATURE:FORGOTTEN_BEAST_495:POISONŠ#CREATURE:FORGOTTEN_BEAST_497:POISONŠ#CREATURE:FORGOTTEN_BEAST_498:POISONŠ#CREATURE:FORGOTTEN_BEAST_499:POISONŠ#CREATURE:FORGOTTEN_BEAST_500:POISONŠ#CREATURE:FORGOTTEN_BEAST_501:POISONŠ#CREATURE:FORGOTTEN_BEAST_502:POISONŠ#CREATURE:FORGOTTEN_BEAST_503:POISONŠ#CREATURE:FORGOTTEN_BEAST_505:POISONŠ#CREATURE:FORGOTTEN_BEAST_506:POISONŠ#CREATURE:FORGOTTEN_BEAST_507:POISONŠ#CREATURE:FORGOTTEN_BEAST_509:POISONŠ#CREATURE:FORGOTTEN_BEAST_512:POISONŠ#CREATURE:FORGOTTEN_BEAST_513:POISONŠ#CREATURE:FORGOTTEN_BEAST_514:POISONŠ#CREATURE:FORGOTTEN_BEAST_515:POISONŠ#CREATURE:FORGOTTEN_BEAST_516:POISONŠ#CREATURE:FORGOTTEN_BEAST_518:POISONŠ#CREATURE:FORGOTTEN_BEAST_519:POISONŠ#CREATURE:FORGOTTEN_BEAST_522:POISONŠ#CREATURE:FORGOTTEN_BEAST_524:POISONŠ#CREATURE:FORGOTTEN_BEAST_525:POISONŠ#CREATURE:FORGOTTEN_BEAST_526:POISONŠ#CREATURE:FORGOTTEN_BEAST_527:POISONŠ#CREATURE:FORGOTTEN_BEAST_528:POISONŠ#CREATURE:FORGOTTEN_BEAST_529:POISONŠ#CREATURE:FORGOTTEN_BEAST_532:POISONŠ#CREATURE:FORGOTTEN_BEAST_533:POISONŠ#CREATURE:FORGOTTEN_BEAST_534:POISONŠ#CREATURE:FORGOTTEN_BEAST_535:POISONŠ#CREATURE:FORGOTTEN_BEAST_536:POISONŠ#CREATURE:FORGOTTEN_BEAST_537:POISONŠ#CREATURE:FORGOTTEN_BEAST_539:POISONŠ#CREATURE:FORGOTTEN_BEAST_540:POISONŠ#CREATURE:FORGOTTEN_BEAST_543:POISONŠ#CREATURE:FORGOTTEN_BEAST_544:POISONŠ#CREATURE:FORGOTTEN_BEAST_545:POISONŠ#CREATURE:FORGOTTEN_BEAST_546:POISONŠ#CREATURE:FORGOTTEN_BEAST_547:POISONŠ#CREATURE:FORGOTTEN_BEAST_548:POISONŠ#CREATURE:FORGOTTEN_BEAST_550:POISONŠ#CREATURE:FORGOTTEN_BEAST_551:POISONŠ#CREATURE:FORGOTTEN_BEAST_554:POISONŠ#CREATURE:FORGOTTEN_BEAST_555:POISONŠ#CREATURE:FORGOTTEN_BEAST_557:POISONŠ#CREATURE:FORGOTTEN_BEAST_558:POISONŠ#CREATURE:FORGOTTEN_BEAST_559:POISONŠ#CREATURE:FORGOTTEN_BEAST_565:POISONŠ#CREATURE:FORGOTTEN_BEAST_569:POISONŠ#CREATURE:FORGOTTEN_BEAST_570:POISONŠ#CREATURE:FORGOTTEN_BEAST_571:POISONŠ#CREATURE:FORGOTTEN_BEAST_572:POISONŠ#CREATURE:FORGOTTEN_BEAST_574:POISONŠ#CREATURE:FORGOTTEN_BEAST_577:POISONŠ#CREATURE:FORGOTTEN_BEAST_578:POISONŠ#CREATURE:FORGOTTEN_BEAST_579:POISONŠ#CREATURE:FORGOTTEN_BEAST_581:POISONŠ#CREATURE:FORGOTTEN_BEAST_583:POISONŠ#CREATURE:FORGOTTEN_BEAST_584:POISONŠ#CREATURE:FORGOTTEN_BEAST_585:POISONŠ#CREATURE:FORGOTTEN_BEAST_586:POISONŠ#CREATURE:FORGOTTEN_BEAST_587:POISONŠ#CREATURE:FORGOTTEN_BEAST_589:POISONŠ#CREATURE:FORGOTTEN_BEAST_590:POISONŠ#CREATURE:FORGOTTEN_BEAST_591:POISONŠ#CREATURE:FORGOTTEN_BEAST_592:POISONŠ#CREATURE:FORGOTTEN_BEAST_593:POISONŠ#CREATURE:FORGOTTEN_BEAST_594:POISONŠ#CREATURE:FORGOTTEN_BEAST_595:POISONŠ#CREATURE:FORGOTTEN_BEAST_597:POISONŠ#CREATURE:FORGOTTEN_BEAST_598:POISONŠ#CREATURE:FORGOTTEN_BEAST_599:POISONŠ#CREATURE:FORGOTTEN_BEAST_601:POISONŠ#CREATURE:FORGOTTEN_BEAST_602:POISONŠ#CREATURE:FORGOTTEN_BEAST_604:POISONŠ#CREATURE:FORGOTTEN_BEAST_607:POISONŠ#CREATURE:FORGOTTEN_BEAST_608:POISONŠ#CREATURE:FORGOTTEN_BEAST_609:POISONŠ#CREATURE:FORGOTTEN_BEAST_610:POISONŠ#CREATURE:FORGOTTEN_BEAST_618:POISONŠ#CREATURE:FORGOTTEN_BEAST_620:POISONŠ#CREATURE:FORGOTTEN_BEAST_621:POISONŠ#CREATURE:FORGOTTEN_BEAST_622:POISONŠ#CREATURE:FORGOTTEN_BEAST_624:POISONŠ#CREATURE:FORGOTTEN_BEAST_626:POISONŠ#CREATURE:FORGOTTEN_BEAST_627:POISONŠ#CREATURE:FORGOTTEN_BEAST_629:POISONŠ#CREATURE:FORGOTTEN_BEAST_632:POISONŠ#CREATURE:FORGOTTEN_BEAST_633:POISONŠ#CREATURE:FORGOTTEN_BEAST_634:POISONŠ#CREATURE:FORGOTTEN_BEAST_636:POISONŠ#CREATURE:FORGOTTEN_BEAST_638:POISONŠ#CREATURE:FORGOTTEN_BEAST_640:POISONŠ#CREATURE:FORGOTTEN_BEAST_641:POISONŠ#CREATURE:FORGOTTEN_BEAST_644:POISONŠ#CREATURE:FORGOTTEN_BEAST_645:POISONŠ#CREATURE:FORGOTTEN_BEAST_646:POISONŠ#CREATURE:FORGOTTEN_BEAST_647:POISONŠ#CREATURE:FORGOTTEN_BEAST_648:POISONŠ#CREATURE:FORGOTTEN_BEAST_649:POISONŠ#CREATURE:FORGOTTEN_BEAST_652:POISONŠ#CREATURE:FORGOTTEN_BEAST_656:POISONŠ#CREATURE:FORGOTTEN_BEAST_658:POISONŠ#CREATURE:FORGOTTEN_BEAST_659:POISONŠ#CREATURE:FORGOTTEN_BEAST_660:POISONŠ#CREATURE:FORGOTTEN_BEAST_663:POISONŠ#CREATURE:FORGOTTEN_BEAST_664:POISONŠ#CREATURE:FORGOTTEN_BEAST_666:POISONŠ#CREATURE:FORGOTTEN_BEAST_667:POISONŠ#CREATURE:FORGOTTEN_BEAST_668:POISONŠ#CREATURE:FORGOTTEN_BEAST_669:POISONŠ#CREATURE:FORGOTTEN_BEAST_670:POISONŠ#CREATURE:FORGOTTEN_BEAST_671:POISONŠ#CREATURE:FORGOTTEN_BEAST_674:POISONŠ#CREATURE:FORGOTTEN_BEAST_675:POISONŠ#CREATURE:FORGOTTEN_BEAST_678:POISONŠ#CREATURE:FORGOTTEN_BEAST_679:POISONŠ#CREATURE:FORGOTTEN_BEAST_681:POISONŠ#CREATURE:FORGOTTEN_BEAST_682:POISONŠ#CREATURE:FORGOTTEN_BEAST_686:POISONŠ#CREATURE:FORGOTTEN_BEAST_688:POISONŠ#CREATURE:FORGOTTEN_BEAST_689:POISONŠ#CREATURE:FORGOTTEN_BEAST_691:POISONŠ#CREATURE:FORGOTTEN_BEAST_692:POISONŠ#CREATURE:FORGOTTEN_BEAST_696:POISONŠ#CREATURE:FORGOTTEN_BEAST_698:POISONŠ#CREATURE:FORGOTTEN_BEAST_699:POISONŠ#CREATURE:FORGOTTEN_BEAST_702:POISONŠ#CREATURE:FORGOTTEN_BEAST_703:POISONŠ#CREATURE:FORGOTTEN_BEAST_704:POISONŠ#CREATURE:FORGOTTEN_BEAST_705:POISONŠ#CREATURE:FORGOTTEN_BEAST_707:POISONŠ#CREATURE:FORGOTTEN_BEAST_708:POISONŠ#CREATURE:FORGOTTEN_BEAST_709:POISONŠ#CREATURE:FORGOTTEN_BEAST_711:POISONŠ#CREATURE:FORGOTTEN_BEAST_712:POISONŠ#CREATURE:FORGOTTEN_BEAST_713:POISONŠ#CREATURE:FORGOTTEN_BEAST_714:POISONŠ#CREATURE:FORGOTTEN_BEAST_715:POISONŠ#CREATURE:FORGOTTEN_BEAST_718:POISONŠ#CREATURE:FORGOTTEN_BEAST_719:POISONŠ#CREATURE:FORGOTTEN_BEAST_721:POISONŠ#CREATURE:FORGOTTEN_BEAST_722:POISONŠ#CREATURE:FORGOTTEN_BEAST_724:POISONŠ#CREATURE:FORGOTTEN_BEAST_725:POISONŠ#CREATURE:FORGOTTEN_BEAST_726:POISONŠ#CREATURE:FORGOTTEN_BEAST_727:POISONŠ#CREATURE:FORGOTTEN_BEAST_731:POISONŠ#CREATURE:FORGOTTEN_BEAST_732:POISONŠ#CREATURE:FORGOTTEN_BEAST_733:POISONŠ#CREATURE:FORGOTTEN_BEAST_735:POISONŠ#CREATURE:FORGOTTEN_BEAST_737:POISONŠ#CREATURE:FORGOTTEN_BEAST_738:POISONŠ#CREATURE:FORGOTTEN_BEAST_739:POISONŠ#CREATURE:FORGOTTEN_BEAST_740:POISONŠ#CREATURE:FORGOTTEN_BEAST_741:POISONŠ#CREATURE:FORGOTTEN_BEAST_742:POISONŠ#CREATURE:FORGOTTEN_BEAST_744:POISONŠ#CREATURE:FORGOTTEN_BEAST_746:POISONŠ#CREATURE:FORGOTTEN_BEAST_748:POISONŠ#CREATURE:FORGOTTEN_BEAST_749:POISONŠ#CREATURE:FORGOTTEN_BEAST_750:POISONŠ#CREATURE:FORGOTTEN_BEAST_751:POISONŠ#CREATURE:FORGOTTEN_BEAST_752:POISONŠ#CREATURE:FORGOTTEN_BEAST_753:POISONŠ#CREATURE:FORGOTTEN_BEAST_756:POISONŠ#CREATURE:FORGOTTEN_BEAST_758:POISONŠ#CREATURE:FORGOTTEN_BEAST_760:POISONŠ#CREATURE:FORGOTTEN_BEAST_761:POISONŠ#CREATURE:FORGOTTEN_BEAST_762:POISONŠ#CREATURE:FORGOTTEN_BEAST_764:POISONŠ#CREATURE:FORGOTTEN_BEAST_766:POISONŠ#CREATURE:FORGOTTEN_BEAST_767:POISONŠ#CREATURE:FORGOTTEN_BEAST_769:POISONŠ#CREATURE:FORGOTTEN_BEAST_770:POISONŠ#CREATURE:FORGOTTEN_BEAST_771:POISONŠ#CREATURE:FORGOTTEN_BEAST_772:POISONŠ#CREATURE:FORGOTTEN_BEAST_773:POISONŠ#CREATURE:FORGOTTEN_BEAST_777:POISONŠ#CREATURE:FORGOTTEN_BEAST_778:POISONŠ#CREATURE:FORGOTTEN_BEAST_779:POISONŠ#CREATURE:FORGOTTEN_BEAST_780:POISONŠ#CREATURE:FORGOTTEN_BEAST_781:POISONŠ#CREATURE:FORGOTTEN_BEAST_783:POISONŠ#CREATURE:FORGOTTEN_BEAST_785:POISONŠ#CREATURE:FORGOTTEN_BEAST_787:POISONŠ#CREATURE:FORGOTTEN_BEAST_788:POISONŠ#CREATURE:FORGOTTEN_BEAST_790:POISONŠ#CREATURE:FORGOTTEN_BEAST_792:POISONŠ#CREATURE:FORGOTTEN_BEAST_793:POISONŠ#CREATURE:FORGOTTEN_BEAST_794:POISONŠ#CREATURE:FORGOTTEN_BEAST_795:POISONŠ#CREATURE:FORGOTTEN_BEAST_796:POISONŠ#CREATURE:FORGOTTEN_BEAST_798:POISONŠ#CREATURE:FORGOTTEN_BEAST_799:POISONŠ#CREATURE:FORGOTTEN_BEAST_800:POISONŠ#CREATURE:FORGOTTEN_BEAST_801:POISONŠ#CREATURE:FORGOTTEN_BEAST_802:POISONŠ#CREATURE:FORGOTTEN_BEAST_803:POISONŠ#CREATURE:FORGOTTEN_BEAST_806:POISONŠ#CREATURE:FORGOTTEN_BEAST_807:POISONŠ#CREATURE:FORGOTTEN_BEAST_810:POISONŠ#CREATURE:FORGOTTEN_BEAST_811:POISONŠ#CREATURE:FORGOTTEN_BEAST_813:POISONŠ#CREATURE:FORGOTTEN_BEAST_815:POISONŠ#CREATURE:FORGOTTEN_BEAST_818:POISONŠ#CREATURE:FORGOTTEN_BEAST_819:POISONŠ#CREATURE:FORGOTTEN_BEAST_820:POISONŠ#CREATURE:FORGOTTEN_BEAST_822:POISONŠ#CREATURE:FORGOTTEN_BEAST_823:POISONŠ#CREATURE:FORGOTTEN_BEAST_825:POISONŠ#CREATURE:FORGOTTEN_BEAST_826:POISONŠ#CREATURE:FORGOTTEN_BEAST_828:POISONŠ#CREATURE:FORGOTTEN_BEAST_830:POISONŠ#CREATURE:FORGOTTEN_BEAST_831:POISONŠ#CREATURE:FORGOTTEN_BEAST_832:POISONŠ#CREATURE:FORGOTTEN_BEAST_835:POISONŠ#CREATURE:FORGOTTEN_BEAST_836:POISONŠ#CREATURE:FORGOTTEN_BEAST_837:POISONŠ#CREATURE:FORGOTTEN_BEAST_839:POISONŠ#CREATURE:FORGOTTEN_BEAST_840:POISONŠ#CREATURE:FORGOTTEN_BEAST_841:POISONŠ#CREATURE:FORGOTTEN_BEAST_844:POISONŠ#CREATURE:FORGOTTEN_BEAST_845:POISONŠ#CREATURE:FORGOTTEN_BEAST_846:POISONŠ#CREATURE:FORGOTTEN_BEAST_847:POISONŠ#CREATURE:FORGOTTEN_BEAST_848:POISONŠ#CREATURE:FORGOTTEN_BEAST_849:POISONŠ#CREATURE:FORGOTTEN_BEAST_850:POISONŠ#CREATURE:FORGOTTEN_BEAST_851:POISONŠ#CREATURE:FORGOTTEN_BEAST_852:POISONŠ#CREATURE:FORGOTTEN_BEAST_853:POISONŠ#CREATURE:FORGOTTEN_BEAST_854:POISONŠ#CREATURE:FORGOTTEN_BEAST_856:POISONŠ#CREATURE:FORGOTTEN_BEAST_857:POISONŠ#CREATURE:FORGOTTEN_BEAST_859:POISONŠ#CREATURE:FORGOTTEN_BEAST_861:POISONŠ#CREATURE:FORGOTTEN_BEAST_862:POISONŠ#CREATURE:FORGOTTEN_BEAST_865:POISONŠCREATURE:TITAN_1:POISONŠCREATURE:TITAN_2:POISONŠCREATURE:TITAN_3:POISONŠCREATURE:TITAN_5:POISONŠCREATURE:TITAN_9:POISONŠCREATURE:TITAN_10:POISONŠCREATURE:TITAN_11:POISONŠCREATURE:TITAN_12:POISONŠCREATURE:TITAN_13:POISONŠCREATURE:TITAN_14:POISONŠCREATURE:TITAN_17:POISONŠCREATURE:TITAN_18:POISONŠCREATURE:TITAN_19:POISONŠCREATURE:TITAN_20:POISONŠCREATURE:TITAN_24:POISONŠCREATURE:TITAN_25:POISONŠCREATURE:TITAN_27:POISONŠCREATURE:TITAN_29:POISONŠCREATURE:TITAN_30:POISONŠCREATURE:TITAN_31:POISONŠCREATURE:DEMON_2:POISONŠCREATURE:DEMON_4:POISONŠCREATURE:DEMON_6:POISONŠCREATURE:DEMON_7:POISONŠCREATURE:DEMON_8:POISONŠCREATURE:DEMON_9:POISONŠCREATURE:DEMON_10:POISONŠCREATURE:DEMON_11:POISONŠCREATURE:DEMON_12:POISONŠCREATURE:DEMON_14:POISONŠCREATURE:DEMON_15:POISONŠCREATURE:DEMON_16:POISONŠCREATURE:DEMON_17:POISONŠCREATURE:DEMON_18:POISONŠCREATURE:DEMON_20:POISONŠCREATURE:DEMON_21:POISONŠCREATURE:DEMON_22:POISONŠCREATURE:DEMON_24:POISONŠCREATURE:DEMON_25:POISONŠCREATURE:DEMON_26:POISONŠCREATURE:DEMON_28:POISONŠCREATURE:DEMON_30:POISONŠCREATURE:DEMON_31:POISONŠCREATURE:DEMON_32:POISONŠCREATURE:DEMON_40:POISONŠCREATURE:DEMON_41:POISONŠCREATURE:DEMON_43:POISONŠCREATURE:DEMON_44:POISONŠCREATURE:DEMON_45:POISONŠCREATURE:DEMON_47:POISONŠCREATURE:DEMON_49:POISONŠ!CREATURE:NIGHT_CREATURE_64:POISONŠ!CREATURE:NIGHT_CREATURE_66:POISONŠ!CREATURE:NIGHT_CREATURE_75:POISONŠ!CREATURE:NIGHT_CREATURE_83:POISONŠ!CREATURE:NIGHT_CREATURE_85:POISONŠCREATURE:HF1108 DIVINE_3:POISONŠCREATURE:HF1249 DIVINE_3:POISONŠCREATURE:HF1345 DIVINE_1:POISON’LYE’INORGANIC:MILK_OF_LIME˜¢CUTTLEFISH:FEMALE¢CUTTLEFISH:MALE¢NAUTILUS:FEMALE¢ NAUTILUS:MALE¢MOGHOPPER:FEMALE¢MOGHOPPER:MALE¢POND_TURTLE:FEMALE¢POND_TURTLE:MALE¢MUSSEL:DEFAULT¢OYSTER:DEFAULT¢FISH_SALMON:FEMALE¢FISH_SALMON:MALE¢FISH_CLOWNFISH:FEMALE¢FISH_CLOWNFISH:MALE¢FISH_HAGFISH:FEMALE¢FISH_HAGFISH:MALE¢FISH_LAMPREY_BROOK:FEMALE¢FISH_LAMPREY_BROOK:MALE¢FISH_RAY_BAT:FEMALE¢FISH_RAY_BAT:MALE¢FISH_RAY_THORNBACK:FEMALE¢FISH_RAY_THORNBACK:MALE¢FISH_RATFISH_SPOTTED:FEMALE¢FISH_RATFISH_SPOTTED:MALE¢FISH_HERRING:FEMALE¢FISH_HERRING:MALE¢FISH_SHAD:FEMALE¢FISH_SHAD:MALE¢FISH_ANCHOVY:FEMALE¢FISH_ANCHOVY:MALE¢FISH_TROUT_STEELHEAD:FEMALE¢FISH_TROUT_STEELHEAD:MALE¢FISH_HAKE:FEMALE¢FISH_HAKE:MALE¢FISH_SEAHORSE:FEMALE¢FISH_SEAHORSE:MALE¢FISH_GLASSEYE:FEMALE¢FISH_GLASSEYE:MALE¢ FISH_PUFFER_WHITE_SPOTTED:FEMALE¢FISH_PUFFER_WHITE_SPOTTED:MALE¢FISH_SOLE:FEMALE¢FISH_SOLE:MALE¢FISH_FLOUNDER:FEMALE¢FISH_FLOUNDER:MALE¢FISH_MACKEREL:FEMALE¢FISH_MACKEREL:MALE¢JELLYFISH_SEA_NETTLE:DEFAULT¢ SQUID:FEMALE¢ +SQUID:MALE¢FISH_LUNGFISH:FEMALE¢FISH_LUNGFISH:MALE¢FISH_LOACH_CLOWN:FEMALE¢FISH_LOACH_CLOWN:MALE¢FISH_BULLHEAD_BROWN:FEMALE¢FISH_BULLHEAD_BROWN:MALE¢FISH_BULLHEAD_YELLOW:FEMALE¢FISH_BULLHEAD_YELLOW:MALE¢FISH_BULLHEAD_BLACK:FEMALE¢FISH_BULLHEAD_BLACK:MALE¢FISH_KNIFEFISH_BANDED:FEMALE¢FISH_KNIFEFISH_BANDED:MALE¢FISH_CHAR:FEMALE¢FISH_CHAR:MALE¢FISH_TROUT_RAINBOW:FEMALE¢FISH_TROUT_RAINBOW:MALE¢FISH_MOLLY_SAILFIN:FEMALE¢FISH_MOLLY_SAILFIN:MALE¢FISH_GUPPY:FEMALE¢FISH_GUPPY:MALE¢FISH_PERCH:FEMALE¢FISH_PERCH:MALE¢FISH_CAVE:FEMALE¢FISH_CAVE:MALE¢LOBSTER_CAVE:FEMALE¢LOBSTER_CAVE:MALE˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/furnitureprefix.dfstock b/data/stockpiles/furnitureprefix.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..6c52781fb100a82428db8d6c2005d998841ffe1a GIT binary patch literal 3259 zcmZ`*O>Y}F5Va7?v8gz1)X%8s#Yf$OAPCaiESK74#U(c+*NSpk7>NuRFq}YjfaYKH z*kgfSdhD^s{aWXN^r5Qyf202BU*V3IUFZ!8pu*1|j zFHR*zDk~F9@-c|hgpAPvP6>W1MBw&en;tG8acPZBsWBeVLPn=17TA z;z+v5L|btLJ+XqNU?>s&*Ol^CoD|xa5S>**kT0BvkWGb{DJq?X`l)y*DbZF%(In%oIJ#M`C>4)NU8*{$)?@K#f^5{V>?u>? zxPS$TAjJT}$~0OK$P*WHY$$*yv3>T;)KBNa1L>ktlUmx88BgP}H_6xSTSmjt6$SxeKAuH=ARrW)UR;q15QR z9XNVDF>3Vn&Idzt9u%^ga>HaQFaU;`%2g`r-K-^`EHeFcV*~+vqiuF4{O#sbV!+{g zowTnuxOYL-ZH1m}z(j|liuvgK9q3L2i;Pk#K)NOoOa+M8cr*HOqe3cAg_bp#1lHLZ zc{TcB_jB8Y|M#(UUMfSV1&e_~-f$6YU?3dY1)HX6^>ayNHm*QMi=tW!GJ=JiP*i3^ zqMSSGyhEY`naCxLF4Rhr)kHd*8CM`JQE?We4yXpkY)kZM>;hz_JmZ+3wzk9RL+X%97FnhZ<%Y2Rz z-}N=f97Dh3y;T}L&f+P^W4Lo4a#f(CyV{X{*CvPYHpAG`s?87UYm2+*>yF?}xPsSB zxcgx}!5B9RLdWG>5oI8dB|aBI*^p}63yDdFt1Zm>TRKeLM8{@GxGL~c8=bWQGU;_F z=tT5Fcfd()En&HEib{uF)h3QnG%D=YT&3F%HrwQG%CKr=^D@8^TL#z@l8@1!RU9$> zWtFBFSgY1x^)>+FbW^g^)^meOwMFx!E zmYgrqwKj4rM4hx(nRQSQ^=c~3S})Aa`4$PWHdl?t?qcm}Op(xGur@He#T;Kn-5ov! zWq6Hj+sOTDbg)E}bMH*mI>?A#YP`MBI_PWecS6;hM&~Y%?spSw;{;EIH~cyRJa0Au zGv82UZHEOuC5W~`GSQ%XiG*({a99^^l_VyMO@c`a9g5QpC~4?gL+)9lPcGtDB4E=d;0VaeXb9 z7niq-_h+Y*tDD93=ey%^`1EnHy!x;_8>nC3Ek52}eYm-}K07Hcez{#NFF#$M9nhj{et{^=e3+dKFVF(_?_ literal 0 HcmV?d00001 diff --git a/data/stockpiles/gemsprefix.dfstock b/data/stockpiles/gemsprefix.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..8d7b229a1145587d2dc0cda9e7cda76c80d8ed76 GIT binary patch literal 7728 zcmZ`;yOQL%5!`m~;-uB;b=NvYbkNa1DCB@7hQu~VZ~?GqI8OhP%rj-mlqpl6ro@9p zlAJTU0Ms+9kVL=#@{jMn@3e64*;y&a^+%(nu;2ZVRvNoFFZ37Qxc-P6zw9?&f1^fY zJ29%}?`B-yuAj^A!B}NRmbj)jR$u>6_Gr<_o9Kj>*WVXut!H`rDq4B{QxR%0DRz~V3Eglj|Y|0_UFsbpL zgj54R*J}+(`l9SAC4-ZmhgD3qsjYb(q1a8*UVki(OserS^yC#f;RbVGZr^5M{ri=o zd=+mg9P;`Vl{?i_3^f8g7O68jZ~a~I0!6b5b*Bt1Qfu?N`bGA=>ZFy+4kS@_Qx;&R zoo*gaaI(^=F*!wzFfBH~=1Lxgp1XN7+d=5y+D~Pzvtqe5z0i#e_2#7(RfQ_mWOPuu zT#txO>8JtsBqZanJ-lHHe=WvejP>e8HNvBHvlS};p!9t$8Wm_T=A4UUCU!AtNJvON z?SL{$F-c9;0^`#fhzY5ABtqWY zSzcpy-EZ&8Hoyh#Pjb}UOp@_3NR2TG9Q>uETaB&i3r5WL%TI%OCTi}v` zBD8#ihp`4^&WrIqRhWdh1jFqJ%|Dm2Ka|nZ-!Ls5rn(QIQr%-jx$czj`#ioM^fYQ5 z#ehaFnIS7Znt!?FNtg0Kx&MlJk#7zLZye;ghLnq}<&5?)aOIfOOvdv84w3F`j{>|i za-twrfF@N|Qlg{8+PPK@V%Ya;oU7-a=?vy$tO9Akko%jvg;`Gth$LqPF?E0#r0>Cv z*lB@@F@P1Ie3aM>8-S0Cnux-w3IT}EDPWS37o||}0=Cw(?zj*K%t;!@Ek_df=HYeP z-JpxiNvh>TjeLJc9lfwxew(pEd5SAQ>W8ie3KJJ7Ev*VjZJ&B!v#a!F;n5-oX@zc? zI-%SPPg956qCcchTE$_tI8$BR5FJpqeKLg9nCRg_RZEAnS6ZBEPmc8H-jCYODzUfYuYF|6Q^W zvnUG>#J>7m##bWtEJ?(^qH-5v&+0c~UlEj;*jF?pBCBSdh1j#S5POyuV$b$BV$Z^j z*jF@!iP)dTk=R$1p}@^qyOcSg9iIR}NUdFw{(xm_}`=M3d5BhgH@zY=>k zRfs+BT!}sJEX1B+pNM@$H&(hn3LU9_o!Ilzf!MRQqhL^H6r$mR3`Q;XJUVrC%(#f zS&2Q59qv zn*L!WCyH;JmDqE`hOHaH{-v5*z@X{*mJE4v1eGM7n)o{x>54H%nNiYGqGpm`2Z1n zzOy5bXKxCzXH;h;rS4qZmDscUX|Cqn(dLcVb7?kWf7X!Wq}z8T_Uv>a_AD*Lo{LJv zo;N3c95Bhqi&CIB68qEkICxIdIBq$Ts3Z@s)9y&@Ptr*2xpkwCjo6>G1F0Xnz7l&j zu##fEd?bBY80wkWGj$3)m?zRom^p2$iHHJD{boe5Q6rFMEl=ewTcpT=uk(jWZhC3`ULf?Hi;4%!@M+DWR>c=8=1QHYF@xBe z?U(q)&%0_71E72$ad=dP`p@M9q!r;Eo^_epDHL({fs?NNLpV+!VxIa%hnFALkIxz9 zJWIM!D(6^&5C0lPAy0d}v-yKxNWY26q02E$F@?u7*rX4R5oaIr8OAQ-FN*<{2fhml ze|e!X6YguSI9`+m{NG2^0YIbR26a;a)zOOve-Ghk6ccrA44x@!<>vuKm>ri|t1bpe zyR&Qfn8oMhJaCTU#a4@*!;Pr{+Tbuj)JtzgFXJN{rwM-`jnAA7LPWPr4wmSX4F6&^ z)ei+TU!`aL)7;{Tu}iJNA2zUy2XNUw!^a7`4w&P`0bWvD*H#Tl1=aVi!e0{+Qxs6G z+%WrI9b3gpp@9gtWbA~l9X-pOS9Lr})&W7(E4V~Y2yW4jAR=Jmu1JkWX9LN@6mh$a zO2ce8OqsQ_SNq{J2z0{{=(q;c=%7Rd2B_!gAOU?+zUk3A=xg@77V}#$ea4Z%e@+oc zG!biK#Ty?-fDb&xF~S2>vKy^z#pLcl7ggw(<<}_p@MdV3@$jID?UK0VBf-Mu`oK3_BPZ4lptR0Omsq A1poj5 literal 0 HcmV?d00001 diff --git a/data/stockpiles/leatherprefix.dfstock b/data/stockpiles/leatherprefix.dfstock new file mode 100644 index 000000000..d8979ca40 --- /dev/null +++ b/data/stockpiles/leatherprefix.dfstock @@ -0,0 +1,1363 @@ +jâê +CREATURE:TOAD:LEATHER +CREATURE:TOAD_MAN:LEATHER +CREATURE:GIANT_TOAD:LEATHER +CREATURE:WORM:LEATHER +CREATURE:WORM_MAN:LEATHER +CREATURE:BIRD_BLUEJAY:LEATHER +CREATURE:BLUEJAY_MAN:LEATHER +CREATURE:GIANT_BLUEJAY:LEATHER +CREATURE:BIRD_CARDINAL:LEATHER +CREATURE:CARDINAL_MAN:LEATHER +CREATURE:GIANT_CARDINAL:LEATHER +CREATURE:BIRD_GRACKLE:LEATHER +CREATURE:GRACKLE_MAN:LEATHER +CREATURE:GIANT_GRACKLE:LEATHER +CREATURE:BIRD_ORIOLE:LEATHER +CREATURE:ORIOLE_MAN:LEATHER +CREATURE:GIANT_ORIOLE:LEATHER +"CREATURE:BIRD_RW_BLACKBIRD:LEATHER +!CREATURE:RW_BLACKBIRD_MAN:LEATHER +#CREATURE:GIANT_RW_BLACKBIRD:LEATHER +CREATURE:BIRD_PENGUIN:LEATHER +$CREATURE:BIRD_PENGUIN_LITTLE:LEATHER +%CREATURE:BIRD_PENGUIN_EMPEROR:LEATHER +CREATURE:PENGUIN MAN:LEATHER +#CREATURE:BIRD_PENGUIN_GIANT:LEATHER +&CREATURE:BIRD_FALCON_PEREGRINE:LEATHER +%CREATURE:PEREGRINE FALCON MAN:LEATHER +'CREATURE:GIANT PEREGRINE FALCON:LEATHER +CREATURE:BIRD_KIWI:LEATHER +CREATURE:KIWI MAN:LEATHER + CREATURE:BIRD_KIWI_GIANT:LEATHER +CREATURE:BIRD_OSTRICH:LEATHER +CREATURE:OSTRICH MAN:LEATHER +#CREATURE:BIRD_OSTRICH_GIANT:LEATHER +CREATURE:BIRD_CROW:LEATHER +CREATURE:CROW_MAN:LEATHER +CREATURE:GIANT_CROW:LEATHER +CREATURE:BIRD_RAVEN:LEATHER +CREATURE:RAVEN_MAN:LEATHER +CREATURE:GIANT_RAVEN:LEATHER +CREATURE:BIRD_CASSOWARY:LEATHER +CREATURE:CASSOWARY_MAN:LEATHER + CREATURE:GIANT_CASSOWARY:LEATHER +CREATURE:BIRD_KEA:LEATHER +CREATURE:KEA_MAN:LEATHER +CREATURE:GIANT_KEA:LEATHER +CREATURE:BIRD_OWL_SNOWY:LEATHER +CREATURE:SNOWY_OWL_MAN:LEATHER + CREATURE:GIANT_SNOWY_OWL:LEATHER +CREATURE:SPARROW:LEATHER +CREATURE:SPARROW_MAN:LEATHER +CREATURE:GIANT_SPARROW:LEATHER +!CREATURE:BIRD_STORK_WHITE:LEATHER + CREATURE:WHITE_STORK_MAN:LEATHER +"CREATURE:GIANT_WHITE_STORK:LEATHER +CREATURE:BIRD_LOON:LEATHER +CREATURE:LOON_MAN:LEATHER +CREATURE:GIANT_LOON:LEATHER +CREATURE:BIRD_OWL_BARN:LEATHER +CREATURE:BARN_OWL_MAN:LEATHER +CREATURE:GIANT_BARN_OWL:LEATHER +CREATURE:BIRD_PARAKEET:LEATHER +CREATURE:PARAKEET_MAN:LEATHER +CREATURE:GIANT_PARAKEET:LEATHER +CREATURE:BIRD_KAKAPO:LEATHER +CREATURE:KAKAPO_MAN:LEATHER +CREATURE:GIANT_KAKAPO:LEATHER +!CREATURE:BIRD_PARROT_GREY:LEATHER + CREATURE:GREY_PARROT_MAN:LEATHER +"CREATURE:GIANT_GREY_PARROT:LEATHER +CREATURE:BIRD_PUFFIN:LEATHER +CREATURE:PUFFIN_MAN:LEATHER +CREATURE:GIANT_PUFFIN:LEATHER +CREATURE:BIRD_SWAN:LEATHER +CREATURE:SWAN_MAN:LEATHER +CREATURE:GIANT_SWAN:LEATHER +CREATURE:BIRD_LORIKEET:LEATHER +CREATURE:LORIKEET_MAN:LEATHER +CREATURE:GIANT_LORIKEET:LEATHER +CREATURE:BIRD_WREN:LEATHER +CREATURE:WREN_MAN:LEATHER +CREATURE:GIANT_WREN:LEATHER +CREATURE:BIRD_OSPREY:LEATHER +CREATURE:OSPREY_MAN:LEATHER +CREATURE:GIANT_OSPREY:LEATHER +CREATURE:BIRD_EMU:LEATHER +CREATURE:EMU_MAN:LEATHER +CREATURE:GIANT_EMU:LEATHER +CREATURE:BIRD_COCKATIEL:LEATHER +CREATURE:COCKATIEL_MAN:LEATHER + CREATURE:GIANT_COCKATIEL:LEATHER +*CREATURE:BIRD_LOVEBIRD_PEACH-FACED:LEATHER +)CREATURE:PEACH-FACED_LOVEBIRD_MAN:LEATHER ++CREATURE:GIANT_PEACH-FACED_LOVEBIRD:LEATHER +CREATURE:BIRD_MAGPIE:LEATHER +CREATURE:MAGPIE_MAN:LEATHER +CREATURE:GIANT_MAGPIE:LEATHER +CREATURE:BIRD_KESTREL:LEATHER +CREATURE:KESTREL_MAN:LEATHER +CREATURE:GIANT_KESTREL:LEATHER +CREATURE:BIRD_ALBATROSS:LEATHER +CREATURE:ALBATROSS_MAN:LEATHER + CREATURE:GIANT_ALBATROSS:LEATHER +&CREATURE:BIRD_OWL_GREAT_HORNED:LEATHER +%CREATURE:GREAT_HORNED_OWL_MAN:LEATHER +'CREATURE:GIANT_GREAT_HORNED_OWL:LEATHER +CREATURE:BIRD_EAGLE:LEATHER +CREATURE:EAGLE_MAN:LEATHER +CREATURE:GIANT_EAGLE:LEATHER +CREATURE:BIRD_HORNBILL:LEATHER +CREATURE:HORNBILL_MAN:LEATHER +CREATURE:GIANT_HORNBILL:LEATHER +%CREATURE:BIRD_LOVEBIRD_MASKED:LEATHER +$CREATURE:MASKED_LOVEBIRD_MAN:LEATHER +&CREATURE:GIANT_MASKED_LOVEBIRD:LEATHER +CREATURE:BIRD_BUSHTIT:LEATHER +CREATURE:BUSHTIT_MAN:LEATHER +CREATURE:GIANT_BUSHTIT:LEATHER +CREATURE:SLUG:LEATHER +CREATURE:SLUG_MAN:LEATHER +CREATURE:GIANT_SLUG:LEATHER +CREATURE:MOON_SNAIL:LEATHER +CREATURE:MOON_SNAIL_MAN:LEATHER +!CREATURE:GIANT_MOON_SNAIL:LEATHER +CREATURE:SNAIL:LEATHER +CREATURE:SNAIL_MAN:LEATHER +CREATURE:GIANT_SNAIL:LEATHER +CREATURE:DOG:LEATHER +CREATURE:CAT:LEATHER +CREATURE:MULE:LEATHER +CREATURE:DONKEY:LEATHER +CREATURE:HORSE:LEATHER +CREATURE:COW:LEATHER +CREATURE:SHEEP:LEATHER +CREATURE:PIG:LEATHER +CREATURE:GOAT:LEATHER +CREATURE:BIRD_CHICKEN:LEATHER +CREATURE:CAVY:LEATHER +CREATURE:BIRD_DUCK:LEATHER +CREATURE:WATER_BUFFALO:LEATHER +CREATURE:REINDEER:LEATHER +CREATURE:BIRD_GOOSE:LEATHER +CREATURE:YAK:LEATHER +CREATURE:LLAMA:LEATHER +CREATURE:ALPACA:LEATHER + CREATURE:BIRD_GUINEAFOWL:LEATHER +"CREATURE:BIRD_PEAFOWL_BLUE:LEATHER +CREATURE:BIRD_TURKEY:LEATHER +CREATURE:RABBIT:LEATHER +CREATURE:GOAT_MOUNTAIN:LEATHER +"CREATURE:GOAT_MOUNTAIN_MAN:LEATHER +$CREATURE:GIANT_GOAT_MOUNTAIN:LEATHER +CREATURE:MARMOT_HOARY:LEATHER +!CREATURE:MARMOT_HOARY_MAN:LEATHER +#CREATURE:GIANT_MARMOT_HOARY:LEATHER +CREATURE:GNOME_MOUNTAIN:LEATHER +CREATURE:GNOME_DARK:LEATHER +CREATURE:WALRUS:LEATHER +CREATURE:WALRUS_MAN:LEATHER +CREATURE:GIANT_WALRUS:LEATHER +!CREATURE:FISH_LAMPREY_SEA:LEATHER +"CREATURE:SHARK_GREAT_WHITE:LEATHER +CREATURE:SHARK_FRILL:LEATHER +$CREATURE:SHARK_SPINY_DOGFISH:LEATHER +(CREATURE:SHARK_WOBBEGONG_SPOTTED:LEATHER +CREATURE:SHARK_WHALE:LEATHER +CREATURE:SHARK_BASKING:LEATHER +CREATURE:SHARK_NURSE:LEATHER +$CREATURE:SHARK_MAKO_SHORTFIN:LEATHER +#CREATURE:SHARK_MAKO_LONGFIN:LEATHER +CREATURE:SHARK_TIGER:LEATHER +CREATURE:SHARK_BULL:LEATHER +$CREATURE:SHARK_REEF_BLACKTIP:LEATHER +$CREATURE:SHARK_REEF_WHITETIP:LEATHER +CREATURE:SHARK_BLUE:LEATHER +!CREATURE:SHARK_HAMMERHEAD:LEATHER +CREATURE:SHARK_ANGEL:LEATHER +"CREATURE:FISH_SKATE_COMMON:LEATHER +CREATURE:FISH_RAY_MANTA:LEATHER +CREATURE:FISH_STINGRAY:LEATHER + CREATURE:FISH_CONGER_EEL:LEATHER +CREATURE:NARWHAL:LEATHER +CREATURE:NARWHAL MAN:LEATHER +CREATURE:NARWHAL, GIANT:LEATHER +CREATURE:HIPPO:LEATHER +CREATURE:HIPPO_MAN:LEATHER +CREATURE:GIANT_HIPPO:LEATHER +CREATURE:PLATYPUS:LEATHER +CREATURE:PLATYPUS MAN:LEATHER + CREATURE:PLATYPUS, GIANT:LEATHER +CREATURE:BEAR_GRIZZLY:LEATHER +!CREATURE:BEAR_GRIZZLY_MAN:LEATHER +#CREATURE:GIANT_BEAR_GRIZZLY:LEATHER +CREATURE:BEAR_BLACK:LEATHER +CREATURE:BEAR_BLACK_MAN:LEATHER +!CREATURE:GIANT_BEAR_BLACK:LEATHER +CREATURE:DEER:LEATHER +CREATURE:DEER_MAN:LEATHER +CREATURE:GIANT_DEER:LEATHER +CREATURE:FOX:LEATHER +CREATURE:FOX_MAN:LEATHER +CREATURE:GIANT_FOX:LEATHER +CREATURE:RACCOON:LEATHER +CREATURE:RACCOON_MAN:LEATHER +CREATURE:GIANT_RACCOON:LEATHER +CREATURE:MACAQUE_RHESUS:LEATHER +#CREATURE:MACAQUE_RHESUS_MAN:LEATHER +%CREATURE:GIANT_MACAQUE_RHESUS:LEATHER +CREATURE:COUGAR:LEATHER +CREATURE:COUGAR_MAN:LEATHER +CREATURE:GIANT_COUGAR:LEATHER +CREATURE:WOLF:LEATHER +CREATURE:WOLF_MAN:LEATHER +CREATURE:GIANT_WOLF:LEATHER +CREATURE:GROUNDHOG:LEATHER +CREATURE:GROUNDHOG_MAN:LEATHER + CREATURE:GIANT_GROUNDHOG:LEATHER +CREATURE:BIRD_BUZZARD:LEATHER +CREATURE:BUZZARD_MAN:LEATHER +CREATURE:GIANT_BUZZARD:LEATHER +CREATURE:PANDA:LEATHER + CREATURE:PANDA, GIGANTIC:LEATHER +CREATURE:PANDA MAN:LEATHER +CREATURE:CAPYBARA:LEATHER + CREATURE:CAPYBARA, GIANT:LEATHER +CREATURE:CAPYBARA MAN:LEATHER +CREATURE:BADGER:LEATHER +CREATURE:BADGER MAN:LEATHER +CREATURE:BADGER, GIANT:LEATHER +CREATURE:MOOSE:LEATHER +CREATURE:MOOSE MAN:LEATHER +CREATURE:MOOSE, GIANT:LEATHER +CREATURE:RED PANDA:LEATHER +CREATURE:RED PANDA MAN:LEATHER +!CREATURE:RED PANDA, GIANT:LEATHER +CREATURE:ELEPHANT:LEATHER +CREATURE:ELEPHANT_MAN:LEATHER +CREATURE:GIANT_ELEPHANT:LEATHER +CREATURE:WARTHOG:LEATHER +CREATURE:WARTHOG_MAN:LEATHER +CREATURE:GIANT_WARTHOG:LEATHER +CREATURE:LION:LEATHER +CREATURE:LION_MAN:LEATHER +CREATURE:GIANT_LION:LEATHER +CREATURE:LEOPARD:LEATHER +CREATURE:LEOPARD_MAN:LEATHER +CREATURE:GIANT_LEOPARD:LEATHER +CREATURE:JAGUAR:LEATHER +CREATURE:JAGUAR_MAN:LEATHER +CREATURE:GIANT_JAGUAR:LEATHER +CREATURE:TIGER:LEATHER +CREATURE:TIGER_MAN:LEATHER +CREATURE:GIANT_TIGER:LEATHER +CREATURE:CHEETAH:LEATHER +CREATURE:CHEETAH_MAN:LEATHER +CREATURE:GIANT_CHEETAH:LEATHER +CREATURE:GAZELLE:LEATHER +CREATURE:GAZELLE_MAN:LEATHER +CREATURE:GIANT_GAZELLE:LEATHER +CREATURE:MANDRILL:LEATHER +CREATURE:MANDRILL_MAN:LEATHER +CREATURE:GIANT_MANDRILL:LEATHER +CREATURE:CHIMPANZEE:LEATHER +CREATURE:BONOBO:LEATHER +CREATURE:GORILLA:LEATHER +CREATURE:ORANGUTAN:LEATHER +CREATURE:GIBBON_SIAMANG:LEATHER +$CREATURE:GIBBON_WHITE_HANDED:LEATHER +$CREATURE:GIBBON_BLACK_HANDED:LEATHER +CREATURE:GIBBON_GRAY:LEATHER +CREATURE:GIBBON_SILVERY:LEATHER + CREATURE:GIBBON_PILEATED:LEATHER +CREATURE:GIBBON_BILOU:LEATHER +$CREATURE:GIBBON_WHITE_BROWED:LEATHER +%CREATURE:GIBBON_BLACK_CRESTED:LEATHER +CREATURE:CAMEL_1_HUMP:LEATHER +!CREATURE:CAMEL_1_HUMP_MAN:LEATHER +#CREATURE:GIANT_CAMEL_1_HUMP:LEATHER +CREATURE:CAMEL_2_HUMP:LEATHER +!CREATURE:CAMEL_2_HUMP_MAN:LEATHER +#CREATURE:GIANT_CAMEL_2_HUMP:LEATHER +CREATURE:BIRD_VULTURE:LEATHER +CREATURE:VULTURE_MAN:LEATHER +CREATURE:GIANT_VULTURE:LEATHER +CREATURE:RHINOCEROS:LEATHER +CREATURE:RHINOCEROS_MAN:LEATHER +!CREATURE:GIANT_RHINOCEROS:LEATHER +CREATURE:GIRAFFE:LEATHER +CREATURE:GIRAFFE_MAN:LEATHER +CREATURE:GIANT_GIRAFFE:LEATHER +CREATURE:HONEY BADGER:LEATHER +!CREATURE:HONEY BADGER MAN:LEATHER +$CREATURE:HONEY BADGER, GIANT:LEATHER +CREATURE:ARMADILLO:LEATHER +CREATURE:ARMADILLO MAN:LEATHER +!CREATURE:ARMADILLO, GIANT:LEATHER +CREATURE:MUSKOX:LEATHER +CREATURE:MUSKOX_MAN:LEATHER +CREATURE:GIANT_MUSKOX:LEATHER +CREATURE:ELK:LEATHER +CREATURE:ELK_MAN:LEATHER +CREATURE:GIANT_ELK:LEATHER +CREATURE:BEAR_POLAR:LEATHER +CREATURE:BEAR_POLAR_MAN:LEATHER +!CREATURE:GIANT_BEAR_POLAR:LEATHER +CREATURE:WOLVERINE:LEATHER +CREATURE:WOLVERINE_MAN:LEATHER + CREATURE:GIANT_WOLVERINE:LEATHER +CREATURE:CHINCHILLA:LEATHER +CREATURE:CHINCHILLA_MAN:LEATHER +!CREATURE:GIANT_CHINCHILLA:LEATHER +CREATURE:FLOATING_GUTS:LEATHER +CREATURE:DRUNIAN:LEATHER +CREATURE:CREEPING_EYE:LEATHER +'CREATURE:VORACIOUS_CAVE_CRAWLER:LEATHER + CREATURE:BLIND_CAVE_OGRE:LEATHER +CREATURE:CAP_HOPPER:LEATHER +CREATURE:HUNGRY_HEAD:LEATHER +CREATURE:ELK_BIRD:LEATHER +CREATURE:GREEN_DEVOURER:LEATHER +CREATURE:RUTHERER:LEATHER +CREATURE:CREEPY_CRAWLER:LEATHER +CREATURE:DRALTHA:LEATHER + CREATURE:GIANT_EARTHWORM:LEATHER +CREATURE:BUGBAT:LEATHER +CREATURE:MANERA:LEATHER +CREATURE:MOLEMARIAN:LEATHER +CREATURE:JABBERER:LEATHER +CREATURE:POND_GRABBER:LEATHER + CREATURE:BLIND_CAVE_BEAR:LEATHER +CREATURE:REACHER:LEATHER +CREATURE:GORLAK:LEATHER +CREATURE:CAVE_FLOATER:LEATHER +CREATURE:CAVE_BLOB:LEATHER +CREATURE:OCTOPUS:LEATHER +CREATURE:OCTOPUS_MAN:LEATHER +CREATURE:GIANT_OCTOPUS:LEATHER +CREATURE:LEOPARD_SEAL:LEATHER +!CREATURE:LEOPARD_SEAL_MAN:LEATHER +#CREATURE:GIANT_LEOPARD_SEAL:LEATHER +CREATURE:CUTTLEFISH:LEATHER +CREATURE:CUTTLEFISH_MAN:LEATHER +!CREATURE:GIANT_CUTTLEFISH:LEATHER +CREATURE:ORCA:LEATHER +CREATURE:ORCA_MAN:LEATHER +CREATURE:GIANT_ORCA:LEATHER +CREATURE:SPERM_WHALE:LEATHER + CREATURE:SPERM_WHALE_MAN:LEATHER +"CREATURE:GIANT_SPERM_WHALE:LEATHER +CREATURE:ELEPHANT_SEAL:LEATHER +"CREATURE:ELEPHANT_SEAL_MAN:LEATHER +$CREATURE:GIANT_ELEPHANT_SEAL:LEATHER +CREATURE:HARP_SEAL:LEATHER +CREATURE:HARP_SEAL_MAN:LEATHER + CREATURE:GIANT_HARP_SEAL:LEATHER +CREATURE:NAUTILUS:LEATHER +CREATURE:NAUTILUS_MAN:LEATHER +CREATURE:GIANT_NAUTILUS:LEATHER +CREATURE:FOXSQUIRREL:LEATHER +CREATURE:MOGHOPPER:LEATHER +CREATURE:RAT_DEMON:LEATHER +CREATURE:WAMBLER_FLUFFY:LEATHER +CREATURE:WORM_KNUCKLE:LEATHER +CREATURE:RIVER OTTER:LEATHER +CREATURE:SEA OTTER:LEATHER +CREATURE:OTTER_MAN:LEATHER +CREATURE:GIANT_OTTER:LEATHER +CREATURE:BEAVER:LEATHER +CREATURE:BEAVER_MAN:LEATHER +CREATURE:GIANT_BEAVER:LEATHER +CREATURE:LEECH:LEATHER +CREATURE:LEECH_MAN:LEATHER +CREATURE:GIANT_LEECH:LEATHER +CREATURE:AXOLOTL:LEATHER +CREATURE:AXOLOTL_MAN:LEATHER +CREATURE:GIANT_AXOLOTL:LEATHER +CREATURE:MINK:LEATHER +CREATURE:MINK_MAN:LEATHER +CREATURE:GIANT_MINK:LEATHER +CREATURE:RAT:LEATHER +CREATURE:RAT_MAN:LEATHER +CREATURE:SQUIRREL_GRAY:LEATHER +"CREATURE:SQUIRREL_GRAY_MAN:LEATHER +$CREATURE:GIANT_SQUIRREL_GRAY:LEATHER +CREATURE:SQUIRREL_RED:LEATHER +!CREATURE:SQUIRREL_RED_MAN:LEATHER +#CREATURE:GIANT_SQUIRREL_RED:LEATHER +CREATURE:CHIPMUNK:LEATHER +CREATURE:CHIPMUNK_MAN:LEATHER +CREATURE:GIANT_CHIPMUNK:LEATHER +CREATURE:HAMSTER:LEATHER +CREATURE:HAMSTER_MAN:LEATHER +CREATURE:GIANT_HAMSTER:LEATHER +CREATURE:HEDGEHOG:LEATHER +CREATURE:HEDGEHOG_MAN:LEATHER +CREATURE:GIANT_HEDGEHOG:LEATHER + CREATURE:SQUIRREL_FLYING:LEATHER +$CREATURE:FLYING_SQUIRREL_MAN:LEATHER +&CREATURE:GIANT_FLYING_SQUIRREL:LEATHER +CREATURE:MUSSEL:LEATHER +CREATURE:OYSTER:LEATHER +CREATURE:FISH_HAGFISH:LEATHER +#CREATURE:FISH_LAMPREY_BROOK:LEATHER +CREATURE:FISH_RAY_BAT:LEATHER +#CREATURE:FISH_RAY_THORNBACK:LEATHER +CREATURE:FISH_SEAHORSE:LEATHER +*CREATURE:FISH_PUFFER_WHITE_SPOTTED:LEATHER +%CREATURE:JELLYFISH_SEA_NETTLE:LEATHER +CREATURE:SQUID:LEATHER +CREATURE:SQUID MAN:LEATHER +CREATURE:GIGANTIC SQUID:LEATHER +CREATURE:DWARF:LEATHER +CREATURE:HUMAN:LEATHER +CREATURE:ELF:LEATHER +CREATURE:GOBLIN:LEATHER +CREATURE:KOBOLD:LEATHER +CREATURE:GREMLIN:LEATHER +CREATURE:TROLL:LEATHER +CREATURE:OGRE:LEATHER +CREATURE:UNICORN:LEATHER +CREATURE:SATYR:LEATHER +CREATURE:GIANT:LEATHER +CREATURE:CYCLOPS:LEATHER +CREATURE:ETTIN:LEATHER +CREATURE:MINOTAUR:LEATHER +CREATURE:YETI:LEATHER +CREATURE:SASQUATCH:LEATHER +CREATURE:BLIZZARD_MAN:LEATHER +CREATURE:WOLF_ICE:LEATHER +CREATURE:FAIRY:LEATHER +CREATURE:PIXIE:LEATHER +CREATURE:BEAK_DOG:LEATHER +CREATURE:GRIMELING:LEATHER +CREATURE:BLENDEC_FOUL:LEATHER +CREATURE:STRANGLER:LEATHER +CREATURE:NIGHTWING:LEATHER +CREATURE:HARPY:LEATHER +CREATURE:MERPERSON:LEATHER +CREATURE:BIRD_ROC:LEATHER + CREATURE:TOAD_GIANT_CAVE:LEATHER +CREATURE:OLM_GIANT:LEATHER +CREATURE:BAT_GIANT:LEATHER +CREATURE:RAT_GIANT:LEATHER +CREATURE:RAT_LARGE:LEATHER +CREATURE:MOLE_DOG_NAKED:LEATHER +CREATURE:TROGLODYTE:LEATHER +CREATURE:MOLE_GIANT:LEATHER +CREATURE:IMP_FIRE:LEATHER +CREATURE:FISH_CAVE:LEATHER +CREATURE:CAVE_FISH_MAN:LEATHER +CREATURE:OLM:LEATHER +CREATURE:OLM_MAN:LEATHER +CREATURE:BAT:LEATHER +CREATURE:BAT_MAN:LEATHER +CREATURE:MAGGOT_PURRING:LEATHER +"CREATURE:BIRD_SWALLOW_CAVE:LEATHER +!CREATURE:CAVE_SWALLOW_MAN:LEATHER +(CREATURE:BIRD_SWALLOW_CAVE_GIANT:LEATHER +CREATURE:AMPHIBIAN_MAN:LEATHER +CREATURE:RODENT MAN:LEATHER +CREATURE:WILD_BOAR:LEATHER +CREATURE:WILD_BOAR_MAN:LEATHER + CREATURE:GIANT_WILD_BOAR:LEATHER +CREATURE:COYOTE:LEATHER +CREATURE:COYOTE_MAN:LEATHER +CREATURE:GIANT_COYOTE:LEATHER +CREATURE:KANGAROO:LEATHER +CREATURE:KANGAROO_MAN:LEATHER +CREATURE:GIANT_KANGAROO:LEATHER +CREATURE:KOALA:LEATHER +CREATURE:KOALA_MAN:LEATHER +CREATURE:GIANT_KOALA:LEATHER +CREATURE:ECHIDNA:LEATHER +CREATURE:ECHIDNA_MAN:LEATHER +CREATURE:GIANT_ECHIDNA:LEATHER +CREATURE:PORCUPINE:LEATHER +CREATURE:PORCUPINE_MAN:LEATHER + CREATURE:GIANT_PORCUPINE:LEATHER +CREATURE:GRAY_LANGUR:LEATHER + CREATURE:GRAY_LANGUR_MAN:LEATHER +"CREATURE:GIANT_GRAY_LANGUR:LEATHER +CREATURE:BOBCAT:LEATHER +CREATURE:BOBCAT_MAN:LEATHER +CREATURE:GIANT_BOBCAT:LEATHER +CREATURE:SKUNK:LEATHER +CREATURE:SKUNK_MAN:LEATHER +CREATURE:GIANT_SKUNK:LEATHER + CREATURE:GREEN_TREE_FROG:LEATHER +$CREATURE:GREEN_TREE_FROG_MAN:LEATHER +&CREATURE:GIANT_GREEN_TREE_FROG:LEATHER +CREATURE:HARE:LEATHER +CREATURE:HARE_MAN:LEATHER +CREATURE:GIANT_HARE:LEATHER +CREATURE:WEASEL:LEATHER +CREATURE:WEASEL_MAN:LEATHER +CREATURE:GIANT_WEASEL:LEATHER +CREATURE:IBEX:LEATHER +CREATURE:IBEX_MAN:LEATHER +CREATURE:GIANT_IBEX:LEATHER +CREATURE:WOMBAT:LEATHER +CREATURE:WOMBAT_MAN:LEATHER +CREATURE:GIANT_WOMBAT:LEATHER +CREATURE:DINGO:LEATHER +CREATURE:DINGO_MAN:LEATHER +CREATURE:GIANT_DINGO:LEATHER +CREATURE:COATI:LEATHER +CREATURE:COATI_MAN:LEATHER +CREATURE:GIANT_COATI:LEATHER +CREATURE:OPOSSUM:LEATHER +CREATURE:OPOSSUM_MAN:LEATHER +CREATURE:GIANT_OPOSSUM:LEATHER +CREATURE:MONGOOSE:LEATHER +CREATURE:MONGOOSE_MAN:LEATHER +CREATURE:GIANT_MONGOOSE:LEATHER +CREATURE:HYENA:LEATHER +CREATURE:HYENA_MAN:LEATHER +CREATURE:GIANT_HYENA:LEATHER +CREATURE:OCELOT:LEATHER +CREATURE:OCELOT_MAN:LEATHER +CREATURE:GIANT_OCELOT:LEATHER +CREATURE:JACKAL:LEATHER +CREATURE:JACKAL_MAN:LEATHER +CREATURE:GIANT_JACKAL:LEATHER +CREATURE:CAPUCHIN:LEATHER +CREATURE:CAPUCHIN_MAN:LEATHER +CREATURE:GIANT_CAPUCHIN:LEATHER +CREATURE:SLOTH:LEATHER +CREATURE:SLOTH_MAN:LEATHER +CREATURE:GIANT_SLOTH:LEATHER +CREATURE:SPIDER_MONKEY:LEATHER +"CREATURE:SPIDER_MONKEY_MAN:LEATHER +$CREATURE:GIANT_SPIDER_MONKEY:LEATHER +CREATURE:BEAR_SLOTH:LEATHER +CREATURE:SLOTH_BEAR_MAN:LEATHER +!CREATURE:GIANT_SLOTH_BEAR:LEATHER +CREATURE:AYE-AYE:LEATHER +CREATURE:AYE-AYE_MAN:LEATHER +CREATURE:GIANT_AYE-AYE:LEATHER +CREATURE:TAPIR:LEATHER +CREATURE:TAPIR_MAN:LEATHER +CREATURE:GIANT_TAPIR:LEATHER +CREATURE:IMPALA:LEATHER +CREATURE:IMPALA_MAN:LEATHER +CREATURE:GIANT_IMPALA:LEATHER +CREATURE:AARDVARK:LEATHER +CREATURE:AARDVARK_MAN:LEATHER +CREATURE:GIANT_AARDVARK:LEATHER +CREATURE:LION_TAMARIN:LEATHER +!CREATURE:LION_TAMARIN_MAN:LEATHER +#CREATURE:GIANT_LION_TAMARIN:LEATHER +CREATURE:STOAT:LEATHER +CREATURE:STOAT_MAN:LEATHER +CREATURE:GIANT_STOAT:LEATHER +CREATURE:LYNX:LEATHER +CREATURE:LYNX_MAN:LEATHER +CREATURE:GIANT_LYNX:LEATHER +CREATURE:GNOLL:LEATHER +"CREATURE:FORGOTTEN_BEAST_2:LEATHER +"CREATURE:FORGOTTEN_BEAST_4:LEATHER +"CREATURE:FORGOTTEN_BEAST_5:LEATHER +"CREATURE:FORGOTTEN_BEAST_6:LEATHER +"CREATURE:FORGOTTEN_BEAST_7:LEATHER +#CREATURE:FORGOTTEN_BEAST_10:LEATHER +#CREATURE:FORGOTTEN_BEAST_12:LEATHER +#CREATURE:FORGOTTEN_BEAST_13:LEATHER +#CREATURE:FORGOTTEN_BEAST_16:LEATHER +#CREATURE:FORGOTTEN_BEAST_17:LEATHER +#CREATURE:FORGOTTEN_BEAST_18:LEATHER +#CREATURE:FORGOTTEN_BEAST_19:LEATHER +#CREATURE:FORGOTTEN_BEAST_20:LEATHER +#CREATURE:FORGOTTEN_BEAST_22:LEATHER +#CREATURE:FORGOTTEN_BEAST_23:LEATHER +#CREATURE:FORGOTTEN_BEAST_24:LEATHER +#CREATURE:FORGOTTEN_BEAST_25:LEATHER +#CREATURE:FORGOTTEN_BEAST_26:LEATHER +#CREATURE:FORGOTTEN_BEAST_27:LEATHER +#CREATURE:FORGOTTEN_BEAST_28:LEATHER +#CREATURE:FORGOTTEN_BEAST_29:LEATHER +#CREATURE:FORGOTTEN_BEAST_32:LEATHER +#CREATURE:FORGOTTEN_BEAST_33:LEATHER +#CREATURE:FORGOTTEN_BEAST_34:LEATHER +#CREATURE:FORGOTTEN_BEAST_35:LEATHER +#CREATURE:FORGOTTEN_BEAST_36:LEATHER +#CREATURE:FORGOTTEN_BEAST_39:LEATHER +#CREATURE:FORGOTTEN_BEAST_41:LEATHER +#CREATURE:FORGOTTEN_BEAST_42:LEATHER +#CREATURE:FORGOTTEN_BEAST_43:LEATHER +#CREATURE:FORGOTTEN_BEAST_44:LEATHER +#CREATURE:FORGOTTEN_BEAST_45:LEATHER +#CREATURE:FORGOTTEN_BEAST_47:LEATHER +#CREATURE:FORGOTTEN_BEAST_50:LEATHER +#CREATURE:FORGOTTEN_BEAST_52:LEATHER +#CREATURE:FORGOTTEN_BEAST_53:LEATHER +#CREATURE:FORGOTTEN_BEAST_55:LEATHER +#CREATURE:FORGOTTEN_BEAST_56:LEATHER +#CREATURE:FORGOTTEN_BEAST_58:LEATHER +#CREATURE:FORGOTTEN_BEAST_59:LEATHER +#CREATURE:FORGOTTEN_BEAST_60:LEATHER +#CREATURE:FORGOTTEN_BEAST_61:LEATHER +#CREATURE:FORGOTTEN_BEAST_64:LEATHER +#CREATURE:FORGOTTEN_BEAST_66:LEATHER +#CREATURE:FORGOTTEN_BEAST_67:LEATHER +#CREATURE:FORGOTTEN_BEAST_69:LEATHER +#CREATURE:FORGOTTEN_BEAST_71:LEATHER +#CREATURE:FORGOTTEN_BEAST_72:LEATHER +#CREATURE:FORGOTTEN_BEAST_73:LEATHER +#CREATURE:FORGOTTEN_BEAST_74:LEATHER +#CREATURE:FORGOTTEN_BEAST_75:LEATHER +#CREATURE:FORGOTTEN_BEAST_78:LEATHER +#CREATURE:FORGOTTEN_BEAST_80:LEATHER +#CREATURE:FORGOTTEN_BEAST_81:LEATHER +#CREATURE:FORGOTTEN_BEAST_82:LEATHER +#CREATURE:FORGOTTEN_BEAST_83:LEATHER +#CREATURE:FORGOTTEN_BEAST_84:LEATHER +#CREATURE:FORGOTTEN_BEAST_86:LEATHER +#CREATURE:FORGOTTEN_BEAST_87:LEATHER +#CREATURE:FORGOTTEN_BEAST_89:LEATHER +#CREATURE:FORGOTTEN_BEAST_90:LEATHER +#CREATURE:FORGOTTEN_BEAST_92:LEATHER +#CREATURE:FORGOTTEN_BEAST_94:LEATHER +#CREATURE:FORGOTTEN_BEAST_95:LEATHER +#CREATURE:FORGOTTEN_BEAST_96:LEATHER +#CREATURE:FORGOTTEN_BEAST_97:LEATHER +#CREATURE:FORGOTTEN_BEAST_98:LEATHER +$CREATURE:FORGOTTEN_BEAST_100:LEATHER +$CREATURE:FORGOTTEN_BEAST_105:LEATHER +$CREATURE:FORGOTTEN_BEAST_106:LEATHER +$CREATURE:FORGOTTEN_BEAST_107:LEATHER +$CREATURE:FORGOTTEN_BEAST_108:LEATHER +$CREATURE:FORGOTTEN_BEAST_109:LEATHER +$CREATURE:FORGOTTEN_BEAST_111:LEATHER +$CREATURE:FORGOTTEN_BEAST_112:LEATHER +$CREATURE:FORGOTTEN_BEAST_113:LEATHER +$CREATURE:FORGOTTEN_BEAST_114:LEATHER +$CREATURE:FORGOTTEN_BEAST_115:LEATHER +$CREATURE:FORGOTTEN_BEAST_116:LEATHER +$CREATURE:FORGOTTEN_BEAST_117:LEATHER +$CREATURE:FORGOTTEN_BEAST_118:LEATHER +$CREATURE:FORGOTTEN_BEAST_119:LEATHER +$CREATURE:FORGOTTEN_BEAST_120:LEATHER +$CREATURE:FORGOTTEN_BEAST_122:LEATHER +$CREATURE:FORGOTTEN_BEAST_123:LEATHER +$CREATURE:FORGOTTEN_BEAST_124:LEATHER +$CREATURE:FORGOTTEN_BEAST_125:LEATHER +$CREATURE:FORGOTTEN_BEAST_127:LEATHER +$CREATURE:FORGOTTEN_BEAST_128:LEATHER +$CREATURE:FORGOTTEN_BEAST_130:LEATHER +$CREATURE:FORGOTTEN_BEAST_131:LEATHER +$CREATURE:FORGOTTEN_BEAST_132:LEATHER +$CREATURE:FORGOTTEN_BEAST_133:LEATHER +$CREATURE:FORGOTTEN_BEAST_134:LEATHER +$CREATURE:FORGOTTEN_BEAST_135:LEATHER +$CREATURE:FORGOTTEN_BEAST_137:LEATHER +$CREATURE:FORGOTTEN_BEAST_138:LEATHER +$CREATURE:FORGOTTEN_BEAST_139:LEATHER +$CREATURE:FORGOTTEN_BEAST_141:LEATHER +$CREATURE:FORGOTTEN_BEAST_142:LEATHER +$CREATURE:FORGOTTEN_BEAST_144:LEATHER +$CREATURE:FORGOTTEN_BEAST_146:LEATHER +$CREATURE:FORGOTTEN_BEAST_148:LEATHER +$CREATURE:FORGOTTEN_BEAST_149:LEATHER +$CREATURE:FORGOTTEN_BEAST_150:LEATHER +$CREATURE:FORGOTTEN_BEAST_152:LEATHER +$CREATURE:FORGOTTEN_BEAST_154:LEATHER +$CREATURE:FORGOTTEN_BEAST_156:LEATHER +$CREATURE:FORGOTTEN_BEAST_157:LEATHER +$CREATURE:FORGOTTEN_BEAST_158:LEATHER +$CREATURE:FORGOTTEN_BEAST_159:LEATHER +$CREATURE:FORGOTTEN_BEAST_161:LEATHER +$CREATURE:FORGOTTEN_BEAST_162:LEATHER +$CREATURE:FORGOTTEN_BEAST_163:LEATHER +$CREATURE:FORGOTTEN_BEAST_165:LEATHER +$CREATURE:FORGOTTEN_BEAST_167:LEATHER +$CREATURE:FORGOTTEN_BEAST_168:LEATHER +$CREATURE:FORGOTTEN_BEAST_169:LEATHER +$CREATURE:FORGOTTEN_BEAST_170:LEATHER +$CREATURE:FORGOTTEN_BEAST_171:LEATHER +$CREATURE:FORGOTTEN_BEAST_172:LEATHER +$CREATURE:FORGOTTEN_BEAST_173:LEATHER +$CREATURE:FORGOTTEN_BEAST_176:LEATHER +$CREATURE:FORGOTTEN_BEAST_177:LEATHER +$CREATURE:FORGOTTEN_BEAST_178:LEATHER +$CREATURE:FORGOTTEN_BEAST_179:LEATHER +$CREATURE:FORGOTTEN_BEAST_180:LEATHER +$CREATURE:FORGOTTEN_BEAST_181:LEATHER +$CREATURE:FORGOTTEN_BEAST_182:LEATHER +$CREATURE:FORGOTTEN_BEAST_183:LEATHER +$CREATURE:FORGOTTEN_BEAST_184:LEATHER +$CREATURE:FORGOTTEN_BEAST_185:LEATHER +$CREATURE:FORGOTTEN_BEAST_186:LEATHER +$CREATURE:FORGOTTEN_BEAST_188:LEATHER +$CREATURE:FORGOTTEN_BEAST_189:LEATHER +$CREATURE:FORGOTTEN_BEAST_190:LEATHER +$CREATURE:FORGOTTEN_BEAST_191:LEATHER +$CREATURE:FORGOTTEN_BEAST_193:LEATHER +$CREATURE:FORGOTTEN_BEAST_194:LEATHER +$CREATURE:FORGOTTEN_BEAST_195:LEATHER +$CREATURE:FORGOTTEN_BEAST_196:LEATHER +$CREATURE:FORGOTTEN_BEAST_197:LEATHER +$CREATURE:FORGOTTEN_BEAST_199:LEATHER +$CREATURE:FORGOTTEN_BEAST_200:LEATHER +$CREATURE:FORGOTTEN_BEAST_201:LEATHER +$CREATURE:FORGOTTEN_BEAST_204:LEATHER +$CREATURE:FORGOTTEN_BEAST_205:LEATHER +$CREATURE:FORGOTTEN_BEAST_206:LEATHER +$CREATURE:FORGOTTEN_BEAST_207:LEATHER +$CREATURE:FORGOTTEN_BEAST_208:LEATHER +$CREATURE:FORGOTTEN_BEAST_209:LEATHER +$CREATURE:FORGOTTEN_BEAST_210:LEATHER +$CREATURE:FORGOTTEN_BEAST_211:LEATHER +$CREATURE:FORGOTTEN_BEAST_212:LEATHER +$CREATURE:FORGOTTEN_BEAST_213:LEATHER +$CREATURE:FORGOTTEN_BEAST_214:LEATHER +$CREATURE:FORGOTTEN_BEAST_215:LEATHER +$CREATURE:FORGOTTEN_BEAST_216:LEATHER +$CREATURE:FORGOTTEN_BEAST_217:LEATHER +$CREATURE:FORGOTTEN_BEAST_218:LEATHER +$CREATURE:FORGOTTEN_BEAST_220:LEATHER +$CREATURE:FORGOTTEN_BEAST_221:LEATHER +$CREATURE:FORGOTTEN_BEAST_224:LEATHER +$CREATURE:FORGOTTEN_BEAST_225:LEATHER +$CREATURE:FORGOTTEN_BEAST_228:LEATHER +$CREATURE:FORGOTTEN_BEAST_231:LEATHER +$CREATURE:FORGOTTEN_BEAST_232:LEATHER +$CREATURE:FORGOTTEN_BEAST_235:LEATHER +$CREATURE:FORGOTTEN_BEAST_236:LEATHER +$CREATURE:FORGOTTEN_BEAST_237:LEATHER +$CREATURE:FORGOTTEN_BEAST_239:LEATHER +$CREATURE:FORGOTTEN_BEAST_240:LEATHER +$CREATURE:FORGOTTEN_BEAST_242:LEATHER +$CREATURE:FORGOTTEN_BEAST_243:LEATHER +$CREATURE:FORGOTTEN_BEAST_244:LEATHER +$CREATURE:FORGOTTEN_BEAST_245:LEATHER +$CREATURE:FORGOTTEN_BEAST_246:LEATHER +$CREATURE:FORGOTTEN_BEAST_247:LEATHER +$CREATURE:FORGOTTEN_BEAST_248:LEATHER +$CREATURE:FORGOTTEN_BEAST_249:LEATHER +$CREATURE:FORGOTTEN_BEAST_253:LEATHER +$CREATURE:FORGOTTEN_BEAST_255:LEATHER +$CREATURE:FORGOTTEN_BEAST_256:LEATHER +$CREATURE:FORGOTTEN_BEAST_259:LEATHER +$CREATURE:FORGOTTEN_BEAST_260:LEATHER +$CREATURE:FORGOTTEN_BEAST_263:LEATHER +$CREATURE:FORGOTTEN_BEAST_264:LEATHER +$CREATURE:FORGOTTEN_BEAST_265:LEATHER +$CREATURE:FORGOTTEN_BEAST_266:LEATHER +$CREATURE:FORGOTTEN_BEAST_268:LEATHER +$CREATURE:FORGOTTEN_BEAST_269:LEATHER +$CREATURE:FORGOTTEN_BEAST_270:LEATHER +$CREATURE:FORGOTTEN_BEAST_271:LEATHER +$CREATURE:FORGOTTEN_BEAST_272:LEATHER +$CREATURE:FORGOTTEN_BEAST_273:LEATHER +$CREATURE:FORGOTTEN_BEAST_274:LEATHER +$CREATURE:FORGOTTEN_BEAST_275:LEATHER +$CREATURE:FORGOTTEN_BEAST_276:LEATHER +$CREATURE:FORGOTTEN_BEAST_277:LEATHER +$CREATURE:FORGOTTEN_BEAST_279:LEATHER +$CREATURE:FORGOTTEN_BEAST_280:LEATHER +$CREATURE:FORGOTTEN_BEAST_281:LEATHER +$CREATURE:FORGOTTEN_BEAST_282:LEATHER +$CREATURE:FORGOTTEN_BEAST_283:LEATHER +$CREATURE:FORGOTTEN_BEAST_285:LEATHER +$CREATURE:FORGOTTEN_BEAST_286:LEATHER +$CREATURE:FORGOTTEN_BEAST_287:LEATHER +$CREATURE:FORGOTTEN_BEAST_288:LEATHER +$CREATURE:FORGOTTEN_BEAST_289:LEATHER +$CREATURE:FORGOTTEN_BEAST_290:LEATHER +$CREATURE:FORGOTTEN_BEAST_293:LEATHER +$CREATURE:FORGOTTEN_BEAST_295:LEATHER +$CREATURE:FORGOTTEN_BEAST_297:LEATHER +$CREATURE:FORGOTTEN_BEAST_300:LEATHER +$CREATURE:FORGOTTEN_BEAST_302:LEATHER +$CREATURE:FORGOTTEN_BEAST_306:LEATHER +$CREATURE:FORGOTTEN_BEAST_307:LEATHER +$CREATURE:FORGOTTEN_BEAST_310:LEATHER +$CREATURE:FORGOTTEN_BEAST_311:LEATHER +$CREATURE:FORGOTTEN_BEAST_312:LEATHER +$CREATURE:FORGOTTEN_BEAST_314:LEATHER +$CREATURE:FORGOTTEN_BEAST_316:LEATHER +$CREATURE:FORGOTTEN_BEAST_317:LEATHER +$CREATURE:FORGOTTEN_BEAST_318:LEATHER +$CREATURE:FORGOTTEN_BEAST_320:LEATHER +$CREATURE:FORGOTTEN_BEAST_321:LEATHER +$CREATURE:FORGOTTEN_BEAST_322:LEATHER +$CREATURE:FORGOTTEN_BEAST_323:LEATHER +$CREATURE:FORGOTTEN_BEAST_324:LEATHER +$CREATURE:FORGOTTEN_BEAST_325:LEATHER +$CREATURE:FORGOTTEN_BEAST_326:LEATHER +$CREATURE:FORGOTTEN_BEAST_327:LEATHER +$CREATURE:FORGOTTEN_BEAST_328:LEATHER +$CREATURE:FORGOTTEN_BEAST_329:LEATHER +$CREATURE:FORGOTTEN_BEAST_330:LEATHER +$CREATURE:FORGOTTEN_BEAST_332:LEATHER +$CREATURE:FORGOTTEN_BEAST_333:LEATHER +$CREATURE:FORGOTTEN_BEAST_335:LEATHER +$CREATURE:FORGOTTEN_BEAST_336:LEATHER +$CREATURE:FORGOTTEN_BEAST_337:LEATHER +$CREATURE:FORGOTTEN_BEAST_338:LEATHER +$CREATURE:FORGOTTEN_BEAST_339:LEATHER +$CREATURE:FORGOTTEN_BEAST_341:LEATHER +$CREATURE:FORGOTTEN_BEAST_342:LEATHER +$CREATURE:FORGOTTEN_BEAST_343:LEATHER +$CREATURE:FORGOTTEN_BEAST_344:LEATHER +$CREATURE:FORGOTTEN_BEAST_347:LEATHER +$CREATURE:FORGOTTEN_BEAST_348:LEATHER +$CREATURE:FORGOTTEN_BEAST_349:LEATHER +$CREATURE:FORGOTTEN_BEAST_351:LEATHER +$CREATURE:FORGOTTEN_BEAST_352:LEATHER +$CREATURE:FORGOTTEN_BEAST_353:LEATHER +$CREATURE:FORGOTTEN_BEAST_354:LEATHER +$CREATURE:FORGOTTEN_BEAST_355:LEATHER +$CREATURE:FORGOTTEN_BEAST_356:LEATHER +$CREATURE:FORGOTTEN_BEAST_357:LEATHER +$CREATURE:FORGOTTEN_BEAST_358:LEATHER +$CREATURE:FORGOTTEN_BEAST_359:LEATHER +$CREATURE:FORGOTTEN_BEAST_361:LEATHER +$CREATURE:FORGOTTEN_BEAST_363:LEATHER +$CREATURE:FORGOTTEN_BEAST_364:LEATHER +$CREATURE:FORGOTTEN_BEAST_365:LEATHER +$CREATURE:FORGOTTEN_BEAST_366:LEATHER +$CREATURE:FORGOTTEN_BEAST_367:LEATHER +$CREATURE:FORGOTTEN_BEAST_368:LEATHER +$CREATURE:FORGOTTEN_BEAST_370:LEATHER +$CREATURE:FORGOTTEN_BEAST_372:LEATHER +$CREATURE:FORGOTTEN_BEAST_374:LEATHER +$CREATURE:FORGOTTEN_BEAST_375:LEATHER +$CREATURE:FORGOTTEN_BEAST_377:LEATHER +$CREATURE:FORGOTTEN_BEAST_378:LEATHER +$CREATURE:FORGOTTEN_BEAST_379:LEATHER +$CREATURE:FORGOTTEN_BEAST_380:LEATHER +$CREATURE:FORGOTTEN_BEAST_381:LEATHER +$CREATURE:FORGOTTEN_BEAST_382:LEATHER +$CREATURE:FORGOTTEN_BEAST_383:LEATHER +$CREATURE:FORGOTTEN_BEAST_384:LEATHER +$CREATURE:FORGOTTEN_BEAST_385:LEATHER +$CREATURE:FORGOTTEN_BEAST_386:LEATHER +$CREATURE:FORGOTTEN_BEAST_387:LEATHER +$CREATURE:FORGOTTEN_BEAST_388:LEATHER +$CREATURE:FORGOTTEN_BEAST_389:LEATHER +$CREATURE:FORGOTTEN_BEAST_390:LEATHER +$CREATURE:FORGOTTEN_BEAST_391:LEATHER +$CREATURE:FORGOTTEN_BEAST_392:LEATHER +$CREATURE:FORGOTTEN_BEAST_393:LEATHER +$CREATURE:FORGOTTEN_BEAST_396:LEATHER +$CREATURE:FORGOTTEN_BEAST_397:LEATHER +$CREATURE:FORGOTTEN_BEAST_398:LEATHER +$CREATURE:FORGOTTEN_BEAST_400:LEATHER +$CREATURE:FORGOTTEN_BEAST_403:LEATHER +$CREATURE:FORGOTTEN_BEAST_404:LEATHER +$CREATURE:FORGOTTEN_BEAST_405:LEATHER +$CREATURE:FORGOTTEN_BEAST_406:LEATHER +$CREATURE:FORGOTTEN_BEAST_407:LEATHER +$CREATURE:FORGOTTEN_BEAST_408:LEATHER +$CREATURE:FORGOTTEN_BEAST_409:LEATHER +$CREATURE:FORGOTTEN_BEAST_410:LEATHER +$CREATURE:FORGOTTEN_BEAST_411:LEATHER +$CREATURE:FORGOTTEN_BEAST_412:LEATHER +$CREATURE:FORGOTTEN_BEAST_413:LEATHER +$CREATURE:FORGOTTEN_BEAST_414:LEATHER +$CREATURE:FORGOTTEN_BEAST_416:LEATHER +$CREATURE:FORGOTTEN_BEAST_417:LEATHER +$CREATURE:FORGOTTEN_BEAST_418:LEATHER +$CREATURE:FORGOTTEN_BEAST_420:LEATHER +$CREATURE:FORGOTTEN_BEAST_421:LEATHER +$CREATURE:FORGOTTEN_BEAST_422:LEATHER +$CREATURE:FORGOTTEN_BEAST_423:LEATHER +$CREATURE:FORGOTTEN_BEAST_424:LEATHER +$CREATURE:FORGOTTEN_BEAST_427:LEATHER +$CREATURE:FORGOTTEN_BEAST_429:LEATHER +$CREATURE:FORGOTTEN_BEAST_430:LEATHER +$CREATURE:FORGOTTEN_BEAST_432:LEATHER +$CREATURE:FORGOTTEN_BEAST_433:LEATHER +$CREATURE:FORGOTTEN_BEAST_434:LEATHER +$CREATURE:FORGOTTEN_BEAST_435:LEATHER +$CREATURE:FORGOTTEN_BEAST_436:LEATHER +$CREATURE:FORGOTTEN_BEAST_437:LEATHER +$CREATURE:FORGOTTEN_BEAST_438:LEATHER +$CREATURE:FORGOTTEN_BEAST_440:LEATHER +$CREATURE:FORGOTTEN_BEAST_441:LEATHER +$CREATURE:FORGOTTEN_BEAST_442:LEATHER +$CREATURE:FORGOTTEN_BEAST_444:LEATHER +$CREATURE:FORGOTTEN_BEAST_446:LEATHER +$CREATURE:FORGOTTEN_BEAST_447:LEATHER +$CREATURE:FORGOTTEN_BEAST_448:LEATHER +$CREATURE:FORGOTTEN_BEAST_449:LEATHER +$CREATURE:FORGOTTEN_BEAST_450:LEATHER +$CREATURE:FORGOTTEN_BEAST_451:LEATHER +$CREATURE:FORGOTTEN_BEAST_453:LEATHER +$CREATURE:FORGOTTEN_BEAST_454:LEATHER +$CREATURE:FORGOTTEN_BEAST_455:LEATHER +$CREATURE:FORGOTTEN_BEAST_457:LEATHER +$CREATURE:FORGOTTEN_BEAST_459:LEATHER +$CREATURE:FORGOTTEN_BEAST_461:LEATHER +$CREATURE:FORGOTTEN_BEAST_462:LEATHER +$CREATURE:FORGOTTEN_BEAST_463:LEATHER +$CREATURE:FORGOTTEN_BEAST_465:LEATHER +$CREATURE:FORGOTTEN_BEAST_466:LEATHER +$CREATURE:FORGOTTEN_BEAST_468:LEATHER +$CREATURE:FORGOTTEN_BEAST_469:LEATHER +$CREATURE:FORGOTTEN_BEAST_470:LEATHER +$CREATURE:FORGOTTEN_BEAST_471:LEATHER +$CREATURE:FORGOTTEN_BEAST_472:LEATHER +$CREATURE:FORGOTTEN_BEAST_474:LEATHER +$CREATURE:FORGOTTEN_BEAST_475:LEATHER +$CREATURE:FORGOTTEN_BEAST_476:LEATHER +$CREATURE:FORGOTTEN_BEAST_478:LEATHER +$CREATURE:FORGOTTEN_BEAST_479:LEATHER +$CREATURE:FORGOTTEN_BEAST_480:LEATHER +$CREATURE:FORGOTTEN_BEAST_481:LEATHER +$CREATURE:FORGOTTEN_BEAST_483:LEATHER +$CREATURE:FORGOTTEN_BEAST_486:LEATHER +$CREATURE:FORGOTTEN_BEAST_487:LEATHER +$CREATURE:FORGOTTEN_BEAST_489:LEATHER +$CREATURE:FORGOTTEN_BEAST_492:LEATHER +$CREATURE:FORGOTTEN_BEAST_494:LEATHER +$CREATURE:FORGOTTEN_BEAST_495:LEATHER +$CREATURE:FORGOTTEN_BEAST_496:LEATHER +$CREATURE:FORGOTTEN_BEAST_497:LEATHER +$CREATURE:FORGOTTEN_BEAST_498:LEATHER +$CREATURE:FORGOTTEN_BEAST_499:LEATHER +$CREATURE:FORGOTTEN_BEAST_501:LEATHER +$CREATURE:FORGOTTEN_BEAST_503:LEATHER +$CREATURE:FORGOTTEN_BEAST_504:LEATHER +$CREATURE:FORGOTTEN_BEAST_505:LEATHER +$CREATURE:FORGOTTEN_BEAST_507:LEATHER +$CREATURE:FORGOTTEN_BEAST_508:LEATHER +$CREATURE:FORGOTTEN_BEAST_510:LEATHER +$CREATURE:FORGOTTEN_BEAST_512:LEATHER +$CREATURE:FORGOTTEN_BEAST_513:LEATHER +$CREATURE:FORGOTTEN_BEAST_514:LEATHER +$CREATURE:FORGOTTEN_BEAST_515:LEATHER +$CREATURE:FORGOTTEN_BEAST_516:LEATHER +$CREATURE:FORGOTTEN_BEAST_517:LEATHER +$CREATURE:FORGOTTEN_BEAST_518:LEATHER +$CREATURE:FORGOTTEN_BEAST_519:LEATHER +$CREATURE:FORGOTTEN_BEAST_520:LEATHER +$CREATURE:FORGOTTEN_BEAST_521:LEATHER +$CREATURE:FORGOTTEN_BEAST_522:LEATHER +$CREATURE:FORGOTTEN_BEAST_523:LEATHER +$CREATURE:FORGOTTEN_BEAST_525:LEATHER +$CREATURE:FORGOTTEN_BEAST_526:LEATHER +$CREATURE:FORGOTTEN_BEAST_527:LEATHER +$CREATURE:FORGOTTEN_BEAST_528:LEATHER +$CREATURE:FORGOTTEN_BEAST_529:LEATHER +$CREATURE:FORGOTTEN_BEAST_530:LEATHER +$CREATURE:FORGOTTEN_BEAST_531:LEATHER +$CREATURE:FORGOTTEN_BEAST_532:LEATHER +$CREATURE:FORGOTTEN_BEAST_533:LEATHER +$CREATURE:FORGOTTEN_BEAST_534:LEATHER +$CREATURE:FORGOTTEN_BEAST_535:LEATHER +$CREATURE:FORGOTTEN_BEAST_536:LEATHER +$CREATURE:FORGOTTEN_BEAST_539:LEATHER +$CREATURE:FORGOTTEN_BEAST_540:LEATHER +$CREATURE:FORGOTTEN_BEAST_541:LEATHER +$CREATURE:FORGOTTEN_BEAST_543:LEATHER +$CREATURE:FORGOTTEN_BEAST_545:LEATHER +$CREATURE:FORGOTTEN_BEAST_546:LEATHER +$CREATURE:FORGOTTEN_BEAST_547:LEATHER +$CREATURE:FORGOTTEN_BEAST_548:LEATHER +$CREATURE:FORGOTTEN_BEAST_549:LEATHER +$CREATURE:FORGOTTEN_BEAST_550:LEATHER +$CREATURE:FORGOTTEN_BEAST_551:LEATHER +$CREATURE:FORGOTTEN_BEAST_554:LEATHER +$CREATURE:FORGOTTEN_BEAST_555:LEATHER +$CREATURE:FORGOTTEN_BEAST_556:LEATHER +$CREATURE:FORGOTTEN_BEAST_557:LEATHER +$CREATURE:FORGOTTEN_BEAST_561:LEATHER +$CREATURE:FORGOTTEN_BEAST_562:LEATHER +$CREATURE:FORGOTTEN_BEAST_564:LEATHER +$CREATURE:FORGOTTEN_BEAST_569:LEATHER +$CREATURE:FORGOTTEN_BEAST_570:LEATHER +$CREATURE:FORGOTTEN_BEAST_571:LEATHER +$CREATURE:FORGOTTEN_BEAST_572:LEATHER +$CREATURE:FORGOTTEN_BEAST_574:LEATHER +$CREATURE:FORGOTTEN_BEAST_576:LEATHER +$CREATURE:FORGOTTEN_BEAST_578:LEATHER +$CREATURE:FORGOTTEN_BEAST_579:LEATHER +$CREATURE:FORGOTTEN_BEAST_580:LEATHER +$CREATURE:FORGOTTEN_BEAST_581:LEATHER +$CREATURE:FORGOTTEN_BEAST_583:LEATHER +$CREATURE:FORGOTTEN_BEAST_584:LEATHER +$CREATURE:FORGOTTEN_BEAST_586:LEATHER +$CREATURE:FORGOTTEN_BEAST_588:LEATHER +$CREATURE:FORGOTTEN_BEAST_589:LEATHER +$CREATURE:FORGOTTEN_BEAST_590:LEATHER +$CREATURE:FORGOTTEN_BEAST_592:LEATHER +$CREATURE:FORGOTTEN_BEAST_593:LEATHER +$CREATURE:FORGOTTEN_BEAST_594:LEATHER +$CREATURE:FORGOTTEN_BEAST_595:LEATHER +$CREATURE:FORGOTTEN_BEAST_596:LEATHER +$CREATURE:FORGOTTEN_BEAST_597:LEATHER +$CREATURE:FORGOTTEN_BEAST_599:LEATHER +$CREATURE:FORGOTTEN_BEAST_600:LEATHER +$CREATURE:FORGOTTEN_BEAST_601:LEATHER +$CREATURE:FORGOTTEN_BEAST_603:LEATHER +$CREATURE:FORGOTTEN_BEAST_605:LEATHER +$CREATURE:FORGOTTEN_BEAST_607:LEATHER +$CREATURE:FORGOTTEN_BEAST_608:LEATHER +$CREATURE:FORGOTTEN_BEAST_609:LEATHER +$CREATURE:FORGOTTEN_BEAST_610:LEATHER +$CREATURE:FORGOTTEN_BEAST_611:LEATHER +$CREATURE:FORGOTTEN_BEAST_612:LEATHER +$CREATURE:FORGOTTEN_BEAST_613:LEATHER +$CREATURE:FORGOTTEN_BEAST_614:LEATHER +$CREATURE:FORGOTTEN_BEAST_616:LEATHER +$CREATURE:FORGOTTEN_BEAST_619:LEATHER +$CREATURE:FORGOTTEN_BEAST_620:LEATHER +$CREATURE:FORGOTTEN_BEAST_621:LEATHER +$CREATURE:FORGOTTEN_BEAST_623:LEATHER +$CREATURE:FORGOTTEN_BEAST_624:LEATHER +$CREATURE:FORGOTTEN_BEAST_625:LEATHER +$CREATURE:FORGOTTEN_BEAST_626:LEATHER +$CREATURE:FORGOTTEN_BEAST_627:LEATHER +$CREATURE:FORGOTTEN_BEAST_628:LEATHER +$CREATURE:FORGOTTEN_BEAST_629:LEATHER +$CREATURE:FORGOTTEN_BEAST_630:LEATHER +$CREATURE:FORGOTTEN_BEAST_631:LEATHER +$CREATURE:FORGOTTEN_BEAST_632:LEATHER +$CREATURE:FORGOTTEN_BEAST_633:LEATHER +$CREATURE:FORGOTTEN_BEAST_634:LEATHER +$CREATURE:FORGOTTEN_BEAST_635:LEATHER +$CREATURE:FORGOTTEN_BEAST_636:LEATHER +$CREATURE:FORGOTTEN_BEAST_637:LEATHER +$CREATURE:FORGOTTEN_BEAST_639:LEATHER +$CREATURE:FORGOTTEN_BEAST_640:LEATHER +$CREATURE:FORGOTTEN_BEAST_643:LEATHER +$CREATURE:FORGOTTEN_BEAST_644:LEATHER +$CREATURE:FORGOTTEN_BEAST_645:LEATHER +$CREATURE:FORGOTTEN_BEAST_646:LEATHER +$CREATURE:FORGOTTEN_BEAST_647:LEATHER +$CREATURE:FORGOTTEN_BEAST_649:LEATHER +$CREATURE:FORGOTTEN_BEAST_650:LEATHER +$CREATURE:FORGOTTEN_BEAST_651:LEATHER +$CREATURE:FORGOTTEN_BEAST_652:LEATHER +$CREATURE:FORGOTTEN_BEAST_656:LEATHER +$CREATURE:FORGOTTEN_BEAST_658:LEATHER +$CREATURE:FORGOTTEN_BEAST_659:LEATHER +$CREATURE:FORGOTTEN_BEAST_661:LEATHER +$CREATURE:FORGOTTEN_BEAST_663:LEATHER +$CREATURE:FORGOTTEN_BEAST_664:LEATHER +$CREATURE:FORGOTTEN_BEAST_666:LEATHER +$CREATURE:FORGOTTEN_BEAST_667:LEATHER +$CREATURE:FORGOTTEN_BEAST_669:LEATHER +$CREATURE:FORGOTTEN_BEAST_670:LEATHER +$CREATURE:FORGOTTEN_BEAST_671:LEATHER +$CREATURE:FORGOTTEN_BEAST_674:LEATHER +$CREATURE:FORGOTTEN_BEAST_675:LEATHER +$CREATURE:FORGOTTEN_BEAST_678:LEATHER +$CREATURE:FORGOTTEN_BEAST_680:LEATHER +$CREATURE:FORGOTTEN_BEAST_681:LEATHER +$CREATURE:FORGOTTEN_BEAST_682:LEATHER +$CREATURE:FORGOTTEN_BEAST_684:LEATHER +$CREATURE:FORGOTTEN_BEAST_685:LEATHER +$CREATURE:FORGOTTEN_BEAST_686:LEATHER +$CREATURE:FORGOTTEN_BEAST_687:LEATHER +$CREATURE:FORGOTTEN_BEAST_688:LEATHER +$CREATURE:FORGOTTEN_BEAST_689:LEATHER +$CREATURE:FORGOTTEN_BEAST_691:LEATHER +$CREATURE:FORGOTTEN_BEAST_692:LEATHER +$CREATURE:FORGOTTEN_BEAST_695:LEATHER +$CREATURE:FORGOTTEN_BEAST_696:LEATHER +$CREATURE:FORGOTTEN_BEAST_697:LEATHER +$CREATURE:FORGOTTEN_BEAST_698:LEATHER +$CREATURE:FORGOTTEN_BEAST_699:LEATHER +$CREATURE:FORGOTTEN_BEAST_700:LEATHER +$CREATURE:FORGOTTEN_BEAST_701:LEATHER +$CREATURE:FORGOTTEN_BEAST_702:LEATHER +$CREATURE:FORGOTTEN_BEAST_704:LEATHER +$CREATURE:FORGOTTEN_BEAST_706:LEATHER +$CREATURE:FORGOTTEN_BEAST_708:LEATHER +$CREATURE:FORGOTTEN_BEAST_709:LEATHER +$CREATURE:FORGOTTEN_BEAST_711:LEATHER +$CREATURE:FORGOTTEN_BEAST_712:LEATHER +$CREATURE:FORGOTTEN_BEAST_713:LEATHER +$CREATURE:FORGOTTEN_BEAST_714:LEATHER +$CREATURE:FORGOTTEN_BEAST_715:LEATHER +$CREATURE:FORGOTTEN_BEAST_717:LEATHER +$CREATURE:FORGOTTEN_BEAST_718:LEATHER +$CREATURE:FORGOTTEN_BEAST_719:LEATHER +$CREATURE:FORGOTTEN_BEAST_720:LEATHER +$CREATURE:FORGOTTEN_BEAST_721:LEATHER +$CREATURE:FORGOTTEN_BEAST_722:LEATHER +$CREATURE:FORGOTTEN_BEAST_723:LEATHER +$CREATURE:FORGOTTEN_BEAST_724:LEATHER +$CREATURE:FORGOTTEN_BEAST_726:LEATHER +$CREATURE:FORGOTTEN_BEAST_730:LEATHER +$CREATURE:FORGOTTEN_BEAST_731:LEATHER +$CREATURE:FORGOTTEN_BEAST_734:LEATHER +$CREATURE:FORGOTTEN_BEAST_735:LEATHER +$CREATURE:FORGOTTEN_BEAST_737:LEATHER +$CREATURE:FORGOTTEN_BEAST_738:LEATHER +$CREATURE:FORGOTTEN_BEAST_739:LEATHER +$CREATURE:FORGOTTEN_BEAST_740:LEATHER +$CREATURE:FORGOTTEN_BEAST_741:LEATHER +$CREATURE:FORGOTTEN_BEAST_742:LEATHER +$CREATURE:FORGOTTEN_BEAST_743:LEATHER +$CREATURE:FORGOTTEN_BEAST_744:LEATHER +$CREATURE:FORGOTTEN_BEAST_746:LEATHER +$CREATURE:FORGOTTEN_BEAST_747:LEATHER +$CREATURE:FORGOTTEN_BEAST_749:LEATHER +$CREATURE:FORGOTTEN_BEAST_750:LEATHER +$CREATURE:FORGOTTEN_BEAST_751:LEATHER +$CREATURE:FORGOTTEN_BEAST_752:LEATHER +$CREATURE:FORGOTTEN_BEAST_753:LEATHER +$CREATURE:FORGOTTEN_BEAST_754:LEATHER +$CREATURE:FORGOTTEN_BEAST_755:LEATHER +$CREATURE:FORGOTTEN_BEAST_756:LEATHER +$CREATURE:FORGOTTEN_BEAST_757:LEATHER +$CREATURE:FORGOTTEN_BEAST_758:LEATHER +$CREATURE:FORGOTTEN_BEAST_759:LEATHER +$CREATURE:FORGOTTEN_BEAST_760:LEATHER +$CREATURE:FORGOTTEN_BEAST_761:LEATHER +$CREATURE:FORGOTTEN_BEAST_762:LEATHER +$CREATURE:FORGOTTEN_BEAST_764:LEATHER +$CREATURE:FORGOTTEN_BEAST_767:LEATHER +$CREATURE:FORGOTTEN_BEAST_768:LEATHER +$CREATURE:FORGOTTEN_BEAST_769:LEATHER +$CREATURE:FORGOTTEN_BEAST_770:LEATHER +$CREATURE:FORGOTTEN_BEAST_773:LEATHER +$CREATURE:FORGOTTEN_BEAST_774:LEATHER +$CREATURE:FORGOTTEN_BEAST_776:LEATHER +$CREATURE:FORGOTTEN_BEAST_777:LEATHER +$CREATURE:FORGOTTEN_BEAST_778:LEATHER +$CREATURE:FORGOTTEN_BEAST_779:LEATHER +$CREATURE:FORGOTTEN_BEAST_780:LEATHER +$CREATURE:FORGOTTEN_BEAST_781:LEATHER +$CREATURE:FORGOTTEN_BEAST_782:LEATHER +$CREATURE:FORGOTTEN_BEAST_783:LEATHER +$CREATURE:FORGOTTEN_BEAST_784:LEATHER +$CREATURE:FORGOTTEN_BEAST_785:LEATHER +$CREATURE:FORGOTTEN_BEAST_786:LEATHER +$CREATURE:FORGOTTEN_BEAST_787:LEATHER +$CREATURE:FORGOTTEN_BEAST_789:LEATHER +$CREATURE:FORGOTTEN_BEAST_792:LEATHER +$CREATURE:FORGOTTEN_BEAST_793:LEATHER +$CREATURE:FORGOTTEN_BEAST_794:LEATHER +$CREATURE:FORGOTTEN_BEAST_795:LEATHER +$CREATURE:FORGOTTEN_BEAST_796:LEATHER +$CREATURE:FORGOTTEN_BEAST_799:LEATHER +$CREATURE:FORGOTTEN_BEAST_800:LEATHER +$CREATURE:FORGOTTEN_BEAST_801:LEATHER +$CREATURE:FORGOTTEN_BEAST_802:LEATHER +$CREATURE:FORGOTTEN_BEAST_803:LEATHER +$CREATURE:FORGOTTEN_BEAST_804:LEATHER +$CREATURE:FORGOTTEN_BEAST_806:LEATHER +$CREATURE:FORGOTTEN_BEAST_807:LEATHER +$CREATURE:FORGOTTEN_BEAST_809:LEATHER +$CREATURE:FORGOTTEN_BEAST_810:LEATHER +$CREATURE:FORGOTTEN_BEAST_811:LEATHER +$CREATURE:FORGOTTEN_BEAST_812:LEATHER +$CREATURE:FORGOTTEN_BEAST_815:LEATHER +$CREATURE:FORGOTTEN_BEAST_817:LEATHER +$CREATURE:FORGOTTEN_BEAST_818:LEATHER +$CREATURE:FORGOTTEN_BEAST_819:LEATHER +$CREATURE:FORGOTTEN_BEAST_820:LEATHER +$CREATURE:FORGOTTEN_BEAST_821:LEATHER +$CREATURE:FORGOTTEN_BEAST_822:LEATHER +$CREATURE:FORGOTTEN_BEAST_824:LEATHER +$CREATURE:FORGOTTEN_BEAST_825:LEATHER +$CREATURE:FORGOTTEN_BEAST_826:LEATHER +$CREATURE:FORGOTTEN_BEAST_827:LEATHER +$CREATURE:FORGOTTEN_BEAST_828:LEATHER +$CREATURE:FORGOTTEN_BEAST_831:LEATHER +$CREATURE:FORGOTTEN_BEAST_833:LEATHER +$CREATURE:FORGOTTEN_BEAST_835:LEATHER +$CREATURE:FORGOTTEN_BEAST_837:LEATHER +$CREATURE:FORGOTTEN_BEAST_838:LEATHER +$CREATURE:FORGOTTEN_BEAST_842:LEATHER +$CREATURE:FORGOTTEN_BEAST_843:LEATHER +$CREATURE:FORGOTTEN_BEAST_844:LEATHER +$CREATURE:FORGOTTEN_BEAST_845:LEATHER +$CREATURE:FORGOTTEN_BEAST_846:LEATHER +$CREATURE:FORGOTTEN_BEAST_847:LEATHER +$CREATURE:FORGOTTEN_BEAST_848:LEATHER +$CREATURE:FORGOTTEN_BEAST_849:LEATHER +$CREATURE:FORGOTTEN_BEAST_851:LEATHER +$CREATURE:FORGOTTEN_BEAST_853:LEATHER +$CREATURE:FORGOTTEN_BEAST_854:LEATHER +$CREATURE:FORGOTTEN_BEAST_855:LEATHER +$CREATURE:FORGOTTEN_BEAST_857:LEATHER +$CREATURE:FORGOTTEN_BEAST_858:LEATHER +$CREATURE:FORGOTTEN_BEAST_859:LEATHER +$CREATURE:FORGOTTEN_BEAST_860:LEATHER +$CREATURE:FORGOTTEN_BEAST_861:LEATHER +$CREATURE:FORGOTTEN_BEAST_862:LEATHER +$CREATURE:FORGOTTEN_BEAST_865:LEATHER +$CREATURE:FORGOTTEN_BEAST_866:LEATHER +$CREATURE:FORGOTTEN_BEAST_867:LEATHER +CREATURE:TITAN_1:LEATHER +CREATURE:TITAN_3:LEATHER +CREATURE:TITAN_4:LEATHER +CREATURE:TITAN_6:LEATHER +CREATURE:TITAN_7:LEATHER +CREATURE:TITAN_8:LEATHER +CREATURE:TITAN_9:LEATHER +CREATURE:TITAN_10:LEATHER +CREATURE:TITAN_11:LEATHER +CREATURE:TITAN_12:LEATHER +CREATURE:TITAN_13:LEATHER +CREATURE:TITAN_14:LEATHER +CREATURE:TITAN_15:LEATHER +CREATURE:TITAN_18:LEATHER +CREATURE:TITAN_19:LEATHER +CREATURE:TITAN_21:LEATHER +CREATURE:TITAN_23:LEATHER +CREATURE:TITAN_24:LEATHER +CREATURE:TITAN_25:LEATHER +CREATURE:TITAN_26:LEATHER +CREATURE:TITAN_27:LEATHER +CREATURE:TITAN_28:LEATHER +CREATURE:TITAN_29:LEATHER +CREATURE:TITAN_30:LEATHER +CREATURE:TITAN_32:LEATHER +CREATURE:TITAN_33:LEATHER +CREATURE:DEMON_7:LEATHER +CREATURE:DEMON_8:LEATHER +CREATURE:DEMON_9:LEATHER +CREATURE:DEMON_10:LEATHER +CREATURE:DEMON_11:LEATHER +CREATURE:DEMON_12:LEATHER +CREATURE:DEMON_13:LEATHER +CREATURE:DEMON_14:LEATHER +CREATURE:DEMON_15:LEATHER +CREATURE:DEMON_16:LEATHER +CREATURE:DEMON_17:LEATHER +CREATURE:DEMON_18:LEATHER +CREATURE:DEMON_19:LEATHER +CREATURE:DEMON_20:LEATHER +CREATURE:DEMON_21:LEATHER +CREATURE:DEMON_22:LEATHER +CREATURE:DEMON_23:LEATHER +CREATURE:DEMON_24:LEATHER +CREATURE:DEMON_25:LEATHER +CREATURE:DEMON_26:LEATHER +CREATURE:DEMON_27:LEATHER +CREATURE:DEMON_28:LEATHER +CREATURE:DEMON_29:LEATHER +CREATURE:DEMON_31:LEATHER +CREATURE:DEMON_32:LEATHER +CREATURE:DEMON_38:LEATHER +CREATURE:DEMON_39:LEATHER +CREATURE:DEMON_40:LEATHER +CREATURE:DEMON_41:LEATHER +CREATURE:DEMON_42:LEATHER +CREATURE:DEMON_43:LEATHER +CREATURE:DEMON_44:LEATHER +CREATURE:DEMON_45:LEATHER +CREATURE:DEMON_47:LEATHER +CREATURE:DEMON_48:LEATHER +CREATURE:DEMON_49:LEATHER +CREATURE:DEMON_52:LEATHER +!CREATURE:NIGHT_CREATURE_1:LEATHER +!CREATURE:NIGHT_CREATURE_2:LEATHER +!CREATURE:NIGHT_CREATURE_3:LEATHER +!CREATURE:NIGHT_CREATURE_4:LEATHER +!CREATURE:NIGHT_CREATURE_5:LEATHER +!CREATURE:NIGHT_CREATURE_6:LEATHER +!CREATURE:NIGHT_CREATURE_7:LEATHER +!CREATURE:NIGHT_CREATURE_8:LEATHER +!CREATURE:NIGHT_CREATURE_9:LEATHER +"CREATURE:NIGHT_CREATURE_10:LEATHER +"CREATURE:NIGHT_CREATURE_11:LEATHER +"CREATURE:NIGHT_CREATURE_12:LEATHER +"CREATURE:NIGHT_CREATURE_13:LEATHER +"CREATURE:NIGHT_CREATURE_14:LEATHER +"CREATURE:NIGHT_CREATURE_15:LEATHER +"CREATURE:NIGHT_CREATURE_16:LEATHER +"CREATURE:NIGHT_CREATURE_17:LEATHER +"CREATURE:NIGHT_CREATURE_18:LEATHER +"CREATURE:NIGHT_CREATURE_19:LEATHER +"CREATURE:NIGHT_CREATURE_20:LEATHER +"CREATURE:NIGHT_CREATURE_21:LEATHER +"CREATURE:NIGHT_CREATURE_22:LEATHER +"CREATURE:NIGHT_CREATURE_23:LEATHER +"CREATURE:NIGHT_CREATURE_24:LEATHER +"CREATURE:NIGHT_CREATURE_25:LEATHER +"CREATURE:NIGHT_CREATURE_26:LEATHER +"CREATURE:NIGHT_CREATURE_27:LEATHER +"CREATURE:NIGHT_CREATURE_28:LEATHER +"CREATURE:NIGHT_CREATURE_29:LEATHER +"CREATURE:NIGHT_CREATURE_30:LEATHER +"CREATURE:NIGHT_CREATURE_31:LEATHER +"CREATURE:NIGHT_CREATURE_32:LEATHER +"CREATURE:NIGHT_CREATURE_33:LEATHER +"CREATURE:NIGHT_CREATURE_34:LEATHER +"CREATURE:NIGHT_CREATURE_35:LEATHER +"CREATURE:NIGHT_CREATURE_36:LEATHER +"CREATURE:NIGHT_CREATURE_37:LEATHER +"CREATURE:NIGHT_CREATURE_38:LEATHER +"CREATURE:NIGHT_CREATURE_39:LEATHER +"CREATURE:NIGHT_CREATURE_40:LEATHER +"CREATURE:NIGHT_CREATURE_41:LEATHER +"CREATURE:NIGHT_CREATURE_42:LEATHER +"CREATURE:NIGHT_CREATURE_43:LEATHER +"CREATURE:NIGHT_CREATURE_44:LEATHER +"CREATURE:NIGHT_CREATURE_45:LEATHER +"CREATURE:NIGHT_CREATURE_46:LEATHER +"CREATURE:NIGHT_CREATURE_47:LEATHER +"CREATURE:NIGHT_CREATURE_48:LEATHER +"CREATURE:NIGHT_CREATURE_49:LEATHER +"CREATURE:NIGHT_CREATURE_50:LEATHER +"CREATURE:NIGHT_CREATURE_51:LEATHER +"CREATURE:NIGHT_CREATURE_52:LEATHER +"CREATURE:NIGHT_CREATURE_53:LEATHER +"CREATURE:NIGHT_CREATURE_54:LEATHER +"CREATURE:NIGHT_CREATURE_55:LEATHER +"CREATURE:NIGHT_CREATURE_56:LEATHER +"CREATURE:NIGHT_CREATURE_57:LEATHER +"CREATURE:NIGHT_CREATURE_58:LEATHER +"CREATURE:NIGHT_CREATURE_59:LEATHER +"CREATURE:NIGHT_CREATURE_60:LEATHER +"CREATURE:NIGHT_CREATURE_61:LEATHER +"CREATURE:NIGHT_CREATURE_62:LEATHER +"CREATURE:NIGHT_CREATURE_63:LEATHER +"CREATURE:NIGHT_CREATURE_64:LEATHER +"CREATURE:NIGHT_CREATURE_65:LEATHER +"CREATURE:NIGHT_CREATURE_66:LEATHER +"CREATURE:NIGHT_CREATURE_67:LEATHER +"CREATURE:NIGHT_CREATURE_68:LEATHER +"CREATURE:NIGHT_CREATURE_69:LEATHER +"CREATURE:NIGHT_CREATURE_70:LEATHER +"CREATURE:NIGHT_CREATURE_71:LEATHER +"CREATURE:NIGHT_CREATURE_72:LEATHER +"CREATURE:NIGHT_CREATURE_73:LEATHER +"CREATURE:NIGHT_CREATURE_74:LEATHER +"CREATURE:NIGHT_CREATURE_75:LEATHER +"CREATURE:NIGHT_CREATURE_76:LEATHER +"CREATURE:NIGHT_CREATURE_77:LEATHER +"CREATURE:NIGHT_CREATURE_78:LEATHER +"CREATURE:NIGHT_CREATURE_79:LEATHER +"CREATURE:NIGHT_CREATURE_80:LEATHER +"CREATURE:NIGHT_CREATURE_81:LEATHER +"CREATURE:NIGHT_CREATURE_82:LEATHER +"CREATURE:NIGHT_CREATURE_83:LEATHER +"CREATURE:NIGHT_CREATURE_84:LEATHER +"CREATURE:NIGHT_CREATURE_85:LEATHER +"CREATURE:NIGHT_CREATURE_86:LEATHER +"CREATURE:NIGHT_CREATURE_87:LEATHER +"CREATURE:NIGHT_CREATURE_88:LEATHER +"CREATURE:NIGHT_CREATURE_89:LEATHER +"CREATURE:NIGHT_CREATURE_90:LEATHER +"CREATURE:NIGHT_CREATURE_91:LEATHER +"CREATURE:NIGHT_CREATURE_92:LEATHER +"CREATURE:NIGHT_CREATURE_93:LEATHER +"CREATURE:NIGHT_CREATURE_94:LEATHER +"CREATURE:NIGHT_CREATURE_95:LEATHER +"CREATURE:NIGHT_CREATURE_96:LEATHER +"CREATURE:NIGHT_CREATURE_97:LEATHER +"CREATURE:NIGHT_CREATURE_98:LEATHER +"CREATURE:NIGHT_CREATURE_99:LEATHER +#CREATURE:NIGHT_CREATURE_100:LEATHER +#CREATURE:NIGHT_CREATURE_101:LEATHER +#CREATURE:NIGHT_CREATURE_102:LEATHER +#CREATURE:NIGHT_CREATURE_103:LEATHER +#CREATURE:NIGHT_CREATURE_104:LEATHER + CREATURE:HF1248 DIVINE_1:LEATHER + CREATURE:HF1248 DIVINE_2:LEATHER + CREATURE:HF1248 DIVINE_3:LEATHER + CREATURE:HF1108 DIVINE_2:LEATHER + CREATURE:HF1249 DIVINE_1:LEATHER + CREATURE:HF1249 DIVINE_3:LEATHER + CREATURE:HF1345 DIVINE_3:LEATHER˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/masterworkammo.dfstock b/data/stockpiles/masterworkammo.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..19d7aebeaa03d193991f789a5e90a78df28510e3 GIT binary patch literal 34 qcmZ?bR^s$cEG|hcN-NEoz{og*k#PYd!wN=;4U7ys7#R*QG5`Rj8VKb8 literal 0 HcmV?d00001 diff --git a/data/stockpiles/masterworkarmor.dfstock b/data/stockpiles/masterworkarmor.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..e3d457dc88d36da0dfd598376e1b424500ccfe9b GIT binary patch literal 39 vcmeBTK-%oY*GM33RN;CA!U&W4EJTSY4O<8TLwdu-r22Q0w2u2xdoq4j-n2og g;VEC%f@%2P{4DC$q_Y=*E5HqkI}{IqCloIzKHl(XWdHyG literal 0 HcmV?d00001 diff --git a/data/stockpiles/metalammo.dfstock b/data/stockpiles/metalammo.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..ed20da3f7cb8b9ed3abfdc3aa198acc683f8af41 GIT binary patch literal 768 zcmZvZJ8r`;5JbfyW&S^hw=fLDa5_?2%L}h4futP74X>0kWy+K(Q|351MhL|M*APp- z9nNxw`u9v;plNNb43^JmTf@(FOO?DzbBDAC%SqXG-6@GaJ0R|IKvVKNI&L|DyiC5@ zD(5(G9-_xhQgm2{;dPvm@@V>o>wuv2HnQ_{H;UwaI(G2l!G;YQkdG7WEp7W;h~f`%LCvQC literal 0 HcmV?d00001 diff --git a/data/stockpiles/metalarmor.dfstock b/data/stockpiles/metalarmor.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..7d781749003e14085659cbbdc1ff602173da1cf6 GIT binary patch literal 773 zcmZvZJyOFk5QU|JlK;Qt7@15a6FRNEadxtnJ(3+DjX4h$B_$;#B_#*q5NJ$1+Kg4| z+o!jBPp|s-1kT7-t}vFe2Xd8_=c3{qF5=u1@6bt-Rdsz#g3lINy6nkj@(zS8dxZ1o z3(j~ifp0K)Tt!7qMc2PhGByp_wv;+#%o-QiX}lXma2t;`J~rsOHCcrF5pEo<8R2F$ zVDl_PF`8U1Hr$r|V}#{@72UWsAm-5YG%g8&9g{5v@m(ANn{3-1O1qBSRUW(khb|)o z@@3m>U@}63gGgp_QyLrUIm+ud$UORhoOVIi&1B{Sn0kWy+K(Q|351MhL|M*APp- z9nNxw_V-L)pfW;h~f{SLCv=S literal 0 HcmV?d00001 diff --git a/data/stockpiles/metalweapons.dfstock b/data/stockpiles/metalweapons.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..d89f668e1e98e46e3a6a1aac1d04e10630588f8e GIT binary patch literal 773 zcmZvZJyOFk5QU|JlK;P?)eSP4Ob8XSy>WK3mOYXkhBUehC!nOHq@<+eKpX;%iAS5U zN`3qER`2Om{+_5aG_@_Y!Tb?zZTPvYsZbYj?vQq1Imzp$IVQnp3&dUaXmWlh3tRS} z&ZBQ;wR0S}7J|oBR5V!n{&k#@ZqalV*8xG^+Q3fZ-5^r8@z}s~3)ZjEfVv;y*5aB# z-HZmLUw9}2UGT-4x~hK+v;42Zk6X)-C^S8dOG02vXv#r+7e^WCeCrRTT}N(f2fqKI z%Lp@cRo8CRWP}C>mP{AA(k9e%l-F&5X!HRE?t;$Gc;*9}C)gsvmI<~}EYYxImV)yX eT%_PK1y>K^NxVt0RW__4l4iv literal 0 HcmV?d00001 diff --git a/data/stockpiles/otherarmor.dfstock b/data/stockpiles/otherarmor.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..b053744be14a1ccb09689d76a5ce5cc31d52d4ce GIT binary patch literal 122 zcmXZRAr1mD5CzcrNeQCCbCz*KnQRG@mUNoHswEs}6%vU=B9R;o#LAa9z?Du zr1U9S?U}=_7Uh*^HRH9Z^sJ48AMD6+XsTrzOx!u9f3^ji)JE@O3&SX_TkJooq&#wO6(`h&28sN=iyfBpOOS4J)v+qjfpQ zGk5M>+pi0LiQ3pkXjD&)p_*T(+94gl=0usUJ122&50tAMQLB7LTBDD^uad5`!a07M zKu5x(?%SLJf$}!8N$QJD{63W(JbSQViyHXj2z!fL0)97oAn}s>B@lJKSWw@d%w&>( zSB7yQ5{ZoF^R&c8ON6=^njg}WB%$`i&p3u z&)L0h2Sm|N{GTlB8V|8AEDCH{V5e8%IDEi97T{ut003N$6$2QaG_RY%&C^t?$Z-Wx;erut*oQ z2uYgsWdW{VJ5i`Fp|2w<%{P#ac}Bev|iumR)81gw9?ch^(C4{La1)5h>A(ac}Wvw7A2l9dFBt+pvQ$H{HSm z;*8OyDzQ>gZ)X)#BCvt&7@}WU!~N+RhHsY@wb?PY@Rfu47~0rgEP}#I#gTT zSz@=mGKFQ+@^);Tu^O*~YnhR8)UZ7EdXWQ743Co2qIi$(mx&pkINh2Fv^dVqs}q;F zdfN9B+=!>Zg5 N6ZFFN4f=q-pdT%C9f<${ literal 0 HcmV?d00001 diff --git a/data/stockpiles/pearlash.dfstock b/data/stockpiles/pearlash.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..1f7d6a945f5dc26a70077d5751533000882def9d GIT binary patch literal 33 pcmWII;^GK!bqw-x4EC77$T)+MaRDR43Py_v!xXzo+M4oS`{>40`U3`p|pQbG{6U_*3+$iOG|Be!D!17yHtSH&A1{ zxEBXFkZ6x;8dOJC^{Q@ib|n2j53Elkf)7J91Z_LJq3!g&d}UeALZg$3rYQ?v6f*QFumo z(8QBPx%W2B?VLnmP<|!=&V8;GiAXTbwYOaMEr_(#>af)H($$kPGC7#HhU%JRm`G1U zPWdvOaPgu|n;Y`piU1z^LZGu4ueCnhyk4-%w@Y4{K{q2eY-wxN#*mdlt2TSth^*RD zlcrjF4oLO=VzHoE3`Ea7spi~x6-4P^jbWpk(m}`#*3Ue#yX>MSMyausf;yw-s7(nV zUY^#NB+a4ergYF24=jv34^YM1Ac|IkC2rF&YcWUQ;rI{K3M@9DGCw* z;HlL?&?oBSIg{JwhP!@9JUjJ4%7VSVAag*Y@F09jBt1Ym!-a<^yPfANeUU#3DB5Wg zDGGB3n1c_6lHcSXLcIO=X#fFSh&R-()GMj$`L@4yFk}t4Hp5pE%N40-a_F}ZCRXHlXP2G#>%t;a3WK+H%7Ze~t!+$_iwWD3;|w9+7pBvFsR)gliQ^mldor#k&xo&E=?zlQ4o literal 0 HcmV?d00001 diff --git a/data/stockpiles/plaster.dfstock b/data/stockpiles/plaster.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..6ac4d4607b4b2df25fa02c132bc910b5046cee8b GIT binary patch literal 103 zcmXpC;1clk^AB=&^z(GKa*qrM4)x^{Mu<52I6657hqwlD2_Xc7U42~rJVRWO6$U$o gc=`ngI0j8%WSqgsxPXyi1tY@-M#deC3dOM2o(yj&B%JIrZZL4uvcEbB945{6$GworsCkh%q0!=YUuJxZ@C?)`YOyF_@uFw2A@gly)9S2+p7@L*a+`1p;cS!uUlP^e>S~ly*`K} zVU1xCGu#Y|JIIN3FfJDbi!iMmWm7{%Ruea~Uw9-NOZ$00>zUR@4P=#$eB)6ktV$@O ziaLO?b3d7>d=7Jon;;U5Glrmoye{5|Z$N2TjCb||gHn}j z_)J!Cbw<1u?>aX(!%B1k_cJb>`$?l-Ms39=MF+p^<2J4{y+GvvYH@tY*Pf-rgLG%(Ou-|9gx2;x zEiAf3p^(*YQeol%(#_;_3TLl$Qg=n~c102(u_rj%tsFE6U5^A$j=#TZA`z z76NXloCIVBz^O`*y*n5zFRfA1!{e?dUp?+j@5LVz3lKp}?)T6{uZIDs6ruD98$hF; z2QdOHn#3_i`BxzH!C;GQM0`kU{-bbUAqw$6MNOje5-I^smcoFs(?K$r1Dfm>J4tto zc^z&m*&=^Ht-ctC?{|tZ5L^Aa$O+6TorqGuT6+8olMY}kory~A?smnr8+uyNTJ)%y zY=?iHgzgpHG{|c)JdJG&mN>-WZwHYR5cLKp&%@@ngXq9Gjt$Sc{935Y4dmGByvrNH e=tHrg1pZl7e^u4r)ywDVg9h6;0#;< literal 0 HcmV?d00001 diff --git a/data/stockpiles/roughglass.dfstock b/data/stockpiles/roughglass.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..9571b3f80ba1a33f3bacad1036beb0e4788c6eba GIT binary patch literal 64 zcma#j|bSivZuCl!KIdN;ULdEwMVze(@E}e-jvo7y~n|2QBigWZ;R83 z`M{MOKSB1B{TNC0-q6O$8fCjqM>$O$FlqYO0WT?{^BB$|H&-mscVT0+ra%bfX;b|K zS7`{5P=O~G_bCsO zkRP}|c*~=>!zgW#5A7cce>SOWfnc_vVW65eiWcOgFKs~hi_&8jjn^K!tlvtS>#b^DNL0b*m1TM|2ew#$vhMEsRInTxOO*%lanRP&#EW8Y8Exhu9 zR-y!l?P9%QmLK|o&S@#vVu_EnG*Rg;bLMtTb+Iw(Ep_8*Xjl(1(BGZB7$bVYga}4~ zjnJ3Y2qeOPH&!&rwMkXTMv7PsK%PbwAPnfA=i2Y2K@xsR%)p^5HQ4JbyG|MqBYa3y zW1z+1!X(V@kPliM$@d9HyS1FrxEsVO@?e;J>k$*ZbiwG-)4LReAh3hx-*E}2YYncEIjDsw6+R+reF_@Fg(wt~j~#?Wf( zQ*(AFMqhx6zOE_?uso%zml!g{^>_l^#N~Dmg-P8sKHp)OkCIs!4>&e+O&jkQ_=BJ# z1zx1_`7lF_XJqncM;cs4-ufz*Itv(XJnNA!ICL80uv(qb7^Jl~hUse9Z`U0TVa-^e z${*Jlh1|*UBdA^*mD9wk(uiKkvb=(3W!4!JL4bumNvJUp*2Vfxfxs|=>#LcFN)VLJ zvKiFI>6h&5hq}JsU>3-X`R)#xUPw0<{#x##ErrEfyX8gdZI?av1UbiRh8q|{zCCE1 znoC%`;mXnN8OoPzZGO#^UvM`mEuUZ{1INqEiPl6|a}F_Y{HQI~;7Ux~?mTFI*GkL_ z&%k{QN`|}-X{}mNX^k^}dl2@zt7TV5e$vAsM$4iFl;PyYkp3#@>9X<@J4i!=W>f;)?L_GDyv?oajp%I&|pJp+i5-Af+lnnhmFgqtR?& zzC7KX-(rX~KtTO9L+Epf!R$bEWdYx8k$zkQUv5~o;C7kX!E{rOqQ1--T#-E-db31+ z*y`yR1{b{{DCR7B_mNR#O!U^)P}u|EF6Y-9v>j}YYO-$$VGqXvp`vOCP2?BtFtvbLDZj(sqgvqU_Q;zto2 ztnp9|yjcF&#Ca!`pn#_7^L|4E=U&Y`MAx4`-oP2%)zC8^bWDjq2&;K;!2FNuI}_k6 zSmabqb!4F-SFs!Xd1Ij?xLIKtJ!4~*PwjAM1B&Q%l*CeF7`@a;V+f#NM_5P2rmcj9 zMT;o}*7Qe$Hd2^ZHKeGrlY3zFp!?GHmwy?x~@g7Y-Kg8He z>TL7WWEEN~Lcf#{%3jbbL(NlSAcfhF#o}>Q$bcJI5MiW9iMKWsiRFT-rHbxsL4L0) zoLId)xod?)Jx%MPiCi`f@)gOuKy=);CJSY0EVw^D9)@6!M%R{>(;ivBoMAJj!&wBv<6~D%w}8tbN4b7vRt~qzKK15(|DP z5UJR>h$Lb?YQD5w!$`X^3X+dz`_*wo=X%-LUi)q3W#yY!LGfVOusk=}OfRSX>Oip5 zvLeC(tXE4Gt|oYxp$8Akj6z7hhx`s=Z|Yx*RFQYBO!E!OXL&`%gf(h*5xgAx9?7)) Xr>^_ib-%jP@9y-c>;879f8FUn@-gPF literal 0 HcmV?d00001 diff --git a/data/stockpiles/stonetools.dfstock b/data/stockpiles/stonetools.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..6f6040189dceed6aa8bc6db86cb406475fb3584e GIT binary patch literal 1886 zcmZ`)yN=XA5L`<{NE~lCjQA0C3yVb#~6}E z3*_6EYbInf^%fpbf`$AM%;3AZzKqOd%L2YwYr5eY_;kgx0lVp;EsTe9Dd_W*!4}!W zlEWqP&00?f-`n6=mV&M5-33O8G0|HaOl1pzJxj&=#KLouSVBi3Rp{B;vj?S8klm6Q zgWW|bmc0g-w0@phJxOL738k1?In3 z-=c?Bu*k8RYR^JLu42#dyOo6wU?)XoaEuLEuG&&R4=AFqqwpp2EeuX-q{(|wupz92 zV$)W_!lDIB0)yS2pb5!OS2d)&#+bR&%}Tj|)$Nn@_h&4!ejK`ve7~H=?HU%jFMT5H zrn?Y2Nu6yTXIX{TDv?`C2xZUY6sFlzVjxA?4aLy@R1$*iSrB2QkRo5LFA~dz)SN21 zvjuryRXA~ad9;@biF!I+4^8B&wRW+FmOkE5CUa6bsA9@*J|6 zUXI(-0dL0TM1%uauQ^$`I>Frp9XMEK6hiWQ*x|RP`Q9`?n$yqb_E&TI-JJe3r+?(r B`*8pO literal 0 HcmV?d00001 diff --git a/data/stockpiles/stoneweapons.dfstock b/data/stockpiles/stoneweapons.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..8a6ee77aa472f31e712f4dc22ec701667beb9a5b GIT binary patch literal 1885 zcmZ`)JCf8e5FHsX!b2Ovfv&h;|E*th9DQAf|NQ##t zI2d$LcDz{rFpKj-Dv>v9%osF)+&)(N(g1ov(CtuGSGh18rXy0Zm& zUsX79dU>>$3W<6;T@Ov(f*ySsAA6F6vv3$TLj@)-KU6BrT4&A=HfBj+$^F0d~G=7md~7lL=1d%e6|Bd&1J z3Ku}bd&D16=@q;IB8I6G3CxSYl@<&ZKwwc7SUd#BL1Sqa5|&XA>|Nn!^yPjLg!7_s z0dUs|JOgA5Qzr%NU5O)-c&bS(%~HbxNDbRtY8*6HZ;GKW6vJUdG3+|Rw~??kQv&sc zurw3Cu!QF{Vf7}7vnyexiQpt5JOc<{HNuTaSiKc)2IXHXm-R(4m=}dBt?>9%1Y1gB zIaF8~lz);0g2rzWjlY64z9bqyI5Zx7+CM2TN2Kv3(f-L8utKgGo(*rcCTmR5y3&nd zr}bAOw$IBM?gZ`(cd9nVee%w5H}B4H=lwmtv4^r~y~d1jKjz1{>~&!Aq{_R0Fzan@ zKChd7_g84+p^vA=j-b6gR32LP8`JLhL|J%n(Sx-)WW!#E-P*!zJ?~Y|+tucHY#Wp1 zZI$i(;*~RE?WV2E${TvQ97=H9TU)L7CaeDg^V}Rv4?Z23Ca+BD(EI&OUFP|C|E}=h Y*C_fHML(k1&*=HDX!bjr{fTCO0Y-01TmS$7 literal 0 HcmV?d00001 diff --git a/data/stockpiles/tools.dfstock b/data/stockpiles/tools.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..abe70454b74d2f1357008979c9a76dc3ec1b6232 GIT binary patch literal 29 lcmYdd<6;T%_xG8=$T)+MaRDR43Py3Z6h+_NLMmIq!X=26Z<5ClCO=*x+Ra{Efu*ITrKP2v+Y*Fngp~JiINS?9 zFq@`aY>s8`o5Xop^XNG>KFbi;iPnK%J hxruYW+U~h1iLFoLFS?@xU4a{L2R*QQf?l9E=nF$-KBNEu literal 0 HcmV?d00001 diff --git a/data/stockpiles/unpreparedfish.dfstock b/data/stockpiles/unpreparedfish.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..465a95dddaf4044c86f9ec7b958739c01d39f275 GIT binary patch literal 1720 zcmZ`)F>>255ER->EK71SacoZ}?{EiEpuh+O7!a_A6~C0yrAJlDlqvJHfLIbZGD@Y< z-rn8fa0~9EGsAa<*>i}pF;JwhB#rI;{Z`jr7~V0h_9eO!IWEvMG-bwXRq z$x|82MFXXwEE!PqNfZ|wdjTm53I-)!}Vt^NWq C!{^BW literal 0 HcmV?d00001 diff --git a/data/stockpiles/wax.dfstock b/data/stockpiles/wax.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..f7ec69912e41e23f568923909774ee2d863ee8f4 GIT binary patch literal 98 zcmWgg&JuGDa&-&|4RW>e@b`0#jCXQ%wF-BPKoWNf^>y-bg^E||kU#z{mgqNAVh5 literal 0 HcmV?d00001 diff --git a/data/stockpiles/weaponsprefix.dfstock b/data/stockpiles/weaponsprefix.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..6a84bf0c70272c8ceb65ef1e09343904535383e4 GIT binary patch literal 3838 zcmZ`*O>f*p7|u#Tl#RkifHvI{P*+Hx+`3g&+7sjP#2$7$V`j$LY%W&RCQ_s*t(H`x zSAGFUjvP61abvy8h#atGBhExg8AV$ z5pK$|3_VcHX7P9(H&x)pI&h?zRMFlQ#%ifE=h2~9BOI5749r+eA!2Wt3>JppMS>>C z=ma0f1Or4mkysPGm8sBv_%NcLA-`j>_bq}cxhcvP^{c-+lT3RAn<$owz3*4_H7TKu zElh1YRl5xPeH&W(0Bn0EQI%k3OpRmZ#P6>Ll=TD#o95b`y(GDZzOG z8+)BZDYLqXj?xtKQdb3)6H0JzLEG(gAvFH7`z^)HV`z?~7UKsSWFYEDiSZX348`U& zDO5G3j+v^8CKzI}Ek(UqwL~hcg7L%72@eJY8{`j$vwQB+Go0x*Zg-SffG&JA(~SX7jmG zYf=eo5+L z#WDa$GrAXC89=oj1;-|XYC|UT%?-A0B7ImAI488#N@h)hM0%MDCK0pHUvRhznkCHA zK5>Jy(-e+C)C(M{T%_v(;zg!cTZVKao0krr$T7g3;B<}tB>b4zFDsm|@JPVS({)qA zuRM|>t;5x#?FG!srmUpaiIqibg7kMP3%s%#Oc966)rN-!5JE-;uOkISP*28|m|E>A z&3TzLs>C}Wh<>fvb8~eZNCcQTo9&i3>|_x5~o`DSsrBYu5#{`Thb&Gp&U;YY>UFE{6li+5Lt_t@g*^5X2(&E(eN vgj$^RElzGNPN>C6XK`|C@rS{WgWm^(KL&$82U~v)_WvGi{WIA5cd+$8%10;W literal 0 HcmV?d00001 diff --git a/data/stockpiles/woodammo.dfstock b/data/stockpiles/woodammo.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..623ca25f239a3faf33bc1e79710a50a0356cb6c8 GIT binary patch literal 29 lcmZ=_6JiPX_jj4V$T)+MaRDR43Py(5VejUvNg_IN)|D|7;yv)L0}!fM*GMTQl(6pGG)q?Sy~1;hXR<-<=#7d z{iZ;nMuRYFOtN4ySTJf15;C~Xw8_r*I}wbw6^{v&Yxt0K#*9ydPgy#wUujRR!nJp| zDrM(N2p4>?LCf$Kd=!Yxkpc^(uZ0tgTrfF;ckOGXvj4x{?p~s0KYE~WOZIiXSz4*o z*X6Rb_@!ZmchlC2#eT+|1Hojm(w;Fy7{k-dLVb#&(PH!nii3X;+&U8{uS+eAQp?GT z7YD!vbMb~njK-&?Ql3J1e(lbu#WAQEKPZ68gl2{}*-F0{(LH4Rk zWZW;|PhWaDJT~>)_L?*ge`#8b6}h6U bYnK6hABG>p@N+o*8cx55)1Tq=cR2k6Qg5E1 literal 0 HcmV?d00001 From 084d28b0aedc93b2b4d7880a9f3bfbd0236102c9 Mon Sep 17 00:00:00 2001 From: 20k Date: Wed, 25 Jan 2023 18:40:27 +0000 Subject: [PATCH 0766/2222] Reworked heap debugging + tools implementation --- docs/dev/Lua API.rst | 48 +++++++++++++ library/LuaApi.cpp | 165 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9f2386660..b5e66ef8b 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -2768,6 +2768,54 @@ and are only documented here for completeness: Returns a numeric identifier of the current thread. +* ``dfhack.internal.msizeAddress(address)`` + + Returns the allocation size of an address. + Does not require a heap snapshot. This function will crash on an invalid pointer. + Windows only. + +* ``dfhack.internal.getHeapState()`` + + Returns the state of the heap. 0 == ok or empty, 1 == heap bad ptr, 2 == heap bad begin, 3 == heap bad node. + Does not require a heap snapshot. This may be unsafe to use directly from lua if the heap is corrupt. + Windows only. + +* ``dfhack.internal.heapTakeSnapshot()`` + + Clears any existing heap snapshot, and takes an internal heap snapshot for later consumption. + Windows only. + Returns the same values as getHeapState() + +* ``dfhack.internal.isAddressInHeap(address)`` + + Checks if an address is a member of the heap. It may be dangling. + Requires a heap snapshot. + +* ``dfhack.internal.isAddressActiveInHeap(address)`` + + Checks if an address is a member of the heap, and actively in use (ie valid). + Requires a heap snapshot. + +* ``dfhack.internal.isAddressUsedAfterFreeInHeap(address)`` + + Checks if an address is a member of the heap, but is not currently allocated (ie use after free). + Requires a heap snapshot. + Note that Windows eagerly removes freed pointers from the heap, so this is unlikely to trigger. + +* ``dfhack.internal.getAddressSizeInHeap(address)`` + + Gets the allocated size of a member of the heap. Useful for detecting misaligns, as this does not return block size. + Requires a heap snapshot. + +* ``dfhack.internal.getRootAddressOfHeapObject(address)`` + + Gets the base heap allocation address of a address that lies internally within a piece of allocated memory. + Eg, if you have a heap allocated struct and call this function on the address of the second member, + it will return the address of the struct. + Returns 0 if the address is not found. + Requires a heap snapshot. + + .. _lua-core-context: Core interpreter context diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index c9bdc3021..babf2f7c0 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2833,12 +2833,177 @@ static int8_t getModstate() { return Core::getInstance().getModstate(); } static std::string internal_strerror(int n) { return strerror(n); } static std::string internal_md5(std::string s) { return md5_wrap.getHashFromString(s); } +struct heap_pointer_info +{ + size_t size = 0; + int status = 0; +}; + +static std::map snapshot; + +static int heap_take_snapshot() +{ + #ifdef _WIN32 + snapshot.clear(); + + std::vector> entries; + //heap allocating while iterating the heap is suboptimal + entries.reserve(256*1024*1024); + + _HEAPINFO hinfo; + int heapstatus; + int numLoops; + hinfo._pentry = NULL; + numLoops = 0; + while((heapstatus = _heapwalk(&hinfo)) == _HEAPOK && + numLoops < 1024*1024*1024) + { + heap_pointer_info inf; + inf.size = hinfo._size; + inf.status = hinfo._useflag; //0 == _FREEENTRY, 1 == _USEDENTRY + + entries.push_back({hinfo._pentry, inf}); + + numLoops++; + } + + for (auto i : entries) + { + uintptr_t val = 0; + memcpy(&val, &i.first, sizeof(void*)); + snapshot[val] = i.second; + } + + if (heapstatus == _HEAPEMPTY || heapstatus == _HEAPEND) + return 0; + + if (heapstatus == _HEAPBADPTR) + return 1; + + if (heapstatus == _HEAPBADBEGIN) + return 2; + + if (heapstatus == _HEAPBADNODE) + return 3; + #endif + + return 0; +} + +static void* address_to_pointer(uintptr_t ptr) +{ + void* as_ptr = nullptr; + memcpy((void*)&as_ptr, &ptr, sizeof(uintptr_t)); + + return as_ptr; +} + +//this function probably should not allocate. Then again we're shimming through lua which.... probably does +static int get_heap_state() +{ + #ifdef _WIN32 + int heapstatus = _heapchk(); + + if (heapstatus == _HEAPEMPTY || heapstatus == _HEAPOK) + return 0; + + if (heapstatus == _HEAPBADPTR) + return 1; + + if (heapstatus == _HEAPBADBEGIN) + return 2; + + if (heapstatus == _HEAPBADNODE) + return 3; + #endif + + return 0; +} + +static bool is_address_in_heap(uintptr_t ptr) +{ + return snapshot.find(ptr) != snapshot.end(); +} + +static bool is_address_active_in_heap(uintptr_t ptr) +{ + auto it = snapshot.find(ptr); + + if (it == snapshot.end()) + return false; + + return it->second.status == 1; +} + +static bool is_address_used_after_free_in_heap(uintptr_t ptr) +{ + auto it = snapshot.find(ptr); + + if (it == snapshot.end()) + return false; + + return it->second.status != 1; +} + +static int get_address_size_in_heap(uintptr_t ptr) +{ + auto it = snapshot.find(ptr); + + if (it == snapshot.end()) + return -1; + + return it->second.size; +} + +//eg if I have a struct, does any address lie within the struct? +static uintptr_t get_root_address_of_heap_object(uintptr_t ptr) +{ + //find the first element strictly greater than our pointer + auto it = snapshot.upper_bound(ptr); + + //if we're at the start of the snapshot, no elements are less than our pointer + //therefore it is invalid + if (it == snapshot.begin()) + return 0; + + //get the first element less than or equal to ours + it--; + + //our pointer is only valid if we lie in the first pointer lower in memory than it + if (ptr >= it->first && ptr < it->first + it->second.size) + return it->first; + + return 0; +} + +//msize crashes if you pass an invalid pointer to it, only use it if you *know* the thing you're looking at +//is in the heap/valid +static int msize_address(uintptr_t ptr) +{ + void* vptr = address_to_pointer(ptr); + + #ifdef _WIN32 + if (vptr) + return _msize(vptr); + #endif + + return -1; +} + static const LuaWrapper::FunctionReg dfhack_internal_module[] = { WRAP(getImageBase), WRAP(getRebaseDelta), WRAP(getModstate), WRAPN(strerror, internal_strerror), WRAPN(md5, internal_md5), + WRAPN(heapTakeSnapshot, heap_take_snapshot), + WRAPN(getHeapState, get_heap_state), + WRAPN(isAddressInHeap, is_address_in_heap), + WRAPN(isAddressActiveInHeap, is_address_active_in_heap), + WRAPN(isAddressUsedAfterFreeInHeap, is_address_used_after_free_in_heap), + WRAPN(getAddressSizeInHeap, get_address_size_in_heap), + WRAPN(getRootAddressOfHeapObject, get_root_address_of_heap_object), + WRAPN(msizeAddress, msize_address), { NULL, NULL } }; From 5cc6293407f68302113c1737ef8bc07074135e2c Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 27 Jan 2023 06:04:55 +0000 Subject: [PATCH 0767/2222] fix unused variable on linux --- library/LuaApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index babf2f7c0..04f771928 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2980,9 +2980,9 @@ static uintptr_t get_root_address_of_heap_object(uintptr_t ptr) //is in the heap/valid static int msize_address(uintptr_t ptr) { + #ifdef _WIN32 void* vptr = address_to_pointer(ptr); - #ifdef _WIN32 if (vptr) return _msize(vptr); #endif From 5a7debfc777fb8c3bfe2b5fc11dfdec92fd551f3 Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 27 Jan 2023 06:12:24 +0000 Subject: [PATCH 0768/2222] cleanup, linux fix --- library/LuaApi.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 04f771928..873b0bcd2 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2869,8 +2869,7 @@ static int heap_take_snapshot() for (auto i : entries) { - uintptr_t val = 0; - memcpy(&val, &i.first, sizeof(void*)); + uintptr_t val = reinterpret_cast(i.first); snapshot[val] = i.second; } @@ -2890,14 +2889,6 @@ static int heap_take_snapshot() return 0; } -static void* address_to_pointer(uintptr_t ptr) -{ - void* as_ptr = nullptr; - memcpy((void*)&as_ptr, &ptr, sizeof(uintptr_t)); - - return as_ptr; -} - //this function probably should not allocate. Then again we're shimming through lua which.... probably does static int get_heap_state() { @@ -2981,7 +2972,7 @@ static uintptr_t get_root_address_of_heap_object(uintptr_t ptr) static int msize_address(uintptr_t ptr) { #ifdef _WIN32 - void* vptr = address_to_pointer(ptr); + void* vptr = reinterpret_cast(ptr); if (vptr) return _msize(vptr); From 1eaee610447244784c5bca8dc20dd700cde5ceb5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 6 Mar 2023 11:46:43 -0800 Subject: [PATCH 0769/2222] Fixes dig-now job list iteration/scanning --- plugins/dig-now.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 037e7ae84..be431722b 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -85,10 +85,11 @@ public: df::job_list_link* node = df::global::world->jobs.list.next; while (node) { df::job* job = node->item; + node = node->next; + if(!job || !Maps::isValidTilePos(job->pos)) continue; - node = node->next; df::tile_designation td = map.designationAt(job->pos); df::tile_occupancy to = map.occupancyAt(job->pos); const auto ctd = td.whole; From 0459831c6a9b86371813ef8072d01593fc826d69 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 6 Mar 2023 12:33:02 -0800 Subject: [PATCH 0770/2222] Revert "revert recent changes to dig-now due to lockups" --- docs/changelog.txt | 2 + plugins/dig-now.cpp | 250 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 207 insertions(+), 45 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a3428031..7cec154f3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -66,6 +66,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. +- `dig-now`: fixed multi-layer channel designations only channeling every second layer - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled - `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's @@ -76,6 +77,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. +- `dig-now`: added handling of dig designations that have been converted into active jobs - `tailor`: add support for adamantine cloth (off by default); improve logging ## API diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index 6227f44f5..e94cb41da 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -6,6 +6,7 @@ #include "PluginManager.h" #include "TileTypes.h" #include "LuaTools.h" +#include "Debug.h" #include "modules/Buildings.h" #include "modules/Gui.h" @@ -14,6 +15,8 @@ #include "modules/Random.h" #include "modules/Units.h" #include "modules/World.h" +#include "modules/EventManager.h" +#include "modules/Job.h" #include #include @@ -26,12 +29,129 @@ #include #include +#include +#include +#include + DFHACK_PLUGIN("dig-now"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); +// Debugging +namespace DFHack { + DBG_DECLARE(dignow, general, DebugCategory::LINFO); + DBG_DECLARE(dignow, channels, DebugCategory::LINFO); +} + +#define COORD "%" PRIi16 " %" PRIi16 " %" PRIi16 +#define COORDARGS(id) id.x, id.y, id.z + using namespace DFHack; +struct designation{ + df::coord pos; + df::tile_designation type; + df::tile_occupancy occupancy; + designation() = default; + designation(const df::coord &c, const df::tile_designation &td, const df::tile_occupancy &to) : pos(c), type(td), occupancy(to) {} + + bool operator==(const designation &rhs) const { + return pos == rhs.pos; + } + + bool operator!=(const designation &rhs) const { + return !(rhs == *this); + } +}; + +namespace std { + template<> + struct hash { + std::size_t operator()(const designation &c) const { + std::hash hash_coord; + return hash_coord(c.pos); + } + }; +} + +class DesignationJobs { +private: + std::unordered_map designations; + std::unordered_map jobs; +public: + void load(MapExtras::MapCache &map) { + designations.clear(); + df::job_list_link* node = df::global::world->jobs.list.next; + while (node) { + df::job* job = node->item; + if(!job || !Maps::isValidTilePos(job->pos)) + continue; + + node = node->next; + df::tile_designation td = map.designationAt(job->pos); + df::tile_occupancy to = map.occupancyAt(job->pos); + const auto ctd = td.whole; + const auto cto = to.whole; + switch (job->job_type){ + case job_type::Dig: + td.bits.dig = tile_dig_designation::Default; + break; + case job_type::DigChannel: + td.bits.dig = tile_dig_designation::Channel; + break; + case job_type::CarveRamp: + td.bits.dig = tile_dig_designation::Ramp; + break; + case job_type::CarveUpwardStaircase: + td.bits.dig = tile_dig_designation::UpStair; + break; + case job_type::CarveDownwardStaircase: + td.bits.dig = tile_dig_designation::DownStair; + break; + case job_type::CarveUpDownStaircase: + td.bits.dig = tile_dig_designation::UpDownStair; + break; + case job_type::DetailWall: + case job_type::DetailFloor: { + df::tiletype tt = map.tiletypeAt(job->pos); + if (tileSpecial(tt) != df::tiletype_special::SMOOTH) { + td.bits.smooth = 1; + } + break; + } + case job_type::CarveTrack: + to.bits.carve_track_north = (job->item_category.whole >> 18) & 1; + to.bits.carve_track_south = (job->item_category.whole >> 19) & 1; + to.bits.carve_track_west = (job->item_category.whole >> 20) & 1; + to.bits.carve_track_east = (job->item_category.whole >> 21) & 1; + break; + default: + break; + } + if (ctd != td.whole || cto != to.whole) { + // we found a designation job + designations.emplace(job->pos, designation(job->pos, td, to)); + jobs.emplace(job->pos, job); + } + } + } + void remove(const df::coord &pos) { + if(jobs.count(pos)) { + Job::removeJob(jobs[pos]); + jobs.erase(pos); + } + } + designation get(const df::coord &pos) { + if (designations.count(pos)) { + return designations[pos]; + } + return {}; + } + bool count(const df::coord &pos) { + return jobs.count(pos); + } +}; + struct boulder_percent_options { // percent chance ([0..100]) for creating a boulder for the given rock type uint32_t layer; @@ -320,8 +440,19 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, std::vector &dug_tiles) { df::tiletype tt = map.tiletypeAt(pos); - if (!is_diggable(map, pos, tt)) + if (!is_diggable(map, pos, tt)) { + DEBUG(general).print("dig_tile: not diggable\n"); return false; + } + + /** The algorithm process seems to be: + * for each tile + * check for a designation + * if a designation exists send it to dig_tile + * + * dig_tile (below) then digs the layer below the channel designated tile + * thereby changing it and causing its designation to be lost + * */ df::tiletype target_type = df::tiletype::Void; switch(designation) { @@ -341,19 +472,22 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, DFCoord pos_below(pos.x, pos.y, pos.z-1); if (can_dig_channel(tt) && map.ensureBlockAt(pos_below) && is_diggable(map, pos_below, map.tiletypeAt(pos_below))) { + TRACE(channels).print("dig_tile: channeling at (" COORD ") [can_dig_channel: true]\n",COORDARGS(pos_below)); target_type = df::tiletype::OpenSpace; DFCoord pos_above(pos.x, pos.y, pos.z+1); - if (map.ensureBlockAt(pos_above)) + if (map.ensureBlockAt(pos_above)) { remove_ramp_top(map, pos_above); - df::tile_dig_designation td_below = - map.designationAt(pos_below).bits.dig; - if (dig_tile(out, map, pos_below, - df::tile_dig_designation::Ramp, dug_tiles)) { + } + df::tile_dig_designation td_below = map.designationAt(pos_below).bits.dig; + if (dig_tile(out, map, pos_below, df::tile_dig_designation::Ramp, dug_tiles)) { clean_ramps(map, pos_below); - if (td_below == df::tile_dig_designation::Default) + if (td_below == df::tile_dig_designation::Default) { dig_tile(out, map, pos_below, td_below, dug_tiles); + } return true; } + } else { + DEBUG(channels).print("dig_tile: failed to channel at (" COORD ") [can_dig_channel: false]\n", COORDARGS(pos_below)); } break; } @@ -407,7 +541,8 @@ static bool dig_tile(color_ostream &out, MapExtras::MapCache &map, if (target_type == df::tiletype::Void || target_type == tt) return false; - dug_tiles.push_back(dug_tile_info(map, pos)); + dug_tiles.emplace_back(map, pos); + TRACE(general).print("dig_tile: digging the designation tile at (" COORD ")\n",COORDARGS(pos)); dig_type(map, pos, target_type); // let light filter down to newly exposed tiles @@ -594,9 +729,12 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, item_coords_t &item_coords, const dig_now_options &options) { MapExtras::MapCache map; Random::MersenneRNG rng; + DesignationJobs jobs; + jobs.load(map); rng.init(); + std::unordered_set buffer; // go down levels instead of up so stacked ramps behave as expected for (int16_t z = options.end.z; z >= options.start.z; --z) { for (int16_t y = options.start.y; y <= options.end.y; ++y) { @@ -609,46 +747,68 @@ static void do_dig(color_ostream &out, std::vector &dug_coords, DFCoord pos(x, y, z); df::tile_designation td = map.designationAt(pos); df::tile_occupancy to = map.occupancyAt(pos); - if (td.bits.dig != df::tile_dig_designation::No && - !to.bits.dig_marked) { - std::vector dug_tiles; - if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { - for (auto info : dug_tiles) { - td = map.designationAt(info.pos); - td.bits.dig = df::tile_dig_designation::No; - map.setDesignationAt(info.pos, td); - - dug_coords.push_back(info.pos); - refresh_adjacent_smooth_walls(map, info.pos); - if (info.imat < 0) - continue; - if (produces_item(options.boulder_percents, - map, rng, info)) { - auto k = std::make_pair(info.itype, info.imat); - item_coords[k].push_back(info.pos); - } - } - } - } else if (td.bits.smooth == 1) { - if (smooth_tile(out, map, pos)) { - td = map.designationAt(pos); - td.bits.smooth = 0; - map.setDesignationAt(pos, td); - } - } else if (to.bits.carve_track_north == 1 - || to.bits.carve_track_east == 1 - || to.bits.carve_track_south == 1 - || to.bits.carve_track_west == 1) { - if (carve_tile(map, pos, to)) { - to = map.occupancyAt(pos); - to.bits.carve_track_north = 0; - to.bits.carve_track_east = 0; - to.bits.carve_track_south = 0; - to.bits.carve_track_west = 0; - map.setOccupancyAt(pos, to); + if (jobs.count(pos)) { + buffer.emplace(jobs.get(pos)); + jobs.remove(pos); + // if it does get removed, then we're gonna buffer the jobs info then remove the job + } else if ((td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) + || td.bits.smooth == 1 + || to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + + // we're only buffering designations, so that processing doesn't affect what we're buffering + buffer.emplace(pos, td, to); + } + } + } + } + + // process designations + for(auto &d : buffer) { + auto pos = d.pos; + auto td = d.type; + auto to = d.occupancy; + + if (td.bits.dig != df::tile_dig_designation::No && !to.bits.dig_marked) { + std::vector dug_tiles; + + if (dig_tile(out, map, pos, td.bits.dig, dug_tiles)) { + for (auto info: dug_tiles) { + td = map.designationAt(info.pos); + td.bits.dig = df::tile_dig_designation::No; + map.setDesignationAt(info.pos, td); + + dug_coords.push_back(info.pos); + refresh_adjacent_smooth_walls(map, info.pos); + if (info.imat < 0) + continue; + if (produces_item(options.boulder_percents, + map, rng, info)) { + auto k = std::make_pair(info.itype, info.imat); + item_coords[k].push_back(info.pos); } } } + } else if (td.bits.smooth == 1) { + if (smooth_tile(out, map, pos)) { + td = map.designationAt(pos); + td.bits.smooth = 0; + map.setDesignationAt(pos, td); + } + } else if (to.bits.carve_track_north == 1 + || to.bits.carve_track_east == 1 + || to.bits.carve_track_south == 1 + || to.bits.carve_track_west == 1) { + if (carve_tile(map, pos, to)) { + to = map.occupancyAt(pos); + to.bits.carve_track_north = 0; + to.bits.carve_track_east = 0; + to.bits.carve_track_south = 0; + to.bits.carve_track_west = 0; + map.setOccupancyAt(pos, to); + } } } From 8cb3c0cf66e06fa128c98832e370cf7b239490e4 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 6 Mar 2023 12:38:14 -0800 Subject: [PATCH 0771/2222] Update changelog.txt --- docs/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7cec154f3..f58dea0b3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,8 +38,10 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ ``widgets.HotkeyLabel``: don't trigger on click if the widget is disabled - ``dfhack.job.isSuitableMaterial``: now properly detects lack of fire and magma safety for vulnerable materials with high melting points +- `dig-now`: fixed multi-layer channel designations only channeling every second layer ## Misc Improvements +- `dig-now`: added handling of dig designations that have been converted into active jobs ## Documentation @@ -66,7 +68,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. -- `dig-now`: fixed multi-layer channel designations only channeling every second layer - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled - `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's @@ -77,7 +78,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `showmood`: now shows the number of items needed for cloth and bars in addition to the technically correct but always confusing "total dimension" (150 per bar or 10,000 per cloth) -@ Stopped mouse clicks from affecting the map when a click on a DFHack screen dismisses the window - `confirm`: configuration data is now persisted globally. -- `dig-now`: added handling of dig designations that have been converted into active jobs - `tailor`: add support for adamantine cloth (off by default); improve logging ## API From 1c36031073a7152e3879a956eff8bd33964087d2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:18:06 -0800 Subject: [PATCH 0772/2222] update all build envs to ubuntu-22.04 --- .github/workflows/build.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 595bde8ed..024a8fe6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,15 +10,14 @@ jobs: fail-fast: false matrix: os: - - ubuntu-18.04 + - ubuntu-22.04 gcc: - 4.8 - 7 plugins: - default include: - - os: ubuntu-22.04 - gcc: 12 + - gcc: 12 plugins: all steps: - name: Set up Python 3 @@ -159,7 +158,7 @@ jobs: path: build/win64-cross/output/* docs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Set up Python 3 uses: actions/setup-python@v2 @@ -182,7 +181,7 @@ jobs: path: docs/html lint: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Set up Python 3 uses: actions/setup-python@v2 From 3569f7e6877b9f2990a23c3da68ce561d3c38795 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:22:15 -0800 Subject: [PATCH 0773/2222] update ruby action --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 024a8fe6b..f3c133bab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,7 +188,7 @@ jobs: with: python-version: 3 - name: Set up Ruby 2.7 - uses: actions/setup-ruby@v1 + uses: ruby/setup-ruby@v1 with: ruby-version: 2.7 - name: Install Lua From 363a3d484eac74b34c87b9db45ab0cc901876994 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:23:45 -0800 Subject: [PATCH 0774/2222] fix matrix build? --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3c133bab..49333f110 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,8 @@ jobs: plugins: - default include: - - gcc: 12 + - os: ubuntu-22.04 + gcc: 12 plugins: all steps: - name: Set up Python 3 From 1bf79afd54fa815cd968231e477a2dc638b22cbe Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 15:29:11 -0800 Subject: [PATCH 0775/2222] ditch gcc 4.8 and 7, replace with 10 --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49333f110..abee9a86a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,8 +12,7 @@ jobs: os: - ubuntu-22.04 gcc: - - 4.8 - - 7 + - 10 plugins: - default include: From bc77a91d46bddc335ea9a63b34bd44e4efed56b3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 6 Mar 2023 20:51:41 -0800 Subject: [PATCH 0776/2222] build zero-item buildings with buildingplan so they can be multi-built like all other buildings (the vanilla ui was handling the build and then exiting out of the build menu) --- plugins/lua/buildingplan.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index a666103e5..0fcfd4c72 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -1713,9 +1713,6 @@ function PlannerOverlay:onInput(keys) if is_choosing_area() or cur_building_has_no_area() then local filters = get_cur_filters() local num_filters = #filters - if num_filters == 0 then - return false -- we don't add value; let the game place it - end local choose = self.subviews.choose if choose.enabled() and choose:getOptionValue() then self:save_placement() From 26a257eb8c0cb4c684483e7a443fd6a9cdaa42d5 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 7 Mar 2023 07:14:18 +0000 Subject: [PATCH 0777/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 8ae81f8d8..9ed162333 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8ae81f8d8f1f96d82b9074b205073bb8e8d29f96 +Subproject commit 9ed16233380857a145b276bfa62a89ea515c0514 From fc4d2605f205d55dd1ccaa0e5257cc79adca0e44 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 08:00:13 -0800 Subject: [PATCH 0778/2222] don't softlock the game on modal popups overlays can prevent clicks from being handled by DF. likewise, vanilla modal popups can prevent clicks from getting handled by DFHack. to prevent a softlock, overlay will skip sending input to the overlay widgets when a modal dialog is visible --- plugins/overlay.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/plugins/overlay.cpp b/plugins/overlay.cpp index 4c7384f3b..8e27c8308 100644 --- a/plugins/overlay.cpp +++ b/plugins/overlay.cpp @@ -13,6 +13,7 @@ #include "df/viewscreen_titlest.h" #include "df/viewscreen_update_regionst.h" #include "df/viewscreen_worldst.h" +#include "df/world.h" #include "Debug.h" #include "LuaTools.h" @@ -27,6 +28,8 @@ using namespace DFHack; DFHACK_PLUGIN("overlay"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); +REQUIRE_GLOBAL(world); + namespace DFHack { DBG_DECLARE(overlay, control, DebugCategory::LINFO); DBG_DECLARE(overlay, event, DebugCategory::LINFO); @@ -67,13 +70,15 @@ struct viewscreen_overlay : T { } DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set *input)) { bool input_is_handled = false; - call_overlay_lua(NULL, "feed_viewscreen_widgets", 2, 1, - [&](lua_State *L) { - Lua::Push(L, T::_identity.getName()); - Lua::PushInterfaceKeys(L, *input); - }, [&](lua_State *L) { - input_is_handled = lua_toboolean(L, -1); - }); + // don't send input to the overlays if there is a modal dialog up + if (!world->status.popups.size()) + call_overlay_lua(NULL, "feed_viewscreen_widgets", 2, 1, + [&](lua_State *L) { + Lua::Push(L, T::_identity.getName()); + Lua::PushInterfaceKeys(L, *input); + }, [&](lua_State *L) { + input_is_handled = lua_toboolean(L, -1); + }); if (!input_is_handled) INTERPOSE_NEXT(feed)(input); } From fccd4cdec59232957ec222afaf672c8a68fe8d35 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 17:13:41 -0800 Subject: [PATCH 0779/2222] add steam deploy workflow --- .github/workflows/steam.yml | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/steam.yml diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml new file mode 100644 index 000000000..683b97fe7 --- /dev/null +++ b/.github/workflows/steam.yml @@ -0,0 +1,57 @@ +name: Deploy to Steam + +on: + workflow_dispatch: + inputs: + commit_hash: + description: Commit hash + type: string + required: true + version: + description: Version + type: string + required: true + release_channel: + description: Release channel + type: string + required: true + default: beta + +jobs: + deploy-to-steam: + name: Deploy to Steam + runs-on: ubuntu-22.04 + steps: + - name: Clone DFHack + uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + ref: ${{ github.event.inputs.commit_hash }} + - name: Fetch ccache + uses: actions/cache@v3 + with: + path: build/win64-cross/ccache + key: ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} + restore-keys: | + ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} + ccache-win64-cross-msvc + - name: Cross-compile win64 artifacts + env: + CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1' + run: | + cd build + bash -x build-win64-from-linux.sh + - name: Steam deploy + uses: game-ci/steam-deploy@v2 + with: + username: ${{ secrets.STEAM_USERNAME }} + password: ${{ secrets.STEAM_PASSWORD }} + configVdf: ${{ secrets.STEAM_CONFIG_VDF}} + ssfnFileName: ${{ secrets.STEAM_SSFN_FILE_NAME }} + ssfnFileContents: ${{ secrets.STEAM_SSFN_FILE_CONTENTS }} + appId: 2346660 + buildDescription: ${{ github.event.inputs.version }} + rootPath: build/win64-cross/output + depot1Path: dfhack-windows-x64 + releaseBranch: ${{ github.event.inputs.release_channel }} From 980f6734540b211c21086010d88af4d66efc85b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 17:34:44 -0800 Subject: [PATCH 0780/2222] fix depot file path --- .github/workflows/steam.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 683b97fe7..035caf805 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -52,6 +52,6 @@ jobs: ssfnFileContents: ${{ secrets.STEAM_SSFN_FILE_CONTENTS }} appId: 2346660 buildDescription: ${{ github.event.inputs.version }} - rootPath: build/win64-cross/output - depot1Path: dfhack-windows-x64 + rootPath: build + depot1Path: win64-cross/output releaseBranch: ${{ github.event.inputs.release_channel }} From f974ac043f994e870741af49d07e0f18c7a3cfd6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 22:13:08 -0800 Subject: [PATCH 0781/2222] add launchdf binary so steam has an exe to launch --- CMakeLists.txt | 2 ++ package/windows/CMakeLists.txt | 7 ++++++ package/windows/launchdf.c | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 package/windows/CMakeLists.txt create mode 100644 package/windows/launchdf.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 56c81ba72..1fb43fa07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -615,3 +615,5 @@ if(BUILD_SIZECHECK) add_subdirectory(depends/sizecheck) add_dependencies(dfhack sizecheck) endif() + +add_subdirectory(package/windows) diff --git a/package/windows/CMakeLists.txt b/package/windows/CMakeLists.txt new file mode 100644 index 000000000..a5877f117 --- /dev/null +++ b/package/windows/CMakeLists.txt @@ -0,0 +1,7 @@ +project(package_windows) + +if(WIN32) + add_executable(launchdf WIN32 launchdf.c) + install(TARGETS launchdf + DESTINATION ${DFHACK_DATA_DESTINATION}) +endif() diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c new file mode 100644 index 000000000..44a2d4d23 --- /dev/null +++ b/package/windows/launchdf.c @@ -0,0 +1,42 @@ +#include + +int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) +{ + STARTUPINFOA si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + CHAR dfdir[1024]; + + if (GetFullPathNameA("..", 1024, dfdir, NULL) == 0) + { + MessageBoxA(NULL, "could not get current directory", NULL, 0); + exit(1); + } + + if (SetCurrentDirectoryA(dfdir) == 0) + { + MessageBoxA(NULL, "could not change to DF directory", NULL, 0); + exit(1); + } + + if (CreateProcessA("Dwarf Fortress.exe", + NULL, + NULL, + NULL, + FALSE, + 0, + NULL, + NULL, + &si, + &pi) == 0) + { + MessageBoxA(NULL, "could not launch 'Dwarf Fortress.exe'", NULL, 0); + exit(1); + } + + exit(0); +} From 787844e8e6de472b63947292d6cc250356e18572 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 7 Mar 2023 22:27:50 -0800 Subject: [PATCH 0782/2222] fix ccache restore key --- .github/workflows/steam.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 035caf805..aae1621af 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -34,7 +34,7 @@ jobs: path: build/win64-cross/ccache key: ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} restore-keys: | - ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} + ccache-win64-cross-msvc-develop-${{ github.event.inputs.commit_hash }} ccache-win64-cross-msvc - name: Cross-compile win64 artifacts env: From fcfe7e4ef86b3381856224e29872c239e532c2d3 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 8 Mar 2023 07:15:19 +0000 Subject: [PATCH 0783/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 9ed162333..09ddae3c2 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9ed16233380857a145b276bfa62a89ea515c0514 +Subproject commit 09ddae3c2851f207c7257b4767182f4c6c938f18 diff --git a/scripts b/scripts index 288b38c9e..c8ceb198b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 288b38c9e9d8fabf1a0934ffbe23104274c39883 +Subproject commit c8ceb198b4fc62155b31a5554b9c96cc6bcb8d55 From eee911b807a6b1463e2e92412af1e3de3d2d8838 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 8 Mar 2023 00:00:22 -0800 Subject: [PATCH 0784/2222] remove changedir, cwd is already root --- package/windows/launchdf.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c index 44a2d4d23..9f4b01fcb 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.c @@ -9,20 +9,6 @@ int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - CHAR dfdir[1024]; - - if (GetFullPathNameA("..", 1024, dfdir, NULL) == 0) - { - MessageBoxA(NULL, "could not get current directory", NULL, 0); - exit(1); - } - - if (SetCurrentDirectoryA(dfdir) == 0) - { - MessageBoxA(NULL, "could not change to DF directory", NULL, 0); - exit(1); - } - if (CreateProcessA("Dwarf Fortress.exe", NULL, NULL, From 18160da82e7c7e723affef6b949e0da0c97bcd81 Mon Sep 17 00:00:00 2001 From: 20k Date: Mon, 6 Mar 2023 18:10:43 +0000 Subject: [PATCH 0785/2222] rework to be allocation free, cleanup --- library/LuaApi.cpp | 102 ++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 873b0bcd2..5e5fa7ff3 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2835,76 +2835,91 @@ static std::string internal_md5(std::string s) { return md5_wrap.getHashFromStri struct heap_pointer_info { + size_t address = 0; size_t size = 0; int status = 0; }; -static std::map snapshot; +//fixed sized, sorted +static std::vector heap_data; +//when dfhack upgrades to c++17, this would do well as a std::optional +static std::pair heap_find(uintptr_t address) +{ + auto it = std::lower_bound(heap_data.begin(), heap_data.end(), address, + [](heap_pointer_info t, uintptr_t address) + { + return t.address < address; + }); + + if (it == heap_data.end() || it->address != address) + return {false, heap_pointer_info()}; + + return {true, *it}; +} + +//this function only allocates the first time it is called static int heap_take_snapshot() { #ifdef _WIN32 - snapshot.clear(); + size_t max_entries = 256 * 1024 * 1024 / sizeof(heap_pointer_info); - std::vector> entries; - //heap allocating while iterating the heap is suboptimal - entries.reserve(256*1024*1024); + //clearing the vector is guaranteed not to deallocate the memory + heap_data.clear(); + heap_data.reserve(max_entries); _HEAPINFO hinfo; - int heapstatus; - int numLoops; - hinfo._pentry = NULL; - numLoops = 0; - while((heapstatus = _heapwalk(&hinfo)) == _HEAPOK && - numLoops < 1024*1024*1024) + hinfo._pentry = nullptr; + int heap_status = 0; + + while ((heap_status = _heapwalk(&hinfo)) == _HEAPOK && heap_data.size() < max_entries) { heap_pointer_info inf; + inf.address = reinterpret_cast(hinfo._pentry); inf.size = hinfo._size; inf.status = hinfo._useflag; //0 == _FREEENTRY, 1 == _USEDENTRY - entries.push_back({hinfo._pentry, inf}); - - numLoops++; + heap_data.push_back(inf); } - for (auto i : entries) + //sort by address + std::sort(heap_data.begin(), heap_data.end(), + [](heap_pointer_info t1, heap_pointer_info t2) { - uintptr_t val = reinterpret_cast(i.first); - snapshot[val] = i.second; - } + return t1.address < t2.address; + }); - if (heapstatus == _HEAPEMPTY || heapstatus == _HEAPEND) + if (heap_status == _HEAPEMPTY || heap_status == _HEAPEND) return 0; - if (heapstatus == _HEAPBADPTR) + if (heap_status == _HEAPBADPTR) return 1; - if (heapstatus == _HEAPBADBEGIN) + if (heap_status == _HEAPBADBEGIN) return 2; - if (heapstatus == _HEAPBADNODE) + if (heap_status == _HEAPBADNODE) return 3; #endif return 0; } -//this function probably should not allocate. Then again we're shimming through lua which.... probably does static int get_heap_state() { #ifdef _WIN32 - int heapstatus = _heapchk(); + int heap_status = _heapchk(); - if (heapstatus == _HEAPEMPTY || heapstatus == _HEAPOK) + if (heap_status == _HEAPEMPTY || heap_status == _HEAPOK) return 0; - if (heapstatus == _HEAPBADPTR) + if (heap_status == _HEAPBADPTR) return 1; - if (heapstatus == _HEAPBADBEGIN) + if (heap_status == _HEAPBADBEGIN) return 2; - if (heapstatus == _HEAPBADNODE) + if (heap_status == _HEAPBADNODE) return 3; #endif @@ -2913,56 +2928,59 @@ static int get_heap_state() static bool is_address_in_heap(uintptr_t ptr) { - return snapshot.find(ptr) != snapshot.end(); + return heap_find(ptr).first; } static bool is_address_active_in_heap(uintptr_t ptr) { - auto it = snapshot.find(ptr); + std::pair inf = heap_find(ptr); - if (it == snapshot.end()) + if (!inf.first) return false; - return it->second.status == 1; + return inf.second.status == 1; } static bool is_address_used_after_free_in_heap(uintptr_t ptr) { - auto it = snapshot.find(ptr); + std::pair inf = heap_find(ptr); - if (it == snapshot.end()) + if (!inf.first) return false; - return it->second.status != 1; + return inf.second.status != 1; } static int get_address_size_in_heap(uintptr_t ptr) { - auto it = snapshot.find(ptr); + std::pair inf = heap_find(ptr); - if (it == snapshot.end()) + if (!inf.first) return -1; - return it->second.size; + return inf.second.size; } //eg if I have a struct, does any address lie within the struct? static uintptr_t get_root_address_of_heap_object(uintptr_t ptr) { //find the first element strictly greater than our pointer - auto it = snapshot.upper_bound(ptr); + auto it = std::upper_bound(heap_data.begin(), heap_data.end(), ptr, [](uintptr_t ptr, heap_pointer_info t1) + { + return ptr < t1.address; + }); //if we're at the start of the snapshot, no elements are less than our pointer //therefore it is invalid - if (it == snapshot.begin()) + if (it == heap_data.begin()) return 0; //get the first element less than or equal to ours it--; //our pointer is only valid if we lie in the first pointer lower in memory than it - if (ptr >= it->first && ptr < it->first + it->second.size) - return it->first; + if (ptr >= it->address && ptr < it->address + it->size) + return it->address; return 0; } From 6373832490843d7cf8bb2fa2a19690f182018d5e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 9 Mar 2023 21:18:45 -0800 Subject: [PATCH 0786/2222] refactor buildingplan into smaller files --- plugins/buildingplan/buildingplan.cpp | 10 +- plugins/lua/buildingplan.lua | 1986 +---------------- plugins/lua/buildingplan/filterselection.lua | 731 ++++++ plugins/lua/buildingplan/inspectoroverlay.lua | 148 ++ plugins/lua/buildingplan/itemselection.lua | 347 +++ plugins/lua/buildingplan/pens.lua | 31 + plugins/lua/buildingplan/planneroverlay.lua | 734 ++++++ 7 files changed, 2020 insertions(+), 1967 deletions(-) create mode 100644 plugins/lua/buildingplan/filterselection.lua create mode 100644 plugins/lua/buildingplan/inspectoroverlay.lua create mode 100644 plugins/lua/buildingplan/itemselection.lua create mode 100644 plugins/lua/buildingplan/pens.lua create mode 100644 plugins/lua/buildingplan/planneroverlay.lua diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index ec85d7952..310d03a5e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -259,7 +259,7 @@ static void validate_config(color_ostream &out, bool verbose = false) { static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "signal_reset"); - call_buildingplan_lua(&out, "reload_cursors"); + call_buildingplan_lua(&out, "reload_pens"); planned_buildings.clear(); tasks.clear(); cur_heat_safety.clear(); @@ -318,14 +318,6 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { return CR_OK; } -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { - if (event == SC_WORLD_UNLOADED) { - DEBUG(status,out).print("world unloaded; clearing state for %s\n", plugin_name); - clear_state(out); - } - return CR_OK; -} - static bool cycle_requested = false; static void do_cycle(color_ostream &out) { diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 0fcfd4c72..d606101be 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -2,7 +2,7 @@ local _ENV = mkmodule('plugins.buildingplan') --[[ - Native functions: + Public native functions: * bool isPlannableBuilding(df::building_type type, int16_t subtype, int32_t custom) * bool isPlannedBuilding(df::building *bld) @@ -13,15 +13,11 @@ local _ENV = mkmodule('plugins.buildingplan') --]] local argparse = require('argparse') -local gui = require('gui') -local guidm = require('gui.dwarfmode') -local overlay = require('plugins.overlay') -local utils = require('utils') -local widgets = require('gui.widgets') +local inspector = require('plugins.buildingplan.inspectoroverlay') +local pens = require('plugins.buildingplan.pens') +local planner = require('plugins.buildingplan.planneroverlay') require('dfhack.buildings') -local uibs = df.global.buildreq - local function process_args(opts, args) if args[1] == 'help' then opts.help = true @@ -44,7 +40,7 @@ function parse_commandline(...) local command = table.remove(positionals, 1) if not command or command == 'status' then printStatus() - elseif command == 'set' then + elseif command == 'set' and positionals then setSetting(positionals[1], positionals[2] == 'true') else return false @@ -66,36 +62,6 @@ function get_job_item(btype, subtype, custom, index) return obj end -local function get_cur_filters() - return dfhack.buildings.getFiltersByType({}, uibs.building_type, - uibs.building_subtype, uibs.custom_type) -end - -local function is_choosing_area() - return uibs.selection_pos.x >= 0 -end - -local function get_cur_area_dims(placement_data) - if not placement_data and not is_choosing_area() then return 1, 1, 1 end - local selection_pos = placement_data and placement_data.p1 or uibs.selection_pos - local pos = placement_data and placement_data.p2 or uibs.pos - return math.abs(selection_pos.x - pos.x) + 1, - math.abs(selection_pos.y - pos.y) + 1, - math.abs(selection_pos.z - pos.z) + 1 -end - -local function get_quantity(filter, hollow, placement_data) - local quantity = filter.quantity or 1 - local dimx, dimy, dimz = get_cur_area_dims(placement_data) - if quantity < 1 then - return (((dimx * dimy) // 4) + 1) * dimz - end - if hollow and dimx > 2 and dimy > 2 then - return quantity * (2*dimx + 2*dimy - 4) * dimz - end - return quantity * dimx * dimy * dimz -end - local function to_title_case(str) str = str:gsub('(%a)([%w_]*)', function (first, rest) return first:upper()..rest:lower() end) @@ -114,12 +80,12 @@ function get_desc(filter) elseif filter.vector_id and filter.vector_id > -1 then desc = to_title_case(df.job_item_vector_id[filter.vector_id]) elseif filter.flags2 and filter.flags2.building_material then - desc = 'Building material'; - if filter.flags2.fire_safe then - desc = 'Fire-safe material'; - end if filter.flags2.magma_safe then - desc = 'Magma-safe material'; + desc = 'Magma-safe material' + elseif filter.flags2.fire_safe then + desc = 'Fire-safe material' + else + desc = 'Building material' end end @@ -131,1930 +97,34 @@ function get_desc(filter) elseif desc == 'Wood' then desc = 'Log' end - return desc -end - -local BUTTON_START_PEN, BUTTON_END_PEN, SELECTED_ITEM_PEN = nil, nil, nil -local reset_counts_flag = false -local reset_inspector_flag = false -function signal_reset() - BUTTON_START_PEN = nil - BUTTON_END_PEN = nil - SELECTED_ITEM_PEN = nil - reset_counts_flag = true - reset_inspector_flag = true -end - -local to_pen = dfhack.pen.parse -local function get_button_start_pen() - if not BUTTON_START_PEN then - local texpos_base = dfhack.textures.getControlPanelTexposStart() - BUTTON_START_PEN = to_pen{ch='[', fg=COLOR_YELLOW, - tile=texpos_base > 0 and texpos_base + 13 or nil} - end - return BUTTON_START_PEN -end -local function get_button_end_pen() - if not BUTTON_END_PEN then - local texpos_base = dfhack.textures.getControlPanelTexposStart() - BUTTON_END_PEN = to_pen{ch=']', fg=COLOR_YELLOW, - tile=texpos_base > 0 and texpos_base + 15 or nil} - end - return BUTTON_END_PEN -end -local function get_selected_item_pen() - if not SELECTED_ITEM_PEN then - local texpos_base = dfhack.textures.getControlPanelTexposStart() - SELECTED_ITEM_PEN = to_pen{ch='x', fg=COLOR_GREEN, - tile=texpos_base > 0 and texpos_base + 9 or nil} - end - return SELECTED_ITEM_PEN -end - -BuildingplanScreen = defclass(BuildingplanScreen, gui.ZScreen) -BuildingplanScreen.ATTRS { - pass_movement_keys=true, - pass_mouse_clicks=false, - defocusable=false, -} - --------------------------------- --- ItemSelection --- - -local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} -local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} - --- map of building type -> {set=set of recently used, list=list of recently used} --- most recent entries are at the *end* of the list -local recently_used = {} - -local function sort_by_type(a, b) - local ad, bd = a.data, b.data - return ad.item_type < bd.item_type or - (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or - (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or - (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) -end - -local function sort_by_recency(a, b) - local tracker = recently_used[uibs.building_type] - if not tracker then return sort_by_type(a, b) end - local recent_a, recent_b = tracker.set[a.search_key], tracker.set[b.search_key] - -- if they're both in the set, return the one with the greater index, - -- indicating more recent - if recent_a and recent_b then return recent_a > recent_b end - if recent_a and not recent_b then return true end - if not recent_a and recent_b then return false end - return sort_by_type(a, b) -end - -local function sort_by_name(a, b) - return a.search_key < b.search_key or - (a.search_key == b.search_key and sort_by_type(a, b)) -end - -local function sort_by_quantity(a, b) - local ad, bd = a.data, b.data - return ad.quantity > bd.quantity or - (ad.quantity == bd.quantity and sort_by_type(a, b)) -end - -ItemSelection = defclass(ItemSelection, widgets.Window) -ItemSelection.ATTRS{ - frame_title='Choose items', - frame={w=56, h=20, l=4, t=8}, - resizable=true, - index=DEFAULT_NIL, - quantity=DEFAULT_NIL, - on_submit=DEFAULT_NIL, - on_cancel=DEFAULT_NIL, -} - -function ItemSelection:init() - local filter = get_cur_filters()[self.index] - self.num_selected = 0 - self.selected_set = {} - local plural = self.quantity == 1 and '' or 's' - - self:addviews{ - widgets.Label{ - frame={t=0, l=0, r=10}, - text={ - get_desc(filter), - plural, - NEWLINE, - ('Select up to %d item%s ('):format(self.quantity, plural), - {text=function() return self.num_selected end}, - ' selected)', - }, - }, - widgets.Label{ - frame={r=0, w=9, t=0, h=3}, - text_pen=BUILD_TEXT_PEN, - text_hpen=BUILD_TEXT_HPEN, - text={ - ' ', NEWLINE, - ' Build ', NEWLINE, - ' ', - }, - on_click=self:callback('submit'), - }, - widgets.FilteredList{ - view_id='flist', - frame={t=3, l=0, r=0, b=4}, - case_sensitive=false, - choices=self:get_choices(sort_by_recency), - icon_width=2, - on_submit=self:callback('toggle_group'), - edit_on_char=function(ch) return ch:match('[%l -]') end, - }, - widgets.CycleHotkeyLabel{ - frame={l=0, b=2}, - key='CUSTOM_SHIFT_R', - label='Sort by:', - options={ - {label='Recently used', value=sort_by_recency}, - {label='Name', value=sort_by_name}, - {label='Amount', value=sort_by_quantity}, - }, - on_change=self:callback('on_sort'), - }, - widgets.HotkeyLabel{ - frame={l=0, b=1}, - key='SELECT', - label='Use all/none', - auto_width=true, - on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.HotkeyLabel{ - frame={l=22, b=1}, - key='CUSTOM_SHIFT_B', - label='Build', - auto_width=true, - on_activate=self:callback('submit'), - }, - widgets.HotkeyLabel{ - frame={l=38, b=1}, - key='LEAVESCREEN', - label='Go back', - auto_width=true, - on_activate=self:callback('on_cancel'), - }, - widgets.HotkeyLabel{ - frame={l=0, b=0}, - key='KEYBOARD_CURSOR_RIGHT_FAST', - key_sep=' : ', - label='Use one', - auto_width=true, - on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=6, b=0, w=5}, - text_pen=COLOR_LIGHTGREEN, - text='Right', - }, - widgets.HotkeyLabel{ - frame={l=23, b=0}, - key='KEYBOARD_CURSOR_LEFT_FAST', - key_sep=' : ', - label='Use one fewer', - auto_width=true, - on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=29, b=0, w=4}, - text_pen=COLOR_LIGHTGREEN, - text='Left', - }, - } -end - --- resort and restore selection -function ItemSelection:on_sort(sort_fn) - local flist = self.subviews.flist - local saved_filter = flist:getFilter() - flist:setFilter('') - flist:setChoices(self:get_choices(sort_fn), flist:getSelected()) - flist:setFilter(saved_filter) -end - -local function make_search_key(str) - local out = '' - for c in str:gmatch("[%w%s]") do - out = out .. c - end - return out -end - -function ItemSelection:get_choices(sort_fn) - local item_ids = getAvailableItems(uibs.building_type, - uibs.building_subtype, uibs.custom_type, self.index-1) - local buckets = {} - for _,item_id in ipairs(item_ids) do - local item = df.item.find(item_id) - if not item then goto continue end - local desc = dfhack.items.getDescription(item, 0, true) - if buckets[desc] then - local bucket = buckets[desc] - table.insert(bucket.data.item_ids, item_id) - bucket.data.quantity = bucket.data.quantity + 1 - else - local entry = { - search_key=make_search_key(desc), - icon=self:callback('get_entry_icon', item_id), - data={ - item_ids={item_id}, - item_type=item:getType(), - item_subtype=item:getSubtype(), - quantity=1, - quality=item:getQuality(), - selected=0, - }, - } - buckets[desc] = entry - end - ::continue:: - end - local choices = {} - for desc,choice in pairs(buckets) do - local data = choice.data - choice.text = { - {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, - {gap=2, text=desc}, - } - table.insert(choices, choice) - end - table.sort(choices, sort_fn) - return choices -end - -function ItemSelection:increment_group(idx, choice) - local data = choice.data - if self.quantity <= self.num_selected then return false end - if data.selected >= data.quantity then return false end - data.selected = data.selected + 1 - self.num_selected = self.num_selected + 1 - local item_id = data.item_ids[data.selected] - self.selected_set[item_id] = true - return true -end - -function ItemSelection:decrement_group(idx, choice) - local data = choice.data - if data.selected <= 0 then return false end - local item_id = data.item_ids[data.selected] - self.selected_set[item_id] = nil - self.num_selected = self.num_selected - 1 - data.selected = data.selected - 1 - return true -end - -function ItemSelection:toggle_group(idx, choice) - local data = choice.data - if data.selected > 0 then - while self:decrement_group(idx, choice) do end - else - while self:increment_group(idx, choice) do end - end -end - -function ItemSelection:get_entry_icon(item_id) - return self.selected_set[item_id] and get_selected_item_pen() or nil -end - -local function track_recently_used(choices) - -- use same set for all subtypes - local tracker = ensure_key(recently_used, uibs.building_type) - for _,choice in ipairs(choices) do - local data = choice.data - if data.selected <= 0 then goto continue end - local key = choice.search_key - local recent_set = ensure_key(tracker, 'set') - local recent_list = ensure_key(tracker, 'list') - if recent_set[key] then - if recent_list[#recent_list] ~= key then - for i,v in ipairs(recent_list) do - if v == key then - table.remove(recent_list, i) - table.insert(recent_list, key) - break - end - end - tracker.set = utils.invert(recent_list) - end - else - -- only keep most recent 10 - if #recent_list >= 10 then - -- remove least recently used from list and set - recent_set[table.remove(recent_list, 1)] = nil - end - table.insert(recent_list, key) - recent_set[key] = #recent_list - end - ::continue:: - end -end - -function ItemSelection:submit() - local selected_items = {} - for item_id in pairs(self.selected_set) do - table.insert(selected_items, item_id) - end - if #selected_items > 0 then - track_recently_used(self.subviews.flist:getChoices()) - end - self.on_submit(selected_items) -end - -function ItemSelection:onInput(keys) - if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then - self.on_cancel() - return true - elseif keys._MOUSE_L_DOWN then - local list = self.subviews.flist.list - local idx = list:getIdxUnderMouse() - if idx then - list:setSelected(idx) - local modstate = dfhack.internal.getModstate() - if modstate & 2 > 0 then -- ctrl - local choice = list:getChoices()[idx] - if modstate & 1 > 0 then -- shift - self:decrement_group(idx, choice) - else - self:increment_group(idx, choice) - end - return true - end - end - end - return ItemSelection.super.onInput(self, keys) -end - -ItemSelectionScreen = defclass(ItemSelectionScreen, BuildingplanScreen) -ItemSelectionScreen.ATTRS { - focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/itemselection', - force_pause=true, - pass_pause=false, - index=DEFAULT_NIL, - quantity=DEFAULT_NIL, - on_submit=DEFAULT_NIL, - on_cancel=DEFAULT_NIL, -} - -function ItemSelectionScreen:init() - self:addviews{ - ItemSelection{ - index=self.index, - quantity=self.quantity, - on_submit=self.on_submit, - on_cancel=self.on_cancel, - } - } -end - --------------------------------- --- Slider --- - -Slider = defclass(Slider, widgets.Widget) -Slider.ATTRS{ - num_stops=DEFAULT_NIL, - get_left_idx_fn=DEFAULT_NIL, - get_right_idx_fn=DEFAULT_NIL, - on_left_change=DEFAULT_NIL, - on_right_change=DEFAULT_NIL, -} - -function Slider:preinit(init_table) - init_table.frame = init_table.frame or {} - init_table.frame.h = init_table.frame.h or 1 -end - -function Slider:init() - if self.num_stops < 2 then error('too few Slider stops') end - self.is_dragging_target = nil -- 'left', 'right', or 'both' - self.is_dragging_idx = nil -- offset from leftmost dragged tile -end - -local function slider_get_width_per_idx(self) - return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) -end - -function Slider:onInput(keys) - if not keys._MOUSE_L_DOWN then return false end - local x = self:getMousePos() - if not x then return false end - local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) - local left_pos = width_per_idx*(left_idx-1) - local right_pos = width_per_idx*(right_idx-1) + 4 - if x < left_pos then - self.on_left_change(self.get_left_idx_fn() - 1) - elseif x < left_pos+3 then - self.is_dragging_target = 'left' - self.is_dragging_idx = x - left_pos - elseif x < right_pos then - self.is_dragging_target = 'both' - self.is_dragging_idx = x - left_pos - elseif x < right_pos+3 then - self.is_dragging_target = 'right' - self.is_dragging_idx = x - right_pos - else - self.on_right_change(self.get_right_idx_fn() + 1) - end - return true -end - -local function slider_do_drag(self, width_per_idx) - local x = self.frame_body:localXY(dfhack.screen.getMousePos()) - local cur_pos = x - self.is_dragging_idx - cur_pos = math.max(0, cur_pos) - cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) - local offset = self.is_dragging_target == 'right' and -2 or 1 - local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 - local new_left_idx, new_right_idx - if self.is_dragging_target == 'right' then - new_right_idx = new_idx - else - new_left_idx = new_idx - if self.is_dragging_target == 'both' then - new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() - if new_right_idx > self.num_stops then - return - end - end - end - if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then - self.on_left_change(new_left_idx) - end - if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then - self.on_right_change(new_right_idx) - end -end - -local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} -local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} -local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} -local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} -local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} - -function Slider:onRenderBody(dc, rect) - local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) - -- draw track - dc:seek(1,0) - dc:char(nil, SLIDER_LEFT_END) - dc:char(nil, SLIDER_TRACK) - for stop_idx=1,self.num_stops-1 do - local track_stop_pen = SLIDER_TRACK_STOP_SELECTED - local track_pen = SLIDER_TRACK_SELECTED - if left_idx > stop_idx or right_idx < stop_idx then - track_stop_pen = SLIDER_TRACK_STOP - track_pen = SLIDER_TRACK - elseif right_idx == stop_idx then - track_pen = SLIDER_TRACK - end - dc:char(nil, track_stop_pen) - for i=2,width_per_idx do - dc:char(nil, track_pen) - end - end - if right_idx >= self.num_stops then - dc:char(nil, SLIDER_TRACK_STOP_SELECTED) - else - dc:char(nil, SLIDER_TRACK_STOP) - end - dc:char(nil, SLIDER_TRACK) - dc:char(nil, SLIDER_RIGHT_END) - -- draw tabs - dc:seek(width_per_idx*(left_idx-1)) - dc:char(nil, SLIDER_TAB_LEFT) - dc:char(nil, SLIDER_TAB_CENTER) - dc:char(nil, SLIDER_TAB_RIGHT) - dc:seek(width_per_idx*(right_idx-1)+4) - dc:char(nil, SLIDER_TAB_LEFT) - dc:char(nil, SLIDER_TAB_CENTER) - dc:char(nil, SLIDER_TAB_RIGHT) - -- manage dragging - if self.is_dragging_target then - slider_do_drag(self, width_per_idx) - end - if df.global.enabler.mouse_lbut == 0 then - self.is_dragging_target = nil - self.is_dragging_idx = nil - end -end - --------------------------------- --- QualityAndMaterialsPage --- - -QualityAndMaterialsPage = defclass(QualityAndMaterialsPage, widgets.Panel) -QualityAndMaterialsPage.ATTRS{ - frame={t=0, l=0}, - index=DEFAULT_NIL, -} - -local TYPE_COL_WIDTH = 20 -local HEADER_HEIGHT = 7 -local QUALITY_HEIGHT = 9 -local FOOTER_HEIGHT = 4 - --- returns whether the items matched by the specified filter can have a quality --- rating. This also conveniently indicates whether an item can be decorated. -local function can_be_improved(idx) - local filter = get_cur_filters()[idx] - if filter.flags2 and filter.flags2.building_material then - return false; - end - return filter.item_type ~= df.item_type.WOOD and - filter.item_type ~= df.item_type.BLOCKS and - filter.item_type ~= df.item_type.BAR and - filter.item_type ~= df.item_type.BOULDER -end - -local function mat_sort_by_name(a, b) - return a.name < b.name -end - -local function mat_sort_by_quantity(a, b) - return a.quantity > b.quantity or - (a.quantity == b.quantity and mat_sort_by_name(a, b)) -end - -function QualityAndMaterialsPage:init() - self.dirty = true - self.summary = '' - - local enable_item_quality = can_be_improved(self.index) - - self:addviews{ - widgets.Panel{ - view_id='header', - frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, - frame_inset={l=1}, - subviews={ - widgets.Label{ - frame={l=0, t=0}, - text='Current filter:', - }, - widgets.WrappedLabel{ - frame={l=16, t=0, h=2, r=0}, - text_pen=COLOR_LIGHTCYAN, - text_to_wrap=function() return self.summary end, - auto_height=false, - }, - widgets.CycleHotkeyLabel{ - view_id='mat_sort', - frame={l=0, t=3, w=21}, - label='Sort by:', - key='CUSTOM_SHIFT_R', - options={ - {label='name', value=mat_sort_by_name}, - {label='available', value=mat_sort_by_quantity} - }, - on_change=function() self.dirty = true end, - }, - widgets.ToggleHotkeyLabel{ - view_id='hide_zero', - frame={l=0, t=4, w=24}, - label='Hide unavailable:', - key='CUSTOM_SHIFT_H', - initial_option=false, - on_change=function() self.dirty = true end, - }, - widgets.EditField{ - view_id='search', - frame={l=26, t=3}, - label_text='Search: ', - on_char=function(ch) return ch:match('[%l -]') end, - }, - widgets.Label{ - frame={l=1, b=0}, - text='Type', - text_pen=COLOR_LIGHTRED, - }, - widgets.Label{ - frame={l=TYPE_COL_WIDTH, b=0}, - text='Material', - text_pen=COLOR_LIGHTRED, - }, - }, - }, - widgets.Panel{ - view_id='materials_lists', - frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, - frame_style=gui.INTERIOR_FRAME, - subviews={ - widgets.List{ - view_id='materials_categories', - frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, - scroll_keys={}, - icon_width=2, - cursor_pen=COLOR_CYAN, - on_submit=self:callback('toggle_category'), - }, - widgets.FilteredList{ - view_id='materials_mats', - frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, - icon_width=2, - on_submit=self:callback('toggle_material'), - }, - }, - }, - widgets.Panel{ - view_id='divider', - frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=FOOTER_HEIGHT+QUALITY_HEIGHT, w=1}, - on_render=self:callback('draw_divider'), - }, - widgets.Panel{ - view_id='quality_panel', - frame={l=0, r=0, h=QUALITY_HEIGHT, b=FOOTER_HEIGHT}, - frame_style=gui.INTERIOR_FRAME, - frame_title='Item quality', - subviews={ - widgets.CycleHotkeyLabel{ - view_id='decorated', - frame={l=0, t=1, w=23}, - key='CUSTOM_SHIFT_D', - label='Decorated only:', - options={ - {label='No', value=false}, - {label='Yes', value=true}, - }, - enabled=enable_item_quality, - on_change=self:callback('set_decorated'), - }, - widgets.CycleHotkeyLabel{ - view_id='min_quality', - frame={l=0, t=3, w=18}, - label='Min quality:', - label_below=true, - key_back='CUSTOM_SHIFT_Z', - key='CUSTOM_SHIFT_X', - options={ - {label='Ordinary', value=0}, - {label='Well Crafted', value=1}, - {label='Finely Crafted', value=2}, - {label='Superior', value=3}, - {label='Exceptional', value=4}, - {label='Masterful', value=5}, - {label='Artifact', value=6}, - }, - enabled=enable_item_quality, - on_change=function(val) self:set_min_quality(val+1) end, - }, - widgets.CycleHotkeyLabel{ - view_id='max_quality', - frame={r=1, t=3, w=18}, - label='Max quality:', - label_below=true, - key_back='CUSTOM_SHIFT_Q', - key='CUSTOM_SHIFT_W', - options={ - {label='Ordinary', value=0}, - {label='Well Crafted', value=1}, - {label='Finely Crafted', value=2}, - {label='Superior', value=3}, - {label='Exceptional', value=4}, - {label='Masterful', value=5}, - {label='Artifact', value=6}, - }, - enabled=enable_item_quality, - on_change=function(val) self:set_max_quality(val+1) end, - }, - Slider{ - frame={l=0, t=6}, - num_stops=7, - get_left_idx_fn=function() - return self.subviews.min_quality:getOptionValue() + 1 - end, - get_right_idx_fn=function() - return self.subviews.max_quality:getOptionValue() + 1 - end, - on_left_change=self:callback('set_min_quality'), - on_right_change=self:callback('set_max_quality'), - active=enable_item_quality, - }, - }, - }, - widgets.Panel{ - view_id='footer', - frame={l=0, r=0, b=0, h=FOOTER_HEIGHT}, - frame_inset={t=1, l=1}, - subviews={ - widgets.HotkeyLabel{ - frame={l=0, t=0}, - label='Toggle', - auto_width=true, - key='SELECT', - }, - widgets.HotkeyLabel{ - frame={l=0, t=2}, - label='Done', - auto_width=true, - key='LEAVESCREEN', - }, - widgets.HotkeyLabel{ - frame={l=30, t=0}, - label='Invert selection', - auto_width=true, - key='CUSTOM_SHIFT_I', - on_activate=self:callback('invert_materials'), - }, - widgets.HotkeyLabel{ - frame={l=30, t=2}, - label='Reset filter', - auto_width=true, - key='CUSTOM_SHIFT_X', - on_activate=self:callback('clear_filter'), - }, - }, - } - } - - -- replace the FilteredList's built-in EditField with our own - self.subviews.materials_mats.list.frame.t = 0 - self.subviews.materials_mats.edit.visible = false - self.subviews.materials_mats.edit = self.subviews.search - self.subviews.search.on_change = self.subviews.materials_mats:callback('onFilterChange') -end - -local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} -local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} - -local function make_cat_choice(label, cat, key, cats) - local enabled = cats[cat] - local icon = nil - if not cats.unset then - icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN - end - return { - text=label, - key=key, - enabled=enabled, - cat=cat, - icon=icon, - } -end - -local function make_mat_choice(name, props, enabled, cats) - local quantity = tonumber(props.count) - local text = ('%5d - %s'):format(quantity, name) - local icon = nil - if not cats.unset then - icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN - end - return { - text=text, - enabled=enabled, - icon=icon, - name=name, - cat=props.category, - quantity=quantity, - } -end - -function QualityAndMaterialsPage:refresh() - local summary = get_desc(get_cur_filters()[self.index]) - local subviews = self.subviews - - local heat = getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) - if heat >= 2 then summary = 'Magma safe ' .. summary - elseif heat == 1 then summary = 'Fire safe ' .. summary - else summary = 'Any ' .. summary - end - - local quality = getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - subviews.decorated:setOption(quality.decorated ~= 0) - subviews.min_quality:setOption(quality.min_quality) - subviews.max_quality:setOption(quality.max_quality) - - local cats = getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - local category_choices={ - make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', cats), - make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), - make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), - make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), - } - self.subviews.materials_categories:setChoices(category_choices) - - local mats = getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - local mat_choices = {} - local hide_zero = self.subviews.hide_zero:getOptionValue() - local enabled_mat_names = {} - for name,props in pairs(mats) do - local enabled = props.enabled == 'true' and cats[props.category] - if not cats.unset and enabled then - table.insert(enabled_mat_names, name) - end - if not hide_zero or tonumber(props.count) > 0 then - table.insert(mat_choices, make_mat_choice(name, props, enabled, cats)) - end - end - table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) - - local prev_filter = self.subviews.search.text - self.subviews.materials_mats:setChoices(mat_choices) - self.subviews.materials_mats:setFilter(prev_filter) - - if #enabled_mat_names > 0 then - table.sort(enabled_mat_names) - summary = summary .. (' of %s'):format(table.concat(enabled_mat_names, ', ')) - end - - self.summary = summary - self.dirty = false - self:updateLayout() -end - -function QualityAndMaterialsPage:toggle_category(_, choice) - local cats = {} - if not choice.icon then - -- toggling from unset to something is set - table.insert(cats, choice.cat) - else - choice.enabled = not choice.enabled - for _,c in ipairs(self.subviews.materials_categories:getChoices()) do - if c.enabled then - table.insert(cats, c.cat) - end - end - end - setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) - self.dirty = true -end - -function QualityAndMaterialsPage:toggle_material(_, choice) - local mats = {} - if not choice.icon then - -- toggling from unset to something is set - table.insert(mats, choice.name) - else - for _,c in ipairs(self.subviews.materials_mats:getChoices()) do - local enabled = c.enabled - if choice.name == c.name then - enabled = not c.enabled - end - if enabled then - table.insert(mats, c.name) - end - end - end - setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) - self.dirty = true -end - -function QualityAndMaterialsPage:invert_materials() - local mats = {} - for _,c in ipairs(self.subviews.materials_mats:getChoices()) do - if not c.icon then return end - if not c.enabled then - table.insert(mats, c.name) - end - end - setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) - self.dirty = true -end - -function QualityAndMaterialsPage:clear_filter() - clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) - self.dirty = true -end - -function QualityAndMaterialsPage:set_decorated(decorated) - local subviews = self.subviews - setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, - decorated and 1 or 0, subviews.min_quality:getOptionValue(), subviews.max_quality:getOptionValue()) - self.dirty = true -end - -function QualityAndMaterialsPage:set_min_quality(idx) - idx = math.min(6, math.max(0, idx-1)) - local subviews = self.subviews - subviews.min_quality:setOption(idx) - if subviews.max_quality:getOptionValue() < idx then - subviews.max_quality:setOption(idx) - end - setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, - subviews.decorated:getOptionValue() and 1 or 0, idx, subviews.max_quality:getOptionValue()) - self.dirty = true -end - -function QualityAndMaterialsPage:set_max_quality(idx) - idx = math.min(6, math.max(0, idx-1)) - local subviews = self.subviews - subviews.max_quality:setOption(idx) - if subviews.min_quality:getOptionValue() > idx then - subviews.min_quality:setOption(idx) - end - setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, - subviews.decorated:getOptionValue() and 1 or 0, subviews.min_quality:getOptionValue(), idx) - self.dirty = true -end - -local texpos = dfhack.textures.getThinBordersTexposStart() -local tp = function(offset) - if texpos == -1 then return nil end - return texpos + offset -end - -local TOP_PEN = to_pen{tile=tp(10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} -local MID_PEN = to_pen{tile=tp(4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} -local BOT_PEN = to_pen{tile=tp(11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} - -function QualityAndMaterialsPage:draw_divider(dc) - local y2 = dc.height - 1 - for y=0,y2 do - dc:seek(0, y) - if y == 0 then - dc:char(nil, TOP_PEN) - elseif y == y2 then - dc:char(nil, BOT_PEN) - else - dc:char(nil, MID_PEN) - end - end -end - -function QualityAndMaterialsPage:onRenderFrame(dc, rect) - QualityAndMaterialsPage.super.onRenderFrame(self, dc, rect) - if self.dirty then - self:refresh() - end -end - --------------------------------- --- GlobalSettingsPage --- - -GlobalSettingsPage = defclass(GlobalSettingsPage, widgets.ResizingPanel) -GlobalSettingsPage.ATTRS{ - autoarrange_subviews=true, - frame={t=0, l=0}, - frame_style=gui.INTERIOR_FRAME, -} - -function GlobalSettingsPage:init() - self:addviews{ - widgets.WrappedLabel{ - frame={l=0}, - text_to_wrap='These options will affect the selection of "Generic Materials" for all future buildings.', - }, - widgets.Panel{ - frame={h=1}, - }, - widgets.ToggleHotkeyLabel{ - view_id='blocks', - frame={l=0}, - key='CUSTOM_B', - label='Blocks', - label_width=8, - on_change=self:callback('update_setting', 'blocks'), - }, - widgets.ToggleHotkeyLabel{ - view_id='logs', - frame={l=0}, - key='CUSTOM_L', - label='Logs', - label_width=8, - on_change=self:callback('update_setting', 'logs'), - }, - widgets.ToggleHotkeyLabel{ - view_id='boulders', - frame={l=0}, - key='CUSTOM_O', - label='Boulders', - label_width=8, - on_change=self:callback('update_setting', 'boulders'), - }, - widgets.ToggleHotkeyLabel{ - view_id='bars', - frame={l=0}, - key='CUSTOM_R', - label='Bars', - label_width=8, - on_change=self:callback('update_setting', 'bars'), - }, - } - - self:init_settings() -end - -function GlobalSettingsPage:init_settings() - local settings = getGlobalSettings() - local subviews = self.subviews - subviews.blocks:setOption(settings.blocks) - subviews.logs:setOption(settings.logs) - subviews.boulders:setOption(settings.boulders) - subviews.bars:setOption(settings.bars) -end - -function GlobalSettingsPage:update_setting(setting, val) - dfhack.run_command('buildingplan', 'set', setting, tostring(val)) - self:init_settings() -end - --------------------------------- --- FilterSelection --- - -FilterSelection = defclass(FilterSelection, widgets.Window) -FilterSelection.ATTRS{ - frame_title='Choose filters', - frame={w=55, h=53, l=30, t=8}, - frame_inset={t=1}, - resizable=true, - index=DEFAULT_NIL, - autoarrange_subviews=true, -} - -function FilterSelection:init() - self:addviews{ - widgets.TabBar{ - frame={t=0}, - labels={ - 'Quality and materials', - 'Global settings', - }, - on_select=function(idx) - self.subviews.pages:setSelected(idx) - self:updateLayout() - end, - get_cur_page=function() return self.subviews.pages:getSelected() end, - key='CUSTOM_CTRL_T', - }, - widgets.Widget{ - frame={h=1}, - }, - widgets.Pages{ - view_id='pages', - frame={t=5, l=0, b=0, r=0}, - subviews={ - QualityAndMaterialsPage{index=self.index}, - GlobalSettingsPage{}, - }, - }, - } -end - -FilterSelectionScreen = defclass(FilterSelectionScreen, BuildingplanScreen) -FilterSelectionScreen.ATTRS { - focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', - index=DEFAULT_NIL, -} - -function FilterSelectionScreen:init() - self:addviews{ - FilterSelection{index=self.index} - } -end - -function FilterSelectionScreen:onShow() - -- don't let the building "shadow" follow the mouse cursor while this screen is open - df.global.game.main_interface.bottom_mode_selected = -1 -end - -function FilterSelectionScreen:onDismiss() - -- re-enable building shadow - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT -end - --------------------------------- --- ItemLine --- - -local function cur_building_has_no_area() - if uibs.building_type == df.building_type.Construction then return false end - local filters = dfhack.buildings.getFiltersByType({}, - uibs.building_type, uibs.building_subtype, uibs.custom_type) - -- this works because all variable-size buildings have either no item - -- filters or a quantity of -1 for their first (and only) item - return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) -end - -local function is_plannable() - return get_cur_filters() and - not (uibs.building_type == df.building_type.Construction - and uibs.building_subtype == df.construction_type.TrackNSEW) -end - -local function is_construction() - return uibs.building_type == df.building_type.Construction -end - -local function is_stairs() - return is_construction() - and uibs.building_subtype == df.construction_type.UpDownStair -end - -local direction_panel_frame = {t=4, h=13, w=46, r=28} - -local direction_panel_types = utils.invert{ - df.building_type.Bridge, - df.building_type.ScrewPump, - df.building_type.WaterWheel, - df.building_type.AxleHorizontal, - df.building_type.Rollers, -} - -local function has_direction_panel() - return direction_panel_types[uibs.building_type] - or (uibs.building_type == df.building_type.Trap - and uibs.building_subtype == df.trap_type.TrackStop) -end - -local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} - -local function has_pressure_plate_panel() - return uibs.building_type == df.building_type.Trap - and uibs.building_subtype == df.trap_type.PressurePlate -end - -local function is_over_options_panel() - local frame = nil - if has_direction_panel() then - frame = direction_panel_frame - elseif has_pressure_plate_panel() then - frame = pressure_plate_panel_frame - else - return false - end - local v = widgets.Widget{frame=frame} - local rect = gui.mkdims_wh(0, 0, dfhack.screen.getWindowSize()) - v:updateLayout(gui.ViewRect{rect=rect}) - return v:getMousePos() -end - -ItemLine = defclass(ItemLine, widgets.Panel) -ItemLine.ATTRS{ - idx=DEFAULT_NIL, - is_selected_fn=DEFAULT_NIL, - is_hollow_fn=DEFAULT_NIL, - on_select=DEFAULT_NIL, - on_filter=DEFAULT_NIL, - on_clear_filter=DEFAULT_NIL, -} - -function ItemLine:init() - self.frame.h = 1 - self.visible = function() return #get_cur_filters() >= self.idx end - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text='*', - auto_width=true, - visible=self.is_selected_fn, - }, - widgets.Label{ - frame={t=0, l=25}, - text={ - {tile=get_button_start_pen}, - {gap=6, tile=get_button_end_pen}, - }, - auto_width=true, - on_click=function() self.on_filter(self.idx) end, - }, - widgets.Label{ - frame={t=0, l=33}, - text={ - {tile=get_button_start_pen}, - {gap=1, tile=get_button_end_pen}, - }, - auto_width=true, - on_click=function() self.on_clear_filter(self.idx) end, - }, - widgets.Label{ - frame={t=0, l=2}, - text={ - {width=21, text=self:callback('get_item_line_text')}, - {gap=3, text='filter', pen=COLOR_GREEN}, - {gap=2, text='x', pen=self:callback('get_x_pen')}, - {gap=3, text=function() return self.note end, - pen=function() return self.note_pen end}, - }, - }, - } -end - -function ItemLine:reset() - self.desc = nil - self.available = nil -end - -function ItemLine:onInput(keys) - if keys._MOUSE_L_DOWN and self:getMousePos() then - self.on_select(self.idx) - end - return ItemLine.super.onInput(self, keys) -end - -function ItemLine:get_x_pen() - return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx - 1) and - COLOR_GREEN or COLOR_GREY -end - -function ItemLine:get_item_line_text() - local idx = self.idx - local filter = get_cur_filters()[idx] - local quantity = get_quantity(filter, self.is_hollow_fn()) - - self.desc = self.desc or get_desc(filter) - - self.available = self.available or countAvailableItems(uibs.building_type, - uibs.building_subtype, uibs.custom_type, idx - 1) - if self.available >= quantity then - self.note_pen = COLOR_GREEN - self.note = 'Available now' - else - self.note_pen = COLOR_YELLOW - self.note = 'Will link later' - end - - return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') -end - -function ItemLine:reduce_quantity(used_quantity) - if not self.available then return end - local filter = get_cur_filters()[self.idx] - used_quantity = used_quantity or get_quantity(filter, self.is_hollow_fn()) - self.available = math.max(0, self.available - used_quantity) -end - -local function get_placement_errors() - local out = '' - for _,str in ipairs(uibs.errors) do - if #out > 0 then out = out .. NEWLINE end - out = out .. str.value - end - return out -end - --------------------------------- --- PlannerOverlay --- - -PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) -PlannerOverlay.ATTRS{ - default_pos={x=5,y=9}, - default_enabled=true, - viewscreens='dwarfmode/Building/Placement', - frame={w=56, h=20}, -} - -function PlannerOverlay:init() - self.selected = 1 - - local main_panel = widgets.Panel{ - view_id='main', - frame={t=0, l=0, r=0, h=14}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, - } - - local function make_is_selected_fn(idx) - return function() return self.selected == idx end - end - - local function on_select_fn(idx) - self.selected = idx - end - - local function is_hollow_fn() - return self.subviews.hollow:getOptionValue() - end - - main_panel:addviews{ - widgets.Label{ - frame={}, - auto_width=true, - text='No items required.', - visible=function() return #get_cur_filters() == 0 end, - }, - ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, - is_selected_fn=make_is_selected_fn(1), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, - is_selected_fn=make_is_selected_fn(2), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, - is_selected_fn=make_is_selected_fn(3), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, - is_selected_fn=make_is_selected_fn(4), is_hollow_fn=is_hollow_fn, - on_select=on_select_fn, on_filter=self:callback('set_filter'), - on_clear_filter=self:callback('clear_filter')}, - widgets.CycleHotkeyLabel{ - view_id='hollow', - frame={t=3, l=4}, - key='CUSTOM_H', - label='Hollow area:', - visible=is_construction, - options={ - {label='No', value=false}, - {label='Yes', value=true}, - }, - }, - widgets.CycleHotkeyLabel{ - view_id='stairs_top_subtype', - frame={t=4, l=4}, - key='CUSTOM_R', - label='Top Stair Type: ', - visible=is_stairs, - options={ - {label='Auto', value='auto'}, - {label='UpDown', value=df.construction_type.UpDownStair}, - {label='Down', value=df.construction_type.DownStair}, - }, - }, - widgets.CycleHotkeyLabel { - view_id='stairs_bottom_subtype', - frame={t=5, l=4}, - key='CUSTOM_B', - label='Bottom Stair Type: ', - visible=is_stairs, - options={ - {label='Auto', value='auto'}, - {label='UpDown', value=df.construction_type.UpDownStair}, - {label='Up', value=df.construction_type.UpStair}, - }, - }, - widgets.Label{ - frame={b=3, l=17}, - text={ - 'Selected area: ', - {text=function() - return ('%dx%dx%d'):format(get_cur_area_dims(self.saved_placement)) - end - }, - }, - visible=function() - return not cur_building_has_no_area() and (self.saved_placement or is_choosing_area()) - end, - }, - widgets.Panel{ - visible=function() return #get_cur_filters() > 0 end, - subviews={ - widgets.HotkeyLabel{ - frame={b=1, l=0}, - key='STRING_A042', - auto_width=true, - enabled=function() return #get_cur_filters() > 1 end, - on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=1}, - key='STRING_A047', - label='Prev/next item', - auto_width=true, - enabled=function() return #get_cur_filters() > 1 end, - on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=21}, - key='CUSTOM_F', - label='Set filter', - auto_width=true, - on_activate=function() self:set_filter(self.selected) end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=37}, - key='CUSTOM_X', - label='Clear filter', - auto_width=true, - on_activate=function() self:clear_filter(self.selected) end, - enabled=function() - return hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) - end - }, - widgets.CycleHotkeyLabel{ - view_id='choose', - frame={b=0, l=0, w=25}, - key='CUSTOM_I', - label='Choose from items:', - options={{label='Yes', value=true}, - {label='No', value=false}}, - initial_option=false, - enabled=function() - for idx = 1,4 do - if (self.subviews['item'..idx].available or 0) > 0 then - return true - end - end - end, - }, - widgets.CycleHotkeyLabel{ - view_id='safety', - frame={b=0, l=29, w=25}, - key='CUSTOM_G', - label='Building safety:', - options={ - {label='Any', value=0}, - {label='Magma', value=2, pen=COLOR_RED}, - {label='Fire', value=1, pen=COLOR_LIGHTRED}, - }, - initial_option=0, - on_change=function(heat) - setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) - end, - }, - }, - }, - } - - local error_panel = widgets.ResizingPanel{ - view_id='errors', - frame={t=14, l=0, r=0}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, - } - - error_panel:addviews{ - widgets.WrappedLabel{ - frame={t=0, l=0, r=0}, - text_pen=COLOR_LIGHTRED, - text_to_wrap=get_placement_errors, - visible=function() return #uibs.errors > 0 end, - }, - widgets.Label{ - frame={t=0, l=0, r=0}, - text_pen=COLOR_GREEN, - text='OK to build', - visible=function() return #uibs.errors == 0 end, - }, - } - - self:addviews{ - main_panel, - error_panel, - } -end - -function PlannerOverlay:reset() - self.subviews.item1:reset() - self.subviews.item2:reset() - self.subviews.item3:reset() - self.subviews.item4:reset() - reset_counts_flag = false -end - -function PlannerOverlay:set_filter(idx) - FilterSelectionScreen{index=idx}:show() -end - -function PlannerOverlay:clear_filter(idx) - clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) -end - -local function get_placement_data() - local pos = uibs.pos - local direction = uibs.direction - local width, height, depth = get_cur_area_dims() - local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( - width, height, uibs.building_type, uibs.building_subtype, - uibs.custom_type, direction) - -- get the upper-left corner of the building/area at min z-level - local has_selection = is_choosing_area() - local start_pos = xyz2pos( - has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, - has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, - has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z - ) - if uibs.building_type == df.building_type.ScrewPump then - if direction == df.screw_pump_direction.FromSouth then - start_pos.y = start_pos.y + 1 - elseif direction == df.screw_pump_direction.FromEast then - start_pos.x = start_pos.x + 1 - end - end - local min_x, max_x = start_pos.x, start_pos.x - local min_y, max_y = start_pos.y, start_pos.y - local min_z, max_z = start_pos.z, start_pos.z - if adjusted_width == 1 and adjusted_height == 1 - and (width > 1 or height > 1 or depth > 1) then - max_x = min_x + width - 1 - max_y = min_y + height - 1 - max_z = math.max(uibs.selection_pos.z, pos.z) - end - return { - p1=xyz2pos(min_x, min_y, min_z), - p2=xyz2pos(max_x, max_y, max_z), - width=adjusted_width, - height=adjusted_height - } -end - -function PlannerOverlay:save_placement() - self.saved_placement = get_placement_data() - if (uibs.selection_pos:isValid()) then - self.saved_selection_pos_valid = true - self.saved_selection_pos = copyall(uibs.selection_pos) - self.saved_pos = copyall(uibs.pos) - uibs.selection_pos:clear() - else - self.saved_selection_pos = copyall(self.saved_placement.p1) - self.saved_pos = copyall(self.saved_placement.p2) - self.saved_pos.x = self.saved_pos.x + self.saved_placement.width - 1 - self.saved_pos.y = self.saved_pos.y + self.saved_placement.height - 1 - end -end - -function PlannerOverlay:restore_placement() - if self.saved_selection_pos_valid then - uibs.selection_pos = self.saved_selection_pos - self.saved_selection_pos_valid = nil - else - uibs.selection_pos:clear() - end - self.saved_selection_pos = nil - self.saved_pos = nil - local placement_data = self.saved_placement - self.saved_placement = nil - return placement_data -end - -function PlannerOverlay:onInput(keys) - if not is_plannable() then return false end - if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then - if uibs.selection_pos:isValid() then - uibs.selection_pos:clear() - return true - end - self.selected = 1 - self.subviews.hollow:setOption(false) - self.subviews.choose:setOption(false) - self:reset() - reset_counts_flag = true - return false - end - if PlannerOverlay.super.onInput(self, keys) then - return true - end - if keys._MOUSE_L_DOWN then - if is_over_options_panel() then return false end - local detect_rect = copyall(self.frame_rect) - detect_rect.height = self.subviews.main.frame_rect.height + - self.subviews.errors.frame_rect.height - detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 - if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) - or self.subviews.errors:getMousePos() then - return true - end - if not is_construction() and #uibs.errors > 0 then return true end - if dfhack.gui.getMousePos() then - if is_choosing_area() or cur_building_has_no_area() then - local filters = get_cur_filters() - local num_filters = #filters - local choose = self.subviews.choose - if choose.enabled() and choose:getOptionValue() then - self:save_placement() - local is_hollow = self.subviews.hollow:getOptionValue() - local chosen_items, active_screens = {}, {} - local pending = num_filters - df.global.game.main_interface.bottom_mode_selected = -1 - for idx = num_filters,1,-1 do - chosen_items[idx] = {} - if (self.subviews['item'..idx].available or 0) > 0 then - active_screens[idx] = ItemSelectionScreen{ - index=idx, - quantity=get_quantity(filters[idx], is_hollow, - self.saved_placement), - on_submit=function(items) - chosen_items[idx] = items - active_screens[idx]:dismiss() - active_screens[idx] = nil - pending = pending - 1 - if pending == 0 then - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT - self:place_building(self:restore_placement(), chosen_items) - end - end, - on_cancel=function() - for i,scr in pairs(active_screens) do - scr:dismiss() - end - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT - self:restore_placement() - end, - }:show() - else - pending = pending - 1 - end - end - else - self:place_building(get_placement_data()) - end - return true - elseif not is_choosing_area() then - return false - end - end - end - return keys._MOUSE_L or keys.SELECT -end -function PlannerOverlay:render(dc) - if not is_plannable() then return end - self.subviews.errors:updateLayout() - PlannerOverlay.super.render(self, dc) -end - -local GOOD_PEN, BAD_PEN -function reload_cursors() - GOOD_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} - BAD_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} -end -reload_cursors() - -local ONE_BY_ONE = xy2pos(1, 1) - -function PlannerOverlay:onRenderFrame(dc, rect) - PlannerOverlay.super.onRenderFrame(self, dc, rect) - - if reset_counts_flag then - self:reset() - self.subviews.safety:setOption(getHeatSafetyFilter( - uibs.building_type, uibs.building_subtype, uibs.custom_type)) - end - - local selection_pos = self.saved_selection_pos or uibs.selection_pos - if not selection_pos or selection_pos.x < 0 then return end - - local pos = self.saved_pos or uibs.pos - local bounds = { - x1 = math.max(0, math.min(selection_pos.x, pos.x)), - x2 = math.min(df.global.world.map.x_count-1, math.max(selection_pos.x, pos.x)), - y1 = math.max(0, math.min(selection_pos.y, pos.y)), - y2 = math.min(df.global.world.map.y_count-1, math.max(selection_pos.y, pos.y)), - } - - local hollow = self.subviews.hollow:getOptionValue() - local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN - - local get_pen_fn = is_construction() and function(pos) - return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN - end or function() - return default_pen - end - - local function get_overlay_pen(pos) - if not hollow then return get_pen_fn(pos) end - if pos.x == bounds.x1 or pos.x == bounds.x2 or - pos.y == bounds.y1 or pos.y == bounds.y2 then - return get_pen_fn(pos) - end - return gui.TRANSPARENT_PEN - end - - guidm.renderMapOverlay(get_overlay_pen, bounds) -end - -function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) - local subtype = uibs.building_subtype - if pos.z == corner1.z then - local opt = self.subviews.stairs_bottom_subtype:getOptionValue() - if opt == 'auto' then - local tt = dfhack.maps.getTileType(pos) - local shape = df.tiletype.attrs[tt].shape - if shape ~= df.tiletype_shape.STAIR_DOWN then - subtype = df.construction_type.UpStair - end - else - subtype = opt - end - elseif pos.z == corner2.z then - local opt = self.subviews.stairs_top_subtype:getOptionValue() - if opt == 'auto' then - local tt = dfhack.maps.getTileType(pos) - local shape = df.tiletype.attrs[tt].shape - if shape ~= df.tiletype_shape.STAIR_UP then - subtype = df.construction_type.DownStair - end - else - subtype = opt - end - end - return subtype -end - -function PlannerOverlay:place_building(placement_data, chosen_items) - local p1, p2 = placement_data.p1, placement_data.p2 - local blds = {} - local hollow = self.subviews.hollow:getOptionValue() - local subtype = uibs.building_subtype - for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do - if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then - goto continue - end - local pos = xyz2pos(x, y, z) - if is_stairs() then - subtype = self:get_stairs_subtype(pos, p1, p2) - end - local bld, err = dfhack.buildings.constructBuilding{pos=pos, - type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, - width=placement_data.width, height=placement_data.height, - direction=uibs.direction} - if err then - -- it's ok if some buildings fail to build - goto continue - end - -- assign fields for the types that need them. we can't pass them all in - -- to the call to constructBuilding since attempting to assign unrelated - -- fields to building types that don't support them causes errors. - for k,v in pairs(bld) do - if k == 'friction' then bld.friction = uibs.friction end - if k == 'use_dump' then bld.use_dump = uibs.use_dump end - if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end - if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end - if k == 'speed' then bld.speed = uibs.speed end - end - table.insert(blds, bld) - ::continue:: - end end end - local used_quantity = is_construction() and #blds or false - self.subviews.item1:reduce_quantity(used_quantity) - self.subviews.item2:reduce_quantity(used_quantity) - self.subviews.item3:reduce_quantity(used_quantity) - self.subviews.item4:reduce_quantity(used_quantity) - for _,bld in ipairs(blds) do - -- attach chosen items and reduce job_item quantity - if chosen_items then - local job = bld.jobs[0] - local jitems = job.job_items - for idx=1,#get_cur_filters() do - local item_ids = chosen_items[idx] - while jitems[idx-1].quantity > 0 and #item_ids > 0 do - local item_id = item_ids[#item_ids] - local item = df.item.find(item_id) - if not item then - dfhack.printerr(('item no longer available: %d'):format(item_id)) - break - end - if not dfhack.job.attachJobItem(job, item, df.job_item_ref.T_role.Hauled, idx-1, -1) then - dfhack.printerr(('cannot attach item: %d'):format(item_id)) - break - end - jitems[idx-1].quantity = jitems[idx-1].quantity - 1 - item_ids[#item_ids] = nil - end - end - end - addPlannedBuilding(bld) - end - scheduleCycle() - uibs.selection_pos:clear() -end - --------------------------------- --- InspectorLine --- - -local function get_building_filters() - local bld = dfhack.gui.getSelectedBuilding() - return dfhack.buildings.getFiltersByType({}, - bld:getType(), bld:getSubtype(), bld:getCustomType()) -end - -InspectorLine = defclass(InspectorLine, widgets.Panel) -InspectorLine.ATTRS{ - idx=DEFAULT_NIL, -} - -function InspectorLine:init() - self.frame.h = 2 - self.visible = function() return #get_building_filters() >= self.idx end - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text={{text=self:callback('get_desc_string')}}, - }, - widgets.Label{ - frame={t=1, l=2}, - text={{text=self:callback('get_status_line')}}, - }, - } -end - -function InspectorLine:get_desc_string() - if self.desc then return self.desc end - self.desc = getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) - return self.desc -end - -function InspectorLine:get_status_line() - if self.status then return self.status end - local queue_pos = getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1) - if queue_pos <= 0 then - return 'Item attached' - end - self.status = ('Position in line: %d'):format(queue_pos) - return self.status -end - -function InspectorLine:reset() - self.desc = nil - self.status = nil -end - --------------------------------- --- InspectorOverlay --- - -InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) -InspectorOverlay.ATTRS{ - default_pos={x=-41,y=14}, - default_enabled=true, - viewscreens='dwarfmode/ViewSheets/BUILDING', - frame={w=30, h=15}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, -} - -function InspectorOverlay:init() - self:addviews{ - widgets.Label{ - frame={t=0, l=0}, - text='Waiting for items:', - }, - InspectorLine{view_id='item1', frame={t=2, l=0}, idx=1}, - InspectorLine{view_id='item2', frame={t=4, l=0}, idx=2}, - InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3}, - InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4}, - widgets.HotkeyLabel{ - frame={t=11, l=0}, - label='adjust filters', - key='CUSTOM_CTRL_F', - visible=false, -- until implemented - }, - widgets.HotkeyLabel{ - frame={t=12, l=0}, - label='make top priority', - key='CUSTOM_CTRL_T', - on_activate=self:callback('make_top_priority'), - }, - } -end - -function InspectorOverlay:reset() - self.subviews.item1:reset() - self.subviews.item2:reset() - self.subviews.item3:reset() - self.subviews.item4:reset() - reset_inspector_flag = false -end - -function InspectorOverlay:make_top_priority() - makeTopPriority(dfhack.gui.getSelectedBuilding()) - self:reset() + return desc end -local RESUME_BUTTON_FRAME = {t=15, h=3, r=73, w=25} - -local function mouse_is_over_resume_button(rect) - local x,y = dfhack.screen.getMousePos() - if not x then return false end - if y < RESUME_BUTTON_FRAME.t or y > RESUME_BUTTON_FRAME.t + RESUME_BUTTON_FRAME.h - 1 then - return false - end - if x > rect.x2 - RESUME_BUTTON_FRAME.r + 1 or x < rect.x2 - RESUME_BUTTON_FRAME.r - RESUME_BUTTON_FRAME.w + 2 then - return false - end - return true +function reload_pens() + pens.reload_pens() end -function InspectorOverlay:onInput(keys) - if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then - return false - end - if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then - return true - elseif keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then - self:reset() - end - return InspectorOverlay.super.onInput(self, keys) +function signal_reset() + planner.reset_counts_flag = true + inspector.reset_inspector_flag = true end -function InspectorOverlay:render(dc) - if not isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then - return - end - if reset_inspector_flag then - self:reset() - end - InspectorOverlay.super.render(self, dc) +-- for use during development to reload all buildingplan modules +function reload_modules() + -- ensure circular deps are refreshed + reload('plugins.buildingplan.pens') + reload('plugins.buildingplan') + reload('plugins.buildingplan.filterselection') + reload('plugins.buildingplan.itemselection') + reload('plugins.buildingplan.planneroverlay') + reload('plugins.buildingplan.inspectoroverlay') + reload('plugins.buildingplan') end OVERLAY_WIDGETS = { - planner=PlannerOverlay, - inspector=InspectorOverlay, + planner=planner.PlannerOverlay, + inspector=inspector.InspectorOverlay, } return _ENV diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua new file mode 100644 index 000000000..d043406f3 --- /dev/null +++ b/plugins/lua/buildingplan/filterselection.lua @@ -0,0 +1,731 @@ +local _ENV = mkmodule('plugins.buildingplan.filterselection') + +local gui = require('gui') +local pens = require('plugins.buildingplan.pens') +local widgets = require('gui.widgets') + +local uibs = df.global.buildreq +local to_pen = dfhack.pen.parse + +local function get_cur_filters() + return dfhack.buildings.getFiltersByType({}, uibs.building_type, + uibs.building_subtype, uibs.custom_type) +end + +-------------------------------- +-- Slider +-- + +Slider = defclass(Slider, widgets.Widget) +Slider.ATTRS{ + num_stops=DEFAULT_NIL, + get_left_idx_fn=DEFAULT_NIL, + get_right_idx_fn=DEFAULT_NIL, + on_left_change=DEFAULT_NIL, + on_right_change=DEFAULT_NIL, +} + +function Slider:preinit(init_table) + init_table.frame = init_table.frame or {} + init_table.frame.h = init_table.frame.h or 1 +end + +function Slider:init() + if self.num_stops < 2 then error('too few Slider stops') end + self.is_dragging_target = nil -- 'left', 'right', or 'both' + self.is_dragging_idx = nil -- offset from leftmost dragged tile +end + +local function slider_get_width_per_idx(self) + return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) +end + +function Slider:onInput(keys) + if not keys._MOUSE_L_DOWN then return false end + local x = self:getMousePos() + if not x then return false end + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + local left_pos = width_per_idx*(left_idx-1) + local right_pos = width_per_idx*(right_idx-1) + 4 + if x < left_pos then + self.on_left_change(self.get_left_idx_fn() - 1) + elseif x < left_pos+3 then + self.is_dragging_target = 'left' + self.is_dragging_idx = x - left_pos + elseif x < right_pos then + self.is_dragging_target = 'both' + self.is_dragging_idx = x - left_pos + elseif x < right_pos+3 then + self.is_dragging_target = 'right' + self.is_dragging_idx = x - right_pos + else + self.on_right_change(self.get_right_idx_fn() + 1) + end + return true +end + +local function slider_do_drag(self, width_per_idx) + local x = self.frame_body:localXY(dfhack.screen.getMousePos()) + local cur_pos = x - self.is_dragging_idx + cur_pos = math.max(0, cur_pos) + cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) + local offset = self.is_dragging_target == 'right' and -2 or 1 + local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 + local new_left_idx, new_right_idx + if self.is_dragging_target == 'right' then + new_right_idx = new_idx + else + new_left_idx = new_idx + if self.is_dragging_target == 'both' then + new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() + if new_right_idx > self.num_stops then + return + end + end + end + if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then + self.on_left_change(new_left_idx) + end + if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then + self.on_right_change(new_right_idx) + end +end + +local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} + +function Slider:onRenderBody(dc, rect) + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + -- draw track + dc:seek(1,0) + dc:char(nil, SLIDER_LEFT_END) + dc:char(nil, SLIDER_TRACK) + for stop_idx=1,self.num_stops-1 do + local track_stop_pen = SLIDER_TRACK_STOP_SELECTED + local track_pen = SLIDER_TRACK_SELECTED + if left_idx > stop_idx or right_idx < stop_idx then + track_stop_pen = SLIDER_TRACK_STOP + track_pen = SLIDER_TRACK + elseif right_idx == stop_idx then + track_pen = SLIDER_TRACK + end + dc:char(nil, track_stop_pen) + for i=2,width_per_idx do + dc:char(nil, track_pen) + end + end + if right_idx >= self.num_stops then + dc:char(nil, SLIDER_TRACK_STOP_SELECTED) + else + dc:char(nil, SLIDER_TRACK_STOP) + end + dc:char(nil, SLIDER_TRACK) + dc:char(nil, SLIDER_RIGHT_END) + -- draw tabs + dc:seek(width_per_idx*(left_idx-1)) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + dc:seek(width_per_idx*(right_idx-1)+4) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + -- manage dragging + if self.is_dragging_target then + slider_do_drag(self, width_per_idx) + end + if df.global.enabler.mouse_lbut == 0 then + self.is_dragging_target = nil + self.is_dragging_idx = nil + end +end + +-------------------------------- +-- QualityAndMaterialsPage +-- + +QualityAndMaterialsPage = defclass(QualityAndMaterialsPage, widgets.Panel) +QualityAndMaterialsPage.ATTRS{ + frame={t=0, l=0}, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, +} + +local TYPE_COL_WIDTH = 20 +local HEADER_HEIGHT = 7 +local QUALITY_HEIGHT = 9 +local FOOTER_HEIGHT = 4 + +-- returns whether the items matched by the specified filter can have a quality +-- rating. This also conveniently indicates whether an item can be decorated. +local function can_be_improved(idx) + local filter = get_cur_filters()[idx] + if filter.flags2 and filter.flags2.building_material then + return false; + end + return filter.item_type ~= df.item_type.WOOD and + filter.item_type ~= df.item_type.BLOCKS and + filter.item_type ~= df.item_type.BAR and + filter.item_type ~= df.item_type.BOULDER +end + +local function mat_sort_by_name(a, b) + return a.name < b.name +end + +local function mat_sort_by_quantity(a, b) + return a.quantity > b.quantity or + (a.quantity == b.quantity and mat_sort_by_name(a, b)) +end + +function QualityAndMaterialsPage:init() + self.dirty = true + self.summary = '' + + local enable_item_quality = can_be_improved(self.index) + + self:addviews{ + widgets.Panel{ + view_id='header', + frame={l=0, t=0, h=HEADER_HEIGHT, r=0}, + frame_inset={l=1}, + subviews={ + widgets.Label{ + frame={l=0, t=0}, + text='Current filter:', + }, + widgets.WrappedLabel{ + frame={l=16, t=0, h=2, r=0}, + text_pen=COLOR_LIGHTCYAN, + text_to_wrap=function() return self.summary end, + auto_height=false, + }, + widgets.CycleHotkeyLabel{ + view_id='mat_sort', + frame={l=0, t=3, w=21}, + label='Sort by:', + key='CUSTOM_SHIFT_R', + options={ + {label='name', value=mat_sort_by_name}, + {label='available', value=mat_sort_by_quantity} + }, + on_change=function() self.dirty = true end, + }, + widgets.ToggleHotkeyLabel{ + view_id='hide_zero', + frame={l=0, t=4, w=24}, + label='Hide unavailable:', + key='CUSTOM_SHIFT_H', + initial_option=false, + on_change=function() self.dirty = true end, + }, + widgets.EditField{ + view_id='search', + frame={l=26, t=3}, + label_text='Search: ', + on_char=function(ch) return ch:match('[%l -]') end, + }, + widgets.Label{ + frame={l=1, b=0}, + text='Type', + text_pen=COLOR_LIGHTRED, + }, + widgets.Label{ + frame={l=TYPE_COL_WIDTH, b=0}, + text='Material', + text_pen=COLOR_LIGHTRED, + }, + }, + }, + widgets.Panel{ + view_id='materials_lists', + frame={l=0, t=HEADER_HEIGHT, r=0, b=FOOTER_HEIGHT+QUALITY_HEIGHT}, + frame_style=gui.INTERIOR_FRAME, + subviews={ + widgets.List{ + view_id='materials_categories', + frame={l=1, t=0, b=0, w=TYPE_COL_WIDTH-3}, + scroll_keys={}, + icon_width=2, + cursor_pen=COLOR_CYAN, + on_submit=self:callback('toggle_category'), + }, + widgets.FilteredList{ + view_id='materials_mats', + frame={l=TYPE_COL_WIDTH, t=0, r=0, b=0}, + icon_width=2, + on_submit=self:callback('toggle_material'), + }, + }, + }, + widgets.Panel{ + view_id='divider', + frame={l=TYPE_COL_WIDTH-1, t=HEADER_HEIGHT, b=FOOTER_HEIGHT+QUALITY_HEIGHT, w=1}, + on_render=self:callback('draw_divider'), + }, + widgets.Panel{ + view_id='quality_panel', + frame={l=0, r=0, h=QUALITY_HEIGHT, b=FOOTER_HEIGHT}, + frame_style=gui.INTERIOR_FRAME, + frame_title='Item quality', + subviews={ + widgets.CycleHotkeyLabel{ + view_id='decorated', + frame={l=0, t=1, w=23}, + key='CUSTOM_SHIFT_D', + label='Decorated only:', + options={ + {label='No', value=false}, + {label='Yes', value=true}, + }, + enabled=enable_item_quality, + on_change=self:callback('set_decorated'), + }, + widgets.CycleHotkeyLabel{ + view_id='min_quality', + frame={l=0, t=3, w=18}, + label='Min quality:', + label_below=true, + key_back='CUSTOM_SHIFT_Z', + key='CUSTOM_SHIFT_X', + options={ + {label='Ordinary', value=0}, + {label='Well Crafted', value=1}, + {label='Finely Crafted', value=2}, + {label='Superior', value=3}, + {label='Exceptional', value=4}, + {label='Masterful', value=5}, + {label='Artifact', value=6}, + }, + enabled=enable_item_quality, + on_change=function(val) self:set_min_quality(val+1) end, + }, + widgets.CycleHotkeyLabel{ + view_id='max_quality', + frame={r=1, t=3, w=18}, + label='Max quality:', + label_below=true, + key_back='CUSTOM_SHIFT_Q', + key='CUSTOM_SHIFT_W', + options={ + {label='Ordinary', value=0}, + {label='Well Crafted', value=1}, + {label='Finely Crafted', value=2}, + {label='Superior', value=3}, + {label='Exceptional', value=4}, + {label='Masterful', value=5}, + {label='Artifact', value=6}, + }, + enabled=enable_item_quality, + on_change=function(val) self:set_max_quality(val+1) end, + }, + Slider{ + frame={l=0, t=6}, + num_stops=7, + get_left_idx_fn=function() + return self.subviews.min_quality:getOptionValue() + 1 + end, + get_right_idx_fn=function() + return self.subviews.max_quality:getOptionValue() + 1 + end, + on_left_change=self:callback('set_min_quality'), + on_right_change=self:callback('set_max_quality'), + active=enable_item_quality, + }, + }, + }, + widgets.Panel{ + view_id='footer', + frame={l=0, r=0, b=0, h=FOOTER_HEIGHT}, + frame_inset={t=1, l=1}, + subviews={ + widgets.HotkeyLabel{ + frame={l=0, t=0}, + label='Toggle', + auto_width=true, + key='SELECT', + }, + widgets.HotkeyLabel{ + frame={l=0, t=2}, + label='Done', + auto_width=true, + key='LEAVESCREEN', + }, + widgets.HotkeyLabel{ + frame={l=30, t=0}, + label='Invert selection', + auto_width=true, + key='CUSTOM_SHIFT_I', + on_activate=self:callback('invert_materials'), + }, + widgets.HotkeyLabel{ + frame={l=30, t=2}, + label='Reset filter', + auto_width=true, + key='CUSTOM_SHIFT_X', + on_activate=self:callback('clear_filter'), + }, + }, + } + } + + -- replace the FilteredList's built-in EditField with our own + self.subviews.materials_mats.list.frame.t = 0 + self.subviews.materials_mats.edit.visible = false + self.subviews.materials_mats.edit = self.subviews.search + self.subviews.search.on_change = self.subviews.materials_mats:callback('onFilterChange') +end + +local MAT_ENABLED_PEN = to_pen{ch=string.char(251), fg=COLOR_LIGHTGREEN} +local MAT_DISABLED_PEN = to_pen{ch='x', fg=COLOR_RED} + +local function make_cat_choice(label, cat, key, cats) + local enabled = cats[cat] + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end + return { + text=label, + key=key, + enabled=enabled, + cat=cat, + icon=icon, + } +end + +local function make_mat_choice(name, props, enabled, cats) + local quantity = tonumber(props.count) + local text = ('%5d - %s'):format(quantity, name) + local icon = nil + if not cats.unset then + icon = enabled and MAT_ENABLED_PEN or MAT_DISABLED_PEN + end + return { + text=text, + enabled=enabled, + icon=icon, + name=name, + cat=props.category, + quantity=quantity, + } +end + +function QualityAndMaterialsPage:refresh() + local summary = self.desc + local subviews = self.subviews + + local buildingplan = require('plugins.buildingplan') + + local heat = buildingplan.getHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type) + if heat >= 2 then summary = 'Magma safe ' .. summary + elseif heat == 1 then summary = 'Fire safe ' .. summary + else summary = 'Any ' .. summary + end + + local quality = buildingplan.getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + subviews.decorated:setOption(quality.decorated ~= 0) + subviews.min_quality:setOption(quality.min_quality) + subviews.max_quality:setOption(quality.max_quality) + + local cats = buildingplan.getMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + local category_choices={ + make_cat_choice('Stone', 'stone', 'CUSTOM_SHIFT_S', cats), + make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), + make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), + make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), + } + self.subviews.materials_categories:setChoices(category_choices) + + local mats = buildingplan.getMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + local mat_choices = {} + local hide_zero = self.subviews.hide_zero:getOptionValue() + local enabled_mat_names = {} + for name,props in pairs(mats) do + local enabled = props.enabled == 'true' and cats[props.category] + if not cats.unset and enabled then + table.insert(enabled_mat_names, name) + end + if not hide_zero or tonumber(props.count) > 0 then + table.insert(mat_choices, make_mat_choice(name, props, enabled, cats)) + end + end + table.sort(mat_choices, self.subviews.mat_sort:getOptionValue()) + + local prev_filter = self.subviews.search.text + self.subviews.materials_mats:setChoices(mat_choices) + self.subviews.materials_mats:setFilter(prev_filter) + + if #enabled_mat_names > 0 then + table.sort(enabled_mat_names) + summary = summary .. (' of %s'):format(table.concat(enabled_mat_names, ', ')) + end + + self.summary = summary + self.dirty = false + self:updateLayout() +end + +function QualityAndMaterialsPage:toggle_category(_, choice) + local cats = {} + if not choice.icon then + -- toggling from unset to something is set + table.insert(cats, choice.cat) + else + choice.enabled = not choice.enabled + for _,c in ipairs(self.subviews.materials_categories:getChoices()) do + if c.enabled then + table.insert(cats, c.cat) + end + end + end + require('plugins.buildingplan').setMaterialMaskFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, cats) + self.dirty = true +end + +function QualityAndMaterialsPage:toggle_material(_, choice) + local mats = {} + if not choice.icon then + -- toggling from unset to something is set + table.insert(mats, choice.name) + else + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + local enabled = c.enabled + if choice.name == c.name then + enabled = not c.enabled + end + if enabled then + table.insert(mats, c.name) + end + end + end + require('plugins.buildingplan').setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) + self.dirty = true +end + +function QualityAndMaterialsPage:invert_materials() + local mats = {} + for _,c in ipairs(self.subviews.materials_mats:getChoices()) do + if not c.icon then return end + if not c.enabled then + table.insert(mats, c.name) + end + end + require('plugins.buildingplan').setMaterialFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, mats) + self.dirty = true +end + +function QualityAndMaterialsPage:clear_filter() + require('plugins.buildingplan').clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) + self.dirty = true +end + +function QualityAndMaterialsPage:set_decorated(decorated) + local subviews = self.subviews + require('plugins.buildingplan').setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + decorated and 1 or 0, subviews.min_quality:getOptionValue(), subviews.max_quality:getOptionValue()) + self.dirty = true +end + +function QualityAndMaterialsPage:set_min_quality(idx) + idx = math.min(6, math.max(0, idx-1)) + local subviews = self.subviews + subviews.min_quality:setOption(idx) + if subviews.max_quality:getOptionValue() < idx then + subviews.max_quality:setOption(idx) + end + require('plugins.buildingplan').setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + subviews.decorated:getOptionValue() and 1 or 0, idx, subviews.max_quality:getOptionValue()) + self.dirty = true +end + +function QualityAndMaterialsPage:set_max_quality(idx) + idx = math.min(6, math.max(0, idx-1)) + local subviews = self.subviews + subviews.max_quality:setOption(idx) + if subviews.min_quality:getOptionValue() > idx then + subviews.min_quality:setOption(idx) + end + require('plugins.buildingplan').setQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1, + subviews.decorated:getOptionValue() and 1 or 0, subviews.min_quality:getOptionValue(), idx) + self.dirty = true +end + +function QualityAndMaterialsPage:draw_divider(dc) + local y2 = dc.height - 1 + for y=0,y2 do + dc:seek(0, y) + if y == 0 then + dc:char(nil, pens.VERT_TOP_PEN) + elseif y == y2 then + dc:char(nil, pens.VERT_BOT_PEN) + else + dc:char(nil, pens.VERT_MID_PEN) + end + end +end + +function QualityAndMaterialsPage:onRenderFrame(dc, rect) + QualityAndMaterialsPage.super.onRenderFrame(self, dc, rect) + if self.dirty then + self:refresh() + end +end + +-------------------------------- +-- GlobalSettingsPage +-- + +GlobalSettingsPage = defclass(GlobalSettingsPage, widgets.ResizingPanel) +GlobalSettingsPage.ATTRS{ + autoarrange_subviews=true, + frame={t=0, l=0}, + frame_style=gui.INTERIOR_FRAME, +} + +function GlobalSettingsPage:init() + self:addviews{ + widgets.WrappedLabel{ + frame={l=0}, + text_to_wrap='These options will affect the selection of "Generic Materials" for all future buildings.', + }, + widgets.Panel{ + frame={h=1}, + }, + widgets.ToggleHotkeyLabel{ + view_id='blocks', + frame={l=0}, + key='CUSTOM_B', + label='Blocks', + label_width=8, + on_change=self:callback('update_setting', 'blocks'), + }, + widgets.ToggleHotkeyLabel{ + view_id='logs', + frame={l=0}, + key='CUSTOM_L', + label='Logs', + label_width=8, + on_change=self:callback('update_setting', 'logs'), + }, + widgets.ToggleHotkeyLabel{ + view_id='boulders', + frame={l=0}, + key='CUSTOM_O', + label='Boulders', + label_width=8, + on_change=self:callback('update_setting', 'boulders'), + }, + widgets.ToggleHotkeyLabel{ + view_id='bars', + frame={l=0}, + key='CUSTOM_R', + label='Bars', + label_width=8, + on_change=self:callback('update_setting', 'bars'), + }, + } + + self:init_settings() +end + +function GlobalSettingsPage:init_settings() + local settings = require('plugins.buildingplan').getGlobalSettings() + local subviews = self.subviews + subviews.blocks:setOption(settings.blocks) + subviews.logs:setOption(settings.logs) + subviews.boulders:setOption(settings.boulders) + subviews.bars:setOption(settings.bars) +end + +function GlobalSettingsPage:update_setting(setting, val) + dfhack.run_command('buildingplan', 'set', setting, tostring(val)) + self:init_settings() +end + +-------------------------------- +-- FilterSelection +-- + +FilterSelection = defclass(FilterSelection, widgets.Window) +FilterSelection.ATTRS{ + frame_title='Choose filters', + frame={w=55, h=53, l=30, t=8}, + frame_inset={t=1}, + resizable=true, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, + autoarrange_subviews=true, +} + +function FilterSelection:init() + self:addviews{ + widgets.TabBar{ + frame={t=0}, + labels={ + 'Quality and materials', + 'Global settings', + }, + on_select=function(idx) + self.subviews.pages:setSelected(idx) + self:updateLayout() + end, + get_cur_page=function() return self.subviews.pages:getSelected() end, + key='CUSTOM_CTRL_T', + }, + widgets.Widget{ + frame={h=1}, + }, + widgets.Pages{ + view_id='pages', + frame={t=5, l=0, b=0, r=0}, + subviews={ + QualityAndMaterialsPage{ + index=self.index, + desc=self.desc + }, + GlobalSettingsPage{}, + }, + }, + } +end + +FilterSelectionScreen = defclass(FilterSelectionScreen, gui.ZScreen) +FilterSelectionScreen.ATTRS { + focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/filterselection', + pass_movement_keys=true, + pass_mouse_clicks=false, + defocusable=false, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, +} + +function FilterSelectionScreen:init() + self:addviews{ + FilterSelection{ + index=self.index, + desc=self.desc + } + } +end + +function FilterSelectionScreen:onShow() + -- don't let the building "shadow" follow the mouse cursor while this screen is open + df.global.game.main_interface.bottom_mode_selected = -1 +end + +function FilterSelectionScreen:onDismiss() + -- re-enable building shadow + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT +end + +return _ENV diff --git a/plugins/lua/buildingplan/inspectoroverlay.lua b/plugins/lua/buildingplan/inspectoroverlay.lua new file mode 100644 index 000000000..5262eccc8 --- /dev/null +++ b/plugins/lua/buildingplan/inspectoroverlay.lua @@ -0,0 +1,148 @@ +local _ENV = mkmodule('plugins.buildingplan.inspectoroverlay') + +local gui = require('gui') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +reset_inspector_flag = false + +local function get_building_filters() + local bld = dfhack.gui.getSelectedBuilding() + return dfhack.buildings.getFiltersByType({}, + bld:getType(), bld:getSubtype(), bld:getCustomType()) +end + +-------------------------------- +-- InspectorLine +-- + +InspectorLine = defclass(InspectorLine, widgets.Panel) +InspectorLine.ATTRS{ + idx=DEFAULT_NIL, +} + +function InspectorLine:init() + self.frame.h = 2 + self.visible = function() return #get_building_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text={{text=self:callback('get_desc_string')}}, + }, + widgets.Label{ + frame={t=1, l=2}, + text={{text=self:callback('get_status_line')}}, + }, + } +end + +function InspectorLine:get_desc_string() + if self.desc then return self.desc end + self.desc = require('plugins.buildingplan').getDescString(dfhack.gui.getSelectedBuilding(), self.idx-1) + return self.desc +end + +function InspectorLine:get_status_line() + if self.status then return self.status end + local queue_pos = require('plugins.buildingplan').getQueuePosition(dfhack.gui.getSelectedBuilding(), self.idx-1) + if queue_pos <= 0 then + return 'Item attached' + end + self.status = ('Position in line: %d'):format(queue_pos) + return self.status +end + +function InspectorLine:reset() + self.desc = nil + self.status = nil +end + +-------------------------------- +-- InspectorOverlay +-- + +InspectorOverlay = defclass(InspectorOverlay, overlay.OverlayWidget) +InspectorOverlay.ATTRS{ + default_pos={x=-41,y=14}, + default_enabled=true, + viewscreens='dwarfmode/ViewSheets/BUILDING', + frame={w=30, h=15}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function InspectorOverlay:init() + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text='Waiting for items:', + }, + InspectorLine{view_id='item1', frame={t=2, l=0}, idx=1}, + InspectorLine{view_id='item2', frame={t=4, l=0}, idx=2}, + InspectorLine{view_id='item3', frame={t=6, l=0}, idx=3}, + InspectorLine{view_id='item4', frame={t=8, l=0}, idx=4}, + widgets.HotkeyLabel{ + frame={t=11, l=0}, + label='adjust filters', + key='CUSTOM_CTRL_F', + visible=false, -- until implemented + }, + widgets.HotkeyLabel{ + frame={t=12, l=0}, + label='make top priority', + key='CUSTOM_CTRL_T', + on_activate=self:callback('make_top_priority'), + }, + } +end + +function InspectorOverlay:reset() + self.subviews.item1:reset() + self.subviews.item2:reset() + self.subviews.item3:reset() + self.subviews.item4:reset() + reset_inspector_flag = false +end + +function InspectorOverlay:make_top_priority() + require('plugins.buildingplan').makeTopPriority(dfhack.gui.getSelectedBuilding()) + self:reset() +end + +local RESUME_BUTTON_FRAME = {t=15, h=3, r=73, w=25} + +local function mouse_is_over_resume_button(rect) + local x,y = dfhack.screen.getMousePos() + if not x then return false end + if y < RESUME_BUTTON_FRAME.t or y > RESUME_BUTTON_FRAME.t + RESUME_BUTTON_FRAME.h - 1 then + return false + end + if x > rect.x2 - RESUME_BUTTON_FRAME.r + 1 or x < rect.x2 - RESUME_BUTTON_FRAME.r - RESUME_BUTTON_FRAME.w + 2 then + return false + end + return true +end + +function InspectorOverlay:onInput(keys) + if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + return false + end + if keys._MOUSE_L_DOWN and mouse_is_over_resume_button(self.frame_parent_rect) then + return true + elseif keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or keys.LEAVESCREEN then + self:reset() + end + return InspectorOverlay.super.onInput(self, keys) +end + +function InspectorOverlay:render(dc) + if not require('plugins.buildingplan').isPlannedBuilding(dfhack.gui.getSelectedBuilding()) then + return + end + if reset_inspector_flag then + self:reset() + end + InspectorOverlay.super.render(self, dc) +end + +return _ENV diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua new file mode 100644 index 000000000..7e6567d04 --- /dev/null +++ b/plugins/lua/buildingplan/itemselection.lua @@ -0,0 +1,347 @@ +local _ENV = mkmodule('plugins.buildingplan.itemselection') + +local gui = require('gui') +local pens = require('plugins.buildingplan.pens') +local utils = require('utils') +local widgets = require('gui.widgets') + +local uibs = df.global.buildreq +local to_pen = dfhack.pen.parse + +local BUILD_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREEN, keep_lower=true} +local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} + +-- map of building type -> {set=set of recently used, list=list of recently used} +-- most recent entries are at the *end* of the list +local recently_used = {} + +local function sort_by_type(a, b) + local ad, bd = a.data, b.data + return ad.item_type < bd.item_type or + (ad.item_type == bd.item_type and ad.item_subtype < bd.item_subtype) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key < b.search_key) or + (ad.item_type == bd.item_type and ad.item_subtype == bd.item_subtype and a.search_key == b.search_key and ad.quality > bd.quality) +end + +local function sort_by_recency(a, b) + local tracker = recently_used[uibs.building_type] + if not tracker then return sort_by_type(a, b) end + local recent_a, recent_b = tracker.set[a.search_key], tracker.set[b.search_key] + -- if they're both in the set, return the one with the greater index, + -- indicating more recent + if recent_a and recent_b then return recent_a > recent_b end + if recent_a and not recent_b then return true end + if not recent_a and recent_b then return false end + return sort_by_type(a, b) +end + +local function sort_by_name(a, b) + return a.search_key < b.search_key or + (a.search_key == b.search_key and sort_by_type(a, b)) +end + +local function sort_by_quantity(a, b) + local ad, bd = a.data, b.data + return ad.quantity > bd.quantity or + (ad.quantity == bd.quantity and sort_by_type(a, b)) +end + +ItemSelection = defclass(ItemSelection, widgets.Window) +ItemSelection.ATTRS{ + frame_title='Choose items', + frame={w=56, h=20, l=4, t=8}, + resizable=true, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, + quantity=DEFAULT_NIL, + on_submit=DEFAULT_NIL, + on_cancel=DEFAULT_NIL, +} + +function ItemSelection:init() + self.num_selected = 0 + self.selected_set = {} + local plural = self.quantity == 1 and '' or 's' + + self:addviews{ + widgets.Label{ + frame={t=0, l=0, r=10}, + text={ + self.desc, + plural, + NEWLINE, + ('Select up to %d item%s ('):format(self.quantity, plural), + {text=function() return self.num_selected end}, + ' selected)', + }, + }, + widgets.Label{ + frame={r=0, w=9, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' ', NEWLINE, + ' Build ', NEWLINE, + ' ', + }, + on_click=self:callback('submit'), + }, + widgets.FilteredList{ + view_id='flist', + frame={t=3, l=0, r=0, b=4}, + case_sensitive=false, + choices=self:get_choices(sort_by_recency), + icon_width=2, + on_submit=self:callback('toggle_group'), + edit_on_char=function(ch) return ch:match('[%l -]') end, + }, + widgets.CycleHotkeyLabel{ + frame={l=0, b=2}, + key='CUSTOM_SHIFT_R', + label='Sort by:', + options={ + {label='Recently used', value=sort_by_recency}, + {label='Name', value=sort_by_name}, + {label='Amount', value=sort_by_quantity}, + }, + on_change=self:callback('on_sort'), + }, + widgets.HotkeyLabel{ + frame={l=0, b=1}, + key='SELECT', + label='Use all/none', + auto_width=true, + on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.HotkeyLabel{ + frame={l=22, b=1}, + key='CUSTOM_SHIFT_B', + label='Build', + auto_width=true, + on_activate=self:callback('submit'), + }, + widgets.HotkeyLabel{ + frame={l=38, b=1}, + key='LEAVESCREEN', + label='Go back', + auto_width=true, + on_activate=self:callback('on_cancel'), + }, + widgets.HotkeyLabel{ + frame={l=0, b=0}, + key='KEYBOARD_CURSOR_RIGHT_FAST', + key_sep=' : ', + label='Use one', + auto_width=true, + on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=6, b=0, w=5}, + text_pen=COLOR_LIGHTGREEN, + text='Right', + }, + widgets.HotkeyLabel{ + frame={l=23, b=0}, + key='KEYBOARD_CURSOR_LEFT_FAST', + key_sep=' : ', + label='Use one fewer', + auto_width=true, + on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=29, b=0, w=4}, + text_pen=COLOR_LIGHTGREEN, + text='Left', + }, + } +end + +-- resort and restore selection +function ItemSelection:on_sort(sort_fn) + local flist = self.subviews.flist + local saved_filter = flist:getFilter() + flist:setFilter('') + flist:setChoices(self:get_choices(sort_fn), flist:getSelected()) + flist:setFilter(saved_filter) +end + +local function make_search_key(str) + local out = '' + for c in str:gmatch("[%w%s]") do + out = out .. c + end + return out +end + +function ItemSelection:get_choices(sort_fn) + local item_ids = require('plugins.buildingplan').getAvailableItems(uibs.building_type, + uibs.building_subtype, uibs.custom_type, self.index-1) + local buckets = {} + for _,item_id in ipairs(item_ids) do + local item = df.item.find(item_id) + if not item then goto continue end + local desc = dfhack.items.getDescription(item, 0, true) + if buckets[desc] then + local bucket = buckets[desc] + table.insert(bucket.data.item_ids, item_id) + bucket.data.quantity = bucket.data.quantity + 1 + else + local entry = { + search_key=make_search_key(desc), + icon=self:callback('get_entry_icon', item_id), + data={ + item_ids={item_id}, + item_type=item:getType(), + item_subtype=item:getSubtype(), + quantity=1, + quality=item:getQuality(), + selected=0, + }, + } + buckets[desc] = entry + end + ::continue:: + end + local choices = {} + for desc,choice in pairs(buckets) do + local data = choice.data + choice.text = { + {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, + {gap=2, text=desc}, + } + table.insert(choices, choice) + end + table.sort(choices, sort_fn) + return choices +end + +function ItemSelection:increment_group(idx, choice) + local data = choice.data + if self.quantity <= self.num_selected then return false end + if data.selected >= data.quantity then return false end + data.selected = data.selected + 1 + self.num_selected = self.num_selected + 1 + local item_id = data.item_ids[data.selected] + self.selected_set[item_id] = true + return true +end + +function ItemSelection:decrement_group(idx, choice) + local data = choice.data + if data.selected <= 0 then return false end + local item_id = data.item_ids[data.selected] + self.selected_set[item_id] = nil + self.num_selected = self.num_selected - 1 + data.selected = data.selected - 1 + return true +end + +function ItemSelection:toggle_group(idx, choice) + local data = choice.data + if data.selected > 0 then + while self:decrement_group(idx, choice) do end + else + while self:increment_group(idx, choice) do end + end +end + +function ItemSelection:get_entry_icon(item_id) + return self.selected_set[item_id] and pens.SELECTED_ITEM_PEN or nil +end + +local function track_recently_used(choices) + -- use same set for all subtypes + local tracker = ensure_key(recently_used, uibs.building_type) + for _,choice in ipairs(choices) do + local data = choice.data + if data.selected <= 0 then goto continue end + local key = choice.search_key + local recent_set = ensure_key(tracker, 'set') + local recent_list = ensure_key(tracker, 'list') + if recent_set[key] then + if recent_list[#recent_list] ~= key then + for i,v in ipairs(recent_list) do + if v == key then + table.remove(recent_list, i) + table.insert(recent_list, key) + break + end + end + tracker.set = utils.invert(recent_list) + end + else + -- only keep most recent 10 + if #recent_list >= 10 then + -- remove least recently used from list and set + recent_set[table.remove(recent_list, 1)] = nil + end + table.insert(recent_list, key) + recent_set[key] = #recent_list + end + ::continue:: + end +end + +function ItemSelection:submit() + local selected_items = {} + for item_id in pairs(self.selected_set) do + table.insert(selected_items, item_id) + end + if #selected_items > 0 then + track_recently_used(self.subviews.flist:getChoices()) + end + self.on_submit(selected_items) +end + +function ItemSelection:onInput(keys) + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + self.on_cancel() + return true + elseif keys._MOUSE_L_DOWN then + local list = self.subviews.flist.list + local idx = list:getIdxUnderMouse() + if idx then + list:setSelected(idx) + local modstate = dfhack.internal.getModstate() + if modstate & 2 > 0 then -- ctrl + local choice = list:getChoices()[idx] + if modstate & 1 > 0 then -- shift + self:decrement_group(idx, choice) + else + self:increment_group(idx, choice) + end + return true + end + end + end + return ItemSelection.super.onInput(self, keys) +end + +ItemSelectionScreen = defclass(ItemSelectionScreen, gui.ZScreen) +ItemSelectionScreen.ATTRS { + focus_path='dwarfmode/Building/Placement/dfhack/lua/buildingplan/itemselection', + force_pause=true, + pass_movement_keys=true, + pass_pause=false, + pass_mouse_clicks=false, + defocusable=false, + index=DEFAULT_NIL, + desc=DEFAULT_NIL, + quantity=DEFAULT_NIL, + on_submit=DEFAULT_NIL, + on_cancel=DEFAULT_NIL, +} + +function ItemSelectionScreen:init() + self:addviews{ + ItemSelection{ + index=self.index, + desc=self.desc, + quantity=self.quantity, + on_submit=self.on_submit, + on_cancel=self.on_cancel, + } + } +end + +return _ENV diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua new file mode 100644 index 000000000..d2198706f --- /dev/null +++ b/plugins/lua/buildingplan/pens.lua @@ -0,0 +1,31 @@ +local _ENV = mkmodule('plugins.buildingplan.pens') + +GOOD_TILE_PEN, BAD_TILE_PEN = nil, nil +VERT_TOP_PEN, VERT_MID_PEN, VERT_BOT_PEN = nil, nil, nil +BUTTON_START_PEN, BUTTON_END_PEN = nil, nil +SELECTED_ITEM_PEN = nil + +local to_pen = dfhack.pen.parse + +local tp = function(base, offset) + if base == -1 then return nil end + return base + offset +end + +function reload_pens() + GOOD_TILE_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} + BAD_TILE_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} + + local tb_texpos = dfhack.textures.getThinBordersTexposStart() + VERT_TOP_PEN = to_pen{tile=tp(tb_texpos, 10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} + VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} + VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} + + local cp_texpos = dfhack.textures.getControlPanelTexposStart() + BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} + BUTTON_END_PEN = to_pen{tile=tp(cp_texpos, 15), ch=']', fg=COLOR_YELLOW} + SELECTED_ITEM_PEN = to_pen{tile=tp(cp_texpos, 9), ch=string.char(251), fg=COLOR_YELLOW} +end +reload_pens() + +return _ENV diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua new file mode 100644 index 000000000..864516436 --- /dev/null +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -0,0 +1,734 @@ +local _ENV = mkmodule('plugins.buildingplan.planneroverlay') + +local itemselection = require('plugins.buildingplan.itemselection') +local filterselection = require('plugins.buildingplan.filterselection') +local gui = require('gui') +local guidm = require('gui.dwarfmode') +local overlay = require('plugins.overlay') +local pens = require('plugins.buildingplan.pens') +local utils = require('utils') +local widgets = require('gui.widgets') +require('dfhack.buildings') + +local uibs = df.global.buildreq + +reset_counts_flag = false + +local function get_cur_filters() + return dfhack.buildings.getFiltersByType({}, uibs.building_type, + uibs.building_subtype, uibs.custom_type) +end + +local function is_choosing_area() + return uibs.selection_pos.x >= 0 +end + +local function get_cur_area_dims(placement_data) + if not placement_data and not is_choosing_area() then return 1, 1, 1 end + local selection_pos = placement_data and placement_data.p1 or uibs.selection_pos + local pos = placement_data and placement_data.p2 or uibs.pos + return math.abs(selection_pos.x - pos.x) + 1, + math.abs(selection_pos.y - pos.y) + 1, + math.abs(selection_pos.z - pos.z) + 1 +end + +local function get_quantity(filter, hollow, placement_data) + local quantity = filter.quantity or 1 + local dimx, dimy, dimz = get_cur_area_dims(placement_data) + if quantity < 1 then + return (((dimx * dimy) // 4) + 1) * dimz + end + if hollow and dimx > 2 and dimy > 2 then + return quantity * (2*dimx + 2*dimy - 4) * dimz + end + return quantity * dimx * dimy * dimz +end + +local function cur_building_has_no_area() + if uibs.building_type == df.building_type.Construction then return false end + local filters = dfhack.buildings.getFiltersByType({}, + uibs.building_type, uibs.building_subtype, uibs.custom_type) + -- this works because all variable-size buildings have either no item + -- filters or a quantity of -1 for their first (and only) item + return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) +end + +local function is_plannable() + return get_cur_filters() and + not (uibs.building_type == df.building_type.Construction + and uibs.building_subtype == df.construction_type.TrackNSEW) +end + +local function is_construction() + return uibs.building_type == df.building_type.Construction +end + +local function is_stairs() + return is_construction() + and uibs.building_subtype == df.construction_type.UpDownStair +end + +local direction_panel_frame = {t=4, h=13, w=46, r=28} + +local direction_panel_types = utils.invert{ + df.building_type.Bridge, + df.building_type.ScrewPump, + df.building_type.WaterWheel, + df.building_type.AxleHorizontal, + df.building_type.Rollers, +} + +local function has_direction_panel() + return direction_panel_types[uibs.building_type] + or (uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.TrackStop) +end + +local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} + +local function has_pressure_plate_panel() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.PressurePlate +end + +local function is_over_options_panel() + local frame = nil + if has_direction_panel() then + frame = direction_panel_frame + elseif has_pressure_plate_panel() then + frame = pressure_plate_panel_frame + else + return false + end + local v = widgets.Widget{frame=frame} + local rect = gui.mkdims_wh(0, 0, dfhack.screen.getWindowSize()) + v:updateLayout(gui.ViewRect{rect=rect}) + return v:getMousePos() +end + +-------------------------------- +-- ItemLine +-- + +ItemLine = defclass(ItemLine, widgets.Panel) +ItemLine.ATTRS{ + idx=DEFAULT_NIL, + is_selected_fn=DEFAULT_NIL, + is_hollow_fn=DEFAULT_NIL, + on_select=DEFAULT_NIL, + on_filter=DEFAULT_NIL, + on_clear_filter=DEFAULT_NIL, +} + +function ItemLine:init() + self.frame.h = 1 + self.visible = function() return #get_cur_filters() >= self.idx end + self:addviews{ + widgets.Label{ + frame={t=0, l=0}, + text='*', + auto_width=true, + visible=self.is_selected_fn, + }, + widgets.Label{ + frame={t=0, l=25}, + text={ + {tile=pens.BUTTON_START_PEN}, + {gap=6, tile=pens.BUTTON_END_PEN}, + }, + auto_width=true, + on_click=function() self.on_filter(self.idx) end, + }, + widgets.Label{ + frame={t=0, l=33}, + text={ + {tile=pens.BUTTON_START_PEN}, + {gap=1, tile=pens.BUTTON_END_PEN}, + }, + auto_width=true, + on_click=function() self.on_clear_filter(self.idx) end, + }, + widgets.Label{ + frame={t=0, l=2}, + text={ + {width=21, text=self:callback('get_item_line_text')}, + {gap=3, text='filter', pen=COLOR_GREEN}, + {gap=2, text='x', pen=self:callback('get_x_pen')}, + {gap=3, text=function() return self.note end, + pen=function() return self.note_pen end}, + }, + }, + } +end + +function ItemLine:reset() + self.desc = nil + self.available = nil +end + +function ItemLine:onInput(keys) + if keys._MOUSE_L_DOWN and self:getMousePos() then + self.on_select(self.idx) + end + return ItemLine.super.onInput(self, keys) +end + +function ItemLine:get_x_pen() + return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) and + COLOR_GREEN or COLOR_GREY +end + +function ItemLine:get_item_line_text() + local idx = self.idx + local filter = get_cur_filters()[idx] + local quantity = get_quantity(filter, self.is_hollow_fn()) + + local buildingplan = require('plugins.buildingplan') + self.desc = self.desc or buildingplan.get_desc(filter) + + self.available = self.available or buildingplan.countAvailableItems( + uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) + if self.available >= quantity then + self.note_pen = COLOR_GREEN + self.note = 'Available now' + else + self.note_pen = COLOR_YELLOW + self.note = 'Will link later' + end + + return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') +end + +function ItemLine:reduce_quantity(used_quantity) + if not self.available then return end + local filter = get_cur_filters()[self.idx] + used_quantity = used_quantity or get_quantity(filter, self.is_hollow_fn()) + self.available = math.max(0, self.available - used_quantity) +end + +local function get_placement_errors() + local out = '' + for _,str in ipairs(uibs.errors) do + if #out > 0 then out = out .. NEWLINE end + out = out .. str.value + end + return out +end + +-------------------------------- +-- PlannerOverlay +-- + +PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) +PlannerOverlay.ATTRS{ + default_pos={x=5,y=9}, + default_enabled=true, + viewscreens='dwarfmode/Building/Placement', + frame={w=56, h=20}, +} + +function PlannerOverlay:init() + self.selected = 1 + + local main_panel = widgets.Panel{ + view_id='main', + frame={t=0, l=0, r=0, h=14}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + } + + local function make_is_selected_fn(idx) + return function() return self.selected == idx end + end + + local function on_select_fn(idx) + self.selected = idx + end + + local function is_hollow_fn() + return self.subviews.hollow:getOptionValue() + end + + local buildingplan = require('plugins.buildingplan') + + main_panel:addviews{ + widgets.Label{ + frame={}, + auto_width=true, + text='No items required.', + visible=function() return #get_cur_filters() == 0 end, + }, + ItemLine{view_id='item1', frame={t=0, l=0, r=0}, idx=1, + is_selected_fn=make_is_selected_fn(1), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item2', frame={t=2, l=0, r=0}, idx=2, + is_selected_fn=make_is_selected_fn(2), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item3', frame={t=4, l=0, r=0}, idx=3, + is_selected_fn=make_is_selected_fn(3), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + ItemLine{view_id='item4', frame={t=6, l=0, r=0}, idx=4, + is_selected_fn=make_is_selected_fn(4), is_hollow_fn=is_hollow_fn, + on_select=on_select_fn, on_filter=self:callback('set_filter'), + on_clear_filter=self:callback('clear_filter')}, + widgets.CycleHotkeyLabel{ + view_id='hollow', + frame={t=3, l=4}, + key='CUSTOM_H', + label='Hollow area:', + visible=is_construction, + options={ + {label='No', value=false}, + {label='Yes', value=true}, + }, + }, + widgets.CycleHotkeyLabel{ + view_id='stairs_top_subtype', + frame={t=4, l=4}, + key='CUSTOM_R', + label='Top Stair Type: ', + visible=is_stairs, + options={ + {label='Auto', value='auto'}, + {label='UpDown', value=df.construction_type.UpDownStair}, + {label='Down', value=df.construction_type.DownStair}, + }, + }, + widgets.CycleHotkeyLabel { + view_id='stairs_bottom_subtype', + frame={t=5, l=4}, + key='CUSTOM_B', + label='Bottom Stair Type: ', + visible=is_stairs, + options={ + {label='Auto', value='auto'}, + {label='UpDown', value=df.construction_type.UpDownStair}, + {label='Up', value=df.construction_type.UpStair}, + }, + }, + widgets.Label{ + frame={b=3, l=17}, + text={ + 'Selected area: ', + {text=function() + return ('%dx%dx%d'):format(get_cur_area_dims(self.saved_placement)) + end + }, + }, + visible=function() + return not cur_building_has_no_area() and (self.saved_placement or is_choosing_area()) + end, + }, + widgets.Panel{ + visible=function() return #get_cur_filters() > 0 end, + subviews={ + widgets.HotkeyLabel{ + frame={b=1, l=0}, + key='STRING_A042', + auto_width=true, + enabled=function() return #get_cur_filters() > 1 end, + on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=1}, + key='STRING_A047', + label='Prev/next item', + auto_width=true, + enabled=function() return #get_cur_filters() > 1 end, + on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=21}, + key='CUSTOM_F', + label='Set filter', + auto_width=true, + on_activate=function() self:set_filter(self.selected) end, + }, + widgets.HotkeyLabel{ + frame={b=1, l=37}, + key='CUSTOM_X', + label='Clear filter', + auto_width=true, + on_activate=function() self:clear_filter(self.selected) end, + enabled=function() + return buildingplan.hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) + end + }, + widgets.CycleHotkeyLabel{ + view_id='choose', + frame={b=0, l=0, w=25}, + key='CUSTOM_I', + label='Choose from items:', + options={{label='Yes', value=true}, + {label='No', value=false}}, + initial_option=false, + enabled=function() + for idx = 1,4 do + if (self.subviews['item'..idx].available or 0) > 0 then + return true + end + end + end, + }, + widgets.CycleHotkeyLabel{ + view_id='safety', + frame={b=0, l=29, w=25}, + key='CUSTOM_G', + label='Building safety:', + options={ + {label='Any', value=0}, + {label='Magma', value=2, pen=COLOR_RED}, + {label='Fire', value=1, pen=COLOR_LIGHTRED}, + }, + initial_option=0, + on_change=function(heat) + buildingplan.setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) + end, + }, + }, + }, + } + + local error_panel = widgets.ResizingPanel{ + view_id='errors', + frame={t=14, l=0, r=0}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + } + + error_panel:addviews{ + widgets.WrappedLabel{ + frame={t=0, l=0, r=0}, + text_pen=COLOR_LIGHTRED, + text_to_wrap=get_placement_errors, + visible=function() return #uibs.errors > 0 end, + }, + widgets.Label{ + frame={t=0, l=0, r=0}, + text_pen=COLOR_GREEN, + text='OK to build', + visible=function() return #uibs.errors == 0 end, + }, + } + + self:addviews{ + main_panel, + error_panel, + } +end + +function PlannerOverlay:reset() + self.subviews.item1:reset() + self.subviews.item2:reset() + self.subviews.item3:reset() + self.subviews.item4:reset() + reset_counts_flag = false +end + +function PlannerOverlay:set_filter(idx) + filterselection.FilterSelectionScreen{index=idx, desc=require('plugins.buildingplan').get_desc(get_cur_filters()[idx])}:show() +end + +function PlannerOverlay:clear_filter(idx) + clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) +end + +local function get_placement_data() + local pos = uibs.pos + local direction = uibs.direction + local width, height, depth = get_cur_area_dims() + local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( + width, height, uibs.building_type, uibs.building_subtype, + uibs.custom_type, direction) + -- get the upper-left corner of the building/area at min z-level + local has_selection = is_choosing_area() + local start_pos = xyz2pos( + has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, + has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, + has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z + ) + if uibs.building_type == df.building_type.ScrewPump then + if direction == df.screw_pump_direction.FromSouth then + start_pos.y = start_pos.y + 1 + elseif direction == df.screw_pump_direction.FromEast then + start_pos.x = start_pos.x + 1 + end + end + local min_x, max_x = start_pos.x, start_pos.x + local min_y, max_y = start_pos.y, start_pos.y + local min_z, max_z = start_pos.z, start_pos.z + if adjusted_width == 1 and adjusted_height == 1 + and (width > 1 or height > 1 or depth > 1) then + max_x = min_x + width - 1 + max_y = min_y + height - 1 + max_z = math.max(uibs.selection_pos.z, pos.z) + end + return { + p1=xyz2pos(min_x, min_y, min_z), + p2=xyz2pos(max_x, max_y, max_z), + width=adjusted_width, + height=adjusted_height + } +end + +function PlannerOverlay:save_placement() + self.saved_placement = get_placement_data() + if (uibs.selection_pos:isValid()) then + self.saved_selection_pos_valid = true + self.saved_selection_pos = copyall(uibs.selection_pos) + self.saved_pos = copyall(uibs.pos) + uibs.selection_pos:clear() + else + self.saved_selection_pos = copyall(self.saved_placement.p1) + self.saved_pos = copyall(self.saved_placement.p2) + self.saved_pos.x = self.saved_pos.x + self.saved_placement.width - 1 + self.saved_pos.y = self.saved_pos.y + self.saved_placement.height - 1 + end +end + +function PlannerOverlay:restore_placement() + if self.saved_selection_pos_valid then + uibs.selection_pos = self.saved_selection_pos + self.saved_selection_pos_valid = nil + else + uibs.selection_pos:clear() + end + self.saved_selection_pos = nil + self.saved_pos = nil + local placement_data = self.saved_placement + self.saved_placement = nil + return placement_data +end + +function PlannerOverlay:onInput(keys) + if not is_plannable() then return false end + if keys.LEAVESCREEN or keys._MOUSE_R_DOWN then + if uibs.selection_pos:isValid() then + uibs.selection_pos:clear() + return true + end + self.selected = 1 + self.subviews.hollow:setOption(false) + self.subviews.choose:setOption(false) + self:reset() + reset_counts_flag = true + return false + end + if PlannerOverlay.super.onInput(self, keys) then + return true + end + if keys._MOUSE_L_DOWN then + if is_over_options_panel() then return false end + local detect_rect = copyall(self.frame_rect) + detect_rect.height = self.subviews.main.frame_rect.height + + self.subviews.errors.frame_rect.height + detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 + if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) + or self.subviews.errors:getMousePos() then + return true + end + if not is_construction() and #uibs.errors > 0 then return true end + if dfhack.gui.getMousePos() then + if is_choosing_area() or cur_building_has_no_area() then + local filters = get_cur_filters() + local num_filters = #filters + local choose = self.subviews.choose + if choose.enabled() and choose:getOptionValue() then + self:save_placement() + local is_hollow = self.subviews.hollow:getOptionValue() + local chosen_items, active_screens = {}, {} + local pending = num_filters + df.global.game.main_interface.bottom_mode_selected = -1 + for idx = num_filters,1,-1 do + chosen_items[idx] = {} + if (self.subviews['item'..idx].available or 0) > 0 then + local filter = filters[idx] + active_screens[idx] = itemselection.ItemSelectionScreen{ + index=idx, + desc=require('plugins.buildingplan').get_desc(filter), + quantity=get_quantity(filter, is_hollow, + self.saved_placement), + on_submit=function(items) + chosen_items[idx] = items + active_screens[idx]:dismiss() + active_screens[idx] = nil + pending = pending - 1 + if pending == 0 then + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT + self:place_building(self:restore_placement(), chosen_items) + end + end, + on_cancel=function() + for i,scr in pairs(active_screens) do + scr:dismiss() + end + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT + self:restore_placement() + end, + }:show() + else + pending = pending - 1 + end + end + else + self:place_building(get_placement_data()) + end + return true + elseif not is_choosing_area() then + return false + end + end + end + return keys._MOUSE_L or keys.SELECT +end + +function PlannerOverlay:render(dc) + if not is_plannable() then return end + self.subviews.errors:updateLayout() + PlannerOverlay.super.render(self, dc) +end + +local ONE_BY_ONE = xy2pos(1, 1) + +function PlannerOverlay:onRenderFrame(dc, rect) + PlannerOverlay.super.onRenderFrame(self, dc, rect) + + if reset_counts_flag then + self:reset() + self.subviews.safety:setOption(require('plugins.buildingplan').getHeatSafetyFilter( + uibs.building_type, uibs.building_subtype, uibs.custom_type)) + end + + local selection_pos = self.saved_selection_pos or uibs.selection_pos + if not selection_pos or selection_pos.x < 0 then return end + + local pos = self.saved_pos or uibs.pos + local bounds = { + x1 = math.max(0, math.min(selection_pos.x, pos.x)), + x2 = math.min(df.global.world.map.x_count-1, math.max(selection_pos.x, pos.x)), + y1 = math.max(0, math.min(selection_pos.y, pos.y)), + y2 = math.min(df.global.world.map.y_count-1, math.max(selection_pos.y, pos.y)), + } + + local hollow = self.subviews.hollow:getOptionValue() + local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN + + local get_pen_fn = is_construction() and function(pos) + return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN + end or function() + return default_pen + end + + local function get_overlay_pen(pos) + if not hollow then return get_pen_fn(pos) end + if pos.x == bounds.x1 or pos.x == bounds.x2 or + pos.y == bounds.y1 or pos.y == bounds.y2 then + return get_pen_fn(pos) + end + return gui.TRANSPARENT_PEN + end + + guidm.renderMapOverlay(get_overlay_pen, bounds) +end + +function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) + local subtype = uibs.building_subtype + if pos.z == corner1.z then + local opt = self.subviews.stairs_bottom_subtype:getOptionValue() + if opt == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape ~= df.tiletype_shape.STAIR_DOWN then + subtype = df.construction_type.UpStair + end + else + subtype = opt + end + elseif pos.z == corner2.z then + local opt = self.subviews.stairs_top_subtype:getOptionValue() + if opt == 'auto' then + local tt = dfhack.maps.getTileType(pos) + local shape = df.tiletype.attrs[tt].shape + if shape ~= df.tiletype_shape.STAIR_UP then + subtype = df.construction_type.DownStair + end + else + subtype = opt + end + end + return subtype +end + +function PlannerOverlay:place_building(placement_data, chosen_items) + local p1, p2 = placement_data.p1, placement_data.p2 + local blds = {} + local hollow = self.subviews.hollow:getOptionValue() + local subtype = uibs.building_subtype + for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do + if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then + goto continue + end + local pos = xyz2pos(x, y, z) + if is_stairs() then + subtype = self:get_stairs_subtype(pos, p1, p2) + end + local bld, err = dfhack.buildings.constructBuilding{pos=pos, + type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, + width=placement_data.width, height=placement_data.height, + direction=uibs.direction} + if err then + -- it's ok if some buildings fail to build + goto continue + end + -- assign fields for the types that need them. we can't pass them all in + -- to the call to constructBuilding since attempting to assign unrelated + -- fields to building types that don't support them causes errors. + for k,v in pairs(bld) do + if k == 'friction' then bld.friction = uibs.friction end + if k == 'use_dump' then bld.use_dump = uibs.use_dump end + if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end + if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end + if k == 'speed' then bld.speed = uibs.speed end + end + table.insert(blds, bld) + ::continue:: + end end end + local used_quantity = is_construction() and #blds or false + self.subviews.item1:reduce_quantity(used_quantity) + self.subviews.item2:reduce_quantity(used_quantity) + self.subviews.item3:reduce_quantity(used_quantity) + self.subviews.item4:reduce_quantity(used_quantity) + local buildingplan = require('plugins.buildingplan') + for _,bld in ipairs(blds) do + -- attach chosen items and reduce job_item quantity + if chosen_items then + local job = bld.jobs[0] + local jitems = job.job_items + for idx=1,#get_cur_filters() do + local item_ids = chosen_items[idx] + while jitems[idx-1].quantity > 0 and #item_ids > 0 do + local item_id = item_ids[#item_ids] + local item = df.item.find(item_id) + if not item then + dfhack.printerr(('item no longer available: %d'):format(item_id)) + break + end + if not dfhack.job.attachJobItem(job, item, df.job_item_ref.T_role.Hauled, idx-1, -1) then + dfhack.printerr(('cannot attach item: %d'):format(item_id)) + break + end + jitems[idx-1].quantity = jitems[idx-1].quantity - 1 + item_ids[#item_ids] = nil + end + end + end + buildingplan.addPlannedBuilding(bld) + end + buildingplan.scheduleCycle() + uibs.selection_pos:clear() +end + +return _ENV From 2818f2334972867c8a8ca9efa9a3291f116b4378 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 10 Mar 2023 07:15:52 +0000 Subject: [PATCH 0787/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 09ddae3c2..267eb20f7 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 09ddae3c2851f207c7257b4767182f4c6c938f18 +Subproject commit 267eb20f75e891fc0ade166e2d316498f454d913 diff --git a/scripts b/scripts index c8ceb198b..89a345d66 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit c8ceb198b4fc62155b31a5554b9c96cc6bcb8d55 +Subproject commit 89a345d66d70eb02293bd2e46dcfad65f0229ee6 From 44fb91056bc59f89b7eb5a3cb0f5dc5a4f6b1f42 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 9 Mar 2023 23:41:13 -0800 Subject: [PATCH 0788/2222] ensure pressure plate config gets copied to the planned building --- plugins/lua/buildingplan/planneroverlay.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 864516436..d90285141 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -692,6 +692,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) if k == 'dump_x_shift' then bld.dump_x_shift = uibs.dump_x_shift end if k == 'dump_y_shift' then bld.dump_y_shift = uibs.dump_y_shift end if k == 'speed' then bld.speed = uibs.speed end + if k == 'plate_info' then utils.assign(bld.plate_info, uibs.plate_info) end end table.insert(blds, bld) ::continue:: From 2f0dc4bd256fc5d03ae40e3020917ea5f2d15c8b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 00:09:12 -0800 Subject: [PATCH 0789/2222] require correct number of mechanisms when building pressure plates --- plugins/lua/buildingplan/planneroverlay.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index d90285141..41da7a429 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -32,7 +32,17 @@ local function get_cur_area_dims(placement_data) math.abs(selection_pos.z - pos.z) + 1 end +local function is_pressure_plate() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.PressurePlate +end + local function get_quantity(filter, hollow, placement_data) + if is_pressure_plate() then + local flags = uibs.plate_info.flags + return (flags.units and 1 or 0) + (flags.water and 1 or 0) + + (flags.magma and 1 or 0) + (flags.track and 1 or 0) + end local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims(placement_data) if quantity < 1 then @@ -87,8 +97,7 @@ end local pressure_plate_panel_frame = {t=4, h=37, w=46, r=28} local function has_pressure_plate_panel() - return uibs.building_type == df.building_type.Trap - and uibs.building_subtype == df.trap_type.PressurePlate + return is_pressure_plate() end local function is_over_options_panel() @@ -667,6 +676,10 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local blds = {} local hollow = self.subviews.hollow:getOptionValue() local subtype = uibs.building_subtype + local filters = get_cur_filters() + if is_pressure_plate() then + filters[1].quantity = get_quantity() + end for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then goto continue @@ -678,7 +691,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local bld, err = dfhack.buildings.constructBuilding{pos=pos, type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, width=placement_data.width, height=placement_data.height, - direction=uibs.direction} + direction=uibs.direction, filters=filters} if err then -- it's ok if some buildings fail to build goto continue From a3f8be3c012bd98a30d6878e6f273a8feeb07f2e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 00:12:56 -0800 Subject: [PATCH 0790/2222] fix pens reference --- plugins/lua/buildingplan/planneroverlay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 864516436..de25af1f9 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -614,10 +614,10 @@ function PlannerOverlay:onRenderFrame(dc, rect) } local hollow = self.subviews.hollow:getOptionValue() - local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and GOOD_PEN or BAD_PEN + local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and pens.GOOD_TILE_PEN or pens.BAD_TILE_PEN local get_pen_fn = is_construction() and function(pos) - return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and GOOD_PEN or BAD_PEN + return dfhack.buildings.checkFreeTiles(pos, ONE_BY_ONE) and pens.GOOD_TILE_PEN or pens.BAD_TILE_PEN end or function() return default_pen end From b884fab7b6f533c21fab125cb7f044ddeadad995 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 01:04:23 -0800 Subject: [PATCH 0791/2222] fix freeze when printing status and there are defunct planned buildings --- plugins/buildingplan/buildingplan.cpp | 12 ++++++++---- plugins/buildingplan/plannedbuilding.cpp | 5 +++-- plugins/buildingplan/plannedbuilding.h | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 310d03a5e..2b67c2353 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -516,16 +516,20 @@ static void printStatus(color_ostream &out) { out.print(" use bars: %s\n", get_config_bool(config, CONFIG_BARS) ? "yes" : "no"); out.print("\n"); + size_t bld_count = 0; map counts; int32_t total = 0; for (auto &entry : planned_buildings) { auto &pb = entry.second; - auto bld = pb.getBuildingIfValidOrRemoveIfNot(out); + // don't actually remove bad buildings from the list while we're + // actively iterating through that list + auto bld = pb.getBuildingIfValidOrRemoveIfNot(out, true); if (!bld || bld->jobs.size() != 1) continue; auto &job_items = bld->jobs[0]->job_items; if (job_items.size() != pb.vector_ids.size()) continue; + ++bld_count; int job_item_idx = 0; for (auto &vec_ids : pb.vector_ids) { auto &jitem = job_items[job_item_idx++]; @@ -537,9 +541,9 @@ static void printStatus(color_ostream &out) { } } - if (planned_buildings.size()) { + if (bld_count) { out.print("Waiting for %d item(s) to be produced for %zd building(s):\n", - total, planned_buildings.size()); + total, bld_count); for (auto &count : counts) out.print(" %3d %s%s\n", count.second, count.first.c_str(), count.second == 1 ? "" : "s"); } else { @@ -949,7 +953,7 @@ static string getDescString(color_ostream &out, df::building *bld, int index) { PlannedBuilding &pb = planned_buildings.at(bld->id); auto &jitem = bld->jobs[0]->job_items[index]; - return get_desc_string(out, jitem, pb.vector_ids[index]); + return int_to_string(jitem->quantity) + " " + get_desc_string(out, jitem, pb.vector_ids[index]); } static int getQueuePosition(color_ostream &out, df::building *bld, int index) { diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index a693f6fcf..e678c850f 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -99,11 +99,12 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_con // Ensure the building still exists and is in a valid state. It can disappear // for lots of reasons, such as running the game with the buildingplan plugin // disabled, manually removing the building, modifying it via the API, etc. -df::building * PlannedBuilding::getBuildingIfValidOrRemoveIfNot(color_ostream &out) { +df::building * PlannedBuilding::getBuildingIfValidOrRemoveIfNot(color_ostream &out, bool skip_remove) { auto bld = df::building::find(id); bool valid = bld && bld->getBuildStage() == 0; if (!valid) { - remove(out); + if (!skip_remove) + remove(out); return NULL; } return bld; diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 5bd09ba5a..59dc24a79 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -29,7 +29,7 @@ public: // Ensure the building still exists and is in a valid state. It can disappear // for lots of reasons, such as running the game with the buildingplan plugin // disabled, manually removing the building, modifying it via the API, etc. - df::building * getBuildingIfValidOrRemoveIfNot(DFHack::color_ostream &out); + df::building * getBuildingIfValidOrRemoveIfNot(DFHack::color_ostream &out, bool skip_remove = false); private: DFHack::PersistentDataItem bld_config; From 43b423cd313a5ebc1a28a6e562b3629a92e84f2a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 01:44:33 -0800 Subject: [PATCH 0792/2222] make number of weapons in trap configurable --- plugins/lua/buildingplan/planneroverlay.lua | 36 +++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index fe3cebd83..924080ec3 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -37,11 +37,21 @@ local function is_pressure_plate() and uibs.building_subtype == df.trap_type.PressurePlate end +local function is_weapon_trap() + return uibs.building_type == df.building_type.Trap + and uibs.building_subtype == df.trap_type.WeaponTrap +end + +-- adjusted from CycleHotkeyLabel on the planner panel +local weapon_quantity = 1 + local function get_quantity(filter, hollow, placement_data) if is_pressure_plate() then local flags = uibs.plate_info.flags return (flags.units and 1 or 0) + (flags.water and 1 or 0) + (flags.magma and 1 or 0) + (flags.track and 1 or 0) + elseif is_weapon_trap() and filter.vector_id == df.job_item_vector_id.ANY_WEAPON then + return weapon_quantity end local quantity = filter.quantity or 1 local dimx, dimy, dimz = get_cur_area_dims(placement_data) @@ -298,7 +308,7 @@ function PlannerOverlay:init() view_id='stairs_top_subtype', frame={t=4, l=4}, key='CUSTOM_R', - label='Top Stair Type: ', + label='Top Stair Type: ', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -310,7 +320,7 @@ function PlannerOverlay:init() view_id='stairs_bottom_subtype', frame={t=5, l=4}, key='CUSTOM_B', - label='Bottom Stair Type: ', + label='Bottom Stair Type:', visible=is_stairs, options={ {label='Auto', value='auto'}, @@ -318,6 +328,16 @@ function PlannerOverlay:init() {label='Up', value=df.construction_type.UpStair}, }, }, + widgets.CycleHotkeyLabel { + view_id='weapons', + frame={t=5, l=4}, + key='CUSTOM_T', + key_back='CUSTOM_SHIFT_T', + label='Num weapons:', + visible=is_weapon_trap, + options={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + on_change=function(val) weapon_quantity = val end, + }, widgets.Label{ frame={b=3, l=17}, text={ @@ -678,7 +698,9 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local subtype = uibs.building_subtype local filters = get_cur_filters() if is_pressure_plate() then - filters[1].quantity = get_quantity() + filters[1].quantity = get_quantity(filters[1]) + elseif is_weapon_trap() then + filters[2].quantity = get_quantity(filters[2]) end for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then @@ -721,9 +743,11 @@ function PlannerOverlay:place_building(placement_data, chosen_items) if chosen_items then local job = bld.jobs[0] local jitems = job.job_items - for idx=1,#get_cur_filters() do + local num_filters = #get_cur_filters() + for idx=1,num_filters do local item_ids = chosen_items[idx] - while jitems[idx-1].quantity > 0 and #item_ids > 0 do + local jitem = jitems[num_filters-idx] + while jitem.quantity > 0 and #item_ids > 0 do local item_id = item_ids[#item_ids] local item = df.item.find(item_id) if not item then @@ -734,7 +758,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) dfhack.printerr(('cannot attach item: %d'):format(item_id)) break end - jitems[idx-1].quantity = jitems[idx-1].quantity - 1 + jitem.quantity = jitem.quantity - 1 item_ids[#item_ids] = nil end end From 6ad2922aca5897991ac164ff37ce5669d963b8ab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 02:34:05 -0800 Subject: [PATCH 0793/2222] filter displayed materials if building heat safety is set --- plugins/buildingplan/buildingplan.cpp | 18 ++++++++++++++++-- plugins/buildingplan/buildingplan.h | 1 + plugins/buildingplan/buildingplan_cycle.cpp | 17 +++++++++++------ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 2b67c2353..abc3c6ebd 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -847,11 +847,25 @@ static int getMaterialFilter(lua_State *L) { const auto &mat_filter = filters[index].getMaterials(); map counts; scanAvailableItems(*out, type, subtype, custom, index, NULL, &counts); - // name -> {count=int, enabled=bool, category=string} + HeatSafety heat = get_heat_safety_filter(key); + df::job_item jitem_cur_heat = getJobItemWithHeatSafety( + get_job_items(*out, key)[index], heat); + df::job_item jitem_fire = getJobItemWithHeatSafety( + get_job_items(*out, key)[index], HEAT_SAFETY_FIRE); + df::job_item jitem_magma = getJobItemWithHeatSafety( + get_job_items(*out, key)[index], HEAT_SAFETY_MAGMA); + // name -> {count=int, enabled=bool, category=string, heat=string} map> ret; for (auto & entry : mat_cache) { - auto &name = entry.first; auto &mat = entry.second.first; + if (!mat.matches(jitem_cur_heat)) + continue; + string heat_safety = ""; + if (mat.matches(jitem_magma)) + heat_safety = "magma-safe"; + else if (mat.matches(jitem_fire)) + heat_safety = "fire-safe"; + auto &name = entry.first; auto &cat = entry.second.second; map props; string count = "0"; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index eef9808e6..0d0d5b45f 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -47,6 +47,7 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item); bool itemPassesScreen(df::item * item); +df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat); bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index f401c90a8..c08324e8a 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -48,6 +48,16 @@ bool itemPassesScreen(df::item * item) { && !item->isAssignedToStockpile(); } +df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat) { + df::job_item jitem = *job_item; + if (heat >= HEAT_SAFETY_MAGMA) { + jitem.flags2.bits.magma_safe = true; + jitem.flags2.bits.fire_safe = false; + } else if (heat == HEAT_SAFETY_FIRE && !jitem.flags2.bits.magma_safe) + jitem.flags2.bits.fire_safe = true; + return jitem; +} + bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) @@ -67,12 +77,7 @@ bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety h && !item->hasToolUse(job_item->has_tool_use)) return false; - df::job_item jitem = *job_item; - if (heat == HEAT_SAFETY_MAGMA) { - jitem.flags2.bits.magma_safe = true; - jitem.flags2.bits.fire_safe = false; - } else if (heat == HEAT_SAFETY_FIRE && !jitem.flags2.bits.magma_safe) - jitem.flags2.bits.fire_safe = true; + df::job_item jitem = getJobItemWithHeatSafety(job_item, heat); return Job::isSuitableItem( &jitem, item->getType(), item->getSubtype()) From c56e24803453e267b2e6cda0f984f099de370a16 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 14:08:31 -0800 Subject: [PATCH 0794/2222] cleanup stockpiles plugin and don't bork on empty type vectors --- plugins/stockpiles/OrganicMatLookup.cpp | 164 +- plugins/stockpiles/OrganicMatLookup.h | 34 +- plugins/stockpiles/StockpileSerializer.cpp | 2322 ++++++++------------ plugins/stockpiles/StockpileSerializer.h | 528 ++--- plugins/stockpiles/StockpileUtils.h | 46 +- plugins/stockpiles/stockpiles.cpp | 121 +- 6 files changed, 1328 insertions(+), 1887 deletions(-) diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp index 5167ece4a..2367eddb5 100644 --- a/plugins/stockpiles/OrganicMatLookup.cpp +++ b/plugins/stockpiles/OrganicMatLookup.cpp @@ -1,154 +1,126 @@ #include "OrganicMatLookup.h" - #include "StockpileUtils.h" -#include "modules/Materials.h" -#include "MiscUtils.h" - -#include "df/world.h" -#include "df/world_data.h" +#include "Debug.h" #include "df/creature_raw.h" #include "df/caste_raw.h" -#include "df/material.h" +#include "df/world.h" using namespace DFHack; using namespace df::enums; using df::global::world; -using std::endl; +namespace DFHack { + DBG_EXTERN(stockpiles, log); +} /** * Helper class for mapping the various organic mats between their material indices * and their index in the stockpile_settings structures. */ -void OrganicMatLookup::food_mat_by_idx ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ) -{ - out << "food_lookup: food_idx(" << food_idx << ") "; - df::world_raws &raws = world->raws; +void OrganicMatLookup::food_mat_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat) { + DEBUG(log).print("food_lookup: food_idx(%zd) ", food_idx); + df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; int32_t main_idx = table.organic_indexes[mat_category][food_idx]; int16_t type = table.organic_types[mat_category][food_idx]; - if ( mat_category == organic_mat_category::Fish || - mat_category == organic_mat_category::UnpreparedFish || - mat_category == organic_mat_category::Eggs ) - { + if (mat_category == organic_mat_category::Fish || + mat_category == organic_mat_category::UnpreparedFish || + mat_category == organic_mat_category::Eggs) { food_mat.creature = raws.creatures.all[type]; food_mat.caste = food_mat.creature->caste[main_idx]; - out << " special creature type(" << type << ") caste("<< main_idx <<")" <::size_type idx ) -{ +std::string OrganicMatLookup::food_token_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx) { FoodMat food_mat; - food_mat_by_idx ( out, mat_category, idx, food_mat ); - if ( food_mat.material.isValid() ) - { + food_mat_by_idx(mat_category, idx, food_mat); + if (food_mat.material.isValid()) { return food_mat.material.getToken(); } - else if ( food_mat.creature ) - { + else if (food_mat.creature) { return food_mat.creature->creature_id + ":" + food_mat.caste->caste_id; } return std::string(); } -size_t OrganicMatLookup::food_max_size ( organic_mat_category::organic_mat_category mat_category ) -{ +size_t OrganicMatLookup::food_max_size(organic_mat_category::organic_mat_category mat_category) { return world->raws.mat_table.organic_types[mat_category].size(); } -void OrganicMatLookup::food_build_map ( std::ostream &out ) -{ - if ( index_built ) +void OrganicMatLookup::food_build_map() { + if (index_built) return; - df::world_raws &raws = world->raws; + df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; - for ( int32_t mat_category = traits::first_item_value; mat_category <= traits::last_item_value; ++mat_category ) - { - for ( size_t i = 0; i < table.organic_indexes[mat_category].size(); ++i ) - { - int16_t type = table.organic_types[mat_category].at ( i ); - int32_t index = table.organic_indexes[mat_category].at ( i ); - food_index[mat_category].insert ( std::make_pair ( std::make_pair ( type,index ), i ) ); // wtf.. only in c++ + for (int32_t mat_category = traits::first_item_value; mat_category <= traits::last_item_value; ++mat_category) { + for (size_t i = 0; i < table.organic_indexes[mat_category].size(); ++i) { + int16_t type = table.organic_types[mat_category].at(i); + int32_t index = table.organic_indexes[mat_category].at(i); + food_index[mat_category].insert(std::make_pair(std::make_pair(type, index), i)); // wtf.. only in c++ } } index_built = true; } -int16_t OrganicMatLookup::food_idx_by_token ( std::ostream &out, organic_mat_category::organic_mat_category mat_category, const std::string & token ) -{ - int16_t food_idx = -1; - df::world_raws &raws = world->raws; +int16_t OrganicMatLookup::food_idx_by_token(organic_mat_category::organic_mat_category mat_category, const std::string& token) { + df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; - out << "food_idx_by_token: "; - if ( mat_category == organic_mat_category::Fish || - mat_category == organic_mat_category::UnpreparedFish || - mat_category == organic_mat_category::Eggs ) - { + DEBUG(log).print("food_idx_by_token: "); + if (mat_category == organic_mat_category::Fish || + mat_category == organic_mat_category::UnpreparedFish || + mat_category == organic_mat_category::Eggs) { std::vector tokens; - split_string ( &tokens, token, ":" ); - if ( tokens.size() != 2 ) - { - out << "creature " << "invalid CREATURE:CASTE token: " << token << endl; + split_string(&tokens, token, ":"); + if (tokens.size() != 2) { + WARN(log).print("creature invalid CREATURE:CASTE token: %s\n", token.c_str()); + return -1; } - else - { - int16_t creature_idx = find_creature ( tokens[0] ); - if ( creature_idx < 0 ) - { - out << " creature invalid token " << tokens[0]; - } - else - { - food_idx = linear_index ( table.organic_types[mat_category], creature_idx ); - if ( tokens[1] == "MALE" ) - food_idx += 1; - if ( table.organic_types[mat_category][food_idx] == creature_idx ) - out << "creature " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; - else - { - out << "ERROR creature caste not found: " << token << " caste " << tokens[1] << " creature_idx(" << creature_idx << ") food_idx("<< food_idx << ")" << endl; - food_idx = -1; - } - } + int16_t creature_idx = find_creature(tokens[0]); + if (creature_idx < 0) { + WARN(log).print("creature invalid token %s\n", tokens[0].c_str()); + return -1; } - } - else - { - if ( !index_built ) - food_build_map ( out ); - MaterialInfo mat_info = food_mat_by_token ( out, token ); - int16_t type = mat_info.type; - int32_t index = mat_info.index; - auto it = food_index[mat_category].find ( std::make_pair ( type, index ) ); - if ( it != food_index[mat_category].end() ) - { - out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx(" << it->second << ")" << endl; - food_idx = it->second; - } - else - { - out << "matinfo: " << token << " type(" << mat_info.type << ") idx(" << mat_info.index << ") food_idx not found :(" << endl; + int16_t food_idx = linear_index(table.organic_types[mat_category], creature_idx); + if (tokens[1] == "MALE") + food_idx += 1; + if (table.organic_types[mat_category][food_idx] == creature_idx) { + DEBUG(log).print("creature %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); + return food_idx; } + WARN(log).print("creature caste not found: %s caste %s creature_idx(%d) food_idx(%d)\n", token.c_str(), tokens[1].c_str(), creature_idx, food_idx); + return -1; } - return food_idx; + + if (!index_built) + food_build_map(); + MaterialInfo mat_info = food_mat_by_token(token); + int16_t type = mat_info.type; + int32_t index = mat_info.index; + auto it = food_index[mat_category].find(std::make_pair(type, index)); + if (it != food_index[mat_category].end()) { + DEBUG(log).print("matinfo: %s type(%d) idx(%d) food_idx(%zd)\n", token.c_str(), mat_info.type, mat_info.index, it->second); + return it->second; + + } + + WARN(log).print("matinfo: %s type(%d) idx(%d) food_idx not found :(\n", token.c_str(), mat_info.type, mat_info.index); + return -1; } -MaterialInfo OrganicMatLookup::food_mat_by_token ( std::ostream &out, const std::string & token ) -{ +MaterialInfo OrganicMatLookup::food_mat_by_token(const std::string& token) { MaterialInfo mat_info; - mat_info.find ( token ); + mat_info.find(token); return mat_info; } bool OrganicMatLookup::index_built = false; -std::vector OrganicMatLookup::food_index = std::vector ( df::enum_traits< df::organic_mat_category >::last_item_value + 1 ); +std::vector OrganicMatLookup::food_index = std::vector(df::enum_traits< df::organic_mat_category >::last_item_value + 1); diff --git a/plugins/stockpiles/OrganicMatLookup.h b/plugins/stockpiles/OrganicMatLookup.h index ef49f3f3c..7ae65b17e 100644 --- a/plugins/stockpiles/OrganicMatLookup.h +++ b/plugins/stockpiles/OrganicMatLookup.h @@ -5,42 +5,42 @@ #include "df/organic_mat_category.h" namespace df { -struct creature_raw; -struct caste_raw; + struct creature_raw; + struct caste_raw; } /** * Helper class for mapping the various organic mats between their material indices * and their index in the stockpile_settings structures. */ -class OrganicMatLookup -{ +class OrganicMatLookup { -// pair of material type and index + // pair of material type and index typedef std::pair FoodMatPair; -// map for using type,index pairs to find the food index + // map for using type,index pairs to find the food index typedef std::map FoodMatMap; public: - struct FoodMat - { + struct FoodMat { DFHack::MaterialInfo material; - df::creature_raw *creature; - df::caste_raw * caste; - FoodMat() : material ( -1 ), creature ( 0 ), caste ( 0 ) {} + df::creature_raw* creature; + df::caste_raw* caste; + FoodMat(): material(-1), creature(0), caste(0) { } }; - static void food_mat_by_idx ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat & food_mat ); - static std::string food_token_by_idx ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx ); - static size_t food_max_size ( df::enums::organic_mat_category::organic_mat_category mat_category ); - static void food_build_map ( std::ostream &out ); + static void food_mat_by_idx(df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat); + static std::string food_token_by_idx(df::enums::organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx); - static int16_t food_idx_by_token ( std::ostream &out, df::enums::organic_mat_category::organic_mat_category mat_category, const std::string & token ); + static size_t food_max_size(df::enums::organic_mat_category::organic_mat_category mat_category); + static void food_build_map(); - static DFHack::MaterialInfo food_mat_by_token ( std::ostream &out, const std::string & token ); + static int16_t food_idx_by_token(df::enums::organic_mat_category::organic_mat_category mat_category, const std::string& token); + + static DFHack::MaterialInfo food_mat_by_token(const std::string& token); static bool index_built; static std::vector food_index; + private: OrganicMatLookup(); }; diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index c413613f6..65feb2711 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1,38 +1,34 @@ -#include "StockpileSerializer.h" - // stockpiles plugin #include "OrganicMatLookup.h" +#include "StockpileSerializer.h" #include "StockpileUtils.h" // dfhack +#include "Debug.h" #include "MiscUtils.h" +#include "modules/Items.h" // df #include "df/building_stockpilest.h" #include "df/inorganic_raw.h" -#include "df/creature_raw.h" -#include "df/caste_raw.h" -#include "df/material.h" -#include "df/inorganic_raw.h" -#include "df/plant_raw.h" -#include "df/stockpile_group_set.h" +#include "df/item_quality.h" #include -#include -#include -#include #include -#include -#include #include +#include #include #include +#include +#include +#include +#include "df/furniture_type.h" +#include "df/material.h" +#include "df/plant_raw.h" +#include "df/stockpile_group_set.h" // protobuf #include -#include - -using std::endl; using std::string; using std::vector; @@ -44,21 +40,12 @@ using df::global::world; using std::placeholders::_1; - - -int NullBuffer::overflow ( int c ) -{ - return c; +namespace DFHack { + DBG_EXTERN(stockpiles, log); } -NullStream::NullStream() : std::ostream ( &m_sb ) {} - -StockpileSerializer::StockpileSerializer ( df::building_stockpilest * stockpile ) - : mDebug ( false ) - , mOut ( 0 ) - , mNull() - , mPile ( stockpile ) -{ +StockpileSerializer::StockpileSerializer(df::building_stockpilest* stockpile) + : mPile(stockpile) { // build other_mats indices furniture_setup_other_mats(); @@ -67,106 +54,87 @@ StockpileSerializer::StockpileSerializer ( df::building_stockpilest * stockpile weapons_armor_setup_other_mats(); } -StockpileSerializer::~StockpileSerializer() {} +StockpileSerializer::~StockpileSerializer() { } -void StockpileSerializer::enable_debug ( std::ostream&out ) -{ - mDebug = true; - mOut = &out; -} - -bool StockpileSerializer::serialize_to_ostream ( std::ostream* output ) -{ - if ( output->fail( ) ) return false; +bool StockpileSerializer::serialize_to_ostream(std::ostream* output) { + if (output->fail()) + return false; mBuffer.Clear(); write(); { - io::OstreamOutputStream zero_copy_output ( output ); - if ( !mBuffer.SerializeToZeroCopyStream ( &zero_copy_output ) ) return false; + io::OstreamOutputStream zero_copy_output(output); + if (!mBuffer.SerializeToZeroCopyStream(&zero_copy_output)) + return false; } return output->good(); } -bool StockpileSerializer::serialize_to_file ( const std::string & file ) -{ - std::fstream output ( file, std::ios::out | std::ios::binary | std::ios::trunc ); - if ( output.fail() ) - { - debug() << "ERROR: failed to open file for writing: " << file << endl; +bool StockpileSerializer::serialize_to_file(const std::string& file) { + std::fstream output(file, std::ios::out | std::ios::binary | std::ios::trunc); + if (output.fail()) { + WARN(log).print("ERROR: failed to open file for writing: '%s'\n", file.c_str()); return false; } - return serialize_to_ostream ( &output ); + return serialize_to_ostream(&output); } -bool StockpileSerializer::parse_from_istream ( std::istream* input ) -{ - if ( input->fail( ) ) return false; +bool StockpileSerializer::parse_from_istream(std::istream* input) { + if (input->fail()) + return false; mBuffer.Clear(); - io::IstreamInputStream zero_copy_input ( input ); - const bool res = mBuffer.ParseFromZeroCopyStream ( &zero_copy_input ) && input->eof(); - if ( res ) read(); + io::IstreamInputStream zero_copy_input(input); + const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); + if (res) + read(); return res; } - -bool StockpileSerializer::unserialize_from_file ( const std::string & file ) -{ - std::fstream input ( file, std::ios::in | std::ios::binary ); - if ( input.fail() ) - { - debug() << "ERROR: failed to open file for reading: " << file << endl; +bool StockpileSerializer::unserialize_from_file(const std::string& file) { + std::fstream input(file, std::ios::in | std::ios::binary); + if (input.fail()) { + WARN(log).print("failed to open file for reading: '%s'\n", file.c_str()); return false; } - return parse_from_istream ( &input ); + return parse_from_istream(&input); } -std::ostream & StockpileSerializer::debug() -{ - if ( mDebug ) - return *mOut; - return mNull; -} - - -void StockpileSerializer::write() -{ - // debug() << "GROUP SET " << bitfield_to_string(mPile->settings.flags) << endl; +void StockpileSerializer::write() { + DEBUG(log).print("GROUP SET %s\n", bitfield_to_string(mPile->settings.flags).c_str()); write_general(); - if ( mPile->settings.flags.bits.animals ) + if (mPile->settings.flags.bits.animals) write_animals(); - if ( mPile->settings.flags.bits.food ) + if (mPile->settings.flags.bits.food) write_food(); - if ( mPile->settings.flags.bits.furniture ) + if (mPile->settings.flags.bits.furniture) write_furniture(); - if ( mPile->settings.flags.bits.refuse ) + if (mPile->settings.flags.bits.refuse) write_refuse(); - if ( mPile->settings.flags.bits.stone ) + if (mPile->settings.flags.bits.stone) write_stone(); - if ( mPile->settings.flags.bits.ammo ) + if (mPile->settings.flags.bits.ammo) write_ammo(); - if ( mPile->settings.flags.bits.coins ) + if (mPile->settings.flags.bits.coins) write_coins(); - if ( mPile->settings.flags.bits.bars_blocks ) + if (mPile->settings.flags.bits.bars_blocks) write_bars_blocks(); - if ( mPile->settings.flags.bits.gems ) + if (mPile->settings.flags.bits.gems) write_gems(); - if ( mPile->settings.flags.bits.finished_goods ) + if (mPile->settings.flags.bits.finished_goods) write_finished_goods(); - if ( mPile->settings.flags.bits.leather ) + if (mPile->settings.flags.bits.leather) write_leather(); - if ( mPile->settings.flags.bits.cloth ) + if (mPile->settings.flags.bits.cloth) write_cloth(); - if ( mPile->settings.flags.bits.wood ) + if (mPile->settings.flags.bits.wood) write_wood(); - if ( mPile->settings.flags.bits.weapons ) + if (mPile->settings.flags.bits.weapons) write_weapons(); - if ( mPile->settings.flags.bits.armor ) + if (mPile->settings.flags.bits.armor) write_armor(); } -void StockpileSerializer::read () -{ - debug() << endl << "==READ==" << endl; +void StockpileSerializer::read() { + DEBUG(log).print("==READ==\n"); read_general(); read_animals(); read_food(); @@ -185,361 +153,297 @@ void StockpileSerializer::read () read_armor(); } - -void StockpileSerializer::serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, organic_mat_category::organic_mat_category cat ) -{ - if ( !list ) - { - debug() << "serialize_list_organic_mat: list null" << endl; +void StockpileSerializer::serialize_list_organic_mat(FuncWriteExport add_value, const std::vector* list, organic_mat_category::organic_mat_category cat) { + if (!list) { + DEBUG(log).print("serialize_list_organic_mat: list null\n"); + return; } - for ( size_t i = 0; i < list->size(); ++i ) - { - if ( list->at ( i ) ) - { - std::string token = OrganicMatLookup::food_token_by_idx ( debug(), cat, i ); - if ( !token.empty() ) - { - add_value ( token ); - debug() << " organic_material " << i << " is " << token << endl; - } - else - { - debug() << "food mat invalid :(" << endl; + for (size_t i = 0; i < list->size(); ++i) { + if (list->at(i)) { + std::string token = OrganicMatLookup::food_token_by_idx(cat, i); + if (token.empty()) { + DEBUG(log).print("food mat invalid :(\n"); + continue; } + DEBUG(log).print("organic_material %zd is %s\n", i, token.c_str()); + add_value(token); } } } -void StockpileSerializer::unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, organic_mat_category::organic_mat_category cat ) -{ +void StockpileSerializer::unserialize_list_organic_mat(FuncReadImport get_value, size_t list_size, std::vector* pile_list, organic_mat_category::organic_mat_category cat) { pile_list->clear(); - pile_list->resize ( OrganicMatLookup::food_max_size ( cat ), '\0' ); - for ( size_t i = 0; i < list_size; ++i ) - { - std::string token = get_value ( i ); - int16_t idx = OrganicMatLookup::food_idx_by_token ( debug(), cat, token ); - debug() << " organic_material " << idx << " is " << token << endl; - if ( size_t(idx) >= pile_list->size() ) - { - debug() << "error organic mat index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + pile_list->resize(OrganicMatLookup::food_max_size(cat), '\0'); + for (size_t i = 0; i < list_size; ++i) { + std::string token = get_value(i); + int16_t idx = OrganicMatLookup::food_idx_by_token(cat, token); + DEBUG(log).print(" organic_material %d is %s\n", idx, token.c_str()); + if (size_t(idx) >= pile_list->size()) { + WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, pile_list->size()); continue; } - pile_list->at ( idx ) = 1; + pile_list->at(idx) = 1; } } - -void StockpileSerializer::serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) -{ +void StockpileSerializer::serialize_list_item_type(FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector& list) { using df::enums::item_type::item_type; using type_traits = df::enum_traits; - debug() << "item_type size = " << list.size() << " size limit = " << type_traits::last_item_value << " typecasted: " << ( size_t ) type_traits::last_item_value << endl; - for ( size_t i = 0; i <= ( size_t ) type_traits::last_item_value; ++i ) - { - if ( list.at ( i ) ) - { - const item_type type = ( item_type ) ( ( df::enum_traits::base_type ) i ); - std::string r_type ( type_traits::key_table[i+1] ); - if ( !is_allowed ( type ) ) continue; - add_value ( r_type ); - debug() << "item_type key_table[" << i+1 << "] type[" << ( int16_t ) type << "] is " << r_type <::base_type)i); + std::string r_type(type_traits::key_table[i + 1]); + if (!is_allowed(type)) + continue; + add_value(r_type); + DEBUG(log).print("item_type key_table[%zd] type[%d] is %s\n", i + 1, (int16_t)type, r_type.c_str()); } } - -void StockpileSerializer::unserialize_list_item_type ( FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) -{ +void StockpileSerializer::unserialize_list_item_type(FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { pile_list->clear(); - pile_list->resize ( 112, '\0' ); // TODO remove hardcoded list size value - for ( size_t i = 0; i < pile_list->size(); ++i ) - { - pile_list->at ( i ) = is_allowed ( ( item_type::item_type ) i ) ? 0 : 1; + pile_list->resize(112, '\0'); // TODO remove hardcoded list size value + for (size_t i = 0; i < pile_list->size(); ++i) { + pile_list->at(i) = is_allowed((item_type::item_type)i) ? 0 : 1; } using df::enums::item_type::item_type; df::enum_traits type_traits; - for ( int32_t i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); + for (int32_t i = 0; i < list_size; ++i) { + const std::string token = read_value(i); // subtract one because item_type starts at -1 - const df::enum_traits::base_type idx = linear_index ( debug(), type_traits, token ) - 1; - const item_type type = ( item_type ) idx; - if ( !is_allowed ( type ) ) continue; - debug() << " item_type " << idx << " is " << token << endl; - if ( size_t(idx) >= pile_list->size() ) - { - debug() << "error item_type index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + const df::enum_traits::base_type idx = linear_index(type_traits, token) - 1; + const item_type type = (item_type)idx; + if (!is_allowed(type)) + continue; + DEBUG(log).print("item_type %d is %s\n", idx, token.c_str()); + if (size_t(idx) >= pile_list->size()) { + WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, pile_list->size()); continue; } - pile_list->at ( idx ) = 1; + pile_list->at(idx) = 1; } } - -void StockpileSerializer::serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ) -{ +void StockpileSerializer::serialize_list_material(FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector& list) { MaterialInfo mi; - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - mi.decode ( 0, i ); - if ( !is_allowed ( mi ) ) continue; - debug() << " material " << i << " is " << mi.getToken() << endl; - add_value ( mi.getToken() ); + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i)) { + mi.decode(0, i); + if (!is_allowed(mi)) + continue; + DEBUG(log).print("material %zd is %s\n", i, mi.getToken().c_str()); + add_value(mi.getToken()); } } } - -void StockpileSerializer::unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) -{ +void StockpileSerializer::unserialize_list_material(FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { // we initialize all possible (allowed) values to 0, // then all other not-allowed values to 1 // why? because that's how the memory is in DF before // we muck with it. std::set idx_set; pile_list->clear(); - pile_list->resize ( world->raws.inorganics.size(), 0 ); - for ( size_t i = 0; i < pile_list->size(); ++i ) - { - MaterialInfo mi ( 0, i ); - pile_list->at ( i ) = is_allowed ( mi ) ? 0 : 1; + pile_list->resize(world->raws.inorganics.size(), 0); + for (size_t i = 0; i < pile_list->size(); ++i) { + MaterialInfo mi(0, i); + pile_list->at(i) = is_allowed(mi) ? 0 : 1; } - for ( int i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); + for (int i = 0; i < list_size; ++i) { + const std::string token = read_value(i); MaterialInfo mi; - mi.find ( token ); - if ( !is_allowed ( mi ) ) continue; - debug() << " material " << mi.index << " is " << token << endl; - if ( size_t(mi.index) >= pile_list->size() ) - { - debug() << "error material index too large! idx[" << mi.index << "] max_size[" << pile_list->size() << "]" << endl; + mi.find(token); + if (!is_allowed(mi)) + continue; + DEBUG(log).print("material %d is %s\n", mi.index, token.c_str()); + if (size_t(mi.index) >= pile_list->size()) { + WARN(log).print("material index too large! idx[%d] max_size[%zd]\n", mi.index, pile_list->size()); continue; } - pile_list->at ( mi.index ) = 1; + pile_list->at(mi.index) = 1; } } - -void StockpileSerializer::serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ) -{ +void StockpileSerializer::serialize_list_quality(FuncWriteExport add_value, const bool(&quality_list)[7]) { using df::enums::item_quality::item_quality; using quality_traits = df::enum_traits; - for ( size_t i = 0; i < 7; ++i ) - { - if ( quality_list[i] ) - { - const std::string f_type ( quality_traits::key_table[i] ); - add_value ( f_type ); - debug() << " quality: " << i << " is " << f_type < 0 && list_size <= 7 ) - { +void StockpileSerializer::unserialize_list_quality(FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { + quality_clear(pile_list); + if (list_size > 0 && list_size <= 7) { using df::enums::item_quality::item_quality; df::enum_traits quality_traits; - for ( int i = 0; i < list_size; ++i ) - { - const std::string quality = read_value ( i ); - df::enum_traits::base_type idx = linear_index ( debug(), quality_traits, quality ); - if ( idx < 0 ) - { - debug() << " invalid quality token " << quality << endl; + for (int i = 0; i < list_size; ++i) { + const std::string quality = read_value(i); + df::enum_traits::base_type idx = linear_index(quality_traits, quality); + if (idx < 0) { + WARN(log).print("invalid quality token: %s\n", quality.c_str()); continue; } - debug() << " quality: " << idx << " is " << quality << endl; + DEBUG(log).print("quality: %d is %s\n", idx, quality.c_str()); pile_list[idx] = true; } } } - -void StockpileSerializer::serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ) -{ - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - const std::string token = other_mats_index ( other_mats, i ); - if ( token.empty() ) - { - debug() << " invalid other material with index " << i << endl; +void StockpileSerializer::serialize_list_other_mats(const std::map other_mats, FuncWriteExport add_value, std::vector list) { + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i)) { + const std::string token = other_mats_index(other_mats, i); + if (token.empty()) { + WARN(log).print("invalid other material with index %zd\n", i); continue; } - add_value ( token ); - debug() << " other mats " << i << " is " << token << endl; + add_value(token); + DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); } } } - -void StockpileSerializer::unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ) -{ +void StockpileSerializer::unserialize_list_other_mats(const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { pile_list->clear(); - pile_list->resize ( other_mats.size(), '\0' ); - for ( int i = 0; i < list_size; ++i ) - { - const std::string token = read_value ( i ); - size_t idx = other_mats_token ( other_mats, token ); - if ( idx < 0 ) - { - debug() << "invalid other mat with token " << token; + pile_list->resize(other_mats.size(), '\0'); + for (int i = 0; i < list_size; ++i) { + const std::string token = read_value(i); + size_t idx = other_mats_token(other_mats, token); + if (idx < 0) { + WARN(log).print("invalid other mat with token %s\n", token.c_str()); continue; } - debug() << " other_mats " << idx << " is " << token << endl; - if ( idx >= pile_list->size() ) - { - debug() << "error other_mats index too large! idx[" << idx << "] max_size[" << pile_list->size() << "]" << endl; + DEBUG(log).print("other_mats %zd is %s\n", idx, token.c_str()); + if (idx >= pile_list->size()) { + WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, pile_list->size()); continue; } - pile_list->at ( idx ) = 1; + pile_list->at(idx) = 1; } } - - -void StockpileSerializer::serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type ) -{ - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) ) - { - const df::itemdef *a = items.at ( i ); +void StockpileSerializer::serialize_list_itemdef(FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type) { + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i)) { + const df::itemdef* a = items.at(i); // skip procedurally generated items - if ( a->base_flags.is_set ( df::itemdef_flags::GENERATED ) ) continue; + if (a->base_flags.is_set(df::itemdef_flags::GENERATED)) + continue; ItemTypeInfo ii; - if ( !ii.decode ( type, i ) ) continue; - add_value ( ii.getToken() ); - debug() << " itemdef type" << i << " is " << ii.getToken() << endl; + if (!ii.decode(type, i)) + continue; + add_value(ii.getToken()); + DEBUG(log).print("itemdef type %zd is %s\n", i, ii.getToken().c_str()); } } } - -void StockpileSerializer::unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, item_type::item_type type ) -{ +void StockpileSerializer::unserialize_list_itemdef(FuncReadImport read_value, int32_t list_size, std::vector* pile_list, item_type::item_type type) { pile_list->clear(); - pile_list->resize ( Items::getSubtypeCount ( type ), '\0' ); - for ( int i = 0; i < list_size; ++i ) - { - std::string token = read_value ( i ); + pile_list->resize(Items::getSubtypeCount(type), '\0'); + for (int i = 0; i < list_size; ++i) { + std::string token = read_value(i); ItemTypeInfo ii; - if ( !ii.find ( token ) ) continue; - debug() << " itemdef " << ii.subtype << " is " << token << endl; - if ( size_t(ii.subtype) >= pile_list->size() ) - { - debug() << "error itemdef index too large! idx[" << ii.subtype << "] max_size[" << pile_list->size() << "]" << endl; + if (!ii.find(token)) + continue; + DEBUG(log).print("itemdef %d is %s\n", ii.subtype, token.c_str()); + if (size_t(ii.subtype) >= pile_list->size()) { + WARN(log).print("itemdef index too large! idx[%d] max_size[%zd]\n", ii.subtype, pile_list->size()); continue; } - pile_list->at ( ii.subtype ) = 1; + pile_list->at(ii.subtype) = 1; } } - -std::string StockpileSerializer::other_mats_index ( const std::map other_mats, int idx ) -{ - auto it = other_mats.find ( idx ); - if ( it == other_mats.end() ) +std::string StockpileSerializer::other_mats_index(const std::map other_mats, int idx) { + auto it = other_mats.find(idx); + if (it == other_mats.end()) return std::string(); return it->second; } -int StockpileSerializer::other_mats_token ( const std::map other_mats, const std::string & token ) -{ - for ( auto it = other_mats.begin(); it != other_mats.end(); ++it ) - { - if ( it->second == token ) +int StockpileSerializer::other_mats_token(const std::map other_mats, const std::string& token) { + for (auto it = other_mats.begin(); it != other_mats.end(); ++it) { + if (it->second == token) return it->first; } return -1; } -void StockpileSerializer::write_general() -{ - mBuffer.set_max_bins ( mPile->max_bins ); - mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows ); - mBuffer.set_max_barrels ( mPile->max_barrels ); - mBuffer.set_use_links_only ( mPile->use_links_only ); - mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic ); - mBuffer.set_allow_organic ( mPile->settings.allow_organic ); - mBuffer.set_corpses ( mPile->settings.flags.bits.corpses ); +void StockpileSerializer::write_general() { + mBuffer.set_max_bins(mPile->max_bins); + mBuffer.set_max_wheelbarrows(mPile->max_wheelbarrows); + mBuffer.set_max_barrels(mPile->max_barrels); + mBuffer.set_use_links_only(mPile->use_links_only); + mBuffer.set_allow_inorganic(mPile->settings.allow_inorganic); + mBuffer.set_allow_organic(mPile->settings.allow_organic); + mBuffer.set_corpses(mPile->settings.flags.bits.corpses); } -void StockpileSerializer::read_general() -{ - if ( mBuffer.has_max_bins() ) +void StockpileSerializer::read_general() { + if (mBuffer.has_max_bins()) mPile->max_bins = mBuffer.max_bins(); - if ( mBuffer.has_max_wheelbarrows() ) + if (mBuffer.has_max_wheelbarrows()) mPile->max_wheelbarrows = mBuffer.max_wheelbarrows(); - if ( mBuffer.has_max_barrels() ) + if (mBuffer.has_max_barrels()) mPile->max_barrels = mBuffer.max_barrels(); - if ( mBuffer.has_use_links_only() ) + if (mBuffer.has_use_links_only()) mPile->use_links_only = mBuffer.use_links_only(); - if ( mBuffer.has_allow_inorganic() ) + if (mBuffer.has_allow_inorganic()) mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); - if ( mBuffer.has_allow_organic() ) + if (mBuffer.has_allow_organic()) mPile->settings.allow_organic = mBuffer.allow_organic(); - if ( mBuffer.has_corpses() ) + if (mBuffer.has_corpses()) mPile->settings.flags.bits.corpses = mBuffer.corpses(); } -void StockpileSerializer::write_animals() -{ - StockpileSettings::AnimalsSet *animals= mBuffer.mutable_animals(); - animals->set_empty_cages ( mPile->settings.animals.empty_cages ); - animals->set_empty_traps ( mPile->settings.animals.empty_traps ); - for ( size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i ) - { - if ( mPile->settings.animals.enabled.at ( i ) == 1 ) - { - df::creature_raw* r = find_creature ( i ); - debug() << "creature "<< r->creature_id << " " << i << endl; - animals->add_enabled ( r->creature_id ); +void StockpileSerializer::write_animals() { + StockpileSettings::AnimalsSet* animals = mBuffer.mutable_animals(); + animals->set_empty_cages(mPile->settings.animals.empty_cages); + animals->set_empty_traps(mPile->settings.animals.empty_traps); + for (size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i) { + if (mPile->settings.animals.enabled.at(i) == 1) { + df::creature_raw* r = find_creature(i); + DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + animals->add_enabled(r->creature_id); } } } -void StockpileSerializer::read_animals() -{ - if ( mBuffer.has_animals() ) - { +void StockpileSerializer::read_animals() { + if (mBuffer.has_animals()) { mPile->settings.flags.bits.animals = 1; - debug() << "animals:" << endl; + DEBUG(log).print("animals:\n"); mPile->settings.animals.empty_cages = mBuffer.animals().empty_cages(); mPile->settings.animals.empty_traps = mBuffer.animals().empty_traps(); mPile->settings.animals.enabled.clear(); - mPile->settings.animals.enabled.resize ( world->raws.creatures.all.size(), '\0' ); - debug() << " pile has " << mPile->settings.animals.enabled.size() << endl; - for ( auto i = 0; i < mBuffer.animals().enabled_size(); ++i ) - { - std::string id = mBuffer.animals().enabled ( i ); - int idx = find_creature ( id ); - debug() << id << " " << idx << endl; - if ( idx < 0 || size_t(idx) >= mPile->settings.animals.enabled.size() ) - { - debug() << "WARNING: animal index invalid: " << idx << endl; + mPile->settings.animals.enabled.resize(world->raws.creatures.all.size(), '\0'); + DEBUG(log).print("pile has %zd\n", mPile->settings.animals.enabled.size()); + for (auto i = 0; i < mBuffer.animals().enabled_size(); ++i) { + std::string id = mBuffer.animals().enabled(i); + int idx = find_creature(id); + DEBUG(log).print("%s %d\n", id.c_str(), idx); + if (idx < 0 || size_t(idx) >= mPile->settings.animals.enabled.size()) { + WARN(log).print("animal index invalid: %d\n", idx); continue; } - mPile->settings.animals.enabled.at ( idx ) = ( char ) 1; + mPile->settings.animals.enabled.at(idx) = (char)1; } } - else - { + else { mPile->settings.animals.enabled.clear(); mPile->settings.flags.bits.animals = 0; mPile->settings.animals.empty_cages = false; @@ -547,183 +451,162 @@ void StockpileSerializer::read_animals() } } -StockpileSerializer::food_pair StockpileSerializer::food_map ( organic_mat_category::organic_mat_category cat ) -{ +StockpileSerializer::food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { using df::enums::organic_mat_category::organic_mat_category; using namespace std::placeholders; - switch ( cat ) - { + switch (cat) { case organic_mat_category::Meat: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_meat ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_meat(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().meat ( idx ); }; - return food_pair ( setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; + return food_pair(setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); } case organic_mat_category::Fish: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_fish ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_fish(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().fish ( idx ); }; - return food_pair ( setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; + return food_pair(setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_unprepared_fish ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_unprepared_fish(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().unprepared_fish ( idx ); }; - return food_pair ( setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; + return food_pair(setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_egg ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_egg(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().egg ( idx ); }; - return food_pair ( setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; + return food_pair(setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); } case organic_mat_category::Plants: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_plants ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_plants(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().plants ( idx ); }; - return food_pair ( setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; + return food_pair(setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); } case organic_mat_category::PlantDrink: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_drink_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_drink_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; + return food_pair(setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); } case organic_mat_category::CreatureDrink: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_drink_animal ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_drink_animal(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().drink_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; + return food_pair(setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); } case organic_mat_category::PlantCheese: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_cheese_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_cheese_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; + return food_pair(setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); } case organic_mat_category::CreatureCheese: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_cheese_animal ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_cheese_animal(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().cheese_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; + return food_pair(setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); } case organic_mat_category::Seed: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_seeds ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_seeds(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().seeds ( idx ); }; - return food_pair ( setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; + return food_pair(setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); } case organic_mat_category::Leaf: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_leaves ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_leaves(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().leaves ( idx ); }; - return food_pair ( setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; + return food_pair(setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); } case organic_mat_category::PlantPowder: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_powder_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_powder_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; + return food_pair(setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); } case organic_mat_category::CreaturePowder: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_powder_creature ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_powder_creature(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().powder_creature ( idx ); }; - return food_pair ( setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; + return food_pair(setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); } case organic_mat_category::Glob: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; + return food_pair(setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); } case organic_mat_category::PlantLiquid: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_plant ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_plant(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_plant ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); } case organic_mat_category::CreatureLiquid: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_animal ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_animal(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_animal ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); } case organic_mat_category::MiscLiquid: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_liquid_misc ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_misc(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().liquid_misc ( idx ); }; - return food_pair ( setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); } case organic_mat_category::Paste: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob_paste ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob_paste(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_paste ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; + return food_pair(setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); } case organic_mat_category::Pressed: { - FuncWriteExport setter = [=] ( const std::string &id ) - { - mBuffer.mutable_food()->add_glob_pressed ( id ); + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob_pressed(id); }; - FuncReadImport getter = [=] ( size_t idx ) -> std::string { return mBuffer.food().glob_pressed ( idx ); }; - return food_pair ( setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size() ); + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; + return food_pair(setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); } case organic_mat_category::Leather: case organic_mat_category::Silk: @@ -750,55 +633,49 @@ StockpileSerializer::food_pair StockpileSerializer::food_map ( organic_mat_categ return food_pair(); } - -void StockpileSerializer::write_food() -{ - StockpileSettings::FoodSet *food = mBuffer.mutable_food(); - debug() << " food: " << endl; - food->set_prepared_meals ( mPile->settings.food.prepared_meals ); +void StockpileSerializer::write_food() { + StockpileSettings::FoodSet* food = mBuffer.mutable_food(); + DEBUG(log).print("food:\n"); + food->set_prepared_meals(mPile->settings.food.prepared_meals); using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; - for ( int32_t mat_category = traits::first_item_value; mat_category ; - if ( mBuffer.has_food() ) - { + if (mBuffer.has_food()) { mPile->settings.flags.bits.food = 1; const StockpileSettings::FoodSet food = mBuffer.food(); - debug() << "food:" <settings.food.prepared_meals = food.prepared_meals(); else mPile->settings.food.prepared_meals = true; - debug() << " prepared_meals: " << mPile->settings.food.prepared_meals << endl; + DEBUG(log).print("prepared_meals: %d\n", mPile->settings.food.prepared_meals); - for ( int32_t mat_category = traits::first_item_value; mat_category clear(); } mPile->settings.flags.bits.food = 0; @@ -806,327 +683,249 @@ void StockpileSerializer::read_food() } } -void StockpileSerializer::furniture_setup_other_mats() -{ - mOtherMatsFurniture.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 3,"TOOTH" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 4,"HORN" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 5,"PEARL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 6,"SHELL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 7,"LEATHER" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 8,"SILK" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 9,"AMBER" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 10,"CORAL" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); - mOtherMatsFurniture.insert ( std::make_pair ( 14,"YARN" ) ); +void StockpileSerializer::furniture_setup_other_mats() { + mOtherMatsFurniture.insert(std::make_pair(0, "WOOD")); + mOtherMatsFurniture.insert(std::make_pair(1, "PLANT_CLOTH")); + mOtherMatsFurniture.insert(std::make_pair(2, "BONE")); + mOtherMatsFurniture.insert(std::make_pair(3, "TOOTH")); + mOtherMatsFurniture.insert(std::make_pair(4, "HORN")); + mOtherMatsFurniture.insert(std::make_pair(5, "PEARL")); + mOtherMatsFurniture.insert(std::make_pair(6, "SHELL")); + mOtherMatsFurniture.insert(std::make_pair(7, "LEATHER")); + mOtherMatsFurniture.insert(std::make_pair(8, "SILK")); + mOtherMatsFurniture.insert(std::make_pair(9, "AMBER")); + mOtherMatsFurniture.insert(std::make_pair(10, "CORAL")); + mOtherMatsFurniture.insert(std::make_pair(11, "GREEN_GLASS")); + mOtherMatsFurniture.insert(std::make_pair(12, "CLEAR_GLASS")); + mOtherMatsFurniture.insert(std::make_pair(13, "CRYSTAL_GLASS")); + mOtherMatsFurniture.insert(std::make_pair(14, "YARN")); } -void StockpileSerializer::write_furniture() -{ - StockpileSettings::FurnitureSet *furniture= mBuffer.mutable_furniture(); +void StockpileSerializer::write_furniture() { + StockpileSettings::FurnitureSet* furniture = mBuffer.mutable_furniture(); // FURNITURE type using df::enums::furniture_type::furniture_type; using type_traits = df::enum_traits; - for ( size_t i = 0; i < mPile->settings.furniture.type.size(); ++i ) - { - if ( mPile->settings.furniture.type.at ( i ) ) - { - std::string f_type ( type_traits::key_table[i] ); - furniture->add_type ( f_type ); - debug() << "furniture_type " << i << " is " << f_type <settings.furniture.type.size(); ++i) { + if (mPile->settings.furniture.type.at(i)) { + std::string f_type(type_traits::key_table[i]); + furniture->add_type(f_type); + DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); } } // metal, stone/clay materials - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::furniture_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - furniture->add_mats ( token ); - }, mPile->settings.furniture.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::furniture_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { furniture->add_mats(token); }, + mPile->settings.furniture.mats); // other mats - serialize_list_other_mats ( mOtherMatsFurniture, [=] ( const std::string &token ) - { - furniture->add_other_mats ( token ); - }, mPile->settings.furniture.other_mats ); + serialize_list_other_mats( + mOtherMatsFurniture, [=](const std::string& token) { furniture->add_other_mats(token); }, + mPile->settings.furniture.other_mats); - serialize_list_quality ( [=] ( const std::string &token ) - { - furniture->add_quality_core ( token ); - }, mPile->settings.furniture.quality_core ); - serialize_list_quality ( [=] ( const std::string &token ) - { - furniture->add_quality_total ( token ); - }, mPile->settings.furniture.quality_total ); + serialize_list_quality([=](const std::string& token) { furniture->add_quality_core(token); }, + mPile->settings.furniture.quality_core); + serialize_list_quality([=](const std::string& token) { furniture->add_quality_total(token); }, + mPile->settings.furniture.quality_total); } -bool StockpileSerializer::furniture_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ); +bool StockpileSerializer::furniture_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::read_furniture() -{ - if ( mBuffer.has_furniture() ) - { +void StockpileSerializer::read_furniture() { + if (mBuffer.has_furniture()) { mPile->settings.flags.bits.furniture = 1; const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); - debug() << "furniture:" < type_traits; mPile->settings.furniture.type.clear(); - mPile->settings.furniture.type.resize ( type_traits.last_item_value+1, '\0' ); - if ( furniture.type_size() > 0 ) - { - for ( int i = 0; i < furniture.type_size(); ++i ) - { - const std::string type = furniture.type ( i ); - df::enum_traits::base_type idx = linear_index ( debug(), type_traits, type ); - debug() << " type " << idx << " is " << type << endl; - if ( idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size() ) - { - debug() << "WARNING: furniture type index invalid " << type << ", idx=" << idx << endl; + mPile->settings.furniture.type.resize(type_traits.last_item_value + 1, '\0'); + if (furniture.type_size() > 0) { + for (int i = 0; i < furniture.type_size(); ++i) { + const std::string type = furniture.type(i); + df::enum_traits::base_type idx = linear_index(type_traits, type); + DEBUG(log).print("type %d is %s\n", idx, type.c_str()); + if (idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size()) { + WARN(log).print("furniture type index invalid %s, idx=%d\n", type.c_str(), idx); continue; } - mPile->settings.furniture.type.at ( idx ) = 1; + mPile->settings.furniture.type.at(idx) = 1; } } - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::furniture_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return furniture.mats ( idx ); - }, furniture.mats_size(), &mPile->settings.furniture.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::furniture_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return furniture.mats(idx); }, + furniture.mats_size(), &mPile->settings.furniture.mats); // other materials - unserialize_list_other_mats ( mOtherMatsFurniture, [=] ( const size_t & idx ) -> const std::string& - { - return furniture.other_mats ( idx ); - }, furniture.other_mats_size(), &mPile->settings.furniture.other_mats ); + unserialize_list_other_mats( + mOtherMatsFurniture, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, + furniture.other_mats_size(), &mPile->settings.furniture.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return furniture.quality_core ( idx ); - }, furniture.quality_core_size(), mPile->settings.furniture.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_core(idx); }, + furniture.quality_core_size(), mPile->settings.furniture.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return furniture.quality_total ( idx ); - }, furniture.quality_total_size(), mPile->settings.furniture.quality_total ); - + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_total(idx); }, + furniture.quality_total_size(), mPile->settings.furniture.quality_total); } - else - { + else { mPile->settings.flags.bits.furniture = 0; mPile->settings.furniture.type.clear(); mPile->settings.furniture.other_mats.clear(); mPile->settings.furniture.mats.clear(); - quality_clear ( mPile->settings.furniture.quality_core ); - quality_clear ( mPile->settings.furniture.quality_total ); + quality_clear(mPile->settings.furniture.quality_core); + quality_clear(mPile->settings.furniture.quality_total); } } -bool StockpileSerializer::refuse_creature_is_allowed ( const df::creature_raw *raw ) -{ - if ( !raw ) return false; +bool StockpileSerializer::refuse_creature_is_allowed(const df::creature_raw* raw) { + if (!raw) + return false; // wagon and generated creatures not allowed, except angels const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; - const bool is_generated = raw->flags.is_set ( creature_raw_flags::GENERATED ); - const bool is_angel = is_generated && raw->creature_id.find ( "DIVINE_" ) != std::string::npos; - return !is_wagon && ! ( is_generated && !is_angel ); + const bool is_generated = raw->flags.is_set(creature_raw_flags::GENERATED); + const bool is_angel = is_generated && raw->creature_id.find("DIVINE_") != std::string::npos; + return !is_wagon && !(is_generated && !is_angel); } -void StockpileSerializer::refuse_write_helper ( std::function add_value, const vector & list ) -{ - for ( size_t i = 0; i < list.size(); ++i ) - { - if ( list.at ( i ) == 1 ) - { - df::creature_raw* r = find_creature ( i ); +void StockpileSerializer::refuse_write_helper(std::function add_value, const vector& list) { + for (size_t i = 0; i < list.size(); ++i) { + if (list.at(i) == 1) { + df::creature_raw* r = find_creature(i); // skip forgotten beasts, titans, demons, and night creatures - if ( !refuse_creature_is_allowed ( r ) ) continue; - debug() << "creature "<< r->creature_id << " " << i << endl; - add_value ( r->creature_id ); + if (!refuse_creature_is_allowed(r)) + continue; + DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + add_value(r->creature_id); } } } -bool StockpileSerializer::refuse_type_is_allowed ( item_type::item_type type ) -{ - if ( type == item_type::NONE - || type == item_type::BAR - || type == item_type::SMALLGEM - || type == item_type::BLOCKS - || type == item_type::ROUGH - || type == item_type::BOULDER - || type == item_type::CORPSE - || type == item_type::CORPSEPIECE - || type == item_type::ROCK - || type == item_type::ORTHOPEDIC_CAST - ) return false; +bool StockpileSerializer::refuse_type_is_allowed(item_type::item_type type) { + if (type == item_type::NONE || type == item_type::BAR || type == item_type::SMALLGEM || type == item_type::BLOCKS || type == item_type::ROUGH || type == item_type::BOULDER || type == item_type::CORPSE || type == item_type::CORPSEPIECE || type == item_type::ROCK || type == item_type::ORTHOPEDIC_CAST) + return false; return true; } - -void StockpileSerializer::write_refuse() -{ - StockpileSettings::RefuseSet *refuse = mBuffer.mutable_refuse(); - refuse->set_fresh_raw_hide ( mPile->settings.refuse.fresh_raw_hide ); - refuse->set_rotten_raw_hide ( mPile->settings.refuse.rotten_raw_hide ); +void StockpileSerializer::write_refuse() { + DEBUG(log).print("refuse:\n"); + StockpileSettings::RefuseSet* refuse = mBuffer.mutable_refuse(); + refuse->set_fresh_raw_hide(mPile->settings.refuse.fresh_raw_hide); + refuse->set_rotten_raw_hide(mPile->settings.refuse.rotten_raw_hide); // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); - serialize_list_item_type ( filter, [=] ( const std::string &token ) - { - refuse->add_type ( token ); - }, mPile->settings.refuse.type ); + DEBUG(log).print("getting types\n"); + FuncItemAllowed filter = std::bind(&StockpileSerializer::refuse_type_is_allowed, this, _1); + serialize_list_item_type( + filter, [=](const std::string& token) { + DEBUG(log).print("adding type: %s\n", token.c_str()); + refuse->add_type(token); + }, + mPile->settings.refuse.type); // corpses - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_corpses ( id ); - }, mPile->settings.refuse.corpses ); + refuse_write_helper([=](const std::string& id) { refuse->add_corpses(id); }, + mPile->settings.refuse.corpses); // body_parts - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_body_parts ( id ); - }, mPile->settings.refuse.body_parts ); + refuse_write_helper([=](const std::string& id) { refuse->add_body_parts(id); }, + mPile->settings.refuse.body_parts); // skulls - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_skulls ( id ); - }, mPile->settings.refuse.skulls ); + refuse_write_helper([=](const std::string& id) { refuse->add_skulls(id); }, + mPile->settings.refuse.skulls); // bones - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_bones ( id ); - }, mPile->settings.refuse.bones ); + refuse_write_helper([=](const std::string& id) { refuse->add_bones(id); }, + mPile->settings.refuse.bones); // hair - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_hair ( id ); - }, mPile->settings.refuse.hair ); + refuse_write_helper([=](const std::string& id) { refuse->add_hair(id); }, + mPile->settings.refuse.hair); // shells - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_shells ( id ); - }, mPile->settings.refuse.shells ); + refuse_write_helper([=](const std::string& id) { refuse->add_shells(id); }, + mPile->settings.refuse.shells); // teeth - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_teeth ( id ); - }, mPile->settings.refuse.teeth ); + refuse_write_helper([=](const std::string& id) { refuse->add_teeth(id); }, + mPile->settings.refuse.teeth); // horns - refuse_write_helper ( [=] ( const std::string &id ) - { - refuse->add_horns ( id ); - }, mPile->settings.refuse.horns ); + refuse_write_helper([=](const std::string& id) { refuse->add_horns(id); }, + mPile->settings.refuse.horns); } -void StockpileSerializer::refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ) -{ +void StockpileSerializer::refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list) { pile_list->clear(); - pile_list->resize ( world->raws.creatures.all.size(), '\0' ); - if ( list_size > 0 ) - { - for ( size_t i = 0; i < list_size; ++i ) - { - const std::string creature_id = get_value ( i ); - const int idx = find_creature ( creature_id ); - const df::creature_raw* creature = find_creature ( idx ); - if ( idx < 0 || !refuse_creature_is_allowed ( creature ) || size_t(idx) >= pile_list->size() ) - { - debug() << "WARNING invalid refuse creature " << creature_id << ", idx=" << idx << endl; + pile_list->resize(world->raws.creatures.all.size(), '\0'); + if (list_size > 0) { + for (size_t i = 0; i < list_size; ++i) { + const std::string creature_id = get_value(i); + const int idx = find_creature(creature_id); + const df::creature_raw* creature = find_creature(idx); + if (idx < 0 || !refuse_creature_is_allowed(creature) || size_t(idx) >= pile_list->size()) { + WARN(log).print("invalid refuse creature %s, idx=%d\n", creature_id.c_str(), idx); continue; } - debug() << " creature " << idx << " is " << creature_id << endl; - pile_list->at ( idx ) = 1; + DEBUG(log).print("creature %d is %s\n", idx, creature_id.c_str()); + pile_list->at(idx) = 1; } } } - - -void StockpileSerializer::read_refuse() -{ - if ( mBuffer.has_refuse() ) - { +void StockpileSerializer::read_refuse() { + if (mBuffer.has_refuse()) { mPile->settings.flags.bits.refuse = 1; const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); - debug() << "refuse: " <settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); - mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); + DEBUG(log).print("refuse:\n"); + DEBUG(log).print(" fresh hide %d\n", refuse.fresh_raw_hide()); + DEBUG(log).print(" rotten hide %d\n", refuse.rotten_raw_hide()); + mPile->settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); + mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::refuse_type_is_allowed, this, _1 ); - unserialize_list_item_type ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return refuse.type ( idx ); - }, refuse.type_size(), &mPile->settings.refuse.type ); + FuncItemAllowed filter = std::bind(&StockpileSerializer::refuse_type_is_allowed, this, _1); + unserialize_list_item_type( + filter, [=](const size_t& idx) -> const std::string& { return refuse.type(idx); }, + refuse.type_size(), &mPile->settings.refuse.type); // corpses - debug() << " corpses" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.corpses ( idx ); - }, refuse.corpses_size(), &mPile->settings.refuse.corpses ); + DEBUG(log).print(" corpses\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.corpses(idx); }, + refuse.corpses_size(), &mPile->settings.refuse.corpses); // body_parts - debug() << " body_parts" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.body_parts ( idx ); - }, refuse.body_parts_size(), &mPile->settings.refuse.body_parts ); + DEBUG(log).print(" body_parts\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.body_parts(idx); }, + refuse.body_parts_size(), &mPile->settings.refuse.body_parts); // skulls - debug() << " skulls" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.skulls ( idx ); - }, refuse.skulls_size(), &mPile->settings.refuse.skulls ); + DEBUG(log).print(" skulls\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.skulls(idx); }, + refuse.skulls_size(), &mPile->settings.refuse.skulls); // bones - debug() << " bones" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.bones ( idx ); - }, refuse.bones_size(), &mPile->settings.refuse.bones ); + DEBUG(log).print(" bones\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.bones(idx); }, + refuse.bones_size(), &mPile->settings.refuse.bones); // hair - debug() << " hair" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.hair ( idx ); - }, refuse.hair_size(), &mPile->settings.refuse.hair ); + DEBUG(log).print(" hair\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.hair(idx); }, + refuse.hair_size(), &mPile->settings.refuse.hair); // shells - debug() << " shells" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.shells ( idx ); - }, refuse.shells_size(), &mPile->settings.refuse.shells ); + DEBUG(log).print(" shells\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.shells(idx); }, + refuse.shells_size(), &mPile->settings.refuse.shells); // teeth - debug() << " teeth" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.teeth ( idx ); - }, refuse.teeth_size(), &mPile->settings.refuse.teeth ); + DEBUG(log).print(" teeth\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.teeth(idx); }, + refuse.teeth_size(), &mPile->settings.refuse.teeth); // horns - debug() << " horns" << endl; - refuse_read_helper ( [=] ( const size_t & idx ) -> const std::string& - { - return refuse.horns ( idx ); - }, refuse.horns_size(), &mPile->settings.refuse.horns ); + DEBUG(log).print(" horns\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.horns(idx); }, + refuse.horns_size(), &mPile->settings.refuse.horns); } - else - { + else { mPile->settings.flags.bits.refuse = 0; mPile->settings.refuse.type.clear(); mPile->settings.refuse.corpses.clear(); @@ -1142,287 +941,232 @@ void StockpileSerializer::read_refuse() } } -bool StockpileSerializer::stone_is_allowed ( const MaterialInfo &mi ) -{ - if ( !mi.isValid() ) return false; - const bool is_allowed_soil = mi.inorganic->flags.is_set ( inorganic_flags::SOIL ) && !mi.inorganic->flags.is_set ( inorganic_flags::AQUIFER ); - const bool is_allowed_stone = mi.material->flags.is_set ( material_flags::IS_STONE ) && !mi.material->flags.is_set ( material_flags::NO_STONE_STOCKPILE ); - return is_allowed_soil || is_allowed_stone; +bool StockpileSerializer::stone_is_allowed(const MaterialInfo& mi) { + if (!mi.isValid()) + return false; + const bool is_allowed_soil = mi.inorganic->flags.is_set(inorganic_flags::SOIL) && !mi.inorganic->flags.is_set(inorganic_flags::AQUIFER); + const bool is_allowed_stone = mi.material->flags.is_set(material_flags::IS_STONE) && !mi.material->flags.is_set(material_flags::NO_STONE_STOCKPILE); + return is_allowed_soil || is_allowed_stone; } -void StockpileSerializer::write_stone() -{ - StockpileSettings::StoneSet *stone= mBuffer.mutable_stone(); +void StockpileSerializer::write_stone() { + StockpileSettings::StoneSet* stone = mBuffer.mutable_stone(); - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::stone_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - stone->add_mats ( token ); - }, mPile->settings.stone.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { stone->add_mats(token); }, + mPile->settings.stone.mats); } -void StockpileSerializer::read_stone() -{ - if ( mBuffer.has_stone() ) - { +void StockpileSerializer::read_stone() { + if (mBuffer.has_stone()) { mPile->settings.flags.bits.stone = 1; const StockpileSettings::StoneSet stone = mBuffer.stone(); - debug() << "stone: " < const std::string& - { - return stone.mats ( idx ); - }, stone.mats_size(), &mPile->settings.stone.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return stone.mats(idx); }, + stone.mats_size(), &mPile->settings.stone.mats); } - else - { + else { mPile->settings.flags.bits.stone = 0; mPile->settings.stone.mats.clear(); } } -bool StockpileSerializer::ammo_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +bool StockpileSerializer::ammo_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::write_ammo() -{ - StockpileSettings::AmmoSet *ammo = mBuffer.mutable_ammo(); +void StockpileSerializer::write_ammo() { + StockpileSettings::AmmoSet* ammo = mBuffer.mutable_ammo(); // ammo type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - ammo->add_type ( token ); - }, mPile->settings.ammo.type, - std::vector ( world->raws.itemdefs.ammo.begin(),world->raws.itemdefs.ammo.end() ), - item_type::AMMO ); + serialize_list_itemdef([=](const std::string& token) { ammo->add_type(token); }, + mPile->settings.ammo.type, + std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), + item_type::AMMO); // metal - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - ammo->add_mats ( token ); - }, mPile->settings.ammo.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::ammo_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { ammo->add_mats(token); }, + mPile->settings.ammo.mats); // other mats - only wood and bone - if ( mPile->settings.ammo.other_mats.size() > 2 ) - { - debug() << "WARNING: ammo other materials > 2! " << mPile->settings.ammo.other_mats.size() << endl; + if (mPile->settings.ammo.other_mats.size() > 2) { + WARN(log).print("ammo other materials > 2: %zd\n", mPile->settings.ammo.other_mats.size()); } - for ( size_t i = 0; i < std::min ( size_t ( 2 ), mPile->settings.ammo.other_mats.size() ); ++i ) - { - if ( !mPile->settings.ammo.other_mats.at ( i ) ) + for (size_t i = 0; i < std::min(size_t(2), mPile->settings.ammo.other_mats.size()); ++i) { + if (!mPile->settings.ammo.other_mats.at(i)) continue; - const std::string token = i == 0 ? "WOOD" : "BONE"; - ammo->add_other_mats ( token ); - debug() << " other mats " << i << " is " << token << endl; + const std::string token = i == 0 ? "WOOD" : "BONE"; + ammo->add_other_mats(token); + DEBUG(log).print(" other mats %zd is %s\n", i, token.c_str()); } // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - ammo->add_quality_core ( token ); - }, mPile->settings.ammo.quality_core ); + serialize_list_quality([=](const std::string& token) { ammo->add_quality_core(token); }, + mPile->settings.ammo.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - ammo->add_quality_total ( token ); - }, mPile->settings.ammo.quality_total ); + serialize_list_quality([=](const std::string& token) { ammo->add_quality_total(token); }, + mPile->settings.ammo.quality_total); } -void StockpileSerializer::read_ammo() -{ - if ( mBuffer.has_ammo() ) - { +void StockpileSerializer::read_ammo() { + if (mBuffer.has_ammo()) { mPile->settings.flags.bits.ammo = 1; const StockpileSettings::AmmoSet ammo = mBuffer.ammo(); - debug() << "ammo: " < const std::string& - { - return ammo.type ( idx ); - }, ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return ammo.type(idx); }, + ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO); // materials metals - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::ammo_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return ammo.mats ( idx ); - }, ammo.mats_size(), &mPile->settings.ammo.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::ammo_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return ammo.mats(idx); }, + ammo.mats_size(), &mPile->settings.ammo.mats); // others mPile->settings.ammo.other_mats.clear(); - mPile->settings.ammo.other_mats.resize ( 2, '\0' ); - if ( ammo.other_mats_size() > 0 ) - { + mPile->settings.ammo.other_mats.resize(2, '\0'); + if (ammo.other_mats_size() > 0) { // TODO remove hardcoded value - for ( int i = 0; i < ammo.other_mats_size(); ++i ) - { - const std::string token = ammo.other_mats ( i ); - const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 : -1; - debug() << " other mats " << idx << " is " << token << endl; - if ( idx != -1 ) - mPile->settings.ammo.other_mats.at ( idx ) = 1; + for (int i = 0; i < ammo.other_mats_size(); ++i) { + const std::string token = ammo.other_mats(i); + const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 + : -1; + DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); + if (idx != -1) + mPile->settings.ammo.other_mats.at(idx) = 1; } } // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return ammo.quality_core ( idx ); - }, ammo.quality_core_size(), mPile->settings.ammo.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return ammo.quality_core(idx); }, + ammo.quality_core_size(), mPile->settings.ammo.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return ammo.quality_total ( idx ); - }, ammo.quality_total_size(), mPile->settings.ammo.quality_total ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return ammo.quality_total(idx); }, + ammo.quality_total_size(), mPile->settings.ammo.quality_total); } - else - { + else { mPile->settings.flags.bits.ammo = 0; mPile->settings.ammo.type.clear(); mPile->settings.ammo.mats.clear(); mPile->settings.ammo.other_mats.clear(); - quality_clear ( mPile->settings.ammo.quality_core ); - quality_clear ( mPile->settings.ammo.quality_total ); + quality_clear(mPile->settings.ammo.quality_core); + quality_clear(mPile->settings.ammo.quality_total); } } -bool StockpileSerializer::coins_mat_is_allowed ( const MaterialInfo &mi ) -{ +bool StockpileSerializer::coins_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid(); } -void StockpileSerializer::write_coins() -{ - StockpileSettings::CoinSet *coins = mBuffer.mutable_coin(); - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::coins_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - coins->add_mats ( token ); - }, mPile->settings.coins.mats ); +void StockpileSerializer::write_coins() { + StockpileSettings::CoinSet* coins = mBuffer.mutable_coin(); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { coins->add_mats(token); }, + mPile->settings.coins.mats); } -void StockpileSerializer::read_coins() -{ - if ( mBuffer.has_coin() ) - { +void StockpileSerializer::read_coins() { + if (mBuffer.has_coin()) { mPile->settings.flags.bits.coins = 1; const StockpileSettings::CoinSet coins = mBuffer.coin(); - debug() << "coins: " < const std::string& - { - return coins.mats ( idx ); - }, coins.mats_size(), &mPile->settings.coins.mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return coins.mats(idx); }, + coins.mats_size(), &mPile->settings.coins.mats); } - else - { + else { mPile->settings.flags.bits.coins = 0; mPile->settings.coins.mats.clear(); } } -void StockpileSerializer::bars_blocks_setup_other_mats() -{ - mOtherMatsBars.insert ( std::make_pair ( 0,"COAL" ) ); - mOtherMatsBars.insert ( std::make_pair ( 1,"POTASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 2,"ASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 3,"PEARLASH" ) ); - mOtherMatsBars.insert ( std::make_pair ( 4,"SOAP" ) ); +void StockpileSerializer::bars_blocks_setup_other_mats() { + mOtherMatsBars.insert(std::make_pair(0, "COAL")); + mOtherMatsBars.insert(std::make_pair(1, "POTASH")); + mOtherMatsBars.insert(std::make_pair(2, "ASH")); + mOtherMatsBars.insert(std::make_pair(3, "PEARLASH")); + mOtherMatsBars.insert(std::make_pair(4, "SOAP")); - mOtherMatsBlocks.insert ( std::make_pair ( 0,"GREEN_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 1,"CLEAR_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 2,"CRYSTAL_GLASS" ) ); - mOtherMatsBlocks.insert ( std::make_pair ( 3,"WOOD" ) ); + mOtherMatsBlocks.insert(std::make_pair(0, "GREEN_GLASS")); + mOtherMatsBlocks.insert(std::make_pair(1, "CLEAR_GLASS")); + mOtherMatsBlocks.insert(std::make_pair(2, "CRYSTAL_GLASS")); + mOtherMatsBlocks.insert(std::make_pair(3, "WOOD")); } -bool StockpileSerializer::bars_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +bool StockpileSerializer::bars_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -bool StockpileSerializer::blocks_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ); +bool StockpileSerializer::blocks_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } - -void StockpileSerializer::write_bars_blocks() -{ - StockpileSettings::BarsBlocksSet *bars_blocks = mBuffer.mutable_barsblocks(); +void StockpileSerializer::write_bars_blocks() { + StockpileSettings::BarsBlocksSet* bars_blocks = mBuffer.mutable_barsblocks(); MaterialInfo mi; - FuncMaterialAllowed filter = std::bind ( &StockpileSerializer::bars_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - bars_blocks->add_bars_mats ( token ); - }, mPile->settings.bars_blocks.bars_mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { bars_blocks->add_bars_mats(token); }, + mPile->settings.bars_blocks.bars_mats); // blocks mats - filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); - serialize_list_material ( filter, [=] ( const std::string &token ) - { - bars_blocks->add_blocks_mats ( token ); - }, mPile->settings.bars_blocks.blocks_mats ); + filter = std::bind(&StockpileSerializer::blocks_mat_is_allowed, this, _1); + serialize_list_material( + filter, [=](const std::string& token) { bars_blocks->add_blocks_mats(token); }, + mPile->settings.bars_blocks.blocks_mats); // bars other mats - serialize_list_other_mats ( mOtherMatsBars, [=] ( const std::string &token ) - { - bars_blocks->add_bars_other_mats ( token ); - }, mPile->settings.bars_blocks.bars_other_mats ); + serialize_list_other_mats( + mOtherMatsBars, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, + mPile->settings.bars_blocks.bars_other_mats); // blocks other mats - serialize_list_other_mats ( mOtherMatsBlocks, [=] ( const std::string &token ) - { - bars_blocks->add_blocks_other_mats ( token ); - }, mPile->settings.bars_blocks.blocks_other_mats ); + serialize_list_other_mats( + mOtherMatsBlocks, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, + mPile->settings.bars_blocks.blocks_other_mats); } -void StockpileSerializer::read_bars_blocks() -{ - if ( mBuffer.has_barsblocks() ) - { +void StockpileSerializer::read_bars_blocks() { + if (mBuffer.has_barsblocks()) { mPile->settings.flags.bits.bars_blocks = 1; const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); - debug() << "bars_blocks: " < const std::string& - { - return bars_blocks.bars_mats ( idx ); - }, bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats ); + FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_mats(idx); }, + bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats); // blocks - filter = std::bind ( &StockpileSerializer::blocks_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.blocks_mats ( idx ); - }, bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats ); + filter = std::bind(&StockpileSerializer::blocks_mat_is_allowed, this, _1); + unserialize_list_material( + filter, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_mats(idx); }, + bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats); // bars other mats - unserialize_list_other_mats ( mOtherMatsBars, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.bars_other_mats ( idx ); - }, bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats ); - + unserialize_list_other_mats( + mOtherMatsBars, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, + bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats); // blocks other mats - unserialize_list_other_mats ( mOtherMatsBlocks, [=] ( const size_t & idx ) -> const std::string& - { - return bars_blocks.blocks_other_mats ( idx ); - }, bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats ); - + unserialize_list_other_mats( + mOtherMatsBlocks, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, + bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats); } - else - { + else { mPile->settings.flags.bits.bars_blocks = 0; mPile->settings.bars_blocks.bars_other_mats.clear(); mPile->settings.bars_blocks.bars_mats.clear(); @@ -1431,118 +1175,102 @@ void StockpileSerializer::read_bars_blocks() } } -bool StockpileSerializer::gem_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_GEM ); +bool StockpileSerializer::gem_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_GEM); } -bool StockpileSerializer::gem_cut_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && ( mi.material->flags.is_set ( material_flags::IS_GEM ) || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; +bool StockpileSerializer::gem_cut_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::gem_other_mat_is_allowed ( MaterialInfo &mi ) -{ - return mi.isValid() && ( mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL" ); +bool StockpileSerializer::gem_other_mat_is_allowed(MaterialInfo& mi) { + return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); } -void StockpileSerializer::write_gems() -{ - StockpileSettings::GemsSet *gems = mBuffer.mutable_gems(); +void StockpileSerializer::write_gems() { + StockpileSettings::GemsSet* gems = mBuffer.mutable_gems(); MaterialInfo mi; // rough mats - FuncMaterialAllowed filter_rough = std::bind ( &StockpileSerializer::gem_mat_is_allowed, this, _1 ); - serialize_list_material ( filter_rough, [=] ( const std::string &token ) - { - gems->add_rough_mats ( token ); - }, mPile->settings.gems.rough_mats ); + FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); + serialize_list_material( + filter_rough, [=](const std::string& token) { gems->add_rough_mats(token); }, + mPile->settings.gems.rough_mats); // cut mats - FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); - serialize_list_material ( filter_cut, [=] ( const std::string &token ) - { - gems->add_cut_mats ( token ); - }, mPile->settings.gems.cut_mats ); + FuncMaterialAllowed filter_cut = std::bind(&StockpileSerializer::gem_cut_mat_is_allowed, this, _1); + serialize_list_material( + filter_cut, [=](const std::string& token) { gems->add_cut_mats(token); }, + mPile->settings.gems.cut_mats); // rough other - for ( size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i ) - { - if ( mPile->settings.gems.rough_other_mats.at ( i ) ) - { - mi.decode ( i, -1 ); - if ( !gem_other_mat_is_allowed ( mi ) ) continue; - debug() << " gem rough_other mat" << i << " is " << mi.getToken() << endl; - gems->add_rough_other_mats ( mi.getToken() ); + for (size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i) { + if (mPile->settings.gems.rough_other_mats.at(i)) { + mi.decode(i, -1); + if (!gem_other_mat_is_allowed(mi)) + continue; + DEBUG(log).print("gem rough_other mat %zd is %s\n", i, mi.getToken().c_str()); + gems->add_rough_other_mats(mi.getToken()); } } // cut other - for ( size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i ) - { - if ( mPile->settings.gems.cut_other_mats.at ( i ) ) - { - mi.decode ( i, -1 ); - if ( !mi.isValid() ) mi.decode ( 0, i ); - if ( !gem_other_mat_is_allowed ( mi ) ) continue; - debug() << " gem cut_other mat" << i << " is " << mi.getToken() << endl; - gems->add_cut_other_mats ( mi.getToken() ); + for (size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i) { + if (mPile->settings.gems.cut_other_mats.at(i)) { + mi.decode(i, -1); + if (!mi.isValid()) + mi.decode(0, i); + if (!gem_other_mat_is_allowed(mi)) + continue; + DEBUG(log).print("gem cut_other mat %zd is %s\n", i, mi.getToken().c_str()); + gems->add_cut_other_mats(mi.getToken()); } } } -void StockpileSerializer::read_gems() -{ - if ( mBuffer.has_gems() ) - { +void StockpileSerializer::read_gems() { + if (mBuffer.has_gems()) { mPile->settings.flags.bits.gems = 1; const StockpileSettings::GemsSet gems = mBuffer.gems(); - debug() << "gems: " < const std::string& - { - return gems.rough_mats ( idx ); - }, gems.rough_mats_size(), &mPile->settings.gems.rough_mats ); + FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); + unserialize_list_material( + filter_rough, [=](const size_t& idx) -> const std::string& { return gems.rough_mats(idx); }, + gems.rough_mats_size(), &mPile->settings.gems.rough_mats); // cut - FuncMaterialAllowed filter_cut = std::bind ( &StockpileSerializer::gem_cut_mat_is_allowed, this, _1 ); - unserialize_list_material ( filter_cut, [=] ( const size_t & idx ) -> const std::string& - { - return gems.cut_mats ( idx ); - }, gems.cut_mats_size(), &mPile->settings.gems.cut_mats ); + FuncMaterialAllowed filter_cut = std::bind(&StockpileSerializer::gem_cut_mat_is_allowed, this, _1); + unserialize_list_material( + filter_cut, [=](const size_t& idx) -> const std::string& { return gems.cut_mats(idx); }, + gems.cut_mats_size(), &mPile->settings.gems.cut_mats); - const size_t builtin_size = std::extentraws.mat_table.builtin ) >::value; + const size_t builtin_size = std::extentraws.mat_table.builtin)>::value; // rough other mPile->settings.gems.rough_other_mats.clear(); - mPile->settings.gems.rough_other_mats.resize ( builtin_size, '\0' ); - for ( int i = 0; i < gems.rough_other_mats_size(); ++i ) - { - const std::string token = gems.rough_other_mats ( i ); + mPile->settings.gems.rough_other_mats.resize(builtin_size, '\0'); + for (int i = 0; i < gems.rough_other_mats_size(); ++i) { + const std::string token = gems.rough_other_mats(i); MaterialInfo mi; - mi.find ( token ); - if ( !mi.isValid() || size_t(mi.type) >= builtin_size ) - { - debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; + mi.find(token); + if (!mi.isValid() || size_t(mi.type) >= builtin_size) { + WARN(log).print("invalid gem mat %s idx=%d\n", token.c_str(), mi.type); continue; } - debug() << " rough_other mats " << mi.type << " is " << token << endl; - mPile->settings.gems.rough_other_mats.at ( mi.type ) = 1; + DEBUG(log).print("rough_other mats %d is %s\n", mi.type, token.c_str()); + mPile->settings.gems.rough_other_mats.at(mi.type) = 1; } // cut other mPile->settings.gems.cut_other_mats.clear(); - mPile->settings.gems.cut_other_mats.resize ( builtin_size, '\0' ); - for ( int i = 0; i < gems.cut_other_mats_size(); ++i ) - { - const std::string token = gems.cut_other_mats ( i ); + mPile->settings.gems.cut_other_mats.resize(builtin_size, '\0'); + for (int i = 0; i < gems.cut_other_mats_size(); ++i) { + const std::string token = gems.cut_other_mats(i); MaterialInfo mi; - mi.find ( token ); - if ( !mi.isValid() || size_t(mi.type) >= builtin_size ) - { - debug() << "WARNING: invalid gem mat " << token << ". idx=" << mi.type << endl; + mi.find(token); + if (!mi.isValid() || size_t(mi.type) >= builtin_size) { + WARN(log).print("invalid gem mat %s idx=%d\n", token.c_str(), mi.type); continue; } - debug() << " cut_other mats " << mi.type << " is " << token << endl; - mPile->settings.gems.cut_other_mats.at ( mi.type ) = 1; + DEBUG(log).print("cut_other mats %d is %s\n", mi.type, token.c_str()); + mPile->settings.gems.cut_other_mats.at(mi.type) = 1; } } - else - { + else { mPile->settings.flags.bits.gems = 0; mPile->settings.gems.cut_other_mats.clear(); mPile->settings.gems.cut_mats.clear(); @@ -1551,10 +1279,8 @@ void StockpileSerializer::read_gems() } } -bool StockpileSerializer::finished_goods_type_is_allowed ( item_type::item_type type ) -{ - switch ( type ) - { +bool StockpileSerializer::finished_goods_type_is_allowed(item_type::item_type type) { + switch (type) { case item_type::CHAIN: case item_type::FLASK: case item_type::GOBLET: @@ -1584,252 +1310,182 @@ bool StockpileSerializer::finished_goods_type_is_allowed ( item_type::item_type default: return false; } - } -void StockpileSerializer::finished_goods_setup_other_mats() -{ - mOtherMatsFinishedGoods.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 3,"TOOTH" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 4,"HORN" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 5,"PEARL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 6,"SHELL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 7,"LEATHER" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 8,"SILK" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 9,"AMBER" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 10,"CORAL" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 11,"GREEN_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 12,"CLEAR_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 13,"CRYSTAL_GLASS" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 14,"YARN" ) ); - mOtherMatsFinishedGoods.insert ( std::make_pair ( 15,"WAX" ) ); +void StockpileSerializer::finished_goods_setup_other_mats() { + mOtherMatsFinishedGoods.insert(std::make_pair(0, "WOOD")); + mOtherMatsFinishedGoods.insert(std::make_pair(1, "PLANT_CLOTH")); + mOtherMatsFinishedGoods.insert(std::make_pair(2, "BONE")); + mOtherMatsFinishedGoods.insert(std::make_pair(3, "TOOTH")); + mOtherMatsFinishedGoods.insert(std::make_pair(4, "HORN")); + mOtherMatsFinishedGoods.insert(std::make_pair(5, "PEARL")); + mOtherMatsFinishedGoods.insert(std::make_pair(6, "SHELL")); + mOtherMatsFinishedGoods.insert(std::make_pair(7, "LEATHER")); + mOtherMatsFinishedGoods.insert(std::make_pair(8, "SILK")); + mOtherMatsFinishedGoods.insert(std::make_pair(9, "AMBER")); + mOtherMatsFinishedGoods.insert(std::make_pair(10, "CORAL")); + mOtherMatsFinishedGoods.insert(std::make_pair(11, "GREEN_GLASS")); + mOtherMatsFinishedGoods.insert(std::make_pair(12, "CLEAR_GLASS")); + mOtherMatsFinishedGoods.insert(std::make_pair(13, "CRYSTAL_GLASS")); + mOtherMatsFinishedGoods.insert(std::make_pair(14, "YARN")); + mOtherMatsFinishedGoods.insert(std::make_pair(15, "WAX")); } -bool StockpileSerializer::finished_goods_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() - && mi.material - && ( mi.material->flags.is_set ( material_flags::IS_GEM ) - || mi.material->flags.is_set ( material_flags::IS_METAL ) - || mi.material->flags.is_set ( material_flags::IS_STONE ) ) ; +bool StockpileSerializer::finished_goods_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_finished_goods() -{ - StockpileSettings::FinishedGoodsSet *finished_goods = mBuffer.mutable_finished_goods(); +void StockpileSerializer::write_finished_goods() { + StockpileSettings::FinishedGoodsSet* finished_goods = mBuffer.mutable_finished_goods(); // type - FuncItemAllowed filter = std::bind ( &StockpileSerializer::finished_goods_type_is_allowed, this, _1 ); - serialize_list_item_type ( filter, [=] ( const std::string &token ) - { - finished_goods->add_type ( token ); - }, mPile->settings.finished_goods.type ); + FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); + serialize_list_item_type( + filter, [=](const std::string& token) { finished_goods->add_type(token); }, + mPile->settings.finished_goods.type); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - finished_goods->add_mats ( token ); - }, mPile->settings.finished_goods.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::finished_goods_mat_is_allowed, this, _1); + serialize_list_material( + mat_filter, [=](const std::string& token) { finished_goods->add_mats(token); }, + mPile->settings.finished_goods.mats); // other mats - serialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const std::string &token ) - { - finished_goods->add_other_mats ( token ); - }, mPile->settings.finished_goods.other_mats ); + serialize_list_other_mats( + mOtherMatsFinishedGoods, [=](const std::string& token) { finished_goods->add_other_mats(token); }, + mPile->settings.finished_goods.other_mats); // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - finished_goods->add_quality_core ( token ); - }, mPile->settings.finished_goods.quality_core ); + serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_core(token); }, + mPile->settings.finished_goods.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - finished_goods->add_quality_total ( token ); - }, mPile->settings.finished_goods.quality_total ); + serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_total(token); }, + mPile->settings.finished_goods.quality_total); } -void StockpileSerializer::read_finished_goods() -{ - if ( mBuffer.has_finished_goods() ) - { +void StockpileSerializer::read_finished_goods() { + if (mBuffer.has_finished_goods()) { mPile->settings.flags.bits.finished_goods = 1; const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); - debug() << "finished_goods: " < const std::string& - { - return finished_goods.type ( idx ); - }, finished_goods.type_size(), &mPile->settings.finished_goods.type ); + FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); + unserialize_list_item_type( + filter, [=](const size_t& idx) -> const std::string& { return finished_goods.type(idx); }, + finished_goods.type_size(), &mPile->settings.finished_goods.type); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::finished_goods_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.mats ( idx ); - }, finished_goods.mats_size(), &mPile->settings.finished_goods.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::finished_goods_mat_is_allowed, this, _1); + unserialize_list_material( + mat_filter, [=](const size_t& idx) -> const std::string& { return finished_goods.mats(idx); }, + finished_goods.mats_size(), &mPile->settings.finished_goods.mats); // other mats - unserialize_list_other_mats ( mOtherMatsFinishedGoods, [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.other_mats ( idx ); - }, finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats ); + unserialize_list_other_mats( + mOtherMatsFinishedGoods, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, + finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.quality_core ( idx ); - }, finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_core(idx); }, + finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return finished_goods.quality_total ( idx ); - }, finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total ); - + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_total(idx); }, + finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total); } - else - { + else { mPile->settings.flags.bits.finished_goods = 0; mPile->settings.finished_goods.type.clear(); mPile->settings.finished_goods.other_mats.clear(); mPile->settings.finished_goods.mats.clear(); - quality_clear ( mPile->settings.finished_goods.quality_core ); - quality_clear ( mPile->settings.finished_goods.quality_total ); + quality_clear(mPile->settings.finished_goods.quality_core); + quality_clear(mPile->settings.finished_goods.quality_total); } } -void StockpileSerializer::write_leather() -{ - StockpileSettings::LeatherSet *leather = mBuffer.mutable_leather(); +void StockpileSerializer::write_leather() { + StockpileSettings::LeatherSet* leather = mBuffer.mutable_leather(); - FuncWriteExport setter = [=] ( const std::string &id ) - { - leather->add_mats ( id ); + FuncWriteExport setter = [=](const std::string& id) { + leather->add_mats(id); }; - serialize_list_organic_mat ( setter, &mPile->settings.leather.mats, organic_mat_category::Leather ); + serialize_list_organic_mat(setter, &mPile->settings.leather.mats, organic_mat_category::Leather); } -void StockpileSerializer::read_leather() -{ - if ( mBuffer.has_leather() ) - { +void StockpileSerializer::read_leather() { + if (mBuffer.has_leather()) { mPile->settings.flags.bits.leather = 1; const StockpileSettings::LeatherSet leather = mBuffer.leather(); - debug() << "leather: " < std::string - { - return leather.mats ( idx ); - }, leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather ); + unserialize_list_organic_mat([=](size_t idx) -> std::string { return leather.mats(idx); }, + leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather); } - else - { + else { mPile->settings.flags.bits.leather = 0; mPile->settings.leather.mats.clear(); } } -void StockpileSerializer::write_cloth() -{ - StockpileSettings::ClothSet * cloth = mBuffer.mutable_cloth(); +void StockpileSerializer::write_cloth() { + StockpileSettings::ClothSet* cloth = mBuffer.mutable_cloth(); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_silk ( token ); - }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_silk(token); }, + &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_plant ( token ); - }, &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_plant(token); }, + &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_yarn ( token ); - }, &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_yarn(token); }, + &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_thread_metal ( token ); - }, &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); - - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_silk ( token ); - }, &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_metal(token); }, + &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_plant ( token ); - }, &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_silk(token); }, + &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_yarn ( token ); - }, &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_plant(token); }, + &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); - serialize_list_organic_mat ( [=] ( const std::string &token ) - { - cloth->add_cloth_metal ( token ); - }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_yarn(token); }, + &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_metal(token); }, + &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); } -void StockpileSerializer::read_cloth() -{ - if ( mBuffer.has_cloth() ) - { +void StockpileSerializer::read_cloth() { + if (mBuffer.has_cloth()) { mPile->settings.flags.bits.cloth = 1; const StockpileSettings::ClothSet cloth = mBuffer.cloth(); - debug() << "cloth: " < std::string - { - return cloth.thread_silk ( idx ); - }, cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_plant ( idx ); - }, cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_yarn ( idx ); - }, cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.thread_metal ( idx ); - }, cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_silk ( idx ); - }, cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_plant ( idx ); - }, cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_yarn ( idx ); - }, cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn ); - - unserialize_list_organic_mat ( [=] ( size_t idx ) -> std::string - { - return cloth.cloth_metal ( idx ); - }, cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread ); - } - else - { + DEBUG(log).print("cloth:\n"); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_silk(idx); }, + cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_plant(idx); }, + cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_yarn(idx); }, + cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_metal(idx); }, + cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_silk(idx); }, + cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_plant(idx); }, + cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_yarn(idx); }, + cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_metal(idx); }, + cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); + } + else { mPile->settings.cloth.thread_metal.clear(); mPile->settings.cloth.thread_plant.clear(); mPile->settings.cloth.thread_silk.clear(); @@ -1842,354 +1498,274 @@ void StockpileSerializer::read_cloth() } } -bool StockpileSerializer::wood_mat_is_allowed ( const df::plant_raw * plant ) -{ - return plant && plant->flags.is_set ( plant_raw_flags::TREE ); +bool StockpileSerializer::wood_mat_is_allowed(const df::plant_raw* plant) { + return plant && plant->flags.is_set(plant_raw_flags::TREE); } -void StockpileSerializer::write_wood() -{ - StockpileSettings::WoodSet * wood = mBuffer.mutable_wood(); - for ( size_t i = 0; i < mPile->settings.wood.mats.size(); ++i ) - { - if ( mPile->settings.wood.mats.at ( i ) ) - { - const df::plant_raw * plant = find_plant ( i ); - if ( !wood_mat_is_allowed ( plant ) ) continue; - wood->add_mats ( plant->id ); - debug() << " plant " << i << " is " << plant->id << endl; +void StockpileSerializer::write_wood() { + StockpileSettings::WoodSet* wood = mBuffer.mutable_wood(); + for (size_t i = 0; i < mPile->settings.wood.mats.size(); ++i) { + if (mPile->settings.wood.mats.at(i)) { + const df::plant_raw* plant = find_plant(i); + if (!wood_mat_is_allowed(plant)) + continue; + wood->add_mats(plant->id); + DEBUG(log).print("plant %zd is %s\n", i, plant->id.c_str()); } } } -void StockpileSerializer::read_wood() -{ - if ( mBuffer.has_wood() ) - { +void StockpileSerializer::read_wood() { + if (mBuffer.has_wood()) { mPile->settings.flags.bits.wood = 1; const StockpileSettings::WoodSet wood = mBuffer.wood(); - debug() << "wood: " <settings.wood.mats.clear(); - mPile->settings.wood.mats.resize ( world->raws.plants.all.size(), '\0' ); - for ( int i = 0; i < wood.mats_size(); ++i ) - { - const std::string token = wood.mats ( i ); - const size_t idx = find_plant ( token ); - if ( idx < 0 || idx >= mPile->settings.wood.mats.size() ) - { - debug() << "WARNING wood mat index invalid " << token << ", idx=" << idx << endl; + mPile->settings.wood.mats.resize(world->raws.plants.all.size(), '\0'); + for (int i = 0; i < wood.mats_size(); ++i) { + const std::string token = wood.mats(i); + const size_t idx = find_plant(token); + if (idx < 0 || idx >= mPile->settings.wood.mats.size()) { + WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); continue; } - debug() << " plant " << idx << " is " << token << endl; - mPile->settings.wood.mats.at ( idx ) = 1; + DEBUG(log).print("plant %zd is %s\n", idx, token.c_str()); + mPile->settings.wood.mats.at(idx) = 1; } } - else - { + else { mPile->settings.flags.bits.wood = 0; mPile->settings.wood.mats.clear(); } } -bool StockpileSerializer::weapons_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && ( - mi.material->flags.is_set ( material_flags::IS_METAL ) || - mi.material->flags.is_set ( material_flags::IS_STONE ) ); - +bool StockpileSerializer::weapons_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_weapons() -{ - StockpileSettings::WeaponsSet * weapons = mBuffer.mutable_weapons(); +void StockpileSerializer::write_weapons() { + StockpileSettings::WeaponsSet* weapons = mBuffer.mutable_weapons(); - weapons->set_unusable ( mPile->settings.weapons.unusable ); - weapons->set_usable ( mPile->settings.weapons.usable ); + weapons->set_unusable(mPile->settings.weapons.unusable); + weapons->set_usable(mPile->settings.weapons.usable); // weapon type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - weapons->add_weapon_type ( token ); - }, mPile->settings.weapons.weapon_type, - std::vector ( world->raws.itemdefs.weapons.begin(),world->raws.itemdefs.weapons.end() ), - item_type::WEAPON ); + serialize_list_itemdef([=](const std::string& token) { weapons->add_weapon_type(token); }, + mPile->settings.weapons.weapon_type, + std::vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), + item_type::WEAPON); // trapcomp type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - weapons->add_trapcomp_type ( token ); - }, mPile->settings.weapons.trapcomp_type, - std::vector ( world->raws.itemdefs.trapcomps.begin(),world->raws.itemdefs.trapcomps.end() ), - item_type::TRAPCOMP ); + serialize_list_itemdef([=](const std::string& token) { weapons->add_trapcomp_type(token); }, + mPile->settings.weapons.trapcomp_type, + std::vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), + item_type::TRAPCOMP); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - weapons->add_mats ( token ); - }, mPile->settings.weapons.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::weapons_mat_is_allowed, this, _1); + serialize_list_material( + mat_filter, [=](const std::string& token) { weapons->add_mats(token); }, + mPile->settings.weapons.mats); // other mats - serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) - { - weapons->add_other_mats ( token ); - }, mPile->settings.weapons.other_mats ); + serialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const std::string& token) { weapons->add_other_mats(token); }, + mPile->settings.weapons.other_mats); // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - weapons->add_quality_core ( token ); - }, mPile->settings.weapons.quality_core ); + serialize_list_quality([=](const std::string& token) { weapons->add_quality_core(token); }, + mPile->settings.weapons.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - weapons->add_quality_total ( token ); - }, mPile->settings.weapons.quality_total ); + serialize_list_quality([=](const std::string& token) { weapons->add_quality_total(token); }, + mPile->settings.weapons.quality_total); } -void StockpileSerializer::read_weapons() -{ - if ( mBuffer.has_weapons() ) - { +void StockpileSerializer::read_weapons() { + if (mBuffer.has_weapons()) { mPile->settings.flags.bits.weapons = 1; const StockpileSettings::WeaponsSet weapons = mBuffer.weapons(); - debug() << "weapons: " <settings.weapons.unusable = unusable; mPile->settings.weapons.usable = usable; // weapon type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.weapon_type ( idx ); - }, weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.weapon_type(idx); }, + weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON); // trapcomp type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.trapcomp_type ( idx ); - }, weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.trapcomp_type(idx); }, + weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::weapons_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return weapons.mats ( idx ); - }, weapons.mats_size(), &mPile->settings.weapons.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::weapons_mat_is_allowed, this, _1); + unserialize_list_material( + mat_filter, [=](const size_t& idx) -> const std::string& { return weapons.mats(idx); }, + weapons.mats_size(), &mPile->settings.weapons.mats); // other mats - unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& - { - return weapons.other_mats ( idx ); - }, weapons.other_mats_size(), &mPile->settings.weapons.other_mats ); - + unserialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const size_t& idx) -> const std::string& { return weapons.other_mats(idx); }, + weapons.other_mats_size(), &mPile->settings.weapons.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.quality_core ( idx ); - }, weapons.quality_core_size(), mPile->settings.weapons.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_core(idx); }, + weapons.quality_core_size(), mPile->settings.weapons.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return weapons.quality_total ( idx ); - }, weapons.quality_total_size(), mPile->settings.weapons.quality_total ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_total(idx); }, + weapons.quality_total_size(), mPile->settings.weapons.quality_total); } - else - { + else { mPile->settings.flags.bits.weapons = 0; mPile->settings.weapons.weapon_type.clear(); mPile->settings.weapons.trapcomp_type.clear(); mPile->settings.weapons.other_mats.clear(); mPile->settings.weapons.mats.clear(); - quality_clear ( mPile->settings.weapons.quality_core ); - quality_clear ( mPile->settings.weapons.quality_total ); + quality_clear(mPile->settings.weapons.quality_core); + quality_clear(mPile->settings.weapons.quality_total); } - } -void StockpileSerializer::weapons_armor_setup_other_mats() -{ - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 0,"WOOD" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 1,"PLANT_CLOTH" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 2,"BONE" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 3,"SHELL" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 4,"LEATHER" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 5,"SILK" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 6,"GREEN_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 7,"CLEAR_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 8,"CRYSTAL_GLASS" ) ); - mOtherMatsWeaponsArmor.insert ( std::make_pair ( 9,"YARN" ) ); +void StockpileSerializer::weapons_armor_setup_other_mats() { + mOtherMatsWeaponsArmor.insert(std::make_pair(0, "WOOD")); + mOtherMatsWeaponsArmor.insert(std::make_pair(1, "PLANT_CLOTH")); + mOtherMatsWeaponsArmor.insert(std::make_pair(2, "BONE")); + mOtherMatsWeaponsArmor.insert(std::make_pair(3, "SHELL")); + mOtherMatsWeaponsArmor.insert(std::make_pair(4, "LEATHER")); + mOtherMatsWeaponsArmor.insert(std::make_pair(5, "SILK")); + mOtherMatsWeaponsArmor.insert(std::make_pair(6, "GREEN_GLASS")); + mOtherMatsWeaponsArmor.insert(std::make_pair(7, "CLEAR_GLASS")); + mOtherMatsWeaponsArmor.insert(std::make_pair(8, "CRYSTAL_GLASS")); + mOtherMatsWeaponsArmor.insert(std::make_pair(9, "YARN")); } -bool StockpileSerializer::armor_mat_is_allowed ( const MaterialInfo &mi ) -{ - return mi.isValid() && mi.material && mi.material->flags.is_set ( material_flags::IS_METAL ); +bool StockpileSerializer::armor_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::write_armor() -{ - StockpileSettings::ArmorSet * armor = mBuffer.mutable_armor(); +void StockpileSerializer::write_armor() { + StockpileSettings::ArmorSet* armor = mBuffer.mutable_armor(); - armor->set_unusable ( mPile->settings.armor.unusable ); - armor->set_usable ( mPile->settings.armor.usable ); + armor->set_unusable(mPile->settings.armor.unusable); + armor->set_usable(mPile->settings.armor.usable); // armor type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_body ( token ); - }, mPile->settings.armor.body, - std::vector ( world->raws.itemdefs.armor.begin(),world->raws.itemdefs.armor.end() ), - item_type::ARMOR ); + serialize_list_itemdef([=](const std::string& token) { armor->add_body(token); }, + mPile->settings.armor.body, + std::vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), + item_type::ARMOR); // helm type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_head ( token ); - }, mPile->settings.armor.head, - std::vector ( world->raws.itemdefs.helms.begin(),world->raws.itemdefs.helms.end() ), - item_type::HELM ); + serialize_list_itemdef([=](const std::string& token) { armor->add_head(token); }, + mPile->settings.armor.head, + std::vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), + item_type::HELM); // shoes type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_feet ( token ); - }, mPile->settings.armor.feet, - std::vector ( world->raws.itemdefs.shoes.begin(),world->raws.itemdefs.shoes.end() ), - item_type::SHOES ); + serialize_list_itemdef([=](const std::string& token) { armor->add_feet(token); }, + mPile->settings.armor.feet, + std::vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), + item_type::SHOES); // gloves type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_hands ( token ); - }, mPile->settings.armor.hands, - std::vector ( world->raws.itemdefs.gloves.begin(),world->raws.itemdefs.gloves.end() ), - item_type::GLOVES ); + serialize_list_itemdef([=](const std::string& token) { armor->add_hands(token); }, + mPile->settings.armor.hands, + std::vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), + item_type::GLOVES); // pant type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_legs ( token ); - }, mPile->settings.armor.legs, - std::vector ( world->raws.itemdefs.pants.begin(),world->raws.itemdefs.pants.end() ), - item_type::PANTS ); + serialize_list_itemdef([=](const std::string& token) { armor->add_legs(token); }, + mPile->settings.armor.legs, + std::vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), + item_type::PANTS); // shield type - serialize_list_itemdef ( [=] ( const std::string &token ) - { - armor->add_shield ( token ); - }, mPile->settings.armor.shield, - std::vector ( world->raws.itemdefs.shields.begin(),world->raws.itemdefs.shields.end() ), - item_type::SHIELD ); + serialize_list_itemdef([=](const std::string& token) { armor->add_shield(token); }, + mPile->settings.armor.shield, + std::vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), + item_type::SHIELD); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); - serialize_list_material ( mat_filter, [=] ( const std::string &token ) - { - armor->add_mats ( token ); - }, mPile->settings.armor.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::armor_mat_is_allowed, this, _1); + serialize_list_material( + mat_filter, [=](const std::string& token) { armor->add_mats(token); }, + mPile->settings.armor.mats); // other mats - serialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const std::string &token ) - { - armor->add_other_mats ( token ); - }, mPile->settings.armor.other_mats ); + serialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const std::string& token) { armor->add_other_mats(token); }, + mPile->settings.armor.other_mats); // quality core - serialize_list_quality ( [=] ( const std::string &token ) - { - armor->add_quality_core ( token ); - }, mPile->settings.armor.quality_core ); + serialize_list_quality([=](const std::string& token) { armor->add_quality_core(token); }, + mPile->settings.armor.quality_core); // quality total - serialize_list_quality ( [=] ( const std::string &token ) - { - armor->add_quality_total ( token ); - }, mPile->settings.armor.quality_total ); + serialize_list_quality([=](const std::string& token) { armor->add_quality_total(token); }, + mPile->settings.armor.quality_total); } -void StockpileSerializer::read_armor() -{ - if ( mBuffer.has_armor() ) - { +void StockpileSerializer::read_armor() { + if (mBuffer.has_armor()) { mPile->settings.flags.bits.armor = 1; const StockpileSettings::ArmorSet armor = mBuffer.armor(); - debug() << "armor: " <settings.armor.unusable = unusable; mPile->settings.armor.usable = usable; // body type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.body ( idx ); - }, armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.body(idx); }, + armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR); // head type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.head ( idx ); - }, armor.head_size(), &mPile->settings.armor.head, item_type::HELM ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.head(idx); }, + armor.head_size(), &mPile->settings.armor.head, item_type::HELM); // feet type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.feet ( idx ); - }, armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.feet(idx); }, + armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES); // hands type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.hands ( idx ); - }, armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.hands(idx); }, + armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES); // legs type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.legs ( idx ); - }, armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.legs(idx); }, + armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS); // shield type - unserialize_list_itemdef ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.shield ( idx ); - }, armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD ); + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.shield(idx); }, + armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD); // materials - FuncMaterialAllowed mat_filter = std::bind ( &StockpileSerializer::armor_mat_is_allowed, this, _1 ); - unserialize_list_material ( mat_filter, [=] ( const size_t & idx ) -> const std::string& - { - return armor.mats ( idx ); - }, armor.mats_size(), &mPile->settings.armor.mats ); + FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::armor_mat_is_allowed, this, _1); + unserialize_list_material( + mat_filter, [=](const size_t& idx) -> const std::string& { return armor.mats(idx); }, + armor.mats_size(), &mPile->settings.armor.mats); // other mats - unserialize_list_other_mats ( mOtherMatsWeaponsArmor, [=] ( const size_t & idx ) -> const std::string& - { - return armor.other_mats ( idx ); - }, armor.other_mats_size(), &mPile->settings.armor.other_mats ); + unserialize_list_other_mats( + mOtherMatsWeaponsArmor, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, + armor.other_mats_size(), &mPile->settings.armor.other_mats); // core quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.quality_core ( idx ); - }, armor.quality_core_size(), mPile->settings.armor.quality_core ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_core(idx); }, + armor.quality_core_size(), mPile->settings.armor.quality_core); // total quality - unserialize_list_quality ( [=] ( const size_t & idx ) -> const std::string& - { - return armor.quality_total ( idx ); - }, armor.quality_total_size(), mPile->settings.armor.quality_total ); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_total(idx); }, + armor.quality_total_size(), mPile->settings.armor.quality_total); } - else - { + else { mPile->settings.flags.bits.armor = 0; mPile->settings.armor.body.clear(); mPile->settings.armor.head.clear(); @@ -2199,7 +1775,7 @@ void StockpileSerializer::read_armor() mPile->settings.armor.shield.clear(); mPile->settings.armor.other_mats.clear(); mPile->settings.armor.mats.clear(); - quality_clear ( mPile->settings.armor.quality_core ); - quality_clear ( mPile->settings.armor.quality_total ); + quality_clear(mPile->settings.armor.quality_core); + quality_clear(mPile->settings.armor.quality_total); } } diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index ff0a38241..3fe2087de 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -1,349 +1,283 @@ #pragma once -// stockpiles plugin -#include "proto/stockpiles.pb.h" - -// dfhack #include "modules/Materials.h" -#include "modules/Items.h" -// df -#include "df/world.h" -#include "df/world_data.h" +#include "df/itemdef.h" #include "df/organic_mat_category.h" -#include "df/furniture_type.h" -#include "df/item_quality.h" -#include "df/item_type.h" - -// stl -#include -#include -#include -#include - -namespace df { -struct building_stockpilest; -} +#include "proto/stockpiles.pb.h" -/** - * Null buffer that acts like /dev/null for when debug is disabled - */ -class NullBuffer : public std::streambuf -{ -public: - int overflow ( int c ); -}; +#include -class NullStream : public std::ostream +namespace df { -public: - NullStream(); -private: - NullBuffer m_sb; -}; - + struct building_stockpilest; +} /** * Class for serializing the stockpile_settings structure into a Google protobuf */ -class StockpileSerializer -{ +class StockpileSerializer { public: - /** - * @param out for debugging - * @param stockpile stockpile to read or write settings to - */ - - StockpileSerializer ( df::building_stockpilest * stockpile ); - - ~StockpileSerializer(); + /** + * @param out for debugging + * @param stockpile stockpile to read or write settings to + */ - void enable_debug ( std::ostream &out ); + StockpileSerializer(df::building_stockpilest* stockpile); - /** - * Since we depend on protobuf-lite, not the full lib, we copy this function from - * protobuf message.cc - */ - bool serialize_to_ostream(std::ostream* output); + ~StockpileSerializer(); - /** - * Will serialize stockpile settings to a file (overwrites existing files) - * @return success/failure - */ - bool serialize_to_file ( const std::string & file ); + /** + * Since we depend on protobuf-lite, not the full lib, we copy this function from + * protobuf message.cc + */ + bool serialize_to_ostream(std::ostream* output); - /** - * Again, copied from message.cc - */ - bool parse_from_istream(std::istream* input); + /** + * Will serialize stockpile settings to a file (overwrites existing files) + * @return success/failure + */ + bool serialize_to_file(const std::string& file); + /** + * Again, copied from message.cc + */ + bool parse_from_istream(std::istream* input); - /** - * Read stockpile settings from file - */ - bool unserialize_from_file ( const std::string & file ); + /** + * Read stockpile settings from file + */ + bool unserialize_from_file(const std::string& file); private: + df::building_stockpilest* mPile; + dfstockpiles::StockpileSettings mBuffer; + std::map mOtherMatsFurniture; + std::map mOtherMatsFinishedGoods; + std::map mOtherMatsBars; + std::map mOtherMatsBlocks; + std::map mOtherMatsWeaponsArmor; + + /** + read memory structures and serialize to protobuf + */ + void write(); + + // parse serialized data into ui indices + void read(); + + /** + * Find an enum's value based off the string label. + * @param traits the enum's trait struct + * @param token the string value in key_table + * @return the enum's value, -1 if not found + */ + template + static typename df::enum_traits::base_type linear_index(df::enum_traits traits, const std::string& token) { + auto j = traits.first_item_value; + auto limit = traits.last_item_value; + // sometimes enums start at -1, which is bad news for array indexing + if (j < 0) { + j += abs(traits.first_item_value); + limit += abs(traits.first_item_value); + } + for (; j <= limit; ++j) { + if (token.compare(traits.key_table[j]) == 0) + return j; + } + return -1; + } + + // read the token from the serailized list during import + typedef std::function FuncReadImport; + // add the token to the serialized list during export + typedef std::function FuncWriteExport; + // are item's of item_type allowed? + typedef std::function FuncItemAllowed; + // is this material allowed? + typedef std::function FuncMaterialAllowed; + + // convenient struct for parsing food stockpile items + struct food_pair { + // exporting + FuncWriteExport set_value; + std::vector* stockpile_values; + // importing + FuncReadImport get_value; + size_t serialized_count; + bool valid; + + food_pair(FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count) + : set_value(s), stockpile_values(sp_v), get_value(g), serialized_count(count), valid(true) { } + food_pair(): valid(false) { } + }; + + /** + * There are many repeated (un)serialization cases throughout the stockpile_settings structure, + * so the most common cases have been generalized into generic functions using lambdas. + * + * The basic process to serialize a stockpile_settings structure is: + * 1. loop through the list + * 2. for every element that is TRUE: + * 3. map the specific stockpile_settings index into a general material, creature, etc index + * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) + * 5. add it to the protobuf using FuncWriteExport + * + * The unserialization process is the same in reverse. + */ + void serialize_list_organic_mat(FuncWriteExport add_value, const std::vector* list, df::enums::organic_mat_category::organic_mat_category cat); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_organic_mat(FuncReadImport get_value, size_t list_size, std::vector* pile_list, df::enums::organic_mat_category::organic_mat_category cat); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_item_type(FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector& list); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_item_type(FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_material(FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector& list); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_material(FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_quality(FuncWriteExport add_value, const bool(&quality_list)[7]); + + /** + * Set all values in a bool[7] to false + */ + void quality_clear(bool(&pile_list)[7]); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_quality(FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_other_mats(const std::map other_mats, FuncWriteExport add_value, std::vector list); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_other_mats(const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); + + /** + * @see serialize_list_organic_mat + */ + void serialize_list_itemdef(FuncWriteExport add_value, std::vector list, std::vector items, df::enums::item_type::item_type type); + + /** + * @see serialize_list_organic_mat + */ + void unserialize_list_itemdef(FuncReadImport read_value, int32_t list_size, std::vector* pile_list, df::enums::item_type::item_type type); + + /** + * Given a list of other_materials and an index, return its corresponding token + * @return empty string if not found + * @see other_mats_token + */ + std::string other_mats_index(const std::map other_mats, int idx); + + /** + * Given a list of other_materials and a token, return its corresponding index + * @return -1 if not found + * @see other_mats_index + */ + int other_mats_token(const std::map other_mats, const std::string& token); + + void write_general(); + void read_general(); + + void write_animals(); + void read_animals(); - bool mDebug; - std::ostream * mOut; - NullStream mNull; - df::building_stockpilest * mPile; - dfstockpiles::StockpileSettings mBuffer; - std::map mOtherMatsFurniture; - std::map mOtherMatsFinishedGoods; - std::map mOtherMatsBars; - std::map mOtherMatsBlocks; - std::map mOtherMatsWeaponsArmor; - - - std::ostream & debug(); - - /** - read memory structures and serialize to protobuf - */ - void write(); - - // parse serialized data into ui indices - void read (); - - /** - * Find an enum's value based off the string label. - * @param traits the enum's trait struct - * @param token the string value in key_table - * @return the enum's value, -1 if not found - */ - template - static typename df::enum_traits::base_type linear_index ( std::ostream & out, df::enum_traits traits, const std::string &token ) - { - auto j = traits.first_item_value; - auto limit = traits.last_item_value; - // sometimes enums start at -1, which is bad news for array indexing - if ( j < 0 ) - { - j += abs ( traits.first_item_value ); - limit += abs ( traits.first_item_value ); - } - for ( ; j <= limit; ++j ) - { - // out << " linear_index("<< token <<") = table["< FuncReadImport; - // add the token to the serialized list during export - typedef std::function FuncWriteExport; - // are item's of item_type allowed? - typedef std::function FuncItemAllowed; - // is this material allowed? - typedef std::function FuncMaterialAllowed; - - // convenient struct for parsing food stockpile items - struct food_pair - { - // exporting - FuncWriteExport set_value; - std::vector * stockpile_values; - // importing - FuncReadImport get_value; - size_t serialized_count; - bool valid; - - food_pair ( FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count ) - : set_value ( s ) - , stockpile_values ( sp_v ) - , get_value ( g ) - , serialized_count ( count ) - , valid ( true ) - {} - food_pair(): valid( false ) {} - }; - - /** - * There are many repeated (un)serialization cases throughout the stockpile_settings structure, - * so the most common cases have been generalized into generic functions using lambdas. - * - * The basic process to serialize a stockpile_settings structure is: - * 1. loop through the list - * 2. for every element that is TRUE: - * 3. map the specific stockpile_settings index into a general material, creature, etc index - * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) - * 5. add it to the protobuf using FuncWriteExport - * - * The unserialization process is the same in reverse. - */ - void serialize_list_organic_mat ( FuncWriteExport add_value, const std::vector * list, df::enums::organic_mat_category::organic_mat_category cat ); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_organic_mat ( FuncReadImport get_value, size_t list_size, std::vector *pile_list, df::enums::organic_mat_category::organic_mat_category cat ); - - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_item_type ( FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ); - - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_item_type ( FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); - - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_material ( FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector &list ); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_material ( FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_quality ( FuncWriteExport add_value, const bool ( &quality_list ) [7] ); - - /** - * Set all values in a bool[7] to false - */ - void quality_clear ( bool ( &pile_list ) [7] ); - + food_pair food_map(df::enums::organic_mat_category::organic_mat_category cat); + + void write_food(); + void read_food(); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_quality ( FuncReadImport read_value, int32_t list_size, bool ( &pile_list ) [7] ); + void furniture_setup_other_mats(); + void write_furniture(); + bool furniture_mat_is_allowed(const DFHack::MaterialInfo& mi); + void read_furniture(); + bool refuse_creature_is_allowed(const df::creature_raw* raw); - /** - * @see serialize_list_organic_mat - */ - void serialize_list_other_mats ( const std::map other_mats, FuncWriteExport add_value, std::vector list ); + void refuse_write_helper(std::function add_value, const std::vector& list); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_other_mats ( const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector *pile_list ); + bool refuse_type_is_allowed(df::enums::item_type::item_type type); + void write_refuse(); + void refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list); - /** - * @see serialize_list_organic_mat - */ - void serialize_list_itemdef ( FuncWriteExport add_value, std::vector list, std::vector items, df::enums::item_type::item_type type ); + void read_refuse(); + bool stone_is_allowed(const DFHack::MaterialInfo& mi); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_itemdef ( FuncReadImport read_value, int32_t list_size, std::vector *pile_list, df::enums::item_type::item_type type ); + void write_stone(); + void read_stone(); - /** - * Given a list of other_materials and an index, return its corresponding token - * @return empty string if not found - * @see other_mats_token - */ - std::string other_mats_index ( const std::map other_mats, int idx ); + bool ammo_mat_is_allowed(const DFHack::MaterialInfo& mi); - /** - * Given a list of other_materials and a token, return its corresponding index - * @return -1 if not found - * @see other_mats_index - */ - int other_mats_token ( const std::map other_mats, const std::string & token ); + void write_ammo(); + void read_ammo(); + bool coins_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_general(); - void read_general(); + void write_coins(); + void read_coins(); - void write_animals(); - void read_animals(); + void bars_blocks_setup_other_mats(); + bool bars_mat_is_allowed(const DFHack::MaterialInfo& mi); - food_pair food_map ( df::enums::organic_mat_category::organic_mat_category cat ); + bool blocks_mat_is_allowed(const DFHack::MaterialInfo& mi); + void write_bars_blocks(); + void read_bars_blocks(); + bool gem_mat_is_allowed(const DFHack::MaterialInfo& mi); + bool gem_cut_mat_is_allowed(const DFHack::MaterialInfo& mi); + bool gem_other_mat_is_allowed(DFHack::MaterialInfo& mi); - void write_food(); - void read_food(); + void write_gems(); - void furniture_setup_other_mats(); - void write_furniture(); - bool furniture_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - void read_furniture(); + void read_gems(); - bool refuse_creature_is_allowed ( const df::creature_raw *raw ); - - - void refuse_write_helper ( std::function add_value, const std::vector & list ); - - - bool refuse_type_is_allowed ( df::enums::item_type::item_type type ); - - - void write_refuse(); - void refuse_read_helper ( std::function get_value, size_t list_size, std::vector* pile_list ); - - void read_refuse(); - - bool stone_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_stone(); - - void read_stone(); - - bool ammo_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_ammo(); - void read_ammo(); - bool coins_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_coins(); - - void read_coins(); - - void bars_blocks_setup_other_mats(); - - bool bars_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - bool blocks_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - - void write_bars_blocks(); - void read_bars_blocks(); - bool gem_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - bool gem_cut_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - bool gem_other_mat_is_allowed(DFHack::MaterialInfo &mi ); - - void write_gems(); - - void read_gems(); - - bool finished_goods_type_is_allowed ( df::enums::item_type::item_type type ); - void finished_goods_setup_other_mats(); - bool finished_goods_mat_is_allowed ( const DFHack::MaterialInfo &mi ); - void write_finished_goods(); - void read_finished_goods(); - void write_leather(); - void read_leather(); - void write_cloth(); + bool finished_goods_type_is_allowed(df::enums::item_type::item_type type); + void finished_goods_setup_other_mats(); + bool finished_goods_mat_is_allowed(const DFHack::MaterialInfo& mi); + void write_finished_goods(); + void read_finished_goods(); + void write_leather(); + void read_leather(); + void write_cloth(); void read_cloth(); - bool wood_mat_is_allowed ( const df::plant_raw * plant ); + bool wood_mat_is_allowed(const df::plant_raw* plant); void write_wood(); void read_wood(); - bool weapons_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + bool weapons_mat_is_allowed(const DFHack::MaterialInfo& mi); void write_weapons(); void read_weapons(); void weapons_armor_setup_other_mats(); - bool armor_mat_is_allowed ( const DFHack::MaterialInfo &mi ); + bool armor_mat_is_allowed(const DFHack::MaterialInfo& mi); void write_armor(); void read_armor(); - }; diff --git a/plugins/stockpiles/StockpileUtils.h b/plugins/stockpiles/StockpileUtils.h index f5570be92..7dcd8c3a2 100644 --- a/plugins/stockpiles/StockpileUtils.h +++ b/plugins/stockpiles/StockpileUtils.h @@ -3,26 +3,16 @@ #include "MiscUtils.h" #include "df/world.h" -#include "df/world_data.h" #include "df/creature_raw.h" #include "df/plant_raw.h" -#include -#include -#include - -// os -#include - // Utility Functions {{{ // A set of convenience functions for doing common lookups - /** * Retrieve creature raw from index */ -static inline df::creature_raw* find_creature ( int32_t idx ) -{ +static inline df::creature_raw* find_creature(int32_t idx) { return df::global::world->raws.creatures.all[idx]; } @@ -30,16 +20,14 @@ static inline df::creature_raw* find_creature ( int32_t idx ) * Retrieve creature index from id string * @return -1 if not found */ -static inline int16_t find_creature ( const std::string &creature_id ) -{ - return linear_index ( df::global::world->raws.creatures.all, &df::creature_raw::creature_id, creature_id ); +static inline int16_t find_creature(const std::string& creature_id) { + return linear_index(df::global::world->raws.creatures.all, &df::creature_raw::creature_id, creature_id); } /** * Retrieve plant raw from index */ -static inline df::plant_raw* find_plant ( size_t idx ) -{ +static inline df::plant_raw* find_plant(size_t idx) { return df::global::world->raws.plants.all[idx]; } @@ -47,32 +35,26 @@ static inline df::plant_raw* find_plant ( size_t idx ) * Retrieve plant index from id string * @return -1 if not found */ -static inline size_t find_plant ( const std::string &plant_id ) -{ - return linear_index ( df::global::world->raws.plants.all, &df::plant_raw::id, plant_id ); +static inline size_t find_plant(const std::string& plant_id) { + return linear_index(df::global::world->raws.plants.all, &df::plant_raw::id, plant_id); } -struct less_than_no_case -{ - bool operator () (char x, char y) const - { - return toupper( static_cast< unsigned char >(x)) < toupper( static_cast< unsigned char >(y)); - } +struct less_than_no_case { + bool operator () (char x, char y) const { + return toupper(static_cast(x)) < toupper(static_cast(y)); + } }; -static inline bool CompareNoCase(const std::string &a, const std::string &b) -{ - return std::lexicographical_compare( a.begin(),a.end(), b.begin(),b.end(), less_than_no_case() ); +static inline bool CompareNoCase(const std::string& a, const std::string& b) { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), less_than_no_case()); } - /** * Checks if the parameter has the dfstock extension. * Doesn't check if the file exists or not. */ -static inline bool is_dfstockfile ( const std::string& filename ) -{ - return filename.rfind ( ".dfstock" ) != std::string::npos; +static inline bool is_dfstockfile(const std::string& filename) { + return filename.rfind(".dfstock") != std::string::npos; } // }}} utility Functions diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 4a2bdc33c..98c94ace0 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -1,3 +1,4 @@ +#include "Debug.h" #include "PluginManager.h" #include "StockpileUtils.h" #include "StockpileSerializer.h" @@ -12,11 +13,16 @@ using namespace DFHack; DFHACK_PLUGIN("stockpiles"); -static command_result savestock ( color_ostream &out, vector & parameters ); -static command_result loadstock ( color_ostream &out, vector & parameters ); +REQUIRE_GLOBAL(world); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands ) -{ +namespace DFHack { + DBG_DECLARE(stockpiles, log, DebugCategory::LINFO); +} + +static command_result savestock(color_ostream& out, vector & parameters); +static command_result loadstock(color_ostream& out, vector & parameters); + +DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) { commands.push_back(PluginCommand( "savestock", "Save the active stockpile's settings to a file.", @@ -31,61 +37,46 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector & parameters ) -{ - df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); +static command_result savestock(color_ostream& out, vector & parameters) { + df::building_stockpilest* sp = Gui::getSelectedStockpile(out, true); + if (!sp) { + out.printerr("Selected building isn't a stockpile.\n"); return CR_WRONG_USAGE; } - if ( parameters.size() > 2 ) - { - out.printerr ( "Invalid parameters\n" ); + if (parameters.size() > 2) { + out.printerr("Invalid parameters\n"); return CR_WRONG_USAGE; } - bool debug = false; std::string file; - for ( size_t i = 0; i < parameters.size(); ++i ) - { - const std::string o = parameters.at ( i ); - if ( o == "--debug" || o == "-d" ) - debug = true; - else if ( !o.empty() && o[0] != '-' ) - { + for (size_t i = 0; i < parameters.size(); ++i) { + const std::string o = parameters.at(i); + if (!o.empty() && o[0] != '-') { file = o; } } - if ( file.empty() ) - { - out.printerr ( "You must supply a valid filename.\n" ); + if (file.empty()) { + out.printerr("You must supply a valid filename.\n"); return CR_WRONG_USAGE; } - StockpileSerializer cereal ( sp ); - if ( debug ) - cereal.enable_debug ( out ); + StockpileSerializer cereal(sp); - if ( !is_dfstockfile ( file ) ) file += ".dfstock"; - try - { - if ( !cereal.serialize_to_file ( file ) ) - { - out.printerr ( "could not save to %s\n", file.c_str() ); + if (!is_dfstockfile(file)) file += ".dfstock"; + try { + if (!cereal.serialize_to_file(file)) { + out.printerr("could not save to %s\n", file.c_str()); return CR_FAILURE; } } - catch ( std::exception &e ) - { - out.printerr ( "serialization failed: protobuf exception: %s\n", e.what() ); + catch (std::exception& e) { + out.printerr("serialization failed: protobuf exception: %s\n", e.what()); return CR_FAILURE; } @@ -94,59 +85,45 @@ static command_result savestock ( color_ostream &out, vector & paramete // importing -static command_result loadstock ( color_ostream &out, vector & parameters ) -{ - df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true); - if ( !sp ) - { - out.printerr ( "Selected building isn't a stockpile.\n" ); +static command_result loadstock(color_ostream& out, vector & parameters) { + df::building_stockpilest* sp = Gui::getSelectedStockpile(out, true); + if (!sp) { + out.printerr("Selected building isn't a stockpile.\n"); return CR_WRONG_USAGE; } - if ( parameters.size() < 1 || parameters.size() > 2 ) - { - out.printerr ( "Invalid parameters\n" ); + if (parameters.size() < 1 || parameters.size() > 2) { + out.printerr("Invalid parameters\n"); return CR_WRONG_USAGE; } - bool debug = false; std::string file; - for ( size_t i = 0; i < parameters.size(); ++i ) - { - const std::string o = parameters.at ( i ); - if ( o == "--debug" || o == "-d" ) - debug = true; - else if ( !o.empty() && o[0] != '-' ) - { + for (size_t i = 0; i < parameters.size(); ++i) { + const std::string o = parameters.at(i); + if (!o.empty() && o[0] != '-') { file = o; } } - if ( file.empty() ) { - out.printerr ( "ERROR: missing .dfstock file parameter\n"); + if (file.empty()) { + out.printerr("ERROR: missing .dfstock file parameter\n"); return DFHack::CR_WRONG_USAGE; } - if ( !is_dfstockfile ( file ) ) + if (!is_dfstockfile(file)) file += ".dfstock"; - if ( !Filesystem::exists ( file ) ) - { - out.printerr ( "ERROR: the .dfstock file doesn't exist: %s\n", file.c_str()); + if (!Filesystem::exists(file)) { + out.printerr("ERROR: the .dfstock file doesn't exist: %s\n", file.c_str()); return CR_WRONG_USAGE; } - StockpileSerializer cereal ( sp ); - if ( debug ) - cereal.enable_debug ( out ); - try - { - if ( !cereal.unserialize_from_file ( file ) ) - { - out.printerr ( "unserialization failed: %s\n", file.c_str() ); + StockpileSerializer cereal(sp); + try { + if (!cereal.unserialize_from_file(file)) { + out.printerr("unserialization failed: %s\n", file.c_str()); return CR_FAILURE; } } - catch ( std::exception &e ) - { - out.printerr ( "unserialization failed: protobuf exception: %s\n", e.what() ); + catch (std::exception& e) { + out.printerr("unserialization failed: protobuf exception: %s\n", e.what()); return CR_FAILURE; } return CR_OK; From 0f31b9897dbee898253d4cc94fc4ec4156b0be4e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 16:22:02 -0800 Subject: [PATCH 0795/2222] slight editing for the quickstart guide --- docs/Quickstart.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 55cfe6f36..065ba6ecc 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -5,19 +5,19 @@ Quickstart guide Welcome to DFHack! This guide will help get you oriented with the DFHack system and teach you how to find and use the tools productively. If you're reading this -in `quickstart-guide`, hit the right arrow key or click on the hotkey hint in -the lower right corner of the window to go to the next page. +in the in-game `quickstart-guide` reader, hit the right arrow key or click on +the hotkey hint in the lower right corner of the window to go to the next page. What is DFHack? --------------- -DFHack is a framework for Dwarf Fortress that provides a unified, cross-platform -environment that enables mods and tools to significantly extend the game. The -default DFHack distribution contains a wide variety of tools, including bugfixes, -interface improvements, automation agents, design blueprints, modding building -blocks, and more. Third-party tools (e.g. mods downloaded from Steam Workshop or -the forums) can also seamlessly integrate with the DFHack framework and extend -the game far beyond what can be done by just modding the raws. +DFHack is an add-on for Dwarf Fortress that enables mods and tools to +significantly extend the game. The default DFHack distribution contains a wide +variety of tools, including bugfixes, interface improvements, automation agents, +design blueprints, modding building blocks, and more. Third-party tools (e.g. +mods downloaded from Steam Workshop or the forums) can also seamlessly integrate +with the DFHack framework and extend the game far beyond what can be done by +just modding the raws. DFHack's mission is to provide tools and interfaces for players and modders to: From 79396c108503d595d9ff09c83a173d5b377001ea Mon Sep 17 00:00:00 2001 From: Will H Date: Sat, 11 Mar 2023 16:13:01 +1100 Subject: [PATCH 0796/2222] Refuse files Refuse aliases now complete and nonzero --- data/stockpiles/bones.dfstock | 157 ++++++++ data/stockpiles/corpses.dfstock | Bin 21 -> 11230 bytes data/stockpiles/craftrefuse.dfstock | 364 +++++++++++++++++ data/stockpiles/hair.dfstock | 157 ++++++++ data/stockpiles/horns.dfstock | Bin 0 -> 730 bytes data/stockpiles/rawhides.dfstock | Bin 0 -> 730 bytes data/stockpiles/refuse.dfstock | 583 ++++++++++++++++++++++++++++ data/stockpiles/shells.dfstock | 157 ++++++++ data/stockpiles/skulls.dfstock | 157 ++++++++ data/stockpiles/tannedhides.dfstock | Bin 0 -> 741 bytes data/stockpiles/teeth.dfstock | 157 ++++++++ data/stockpiles/usablehair.dfstock | Bin 0 -> 759 bytes 12 files changed, 1732 insertions(+) create mode 100644 data/stockpiles/craftrefuse.dfstock create mode 100644 data/stockpiles/usablehair.dfstock diff --git a/data/stockpiles/bones.dfstock b/data/stockpiles/bones.dfstock index e69de29bb..aeb2cdcf8 100644 --- a/data/stockpiles/bones.dfstock +++ b/data/stockpiles/bones.dfstock @@ -0,0 +1,157 @@ +*ÆW +WOOD +DOOR + FLOODGATE +BED +CHAIR +CHAIN +FLASK +GOBLET + +INSTRUMENT +TOY +WINDOW +CAGE +BARREL +BUCKET + +ANIMALTRAP +TABLE +COFFIN +STATUE +WEAPON +ARMOR +SHOES +SHIELD +HELM +GLOVES +BOX +BAG +BIN + +ARMORSTAND + +WEAPONRACK +CABINET +FIGURINE +AMULET +SCEPTER +AMMO +CROWN +RING +EARRING +BRACELET +GEM +ANVIL +REMAINS +MEAT +FISH +FISH_RAW +VERMIN +PET +SEEDS +PLANT + SKIN_TANNED + PLANT_GROWTH +THREAD +CLOTH +TOTEM +PANTS +BACKPACK +QUIVER + CATAPULTPARTS + BALLISTAPARTS + SIEGEAMMO +BALLISTAARROWHEAD + TRAPPARTS +TRAPCOMP +DRINK + POWDER_MISC +CHEESE +FOOD + LIQUID_MISC +COIN +GLOB + PIPE_SECTION + HATCH_COVER +GRATE +QUERN + MILLSTONE +SPLINT +CRUTCH +TRACTION_BENCH +TOOL +SLAB +EGG +BOOK +SHEET +BRANCH2TOAD2TOAD_MAN2 +GIANT_TOAD2WORM2WORM_MAN2 BIRD_BLUEJAY2 BLUEJAY_MAN2 GIANT_BLUEJAY2 BIRD_CARDINAL2 CARDINAL_MAN2GIANT_CARDINAL2 BIRD_GRACKLE2 GRACKLE_MAN2 GIANT_GRACKLE2 BIRD_ORIOLE2 +ORIOLE_MAN2 GIANT_ORIOLE2BIRD_RW_BLACKBIRD2RW_BLACKBIRD_MAN2GIANT_RW_BLACKBIRD2 BIRD_PENGUIN2BIRD_PENGUIN_LITTLE2BIRD_PENGUIN_EMPEROR2 PENGUIN MAN2BIRD_PENGUIN_GIANT2BIRD_FALCON_PEREGRINE2PEREGRINE FALCON MAN2GIANT PEREGRINE FALCON2 BIRD_KIWI2KIWI MAN2BIRD_KIWI_GIANT2 BIRD_OSTRICH2 OSTRICH MAN2BIRD_OSTRICH_GIANT2 BIRD_CROW2CROW_MAN2 +GIANT_CROW2 +BIRD_RAVEN2 RAVEN_MAN2 GIANT_RAVEN2BIRD_CASSOWARY2 CASSOWARY_MAN2GIANT_CASSOWARY2BIRD_KEA2KEA_MAN2 GIANT_KEA2BIRD_OWL_SNOWY2 SNOWY_OWL_MAN2GIANT_SNOWY_OWL2SPARROW2 SPARROW_MAN2 GIANT_SPARROW2BIRD_STORK_WHITE2WHITE_STORK_MAN2GIANT_WHITE_STORK2 BIRD_LOON2LOON_MAN2 +GIANT_LOON2 BIRD_OWL_BARN2 BARN_OWL_MAN2GIANT_BARN_OWL2 BIRD_PARAKEET2 PARAKEET_MAN2GIANT_PARAKEET2 BIRD_KAKAPO2 +KAKAPO_MAN2 GIANT_KAKAPO2BIRD_PARROT_GREY2GREY_PARROT_MAN2GIANT_GREY_PARROT2 BIRD_PUFFIN2 +PUFFIN_MAN2 GIANT_PUFFIN2 BIRD_SWAN2SWAN_MAN2 +GIANT_SWAN2 BIRD_LORIKEET2 LORIKEET_MAN2GIANT_LORIKEET2 BIRD_WREN2WREN_MAN2 +GIANT_WREN2 BIRD_OSPREY2 +OSPREY_MAN2 GIANT_OSPREY2BIRD_EMU2EMU_MAN2 GIANT_EMU2BIRD_COCKATIEL2 COCKATIEL_MAN2GIANT_COCKATIEL2BIRD_LOVEBIRD_PEACH-FACED2PEACH-FACED_LOVEBIRD_MAN2GIANT_PEACH-FACED_LOVEBIRD2 BIRD_MAGPIE2 +MAGPIE_MAN2 GIANT_MAGPIE2 BIRD_KESTREL2 KESTREL_MAN2 GIANT_KESTREL2BIRD_ALBATROSS2 ALBATROSS_MAN2GIANT_ALBATROSS2BIRD_OWL_GREAT_HORNED2GREAT_HORNED_OWL_MAN2GIANT_GREAT_HORNED_OWL2 +BIRD_EAGLE2 EAGLE_MAN2 GIANT_EAGLE2 BIRD_HORNBILL2 HORNBILL_MAN2GIANT_HORNBILL2BIRD_LOVEBIRD_MASKED2MASKED_LOVEBIRD_MAN2GIANT_MASKED_LOVEBIRD2 BIRD_BUSHTIT2 BUSHTIT_MAN2 GIANT_BUSHTIT2 DAMSELFLY2 DAMSELFLY_MAN2GIANT_DAMSELFLY2MOTH2MOTH_MAN2 +GIANT_MOTH2 GRASSHOPPER2GRASSHOPPER_MAN2GIANT_GRASSHOPPER2 BARK_SCORPION2BARK_SCORPION_MAN2GIANT_BARK_SCORPION2MANTIS2 +MANTIS_MAN2 GIANT_MANTIS2TICK2TICK_MAN2 +GIANT_TICK2LOUSE2 LOUSE_MAN2 GIANT_LOUSE2THRIPS2 +THRIPS_MAN2 GIANT_THRIPS2SLUG2SLUG_MAN2 +GIANT_SLUG2MOSQUITO2 MOSQUITO_MAN2GIANT_MOSQUITO2SPIDER_JUMPING2JUMPING_SPIDER_MAN2GIANT_JUMPING_SPIDER2TERMITE2 +MOON_SNAIL2MOON_SNAIL_MAN2GIANT_MOON_SNAIL2SPIDER_BROWN_RECLUSE2BROWN_RECLUSE_SPIDER_MAN2GIANT_BROWN_RECLUSE_SPIDER2SNAIL2 SNAIL_MAN2 GIANT_SNAIL2 GECKO_LEOPARD2LEOPARD_GECKO_MAN2GIANT_LEOPARD_GECKO2DESERT TORTOISE2DESERT_TORTOISE_MAN2GIANT_DESERT_TORTOISE2 GILA_MONSTER2GILA_MONSTER_MAN2GIANT_GILA_MONSTER2DOG2CAT2MULE2DONKEY2HORSE2COW2SHEEP2PIG2GOAT2 BIRD_CHICKEN2CAVY2 BIRD_DUCK2 WATER_BUFFALO2REINDEER2 +BIRD_GOOSE2YAK2LLAMA2ALPACA2BIRD_GUINEAFOWL2BIRD_PEAFOWL_BLUE2 BIRD_TURKEY2RABBIT2CHIMERA2CENTAUR2GRIFFON2FLY2FLY_MAN2 GIANT_FLY2 ROACH_LARGE2 ROACH_MAN2 GIANT_ROACH2BEETLE2 +BEETLE_MAN2 GIANT_BEETLE2ANT2BUTTERFLY_MONARCH2BUTTERFLY_MONARCH_MAN2GIANT_BUTTERFLY_MONARCH2FIREFLY2 FIREFLY_MAN2 GIANT_FIREFLY2 DRAGONFLY2 DRAGONFLY_MAN2GIANT_DRAGONFLY2 HONEY_BEE2 BUMBLEBEE2 GOAT_MOUNTAIN2GOAT_MOUNTAIN_MAN2GIANT_GOAT_MOUNTAIN2 MARMOT_HOARY2MARMOT_HOARY_MAN2GIANT_MARMOT_HOARY2GNOME_MOUNTAIN2 +GNOME_DARK2WALRUS2 +WALRUS_MAN2 GIANT_WALRUS2FISH_LAMPREY_SEA2SHARK_GREAT_WHITE2 SHARK_FRILL2SHARK_SPINY_DOGFISH2SHARK_WOBBEGONG_SPOTTED2 SHARK_WHALE2 SHARK_BASKING2 SHARK_NURSE2SHARK_MAKO_SHORTFIN2SHARK_MAKO_LONGFIN2 SHARK_TIGER2 +SHARK_BULL2SHARK_REEF_BLACKTIP2SHARK_REEF_WHITETIP2 +SHARK_BLUE2SHARK_HAMMERHEAD2 SHARK_ANGEL2FISH_SKATE_COMMON2FISH_RAY_MANTA2 FISH_STINGRAY2FISH_COELACANTH2 FISH_STURGEON2FISH_CONGER_EEL2 FISH_MILKFISH2FISH_COD2 FISH_OPAH2FISH_GROUPER_GIANT2 FISH_BLUEFISH2FISH_SUNFISH_OCEAN2FISH_SWORDFISH2 FISH_MARLIN2 FISH_HALIBUT2FISH_BARRACUDA_GREAT2FISH_TUNA_BLUEFIN2NARWHAL2 NARWHAL MAN2NARWHAL, GIANT2HIPPO2 HIPPO_MAN2 GIANT_HIPPO2FISH_GAR_LONGNOSE2 FISH_CARP2FISH_TIGERFISH2 FISH_PIKE2PLATYPUS2 PLATYPUS MAN2PLATYPUS, GIANT2 BEAR_GRIZZLY2BEAR_GRIZZLY_MAN2GIANT_BEAR_GRIZZLY2 +BEAR_BLACK2BEAR_BLACK_MAN2GIANT_BEAR_BLACK2DEER2DEER_MAN2 +GIANT_DEER2FOX2FOX_MAN2 GIANT_FOX2RACCOON2 RACCOON_MAN2 GIANT_RACCOON2MACAQUE_RHESUS2MACAQUE_RHESUS_MAN2GIANT_MACAQUE_RHESUS2COUGAR2 +COUGAR_MAN2 GIANT_COUGAR2WOLF2WOLF_MAN2 +GIANT_WOLF2 GROUNDHOG2 GROUNDHOG_MAN2GIANT_GROUNDHOG2 ALLIGATOR2 ALLIGATOR_MAN2GIANT_ALLIGATOR2 BIRD_BUZZARD2 BUZZARD_MAN2 GIANT_BUZZARD2PANDA2PANDA, GIGANTIC2 PANDA MAN2CAPYBARA2CAPYBARA, GIANT2 CAPYBARA MAN2BADGER2 +BADGER MAN2 BADGER, GIANT2MOOSE2 MOOSE MAN2 MOOSE, GIANT2 RED PANDA2 RED PANDA MAN2RED PANDA, GIANT2ELEPHANT2 ELEPHANT_MAN2GIANT_ELEPHANT2WARTHOG2 WARTHOG_MAN2 GIANT_WARTHOG2LION2LION_MAN2 +GIANT_LION2LEOPARD2 LEOPARD_MAN2 GIANT_LEOPARD2JAGUAR2 +JAGUAR_MAN2 GIANT_JAGUAR2TIGER2 TIGER_MAN2 GIANT_TIGER2CHEETAH2 CHEETAH_MAN2 GIANT_CHEETAH2GAZELLE2 GAZELLE_MAN2 GIANT_GAZELLE2MANDRILL2 MANDRILL_MAN2GIANT_MANDRILL2 +CHIMPANZEE2BONOBO2GORILLA2 ORANGUTAN2GIBBON_SIAMANG2GIBBON_WHITE_HANDED2GIBBON_BLACK_HANDED2 GIBBON_GRAY2GIBBON_SILVERY2GIBBON_PILEATED2 GIBBON_BILOU2GIBBON_WHITE_BROWED2GIBBON_BLACK_CRESTED2 CAMEL_1_HUMP2CAMEL_1_HUMP_MAN2GIANT_CAMEL_1_HUMP2 CAMEL_2_HUMP2CAMEL_2_HUMP_MAN2GIANT_CAMEL_2_HUMP2CROCODILE_SALTWATER2CROCODILE_SALTWATER_MAN2GIANT_CROCODILE_SALTWATER2 BIRD_VULTURE2 VULTURE_MAN2 GIANT_VULTURE2 +RHINOCEROS2RHINOCEROS_MAN2GIANT_RHINOCEROS2GIRAFFE2 GIRAFFE_MAN2 GIANT_GIRAFFE2 HONEY BADGER2HONEY BADGER MAN2HONEY BADGER, GIANT2GIANT TORTOISE2GIANT TORTOISE MAN2GIGANTIC TORTOISE2 ARMADILLO2 ARMADILLO MAN2ARMADILLO, GIANT2MUSKOX2 +MUSKOX_MAN2 GIANT_MUSKOX2ELK2ELK_MAN2 GIANT_ELK2 +BEAR_POLAR2BEAR_POLAR_MAN2GIANT_BEAR_POLAR2 WOLVERINE2 WOLVERINE_MAN2GIANT_WOLVERINE2 +CHINCHILLA2CHINCHILLA_MAN2GIANT_CHINCHILLA2 FLOATING_GUTS2DRUNIAN2 CREEPING_EYE2VORACIOUS_CAVE_CRAWLER2BLIND_CAVE_OGRE2 +CAP_HOPPER2 +MAGMA_CRAB2CRUNDLE2 HUNGRY_HEAD2 +FLESH_BALL2ELK_BIRD2 HELMET_SNAKE2GREEN_DEVOURER2RUTHERER2CREEPY_CRAWLER2DRALTHA2GIANT_EARTHWORM2 BLOOD_MAN2BUGBAT2MANERA2 +MOLEMARIAN2JABBERER2 POND_GRABBER2BLIND_CAVE_BEAR2 CAVE_DRAGON2REACHER2ELEMENTMAN_GABBRO2GORLAK2 CAVE_FLOATER2PLUMP_HELMET_MAN2 CAVE_BLOB2ELEMENTMAN_AMETHYST2OCTOPUS2 OCTOPUS_MAN2 GIANT_OCTOPUS2CRAB2CRAB_MAN2 +GIANT_CRAB2 LEOPARD_SEAL2LEOPARD_SEAL_MAN2GIANT_LEOPARD_SEAL2 +CUTTLEFISH2CUTTLEFISH_MAN2GIANT_CUTTLEFISH2ORCA2ORCA_MAN2 +GIANT_ORCA2SPONGE2 +SPONGE_MAN2 GIANT_SPONGE2HORSESHOE_CRAB2HORSESHOE_CRAB_MAN2GIANT_HORSESHOE_CRAB2 SPERM_WHALE2SPERM_WHALE_MAN2GIANT_SPERM_WHALE2 ELEPHANT_SEAL2ELEPHANT_SEAL_MAN2GIANT_ELEPHANT_SEAL2 HARP_SEAL2 HARP_SEAL_MAN2GIANT_HARP_SEAL2NAUTILUS2 NAUTILUS_MAN2GIANT_NAUTILUS2 FOXSQUIRREL2 MOGHOPPER2 RAT_DEMON2WAMBLER_FLUFFY2LIZARD_RHINO_TWO_LEGGED2 WORM_KNUCKLE2SPIDER_PHANTOM2 FLY_ACORN2 +GNAT_BLOOD2LIZARD2 +LIZARD_MAN2 GIANT_LIZARD2SKINK2 SKINK_MAN2 GIANT_SKINK2 CHAMELEON2 CHAMELEON_MAN2GIANT_CHAMELEON2ANOLE2 ANOLE_MAN2 GIANT_ANOLE2IGUANA2 +IGUANA_MAN2 GIANT_IGUANA2 RIVER OTTER2 SEA OTTER2 OTTER_MAN2 GIANT_OTTER2SNAPPING TURTLE2ALLIGATOR SNAPPING TURTLE2SNAPPING_TURTLE_MAN2GIANT_SNAPPING_TURTLE2BEAVER2 +BEAVER_MAN2 GIANT_BEAVER2LEECH2 LEECH_MAN2 GIANT_LEECH2AXOLOTL2 AXOLOTL_MAN2 GIANT_AXOLOTL2MINK2MINK_MAN2 +GIANT_MINK2 POND_TURTLE2POND_TURTLE_MAN2GIANT_POND_TURTLE2RAT2RAT_MAN2 SQUIRREL_GRAY2SQUIRREL_GRAY_MAN2GIANT_SQUIRREL_GRAY2 SQUIRREL_RED2SQUIRREL_RED_MAN2GIANT_SQUIRREL_RED2CHIPMUNK2 CHIPMUNK_MAN2GIANT_CHIPMUNK2HAMSTER2 HAMSTER_MAN2 GIANT_HAMSTER2HEDGEHOG2 HEDGEHOG_MAN2GIANT_HEDGEHOG2SQUIRREL_FLYING2FLYING_SQUIRREL_MAN2GIANT_FLYING_SQUIRREL2MUSSEL2OYSTER2 FISH_SALMON2FISH_CLOWNFISH2 FISH_HAGFISH2FISH_LAMPREY_BROOK2 FISH_RAY_BAT2FISH_RAY_THORNBACK2FISH_RATFISH_SPOTTED2 FISH_HERRING2 FISH_SHAD2 FISH_ANCHOVY2FISH_TROUT_STEELHEAD2 FISH_HAKE2 FISH_SEAHORSE2 FISH_GLASSEYE2FISH_PUFFER_WHITE_SPOTTED2 FISH_SOLE2 FISH_FLOUNDER2 FISH_MACKEREL2JELLYFISH_SEA_NETTLE2SQUID2 SQUID MAN2GIGANTIC SQUID2 FISH_LUNGFISH2FISH_LOACH_CLOWN2FISH_BULLHEAD_BROWN2FISH_BULLHEAD_YELLOW2FISH_BULLHEAD_BLACK2FISH_KNIFEFISH_BANDED2 FISH_CHAR2FISH_TROUT_RAINBOW2FISH_MOLLY_SAILFIN2 +FISH_GUPPY2 +FISH_PERCH2DWARF2HUMAN2ELF2GOBLIN2KOBOLD2GREMLIN2TROLL2OGRE2UNICORN2DRAGON2SATYR2COLOSSUS_BRONZE2GIANT2CYCLOPS2ETTIN2MINOTAUR2YETI2 SASQUATCH2 BLIZZARD_MAN2WOLF_ICE2FAIRY2PIXIE2BEAK_DOG2 GRIMELING2 BLENDEC_FOUL2 STRANGLER2 NIGHTWING2HARPY2HYDRA2 MERPERSON2 SEA_SERPENT2 SEA_MONSTER2BIRD_ROC2CROCODILE_CAVE2TOAD_GIANT_CAVE2 OLM_GIANT2 BAT_GIANT2 RAT_GIANT2 RAT_LARGE2MOLE_DOG_NAKED2 +TROGLODYTE2 +MOLE_GIANT2IMP_FIRE2SPIDER_CAVE_GIANT2 SPIDER_CAVE2 FISH_CAVE2 CAVE_FISH_MAN2 LOBSTER_CAVE2 +SNAKE_FIRE2OLM2OLM_MAN2BAT2BAT_MAN2MAGGOT_PURRING2ELEMENTMAN_FIRE2ELEMENTMAN_MAGMA2ELEMENTMAN_IRON2ELEMENTMAN_MUD2BIRD_SWALLOW_CAVE2CAVE_SWALLOW_MAN2BIRD_SWALLOW_CAVE_GIANT2 AMPHIBIAN_MAN2 REPTILE_MAN2 SERPENT_MAN2ANT_MAN2 +RODENT MAN2 WILD_BOAR2 WILD_BOAR_MAN2GIANT_WILD_BOAR2COYOTE2 +COYOTE_MAN2 GIANT_COYOTE2KANGAROO2 KANGAROO_MAN2GIANT_KANGAROO2KOALA2 KOALA_MAN2 GIANT_KOALA2ADDER2 ADDER_MAN2 GIANT_ADDER2ECHIDNA2 ECHIDNA_MAN2 GIANT_ECHIDNA2 PORCUPINE2 PORCUPINE_MAN2GIANT_PORCUPINE2 KINGSNAKE2 KINGSNAKE_MAN2GIANT_KINGSNAKE2 GRAY_LANGUR2GRAY_LANGUR_MAN2GIANT_GRAY_LANGUR2BOBCAT2 +BOBCAT_MAN2 GIANT_BOBCAT2SKUNK2 SKUNK_MAN2 GIANT_SKUNK2GREEN_TREE_FROG2GREEN_TREE_FROG_MAN2GIANT_GREEN_TREE_FROG2HARE2HARE_MAN2 +GIANT_HARE2 RATTLESNAKE2RATTLESNAKE_MAN2GIANT_RATTLESNAKE2WEASEL2 +WEASEL_MAN2 GIANT_WEASEL2COPPERHEAD_SNAKE2COPPERHEAD_SNAKE_MAN2GIANT_COPPERHEAD_SNAKE2IBEX2IBEX_MAN2 +GIANT_IBEX2WOMBAT2 +WOMBAT_MAN2 GIANT_WOMBAT2DINGO2 DINGO_MAN2 GIANT_DINGO2COATI2 COATI_MAN2 GIANT_COATI2OPOSSUM2 OPOSSUM_MAN2 GIANT_OPOSSUM2MONGOOSE2 MONGOOSE_MAN2GIANT_MONGOOSE2HYENA2 HYENA_MAN2 GIANT_HYENA2ANACONDA2 ANACONDA_MAN2GIANT_ANACONDA2MONITOR_LIZARD2MONITOR_LIZARD_MAN2GIANT_MONITOR_LIZARD2 +KING_COBRA2KING_COBRA_MAN2GIANT_KING_COBRA2OCELOT2 +OCELOT_MAN2 GIANT_OCELOT2JACKAL2 +JACKAL_MAN2 GIANT_JACKAL2CAPUCHIN2 CAPUCHIN_MAN2GIANT_CAPUCHIN2SLOTH2 SLOTH_MAN2 GIANT_SLOTH2 SPIDER_MONKEY2SPIDER_MONKEY_MAN2GIANT_SPIDER_MONKEY2PANGOLIN2 PANGOLIN_MAN2GIANT_PANGOLIN2 BLACK_MAMBA2BLACK_MAMBA_MAN2GIANT_BLACK_MAMBA2 +BEAR_SLOTH2SLOTH_BEAR_MAN2GIANT_SLOTH_BEAR2AYE-AYE2 AYE-AYE_MAN2 GIANT_AYE-AYE2 +BUSHMASTER2BUSHMASTER_MAN2GIANT_BUSHMASTER2PYTHON2 +PYTHON_MAN2 GIANT_PYTHON2TAPIR2 TAPIR_MAN2 GIANT_TAPIR2IMPALA2 +IMPALA_MAN2 GIANT_IMPALA2AARDVARK2 AARDVARK_MAN2GIANT_AARDVARK2 LION_TAMARIN2LION_TAMARIN_MAN2GIANT_LION_TAMARIN2STOAT2 STOAT_MAN2 GIANT_STOAT2LYNX2LYNX_MAN2 +GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/corpses.dfstock b/data/stockpiles/corpses.dfstock index e71f2102467c90086585cd1ff3162d2fcd1fbf84..dbaa101c1581aa08336589f4ba7a7826d01cc6df 100644 GIT binary patch literal 11230 zcmZu%xtilTcJBI0E45U0SKsNmh$^1X6WDJ6k`TofNpQHdTAlEf96EI9a6*TU{9N;& z1r${G1`>dC4)(KyfBXMyvbcJmCyU(sF4>+cGzvSo{ES@Tx6vH?o ze=b~&l{G)gDp_}~w%C5MsGS{>#i{JiG(&%$I(to4KU`O1QO_;-UEy9pWk6+f zv!VZ~wv9=O66ZBB6rA1FW<&ps+oxrhPiZx}@AhVPT4B^dx&_>UDKp#UWn(L|4>hJe z3T=QH85Y~jE|oJot;EC5I3Ct_r3YamrL0VbRLo~4>#kTT#-xUMja%I1i*$+Mx{}s4 z#aK4xjfc_JRF%VkS-(C2UER9QcV?$l?{tJ;9!Pi^^BcoXw#s}np^qy_l+4$;`Yt#g z;4j?g{Ugq7d5}waEzO4hnfPS}PBN7or-y1OalbpQL@QH>lFOhZyJkcGd^pMl3{7}~ z&i-(X*>b@oJ3T*!ADJezeebWfyBWxqR+;uiXlJ0=$fBJ!>x;8YvlSXNjg026)zmls ziqW{32tennqs>6G?jee_ft^;EOj$DEUU^E0PIsBE=W=l7g^Ni*H1KCxLIl<$hf;6M zhW>eEJspM$r_<7)t{ZR={^yk{rw5o3Vl38PAcJNfYCNhE2N>jwr0rraQ0XR-k_q>E zQi73v@c|6bgzkp(!T+R?XkQUn2X99*A10A99$YcP9Q_rCv!VZZ`wW#GRS-PcQK-c` z3NSs)YX?T!(0@D{LuJdpZ)qEel<}$*Bi!Ta(X8>u1j;=GQ(pAh#SXB0;Kfj7%D{^O zXnyB&CZoue*4ga*_Y;g(ZvGlIrkR3{)W{wD8D7t}wnbY)OGz2;LNOwz7Y9MbDtB68 zLS@~6lO5SAwL|Co-W=v?yrUVY%7%CnS8j*t>^oRX^EGbGJSxiOfs$}W#NOEgN^i>r zPo`ooaJhXNK80B_`%q&_<>vrX%^96yf5D>OYPdu$e>~ma>L&+BPaH3kMViK#l!IY{lA}|EpLcR2v z?^+mg1J5C9a;G=h+5^I@z*0lm6SGjsgev8VSqx=%ftkjCyh?^HD?j$QBrX`Mmn3j< zi^>**OBoNY82AWd0RhK-@|7ayxE5u)9CF6m^t=sNnX$Qv2Nk_nJ-$I6i!0( zRrdafF>APxHR1ildHu38Qv^q z(k&M}pPrz=4~5GveyW^@Qq9e0txN)@o$0MUAk0e+&#xQaVR8mvVsUQ*fO91Po=JKA z1Ipm~D;P$P5COc0?cZD6P2HCmZk}^rm}j`PU}|{iW|jNq0!_Mv3}X7{3?`D@zt%jr zWnmVD2d(I#>-c7qhG? zTU)cT75r>##4#aH%GncX6miR-GO5LGq^inc>}Z**&Za2@dyTc!u7huoAvCgMXV&nR zPA3S|GfDIse|!e2^a-}>Jk<77*{*;jNP{jWcqxQ6s}xq3JVz;c3>7oQJVXAl&T+t& zc^2Q;4n2SS1Bg3+Q(^uC{rXgPj@P9XCReo{aNE4Ig>Q)LFjtvSZPW}j+cP}=8ytIV zE*)#cepEfs{eeeDoD$6V=Z7W_qM`nRswb!`Cos_j=xH%S$uibsFMA|v@ zn^|3L)s0XvQt}vjtcrQ%$PU~=h8wCma8b`R1>VOK~IDXtkw1a#QiLY$%3yMf%FFVca56^134 z_8Q89I9*2^?+fdkJ4tvklx-N6fxH<4hF%Dl%&UxWMm*^{iXg)TwkcrzKlAzYa9A9o zJ_OT^IchM>oIY4{5Rw5~>JWUra5M7`5e|6vJmW_Q0J;Y}hVLdvUJ!Rc^mw6)kytzP zx!E!Tn(b`9a23(?2z#gDf%K!ej{6BoKQ_`UbC5AvYL756XPO=3z&;31&7K>$m=Ypn zzH$r355eA;Te*MT{xCL{wE~_08gfV1X?9xS_~odLe|u-agJLUNILBKqnD12H1un;0 z*bd%B1BL|y$ZipOwz_P-VP3YNS~#+1gS^6UYr)f$Vc?p?TA>vduuqZaz%I&PzaYi0 zaVr+37*5&;eK<6QuR7P6Ud`McY8I?(H}uaG>Ygz4`Q(2>8~UG&3EGfgINpq6mYr6Z zK-Evc9c!=-vI-Lzvd31xJhr)uTI@%RLE34=shUJerc{Af3^E7Ri82S(iLttBhHi<$ zn*0n~b(pJ6rD|^mnk}VdkWE8`4WC!5G3gAPeZybB$V9_8BaX}YZ zkkgf!Ed%(TH<@j3;8IY9+I-^yz=X)r<*Xs3WEgxvf`txCcrwIzTrdF~DL?{#+qwK* zmUNh_O!qoB1MLQhwDwFkY9DI6Lv_Xi%&ZZF4|Kpgt;FXLaEppu)D8VJIz+VuT?b!h z7hD=Lrh1S8xB702k=Q^=9z@wdF-y^(TP_%{@+oi_dKhFVo}E_WWd_{3u)kady}r{* zOcZb%EDzZ?ia|n+$%2;}unD;qIP71ro~zV1KJ{=e5WT{6F)M zj|N5rc9h$a8o&+Y6uKyoMR`&nHprB$rSHR3NaayvF37g55SL*H92Zj~eDdQwpv*#^ zDo7xLLPEK$DeDj~Qpe2JuA2URI>X;KuW^evt~NY|hd%%D&_`N-?4wj~NCCl0V7*h1 z+%x-v=G&hDd4%7^ANmut%I6QHTgMJ}sTJ;}Y!z@xcP<;BA+Oe()4K}mYusW+~$r50Y!VvVCbx_wm2~O%7?is z@;o=g6svmdFVNSCl)QA6AjLep>VhmsB~f3kbCD+;MoxCptxJbXb+Zd>@J9B)(Rq_#wxA`-y3IVpkj2nXmku@9A; zDu)*XO3xdp#>+$r9AqKz%;5$KBw@;r7#5kP-2L!iybkfpIGmAx#i{WUZ}VcXc5wI^ zI$yXLmU%dH27H^+s}7$49%BlmVK`!^5Ct)7g{O>;_PqIy_=Shw;}Ir@TfpsyUDBOE z-O(Y4cMn@l2`8-QGt@V|1wr9V1$uVg;H9Xm5LrRm(SeT|gs*J{p~n&MHbHK-jK?Zd z^A?Q)6)~J|Jt8Wf4W8VYomO}+Dt7_5;FCrU*rtqDd%)0rFaz-2EAtw+m{!$I498s} zEULt9Jl^X}uX22cnuYHWGj8ZVo}Hn1>45(R?vRr4Dkh3Ka)e9IUBWM49^2eQeVIoL z{BZcXH8R2!I=n;;?xQK52y70)%2FHy*ZrZ%oiylrK)^?Z&naCFbCt=J-DaTKGS%8@xJU`0K&?_PN7ljHAlJo-@o}-AXrC_lG?;w7^nQv7|{t;Y0sMu+^ zQc|NR$e!>c&!s`ck8=pg9QHMQ4X+syw?$Tq-iI2D&H`apGMY(rRANQF7sV_o2D_Lo z7rY5|02GerQJ66bSPpYFK9m_~@Nh_vK3gv0kp#ZNgqhMOT*fO^%(8M0 zIfpG5@xlbYwm&@{I1w6Yg-KMH18#xmQ{)TjpV2DJ48;Q_w3F`YC2BCGvU~(K&tNEu zDUfTx4Icu}k%GG+l2;bwCR3;YJRr<|Zo;iaKpc9Z#hp}z#Be-FA&{#_>|`HmOskR* zU}g-3ew=Q%c(?1qHaAxQ%D@3C!;mvPK6a)ZX_)Pi%5oI4ys zji_-qOc@d)8g)B8z2P;DVheT)BE$f|6k>cmveeXoiWCfPBEXHTs$$Mb3scHF4fIf1)ZV}^{rKCyL*$rocCx9hR7lV;n@SIp(+}u#&m@N*KzBz!K4$aLbg4QGfe3@aS z6TV|XCb=zt;!$e@O?9EPoIx;;TrE}zse+-w5oOcKkFZC0)?~s%XK)4o)Q&M$gqxRBDm#7NPWv=AEFR)*g`Bq{*HO!j}|oKAi&mF_57+0 zZ6&*Ba~M@6M~nvs#5CB#L~de^*OAWSQ;hs34-vpgmkr{@i88iObn?Q%zLp^2Jn*9j zFGo3p7J}z*^4Ev~=@|EL%t#xsqYE#FW|T3Xv0`{`5ZUO61=^Z732^}XT%_(%2Xi+2|!9P-SF`aC77sW zBDLm<*<8R}7CO@GLyf7_G6a}eUc9Byo(rZ>2SngYo0Cr3auK5izJ?txbGQ{dtuTq2 z+JM`(a8gHj3b^;V%7nVF8Hi;-2>ANVVXnrcGmt-nx#2wn2?=?63=Jl~KRpI8r881` z`bnf@GCecJz!@V`3TF%j<7a1#;Plx5A|<>=gE=7#NJl&ZxQp7o4=A&MU_&p^Ka;7y z!B8Y2NvX@eU!n$6>Q*8!9u%A6guQRC*d54-+dxKj^UjuQ;RC+gx!B?kk32n&*6 z{o7Nwt4`ESs)??%L$GiWI*l?>Z&tSHxdrNH$N5gfqN9^SWJ4}#^AZZ3ihiYGw_(zq zI>U?5Fmg)buy5vG(>Ykrt|X4s7e)&jjLx$SG~~i%L%a(|1rR3>Hho@IGG13J4ir~$ z9_UWraeEr|0}C%$(1IQ%=o2QQXPd<4PG`e-2K;5V%a(AYf&EtbpRbq^6%1@n9C-t)p`i&yY`@7cSJzHm?;SuSepYhIdIGq)riaeHNKA z_0cf~b<1}f4$ur3@2AJzu>iQNG?X<7*P%N_*cUAoF`_Jk2h_x>Xn5N!`qPWy!f|sY zazet($j(^|;2B=Cl|>%HzWjfIB;D5vajh;q6EF2>)Wehsitlubx=)f0Z?l0zt4J0o zte@oOEWoGdQ)d(sJJ>Noh>db&F~!2Z+sehPhk;KpaY^+%^>3S`Kv!>_%SEdVIXa7p z3?^+zr_Uhrs6R#th`XZdsLPH=I?1w7H>%$2rw~kafG^03%}4XCZNInekGA>QHow~D Jcia4Fn}2%@sj2_~ literal 0 HcmV?d00001 diff --git a/data/stockpiles/rawhides.dfstock b/data/stockpiles/rawhides.dfstock index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b58c77edecc3fd043a6cd9e855cbe5308d164ccd 100644 GIT binary patch literal 730 zcmXw1F_POb47A3QY{R+C6 z{o7HytB%x;s)=rsLojm@Iu9~ZXBM{UxdrMb$9biG*1<_3vLTnWc?pHiMZZ$N+c4@* zt>IayA2=m(*f+D*bPm?DD~Tg@h0%g~qtj#q^|^4_5bwfK0mR9JO_vvyjMv4A1I0z0 z2fEXD+@1&hz``>Yw4g@``h=0_*(R}B>0%hqfWJ<5-V%<~v)?NJOEwaVBx>1vg~+6P zpcXkZT?FLo;X0s=)b#T#9?ZhKb#zJM8S<(1!iCz&=A|O!K00L_5$etO&;Gl0uNeOZ!l9lBG5UC~kzBg!&(KuxTQ`nS!ZKfM?(95+`Y zCnUTK?3~2_p5ZN9ndKqu%l{Wh(tW8Ax9Y+(@mh~YJxm#)c%^&PeUx-~n++UVM6y6( z{Ump10X{yTTBDHI!j1t#Y?LF5DHitK7cOQw419u#bE@B|d)p)hx_RqdE?RBK(OFES zH)%sUeFl+7{V_;D+!j?wZFW4+S>~DAL3OR4LNL`Hz91_$AI-O>`Q9`?n$yqb^s71j JZccxi(?5GUsjL71 literal 0 HcmV?d00001 diff --git a/data/stockpiles/refuse.dfstock b/data/stockpiles/refuse.dfstock index e69de29bb..4de87f545 100644 --- a/data/stockpiles/refuse.dfstock +++ b/data/stockpiles/refuse.dfstock @@ -0,0 +1,583 @@ +*ÞÐ +WOOD +DOOR + FLOODGATE +BED +CHAIR +CHAIN +FLASK +GOBLET + +INSTRUMENT +TOY +WINDOW +CAGE +BARREL +BUCKET + +ANIMALTRAP +TABLE +COFFIN +STATUE +WEAPON +ARMOR +SHOES +SHIELD +HELM +GLOVES +BOX +BAG +BIN + +ARMORSTAND + +WEAPONRACK +CABINET +FIGURINE +AMULET +SCEPTER +AMMO +CROWN +RING +EARRING +BRACELET +GEM +ANVIL +REMAINS +MEAT +FISH +FISH_RAW +VERMIN +PET +SEEDS +PLANT + SKIN_TANNED + PLANT_GROWTH +THREAD +CLOTH +TOTEM +PANTS +BACKPACK +QUIVER + CATAPULTPARTS + BALLISTAPARTS + SIEGEAMMO +BALLISTAARROWHEAD + TRAPPARTS +TRAPCOMP +DRINK + POWDER_MISC +CHEESE +FOOD + LIQUID_MISC +COIN +GLOB + PIPE_SECTION + HATCH_COVER +GRATE +QUERN + MILLSTONE +SPLINT +CRUTCH +TRACTION_BENCH +TOOL +SLAB +EGG +BOOK +SHEET +BRANCHTOADTOAD_MAN +GIANT_TOADWORMWORM_MAN BIRD_BLUEJAY BLUEJAY_MAN GIANT_BLUEJAY BIRD_CARDINAL CARDINAL_MANGIANT_CARDINAL BIRD_GRACKLE GRACKLE_MAN GIANT_GRACKLE BIRD_ORIOLE +ORIOLE_MAN GIANT_ORIOLEBIRD_RW_BLACKBIRDRW_BLACKBIRD_MANGIANT_RW_BLACKBIRD BIRD_PENGUINBIRD_PENGUIN_LITTLEBIRD_PENGUIN_EMPEROR PENGUIN MANBIRD_PENGUIN_GIANTBIRD_FALCON_PEREGRINEPEREGRINE FALCON MANGIANT PEREGRINE FALCON BIRD_KIWIKIWI MANBIRD_KIWI_GIANT BIRD_OSTRICH OSTRICH MANBIRD_OSTRICH_GIANT BIRD_CROWCROW_MAN +GIANT_CROW +BIRD_RAVEN RAVEN_MAN GIANT_RAVENBIRD_CASSOWARY CASSOWARY_MANGIANT_CASSOWARYBIRD_KEAKEA_MAN GIANT_KEABIRD_OWL_SNOWY SNOWY_OWL_MANGIANT_SNOWY_OWLSPARROW SPARROW_MAN GIANT_SPARROWBIRD_STORK_WHITEWHITE_STORK_MANGIANT_WHITE_STORK BIRD_LOONLOON_MAN +GIANT_LOON BIRD_OWL_BARN BARN_OWL_MANGIANT_BARN_OWL BIRD_PARAKEET PARAKEET_MANGIANT_PARAKEET BIRD_KAKAPO +KAKAPO_MAN GIANT_KAKAPOBIRD_PARROT_GREYGREY_PARROT_MANGIANT_GREY_PARROT BIRD_PUFFIN +PUFFIN_MAN GIANT_PUFFIN BIRD_SWANSWAN_MAN +GIANT_SWAN BIRD_LORIKEET LORIKEET_MANGIANT_LORIKEET BIRD_WRENWREN_MAN +GIANT_WREN BIRD_OSPREY +OSPREY_MAN GIANT_OSPREYBIRD_EMUEMU_MAN GIANT_EMUBIRD_COCKATIEL COCKATIEL_MANGIANT_COCKATIELBIRD_LOVEBIRD_PEACH-FACEDPEACH-FACED_LOVEBIRD_MANGIANT_PEACH-FACED_LOVEBIRD BIRD_MAGPIE +MAGPIE_MAN GIANT_MAGPIE BIRD_KESTREL KESTREL_MAN GIANT_KESTRELBIRD_ALBATROSS ALBATROSS_MANGIANT_ALBATROSSBIRD_OWL_GREAT_HORNEDGREAT_HORNED_OWL_MANGIANT_GREAT_HORNED_OWL +BIRD_EAGLE EAGLE_MAN GIANT_EAGLE BIRD_HORNBILL HORNBILL_MANGIANT_HORNBILLBIRD_LOVEBIRD_MASKEDTOADTOAD_MAN +GIANT_TOADWORMWORM_MAN BIRD_BLUEJAY BLUEJAY_MAN GIANT_BLUEJAY BIRD_CARDINAL CARDINAL_MANGIANT_CARDINAL BIRD_GRACKLE GRACKLE_MAN GIANT_GRACKLE BIRD_ORIOLE +ORIOLE_MAN GIANT_ORIOLEBIRD_RW_BLACKBIRDRW_BLACKBIRD_MANGIANT_RW_BLACKBIRD BIRD_PENGUINBIRD_PENGUIN_LITTLEBIRD_PENGUIN_EMPEROR PENGUIN MANBIRD_PENGUIN_GIANTBIRD_FALCON_PEREGRINEPEREGRINE FALCON MANGIANT PEREGRINE FALCON BIRD_KIWIKIWI MANBIRD_KIWI_GIANT BIRD_OSTRICH OSTRICH MANBIRD_OSTRICH_GIANT BIRD_CROWCROW_MAN +GIANT_CROW +BIRD_RAVEN RAVEN_MAN GIANT_RAVENBIRD_CASSOWARY CASSOWARY_MANGIANT_CASSOWARYBIRD_KEAKEA_MAN GIANT_KEABIRD_OWL_SNOWY SNOWY_OWL_MANGIANT_SNOWY_OWLSPARROW SPARROW_MAN GIANT_SPARROWBIRD_STORK_WHITEWHITE_STORK_MANGIANT_WHITE_STORK BIRD_LOONLOON_MAN +GIANT_LOON BIRD_OWL_BARN BARN_OWL_MANGIANT_BARN_OWL BIRD_PARAKEET PARAKEET_MANGIANT_PARAKEET BIRD_KAKAPO +KAKAPO_MAN GIANT_KAKAPOBIRD_PARROT_GREYGREY_PARROT_MANGIANT_GREY_PARROT BIRD_PUFFIN +PUFFIN_MAN GIANT_PUFFIN BIRD_SWANSWAN_MAN +GIANT_SWAN BIRD_LORIKEET LORIKEET_MANGIANT_LORIKEET BIRD_WRENWREN_MAN +GIANT_WREN BIRD_OSPREY +OSPREY_MAN GIANT_OSPREYBIRD_EMUEMU_MAN GIANT_EMUBIRD_COCKATIEL COCKATIEL_MANGIANT_COCKATIELBIRD_LOVEBIRD_PEACH-FACEDPEACH-FACED_LOVEBIRD_MANGIANT_PEACH-FACED_LOVEBIRD BIRD_MAGPIE +MAGPIE_MAN GIANT_MAGPIE BIRD_KESTREL KESTREL_MAN GIANT_KESTRELBIRD_ALBATROSS ALBATROSS_MANGIANT_ALBATROSSBIRD_OWL_GREAT_HORNEDGREAT_HORNED_OWL_MANGIANT_GREAT_HORNED_OWL +BIRD_EAGLE EAGLE_MAN GIANT_EAGLE BIRD_HORNBILL HORNBILL_MANGIANT_HORNBILLBIRD_LOVEBIRD_MASKEDMASKED_LOVEBIRD_MANGIANT_MASKED_LOVEBIRD BIRD_BUSHTIT BUSHTIT_MAN GIANT_BUSHTIT DAMSELFLY DAMSELFLY_MANGIANT_DAMSELFLYMOTHMOTH_MAN +GIANT_MOTH GRASSHOPPERGRASSHOPPER_MANGIANT_GRASSHOPPER BARK_SCORPIONBARK_SCORPION_MANGIANT_BARK_SCORPIONMANTIS +MANTIS_MAN GIANT_MANTISTICKTICK_MAN +GIANT_TICKLOUSE LOUSE_MAN GIANT_LOUSETHRIPS +THRIPS_MAN GIANT_THRIPSSLUGSLUG_MAN +GIANT_SLUGMOSQUITO MOSQUITO_MANGIANT_MOSQUITOSPIDER_JUMPINGJUMPING_SPIDER_MANGIANT_JUMPING_SPIDERTERMITE +MOON_SNAILMOON_SNAIL_MANGIANT_MOON_SNAILSPIDER_BROWN_RECLUSEBROWN_RECLUSE_SPIDER_MANGIANT_BROWN_RECLUSE_SPIDERSNAIL SNAIL_MAN GIANT_SNAIL GECKO_LEOPARDLEOPARD_GECKO_MANGIANT_LEOPARD_GECKODESERT TORTOISEDESERT_TORTOISE_MANGIANT_DESERT_TORTOISE GILA_MONSTERGILA_MONSTER_MANGIANT_GILA_MONSTERDOGCATMULEDONKEYHORSECOWSHEEPPIGGOAT BIRD_CHICKENCAVY BIRD_DUCK WATER_BUFFALOREINDEER +BIRD_GOOSEYAKLLAMAALPACABIRD_GUINEAFOWLBIRD_PEAFOWL_BLUE BIRD_TURKEYRABBITCHIMERACENTAURGRIFFONFLYFLY_MAN GIANT_FLY ROACH_LARGE ROACH_MAN GIANT_ROACHBEETLE +BEETLE_MAN GIANT_BEETLEANTBUTTERFLY_MONARCHBUTTERFLY_MONARCH_MANGIANT_BUTTERFLY_MONARCHFIREFLY FIREFLY_MAN GIANT_FIREFLY DRAGONFLY DRAGONFLY_MANGIANT_DRAGONFLY HONEY_BEE BUMBLEBEE GOAT_MOUNTAINGOAT_MOUNTAIN_MANGIANT_GOAT_MOUNTAIN MARMOT_HOARYMARMOT_HOARY_MANGIANT_MARMOT_HOARYGNOME_MOUNTAIN +GNOME_DARKWALRUS +WALRUS_MAN GIANT_WALRUSFISH_LAMPREY_SEASHARK_GREAT_WHITE SHARK_FRILLSHARK_SPINY_DOGFISHSHARK_WOBBEGONG_SPOTTED SHARK_WHALE SHARK_BASKING SHARK_NURSESHARK_MAKO_SHORTFINSHARK_MAKO_LONGFIN SHARK_TIGER +SHARK_BULLSHARK_REEF_BLACKTIPSHARK_REEF_WHITETIP +SHARK_BLUESHARK_HAMMERHEAD SHARK_ANGELFISH_SKATE_COMMONFISH_RAY_MANTA FISH_STINGRAYFISH_COELACANTH FISH_STURGEONFISH_CONGER_EEL FISH_MILKFISHFISH_COD FISH_OPAHFISH_GROUPER_GIANT FISH_BLUEFISHFISH_SUNFISH_OCEANFISH_SWORDFISH FISH_MARLIN FISH_HALIBUTFISH_BARRACUDA_GREATFISH_TUNA_BLUEFINNARWHAL NARWHAL MANNARWHAL, GIANTHIPPO HIPPO_MAN GIANT_HIPPOFISH_GAR_LONGNOSE FISH_CARPFISH_TIGERFISH FISH_PIKEPLATYPUS PLATYPUS MANPLATYPUS, GIANT BEAR_GRIZZLYBEAR_GRIZZLY_MANGIANT_BEAR_GRIZZLY +BEAR_BLACKBEAR_BLACK_MANGIANT_BEAR_BLACKDEERDEER_MAN +GIANT_DEERFOXFOX_MAN GIANT_FOXRACCOON RACCOON_MAN GIANT_RACCOONMACAQUE_RHESUSMACAQUE_RHESUS_MANGIANT_MACAQUE_RHESUSCOUGAR +COUGAR_MAN GIANT_COUGARWOLFWOLF_MAN +GIANT_WOLF GROUNDHOG GROUNDHOG_MANGIANT_GROUNDHOG ALLIGATOR ALLIGATOR_MANGIANT_ALLIGATOR BIRD_BUZZARD BUZZARD_MAN GIANT_BUZZARDPANDAPANDA, GIGANTIC PANDA MANCAPYBARACAPYBARA, GIANT CAPYBARA MANBADGER +BADGER MAN BADGER, GIANTMOOSE MOOSE MAN MOOSE, GIANT RED PANDA RED PANDA MANRED PANDA, GIANTELEPHANT ELEPHANT_MANGIANT_ELEPHANTWARTHOG WARTHOG_MAN GIANT_WARTHOGLIONLION_MAN +GIANT_LIONLEOPARD LEOPARD_MAN GIANT_LEOPARDJAGUAR +JAGUAR_MAN GIANT_JAGUARTIGER TIGER_MAN GIANT_TIGERCHEETAH CHEETAH_MAN GIANT_CHEETAHGAZELLE GAZELLE_MAN GIANT_GAZELLEMANDRILL MANDRILL_MANGIANT_MANDRILL +CHIMPANZEEBONOBOGORILLA ORANGUTANGIBBON_SIAMANGGIBBON_WHITE_HANDEDGIBBON_BLACK_HANDED GIBBON_GRAYGIBBON_SILVERYGIBBON_PILEATED GIBBON_BILOUGIBBON_WHITE_BROWEDGIBBON_BLACK_CRESTED CAMEL_1_HUMPCAMEL_1_HUMP_MANGIANT_CAMEL_1_HUMP CAMEL_2_HUMPCAMEL_2_HUMP_MANGIANT_CAMEL_2_HUMPCROCODILE_SALTWATERCROCODILE_SALTWATER_MANGIANT_CROCODILE_SALTWATER BIRD_VULTURE VULTURE_MAN GIANT_VULTURE +RHINOCEROSRHINOCEROS_MANGIANT_RHINOCEROSGIRAFFE GIRAFFE_MAN GIANT_GIRAFFE HONEY BADGERHONEY BADGER MANHONEY BADGER, GIANTGIANT TORTOISEGIANT TORTOISE MANGIGANTIC TORTOISE ARMADILLO ARMADILLO MANARMADILLO, GIANTMUSKOX +MUSKOX_MAN GIANT_MUSKOXELKELK_MAN GIANT_ELK +BEAR_POLARBEAR_POLAR_MANGIANT_BEAR_POLAR WOLVERINE WOLVERINE_MANGIANT_WOLVERINE +CHINCHILLACHINCHILLA_MANGIANT_CHINCHILLA FLOATING_GUTSDRUNIAN CREEPING_EYEVORACIOUS_CAVE_CRAWLERBLIND_CAVE_OGRE +CAP_HOPPER +MAGMA_CRABCRUNDLE HUNGRY_HEAD +FLESH_BALLELK_BIRD HELMET_SNAKEGREEN_DEVOURERRUTHERERCREEPY_CRAWLERDRALTHAGIANT_EARTHWORM BLOOD_MANBUGBATMANERA +MOLEMARIANJABBERER POND_GRABBERBLIND_CAVE_BEAR CAVE_DRAGONREACHERELEMENTMAN_GABBROGORLAK CAVE_FLOATERPLUMP_HELMET_MAN CAVE_BLOBELEMENTMAN_AMETHYSTOCTOPUS OCTOPUS_MAN GIANT_OCTOPUSCRABCRAB_MAN +GIANT_CRAB LEOPARD_SEALLEOPARD_SEAL_MANGIANT_LEOPARD_SEAL +CUTTLEFISHCUTTLEFISH_MANGIANT_CUTTLEFISHORCAORCA_MAN +GIANT_ORCASPONGE +SPONGE_MAN GIANT_SPONGEHORSESHOE_CRABHORSESHOE_CRAB_MANGIANT_HORSESHOE_CRAB SPERM_WHALESPERM_WHALE_MANGIANT_SPERM_WHALE ELEPHANT_SEALELEPHANT_SEAL_MANGIANT_ELEPHANT_SEAL HARP_SEAL HARP_SEAL_MANGIANT_HARP_SEALNAUTILUS NAUTILUS_MANGIANT_NAUTILUS FOXSQUIRREL MOGHOPPER RAT_DEMONWAMBLER_FLUFFYLIZARD_RHINO_TWO_LEGGED WORM_KNUCKLESPIDER_PHANTOM FLY_ACORN +GNAT_BLOODLIZARD +LIZARD_MAN GIANT_LIZARDSKINK SKINK_MAN GIANT_SKINK CHAMELEON CHAMELEON_MANGIANT_CHAMELEONANOLE ANOLE_MAN GIANT_ANOLEIGUANA +IGUANA_MAN GIANT_IGUANA RIVER OTTER SEA OTTER OTTER_MAN GIANT_OTTERSNAPPING TURTLEALLIGATOR SNAPPING TURTLESNAPPING_TURTLE_MANGIANT_SNAPPING_TURTLEBEAVER +BEAVER_MAN GIANT_BEAVERLEECH LEECH_MAN GIANT_LEECHAXOLOTL AXOLOTL_MAN GIANT_AXOLOTLMINKMINK_MAN +GIANT_MINK POND_TURTLEPOND_TURTLE_MANGIANT_POND_TURTLERATRAT_MAN SQUIRREL_GRAYSQUIRREL_GRAY_MANGIANT_SQUIRREL_GRAY SQUIRREL_REDSQUIRREL_RED_MANGIANT_SQUIRREL_REDCHIPMUNK CHIPMUNK_MANGIANT_CHIPMUNKHAMSTER HAMSTER_MAN GIANT_HAMSTERHEDGEHOG HEDGEHOG_MANGIANT_HEDGEHOGSQUIRREL_FLYINGFLYING_SQUIRREL_MANGIANT_FLYING_SQUIRRELMUSSELOYSTER FISH_SALMONFISH_CLOWNFISH FISH_HAGFISHFISH_LAMPREY_BROOK FISH_RAY_BATFISH_RAY_THORNBACKFISH_RATFISH_SPOTTED FISH_HERRING FISH_SHAD FISH_ANCHOVYFISH_TROUT_STEELHEAD FISH_HAKE FISH_SEAHORSE FISH_GLASSEYEFISH_PUFFER_WHITE_SPOTTED FISH_SOLE FISH_FLOUNDER FISH_MACKERELJELLYFISH_SEA_NETTLESQUID SQUID MANGIGANTIC SQUID FISH_LUNGFISHFISH_LOACH_CLOWNFISH_BULLHEAD_BROWNFISH_BULLHEAD_YELLOWFISH_BULLHEAD_BLACKFISH_KNIFEFISH_BANDED FISH_CHARFISH_TROUT_RAINBOWFISH_MOLLY_SAILFIN +FISH_GUPPY +FISH_PERCHDWARFHUMANELFGOBLINKOBOLDGREMLINTROLLOGREUNICORNDRAGONSATYRCOLOSSUS_BRONZEGIANTCYCLOPSETTINMINOTAURYETI SASQUATCH BLIZZARD_MANWOLF_ICEFAIRYPIXIEBEAK_DOG GRIMELING BLENDEC_FOUL STRANGLER NIGHTWINGHARPYHYDRA MERPERSON SEA_SERPENT SEA_MONSTERBIRD_ROCCROCODILE_CAVETOAD_GIANT_CAVE OLM_GIANT BAT_GIANT RAT_GIANT RAT_LARGEMOLE_DOG_NAKED +TROGLODYTE +MOLE_GIANTIMP_FIRESPIDER_CAVE_GIANT SPIDER_CAVE FISH_CAVE CAVE_FISH_MAN LOBSTER_CAVE +SNAKE_FIREOLMOLM_MANBATBAT_MANMAGGOT_PURRINGELEMENTMAN_FIREELEMENTMAN_MAGMAELEMENTMAN_IRONELEMENTMAN_MUDBIRD_SWALLOW_CAVECAVE_SWALLOW_MANBIRD_SWALLOW_CAVE_GIANT AMPHIBIAN_MAN REPTILE_MAN SERPENT_MANANT_MAN +RODENT MAN WILD_BOAR WILD_BOAR_MANGIANT_WILD_BOARCOYOTE +COYOTE_MAN GIANT_COYOTEKANGAROO KANGAROO_MANGIANT_KANGAROOKOALA KOALA_MAN GIANT_KOALAADDER ADDER_MAN GIANT_ADDERECHIDNA ECHIDNA_MAN GIANT_ECHIDNA PORCUPINE PORCUPINE_MANGIANT_PORCUPINE KINGSNAKE KINGSNAKE_MANGIANT_KINGSNAKE GRAY_LANGURGRAY_LANGUR_MANGIANT_GRAY_LANGURBOBCAT +BOBCAT_MAN GIANT_BOBCATSKUNK SKUNK_MAN GIANT_SKUNKGREEN_TREE_FROGGREEN_TREE_FROG_MANGIANT_GREEN_TREE_FROGHAREHARE_MAN +GIANT_HARE RATTLESNAKERATTLESNAKE_MANGIANT_RATTLESNAKEWEASEL +WEASEL_MAN GIANT_WEASELCOPPERHEAD_SNAKECOPPERHEAD_SNAKE_MANGIANT_COPPERHEAD_SNAKEIBEXIBEX_MAN +GIANT_IBEXWOMBAT +WOMBAT_MAN GIANT_WOMBATDINGO DINGO_MAN GIANT_DINGOCOATI COATI_MAN GIANT_COATIOPOSSUM OPOSSUM_MAN GIANT_OPOSSUMMONGOOSE MONGOOSE_MANGIANT_MONGOOSEHYENA HYENA_MAN GIANT_HYENAANACONDA ANACONDA_MANGIANT_ANACONDAMONITOR_LIZARDMONITOR_LIZARD_MANGIANT_MONITOR_LIZARD +KING_COBRAKING_COBRA_MANGIANT_KING_COBRAOCELOT +OCELOT_MAN GIANT_OCELOTJACKAL +JACKAL_MAN GIANT_JACKALCAPUCHIN CAPUCHIN_MANGIANT_CAPUCHINSLOTH SLOTH_MAN GIANT_SLOTH SPIDER_MONKEYSPIDER_MONKEY_MANGIANT_SPIDER_MONKEYPANGOLIN PANGOLIN_MANGIANT_PANGOLIN BLACK_MAMBABLACK_MAMBA_MANGIANT_BLACK_MAMBA +BEAR_SLOTHSLOTH_BEAR_MANGIANT_SLOTH_BEARAYE-AYE AYE-AYE_MAN GIANT_AYE-AYE +BUSHMASTERBUSHMASTER_MANGIANT_BUSHMASTERPYTHON +PYTHON_MAN GIANT_PYTHONTAPIR TAPIR_MAN GIANT_TAPIRIMPALA +IMPALA_MAN GIANT_IMPALAAARDVARK AARDVARK_MANGIANT_AARDVARK LION_TAMARINLION_TAMARIN_MANGIANT_LION_TAMARINSTOAT STOAT_MAN GIANT_STOATLYNXLYNX_MAN +GIANT_LYNX"TOAD"TOAD_MAN" +GIANT_TOAD"WORM"WORM_MAN" BIRD_BLUEJAY" BLUEJAY_MAN" GIANT_BLUEJAY" BIRD_CARDINAL" CARDINAL_MAN"GIANT_CARDINAL" BIRD_GRACKLE" GRACKLE_MAN" GIANT_GRACKLE" BIRD_ORIOLE" +ORIOLE_MAN" GIANT_ORIOLE"BIRD_RW_BLACKBIRD"RW_BLACKBIRD_MAN"GIANT_RW_BLACKBIRD" BIRD_PENGUIN"BIRD_PENGUIN_LITTLE"BIRD_PENGUIN_EMPEROR" PENGUIN MAN"BIRD_PENGUIN_GIANT"BIRD_FALCON_PEREGRINE"PEREGRINE FALCON MAN"GIANT PEREGRINE FALCON" BIRD_KIWI"KIWI MAN"BIRD_KIWI_GIANT" BIRD_OSTRICH" OSTRICH MAN"BIRD_OSTRICH_GIANT" BIRD_CROW"CROW_MAN" +GIANT_CROW" +BIRD_RAVEN" RAVEN_MAN" GIANT_RAVEN"BIRD_CASSOWARY" CASSOWARY_MAN"GIANT_CASSOWARY"BIRD_KEA"KEA_MAN" GIANT_KEA"BIRD_OWL_SNOWY" SNOWY_OWL_MAN"GIANT_SNOWY_OWL"SPARROW" SPARROW_MAN" GIANT_SPARROW"BIRD_STORK_WHITE"WHITE_STORK_MAN"GIANT_WHITE_STORK" BIRD_LOON"LOON_MAN" +GIANT_LOON" BIRD_OWL_BARN" BARN_OWL_MAN"GIANT_BARN_OWL" BIRD_PARAKEET" PARAKEET_MAN"GIANT_PARAKEET" BIRD_KAKAPO" +KAKAPO_MAN" GIANT_KAKAPO"BIRD_PARROT_GREY"GREY_PARROT_MAN"GIANT_GREY_PARROT" BIRD_PUFFIN" +PUFFIN_MAN" GIANT_PUFFIN" BIRD_SWAN"SWAN_MAN" +GIANT_SWAN" BIRD_LORIKEET" LORIKEET_MAN"GIANT_LORIKEET" BIRD_WREN"WREN_MAN" +GIANT_WREN" BIRD_OSPREY" +OSPREY_MAN" GIANT_OSPREY"BIRD_EMU"EMU_MAN" GIANT_EMU"BIRD_COCKATIEL" COCKATIEL_MAN"GIANT_COCKATIEL"BIRD_LOVEBIRD_PEACH-FACED"PEACH-FACED_LOVEBIRD_MAN"GIANT_PEACH-FACED_LOVEBIRD" BIRD_MAGPIE" +MAGPIE_MAN" GIANT_MAGPIE" BIRD_KESTREL" KESTREL_MAN" GIANT_KESTREL"BIRD_ALBATROSS" ALBATROSS_MAN"GIANT_ALBATROSS"BIRD_OWL_GREAT_HORNED"GREAT_HORNED_OWL_MAN"GIANT_GREAT_HORNED_OWL" +BIRD_EAGLE" EAGLE_MAN" GIANT_EAGLE" BIRD_HORNBILL" HORNBILL_MAN"GIANT_HORNBILL"BIRD_LOVEBIRD_MASKED"MASKED_LOVEBIRD_MAN"GIANT_MASKED_LOVEBIRD" BIRD_BUSHTIT" BUSHTIT_MAN" GIANT_BUSHTIT" DAMSELFLY" DAMSELFLY_MAN"GIANT_DAMSELFLY"MOTH"MOTH_MAN" +GIANT_MOTH" GRASSHOPPER"GRASSHOPPER_MAN"GIANT_GRASSHOPPER" BARK_SCORPION"BARK_SCORPION_MAN"GIANT_BARK_SCORPION"MANTIS" +MANTIS_MAN" GIANT_MANTIS"TICK"TICK_MAN" +GIANT_TICK"LOUSE" LOUSE_MAN" GIANT_LOUSE"THRIPS" +THRIPS_MAN" GIANT_THRIPS"SLUG"SLUG_MAN" +GIANT_SLUG"MOSQUITO" MOSQUITO_MAN"GIANT_MOSQUITO"SPIDER_JUMPING"JUMPING_SPIDER_MAN"GIANT_JUMPING_SPIDER"TERMITE" +MOON_SNAIL"MOON_SNAIL_MAN"GIANT_MOON_SNAIL"SPIDER_BROWN_RECLUSE"BROWN_RECLUSE_SPIDER_MAN"GIANT_BROWN_RECLUSE_SPIDER"SNAIL" SNAIL_MAN" GIANT_SNAIL" GECKO_LEOPARD"LEOPARD_GECKO_MAN"GIANT_LEOPARD_GECKO"DESERT TORTOISE"DESERT_TORTOISE_MAN"GIANT_DESERT_TORTOISE" GILA_MONSTER"GILA_MONSTER_MAN"GIANT_GILA_MONSTER"DOG"CAT"MULE"DONKEY"HORSE"COW"SHEEP"PIG"GOAT" BIRD_CHICKEN"CAVY" BIRD_DUCK" WATER_BUFFALO"REINDEER" +BIRD_GOOSE"YAK"LLAMA"ALPACA"BIRD_GUINEAFOWL"BIRD_PEAFOWL_BLUE" BIRD_TURKEY"RABBIT"CHIMERA"CENTAUR"GRIFFON"FLY"FLY_MAN" GIANT_FLY" ROACH_LARGE" ROACH_MAN" GIANT_ROACH"BEETLE" +BEETLE_MAN" GIANT_BEETLE"ANT"BUTTERFLY_MONARCH"BUTTERFLY_MONARCH_MAN"GIANT_BUTTERFLY_MONARCH"FIREFLY" FIREFLY_MAN" GIANT_FIREFLY" DRAGONFLY" DRAGONFLY_MAN"GIANT_DRAGONFLY" HONEY_BEE" BUMBLEBEE" GOAT_MOUNTAIN"GOAT_MOUNTAIN_MAN"GIANT_GOAT_MOUNTAIN" MARMOT_HOARY"MARMOT_HOARY_MAN"GIANT_MARMOT_HOARY"GNOME_MOUNTAIN" +GNOME_DARK"WALRUS" +WALRUS_MAN" GIANT_WALRUS"FISH_LAMPREY_SEA"SHARK_GREAT_WHITE" SHARK_FRILL"SHARK_SPINY_DOGFISH"SHARK_WOBBEGONG_SPOTTED" SHARK_WHALE" SHARK_BASKING" SHARK_NURSE"SHARK_MAKO_SHORTFIN"SHARK_MAKO_LONGFIN" SHARK_TIGER" +SHARK_BULL"SHARK_REEF_BLACKTIP"SHARK_REEF_WHITETIP" +SHARK_BLUE"SHARK_HAMMERHEAD" SHARK_ANGEL"FISH_SKATE_COMMON"FISH_RAY_MANTA" FISH_STINGRAY"FISH_COELACANTH" FISH_STURGEON"FISH_CONGER_EEL" FISH_MILKFISH"FISH_COD" FISH_OPAH"FISH_GROUPER_GIANT" FISH_BLUEFISH"FISH_SUNFISH_OCEAN"FISH_SWORDFISH" FISH_MARLIN" FISH_HALIBUT"FISH_BARRACUDA_GREAT"FISH_TUNA_BLUEFIN"NARWHAL" NARWHAL MAN"NARWHAL, GIANT"HIPPO" HIPPO_MAN" GIANT_HIPPO"FISH_GAR_LONGNOSE" FISH_CARP"FISH_TIGERFISH" FISH_PIKE"PLATYPUS" PLATYPUS MAN"PLATYPUS, GIANT" BEAR_GRIZZLY"BEAR_GRIZZLY_MAN"GIANT_BEAR_GRIZZLY" +BEAR_BLACK"BEAR_BLACK_MAN"GIANT_BEAR_BLACK"DEER"DEER_MAN" +GIANT_DEER"FOX"FOX_MAN" GIANT_FOX"RACCOON" RACCOON_MAN" GIANT_RACCOON"MACAQUE_RHESUS"MACAQUE_RHESUS_MAN"GIANT_MACAQUE_RHESUS"COUGAR" +COUGAR_MAN" GIANT_COUGAR"WOLF"WOLF_MAN" +GIANT_WOLF" GROUNDHOG" GROUNDHOG_MAN"GIANT_GROUNDHOG" ALLIGATOR" ALLIGATOR_MAN"GIANT_ALLIGATOR" BIRD_BUZZARD" BUZZARD_MAN" GIANT_BUZZARD"PANDA"PANDA, GIGANTIC" PANDA MAN"CAPYBARA"CAPYBARA, GIANT" CAPYBARA MAN"BADGER" +BADGER MAN" BADGER, GIANT"MOOSE" MOOSE MAN" MOOSE, GIANT" RED PANDA" RED PANDA MAN"RED PANDA, GIANT"ELEPHANT" ELEPHANT_MAN"GIANT_ELEPHANT"WARTHOG" WARTHOG_MAN" GIANT_WARTHOG"LION"LION_MAN" +GIANT_LION"LEOPARD" LEOPARD_MAN" GIANT_LEOPARD"JAGUAR" +JAGUAR_MAN" GIANT_JAGUAR"TIGER" TIGER_MAN" GIANT_TIGER"CHEETAH" CHEETAH_MAN" GIANT_CHEETAH"GAZELLE" GAZELLE_MAN" GIANT_GAZELLE"MANDRILL" MANDRILL_MAN"GIANT_MANDRILL" +CHIMPANZEE"BONOBO"GORILLA" ORANGUTAN"GIBBON_SIAMANG"GIBBON_WHITE_HANDED"GIBBON_BLACK_HANDED" GIBBON_GRAY"GIBBON_SILVERY"GIBBON_PILEATED" GIBBON_BILOU"GIBBON_WHITE_BROWED"GIBBON_BLACK_CRESTED" CAMEL_1_HUMP"CAMEL_1_HUMP_MAN"GIANT_CAMEL_1_HUMP" CAMEL_2_HUMP"CAMEL_2_HUMP_MAN"GIANT_CAMEL_2_HUMP"CROCODILE_SALTWATER"CROCODILE_SALTWATER_MAN"GIANT_CROCODILE_SALTWATER" BIRD_VULTURE" VULTURE_MAN" GIANT_VULTURE" +RHINOCEROS"RHINOCEROS_MAN"GIANT_RHINOCEROS"GIRAFFE" GIRAFFE_MAN" GIANT_GIRAFFE" HONEY BADGER"HONEY BADGER MAN"HONEY BADGER, GIANT"GIANT TORTOISE"GIANT TORTOISE MAN"GIGANTIC TORTOISE" ARMADILLO" ARMADILLO MAN"ARMADILLO, GIANT"MUSKOX" +MUSKOX_MAN" GIANT_MUSKOX"ELK"ELK_MAN" GIANT_ELK" +BEAR_POLAR"BEAR_POLAR_MAN"GIANT_BEAR_POLAR" WOLVERINE" WOLVERINE_MAN"GIANT_WOLVERINE" +CHINCHILLA"CHINCHILLA_MAN"GIANT_CHINCHILLA" FLOATING_GUTS"DRUNIAN" CREEPING_EYE"VORACIOUS_CAVE_CRAWLER"BLIND_CAVE_OGRE" +CAP_HOPPER" +MAGMA_CRAB"CRUNDLE" HUNGRY_HEAD" +FLESH_BALL"ELK_BIRD" HELMET_SNAKE"GREEN_DEVOURER"RUTHERER"CREEPY_CRAWLER"DRALTHA"GIANT_EARTHWORM" BLOOD_MAN"BUGBAT"MANERA" +MOLEMARIAN"JABBERER" POND_GRABBER"BLIND_CAVE_BEAR" CAVE_DRAGON"REACHER"ELEMENTMAN_GABBRO"GORLAK" CAVE_FLOATER"PLUMP_HELMET_MAN" CAVE_BLOB"ELEMENTMAN_AMETHYST"OCTOPUS" OCTOPUS_MAN" GIANT_OCTOPUS"CRAB"CRAB_MAN" +GIANT_CRAB" LEOPARD_SEAL"LEOPARD_SEAL_MAN"GIANT_LEOPARD_SEAL" +CUTTLEFISH"CUTTLEFISH_MAN"GIANT_CUTTLEFISH"ORCA"ORCA_MAN" +GIANT_ORCA"SPONGE" +SPONGE_MAN" GIANT_SPONGE"HORSESHOE_CRAB"HORSESHOE_CRAB_MAN"GIANT_HORSESHOE_CRAB" SPERM_WHALE"SPERM_WHALE_MAN"GIANT_SPERM_WHALE" ELEPHANT_SEAL"ELEPHANT_SEAL_MAN"GIANT_ELEPHANT_SEAL" HARP_SEAL" HARP_SEAL_MAN"GIANT_HARP_SEAL"NAUTILUS" NAUTILUS_MAN"GIANT_NAUTILUS" FOXSQUIRREL" MOGHOPPER" RAT_DEMON"WAMBLER_FLUFFY"LIZARD_RHINO_TWO_LEGGED" WORM_KNUCKLE"SPIDER_PHANTOM" FLY_ACORN" +GNAT_BLOOD"LIZARD" +LIZARD_MAN" GIANT_LIZARD"SKINK" SKINK_MAN" GIANT_SKINK" CHAMELEON" CHAMELEON_MAN"GIANT_CHAMELEON"ANOLE" ANOLE_MAN" GIANT_ANOLE"IGUANA" +IGUANA_MAN" GIANT_IGUANA" RIVER OTTER" SEA OTTER" OTTER_MAN" GIANT_OTTER"SNAPPING TURTLE"ALLIGATOR SNAPPING TURTLE"SNAPPING_TURTLE_MAN"GIANT_SNAPPING_TURTLE"BEAVER" +BEAVER_MAN" GIANT_BEAVER"LEECH" LEECH_MAN" GIANT_LEECH"AXOLOTL" AXOLOTL_MAN" GIANT_AXOLOTL"MINK"MINK_MAN" +GIANT_MINK" POND_TURTLE"POND_TURTLE_MAN"GIANT_POND_TURTLE"RAT"RAT_MAN" SQUIRREL_GRAY"SQUIRREL_GRAY_MAN"GIANT_SQUIRREL_GRAY" SQUIRREL_RED"SQUIRREL_RED_MAN"GIANT_SQUIRREL_RED"CHIPMUNK" CHIPMUNK_MAN"GIANT_CHIPMUNK"HAMSTER" HAMSTER_MAN" GIANT_HAMSTER"HEDGEHOG" HEDGEHOG_MAN"GIANT_HEDGEHOG"SQUIRREL_FLYING"FLYING_SQUIRREL_MAN"GIANT_FLYING_SQUIRREL"MUSSEL"OYSTER" FISH_SALMON"FISH_CLOWNFISH" FISH_HAGFISH"FISH_LAMPREY_BROOK" FISH_RAY_BAT"FISH_RAY_THORNBACK"FISH_RATFISH_SPOTTED" FISH_HERRING" FISH_SHAD" FISH_ANCHOVY"FISH_TROUT_STEELHEAD" FISH_HAKE" FISH_SEAHORSE" FISH_GLASSEYE"FISH_PUFFER_WHITE_SPOTTED" FISH_SOLE" FISH_FLOUNDER" FISH_MACKEREL"JELLYFISH_SEA_NETTLE"SQUID" SQUID MAN"GIGANTIC SQUID" FISH_LUNGFISH"FISH_LOACH_CLOWN"FISH_BULLHEAD_BROWN"FISH_BULLHEAD_YELLOW"FISH_BULLHEAD_BLACK"FISH_KNIFEFISH_BANDED" FISH_CHAR"FISH_TROUT_RAINBOW"FISH_MOLLY_SAILFIN" +FISH_GUPPY" +FISH_PERCH"DWARF"HUMAN"ELF"GOBLIN"KOBOLD"GREMLIN"TROLL"OGRE"UNICORN"DRAGON"SATYR"COLOSSUS_BRONZE"GIANT"CYCLOPS"ETTIN"MINOTAUR"YETI" SASQUATCH" BLIZZARD_MAN"WOLF_ICE"FAIRY"PIXIE"BEAK_DOG" GRIMELING" BLENDEC_FOUL" STRANGLER" NIGHTWING"HARPY"HYDRA" MERPERSON" SEA_SERPENT" SEA_MONSTER"BIRD_ROC"CROCODILE_CAVE"TOAD_GIANT_CAVE" OLM_GIANT" BAT_GIANT" RAT_GIANT" RAT_LARGE"MOLE_DOG_NAKED" +TROGLODYTE" +MOLE_GIANT"IMP_FIRE"SPIDER_CAVE_GIANT" SPIDER_CAVE" FISH_CAVE" CAVE_FISH_MAN" LOBSTER_CAVE" +SNAKE_FIRE"OLM"OLM_MAN"BAT"BAT_MAN"MAGGOT_PURRING"ELEMENTMAN_FIRE"ELEMENTMAN_MAGMA"ELEMENTMAN_IRON"ELEMENTMAN_MUD"BIRD_SWALLOW_CAVE"CAVE_SWALLOW_MAN"BIRD_SWALLOW_CAVE_GIANT" AMPHIBIAN_MAN" REPTILE_MAN" SERPENT_MAN"ANT_MAN" +RODENT MAN" WILD_BOAR" WILD_BOAR_MAN"GIANT_WILD_BOAR"COYOTE" +COYOTE_MAN" GIANT_COYOTE"KANGAROO" KANGAROO_MAN"GIANT_KANGAROO"KOALA" KOALA_MAN" GIANT_KOALA"ADDER" ADDER_MAN" GIANT_ADDER"ECHIDNA" ECHIDNA_MAN" GIANT_ECHIDNA" PORCUPINE" PORCUPINE_MAN"GIANT_PORCUPINE" KINGSNAKE" KINGSNAKE_MAN"GIANT_KINGSNAKE" GRAY_LANGUR"GRAY_LANGUR_MAN"GIANT_GRAY_LANGUR"BOBCAT" +BOBCAT_MAN" GIANT_BOBCAT"SKUNK" SKUNK_MAN" GIANT_SKUNK"GREEN_TREE_FROG"GREEN_TREE_FROG_MAN"GIANT_GREEN_TREE_FROG"HARE"HARE_MAN" +GIANT_HARE" RATTLESNAKE"RATTLESNAKE_MAN"GIANT_RATTLESNAKE"WEASEL" +WEASEL_MAN" GIANT_WEASEL"COPPERHEAD_SNAKE"COPPERHEAD_SNAKE_MAN"GIANT_COPPERHEAD_SNAKE"IBEX"IBEX_MAN" +GIANT_IBEX"WOMBAT" +WOMBAT_MAN" GIANT_WOMBAT"DINGO" DINGO_MAN" GIANT_DINGO"COATI" COATI_MAN" GIANT_COATI"OPOSSUM" OPOSSUM_MAN" GIANT_OPOSSUM"MONGOOSE" MONGOOSE_MAN"GIANT_MONGOOSE"HYENA" HYENA_MAN" GIANT_HYENA"ANACONDA" ANACONDA_MAN"GIANT_ANACONDA"MONITOR_LIZARD"MONITOR_LIZARD_MAN"GIANT_MONITOR_LIZARD" +KING_COBRA"KING_COBRA_MAN"GIANT_KING_COBRA"OCELOT" +OCELOT_MAN" GIANT_OCELOT"JACKAL" +JACKAL_MAN" GIANT_JACKAL"CAPUCHIN" CAPUCHIN_MAN"GIANT_CAPUCHIN"SLOTH" SLOTH_MAN" GIANT_SLOTH" SPIDER_MONKEY"SPIDER_MONKEY_MAN"GIANT_SPIDER_MONKEY"PANGOLIN" PANGOLIN_MAN"GIANT_PANGOLIN" BLACK_MAMBA"BLACK_MAMBA_MAN"GIANT_BLACK_MAMBA" +BEAR_SLOTH"SLOTH_BEAR_MAN"GIANT_SLOTH_BEAR"AYE-AYE" AYE-AYE_MAN" GIANT_AYE-AYE" +BUSHMASTER"BUSHMASTER_MAN"GIANT_BUSHMASTER"PYTHON" +PYTHON_MAN" GIANT_PYTHON"TAPIR" TAPIR_MAN" GIANT_TAPIR"IMPALA" +IMPALA_MAN" GIANT_IMPALA"AARDVARK" AARDVARK_MAN"GIANT_AARDVARK" LION_TAMARIN"LION_TAMARIN_MAN"GIANT_LION_TAMARIN"STOAT" STOAT_MAN" GIANT_STOAT"LYNX"LYNX_MAN" +GIANT_LYNX*TOAD*TOAD_MAN* +GIANT_TOAD*WORM*WORM_MAN* BIRD_BLUEJAY* BLUEJAY_MAN* GIANT_BLUEJAY* BIRD_CARDINAL* CARDINAL_MAN*GIANT_CARDINAL* BIRD_GRACKLE* GRACKLE_MAN* GIANT_GRACKLE* BIRD_ORIOLE* +ORIOLE_MAN* GIANT_ORIOLE*BIRD_RW_BLACKBIRD*RW_BLACKBIRD_MAN*GIANT_RW_BLACKBIRD* BIRD_PENGUIN*BIRD_PENGUIN_LITTLE*BIRD_PENGUIN_EMPEROR* PENGUIN MAN*BIRD_PENGUIN_GIANT*BIRD_FALCON_PEREGRINE*PEREGRINE FALCON MAN*GIANT PEREGRINE FALCON* BIRD_KIWI*KIWI MAN*BIRD_KIWI_GIANT* BIRD_OSTRICH* OSTRICH MAN*BIRD_OSTRICH_GIANT* BIRD_CROW*CROW_MAN* +GIANT_CROW* +BIRD_RAVEN* RAVEN_MAN* GIANT_RAVEN*BIRD_CASSOWARY* CASSOWARY_MAN*GIANT_CASSOWARY*BIRD_KEA*KEA_MAN* GIANT_KEA*BIRD_OWL_SNOWY* SNOWY_OWL_MAN*GIANT_SNOWY_OWL*SPARROW* SPARROW_MAN* GIANT_SPARROW*BIRD_STORK_WHITE*WHITE_STORK_MAN*GIANT_WHITE_STORK* BIRD_LOON*LOON_MAN* +GIANT_LOON* BIRD_OWL_BARN* BARN_OWL_MAN*GIANT_BARN_OWL* BIRD_PARAKEET* PARAKEET_MAN*GIANT_PARAKEET* BIRD_KAKAPO* +KAKAPO_MAN* GIANT_KAKAPO*BIRD_PARROT_GREY*GREY_PARROT_MAN*GIANT_GREY_PARROT* BIRD_PUFFIN* +PUFFIN_MAN* GIANT_PUFFIN* BIRD_SWAN*SWAN_MAN* +GIANT_SWAN* BIRD_LORIKEET* LORIKEET_MAN*GIANT_LORIKEET* BIRD_WREN*WREN_MAN* +GIANT_WREN* BIRD_OSPREY* +OSPREY_MAN* GIANT_OSPREY*BIRD_EMU*EMU_MAN* GIANT_EMU*BIRD_COCKATIEL* COCKATIEL_MAN*GIANT_COCKATIEL*BIRD_LOVEBIRD_PEACH-FACED*PEACH-FACED_LOVEBIRD_MAN*GIANT_PEACH-FACED_LOVEBIRD* BIRD_MAGPIE* +MAGPIE_MAN* GIANT_MAGPIE* BIRD_KESTREL* KESTREL_MAN* GIANT_KESTREL*BIRD_ALBATROSS* ALBATROSS_MAN*GIANT_ALBATROSS*BIRD_OWL_GREAT_HORNED*GREAT_HORNED_OWL_MAN*GIANT_GREAT_HORNED_OWL* +BIRD_EAGLE* EAGLE_MAN* GIANT_EAGLE* BIRD_HORNBILL* HORNBILL_MAN*GIANT_HORNBILL*BIRD_LOVEBIRD_MASKED*MASKED_LOVEBIRD_MAN*GIANT_MASKED_LOVEBIRD* BIRD_BUSHTIT* BUSHTIT_MAN* GIANT_BUSHTIT* DAMSELFLY* DAMSELFLY_MAN*GIANT_DAMSELFLY*MOTH*MOTH_MAN* +GIANT_MOTH* GRASSHOPPER*GRASSHOPPER_MAN*GIANT_GRASSHOPPER* BARK_SCORPION*BARK_SCORPION_MAN*GIANT_BARK_SCORPION*MANTIS* +MANTIS_MAN* GIANT_MANTIS*TICK*TICK_MAN* +GIANT_TICK*LOUSE* LOUSE_MAN* GIANT_LOUSE*THRIPS* +THRIPS_MAN* GIANT_THRIPS*SLUG*SLUG_MAN* +GIANT_SLUG*MOSQUITO* MOSQUITO_MAN*GIANT_MOSQUITO*SPIDER_JUMPING*JUMPING_SPIDER_MAN*GIANT_JUMPING_SPIDER*TERMITE* +MOON_SNAIL*MOON_SNAIL_MAN*GIANT_MOON_SNAIL*SPIDER_BROWN_RECLUSE*BROWN_RECLUSE_SPIDER_MAN*GIANT_BROWN_RECLUSE_SPIDER*SNAIL* SNAIL_MAN* GIANT_SNAIL* GECKO_LEOPARD*LEOPARD_GECKO_MAN*GIANT_LEOPARD_GECKO*DESERT TORTOISE*DESERT_TORTOISE_MAN*GIANT_DESERT_TORTOISE* GILA_MONSTER*GILA_MONSTER_MAN*GIANT_GILA_MONSTER*DOG*CAT*MULE*DONKEY*HORSE*COW*SHEEP*PIG*GOAT* BIRD_CHICKEN*CAVY* BIRD_DUCK* WATER_BUFFALO*REINDEER* +BIRD_GOOSE*YAK*LLAMA*ALPACA*BIRD_GUINEAFOWL*BIRD_PEAFOWL_BLUE* BIRD_TURKEY*RABBIT*CHIMERA*CENTAUR*GRIFFON*FLY*FLY_MAN* GIANT_FLY* ROACH_LARGE* ROACH_MAN* GIANT_ROACH*BEETLE* +BEETLE_MAN* GIANT_BEETLE*ANT*BUTTERFLY_MONARCH*BUTTERFLY_MONARCH_MAN*GIANT_BUTTERFLY_MONARCH*FIREFLY* FIREFLY_MAN* GIANT_FIREFLY* DRAGONFLY* DRAGONFLY_MAN*GIANT_DRAGONFLY* HONEY_BEE* BUMBLEBEE* GOAT_MOUNTAIN*GOAT_MOUNTAIN_MAN*GIANT_GOAT_MOUNTAIN* MARMOT_HOARY*MARMOT_HOARY_MAN*GIANT_MARMOT_HOARY*GNOME_MOUNTAIN* +GNOME_DARK*WALRUS* +WALRUS_MAN* GIANT_WALRUS*FISH_LAMPREY_SEA*SHARK_GREAT_WHITE* SHARK_FRILL*SHARK_SPINY_DOGFISH*SHARK_WOBBEGONG_SPOTTED* SHARK_WHALE* SHARK_BASKING* SHARK_NURSE*SHARK_MAKO_SHORTFIN*SHARK_MAKO_LONGFIN* SHARK_TIGER* +SHARK_BULL*SHARK_REEF_BLACKTIP*SHARK_REEF_WHITETIP* +SHARK_BLUE*SHARK_HAMMERHEAD* SHARK_ANGEL*FISH_SKATE_COMMON*FISH_RAY_MANTA* FISH_STINGRAY*FISH_COELACANTH* FISH_STURGEON*FISH_CONGER_EEL* FISH_MILKFISH*FISH_COD* FISH_OPAH*FISH_GROUPER_GIANT* FISH_BLUEFISH*FISH_SUNFISH_OCEAN*FISH_SWORDFISH* FISH_MARLIN* FISH_HALIBUT*FISH_BARRACUDA_GREAT*FISH_TUNA_BLUEFIN*NARWHAL* NARWHAL MAN*NARWHAL, GIANT*HIPPO* HIPPO_MAN* GIANT_HIPPO*FISH_GAR_LONGNOSE* FISH_CARP*FISH_TIGERFISH* FISH_PIKE*PLATYPUS* PLATYPUS MAN*PLATYPUS, GIANT* BEAR_GRIZZLY*BEAR_GRIZZLY_MAN*GIANT_BEAR_GRIZZLY* +BEAR_BLACK*BEAR_BLACK_MAN*GIANT_BEAR_BLACK*DEER*DEER_MAN* +GIANT_DEER*FOX*FOX_MAN* GIANT_FOX*RACCOON* RACCOON_MAN* GIANT_RACCOON*MACAQUE_RHESUS*MACAQUE_RHESUS_MAN*GIANT_MACAQUE_RHESUS*COUGAR* +COUGAR_MAN* GIANT_COUGAR*WOLF*WOLF_MAN* +GIANT_WOLF* GROUNDHOG* GROUNDHOG_MAN*GIANT_GROUNDHOG* ALLIGATOR* ALLIGATOR_MAN*GIANT_ALLIGATOR* BIRD_BUZZARD* BUZZARD_MAN* GIANT_BUZZARD*PANDA*PANDA, GIGANTIC* PANDA MAN*CAPYBARA*CAPYBARA, GIANT* CAPYBARA MAN*BADGER* +BADGER MAN* BADGER, GIANT*MOOSE* MOOSE MAN* MOOSE, GIANT* RED PANDA* RED PANDA MAN*RED PANDA, GIANT*ELEPHANT* ELEPHANT_MAN*GIANT_ELEPHANT*WARTHOG* WARTHOG_MAN* GIANT_WARTHOG*LION*LION_MAN* +GIANT_LION*LEOPARD* LEOPARD_MAN* GIANT_LEOPARD*JAGUAR* +JAGUAR_MAN* GIANT_JAGUAR*TIGER* TIGER_MAN* GIANT_TIGER*CHEETAH* CHEETAH_MAN* GIANT_CHEETAH*GAZELLE* GAZELLE_MAN* GIANT_GAZELLE*MANDRILL* MANDRILL_MAN*GIANT_MANDRILL* +CHIMPANZEE*BONOBO*GORILLA* ORANGUTAN*GIBBON_SIAMANG*GIBBON_WHITE_HANDED*GIBBON_BLACK_HANDED* GIBBON_GRAY*GIBBON_SILVERY*GIBBON_PILEATED* GIBBON_BILOU*GIBBON_WHITE_BROWED*GIBBON_BLACK_CRESTED* CAMEL_1_HUMP*CAMEL_1_HUMP_MAN*GIANT_CAMEL_1_HUMP* CAMEL_2_HUMP*CAMEL_2_HUMP_MAN*GIANT_CAMEL_2_HUMP*CROCODILE_SALTWATER*CROCODILE_SALTWATER_MAN*GIANT_CROCODILE_SALTWATER* BIRD_VULTURE* VULTURE_MAN* GIANT_VULTURE* +RHINOCEROS*RHINOCEROS_MAN*GIANT_RHINOCEROS*GIRAFFE* GIRAFFE_MAN* GIANT_GIRAFFE* HONEY BADGER*HONEY BADGER MAN*HONEY BADGER, GIANT*GIANT TORTOISE*GIANT TORTOISE MAN*GIGANTIC TORTOISE* ARMADILLO* ARMADILLO MAN*ARMADILLO, GIANT*MUSKOX* +MUSKOX_MAN* GIANT_MUSKOX*ELK*ELK_MAN* GIANT_ELK* +BEAR_POLAR*BEAR_POLAR_MAN*GIANT_BEAR_POLAR* WOLVERINE* WOLVERINE_MAN*GIANT_WOLVERINE* +CHINCHILLA*CHINCHILLA_MAN*GIANT_CHINCHILLA* FLOATING_GUTS*DRUNIAN* CREEPING_EYE*VORACIOUS_CAVE_CRAWLER*BLIND_CAVE_OGRE* +CAP_HOPPER* +MAGMA_CRAB*CRUNDLE* HUNGRY_HEAD* +FLESH_BALL*ELK_BIRD* HELMET_SNAKE*GREEN_DEVOURER*RUTHERER*CREEPY_CRAWLER*DRALTHA*GIANT_EARTHWORM* BLOOD_MAN*BUGBAT*MANERA* +MOLEMARIAN*JABBERER* POND_GRABBER*BLIND_CAVE_BEAR* CAVE_DRAGON*REACHER*ELEMENTMAN_GABBRO*GORLAK* CAVE_FLOATER*PLUMP_HELMET_MAN* CAVE_BLOB*ELEMENTMAN_AMETHYST*OCTOPUS* OCTOPUS_MAN* GIANT_OCTOPUS*CRAB*CRAB_MAN* +GIANT_CRAB* LEOPARD_SEAL*LEOPARD_SEAL_MAN*GIANT_LEOPARD_SEAL* +CUTTLEFISH*CUTTLEFISH_MAN*GIANT_CUTTLEFISH*ORCA*ORCA_MAN* +GIANT_ORCA*SPONGE* +SPONGE_MAN* GIANT_SPONGE*HORSESHOE_CRAB*HORSESHOE_CRAB_MAN*GIANT_HORSESHOE_CRAB* SPERM_WHALE*SPERM_WHALE_MAN*GIANT_SPERM_WHALE* ELEPHANT_SEAL*ELEPHANT_SEAL_MAN*GIANT_ELEPHANT_SEAL* HARP_SEAL* HARP_SEAL_MAN*GIANT_HARP_SEAL*NAUTILUS* NAUTILUS_MAN*GIANT_NAUTILUS* FOXSQUIRREL* MOGHOPPER* RAT_DEMON*WAMBLER_FLUFFY*LIZARD_RHINO_TWO_LEGGED* WORM_KNUCKLE*SPIDER_PHANTOM* FLY_ACORN* +GNAT_BLOOD*LIZARD* +LIZARD_MAN* GIANT_LIZARD*SKINK* SKINK_MAN* GIANT_SKINK* CHAMELEON* CHAMELEON_MAN*GIANT_CHAMELEON*ANOLE* ANOLE_MAN* GIANT_ANOLE*IGUANA* +IGUANA_MAN* GIANT_IGUANA* RIVER OTTER* SEA OTTER* OTTER_MAN* GIANT_OTTER*SNAPPING TURTLE*ALLIGATOR SNAPPING TURTLE*SNAPPING_TURTLE_MAN*GIANT_SNAPPING_TURTLE*BEAVER* +BEAVER_MAN* GIANT_BEAVER*LEECH* LEECH_MAN* GIANT_LEECH*AXOLOTL* AXOLOTL_MAN* GIANT_AXOLOTL*MINK*MINK_MAN* +GIANT_MINK* POND_TURTLE*POND_TURTLE_MAN*GIANT_POND_TURTLE*RAT*RAT_MAN* SQUIRREL_GRAY*SQUIRREL_GRAY_MAN*GIANT_SQUIRREL_GRAY* SQUIRREL_RED*SQUIRREL_RED_MAN*GIANT_SQUIRREL_RED*CHIPMUNK* CHIPMUNK_MAN*GIANT_CHIPMUNK*HAMSTER* HAMSTER_MAN* GIANT_HAMSTER*HEDGEHOG* HEDGEHOG_MAN*GIANT_HEDGEHOG*SQUIRREL_FLYING*FLYING_SQUIRREL_MAN*GIANT_FLYING_SQUIRREL*MUSSEL*OYSTER* FISH_SALMON*FISH_CLOWNFISH* FISH_HAGFISH*FISH_LAMPREY_BROOK* FISH_RAY_BAT*FISH_RAY_THORNBACK*FISH_RATFISH_SPOTTED* FISH_HERRING* FISH_SHAD* FISH_ANCHOVY*FISH_TROUT_STEELHEAD* FISH_HAKE* FISH_SEAHORSE* FISH_GLASSEYE*FISH_PUFFER_WHITE_SPOTTED* FISH_SOLE* FISH_FLOUNDER* FISH_MACKEREL*JELLYFISH_SEA_NETTLE*SQUID* SQUID MAN*GIGANTIC SQUID* FISH_LUNGFISH*FISH_LOACH_CLOWN*FISH_BULLHEAD_BROWN*FISH_BULLHEAD_YELLOW*FISH_BULLHEAD_BLACK*FISH_KNIFEFISH_BANDED* FISH_CHAR*FISH_TROUT_RAINBOW*FISH_MOLLY_SAILFIN* +FISH_GUPPY* +FISH_PERCH*DWARF*HUMAN*ELF*GOBLIN*KOBOLD*GREMLIN*TROLL*OGRE*UNICORN*DRAGON*SATYR*COLOSSUS_BRONZE*GIANT*CYCLOPS*ETTIN*MINOTAUR*YETI* SASQUATCH* BLIZZARD_MAN*WOLF_ICE*FAIRY*PIXIE*BEAK_DOG* GRIMELING* BLENDEC_FOUL* STRANGLER* NIGHTWING*HARPY*HYDRA* MERPERSON* SEA_SERPENT* SEA_MONSTER*BIRD_ROC*CROCODILE_CAVE*TOAD_GIANT_CAVE* OLM_GIANT* BAT_GIANT* RAT_GIANT* RAT_LARGE*MOLE_DOG_NAKED* +TROGLODYTE* +MOLE_GIANT*IMP_FIRE*SPIDER_CAVE_GIANT* SPIDER_CAVE* FISH_CAVE* CAVE_FISH_MAN* LOBSTER_CAVE* +SNAKE_FIRE*OLM*OLM_MAN*BAT*BAT_MAN*MAGGOT_PURRING*ELEMENTMAN_FIRE*ELEMENTMAN_MAGMA*ELEMENTMAN_IRON*ELEMENTMAN_MUD*BIRD_SWALLOW_CAVE*CAVE_SWALLOW_MAN*BIRD_SWALLOW_CAVE_GIANT* AMPHIBIAN_MAN* REPTILE_MAN* SERPENT_MAN*ANT_MAN* +RODENT MAN* WILD_BOAR* WILD_BOAR_MAN*GIANT_WILD_BOAR*COYOTE* +COYOTE_MAN* GIANT_COYOTE*KANGAROO* KANGAROO_MAN*GIANT_KANGAROO*KOALA* KOALA_MAN* GIANT_KOALA*ADDER* ADDER_MAN* GIANT_ADDER*ECHIDNA* ECHIDNA_MAN* GIANT_ECHIDNA* PORCUPINE* PORCUPINE_MAN*GIANT_PORCUPINE* KINGSNAKE* KINGSNAKE_MAN*GIANT_KINGSNAKE* GRAY_LANGUR*GRAY_LANGUR_MAN*GIANT_GRAY_LANGUR*BOBCAT* +BOBCAT_MAN* GIANT_BOBCAT*SKUNK* SKUNK_MAN* GIANT_SKUNK*GREEN_TREE_FROG*GREEN_TREE_FROG_MAN*GIANT_GREEN_TREE_FROG*HARE*HARE_MAN* +GIANT_HARE* RATTLESNAKE*RATTLESNAKE_MAN*GIANT_RATTLESNAKE*WEASEL* +WEASEL_MAN* GIANT_WEASEL*COPPERHEAD_SNAKE*COPPERHEAD_SNAKE_MAN*GIANT_COPPERHEAD_SNAKE*IBEX*IBEX_MAN* +GIANT_IBEX*WOMBAT* +WOMBAT_MAN* GIANT_WOMBAT*DINGO* DINGO_MAN* GIANT_DINGO*COATI* COATI_MAN* GIANT_COATI*OPOSSUM* OPOSSUM_MAN* GIANT_OPOSSUM*MONGOOSE* MONGOOSE_MAN*GIANT_MONGOOSE*HYENA* HYENA_MAN* GIANT_HYENA*ANACONDA* ANACONDA_MAN*GIANT_ANACONDA*MONITOR_LIZARD*MONITOR_LIZARD_MAN*GIANT_MONITOR_LIZARD* +KING_COBRA*KING_COBRA_MAN*GIANT_KING_COBRA*OCELOT* +OCELOT_MAN* GIANT_OCELOT*JACKAL* +JACKAL_MAN* GIANT_JACKAL*CAPUCHIN* CAPUCHIN_MAN*GIANT_CAPUCHIN*SLOTH* SLOTH_MAN* GIANT_SLOTH* SPIDER_MONKEY*SPIDER_MONKEY_MAN*GIANT_SPIDER_MONKEY*PANGOLIN* PANGOLIN_MAN*GIANT_PANGOLIN* BLACK_MAMBA*BLACK_MAMBA_MAN*GIANT_BLACK_MAMBA* +BEAR_SLOTH*SLOTH_BEAR_MAN*GIANT_SLOTH_BEAR*AYE-AYE* AYE-AYE_MAN* GIANT_AYE-AYE* +BUSHMASTER*BUSHMASTER_MAN*GIANT_BUSHMASTER*PYTHON* +PYTHON_MAN* GIANT_PYTHON*TAPIR* TAPIR_MAN* GIANT_TAPIR*IMPALA* +IMPALA_MAN* GIANT_IMPALA*AARDVARK* AARDVARK_MAN*GIANT_AARDVARK* LION_TAMARIN*LION_TAMARIN_MAN*GIANT_LION_TAMARIN*STOAT* STOAT_MAN* GIANT_STOAT*LYNX*LYNX_MAN* +GIANT_LYNX2TOAD2TOAD_MAN2 +GIANT_TOAD2WORM2WORM_MAN2 BIRD_BLUEJAY2 BLUEJAY_MAN2 GIANT_BLUEJAY2 BIRD_CARDINAL2 CARDINAL_MAN2GIANT_CARDINAL2 BIRD_GRACKLE2 GRACKLE_MAN2 GIANT_GRACKLE2 BIRD_ORIOLE2 +ORIOLE_MAN2 GIANT_ORIOLE2BIRD_RW_BLACKBIRD2RW_BLACKBIRD_MAN2GIANT_RW_BLACKBIRD2 BIRD_PENGUIN2BIRD_PENGUIN_LITTLE2BIRD_PENGUIN_EMPEROR2 PENGUIN MAN2BIRD_PENGUIN_GIANT2BIRD_FALCON_PEREGRINE2PEREGRINE FALCON MAN2GIANT PEREGRINE FALCON2 BIRD_KIWI2KIWI MAN2BIRD_KIWI_GIANT2 BIRD_OSTRICH2 OSTRICH MAN2BIRD_OSTRICH_GIANT2 BIRD_CROW2CROW_MAN2 +GIANT_CROW2 +BIRD_RAVEN2 RAVEN_MAN2 GIANT_RAVEN2BIRD_CASSOWARY2 CASSOWARY_MAN2GIANT_CASSOWARY2BIRD_KEA2KEA_MAN2 GIANT_KEA2BIRD_OWL_SNOWY2 SNOWY_OWL_MAN2GIANT_SNOWY_OWL2SPARROW2 SPARROW_MAN2 GIANT_SPARROW2BIRD_STORK_WHITE2WHITE_STORK_MAN2GIANT_WHITE_STORK2 BIRD_LOON2LOON_MAN2 +GIANT_LOON2 BIRD_OWL_BARN2 BARN_OWL_MAN2GIANT_BARN_OWL2 BIRD_PARAKEET2 PARAKEET_MAN2GIANT_PARAKEET2 BIRD_KAKAPO2 +KAKAPO_MAN2 GIANT_KAKAPO2BIRD_PARROT_GREY2GREY_PARROT_MAN2GIANT_GREY_PARROT2 BIRD_PUFFIN2 +PUFFIN_MAN2 GIANT_PUFFIN2 BIRD_SWAN2SWAN_MAN2 +GIANT_SWAN2 BIRD_LORIKEET2 LORIKEET_MAN2GIANT_LORIKEET2 BIRD_WREN2WREN_MAN2 +GIANT_WREN2 BIRD_OSPREY2 +OSPREY_MAN2 GIANT_OSPREY2BIRD_EMU2EMU_MAN2 GIANT_EMU2BIRD_COCKATIEL2 COCKATIEL_MAN2GIANT_COCKATIEL2BIRD_LOVEBIRD_PEACH-FACED2PEACH-FACED_LOVEBIRD_MAN2GIANT_PEACH-FACED_LOVEBIRD2 BIRD_MAGPIE2 +MAGPIE_MAN2 GIANT_MAGPIE2 BIRD_KESTREL2 KESTREL_MAN2 GIANT_KESTREL2BIRD_ALBATROSS2 ALBATROSS_MAN2GIANT_ALBATROSS2BIRD_OWL_GREAT_HORNED2GREAT_HORNED_OWL_MAN2GIANT_GREAT_HORNED_OWL2 +BIRD_EAGLE2 EAGLE_MAN2 GIANT_EAGLE2 BIRD_HORNBILL2 HORNBILL_MAN2GIANT_HORNBILL2BIRD_LOVEBIRD_MASKED2MASKED_LOVEBIRD_MAN2GIANT_MASKED_LOVEBIRD2 BIRD_BUSHTIT2 BUSHTIT_MAN2 GIANT_BUSHTIT2 DAMSELFLY2 DAMSELFLY_MAN2GIANT_DAMSELFLY2MOTH2MOTH_MAN2 +GIANT_MOTH2 GRASSHOPPER2GRASSHOPPER_MAN2GIANT_GRASSHOPPER2 BARK_SCORPION2BARK_SCORPION_MAN2GIANT_BARK_SCORPION2MANTIS2 +MANTIS_MAN2 GIANT_MANTIS2TICK2TICK_MAN2 +GIANT_TICK2LOUSE2 LOUSE_MAN2 GIANT_LOUSE2THRIPS2 +THRIPS_MAN2 GIANT_THRIPS2SLUG2SLUG_MAN2 +GIANT_SLUG2MOSQUITO2 MOSQUITO_MAN2GIANT_MOSQUITO2SPIDER_JUMPING2JUMPING_SPIDER_MAN2GIANT_JUMPING_SPIDER2TERMITE2 +MOON_SNAIL2MOON_SNAIL_MAN2GIANT_MOON_SNAIL2SPIDER_BROWN_RECLUSE2BROWN_RECLUSE_SPIDER_MAN2GIANT_BROWN_RECLUSE_SPIDER2SNAIL2 SNAIL_MAN2 GIANT_SNAIL2 GECKO_LEOPARD2LEOPARD_GECKO_MAN2GIANT_LEOPARD_GECKO2DESERT TORTOISE2DESERT_TORTOISE_MAN2GIANT_DESERT_TORTOISE2 GILA_MONSTER2GILA_MONSTER_MAN2GIANT_GILA_MONSTER2DOG2CAT2MULE2DONKEY2HORSE2COW2SHEEP2PIG2GOAT2 BIRD_CHICKEN2CAVY2 BIRD_DUCK2 WATER_BUFFALO2REINDEER2 +BIRD_GOOSE2YAK2LLAMA2ALPACA2BIRD_GUINEAFOWL2BIRD_PEAFOWL_BLUE2 BIRD_TURKEY2RABBIT2CHIMERA2CENTAUR2GRIFFON2FLY2FLY_MAN2 GIANT_FLY2 ROACH_LARGE2 ROACH_MAN2 GIANT_ROACH2BEETLE2 +BEETLE_MAN2 GIANT_BEETLE2ANT2BUTTERFLY_MONARCH2BUTTERFLY_MONARCH_MAN2GIANT_BUTTERFLY_MONARCH2FIREFLY2 FIREFLY_MAN2 GIANT_FIREFLY2 DRAGONFLY2 DRAGONFLY_MAN2GIANT_DRAGONFLY2 HONEY_BEE2 BUMBLEBEE2 GOAT_MOUNTAIN2GOAT_MOUNTAIN_MAN2GIANT_GOAT_MOUNTAIN2 MARMOT_HOARY2MARMOT_HOARY_MAN2GIANT_MARMOT_HOARY2GNOME_MOUNTAIN2 +GNOME_DARK2WALRUS2 +WALRUS_MAN2 GIANT_WALRUS2FISH_LAMPREY_SEA2SHARK_GREAT_WHITE2 SHARK_FRILL2SHARK_SPINY_DOGFISH2SHARK_WOBBEGONG_SPOTTED2 SHARK_WHALE2 SHARK_BASKING2 SHARK_NURSE2SHARK_MAKO_SHORTFIN2SHARK_MAKO_LONGFIN2 SHARK_TIGER2 +SHARK_BULL2SHARK_REEF_BLACKTIP2SHARK_REEF_WHITETIP2 +SHARK_BLUE2SHARK_HAMMERHEAD2 SHARK_ANGEL2FISH_SKATE_COMMON2FISH_RAY_MANTA2 FISH_STINGRAY2FISH_COELACANTH2 FISH_STURGEON2FISH_CONGER_EEL2 FISH_MILKFISH2FISH_COD2 FISH_OPAH2FISH_GROUPER_GIANT2 FISH_BLUEFISH2FISH_SUNFISH_OCEAN2FISH_SWORDFISH2 FISH_MARLIN2 FISH_HALIBUT2FISH_BARRACUDA_GREAT2FISH_TUNA_BLUEFIN2NARWHAL2 NARWHAL MAN2NARWHAL, GIANT2HIPPO2 HIPPO_MAN2 GIANT_HIPPO2FISH_GAR_LONGNOSE2 FISH_CARP2FISH_TIGERFISH2 FISH_PIKE2PLATYPUS2 PLATYPUS MAN2PLATYPUS, GIANT2 BEAR_GRIZZLY2BEAR_GRIZZLY_MAN2GIANT_BEAR_GRIZZLY2 +BEAR_BLACK2BEAR_BLACK_MAN2GIANT_BEAR_BLACK2DEER2DEER_MAN2 +GIANT_DEER2FOX2FOX_MAN2 GIANT_FOX2RACCOON2 RACCOON_MAN2 GIANT_RACCOON2MACAQUE_RHESUS2MACAQUE_RHESUS_MAN2GIANT_MACAQUE_RHESUS2COUGAR2 +COUGAR_MAN2 GIANT_COUGAR2WOLF2WOLF_MAN2 +GIANT_WOLF2 GROUNDHOG2 GROUNDHOG_MAN2GIANT_GROUNDHOG2 ALLIGATOR2 ALLIGATOR_MAN2GIANT_ALLIGATOR2 BIRD_BUZZARD2 BUZZARD_MAN2 GIANT_BUZZARD2PANDA2PANDA, GIGANTIC2 PANDA MAN2CAPYBARA2CAPYBARA, GIANT2 CAPYBARA MAN2BADGER2 +BADGER MAN2 BADGER, GIANT2MOOSE2 MOOSE MAN2 MOOSE, GIANT2 RED PANDA2 RED PANDA MAN2RED PANDA, GIANT2ELEPHANT2 ELEPHANT_MAN2GIANT_ELEPHANT2WARTHOG2 WARTHOG_MAN2 GIANT_WARTHOG2LION2LION_MAN2 +GIANT_LION2LEOPARD2 LEOPARD_MAN2 GIANT_LEOPARD2JAGUAR2 +JAGUAR_MAN2 GIANT_JAGUAR2TIGER2 TIGER_MAN2 GIANT_TIGER2CHEETAH2 CHEETAH_MAN2 GIANT_CHEETAH2GAZELLE2 GAZELLE_MAN2 GIANT_GAZELLE2MANDRILL2 MANDRILL_MAN2GIANT_MANDRILL2 +CHIMPANZEE2BONOBO2GORILLA2 ORANGUTAN2GIBBON_SIAMANG2GIBBON_WHITE_HANDED2GIBBON_BLACK_HANDED2 GIBBON_GRAY2GIBBON_SILVERY2GIBBON_PILEATED2 GIBBON_BILOU2GIBBON_WHITE_BROWED2GIBBON_BLACK_CRESTED2 CAMEL_1_HUMP2CAMEL_1_HUMP_MAN2GIANT_CAMEL_1_HUMP2 CAMEL_2_HUMP2CAMEL_2_HUMP_MAN2GIANT_CAMEL_2_HUMP2CROCODILE_SALTWATER2CROCODILE_SALTWATER_MAN2GIANT_CROCODILE_SALTWATER2 BIRD_VULTURE2 VULTURE_MAN2 GIANT_VULTURE2 +RHINOCEROS2RHINOCEROS_MAN2GIANT_RHINOCEROS2GIRAFFE2 GIRAFFE_MAN2 GIANT_GIRAFFE2 HONEY BADGER2HONEY BADGER MAN2HONEY BADGER, GIANT2GIANT TORTOISE2GIANT TORTOISE MAN2GIGANTIC TORTOISE2 ARMADILLO2 ARMADILLO MAN2ARMADILLO, GIANT2MUSKOX2 +MUSKOX_MAN2 GIANT_MUSKOX2ELK2ELK_MAN2 GIANT_ELK2 +BEAR_POLAR2BEAR_POLAR_MAN2GIANT_BEAR_POLAR2 WOLVERINE2 WOLVERINE_MAN2GIANT_WOLVERINE2 +CHINCHILLA2CHINCHILLA_MAN2GIANT_CHINCHILLA2 FLOATING_GUTS2DRUNIAN2 CREEPING_EYE2VORACIOUS_CAVE_CRAWLER2BLIND_CAVE_OGRE2 +CAP_HOPPER2 +MAGMA_CRAB2CRUNDLE2 HUNGRY_HEAD2 +FLESH_BALL2ELK_BIRD2 HELMET_SNAKE2GREEN_DEVOURER2RUTHERER2CREEPY_CRAWLER2DRALTHA2GIANT_EARTHWORM2 BLOOD_MAN2BUGBAT2MANERA2 +MOLEMARIAN2JABBERER2 POND_GRABBER2BLIND_CAVE_BEAR2 CAVE_DRAGON2REACHER2ELEMENTMAN_GABBRO2GORLAK2 CAVE_FLOATER2PLUMP_HELMET_MAN2 CAVE_BLOB2ELEMENTMAN_AMETHYST2OCTOPUS2 OCTOPUS_MAN2 GIANT_OCTOPUS2CRAB2CRAB_MAN2 +GIANT_CRAB2 LEOPARD_SEAL2LEOPARD_SEAL_MAN2GIANT_LEOPARD_SEAL2 +CUTTLEFISH2CUTTLEFISH_MAN2GIANT_CUTTLEFISH2ORCA2ORCA_MAN2 +GIANT_ORCA2SPONGE2 +SPONGE_MAN2 GIANT_SPONGE2HORSESHOE_CRAB2HORSESHOE_CRAB_MAN2GIANT_HORSESHOE_CRAB2 SPERM_WHALE2SPERM_WHALE_MAN2GIANT_SPERM_WHALE2 ELEPHANT_SEAL2ELEPHANT_SEAL_MAN2GIANT_ELEPHANT_SEAL2 HARP_SEAL2 HARP_SEAL_MAN2GIANT_HARP_SEAL2NAUTILUS2 NAUTILUS_MAN2GIANT_NAUTILUS2 FOXSQUIRREL2 MOGHOPPER2 RAT_DEMON2WAMBLER_FLUFFY2LIZARD_RHINO_TWO_LEGGED2 WORM_KNUCKLE2SPIDER_PHANTOM2 FLY_ACORN2 +GNAT_BLOOD2LIZARD2 +LIZARD_MAN2 GIANT_LIZARD2SKINK2 SKINK_MAN2 GIANT_SKINK2 CHAMELEON2 CHAMELEON_MAN2GIANT_CHAMELEON2ANOLE2 ANOLE_MAN2 GIANT_ANOLE2IGUANA2 +IGUANA_MAN2 GIANT_IGUANA2 RIVER OTTER2 SEA OTTER2 OTTER_MAN2 GIANT_OTTER2SNAPPING TURTLE2ALLIGATOR SNAPPING TURTLE2SNAPPING_TURTLE_MAN2GIANT_SNAPPING_TURTLE2BEAVER2 +BEAVER_MAN2 GIANT_BEAVER2LEECH2 LEECH_MAN2 GIANT_LEECH2AXOLOTL2 AXOLOTL_MAN2 GIANT_AXOLOTL2MINK2MINK_MAN2 +GIANT_MINK2 POND_TURTLE2POND_TURTLE_MAN2GIANT_POND_TURTLE2RAT2RAT_MAN2 SQUIRREL_GRAY2SQUIRREL_GRAY_MAN2GIANT_SQUIRREL_GRAY2 SQUIRREL_RED2SQUIRREL_RED_MAN2GIANT_SQUIRREL_RED2CHIPMUNK2 CHIPMUNK_MAN2GIANT_CHIPMUNK2HAMSTER2 HAMSTER_MAN2 GIANT_HAMSTER2HEDGEHOG2 HEDGEHOG_MAN2GIANT_HEDGEHOG2SQUIRREL_FLYING2FLYING_SQUIRREL_MAN2GIANT_FLYING_SQUIRREL2MUSSEL2OYSTER2 FISH_SALMON2FISH_CLOWNFISH2 FISH_HAGFISH2FISH_LAMPREY_BROOK2 FISH_RAY_BAT2FISH_RAY_THORNBACK2FISH_RATFISH_SPOTTED2 FISH_HERRING2 FISH_SHAD2 FISH_ANCHOVY2FISH_TROUT_STEELHEAD2 FISH_HAKE2 FISH_SEAHORSE2 FISH_GLASSEYE2FISH_PUFFER_WHITE_SPOTTED2 FISH_SOLE2 FISH_FLOUNDER2 FISH_MACKEREL2JELLYFISH_SEA_NETTLE2SQUID2 SQUID MAN2GIGANTIC SQUID2 FISH_LUNGFISH2FISH_LOACH_CLOWN2FISH_BULLHEAD_BROWN2FISH_BULLHEAD_YELLOW2FISH_BULLHEAD_BLACK2FISH_KNIFEFISH_BANDED2 FISH_CHAR2FISH_TROUT_RAINBOW2FISH_MOLLY_SAILFIN2 +FISH_GUPPY2 +FISH_PERCH2DWARF2HUMAN2ELF2GOBLIN2KOBOLD2GREMLIN2TROLL2OGRE2UNICORN2DRAGON2SATYR2COLOSSUS_BRONZE2GIANT2CYCLOPS2ETTIN2MINOTAUR2YETI2 SASQUATCH2 BLIZZARD_MAN2WOLF_ICE2FAIRY2PIXIE2BEAK_DOG2 GRIMELING2 BLENDEC_FOUL2 STRANGLER2 NIGHTWING2HARPY2HYDRA2 MERPERSON2 SEA_SERPENT2 SEA_MONSTER2BIRD_ROC2CROCODILE_CAVE2TOAD_GIANT_CAVE2 OLM_GIANT2 BAT_GIANT2 RAT_GIANT2 RAT_LARGE2MOLE_DOG_NAKED2 +TROGLODYTE2 +MOLE_GIANT2IMP_FIRE2SPIDER_CAVE_GIANT2 SPIDER_CAVE2 FISH_CAVE2 CAVE_FISH_MAN2 LOBSTER_CAVE2 +SNAKE_FIRE2OLM2OLM_MAN2BAT2BAT_MAN2MAGGOT_PURRING2ELEMENTMAN_FIRE2ELEMENTMAN_MAGMA2ELEMENTMAN_IRON2ELEMENTMAN_MUD2BIRD_SWALLOW_CAVE2CAVE_SWALLOW_MAN2BIRD_SWALLOW_CAVE_GIANT2 AMPHIBIAN_MAN2 REPTILE_MAN2 SERPENT_MAN2ANT_MAN2 +RODENT MAN2 WILD_BOAR2 WILD_BOAR_MAN2GIANT_WILD_BOAR2COYOTE2 +COYOTE_MAN2 GIANT_COYOTE2KANGAROO2 KANGAROO_MAN2GIANT_KANGAROO2KOALA2 KOALA_MAN2 GIANT_KOALA2ADDER2 ADDER_MAN2 GIANT_ADDER2ECHIDNA2 ECHIDNA_MAN2 GIANT_ECHIDNA2 PORCUPINE2 PORCUPINE_MAN2GIANT_PORCUPINE2 KINGSNAKE2 KINGSNAKE_MAN2GIANT_KINGSNAKE2 GRAY_LANGUR2GRAY_LANGUR_MAN2GIANT_GRAY_LANGUR2BOBCAT2 +BOBCAT_MAN2 GIANT_BOBCAT2SKUNK2 SKUNK_MAN2 GIANT_SKUNK2GREEN_TREE_FROG2GREEN_TREE_FROG_MAN2GIANT_GREEN_TREE_FROG2HARE2HARE_MAN2 +GIANT_HARE2 RATTLESNAKE2RATTLESNAKE_MAN2GIANT_RATTLESNAKE2WEASEL2 +WEASEL_MAN2 GIANT_WEASEL2COPPERHEAD_SNAKE2COPPERHEAD_SNAKE_MAN2GIANT_COPPERHEAD_SNAKE2IBEX2IBEX_MAN2 +GIANT_IBEX2WOMBAT2 +WOMBAT_MAN2 GIANT_WOMBAT2DINGO2 DINGO_MAN2 GIANT_DINGO2COATI2 COATI_MAN2 GIANT_COATI2OPOSSUM2 OPOSSUM_MAN2 GIANT_OPOSSUM2MONGOOSE2 MONGOOSE_MAN2GIANT_MONGOOSE2HYENA2 HYENA_MAN2 GIANT_HYENA2ANACONDA2 ANACONDA_MAN2GIANT_ANACONDA2MONITOR_LIZARD2MONITOR_LIZARD_MAN2GIANT_MONITOR_LIZARD2 +KING_COBRA2KING_COBRA_MAN2GIANT_KING_COBRA2OCELOT2 +OCELOT_MAN2 GIANT_OCELOT2JACKAL2 +JACKAL_MAN2 GIANT_JACKAL2CAPUCHIN2 CAPUCHIN_MAN2GIANT_CAPUCHIN2SLOTH2 SLOTH_MAN2 GIANT_SLOTH2 SPIDER_MONKEY2SPIDER_MONKEY_MAN2GIANT_SPIDER_MONKEY2PANGOLIN2 PANGOLIN_MAN2GIANT_PANGOLIN2 BLACK_MAMBA2BLACK_MAMBA_MAN2GIANT_BLACK_MAMBA2 +BEAR_SLOTH2SLOTH_BEAR_MAN2GIANT_SLOTH_BEAR2AYE-AYE2 AYE-AYE_MAN2 GIANT_AYE-AYE2 +BUSHMASTER2BUSHMASTER_MAN2GIANT_BUSHMASTER2PYTHON2 +PYTHON_MAN2 GIANT_PYTHON2TAPIR2 TAPIR_MAN2 GIANT_TAPIR2IMPALA2 +IMPALA_MAN2 GIANT_IMPALA2AARDVARK2 AARDVARK_MAN2GIANT_AARDVARK2 LION_TAMARIN2LION_TAMARIN_MAN2GIANT_LION_TAMARIN2STOAT2 STOAT_MAN2 GIANT_STOAT2LYNX2LYNX_MAN2 +GIANT_LYNX:TOAD:TOAD_MAN: +GIANT_TOAD:WORM:WORM_MAN: BIRD_BLUEJAY: BLUEJAY_MAN: GIANT_BLUEJAY: BIRD_CARDINAL: CARDINAL_MAN:GIANT_CARDINAL: BIRD_GRACKLE: GRACKLE_MAN: GIANT_GRACKLE: BIRD_ORIOLE: +ORIOLE_MAN: GIANT_ORIOLE:BIRD_RW_BLACKBIRD:RW_BLACKBIRD_MAN:GIANT_RW_BLACKBIRD: BIRD_PENGUIN:BIRD_PENGUIN_LITTLE:BIRD_PENGUIN_EMPEROR: PENGUIN MAN:BIRD_PENGUIN_GIANT:BIRD_FALCON_PEREGRINE:PEREGRINE FALCON MAN:GIANT PEREGRINE FALCON: BIRD_KIWI:KIWI MAN:BIRD_KIWI_GIANT: BIRD_OSTRICH: OSTRICH MAN:BIRD_OSTRICH_GIANT: BIRD_CROW:CROW_MAN: +GIANT_CROW: +BIRD_RAVEN: RAVEN_MAN: GIANT_RAVEN:BIRD_CASSOWARY: CASSOWARY_MAN:GIANT_CASSOWARY:BIRD_KEA:KEA_MAN: GIANT_KEA:BIRD_OWL_SNOWY: SNOWY_OWL_MAN:GIANT_SNOWY_OWL:SPARROW: SPARROW_MAN: GIANT_SPARROW:BIRD_STORK_WHITE:WHITE_STORK_MAN:GIANT_WHITE_STORK: BIRD_LOON:LOON_MAN: +GIANT_LOON: BIRD_OWL_BARN: BARN_OWL_MAN:GIANT_BARN_OWL: BIRD_PARAKEET: PARAKEET_MAN:GIANT_PARAKEET: BIRD_KAKAPO: +KAKAPO_MAN: GIANT_KAKAPO:BIRD_PARROT_GREY:GREY_PARROT_MAN:GIANT_GREY_PARROT: BIRD_PUFFIN: +PUFFIN_MAN: GIANT_PUFFIN: BIRD_SWAN:SWAN_MAN: +GIANT_SWAN: BIRD_LORIKEET: LORIKEET_MAN:GIANT_LORIKEET: BIRD_WREN:WREN_MAN: +GIANT_WREN: BIRD_OSPREY: +OSPREY_MAN: GIANT_OSPREY:BIRD_EMU:EMU_MAN: GIANT_EMU:BIRD_COCKATIEL: COCKATIEL_MAN:GIANT_COCKATIEL:BIRD_LOVEBIRD_PEACH-FACED:PEACH-FACED_LOVEBIRD_MAN:GIANT_PEACH-FACED_LOVEBIRD: BIRD_MAGPIE: +MAGPIE_MAN: GIANT_MAGPIE: BIRD_KESTREL: KESTREL_MAN: GIANT_KESTREL:BIRD_ALBATROSS: ALBATROSS_MAN:GIANT_ALBATROSS:BIRD_OWL_GREAT_HORNED:GREAT_HORNED_OWL_MAN:GIANT_GREAT_HORNED_OWL: +BIRD_EAGLE: EAGLE_MAN: GIANT_EAGLE: BIRD_HORNBILL: HORNBILL_MAN:GIANT_HORNBILL:BIRD_LOVEBIRD_MASKED:MASKED_LOVEBIRD_MAN:GIANT_MASKED_LOVEBIRD: BIRD_BUSHTIT: BUSHTIT_MAN: GIANT_BUSHTIT: DAMSELFLY: DAMSELFLY_MAN:GIANT_DAMSELFLY:MOTH:MOTH_MAN: +GIANT_MOTH: GRASSHOPPER:GRASSHOPPER_MAN:GIANT_GRASSHOPPER: BARK_SCORPION:BARK_SCORPION_MAN:GIANT_BARK_SCORPION:MANTIS: +MANTIS_MAN: GIANT_MANTIS:TICK:TICK_MAN: +GIANT_TICK:LOUSE: LOUSE_MAN: GIANT_LOUSE:THRIPS: +THRIPS_MAN: GIANT_THRIPS:SLUG:SLUG_MAN: +GIANT_SLUG:MOSQUITO: MOSQUITO_MAN:GIANT_MOSQUITO:SPIDER_JUMPING:JUMPING_SPIDER_MAN:GIANT_JUMPING_SPIDER:TERMITE: +MOON_SNAIL:MOON_SNAIL_MAN:GIANT_MOON_SNAIL:SPIDER_BROWN_RECLUSE:BROWN_RECLUSE_SPIDER_MAN:GIANT_BROWN_RECLUSE_SPIDER:SNAIL: SNAIL_MAN: GIANT_SNAIL: GECKO_LEOPARD:LEOPARD_GECKO_MAN:GIANT_LEOPARD_GECKO:DESERT TORTOISE:DESERT_TORTOISE_MAN:GIANT_DESERT_TORTOISE: GILA_MONSTER:GILA_MONSTER_MAN:GIANT_GILA_MONSTER:DOG:CAT:MULE:DONKEY:HORSE:COW:SHEEP:PIG:GOAT: BIRD_CHICKEN:CAVY: BIRD_DUCK: WATER_BUFFALO:REINDEER: +BIRD_GOOSE:YAK:LLAMA:ALPACA:BIRD_GUINEAFOWL:BIRD_PEAFOWL_BLUE: BIRD_TURKEY:RABBIT:CHIMERA:CENTAUR:GRIFFON:FLY:FLY_MAN: GIANT_FLY: ROACH_LARGE: ROACH_MAN: GIANT_ROACH:BEETLE: +BEETLE_MAN: GIANT_BEETLE:ANT:BUTTERFLY_MONARCH:BUTTERFLY_MONARCH_MAN:GIANT_BUTTERFLY_MONARCH:FIREFLY: FIREFLY_MAN: GIANT_FIREFLY: DRAGONFLY: DRAGONFLY_MAN:GIANT_DRAGONFLY: HONEY_BEE: BUMBLEBEE: GOAT_MOUNTAIN:GOAT_MOUNTAIN_MAN:GIANT_GOAT_MOUNTAIN: MARMOT_HOARY:MARMOT_HOARY_MAN:GIANT_MARMOT_HOARY:GNOME_MOUNTAIN: +GNOME_DARK:WALRUS: +WALRUS_MAN: GIANT_WALRUS:FISH_LAMPREY_SEA:SHARK_GREAT_WHITE: SHARK_FRILL:SHARK_SPINY_DOGFISH:SHARK_WOBBEGONG_SPOTTED: SHARK_WHALE: SHARK_BASKING: SHARK_NURSE:SHARK_MAKO_SHORTFIN:SHARK_MAKO_LONGFIN: SHARK_TIGER: +SHARK_BULL:SHARK_REEF_BLACKTIP:SHARK_REEF_WHITETIP: +SHARK_BLUE:SHARK_HAMMERHEAD: SHARK_ANGEL:FISH_SKATE_COMMON:FISH_RAY_MANTA: FISH_STINGRAY:FISH_COELACANTH: FISH_STURGEON:FISH_CONGER_EEL: FISH_MILKFISH:FISH_COD: FISH_OPAH:FISH_GROUPER_GIANT: FISH_BLUEFISH:FISH_SUNFISH_OCEAN:FISH_SWORDFISH: FISH_MARLIN: FISH_HALIBUT:FISH_BARRACUDA_GREAT:FISH_TUNA_BLUEFIN:NARWHAL: NARWHAL MAN:NARWHAL, GIANT:HIPPO: HIPPO_MAN: GIANT_HIPPO:FISH_GAR_LONGNOSE: FISH_CARP:FISH_TIGERFISH: FISH_PIKE:PLATYPUS: PLATYPUS MAN:PLATYPUS, GIANT: BEAR_GRIZZLY:BEAR_GRIZZLY_MAN:GIANT_BEAR_GRIZZLY: +BEAR_BLACK:BEAR_BLACK_MAN:GIANT_BEAR_BLACK:DEER:DEER_MAN: +GIANT_DEER:FOX:FOX_MAN: GIANT_FOX:RACCOON: RACCOON_MAN: GIANT_RACCOON:MACAQUE_RHESUS:MACAQUE_RHESUS_MAN:GIANT_MACAQUE_RHESUS:COUGAR: +COUGAR_MAN: GIANT_COUGAR:WOLF:WOLF_MAN: +GIANT_WOLF: GROUNDHOG: GROUNDHOG_MAN:GIANT_GROUNDHOG: ALLIGATOR: ALLIGATOR_MAN:GIANT_ALLIGATOR: BIRD_BUZZARD: BUZZARD_MAN: GIANT_BUZZARD:PANDA:PANDA, GIGANTIC: PANDA MAN:CAPYBARA:CAPYBARA, GIANT: CAPYBARA MAN:BADGER: +BADGER MAN: BADGER, GIANT:MOOSE: MOOSE MAN: MOOSE, GIANT: RED PANDA: RED PANDA MAN:RED PANDA, GIANT:ELEPHANT: ELEPHANT_MAN:GIANT_ELEPHANT:WARTHOG: WARTHOG_MAN: GIANT_WARTHOG:LION:LION_MAN: +GIANT_LION:LEOPARD: LEOPARD_MAN: GIANT_LEOPARD:JAGUAR: +JAGUAR_MAN: GIANT_JAGUAR:TIGER: TIGER_MAN: GIANT_TIGER:CHEETAH: CHEETAH_MAN: GIANT_CHEETAH:GAZELLE: GAZELLE_MAN: GIANT_GAZELLE:MANDRILL: MANDRILL_MAN:GIANT_MANDRILL: +CHIMPANZEE:BONOBO:GORILLA: ORANGUTAN:GIBBON_SIAMANG:GIBBON_WHITE_HANDED:GIBBON_BLACK_HANDED: GIBBON_GRAY:GIBBON_SILVERY:GIBBON_PILEATED: GIBBON_BILOU:GIBBON_WHITE_BROWED:GIBBON_BLACK_CRESTED: CAMEL_1_HUMP:CAMEL_1_HUMP_MAN:GIANT_CAMEL_1_HUMP: CAMEL_2_HUMP:CAMEL_2_HUMP_MAN:GIANT_CAMEL_2_HUMP:CROCODILE_SALTWATER:CROCODILE_SALTWATER_MAN:GIANT_CROCODILE_SALTWATER: BIRD_VULTURE: VULTURE_MAN: GIANT_VULTURE: +RHINOCEROS:RHINOCEROS_MAN:GIANT_RHINOCEROS:GIRAFFE: GIRAFFE_MAN: GIANT_GIRAFFE: HONEY BADGER:HONEY BADGER MAN:HONEY BADGER, GIANT:GIANT TORTOISE:GIANT TORTOISE MAN:GIGANTIC TORTOISE: ARMADILLO: ARMADILLO MAN:ARMADILLO, GIANT:MUSKOX: +MUSKOX_MAN: GIANT_MUSKOX:ELK:ELK_MAN: GIANT_ELK: +BEAR_POLAR:BEAR_POLAR_MAN:GIANT_BEAR_POLAR: WOLVERINE: WOLVERINE_MAN:GIANT_WOLVERINE: +CHINCHILLA:CHINCHILLA_MAN:GIANT_CHINCHILLA: FLOATING_GUTS:DRUNIAN: CREEPING_EYE:VORACIOUS_CAVE_CRAWLER:BLIND_CAVE_OGRE: +CAP_HOPPER: +MAGMA_CRAB:CRUNDLE: HUNGRY_HEAD: +FLESH_BALL:ELK_BIRD: HELMET_SNAKE:GREEN_DEVOURER:RUTHERER:CREEPY_CRAWLER:DRALTHA:GIANT_EARTHWORM: BLOOD_MAN:BUGBAT:MANERA: +MOLEMARIAN:JABBERER: POND_GRABBER:BLIND_CAVE_BEAR: CAVE_DRAGON:REACHER:ELEMENTMAN_GABBRO:GORLAK: CAVE_FLOATER:PLUMP_HELMET_MAN: CAVE_BLOB:ELEMENTMAN_AMETHYST:OCTOPUS: OCTOPUS_MAN: GIANT_OCTOPUS:CRAB:CRAB_MAN: +GIANT_CRAB: LEOPARD_SEAL:LEOPARD_SEAL_MAN:GIANT_LEOPARD_SEAL: +CUTTLEFISH:CUTTLEFISH_MAN:GIANT_CUTTLEFISH:ORCA:ORCA_MAN: +GIANT_ORCA:SPONGE: +SPONGE_MAN: GIANT_SPONGE:HORSESHOE_CRAB:HORSESHOE_CRAB_MAN:GIANT_HORSESHOE_CRAB: SPERM_WHALE:SPERM_WHALE_MAN:GIANT_SPERM_WHALE: ELEPHANT_SEAL:ELEPHANT_SEAL_MAN:GIANT_ELEPHANT_SEAL: HARP_SEAL: HARP_SEAL_MAN:GIANT_HARP_SEAL:NAUTILUS: NAUTILUS_MAN:GIANT_NAUTILUS: FOXSQUIRREL: MOGHOPPER: RAT_DEMON:WAMBLER_FLUFFY:LIZARD_RHINO_TWO_LEGGED: WORM_KNUCKLE:SPIDER_PHANTOM: FLY_ACORN: +GNAT_BLOOD:LIZARD: +LIZARD_MAN: GIANT_LIZARD:SKINK: SKINK_MAN: GIANT_SKINK: CHAMELEON: CHAMELEON_MAN:GIANT_CHAMELEON:ANOLE: ANOLE_MAN: GIANT_ANOLE:IGUANA: +IGUANA_MAN: GIANT_IGUANA: RIVER OTTER: SEA OTTER: OTTER_MAN: GIANT_OTTER:SNAPPING TURTLE:ALLIGATOR SNAPPING TURTLE:SNAPPING_TURTLE_MAN:GIANT_SNAPPING_TURTLE:BEAVER: +BEAVER_MAN: GIANT_BEAVER:LEECH: LEECH_MAN: GIANT_LEECH:AXOLOTL: AXOLOTL_MAN: GIANT_AXOLOTL:MINK:MINK_MAN: +GIANT_MINK: POND_TURTLE:POND_TURTLE_MAN:GIANT_POND_TURTLE:RAT:RAT_MAN: SQUIRREL_GRAY:SQUIRREL_GRAY_MAN:GIANT_SQUIRREL_GRAY: SQUIRREL_RED:SQUIRREL_RED_MAN:GIANT_SQUIRREL_RED:CHIPMUNK: CHIPMUNK_MAN:GIANT_CHIPMUNK:HAMSTER: HAMSTER_MAN: GIANT_HAMSTER:HEDGEHOG: HEDGEHOG_MAN:GIANT_HEDGEHOG:SQUIRREL_FLYING:FLYING_SQUIRREL_MAN:GIANT_FLYING_SQUIRREL:MUSSEL:OYSTER: FISH_SALMON:FISH_CLOWNFISH: FISH_HAGFISH:FISH_LAMPREY_BROOK: FISH_RAY_BAT:FISH_RAY_THORNBACK:FISH_RATFISH_SPOTTED: FISH_HERRING: FISH_SHAD: FISH_ANCHOVY:FISH_TROUT_STEELHEAD: FISH_HAKE: FISH_SEAHORSE: FISH_GLASSEYE:FISH_PUFFER_WHITE_SPOTTED: FISH_SOLE: FISH_FLOUNDER: FISH_MACKEREL:JELLYFISH_SEA_NETTLE:SQUID: SQUID MAN:GIGANTIC SQUID: FISH_LUNGFISH:FISH_LOACH_CLOWN:FISH_BULLHEAD_BROWN:FISH_BULLHEAD_YELLOW:FISH_BULLHEAD_BLACK:FISH_KNIFEFISH_BANDED: FISH_CHAR:FISH_TROUT_RAINBOW:FISH_MOLLY_SAILFIN: +FISH_GUPPY: +FISH_PERCH:DWARF:HUMAN:ELF:GOBLIN:KOBOLD:GREMLIN:TROLL:OGRE:UNICORN:DRAGON:SATYR:COLOSSUS_BRONZE:GIANT:CYCLOPS:ETTIN:MINOTAUR:YETI: SASQUATCH: BLIZZARD_MAN:WOLF_ICE:FAIRY:PIXIE:BEAK_DOG: GRIMELING: BLENDEC_FOUL: STRANGLER: NIGHTWING:HARPY:HYDRA: MERPERSON: SEA_SERPENT: SEA_MONSTER:BIRD_ROC:CROCODILE_CAVE:TOAD_GIANT_CAVE: OLM_GIANT: BAT_GIANT: RAT_GIANT: RAT_LARGE:MOLE_DOG_NAKED: +TROGLODYTE: +MOLE_GIANT:IMP_FIRE:SPIDER_CAVE_GIANT: SPIDER_CAVE: FISH_CAVE: CAVE_FISH_MAN: LOBSTER_CAVE: +SNAKE_FIRE:OLM:OLM_MAN:BAT:BAT_MAN:MAGGOT_PURRING:ELEMENTMAN_FIRE:ELEMENTMAN_MAGMA:ELEMENTMAN_IRON:ELEMENTMAN_MUD:BIRD_SWALLOW_CAVE:CAVE_SWALLOW_MAN:BIRD_SWALLOW_CAVE_GIANT: AMPHIBIAN_MAN: REPTILE_MAN: SERPENT_MAN:ANT_MAN: +RODENT MAN: WILD_BOAR: WILD_BOAR_MAN:GIANT_WILD_BOAR:COYOTE: +COYOTE_MAN: GIANT_COYOTE:KANGAROO: KANGAROO_MAN:GIANT_KANGAROO:KOALA: KOALA_MAN: GIANT_KOALA:ADDER: ADDER_MAN: GIANT_ADDER:ECHIDNA: ECHIDNA_MAN: GIANT_ECHIDNA: PORCUPINE: PORCUPINE_MAN:GIANT_PORCUPINE: KINGSNAKE: KINGSNAKE_MAN:GIANT_KINGSNAKE: GRAY_LANGUR:GRAY_LANGUR_MAN:GIANT_GRAY_LANGUR:BOBCAT: +BOBCAT_MAN: GIANT_BOBCAT:SKUNK: SKUNK_MAN: GIANT_SKUNK:GREEN_TREE_FROG:GREEN_TREE_FROG_MAN:GIANT_GREEN_TREE_FROG:HARE:HARE_MAN: +GIANT_HARE: RATTLESNAKE:RATTLESNAKE_MAN:GIANT_RATTLESNAKE:WEASEL: +WEASEL_MAN: GIANT_WEASEL:COPPERHEAD_SNAKE:COPPERHEAD_SNAKE_MAN:GIANT_COPPERHEAD_SNAKE:IBEX:IBEX_MAN: +GIANT_IBEX:WOMBAT: +WOMBAT_MAN: GIANT_WOMBAT:DINGO: DINGO_MAN: GIANT_DINGO:COATI: COATI_MAN: GIANT_COATI:OPOSSUM: OPOSSUM_MAN: GIANT_OPOSSUM:MONGOOSE: MONGOOSE_MAN:GIANT_MONGOOSE:HYENA: HYENA_MAN: GIANT_HYENA:ANACONDA: ANACONDA_MAN:GIANT_ANACONDA:MONITOR_LIZARD:MONITOR_LIZARD_MAN:GIANT_MONITOR_LIZARD: +KING_COBRA:KING_COBRA_MAN:GIANT_KING_COBRA:OCELOT: +OCELOT_MAN: GIANT_OCELOT:JACKAL: +JACKAL_MAN: GIANT_JACKAL:CAPUCHIN: CAPUCHIN_MAN:GIANT_CAPUCHIN:SLOTH: SLOTH_MAN: GIANT_SLOTH: SPIDER_MONKEY:SPIDER_MONKEY_MAN:GIANT_SPIDER_MONKEY:PANGOLIN: PANGOLIN_MAN:GIANT_PANGOLIN: BLACK_MAMBA:BLACK_MAMBA_MAN:GIANT_BLACK_MAMBA: +BEAR_SLOTH:SLOTH_BEAR_MAN:GIANT_SLOTH_BEAR:AYE-AYE: AYE-AYE_MAN: GIANT_AYE-AYE: +BUSHMASTER:BUSHMASTER_MAN:GIANT_BUSHMASTER:PYTHON: +PYTHON_MAN: GIANT_PYTHON:TAPIR: TAPIR_MAN: GIANT_TAPIR:IMPALA: +IMPALA_MAN: GIANT_IMPALA:AARDVARK: AARDVARK_MAN:GIANT_AARDVARK: LION_TAMARIN:LION_TAMARIN_MAN:GIANT_LION_TAMARIN:STOAT: STOAT_MAN: GIANT_STOAT:LYNX:LYNX_MAN: +GIANT_LYNXBTOADBTOAD_MANB +GIANT_TOADBWORMBWORM_MANB BIRD_BLUEJAYB BLUEJAY_MANB GIANT_BLUEJAYB BIRD_CARDINALB CARDINAL_MANBGIANT_CARDINALB BIRD_GRACKLEB GRACKLE_MANB GIANT_GRACKLEB BIRD_ORIOLEB +ORIOLE_MANB GIANT_ORIOLEBBIRD_RW_BLACKBIRDBRW_BLACKBIRD_MANBGIANT_RW_BLACKBIRDB BIRD_PENGUINBBIRD_PENGUIN_LITTLEBBIRD_PENGUIN_EMPERORB PENGUIN MANBBIRD_PENGUIN_GIANTBBIRD_FALCON_PEREGRINEBPEREGRINE FALCON MANBGIANT PEREGRINE FALCONB BIRD_KIWIBKIWI MANBBIRD_KIWI_GIANTB BIRD_OSTRICHB OSTRICH MANBBIRD_OSTRICH_GIANTB BIRD_CROWBCROW_MANB +GIANT_CROWB +BIRD_RAVENB RAVEN_MANB GIANT_RAVENBBIRD_CASSOWARYB CASSOWARY_MANBGIANT_CASSOWARYBBIRD_KEABKEA_MANB GIANT_KEABBIRD_OWL_SNOWYB SNOWY_OWL_MANBGIANT_SNOWY_OWLBSPARROWB SPARROW_MANB GIANT_SPARROWBBIRD_STORK_WHITEBWHITE_STORK_MANBGIANT_WHITE_STORKB BIRD_LOONBLOON_MANB +GIANT_LOONB BIRD_OWL_BARNB BARN_OWL_MANBGIANT_BARN_OWLB BIRD_PARAKEETB PARAKEET_MANBGIANT_PARAKEETB BIRD_KAKAPOB +KAKAPO_MANB GIANT_KAKAPOBBIRD_PARROT_GREYBGREY_PARROT_MANBGIANT_GREY_PARROTB BIRD_PUFFINB +PUFFIN_MANB GIANT_PUFFINB BIRD_SWANBSWAN_MANB +GIANT_SWANB BIRD_LORIKEETB LORIKEET_MANBGIANT_LORIKEETB BIRD_WRENBWREN_MANB +GIANT_WRENB BIRD_OSPREYB +OSPREY_MANB GIANT_OSPREYBBIRD_EMUBEMU_MANB GIANT_EMUBBIRD_COCKATIELB COCKATIEL_MANBGIANT_COCKATIELBBIRD_LOVEBIRD_PEACH-FACEDBPEACH-FACED_LOVEBIRD_MANBGIANT_PEACH-FACED_LOVEBIRDB BIRD_MAGPIEB +MAGPIE_MANB GIANT_MAGPIEB BIRD_KESTRELB KESTREL_MANB GIANT_KESTRELBBIRD_ALBATROSSB ALBATROSS_MANBGIANT_ALBATROSSBBIRD_OWL_GREAT_HORNEDBGREAT_HORNED_OWL_MANBGIANT_GREAT_HORNED_OWLB +BIRD_EAGLEB EAGLE_MANB GIANT_EAGLEB BIRD_HORNBILLB HORNBILL_MANBGIANT_HORNBILLBBIRD_LOVEBIRD_MASKEDBMASKED_LOVEBIRD_MANBGIANT_MASKED_LOVEBIRDB BIRD_BUSHTITB BUSHTIT_MANB GIANT_BUSHTITB DAMSELFLYB DAMSELFLY_MANBGIANT_DAMSELFLYBMOTHBMOTH_MANB +GIANT_MOTHB GRASSHOPPERBGRASSHOPPER_MANBGIANT_GRASSHOPPERB BARK_SCORPIONBBARK_SCORPION_MANBGIANT_BARK_SCORPIONBMANTISB +MANTIS_MANB GIANT_MANTISBTICKBTICK_MANB +GIANT_TICKBLOUSEB LOUSE_MANB GIANT_LOUSEBTHRIPSB +THRIPS_MANB GIANT_THRIPSBSLUGBSLUG_MANB +GIANT_SLUGBMOSQUITOB MOSQUITO_MANBGIANT_MOSQUITOBSPIDER_JUMPINGBJUMPING_SPIDER_MANBGIANT_JUMPING_SPIDERBTERMITEB +MOON_SNAILBMOON_SNAIL_MANBGIANT_MOON_SNAILBSPIDER_BROWN_RECLUSEBBROWN_RECLUSE_SPIDER_MANBGIANT_BROWN_RECLUSE_SPIDERBSNAILB SNAIL_MANB GIANT_SNAILB GECKO_LEOPARDBLEOPARD_GECKO_MANBGIANT_LEOPARD_GECKOBDESERT TORTOISEBDESERT_TORTOISE_MANBGIANT_DESERT_TORTOISEB GILA_MONSTERBGILA_MONSTER_MANBGIANT_GILA_MONSTERBDOGBCATBMULEBDONKEYBHORSEBCOWBSHEEPBPIGBGOATB BIRD_CHICKENBCAVYB BIRD_DUCKB WATER_BUFFALOBREINDEERB +BIRD_GOOSEBYAKBLLAMABALPACABBIRD_GUINEAFOWLBBIRD_PEAFOWL_BLUEB BIRD_TURKEYBRABBITBCHIMERABCENTAURBGRIFFONBFLYBFLY_MANB GIANT_FLYB ROACH_LARGEB ROACH_MANB GIANT_ROACHBBEETLEB +BEETLE_MANB GIANT_BEETLEBANTBBUTTERFLY_MONARCHBBUTTERFLY_MONARCH_MANBGIANT_BUTTERFLY_MONARCHBFIREFLYB FIREFLY_MANB GIANT_FIREFLYB DRAGONFLYB DRAGONFLY_MANBGIANT_DRAGONFLYB HONEY_BEEB BUMBLEBEEB GOAT_MOUNTAINBGOAT_MOUNTAIN_MANBGIANT_GOAT_MOUNTAINB MARMOT_HOARYBMARMOT_HOARY_MANBGIANT_MARMOT_HOARYBGNOME_MOUNTAINB +GNOME_DARKBWALRUSB +WALRUS_MANB GIANT_WALRUSBFISH_LAMPREY_SEABSHARK_GREAT_WHITEB SHARK_FRILLBSHARK_SPINY_DOGFISHBSHARK_WOBBEGONG_SPOTTEDB SHARK_WHALEB SHARK_BASKINGB SHARK_NURSEBSHARK_MAKO_SHORTFINBSHARK_MAKO_LONGFINB SHARK_TIGERB +SHARK_BULLBSHARK_REEF_BLACKTIPBSHARK_REEF_WHITETIPB +SHARK_BLUEBSHARK_HAMMERHEADB SHARK_ANGELBFISH_SKATE_COMMONBFISH_RAY_MANTAB FISH_STINGRAYBFISH_COELACANTHB FISH_STURGEONBFISH_CONGER_EELB FISH_MILKFISHBFISH_CODB FISH_OPAHBFISH_GROUPER_GIANTB FISH_BLUEFISHBFISH_SUNFISH_OCEANBFISH_SWORDFISHB FISH_MARLINB FISH_HALIBUTBFISH_BARRACUDA_GREATBFISH_TUNA_BLUEFINBNARWHALB NARWHAL MANBNARWHAL, GIANTBHIPPOB HIPPO_MANB GIANT_HIPPOBFISH_GAR_LONGNOSEB FISH_CARPBFISH_TIGERFISHB FISH_PIKEBPLATYPUSB PLATYPUS MANBPLATYPUS, GIANTB BEAR_GRIZZLYBBEAR_GRIZZLY_MANBGIANT_BEAR_GRIZZLYB +BEAR_BLACKBBEAR_BLACK_MANBGIANT_BEAR_BLACKBDEERBDEER_MANB +GIANT_DEERBFOXBFOX_MANB GIANT_FOXBRACCOONB RACCOON_MANB GIANT_RACCOONBMACAQUE_RHESUSBMACAQUE_RHESUS_MANBGIANT_MACAQUE_RHESUSBCOUGARB +COUGAR_MANB GIANT_COUGARBWOLFBWOLF_MANB +GIANT_WOLFB GROUNDHOGB GROUNDHOG_MANBGIANT_GROUNDHOGB ALLIGATORB ALLIGATOR_MANBGIANT_ALLIGATORB BIRD_BUZZARDB BUZZARD_MANB GIANT_BUZZARDBPANDABPANDA, GIGANTICB PANDA MANBCAPYBARABCAPYBARA, GIANTB CAPYBARA MANBBADGERB +BADGER MANB BADGER, GIANTBMOOSEB MOOSE MANB MOOSE, GIANTB RED PANDAB RED PANDA MANBRED PANDA, GIANTBELEPHANTB ELEPHANT_MANBGIANT_ELEPHANTBWARTHOGB WARTHOG_MANB GIANT_WARTHOGBLIONBLION_MANB +GIANT_LIONBLEOPARDB LEOPARD_MANB GIANT_LEOPARDBJAGUARB +JAGUAR_MANB GIANT_JAGUARBTIGERB TIGER_MANB GIANT_TIGERBCHEETAHB CHEETAH_MANB GIANT_CHEETAHBGAZELLEB GAZELLE_MANB GIANT_GAZELLEBMANDRILLB MANDRILL_MANBGIANT_MANDRILLB +CHIMPANZEEBBONOBOBGORILLAB ORANGUTANBGIBBON_SIAMANGBGIBBON_WHITE_HANDEDBGIBBON_BLACK_HANDEDB GIBBON_GRAYBGIBBON_SILVERYBGIBBON_PILEATEDB GIBBON_BILOUBGIBBON_WHITE_BROWEDBGIBBON_BLACK_CRESTEDB CAMEL_1_HUMPBCAMEL_1_HUMP_MANBGIANT_CAMEL_1_HUMPB CAMEL_2_HUMPBCAMEL_2_HUMP_MANBGIANT_CAMEL_2_HUMPBCROCODILE_SALTWATERBCROCODILE_SALTWATER_MANBGIANT_CROCODILE_SALTWATERB BIRD_VULTUREB VULTURE_MANB GIANT_VULTUREB +RHINOCEROSBRHINOCEROS_MANBGIANT_RHINOCEROSBGIRAFFEB GIRAFFE_MANB GIANT_GIRAFFEB HONEY BADGERBHONEY BADGER MANBHONEY BADGER, GIANTBGIANT TORTOISEBGIANT TORTOISE MANBGIGANTIC TORTOISEB ARMADILLOB ARMADILLO MANBARMADILLO, GIANTBMUSKOXB +MUSKOX_MANB GIANT_MUSKOXBELKBELK_MANB GIANT_ELKB +BEAR_POLARBBEAR_POLAR_MANBGIANT_BEAR_POLARB WOLVERINEB WOLVERINE_MANBGIANT_WOLVERINEB +CHINCHILLABCHINCHILLA_MANBGIANT_CHINCHILLAB FLOATING_GUTSBDRUNIANB CREEPING_EYEBVORACIOUS_CAVE_CRAWLERBBLIND_CAVE_OGREB +CAP_HOPPERB +MAGMA_CRABBCRUNDLEB HUNGRY_HEADB +FLESH_BALLBELK_BIRDB HELMET_SNAKEBGREEN_DEVOURERBRUTHERERBCREEPY_CRAWLERBDRALTHABGIANT_EARTHWORMB BLOOD_MANBBUGBATBMANERAB +MOLEMARIANBJABBERERB POND_GRABBERBBLIND_CAVE_BEARB CAVE_DRAGONBREACHERBELEMENTMAN_GABBROBGORLAKB CAVE_FLOATERBPLUMP_HELMET_MANB CAVE_BLOBBELEMENTMAN_AMETHYSTBOCTOPUSB OCTOPUS_MANB GIANT_OCTOPUSBCRABBCRAB_MANB +GIANT_CRABB LEOPARD_SEALBLEOPARD_SEAL_MANBGIANT_LEOPARD_SEALB +CUTTLEFISHBCUTTLEFISH_MANBGIANT_CUTTLEFISHBORCABORCA_MANB +GIANT_ORCABSPONGEB +SPONGE_MANB GIANT_SPONGEBHORSESHOE_CRABBHORSESHOE_CRAB_MANBGIANT_HORSESHOE_CRABB SPERM_WHALEBSPERM_WHALE_MANBGIANT_SPERM_WHALEB ELEPHANT_SEALBELEPHANT_SEAL_MANBGIANT_ELEPHANT_SEALB HARP_SEALB HARP_SEAL_MANBGIANT_HARP_SEALBNAUTILUSB NAUTILUS_MANBGIANT_NAUTILUSB FOXSQUIRRELB MOGHOPPERB RAT_DEMONBWAMBLER_FLUFFYBLIZARD_RHINO_TWO_LEGGEDB WORM_KNUCKLEBSPIDER_PHANTOMB FLY_ACORNB +GNAT_BLOODBLIZARDB +LIZARD_MANB GIANT_LIZARDBSKINKB SKINK_MANB GIANT_SKINKB CHAMELEONB CHAMELEON_MANBGIANT_CHAMELEONBANOLEB ANOLE_MANB GIANT_ANOLEBIGUANAB +IGUANA_MANB GIANT_IGUANAB RIVER OTTERB SEA OTTERB OTTER_MANB GIANT_OTTERBSNAPPING TURTLEBALLIGATOR SNAPPING TURTLEBSNAPPING_TURTLE_MANBGIANT_SNAPPING_TURTLEBBEAVERB +BEAVER_MANB GIANT_BEAVERBLEECHB LEECH_MANB GIANT_LEECHBAXOLOTLB AXOLOTL_MANB GIANT_AXOLOTLBMINKBMINK_MANB +GIANT_MINKB POND_TURTLEBPOND_TURTLE_MANBGIANT_POND_TURTLEBRATBRAT_MANB SQUIRREL_GRAYBSQUIRREL_GRAY_MANBGIANT_SQUIRREL_GRAYB SQUIRREL_REDBSQUIRREL_RED_MANBGIANT_SQUIRREL_REDBCHIPMUNKB CHIPMUNK_MANBGIANT_CHIPMUNKBHAMSTERB HAMSTER_MANB GIANT_HAMSTERBHEDGEHOGB HEDGEHOG_MANBGIANT_HEDGEHOGBSQUIRREL_FLYINGBFLYING_SQUIRREL_MANBGIANT_FLYING_SQUIRRELBMUSSELBOYSTERB FISH_SALMONBFISH_CLOWNFISHB FISH_HAGFISHBFISH_LAMPREY_BROOKB FISH_RAY_BATBFISH_RAY_THORNBACKBFISH_RATFISH_SPOTTEDB FISH_HERRINGB FISH_SHADB FISH_ANCHOVYBFISH_TROUT_STEELHEADB FISH_HAKEB FISH_SEAHORSEB FISH_GLASSEYEBFISH_PUFFER_WHITE_SPOTTEDB FISH_SOLEB FISH_FLOUNDERB FISH_MACKERELBJELLYFISH_SEA_NETTLEBSQUIDB SQUID MANBGIGANTIC SQUIDB FISH_LUNGFISHBFISH_LOACH_CLOWNBFISH_BULLHEAD_BROWNBFISH_BULLHEAD_YELLOWBFISH_BULLHEAD_BLACKBFISH_KNIFEFISH_BANDEDB FISH_CHARBFISH_TROUT_RAINBOWBFISH_MOLLY_SAILFINB +FISH_GUPPYB +FISH_PERCHBDWARFBHUMANBELFBGOBLINBKOBOLDBGREMLINBTROLLBOGREBUNICORNBDRAGONBSATYRBCOLOSSUS_BRONZEBGIANTBCYCLOPSBETTINBMINOTAURBYETIB SASQUATCHB BLIZZARD_MANBWOLF_ICEBFAIRYBPIXIEBBEAK_DOGB GRIMELINGB BLENDEC_FOULB STRANGLERB NIGHTWINGBHARPYBHYDRAB MERPERSONB SEA_SERPENTB SEA_MONSTERBBIRD_ROCBCROCODILE_CAVEBTOAD_GIANT_CAVEB OLM_GIANTB BAT_GIANTB RAT_GIANTB RAT_LARGEBMOLE_DOG_NAKEDB +TROGLODYTEB +MOLE_GIANTBIMP_FIREBSPIDER_CAVE_GIANTB SPIDER_CAVEB FISH_CAVEB CAVE_FISH_MANB LOBSTER_CAVEB +SNAKE_FIREBOLMBOLM_MANBBATBBAT_MANBMAGGOT_PURRINGBELEMENTMAN_FIREBELEMENTMAN_MAGMABELEMENTMAN_IRONBELEMENTMAN_MUDBBIRD_SWALLOW_CAVEBCAVE_SWALLOW_MANBBIRD_SWALLOW_CAVE_GIANTB AMPHIBIAN_MANB REPTILE_MANB SERPENT_MANBANT_MANB +RODENT MANB WILD_BOARB WILD_BOAR_MANBGIANT_WILD_BOARBCOYOTEB +COYOTE_MANB GIANT_COYOTEBKANGAROOB KANGAROO_MANBGIANT_KANGAROOBKOALAB KOALA_MANB GIANT_KOALABADDERB ADDER_MANB GIANT_ADDERBECHIDNAB ECHIDNA_MANB GIANT_ECHIDNAB PORCUPINEB PORCUPINE_MANBGIANT_PORCUPINEB KINGSNAKEB KINGSNAKE_MANBGIANT_KINGSNAKEB GRAY_LANGURBGRAY_LANGUR_MANBGIANT_GRAY_LANGURBBOBCATB +BOBCAT_MANB GIANT_BOBCATBSKUNKB SKUNK_MANB GIANT_SKUNKBGREEN_TREE_FROGBGREEN_TREE_FROG_MANBGIANT_GREEN_TREE_FROGBHAREBHARE_MANB +GIANT_HAREB RATTLESNAKEBRATTLESNAKE_MANBGIANT_RATTLESNAKEBWEASELB +WEASEL_MANB GIANT_WEASELBCOPPERHEAD_SNAKEBCOPPERHEAD_SNAKE_MANBGIANT_COPPERHEAD_SNAKEBIBEXBIBEX_MANB +GIANT_IBEXBWOMBATB +WOMBAT_MANB GIANT_WOMBATBDINGOB DINGO_MANB GIANT_DINGOBCOATIB COATI_MANB GIANT_COATIBOPOSSUMB OPOSSUM_MANB GIANT_OPOSSUMBMONGOOSEB MONGOOSE_MANBGIANT_MONGOOSEBHYENAB HYENA_MANB GIANT_HYENABANACONDAB ANACONDA_MANBGIANT_ANACONDABMONITOR_LIZARDBMONITOR_LIZARD_MANBGIANT_MONITOR_LIZARDB +KING_COBRABKING_COBRA_MANBGIANT_KING_COBRABOCELOTB +OCELOT_MANB GIANT_OCELOTBJACKALB +JACKAL_MANB GIANT_JACKALBCAPUCHINB CAPUCHIN_MANBGIANT_CAPUCHINBSLOTHB SLOTH_MANB GIANT_SLOTHB SPIDER_MONKEYBSPIDER_MONKEY_MANBGIANT_SPIDER_MONKEYBPANGOLINB PANGOLIN_MANBGIANT_PANGOLINB BLACK_MAMBABBLACK_MAMBA_MANBGIANT_BLACK_MAMBAB +BEAR_SLOTHBSLOTH_BEAR_MANBGIANT_SLOTH_BEARBAYE-AYEB AYE-AYE_MANB GIANT_AYE-AYEB +BUSHMASTERBBUSHMASTER_MANBGIANT_BUSHMASTERBPYTHONB +PYTHON_MANB GIANT_PYTHONBTAPIRB TAPIR_MANB GIANT_TAPIRBIMPALAB +IMPALA_MANB GIANT_IMPALABAARDVARKB AARDVARK_MANBGIANT_AARDVARKB LION_TAMARINBLION_TAMARIN_MANBGIANT_LION_TAMARINBSTOATB STOAT_MANB GIANT_STOATBLYNXBLYNX_MANB +GIANT_LYNXJTOADJTOAD_MANJ +GIANT_TOADJWORMJWORM_MANJ BIRD_BLUEJAYJ BLUEJAY_MANJ GIANT_BLUEJAYJ BIRD_CARDINALJ CARDINAL_MANJGIANT_CARDINALJ BIRD_GRACKLEJ GRACKLE_MANJ GIANT_GRACKLEJ BIRD_ORIOLEJ +ORIOLE_MANJ GIANT_ORIOLEJBIRD_RW_BLACKBIRDJRW_BLACKBIRD_MANJGIANT_RW_BLACKBIRDJ BIRD_PENGUINJBIRD_PENGUIN_LITTLEJBIRD_PENGUIN_EMPERORJ PENGUIN MANJBIRD_PENGUIN_GIANTJBIRD_FALCON_PEREGRINEJPEREGRINE FALCON MANJGIANT PEREGRINE FALCONJ BIRD_KIWIJKIWI MANJBIRD_KIWI_GIANTJ BIRD_OSTRICHJ OSTRICH MANJBIRD_OSTRICH_GIANTJ BIRD_CROWJCROW_MANJ +GIANT_CROWJ +BIRD_RAVENJ RAVEN_MANJ GIANT_RAVENJBIRD_CASSOWARYJ CASSOWARY_MANJGIANT_CASSOWARYJBIRD_KEAJKEA_MANJ GIANT_KEAJBIRD_OWL_SNOWYJ SNOWY_OWL_MANJGIANT_SNOWY_OWLJSPARROWJ SPARROW_MANJ GIANT_SPARROWJBIRD_STORK_WHITEJWHITE_STORK_MANJGIANT_WHITE_STORKJ BIRD_LOONJLOON_MANJ +GIANT_LOONJ BIRD_OWL_BARNJ BARN_OWL_MANJGIANT_BARN_OWLJ BIRD_PARAKEETJ PARAKEET_MANJGIANT_PARAKEETJ BIRD_KAKAPOJ +KAKAPO_MANJ GIANT_KAKAPOJBIRD_PARROT_GREYJGREY_PARROT_MANJGIANT_GREY_PARROTJ BIRD_PUFFINJ +PUFFIN_MANJ GIANT_PUFFINJ BIRD_SWANJSWAN_MANJ +GIANT_SWANJ BIRD_LORIKEETJ LORIKEET_MANJGIANT_LORIKEETJ BIRD_WRENJWREN_MANJ +GIANT_WRENJ BIRD_OSPREYJ +OSPREY_MANJ GIANT_OSPREYJBIRD_EMUJEMU_MANJ GIANT_EMUJBIRD_COCKATIELJ COCKATIEL_MANJGIANT_COCKATIELJBIRD_LOVEBIRD_PEACH-FACEDJPEACH-FACED_LOVEBIRD_MANJGIANT_PEACH-FACED_LOVEBIRDJ BIRD_MAGPIEJ +MAGPIE_MANJ GIANT_MAGPIEJ BIRD_KESTRELJ KESTREL_MANJ GIANT_KESTRELJBIRD_ALBATROSSJ ALBATROSS_MANJGIANT_ALBATROSSJBIRD_OWL_GREAT_HORNEDJGREAT_HORNED_OWL_MANJGIANT_GREAT_HORNED_OWLJ +BIRD_EAGLEJ EAGLE_MANJ GIANT_EAGLEJ BIRD_HORNBILLJ HORNBILL_MANJGIANT_HORNBILLJBIRD_LOVEBIRD_MASKEDJMASKED_LOVEBIRD_MANJGIANT_MASKED_LOVEBIRDJ BIRD_BUSHTITJ BUSHTIT_MANJ GIANT_BUSHTITJ DAMSELFLYJ DAMSELFLY_MANJGIANT_DAMSELFLYJMOTHJMOTH_MANJ +GIANT_MOTHJ GRASSHOPPERJGRASSHOPPER_MANJGIANT_GRASSHOPPERJ BARK_SCORPIONJBARK_SCORPION_MANJGIANT_BARK_SCORPIONJMANTISJ +MANTIS_MANJ GIANT_MANTISJTICKJTICK_MANJ +GIANT_TICKJLOUSEJ LOUSE_MANJ GIANT_LOUSEJTHRIPSJ +THRIPS_MANJ GIANT_THRIPSJSLUGJSLUG_MANJ +GIANT_SLUGJMOSQUITOJ MOSQUITO_MANJGIANT_MOSQUITOJSPIDER_JUMPINGJJUMPING_SPIDER_MANJGIANT_JUMPING_SPIDERJTERMITEJ +MOON_SNAILJMOON_SNAIL_MANJGIANT_MOON_SNAILJSPIDER_BROWN_RECLUSEJBROWN_RECLUSE_SPIDER_MANJGIANT_BROWN_RECLUSE_SPIDERJSNAILJ SNAIL_MANJ GIANT_SNAILJ GECKO_LEOPARDJLEOPARD_GECKO_MANJGIANT_LEOPARD_GECKOJDESERT TORTOISEJDESERT_TORTOISE_MANJGIANT_DESERT_TORTOISEJ GILA_MONSTERJGILA_MONSTER_MANJGIANT_GILA_MONSTERJDOGJCATJMULEJDONKEYJHORSEJCOWJSHEEPJPIGJGOATJ BIRD_CHICKENJCAVYJ BIRD_DUCKJ WATER_BUFFALOJREINDEERJ +BIRD_GOOSEJYAKJLLAMAJALPACAJBIRD_GUINEAFOWLJBIRD_PEAFOWL_BLUEJ BIRD_TURKEYJRABBITJCHIMERAJCENTAURJGRIFFONJFLYJFLY_MANJ GIANT_FLYJ ROACH_LARGEJ ROACH_MANJ GIANT_ROACHJBEETLEJ +BEETLE_MANJ GIANT_BEETLEJANTJBUTTERFLY_MONARCHJBUTTERFLY_MONARCH_MANJGIANT_BUTTERFLY_MONARCHJFIREFLYJ FIREFLY_MANJ GIANT_FIREFLYJ DRAGONFLYJ DRAGONFLY_MANJGIANT_DRAGONFLYJ HONEY_BEEJ BUMBLEBEEJ GOAT_MOUNTAINJGOAT_MOUNTAIN_MANJGIANT_GOAT_MOUNTAINJ MARMOT_HOARYJMARMOT_HOARY_MANJGIANT_MARMOT_HOARYJGNOME_MOUNTAINJ +GNOME_DARKJWALRUSJ +WALRUS_MANJ GIANT_WALRUSJFISH_LAMPREY_SEAJSHARK_GREAT_WHITEJ SHARK_FRILLJSHARK_SPINY_DOGFISHJSHARK_WOBBEGONG_SPOTTEDJ SHARK_WHALEJ SHARK_BASKINGJ SHARK_NURSEJSHARK_MAKO_SHORTFINJSHARK_MAKO_LONGFINJ SHARK_TIGERJ +SHARK_BULLJSHARK_REEF_BLACKTIPJSHARK_REEF_WHITETIPJ +SHARK_BLUEJSHARK_HAMMERHEADJ SHARK_ANGELJFISH_SKATE_COMMONJFISH_RAY_MANTAJ FISH_STINGRAYJFISH_COELACANTHJ FISH_STURGEONJFISH_CONGER_EELJ FISH_MILKFISHJFISH_CODJ FISH_OPAHJFISH_GROUPER_GIANTJ FISH_BLUEFISHJFISH_SUNFISH_OCEANJFISH_SWORDFISHJ FISH_MARLINJ FISH_HALIBUTJFISH_BARRACUDA_GREATJFISH_TUNA_BLUEFINJNARWHALJ NARWHAL MANJNARWHAL, GIANTJHIPPOJ HIPPO_MANJ GIANT_HIPPOJFISH_GAR_LONGNOSEJ FISH_CARPJFISH_TIGERFISHJ FISH_PIKEJPLATYPUSJ PLATYPUS MANJPLATYPUS, GIANTJ BEAR_GRIZZLYJBEAR_GRIZZLY_MANJGIANT_BEAR_GRIZZLYJ +BEAR_BLACKJBEAR_BLACK_MANJGIANT_BEAR_BLACKJDEERJDEER_MANJ +GIANT_DEERJFOXJFOX_MANJ GIANT_FOXJRACCOONJ RACCOON_MANJ GIANT_RACCOONJMACAQUE_RHESUSJMACAQUE_RHESUS_MANJGIANT_MACAQUE_RHESUSJCOUGARJ +COUGAR_MANJ GIANT_COUGARJWOLFJWOLF_MANJ +GIANT_WOLFJ GROUNDHOGJ GROUNDHOG_MANJGIANT_GROUNDHOGJ ALLIGATORJ ALLIGATOR_MANJGIANT_ALLIGATORJ BIRD_BUZZARDJ BUZZARD_MANJ GIANT_BUZZARDJPANDAJPANDA, GIGANTICJ PANDA MANJCAPYBARAJCAPYBARA, GIANTJ CAPYBARA MANJBADGERJ +BADGER MANJ BADGER, GIANTJMOOSEJ MOOSE MANJ MOOSE, GIANTJ RED PANDAJ RED PANDA MANJRED PANDA, GIANTJELEPHANTJ ELEPHANT_MANJGIANT_ELEPHANTJWARTHOGJ WARTHOG_MANJ GIANT_WARTHOGJLIONJLION_MANJ +GIANT_LIONJLEOPARDJ LEOPARD_MANJ GIANT_LEOPARDJJAGUARJ +JAGUAR_MANJ GIANT_JAGUARJTIGERJ TIGER_MANJ GIANT_TIGERJCHEETAHJ CHEETAH_MANJ GIANT_CHEETAHJGAZELLEJ GAZELLE_MANJ GIANT_GAZELLEJMANDRILLJ MANDRILL_MANJGIANT_MANDRILLJ +CHIMPANZEEJBONOBOJGORILLAJ ORANGUTANJGIBBON_SIAMANGJGIBBON_WHITE_HANDEDJGIBBON_BLACK_HANDEDJ GIBBON_GRAYJGIBBON_SILVERYJGIBBON_PILEATEDJ GIBBON_BILOUJGIBBON_WHITE_BROWEDJGIBBON_BLACK_CRESTEDJ CAMEL_1_HUMPJCAMEL_1_HUMP_MANJGIANT_CAMEL_1_HUMPJ CAMEL_2_HUMPJCAMEL_2_HUMP_MANJGIANT_CAMEL_2_HUMPJCROCODILE_SALTWATERJCROCODILE_SALTWATER_MANJGIANT_CROCODILE_SALTWATERJ BIRD_VULTUREJ VULTURE_MANJ GIANT_VULTUREJ +RHINOCEROSJRHINOCEROS_MANJGIANT_RHINOCEROSJGIRAFFEJ GIRAFFE_MANJ GIANT_GIRAFFEJ HONEY BADGERJHONEY BADGER MANJHONEY BADGER, GIANTJGIANT TORTOISEJGIANT TORTOISE MANJGIGANTIC TORTOISEJ ARMADILLOJ ARMADILLO MANJARMADILLO, GIANTJMUSKOXJ +MUSKOX_MANJ GIANT_MUSKOXJELKJELK_MANJ GIANT_ELKJ +BEAR_POLARJBEAR_POLAR_MANJGIANT_BEAR_POLARJ WOLVERINEJ WOLVERINE_MANJGIANT_WOLVERINEJ +CHINCHILLAJCHINCHILLA_MANJGIANT_CHINCHILLAJ FLOATING_GUTSJDRUNIANJ CREEPING_EYEJVORACIOUS_CAVE_CRAWLERJBLIND_CAVE_OGREJ +CAP_HOPPERJ +MAGMA_CRABJCRUNDLEJ HUNGRY_HEADJ +FLESH_BALLJELK_BIRDJ HELMET_SNAKEJGREEN_DEVOURERJRUTHERERJCREEPY_CRAWLERJDRALTHAJGIANT_EARTHWORMJ BLOOD_MANJBUGBATJMANERAJ +MOLEMARIANJJABBERERJ POND_GRABBERJBLIND_CAVE_BEARJ CAVE_DRAGONJREACHERJELEMENTMAN_GABBROJGORLAKJ CAVE_FLOATERJPLUMP_HELMET_MANJ CAVE_BLOBJELEMENTMAN_AMETHYSTJOCTOPUSJ OCTOPUS_MANJ GIANT_OCTOPUSJCRABJCRAB_MANJ +GIANT_CRABJ LEOPARD_SEALJLEOPARD_SEAL_MANJGIANT_LEOPARD_SEALJ +CUTTLEFISHJCUTTLEFISH_MANJGIANT_CUTTLEFISHJORCAJORCA_MANJ +GIANT_ORCAJSPONGEJ +SPONGE_MANJ GIANT_SPONGEJHORSESHOE_CRABJHORSESHOE_CRAB_MANJGIANT_HORSESHOE_CRABJ SPERM_WHALEJSPERM_WHALE_MANJGIANT_SPERM_WHALEJ ELEPHANT_SEALJELEPHANT_SEAL_MANJGIANT_ELEPHANT_SEALJ HARP_SEALJ HARP_SEAL_MANJGIANT_HARP_SEALJNAUTILUSJ NAUTILUS_MANJGIANT_NAUTILUSJ FOXSQUIRRELJ MOGHOPPERJ RAT_DEMONJWAMBLER_FLUFFYJLIZARD_RHINO_TWO_LEGGEDJ WORM_KNUCKLEJSPIDER_PHANTOMJ FLY_ACORNJ +GNAT_BLOODJLIZARDJ +LIZARD_MANJ GIANT_LIZARDJSKINKJ SKINK_MANJ GIANT_SKINKJ CHAMELEONJ CHAMELEON_MANJGIANT_CHAMELEONJANOLEJ ANOLE_MANJ GIANT_ANOLEJIGUANAJ +IGUANA_MANJ GIANT_IGUANAJ RIVER OTTERJ SEA OTTERJ OTTER_MANJ GIANT_OTTERJSNAPPING TURTLEJALLIGATOR SNAPPING TURTLEJSNAPPING_TURTLE_MANJGIANT_SNAPPING_TURTLEJBEAVERJ +BEAVER_MANJ GIANT_BEAVERJLEECHJ LEECH_MANJ GIANT_LEECHJAXOLOTLJ AXOLOTL_MANJ GIANT_AXOLOTLJMINKJMINK_MANJ +GIANT_MINKJ POND_TURTLEJPOND_TURTLE_MANJGIANT_POND_TURTLEJRATJRAT_MANJ SQUIRREL_GRAYJSQUIRREL_GRAY_MANJGIANT_SQUIRREL_GRAYJ SQUIRREL_REDJSQUIRREL_RED_MANJGIANT_SQUIRREL_REDJCHIPMUNKJ CHIPMUNK_MANJGIANT_CHIPMUNKJHAMSTERJ HAMSTER_MANJ GIANT_HAMSTERJHEDGEHOGJ HEDGEHOG_MANJGIANT_HEDGEHOGJSQUIRREL_FLYINGJFLYING_SQUIRREL_MANJGIANT_FLYING_SQUIRRELJMUSSELJOYSTERJ FISH_SALMONJFISH_CLOWNFISHJ FISH_HAGFISHJFISH_LAMPREY_BROOKJ FISH_RAY_BATJFISH_RAY_THORNBACKJFISH_RATFISH_SPOTTEDJ FISH_HERRINGJ FISH_SHADJ FISH_ANCHOVYJFISH_TROUT_STEELHEADJ FISH_HAKEJ FISH_SEAHORSEJ FISH_GLASSEYEJFISH_PUFFER_WHITE_SPOTTEDJ FISH_SOLEJ FISH_FLOUNDERJ FISH_MACKERELJJELLYFISH_SEA_NETTLEJSQUIDJ SQUID MANJGIGANTIC SQUIDJ FISH_LUNGFISHJFISH_LOACH_CLOWNJFISH_BULLHEAD_BROWNJFISH_BULLHEAD_YELLOWJFISH_BULLHEAD_BLACKJFISH_KNIFEFISH_BANDEDJ FISH_CHARJFISH_TROUT_RAINBOWJFISH_MOLLY_SAILFINJ +FISH_GUPPYJ +FISH_PERCHJDWARFJHUMANJELFJGOBLINJKOBOLDJGREMLINJTROLLJOGREJUNICORNJDRAGONJSATYRJCOLOSSUS_BRONZEJGIANTJCYCLOPSJETTINJMINOTAURJYETIJ SASQUATCHJ BLIZZARD_MANJWOLF_ICEJFAIRYJPIXIEJBEAK_DOGJ GRIMELINGJ BLENDEC_FOULJ STRANGLERJ NIGHTWINGJHARPYJHYDRAJ MERPERSONJ SEA_SERPENTJ SEA_MONSTERJBIRD_ROCJCROCODILE_CAVEJTOAD_GIANT_CAVEJ OLM_GIANTJ BAT_GIANTJ RAT_GIANTJ RAT_LARGEJMOLE_DOG_NAKEDJ +TROGLODYTEJ +MOLE_GIANTJIMP_FIREJSPIDER_CAVE_GIANTJ SPIDER_CAVEJ FISH_CAVEJ CAVE_FISH_MANJ LOBSTER_CAVEJ +SNAKE_FIREJOLMJOLM_MANJBATJBAT_MANJMAGGOT_PURRINGJELEMENTMAN_FIREJELEMENTMAN_MAGMAJELEMENTMAN_IRONJELEMENTMAN_MUDJBIRD_SWALLOW_CAVEJCAVE_SWALLOW_MANJBIRD_SWALLOW_CAVE_GIANTJ AMPHIBIAN_MANJ REPTILE_MANJ SERPENT_MANJANT_MANJ +RODENT MANJ WILD_BOARJ WILD_BOAR_MANJGIANT_WILD_BOARJCOYOTEJ +COYOTE_MANJ GIANT_COYOTEJKANGAROOJ KANGAROO_MANJGIANT_KANGAROOJKOALAJ KOALA_MANJ GIANT_KOALAJADDERJ ADDER_MANJ GIANT_ADDERJECHIDNAJ ECHIDNA_MANJ GIANT_ECHIDNAJ PORCUPINEJ PORCUPINE_MANJGIANT_PORCUPINEJ KINGSNAKEJ KINGSNAKE_MANJGIANT_KINGSNAKEJ GRAY_LANGURJGRAY_LANGUR_MANJGIANT_GRAY_LANGURJBOBCATJ +BOBCAT_MANJ GIANT_BOBCATJSKUNKJ SKUNK_MANJ GIANT_SKUNKJGREEN_TREE_FROGJGREEN_TREE_FROG_MANJGIANT_GREEN_TREE_FROGJHAREJHARE_MANJ +GIANT_HAREJ RATTLESNAKEJRATTLESNAKE_MANJGIANT_RATTLESNAKEJWEASELJ +WEASEL_MANJ GIANT_WEASELJCOPPERHEAD_SNAKEJCOPPERHEAD_SNAKE_MANJGIANT_COPPERHEAD_SNAKEJIBEXJIBEX_MANJ +GIANT_IBEXJWOMBATJ +WOMBAT_MANJ GIANT_WOMBATJDINGOJ DINGO_MANJ GIANT_DINGOJCOATIJ COATI_MANJ GIANT_COATIJOPOSSUMJ OPOSSUM_MANJ GIANT_OPOSSUMJMONGOOSEJ MONGOOSE_MANJGIANT_MONGOOSEJHYENAJ HYENA_MANJ GIANT_HYENAJANACONDAJ ANACONDA_MANJGIANT_ANACONDAJMONITOR_LIZARDJMONITOR_LIZARD_MANJGIANT_MONITOR_LIZARDJ +KING_COBRAJKING_COBRA_MANJGIANT_KING_COBRAJOCELOTJ +OCELOT_MANJ GIANT_OCELOTJJACKALJ +JACKAL_MANJ GIANT_JACKALJCAPUCHINJ CAPUCHIN_MANJGIANT_CAPUCHINJSLOTHJ SLOTH_MANJ GIANT_SLOTHJ SPIDER_MONKEYJSPIDER_MONKEY_MANJGIANT_SPIDER_MONKEYJPANGOLINJ PANGOLIN_MANJGIANT_PANGOLINJ BLACK_MAMBAJBLACK_MAMBA_MANJGIANT_BLACK_MAMBAJ +BEAR_SLOTHJSLOTH_BEAR_MANJGIANT_SLOTH_BEARJAYE-AYEJ AYE-AYE_MANJ GIANT_AYE-AYEJ +BUSHMASTERJBUSHMASTER_MANJGIANT_BUSHMASTERJPYTHONJ +PYTHON_MANJ GIANT_PYTHONJTAPIRJ TAPIR_MANJ GIANT_TAPIRJIMPALAJ +IMPALA_MANJ GIANT_IMPALAJAARDVARKJ AARDVARK_MANJGIANT_AARDVARKJ LION_TAMARINJLION_TAMARIN_MANJGIANT_LION_TAMARINJSTOATJ STOAT_MANJ GIANT_STOATJLYNXJLYNX_MANJ +GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/shells.dfstock b/data/stockpiles/shells.dfstock index e69de29bb..548152764 100644 --- a/data/stockpiles/shells.dfstock +++ b/data/stockpiles/shells.dfstock @@ -0,0 +1,157 @@ +*ÆW +WOOD +DOOR + FLOODGATE +BED +CHAIR +CHAIN +FLASK +GOBLET + +INSTRUMENT +TOY +WINDOW +CAGE +BARREL +BUCKET + +ANIMALTRAP +TABLE +COFFIN +STATUE +WEAPON +ARMOR +SHOES +SHIELD +HELM +GLOVES +BOX +BAG +BIN + +ARMORSTAND + +WEAPONRACK +CABINET +FIGURINE +AMULET +SCEPTER +AMMO +CROWN +RING +EARRING +BRACELET +GEM +ANVIL +REMAINS +MEAT +FISH +FISH_RAW +VERMIN +PET +SEEDS +PLANT + SKIN_TANNED + PLANT_GROWTH +THREAD +CLOTH +TOTEM +PANTS +BACKPACK +QUIVER + CATAPULTPARTS + BALLISTAPARTS + SIEGEAMMO +BALLISTAARROWHEAD + TRAPPARTS +TRAPCOMP +DRINK + POWDER_MISC +CHEESE +FOOD + LIQUID_MISC +COIN +GLOB + PIPE_SECTION + HATCH_COVER +GRATE +QUERN + MILLSTONE +SPLINT +CRUTCH +TRACTION_BENCH +TOOL +SLAB +EGG +BOOK +SHEET +BRANCHBTOADBTOAD_MANB +GIANT_TOADBWORMBWORM_MANB BIRD_BLUEJAYB BLUEJAY_MANB GIANT_BLUEJAYB BIRD_CARDINALB CARDINAL_MANBGIANT_CARDINALB BIRD_GRACKLEB GRACKLE_MANB GIANT_GRACKLEB BIRD_ORIOLEB +ORIOLE_MANB GIANT_ORIOLEBBIRD_RW_BLACKBIRDBRW_BLACKBIRD_MANBGIANT_RW_BLACKBIRDB BIRD_PENGUINBBIRD_PENGUIN_LITTLEBBIRD_PENGUIN_EMPERORB PENGUIN MANBBIRD_PENGUIN_GIANTBBIRD_FALCON_PEREGRINEBPEREGRINE FALCON MANBGIANT PEREGRINE FALCONB BIRD_KIWIBKIWI MANBBIRD_KIWI_GIANTB BIRD_OSTRICHB OSTRICH MANBBIRD_OSTRICH_GIANTB BIRD_CROWBCROW_MANB +GIANT_CROWB +BIRD_RAVENB RAVEN_MANB GIANT_RAVENBBIRD_CASSOWARYB CASSOWARY_MANBGIANT_CASSOWARYBBIRD_KEABKEA_MANB GIANT_KEABBIRD_OWL_SNOWYB SNOWY_OWL_MANBGIANT_SNOWY_OWLBSPARROWB SPARROW_MANB GIANT_SPARROWBBIRD_STORK_WHITEBWHITE_STORK_MANBGIANT_WHITE_STORKB BIRD_LOONBLOON_MANB +GIANT_LOONB BIRD_OWL_BARNB BARN_OWL_MANBGIANT_BARN_OWLB BIRD_PARAKEETB PARAKEET_MANBGIANT_PARAKEETB BIRD_KAKAPOB +KAKAPO_MANB GIANT_KAKAPOBBIRD_PARROT_GREYBGREY_PARROT_MANBGIANT_GREY_PARROTB BIRD_PUFFINB +PUFFIN_MANB GIANT_PUFFINB BIRD_SWANBSWAN_MANB +GIANT_SWANB BIRD_LORIKEETB LORIKEET_MANBGIANT_LORIKEETB BIRD_WRENBWREN_MANB +GIANT_WRENB BIRD_OSPREYB +OSPREY_MANB GIANT_OSPREYBBIRD_EMUBEMU_MANB GIANT_EMUBBIRD_COCKATIELB COCKATIEL_MANBGIANT_COCKATIELBBIRD_LOVEBIRD_PEACH-FACEDBPEACH-FACED_LOVEBIRD_MANBGIANT_PEACH-FACED_LOVEBIRDB BIRD_MAGPIEB +MAGPIE_MANB GIANT_MAGPIEB BIRD_KESTRELB KESTREL_MANB GIANT_KESTRELBBIRD_ALBATROSSB ALBATROSS_MANBGIANT_ALBATROSSBBIRD_OWL_GREAT_HORNEDBGREAT_HORNED_OWL_MANBGIANT_GREAT_HORNED_OWLB +BIRD_EAGLEB EAGLE_MANB GIANT_EAGLEB BIRD_HORNBILLB HORNBILL_MANBGIANT_HORNBILLBBIRD_LOVEBIRD_MASKEDBMASKED_LOVEBIRD_MANBGIANT_MASKED_LOVEBIRDB BIRD_BUSHTITB BUSHTIT_MANB GIANT_BUSHTITB DAMSELFLYB DAMSELFLY_MANBGIANT_DAMSELFLYBMOTHBMOTH_MANB +GIANT_MOTHB GRASSHOPPERBGRASSHOPPER_MANBGIANT_GRASSHOPPERB BARK_SCORPIONBBARK_SCORPION_MANBGIANT_BARK_SCORPIONBMANTISB +MANTIS_MANB GIANT_MANTISBTICKBTICK_MANB +GIANT_TICKBLOUSEB LOUSE_MANB GIANT_LOUSEBTHRIPSB +THRIPS_MANB GIANT_THRIPSBSLUGBSLUG_MANB +GIANT_SLUGBMOSQUITOB MOSQUITO_MANBGIANT_MOSQUITOBSPIDER_JUMPINGBJUMPING_SPIDER_MANBGIANT_JUMPING_SPIDERBTERMITEB +MOON_SNAILBMOON_SNAIL_MANBGIANT_MOON_SNAILBSPIDER_BROWN_RECLUSEBBROWN_RECLUSE_SPIDER_MANBGIANT_BROWN_RECLUSE_SPIDERBSNAILB SNAIL_MANB GIANT_SNAILB GECKO_LEOPARDBLEOPARD_GECKO_MANBGIANT_LEOPARD_GECKOBDESERT TORTOISEBDESERT_TORTOISE_MANBGIANT_DESERT_TORTOISEB GILA_MONSTERBGILA_MONSTER_MANBGIANT_GILA_MONSTERBDOGBCATBMULEBDONKEYBHORSEBCOWBSHEEPBPIGBGOATB BIRD_CHICKENBCAVYB BIRD_DUCKB WATER_BUFFALOBREINDEERB +BIRD_GOOSEBYAKBLLAMABALPACABBIRD_GUINEAFOWLBBIRD_PEAFOWL_BLUEB BIRD_TURKEYBRABBITBCHIMERABCENTAURBGRIFFONBFLYBFLY_MANB GIANT_FLYB ROACH_LARGEB ROACH_MANB GIANT_ROACHBBEETLEB +BEETLE_MANB GIANT_BEETLEBANTBBUTTERFLY_MONARCHBBUTTERFLY_MONARCH_MANBGIANT_BUTTERFLY_MONARCHBFIREFLYB FIREFLY_MANB GIANT_FIREFLYB DRAGONFLYB DRAGONFLY_MANBGIANT_DRAGONFLYB HONEY_BEEB BUMBLEBEEB GOAT_MOUNTAINBGOAT_MOUNTAIN_MANBGIANT_GOAT_MOUNTAINB MARMOT_HOARYBMARMOT_HOARY_MANBGIANT_MARMOT_HOARYBGNOME_MOUNTAINB +GNOME_DARKBWALRUSB +WALRUS_MANB GIANT_WALRUSBFISH_LAMPREY_SEABSHARK_GREAT_WHITEB SHARK_FRILLBSHARK_SPINY_DOGFISHBSHARK_WOBBEGONG_SPOTTEDB SHARK_WHALEB SHARK_BASKINGB SHARK_NURSEBSHARK_MAKO_SHORTFINBSHARK_MAKO_LONGFINB SHARK_TIGERB +SHARK_BULLBSHARK_REEF_BLACKTIPBSHARK_REEF_WHITETIPB +SHARK_BLUEBSHARK_HAMMERHEADB SHARK_ANGELBFISH_SKATE_COMMONBFISH_RAY_MANTAB FISH_STINGRAYBFISH_COELACANTHB FISH_STURGEONBFISH_CONGER_EELB FISH_MILKFISHBFISH_CODB FISH_OPAHBFISH_GROUPER_GIANTB FISH_BLUEFISHBFISH_SUNFISH_OCEANBFISH_SWORDFISHB FISH_MARLINB FISH_HALIBUTBFISH_BARRACUDA_GREATBFISH_TUNA_BLUEFINBNARWHALB NARWHAL MANBNARWHAL, GIANTBHIPPOB HIPPO_MANB GIANT_HIPPOBFISH_GAR_LONGNOSEB FISH_CARPBFISH_TIGERFISHB FISH_PIKEBPLATYPUSB PLATYPUS MANBPLATYPUS, GIANTB BEAR_GRIZZLYBBEAR_GRIZZLY_MANBGIANT_BEAR_GRIZZLYB +BEAR_BLACKBBEAR_BLACK_MANBGIANT_BEAR_BLACKBDEERBDEER_MANB +GIANT_DEERBFOXBFOX_MANB GIANT_FOXBRACCOONB RACCOON_MANB GIANT_RACCOONBMACAQUE_RHESUSBMACAQUE_RHESUS_MANBGIANT_MACAQUE_RHESUSBCOUGARB +COUGAR_MANB GIANT_COUGARBWOLFBWOLF_MANB +GIANT_WOLFB GROUNDHOGB GROUNDHOG_MANBGIANT_GROUNDHOGB ALLIGATORB ALLIGATOR_MANBGIANT_ALLIGATORB BIRD_BUZZARDB BUZZARD_MANB GIANT_BUZZARDBPANDABPANDA, GIGANTICB PANDA MANBCAPYBARABCAPYBARA, GIANTB CAPYBARA MANBBADGERB +BADGER MANB BADGER, GIANTBMOOSEB MOOSE MANB MOOSE, GIANTB RED PANDAB RED PANDA MANBRED PANDA, GIANTBELEPHANTB ELEPHANT_MANBGIANT_ELEPHANTBWARTHOGB WARTHOG_MANB GIANT_WARTHOGBLIONBLION_MANB +GIANT_LIONBLEOPARDB LEOPARD_MANB GIANT_LEOPARDBJAGUARB +JAGUAR_MANB GIANT_JAGUARBTIGERB TIGER_MANB GIANT_TIGERBCHEETAHB CHEETAH_MANB GIANT_CHEETAHBGAZELLEB GAZELLE_MANB GIANT_GAZELLEBMANDRILLB MANDRILL_MANBGIANT_MANDRILLB +CHIMPANZEEBBONOBOBGORILLAB ORANGUTANBGIBBON_SIAMANGBGIBBON_WHITE_HANDEDBGIBBON_BLACK_HANDEDB GIBBON_GRAYBGIBBON_SILVERYBGIBBON_PILEATEDB GIBBON_BILOUBGIBBON_WHITE_BROWEDBGIBBON_BLACK_CRESTEDB CAMEL_1_HUMPBCAMEL_1_HUMP_MANBGIANT_CAMEL_1_HUMPB CAMEL_2_HUMPBCAMEL_2_HUMP_MANBGIANT_CAMEL_2_HUMPBCROCODILE_SALTWATERBCROCODILE_SALTWATER_MANBGIANT_CROCODILE_SALTWATERB BIRD_VULTUREB VULTURE_MANB GIANT_VULTUREB +RHINOCEROSBRHINOCEROS_MANBGIANT_RHINOCEROSBGIRAFFEB GIRAFFE_MANB GIANT_GIRAFFEB HONEY BADGERBHONEY BADGER MANBHONEY BADGER, GIANTBGIANT TORTOISEBGIANT TORTOISE MANBGIGANTIC TORTOISEB ARMADILLOB ARMADILLO MANBARMADILLO, GIANTBMUSKOXB +MUSKOX_MANB GIANT_MUSKOXBELKBELK_MANB GIANT_ELKB +BEAR_POLARBBEAR_POLAR_MANBGIANT_BEAR_POLARB WOLVERINEB WOLVERINE_MANBGIANT_WOLVERINEB +CHINCHILLABCHINCHILLA_MANBGIANT_CHINCHILLAB FLOATING_GUTSBDRUNIANB CREEPING_EYEBVORACIOUS_CAVE_CRAWLERBBLIND_CAVE_OGREB +CAP_HOPPERB +MAGMA_CRABBCRUNDLEB HUNGRY_HEADB +FLESH_BALLBELK_BIRDB HELMET_SNAKEBGREEN_DEVOURERBRUTHERERBCREEPY_CRAWLERBDRALTHABGIANT_EARTHWORMB BLOOD_MANBBUGBATBMANERAB +MOLEMARIANBJABBERERB POND_GRABBERBBLIND_CAVE_BEARB CAVE_DRAGONBREACHERBELEMENTMAN_GABBROBGORLAKB CAVE_FLOATERBPLUMP_HELMET_MANB CAVE_BLOBBELEMENTMAN_AMETHYSTBOCTOPUSB OCTOPUS_MANB GIANT_OCTOPUSBCRABBCRAB_MANB +GIANT_CRABB LEOPARD_SEALBLEOPARD_SEAL_MANBGIANT_LEOPARD_SEALB +CUTTLEFISHBCUTTLEFISH_MANBGIANT_CUTTLEFISHBORCABORCA_MANB +GIANT_ORCABSPONGEB +SPONGE_MANB GIANT_SPONGEBHORSESHOE_CRABBHORSESHOE_CRAB_MANBGIANT_HORSESHOE_CRABB SPERM_WHALEBSPERM_WHALE_MANBGIANT_SPERM_WHALEB ELEPHANT_SEALBELEPHANT_SEAL_MANBGIANT_ELEPHANT_SEALB HARP_SEALB HARP_SEAL_MANBGIANT_HARP_SEALBNAUTILUSB NAUTILUS_MANBGIANT_NAUTILUSB FOXSQUIRRELB MOGHOPPERB RAT_DEMONBWAMBLER_FLUFFYBLIZARD_RHINO_TWO_LEGGEDB WORM_KNUCKLEBSPIDER_PHANTOMB FLY_ACORNB +GNAT_BLOODBLIZARDB +LIZARD_MANB GIANT_LIZARDBSKINKB SKINK_MANB GIANT_SKINKB CHAMELEONB CHAMELEON_MANBGIANT_CHAMELEONBANOLEB ANOLE_MANB GIANT_ANOLEBIGUANAB +IGUANA_MANB GIANT_IGUANAB RIVER OTTERB SEA OTTERB OTTER_MANB GIANT_OTTERBSNAPPING TURTLEBALLIGATOR SNAPPING TURTLEBSNAPPING_TURTLE_MANBGIANT_SNAPPING_TURTLEBBEAVERB +BEAVER_MANB GIANT_BEAVERBLEECHB LEECH_MANB GIANT_LEECHBAXOLOTLB AXOLOTL_MANB GIANT_AXOLOTLBMINKBMINK_MANB +GIANT_MINKB POND_TURTLEBPOND_TURTLE_MANBGIANT_POND_TURTLEBRATBRAT_MANB SQUIRREL_GRAYBSQUIRREL_GRAY_MANBGIANT_SQUIRREL_GRAYB SQUIRREL_REDBSQUIRREL_RED_MANBGIANT_SQUIRREL_REDBCHIPMUNKB CHIPMUNK_MANBGIANT_CHIPMUNKBHAMSTERB HAMSTER_MANB GIANT_HAMSTERBHEDGEHOGB HEDGEHOG_MANBGIANT_HEDGEHOGBSQUIRREL_FLYINGBFLYING_SQUIRREL_MANBGIANT_FLYING_SQUIRRELBMUSSELBOYSTERB FISH_SALMONBFISH_CLOWNFISHB FISH_HAGFISHBFISH_LAMPREY_BROOKB FISH_RAY_BATBFISH_RAY_THORNBACKBFISH_RATFISH_SPOTTEDB FISH_HERRINGB FISH_SHADB FISH_ANCHOVYBFISH_TROUT_STEELHEADB FISH_HAKEB FISH_SEAHORSEB FISH_GLASSEYEBFISH_PUFFER_WHITE_SPOTTEDB FISH_SOLEB FISH_FLOUNDERB FISH_MACKERELBJELLYFISH_SEA_NETTLEBSQUIDB SQUID MANBGIGANTIC SQUIDB FISH_LUNGFISHBFISH_LOACH_CLOWNBFISH_BULLHEAD_BROWNBFISH_BULLHEAD_YELLOWBFISH_BULLHEAD_BLACKBFISH_KNIFEFISH_BANDEDB FISH_CHARBFISH_TROUT_RAINBOWBFISH_MOLLY_SAILFINB +FISH_GUPPYB +FISH_PERCHBDWARFBHUMANBELFBGOBLINBKOBOLDBGREMLINBTROLLBOGREBUNICORNBDRAGONBSATYRBCOLOSSUS_BRONZEBGIANTBCYCLOPSBETTINBMINOTAURBYETIB SASQUATCHB BLIZZARD_MANBWOLF_ICEBFAIRYBPIXIEBBEAK_DOGB GRIMELINGB BLENDEC_FOULB STRANGLERB NIGHTWINGBHARPYBHYDRAB MERPERSONB SEA_SERPENTB SEA_MONSTERBBIRD_ROCBCROCODILE_CAVEBTOAD_GIANT_CAVEB OLM_GIANTB BAT_GIANTB RAT_GIANTB RAT_LARGEBMOLE_DOG_NAKEDB +TROGLODYTEB +MOLE_GIANTBIMP_FIREBSPIDER_CAVE_GIANTB SPIDER_CAVEB FISH_CAVEB CAVE_FISH_MANB LOBSTER_CAVEB +SNAKE_FIREBOLMBOLM_MANBBATBBAT_MANBMAGGOT_PURRINGBELEMENTMAN_FIREBELEMENTMAN_MAGMABELEMENTMAN_IRONBELEMENTMAN_MUDBBIRD_SWALLOW_CAVEBCAVE_SWALLOW_MANBBIRD_SWALLOW_CAVE_GIANTB AMPHIBIAN_MANB REPTILE_MANB SERPENT_MANBANT_MANB +RODENT MANB WILD_BOARB WILD_BOAR_MANBGIANT_WILD_BOARBCOYOTEB +COYOTE_MANB GIANT_COYOTEBKANGAROOB KANGAROO_MANBGIANT_KANGAROOBKOALAB KOALA_MANB GIANT_KOALABADDERB ADDER_MANB GIANT_ADDERBECHIDNAB ECHIDNA_MANB GIANT_ECHIDNAB PORCUPINEB PORCUPINE_MANBGIANT_PORCUPINEB KINGSNAKEB KINGSNAKE_MANBGIANT_KINGSNAKEB GRAY_LANGURBGRAY_LANGUR_MANBGIANT_GRAY_LANGURBBOBCATB +BOBCAT_MANB GIANT_BOBCATBSKUNKB SKUNK_MANB GIANT_SKUNKBGREEN_TREE_FROGBGREEN_TREE_FROG_MANBGIANT_GREEN_TREE_FROGBHAREBHARE_MANB +GIANT_HAREB RATTLESNAKEBRATTLESNAKE_MANBGIANT_RATTLESNAKEBWEASELB +WEASEL_MANB GIANT_WEASELBCOPPERHEAD_SNAKEBCOPPERHEAD_SNAKE_MANBGIANT_COPPERHEAD_SNAKEBIBEXBIBEX_MANB +GIANT_IBEXBWOMBATB +WOMBAT_MANB GIANT_WOMBATBDINGOB DINGO_MANB GIANT_DINGOBCOATIB COATI_MANB GIANT_COATIBOPOSSUMB OPOSSUM_MANB GIANT_OPOSSUMBMONGOOSEB MONGOOSE_MANBGIANT_MONGOOSEBHYENAB HYENA_MANB GIANT_HYENABANACONDAB ANACONDA_MANBGIANT_ANACONDABMONITOR_LIZARDBMONITOR_LIZARD_MANBGIANT_MONITOR_LIZARDB +KING_COBRABKING_COBRA_MANBGIANT_KING_COBRABOCELOTB +OCELOT_MANB GIANT_OCELOTBJACKALB +JACKAL_MANB GIANT_JACKALBCAPUCHINB CAPUCHIN_MANBGIANT_CAPUCHINBSLOTHB SLOTH_MANB GIANT_SLOTHB SPIDER_MONKEYBSPIDER_MONKEY_MANBGIANT_SPIDER_MONKEYBPANGOLINB PANGOLIN_MANBGIANT_PANGOLINB BLACK_MAMBABBLACK_MAMBA_MANBGIANT_BLACK_MAMBAB +BEAR_SLOTHBSLOTH_BEAR_MANBGIANT_SLOTH_BEARBAYE-AYEB AYE-AYE_MANB GIANT_AYE-AYEB +BUSHMASTERBBUSHMASTER_MANBGIANT_BUSHMASTERBPYTHONB +PYTHON_MANB GIANT_PYTHONBTAPIRB TAPIR_MANB GIANT_TAPIRBIMPALAB +IMPALA_MANB GIANT_IMPALABAARDVARKB AARDVARK_MANBGIANT_AARDVARKB LION_TAMARINBLION_TAMARIN_MANBGIANT_LION_TAMARINBSTOATB STOAT_MANB GIANT_STOATBLYNXBLYNX_MANB +GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/skulls.dfstock b/data/stockpiles/skulls.dfstock index e69de29bb..ae52e4537 100644 --- a/data/stockpiles/skulls.dfstock +++ b/data/stockpiles/skulls.dfstock @@ -0,0 +1,157 @@ +*ÆW +WOOD +DOOR + FLOODGATE +BED +CHAIR +CHAIN +FLASK +GOBLET + +INSTRUMENT +TOY +WINDOW +CAGE +BARREL +BUCKET + +ANIMALTRAP +TABLE +COFFIN +STATUE +WEAPON +ARMOR +SHOES +SHIELD +HELM +GLOVES +BOX +BAG +BIN + +ARMORSTAND + +WEAPONRACK +CABINET +FIGURINE +AMULET +SCEPTER +AMMO +CROWN +RING +EARRING +BRACELET +GEM +ANVIL +REMAINS +MEAT +FISH +FISH_RAW +VERMIN +PET +SEEDS +PLANT + SKIN_TANNED + PLANT_GROWTH +THREAD +CLOTH +TOTEM +PANTS +BACKPACK +QUIVER + CATAPULTPARTS + BALLISTAPARTS + SIEGEAMMO +BALLISTAARROWHEAD + TRAPPARTS +TRAPCOMP +DRINK + POWDER_MISC +CHEESE +FOOD + LIQUID_MISC +COIN +GLOB + PIPE_SECTION + HATCH_COVER +GRATE +QUERN + MILLSTONE +SPLINT +CRUTCH +TRACTION_BENCH +TOOL +SLAB +EGG +BOOK +SHEET +BRANCH*TOAD*TOAD_MAN* +GIANT_TOAD*WORM*WORM_MAN* BIRD_BLUEJAY* BLUEJAY_MAN* GIANT_BLUEJAY* BIRD_CARDINAL* CARDINAL_MAN*GIANT_CARDINAL* BIRD_GRACKLE* GRACKLE_MAN* GIANT_GRACKLE* BIRD_ORIOLE* +ORIOLE_MAN* GIANT_ORIOLE*BIRD_RW_BLACKBIRD*RW_BLACKBIRD_MAN*GIANT_RW_BLACKBIRD* BIRD_PENGUIN*BIRD_PENGUIN_LITTLE*BIRD_PENGUIN_EMPEROR* PENGUIN MAN*BIRD_PENGUIN_GIANT*BIRD_FALCON_PEREGRINE*PEREGRINE FALCON MAN*GIANT PEREGRINE FALCON* BIRD_KIWI*KIWI MAN*BIRD_KIWI_GIANT* BIRD_OSTRICH* OSTRICH MAN*BIRD_OSTRICH_GIANT* BIRD_CROW*CROW_MAN* +GIANT_CROW* +BIRD_RAVEN* RAVEN_MAN* GIANT_RAVEN*BIRD_CASSOWARY* CASSOWARY_MAN*GIANT_CASSOWARY*BIRD_KEA*KEA_MAN* GIANT_KEA*BIRD_OWL_SNOWY* SNOWY_OWL_MAN*GIANT_SNOWY_OWL*SPARROW* SPARROW_MAN* GIANT_SPARROW*BIRD_STORK_WHITE*WHITE_STORK_MAN*GIANT_WHITE_STORK* BIRD_LOON*LOON_MAN* +GIANT_LOON* BIRD_OWL_BARN* BARN_OWL_MAN*GIANT_BARN_OWL* BIRD_PARAKEET* PARAKEET_MAN*GIANT_PARAKEET* BIRD_KAKAPO* +KAKAPO_MAN* GIANT_KAKAPO*BIRD_PARROT_GREY*GREY_PARROT_MAN*GIANT_GREY_PARROT* BIRD_PUFFIN* +PUFFIN_MAN* GIANT_PUFFIN* BIRD_SWAN*SWAN_MAN* +GIANT_SWAN* BIRD_LORIKEET* LORIKEET_MAN*GIANT_LORIKEET* BIRD_WREN*WREN_MAN* +GIANT_WREN* BIRD_OSPREY* +OSPREY_MAN* GIANT_OSPREY*BIRD_EMU*EMU_MAN* GIANT_EMU*BIRD_COCKATIEL* COCKATIEL_MAN*GIANT_COCKATIEL*BIRD_LOVEBIRD_PEACH-FACED*PEACH-FACED_LOVEBIRD_MAN*GIANT_PEACH-FACED_LOVEBIRD* BIRD_MAGPIE* +MAGPIE_MAN* GIANT_MAGPIE* BIRD_KESTREL* KESTREL_MAN* GIANT_KESTREL*BIRD_ALBATROSS* ALBATROSS_MAN*GIANT_ALBATROSS*BIRD_OWL_GREAT_HORNED*GREAT_HORNED_OWL_MAN*GIANT_GREAT_HORNED_OWL* +BIRD_EAGLE* EAGLE_MAN* GIANT_EAGLE* BIRD_HORNBILL* HORNBILL_MAN*GIANT_HORNBILL*BIRD_LOVEBIRD_MASKED*MASKED_LOVEBIRD_MAN*GIANT_MASKED_LOVEBIRD* BIRD_BUSHTIT* BUSHTIT_MAN* GIANT_BUSHTIT* DAMSELFLY* DAMSELFLY_MAN*GIANT_DAMSELFLY*MOTH*MOTH_MAN* +GIANT_MOTH* GRASSHOPPER*GRASSHOPPER_MAN*GIANT_GRASSHOPPER* BARK_SCORPION*BARK_SCORPION_MAN*GIANT_BARK_SCORPION*MANTIS* +MANTIS_MAN* GIANT_MANTIS*TICK*TICK_MAN* +GIANT_TICK*LOUSE* LOUSE_MAN* GIANT_LOUSE*THRIPS* +THRIPS_MAN* GIANT_THRIPS*SLUG*SLUG_MAN* +GIANT_SLUG*MOSQUITO* MOSQUITO_MAN*GIANT_MOSQUITO*SPIDER_JUMPING*JUMPING_SPIDER_MAN*GIANT_JUMPING_SPIDER*TERMITE* +MOON_SNAIL*MOON_SNAIL_MAN*GIANT_MOON_SNAIL*SPIDER_BROWN_RECLUSE*BROWN_RECLUSE_SPIDER_MAN*GIANT_BROWN_RECLUSE_SPIDER*SNAIL* SNAIL_MAN* GIANT_SNAIL* GECKO_LEOPARD*LEOPARD_GECKO_MAN*GIANT_LEOPARD_GECKO*DESERT TORTOISE*DESERT_TORTOISE_MAN*GIANT_DESERT_TORTOISE* GILA_MONSTER*GILA_MONSTER_MAN*GIANT_GILA_MONSTER*DOG*CAT*MULE*DONKEY*HORSE*COW*SHEEP*PIG*GOAT* BIRD_CHICKEN*CAVY* BIRD_DUCK* WATER_BUFFALO*REINDEER* +BIRD_GOOSE*YAK*LLAMA*ALPACA*BIRD_GUINEAFOWL*BIRD_PEAFOWL_BLUE* BIRD_TURKEY*RABBIT*CHIMERA*CENTAUR*GRIFFON*FLY*FLY_MAN* GIANT_FLY* ROACH_LARGE* ROACH_MAN* GIANT_ROACH*BEETLE* +BEETLE_MAN* GIANT_BEETLE*ANT*BUTTERFLY_MONARCH*BUTTERFLY_MONARCH_MAN*GIANT_BUTTERFLY_MONARCH*FIREFLY* FIREFLY_MAN* GIANT_FIREFLY* DRAGONFLY* DRAGONFLY_MAN*GIANT_DRAGONFLY* HONEY_BEE* BUMBLEBEE* GOAT_MOUNTAIN*GOAT_MOUNTAIN_MAN*GIANT_GOAT_MOUNTAIN* MARMOT_HOARY*MARMOT_HOARY_MAN*GIANT_MARMOT_HOARY*GNOME_MOUNTAIN* +GNOME_DARK*WALRUS* +WALRUS_MAN* GIANT_WALRUS*FISH_LAMPREY_SEA*SHARK_GREAT_WHITE* SHARK_FRILL*SHARK_SPINY_DOGFISH*SHARK_WOBBEGONG_SPOTTED* SHARK_WHALE* SHARK_BASKING* SHARK_NURSE*SHARK_MAKO_SHORTFIN*SHARK_MAKO_LONGFIN* SHARK_TIGER* +SHARK_BULL*SHARK_REEF_BLACKTIP*SHARK_REEF_WHITETIP* +SHARK_BLUE*SHARK_HAMMERHEAD* SHARK_ANGEL*FISH_SKATE_COMMON*FISH_RAY_MANTA* FISH_STINGRAY*FISH_COELACANTH* FISH_STURGEON*FISH_CONGER_EEL* FISH_MILKFISH*FISH_COD* FISH_OPAH*FISH_GROUPER_GIANT* FISH_BLUEFISH*FISH_SUNFISH_OCEAN*FISH_SWORDFISH* FISH_MARLIN* FISH_HALIBUT*FISH_BARRACUDA_GREAT*FISH_TUNA_BLUEFIN*NARWHAL* NARWHAL MAN*NARWHAL, GIANT*HIPPO* HIPPO_MAN* GIANT_HIPPO*FISH_GAR_LONGNOSE* FISH_CARP*FISH_TIGERFISH* FISH_PIKE*PLATYPUS* PLATYPUS MAN*PLATYPUS, GIANT* BEAR_GRIZZLY*BEAR_GRIZZLY_MAN*GIANT_BEAR_GRIZZLY* +BEAR_BLACK*BEAR_BLACK_MAN*GIANT_BEAR_BLACK*DEER*DEER_MAN* +GIANT_DEER*FOX*FOX_MAN* GIANT_FOX*RACCOON* RACCOON_MAN* GIANT_RACCOON*MACAQUE_RHESUS*MACAQUE_RHESUS_MAN*GIANT_MACAQUE_RHESUS*COUGAR* +COUGAR_MAN* GIANT_COUGAR*WOLF*WOLF_MAN* +GIANT_WOLF* GROUNDHOG* GROUNDHOG_MAN*GIANT_GROUNDHOG* ALLIGATOR* ALLIGATOR_MAN*GIANT_ALLIGATOR* BIRD_BUZZARD* BUZZARD_MAN* GIANT_BUZZARD*PANDA*PANDA, GIGANTIC* PANDA MAN*CAPYBARA*CAPYBARA, GIANT* CAPYBARA MAN*BADGER* +BADGER MAN* BADGER, GIANT*MOOSE* MOOSE MAN* MOOSE, GIANT* RED PANDA* RED PANDA MAN*RED PANDA, GIANT*ELEPHANT* ELEPHANT_MAN*GIANT_ELEPHANT*WARTHOG* WARTHOG_MAN* GIANT_WARTHOG*LION*LION_MAN* +GIANT_LION*LEOPARD* LEOPARD_MAN* GIANT_LEOPARD*JAGUAR* +JAGUAR_MAN* GIANT_JAGUAR*TIGER* TIGER_MAN* GIANT_TIGER*CHEETAH* CHEETAH_MAN* GIANT_CHEETAH*GAZELLE* GAZELLE_MAN* GIANT_GAZELLE*MANDRILL* MANDRILL_MAN*GIANT_MANDRILL* +CHIMPANZEE*BONOBO*GORILLA* ORANGUTAN*GIBBON_SIAMANG*GIBBON_WHITE_HANDED*GIBBON_BLACK_HANDED* GIBBON_GRAY*GIBBON_SILVERY*GIBBON_PILEATED* GIBBON_BILOU*GIBBON_WHITE_BROWED*GIBBON_BLACK_CRESTED* CAMEL_1_HUMP*CAMEL_1_HUMP_MAN*GIANT_CAMEL_1_HUMP* CAMEL_2_HUMP*CAMEL_2_HUMP_MAN*GIANT_CAMEL_2_HUMP*CROCODILE_SALTWATER*CROCODILE_SALTWATER_MAN*GIANT_CROCODILE_SALTWATER* BIRD_VULTURE* VULTURE_MAN* GIANT_VULTURE* +RHINOCEROS*RHINOCEROS_MAN*GIANT_RHINOCEROS*GIRAFFE* GIRAFFE_MAN* GIANT_GIRAFFE* HONEY BADGER*HONEY BADGER MAN*HONEY BADGER, GIANT*GIANT TORTOISE*GIANT TORTOISE MAN*GIGANTIC TORTOISE* ARMADILLO* ARMADILLO MAN*ARMADILLO, GIANT*MUSKOX* +MUSKOX_MAN* GIANT_MUSKOX*ELK*ELK_MAN* GIANT_ELK* +BEAR_POLAR*BEAR_POLAR_MAN*GIANT_BEAR_POLAR* WOLVERINE* WOLVERINE_MAN*GIANT_WOLVERINE* +CHINCHILLA*CHINCHILLA_MAN*GIANT_CHINCHILLA* FLOATING_GUTS*DRUNIAN* CREEPING_EYE*VORACIOUS_CAVE_CRAWLER*BLIND_CAVE_OGRE* +CAP_HOPPER* +MAGMA_CRAB*CRUNDLE* HUNGRY_HEAD* +FLESH_BALL*ELK_BIRD* HELMET_SNAKE*GREEN_DEVOURER*RUTHERER*CREEPY_CRAWLER*DRALTHA*GIANT_EARTHWORM* BLOOD_MAN*BUGBAT*MANERA* +MOLEMARIAN*JABBERER* POND_GRABBER*BLIND_CAVE_BEAR* CAVE_DRAGON*REACHER*ELEMENTMAN_GABBRO*GORLAK* CAVE_FLOATER*PLUMP_HELMET_MAN* CAVE_BLOB*ELEMENTMAN_AMETHYST*OCTOPUS* OCTOPUS_MAN* GIANT_OCTOPUS*CRAB*CRAB_MAN* +GIANT_CRAB* LEOPARD_SEAL*LEOPARD_SEAL_MAN*GIANT_LEOPARD_SEAL* +CUTTLEFISH*CUTTLEFISH_MAN*GIANT_CUTTLEFISH*ORCA*ORCA_MAN* +GIANT_ORCA*SPONGE* +SPONGE_MAN* GIANT_SPONGE*HORSESHOE_CRAB*HORSESHOE_CRAB_MAN*GIANT_HORSESHOE_CRAB* SPERM_WHALE*SPERM_WHALE_MAN*GIANT_SPERM_WHALE* ELEPHANT_SEAL*ELEPHANT_SEAL_MAN*GIANT_ELEPHANT_SEAL* HARP_SEAL* HARP_SEAL_MAN*GIANT_HARP_SEAL*NAUTILUS* NAUTILUS_MAN*GIANT_NAUTILUS* FOXSQUIRREL* MOGHOPPER* RAT_DEMON*WAMBLER_FLUFFY*LIZARD_RHINO_TWO_LEGGED* WORM_KNUCKLE*SPIDER_PHANTOM* FLY_ACORN* +GNAT_BLOOD*LIZARD* +LIZARD_MAN* GIANT_LIZARD*SKINK* SKINK_MAN* GIANT_SKINK* CHAMELEON* CHAMELEON_MAN*GIANT_CHAMELEON*ANOLE* ANOLE_MAN* GIANT_ANOLE*IGUANA* +IGUANA_MAN* GIANT_IGUANA* RIVER OTTER* SEA OTTER* OTTER_MAN* GIANT_OTTER*SNAPPING TURTLE*ALLIGATOR SNAPPING TURTLE*SNAPPING_TURTLE_MAN*GIANT_SNAPPING_TURTLE*BEAVER* +BEAVER_MAN* GIANT_BEAVER*LEECH* LEECH_MAN* GIANT_LEECH*AXOLOTL* AXOLOTL_MAN* GIANT_AXOLOTL*MINK*MINK_MAN* +GIANT_MINK* POND_TURTLE*POND_TURTLE_MAN*GIANT_POND_TURTLE*RAT*RAT_MAN* SQUIRREL_GRAY*SQUIRREL_GRAY_MAN*GIANT_SQUIRREL_GRAY* SQUIRREL_RED*SQUIRREL_RED_MAN*GIANT_SQUIRREL_RED*CHIPMUNK* CHIPMUNK_MAN*GIANT_CHIPMUNK*HAMSTER* HAMSTER_MAN* GIANT_HAMSTER*HEDGEHOG* HEDGEHOG_MAN*GIANT_HEDGEHOG*SQUIRREL_FLYING*FLYING_SQUIRREL_MAN*GIANT_FLYING_SQUIRREL*MUSSEL*OYSTER* FISH_SALMON*FISH_CLOWNFISH* FISH_HAGFISH*FISH_LAMPREY_BROOK* FISH_RAY_BAT*FISH_RAY_THORNBACK*FISH_RATFISH_SPOTTED* FISH_HERRING* FISH_SHAD* FISH_ANCHOVY*FISH_TROUT_STEELHEAD* FISH_HAKE* FISH_SEAHORSE* FISH_GLASSEYE*FISH_PUFFER_WHITE_SPOTTED* FISH_SOLE* FISH_FLOUNDER* FISH_MACKEREL*JELLYFISH_SEA_NETTLE*SQUID* SQUID MAN*GIGANTIC SQUID* FISH_LUNGFISH*FISH_LOACH_CLOWN*FISH_BULLHEAD_BROWN*FISH_BULLHEAD_YELLOW*FISH_BULLHEAD_BLACK*FISH_KNIFEFISH_BANDED* FISH_CHAR*FISH_TROUT_RAINBOW*FISH_MOLLY_SAILFIN* +FISH_GUPPY* +FISH_PERCH*DWARF*HUMAN*ELF*GOBLIN*KOBOLD*GREMLIN*TROLL*OGRE*UNICORN*DRAGON*SATYR*COLOSSUS_BRONZE*GIANT*CYCLOPS*ETTIN*MINOTAUR*YETI* SASQUATCH* BLIZZARD_MAN*WOLF_ICE*FAIRY*PIXIE*BEAK_DOG* GRIMELING* BLENDEC_FOUL* STRANGLER* NIGHTWING*HARPY*HYDRA* MERPERSON* SEA_SERPENT* SEA_MONSTER*BIRD_ROC*CROCODILE_CAVE*TOAD_GIANT_CAVE* OLM_GIANT* BAT_GIANT* RAT_GIANT* RAT_LARGE*MOLE_DOG_NAKED* +TROGLODYTE* +MOLE_GIANT*IMP_FIRE*SPIDER_CAVE_GIANT* SPIDER_CAVE* FISH_CAVE* CAVE_FISH_MAN* LOBSTER_CAVE* +SNAKE_FIRE*OLM*OLM_MAN*BAT*BAT_MAN*MAGGOT_PURRING*ELEMENTMAN_FIRE*ELEMENTMAN_MAGMA*ELEMENTMAN_IRON*ELEMENTMAN_MUD*BIRD_SWALLOW_CAVE*CAVE_SWALLOW_MAN*BIRD_SWALLOW_CAVE_GIANT* AMPHIBIAN_MAN* REPTILE_MAN* SERPENT_MAN*ANT_MAN* +RODENT MAN* WILD_BOAR* WILD_BOAR_MAN*GIANT_WILD_BOAR*COYOTE* +COYOTE_MAN* GIANT_COYOTE*KANGAROO* KANGAROO_MAN*GIANT_KANGAROO*KOALA* KOALA_MAN* GIANT_KOALA*ADDER* ADDER_MAN* GIANT_ADDER*ECHIDNA* ECHIDNA_MAN* GIANT_ECHIDNA* PORCUPINE* PORCUPINE_MAN*GIANT_PORCUPINE* KINGSNAKE* KINGSNAKE_MAN*GIANT_KINGSNAKE* GRAY_LANGUR*GRAY_LANGUR_MAN*GIANT_GRAY_LANGUR*BOBCAT* +BOBCAT_MAN* GIANT_BOBCAT*SKUNK* SKUNK_MAN* GIANT_SKUNK*GREEN_TREE_FROG*GREEN_TREE_FROG_MAN*GIANT_GREEN_TREE_FROG*HARE*HARE_MAN* +GIANT_HARE* RATTLESNAKE*RATTLESNAKE_MAN*GIANT_RATTLESNAKE*WEASEL* +WEASEL_MAN* GIANT_WEASEL*COPPERHEAD_SNAKE*COPPERHEAD_SNAKE_MAN*GIANT_COPPERHEAD_SNAKE*IBEX*IBEX_MAN* +GIANT_IBEX*WOMBAT* +WOMBAT_MAN* GIANT_WOMBAT*DINGO* DINGO_MAN* GIANT_DINGO*COATI* COATI_MAN* GIANT_COATI*OPOSSUM* OPOSSUM_MAN* GIANT_OPOSSUM*MONGOOSE* MONGOOSE_MAN*GIANT_MONGOOSE*HYENA* HYENA_MAN* GIANT_HYENA*ANACONDA* ANACONDA_MAN*GIANT_ANACONDA*MONITOR_LIZARD*MONITOR_LIZARD_MAN*GIANT_MONITOR_LIZARD* +KING_COBRA*KING_COBRA_MAN*GIANT_KING_COBRA*OCELOT* +OCELOT_MAN* GIANT_OCELOT*JACKAL* +JACKAL_MAN* GIANT_JACKAL*CAPUCHIN* CAPUCHIN_MAN*GIANT_CAPUCHIN*SLOTH* SLOTH_MAN* GIANT_SLOTH* SPIDER_MONKEY*SPIDER_MONKEY_MAN*GIANT_SPIDER_MONKEY*PANGOLIN* PANGOLIN_MAN*GIANT_PANGOLIN* BLACK_MAMBA*BLACK_MAMBA_MAN*GIANT_BLACK_MAMBA* +BEAR_SLOTH*SLOTH_BEAR_MAN*GIANT_SLOTH_BEAR*AYE-AYE* AYE-AYE_MAN* GIANT_AYE-AYE* +BUSHMASTER*BUSHMASTER_MAN*GIANT_BUSHMASTER*PYTHON* +PYTHON_MAN* GIANT_PYTHON*TAPIR* TAPIR_MAN* GIANT_TAPIR*IMPALA* +IMPALA_MAN* GIANT_IMPALA*AARDVARK* AARDVARK_MAN*GIANT_AARDVARK* LION_TAMARIN*LION_TAMARIN_MAN*GIANT_LION_TAMARIN*STOAT* STOAT_MAN* GIANT_STOAT*LYNX*LYNX_MAN* +GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/tannedhides.dfstock b/data/stockpiles/tannedhides.dfstock index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..226bd0ec29b204e8be8e885e87ec364115542e1b 100644 GIT binary patch literal 741 zcmXw1JF?p_43x%_Y{Pk(Nsf_QKolf#OcHz~_4HEdN-0yOOqnueE|5EAX-^uGAbz`8 zeEHi`x2sOnO{$5mvxBj45ju@BQEyhZ>3Id}XUF+Y!=j^;LS#cOY4Z{aor->?VYgw@ zojSvd&@ggJ;&5-~Uc)(9&#okn)E7nx8jQ}f4K(D!WrMv7M@1t}9%%Z!s$_UqD-IA> zaUR{BzTx*Y>N^fzaG(S|Qcx#MM9(sb&7IDM;YRz*Y?m$JNdx<>;y-62u}Gqhy;qP- zx(DhIv(Q-pzCJD^%18}AE#iSJyje%*B%VQ^Ixk$Pn{8fehrAw%cN*R#d4M`au( z%G5{47}PD_Z8!ikV7wn5f5(ExWu>94Nw^N(Da^iTsU0KIGH`%Rtc-@&&7wZN7%m() zS0X0_yo~Ie#Q>h>HCtKaLG06i4=Cxr))v>Q!ZYwvpGG}InZWo?x2XFh>F_oiD71=X z0mHf^H)qj&db&EJpx8l<5kzd1Ba0~(^4(T0W<3maf{071>(sw)k^)`5buJgJGUVti zCNh|`A)Gz}$Rqz4CBW{As-rGD9_b{@Lfxo(uS+4A>Hu93_4zv5G#%L55S#brTibqb T+aGQ7vu%F0&F{AP(>DJA0E(=L literal 0 HcmV?d00001 diff --git a/data/stockpiles/teeth.dfstock b/data/stockpiles/teeth.dfstock index e69de29bb..06875f8b5 100644 --- a/data/stockpiles/teeth.dfstock +++ b/data/stockpiles/teeth.dfstock @@ -0,0 +1,157 @@ +*ÆW +WOOD +DOOR + FLOODGATE +BED +CHAIR +CHAIN +FLASK +GOBLET + +INSTRUMENT +TOY +WINDOW +CAGE +BARREL +BUCKET + +ANIMALTRAP +TABLE +COFFIN +STATUE +WEAPON +ARMOR +SHOES +SHIELD +HELM +GLOVES +BOX +BAG +BIN + +ARMORSTAND + +WEAPONRACK +CABINET +FIGURINE +AMULET +SCEPTER +AMMO +CROWN +RING +EARRING +BRACELET +GEM +ANVIL +REMAINS +MEAT +FISH +FISH_RAW +VERMIN +PET +SEEDS +PLANT + SKIN_TANNED + PLANT_GROWTH +THREAD +CLOTH +TOTEM +PANTS +BACKPACK +QUIVER + CATAPULTPARTS + BALLISTAPARTS + SIEGEAMMO +BALLISTAARROWHEAD + TRAPPARTS +TRAPCOMP +DRINK + POWDER_MISC +CHEESE +FOOD + LIQUID_MISC +COIN +GLOB + PIPE_SECTION + HATCH_COVER +GRATE +QUERN + MILLSTONE +SPLINT +CRUTCH +TRACTION_BENCH +TOOL +SLAB +EGG +BOOK +SHEET +BRANCHJTOADJTOAD_MANJ +GIANT_TOADJWORMJWORM_MANJ BIRD_BLUEJAYJ BLUEJAY_MANJ GIANT_BLUEJAYJ BIRD_CARDINALJ CARDINAL_MANJGIANT_CARDINALJ BIRD_GRACKLEJ GRACKLE_MANJ GIANT_GRACKLEJ BIRD_ORIOLEJ +ORIOLE_MANJ GIANT_ORIOLEJBIRD_RW_BLACKBIRDJRW_BLACKBIRD_MANJGIANT_RW_BLACKBIRDJ BIRD_PENGUINJBIRD_PENGUIN_LITTLEJBIRD_PENGUIN_EMPERORJ PENGUIN MANJBIRD_PENGUIN_GIANTJBIRD_FALCON_PEREGRINEJPEREGRINE FALCON MANJGIANT PEREGRINE FALCONJ BIRD_KIWIJKIWI MANJBIRD_KIWI_GIANTJ BIRD_OSTRICHJ OSTRICH MANJBIRD_OSTRICH_GIANTJ BIRD_CROWJCROW_MANJ +GIANT_CROWJ +BIRD_RAVENJ RAVEN_MANJ GIANT_RAVENJBIRD_CASSOWARYJ CASSOWARY_MANJGIANT_CASSOWARYJBIRD_KEAJKEA_MANJ GIANT_KEAJBIRD_OWL_SNOWYJ SNOWY_OWL_MANJGIANT_SNOWY_OWLJSPARROWJ SPARROW_MANJ GIANT_SPARROWJBIRD_STORK_WHITEJWHITE_STORK_MANJGIANT_WHITE_STORKJ BIRD_LOONJLOON_MANJ +GIANT_LOONJ BIRD_OWL_BARNJ BARN_OWL_MANJGIANT_BARN_OWLJ BIRD_PARAKEETJ PARAKEET_MANJGIANT_PARAKEETJ BIRD_KAKAPOJ +KAKAPO_MANJ GIANT_KAKAPOJBIRD_PARROT_GREYJGREY_PARROT_MANJGIANT_GREY_PARROTJ BIRD_PUFFINJ +PUFFIN_MANJ GIANT_PUFFINJ BIRD_SWANJSWAN_MANJ +GIANT_SWANJ BIRD_LORIKEETJ LORIKEET_MANJGIANT_LORIKEETJ BIRD_WRENJWREN_MANJ +GIANT_WRENJ BIRD_OSPREYJ +OSPREY_MANJ GIANT_OSPREYJBIRD_EMUJEMU_MANJ GIANT_EMUJBIRD_COCKATIELJ COCKATIEL_MANJGIANT_COCKATIELJBIRD_LOVEBIRD_PEACH-FACEDJPEACH-FACED_LOVEBIRD_MANJGIANT_PEACH-FACED_LOVEBIRDJ BIRD_MAGPIEJ +MAGPIE_MANJ GIANT_MAGPIEJ BIRD_KESTRELJ KESTREL_MANJ GIANT_KESTRELJBIRD_ALBATROSSJ ALBATROSS_MANJGIANT_ALBATROSSJBIRD_OWL_GREAT_HORNEDJGREAT_HORNED_OWL_MANJGIANT_GREAT_HORNED_OWLJ +BIRD_EAGLEJ EAGLE_MANJ GIANT_EAGLEJ BIRD_HORNBILLJ HORNBILL_MANJGIANT_HORNBILLJBIRD_LOVEBIRD_MASKEDJMASKED_LOVEBIRD_MANJGIANT_MASKED_LOVEBIRDJ BIRD_BUSHTITJ BUSHTIT_MANJ GIANT_BUSHTITJ DAMSELFLYJ DAMSELFLY_MANJGIANT_DAMSELFLYJMOTHJMOTH_MANJ +GIANT_MOTHJ GRASSHOPPERJGRASSHOPPER_MANJGIANT_GRASSHOPPERJ BARK_SCORPIONJBARK_SCORPION_MANJGIANT_BARK_SCORPIONJMANTISJ +MANTIS_MANJ GIANT_MANTISJTICKJTICK_MANJ +GIANT_TICKJLOUSEJ LOUSE_MANJ GIANT_LOUSEJTHRIPSJ +THRIPS_MANJ GIANT_THRIPSJSLUGJSLUG_MANJ +GIANT_SLUGJMOSQUITOJ MOSQUITO_MANJGIANT_MOSQUITOJSPIDER_JUMPINGJJUMPING_SPIDER_MANJGIANT_JUMPING_SPIDERJTERMITEJ +MOON_SNAILJMOON_SNAIL_MANJGIANT_MOON_SNAILJSPIDER_BROWN_RECLUSEJBROWN_RECLUSE_SPIDER_MANJGIANT_BROWN_RECLUSE_SPIDERJSNAILJ SNAIL_MANJ GIANT_SNAILJ GECKO_LEOPARDJLEOPARD_GECKO_MANJGIANT_LEOPARD_GECKOJDESERT TORTOISEJDESERT_TORTOISE_MANJGIANT_DESERT_TORTOISEJ GILA_MONSTERJGILA_MONSTER_MANJGIANT_GILA_MONSTERJDOGJCATJMULEJDONKEYJHORSEJCOWJSHEEPJPIGJGOATJ BIRD_CHICKENJCAVYJ BIRD_DUCKJ WATER_BUFFALOJREINDEERJ +BIRD_GOOSEJYAKJLLAMAJALPACAJBIRD_GUINEAFOWLJBIRD_PEAFOWL_BLUEJ BIRD_TURKEYJRABBITJCHIMERAJCENTAURJGRIFFONJFLYJFLY_MANJ GIANT_FLYJ ROACH_LARGEJ ROACH_MANJ GIANT_ROACHJBEETLEJ +BEETLE_MANJ GIANT_BEETLEJANTJBUTTERFLY_MONARCHJBUTTERFLY_MONARCH_MANJGIANT_BUTTERFLY_MONARCHJFIREFLYJ FIREFLY_MANJ GIANT_FIREFLYJ DRAGONFLYJ DRAGONFLY_MANJGIANT_DRAGONFLYJ HONEY_BEEJ BUMBLEBEEJ GOAT_MOUNTAINJGOAT_MOUNTAIN_MANJGIANT_GOAT_MOUNTAINJ MARMOT_HOARYJMARMOT_HOARY_MANJGIANT_MARMOT_HOARYJGNOME_MOUNTAINJ +GNOME_DARKJWALRUSJ +WALRUS_MANJ GIANT_WALRUSJFISH_LAMPREY_SEAJSHARK_GREAT_WHITEJ SHARK_FRILLJSHARK_SPINY_DOGFISHJSHARK_WOBBEGONG_SPOTTEDJ SHARK_WHALEJ SHARK_BASKINGJ SHARK_NURSEJSHARK_MAKO_SHORTFINJSHARK_MAKO_LONGFINJ SHARK_TIGERJ +SHARK_BULLJSHARK_REEF_BLACKTIPJSHARK_REEF_WHITETIPJ +SHARK_BLUEJSHARK_HAMMERHEADJ SHARK_ANGELJFISH_SKATE_COMMONJFISH_RAY_MANTAJ FISH_STINGRAYJFISH_COELACANTHJ FISH_STURGEONJFISH_CONGER_EELJ FISH_MILKFISHJFISH_CODJ FISH_OPAHJFISH_GROUPER_GIANTJ FISH_BLUEFISHJFISH_SUNFISH_OCEANJFISH_SWORDFISHJ FISH_MARLINJ FISH_HALIBUTJFISH_BARRACUDA_GREATJFISH_TUNA_BLUEFINJNARWHALJ NARWHAL MANJNARWHAL, GIANTJHIPPOJ HIPPO_MANJ GIANT_HIPPOJFISH_GAR_LONGNOSEJ FISH_CARPJFISH_TIGERFISHJ FISH_PIKEJPLATYPUSJ PLATYPUS MANJPLATYPUS, GIANTJ BEAR_GRIZZLYJBEAR_GRIZZLY_MANJGIANT_BEAR_GRIZZLYJ +BEAR_BLACKJBEAR_BLACK_MANJGIANT_BEAR_BLACKJDEERJDEER_MANJ +GIANT_DEERJFOXJFOX_MANJ GIANT_FOXJRACCOONJ RACCOON_MANJ GIANT_RACCOONJMACAQUE_RHESUSJMACAQUE_RHESUS_MANJGIANT_MACAQUE_RHESUSJCOUGARJ +COUGAR_MANJ GIANT_COUGARJWOLFJWOLF_MANJ +GIANT_WOLFJ GROUNDHOGJ GROUNDHOG_MANJGIANT_GROUNDHOGJ ALLIGATORJ ALLIGATOR_MANJGIANT_ALLIGATORJ BIRD_BUZZARDJ BUZZARD_MANJ GIANT_BUZZARDJPANDAJPANDA, GIGANTICJ PANDA MANJCAPYBARAJCAPYBARA, GIANTJ CAPYBARA MANJBADGERJ +BADGER MANJ BADGER, GIANTJMOOSEJ MOOSE MANJ MOOSE, GIANTJ RED PANDAJ RED PANDA MANJRED PANDA, GIANTJELEPHANTJ ELEPHANT_MANJGIANT_ELEPHANTJWARTHOGJ WARTHOG_MANJ GIANT_WARTHOGJLIONJLION_MANJ +GIANT_LIONJLEOPARDJ LEOPARD_MANJ GIANT_LEOPARDJJAGUARJ +JAGUAR_MANJ GIANT_JAGUARJTIGERJ TIGER_MANJ GIANT_TIGERJCHEETAHJ CHEETAH_MANJ GIANT_CHEETAHJGAZELLEJ GAZELLE_MANJ GIANT_GAZELLEJMANDRILLJ MANDRILL_MANJGIANT_MANDRILLJ +CHIMPANZEEJBONOBOJGORILLAJ ORANGUTANJGIBBON_SIAMANGJGIBBON_WHITE_HANDEDJGIBBON_BLACK_HANDEDJ GIBBON_GRAYJGIBBON_SILVERYJGIBBON_PILEATEDJ GIBBON_BILOUJGIBBON_WHITE_BROWEDJGIBBON_BLACK_CRESTEDJ CAMEL_1_HUMPJCAMEL_1_HUMP_MANJGIANT_CAMEL_1_HUMPJ CAMEL_2_HUMPJCAMEL_2_HUMP_MANJGIANT_CAMEL_2_HUMPJCROCODILE_SALTWATERJCROCODILE_SALTWATER_MANJGIANT_CROCODILE_SALTWATERJ BIRD_VULTUREJ VULTURE_MANJ GIANT_VULTUREJ +RHINOCEROSJRHINOCEROS_MANJGIANT_RHINOCEROSJGIRAFFEJ GIRAFFE_MANJ GIANT_GIRAFFEJ HONEY BADGERJHONEY BADGER MANJHONEY BADGER, GIANTJGIANT TORTOISEJGIANT TORTOISE MANJGIGANTIC TORTOISEJ ARMADILLOJ ARMADILLO MANJARMADILLO, GIANTJMUSKOXJ +MUSKOX_MANJ GIANT_MUSKOXJELKJELK_MANJ GIANT_ELKJ +BEAR_POLARJBEAR_POLAR_MANJGIANT_BEAR_POLARJ WOLVERINEJ WOLVERINE_MANJGIANT_WOLVERINEJ +CHINCHILLAJCHINCHILLA_MANJGIANT_CHINCHILLAJ FLOATING_GUTSJDRUNIANJ CREEPING_EYEJVORACIOUS_CAVE_CRAWLERJBLIND_CAVE_OGREJ +CAP_HOPPERJ +MAGMA_CRABJCRUNDLEJ HUNGRY_HEADJ +FLESH_BALLJELK_BIRDJ HELMET_SNAKEJGREEN_DEVOURERJRUTHERERJCREEPY_CRAWLERJDRALTHAJGIANT_EARTHWORMJ BLOOD_MANJBUGBATJMANERAJ +MOLEMARIANJJABBERERJ POND_GRABBERJBLIND_CAVE_BEARJ CAVE_DRAGONJREACHERJELEMENTMAN_GABBROJGORLAKJ CAVE_FLOATERJPLUMP_HELMET_MANJ CAVE_BLOBJELEMENTMAN_AMETHYSTJOCTOPUSJ OCTOPUS_MANJ GIANT_OCTOPUSJCRABJCRAB_MANJ +GIANT_CRABJ LEOPARD_SEALJLEOPARD_SEAL_MANJGIANT_LEOPARD_SEALJ +CUTTLEFISHJCUTTLEFISH_MANJGIANT_CUTTLEFISHJORCAJORCA_MANJ +GIANT_ORCAJSPONGEJ +SPONGE_MANJ GIANT_SPONGEJHORSESHOE_CRABJHORSESHOE_CRAB_MANJGIANT_HORSESHOE_CRABJ SPERM_WHALEJSPERM_WHALE_MANJGIANT_SPERM_WHALEJ ELEPHANT_SEALJELEPHANT_SEAL_MANJGIANT_ELEPHANT_SEALJ HARP_SEALJ HARP_SEAL_MANJGIANT_HARP_SEALJNAUTILUSJ NAUTILUS_MANJGIANT_NAUTILUSJ FOXSQUIRRELJ MOGHOPPERJ RAT_DEMONJWAMBLER_FLUFFYJLIZARD_RHINO_TWO_LEGGEDJ WORM_KNUCKLEJSPIDER_PHANTOMJ FLY_ACORNJ +GNAT_BLOODJLIZARDJ +LIZARD_MANJ GIANT_LIZARDJSKINKJ SKINK_MANJ GIANT_SKINKJ CHAMELEONJ CHAMELEON_MANJGIANT_CHAMELEONJANOLEJ ANOLE_MANJ GIANT_ANOLEJIGUANAJ +IGUANA_MANJ GIANT_IGUANAJ RIVER OTTERJ SEA OTTERJ OTTER_MANJ GIANT_OTTERJSNAPPING TURTLEJALLIGATOR SNAPPING TURTLEJSNAPPING_TURTLE_MANJGIANT_SNAPPING_TURTLEJBEAVERJ +BEAVER_MANJ GIANT_BEAVERJLEECHJ LEECH_MANJ GIANT_LEECHJAXOLOTLJ AXOLOTL_MANJ GIANT_AXOLOTLJMINKJMINK_MANJ +GIANT_MINKJ POND_TURTLEJPOND_TURTLE_MANJGIANT_POND_TURTLEJRATJRAT_MANJ SQUIRREL_GRAYJSQUIRREL_GRAY_MANJGIANT_SQUIRREL_GRAYJ SQUIRREL_REDJSQUIRREL_RED_MANJGIANT_SQUIRREL_REDJCHIPMUNKJ CHIPMUNK_MANJGIANT_CHIPMUNKJHAMSTERJ HAMSTER_MANJ GIANT_HAMSTERJHEDGEHOGJ HEDGEHOG_MANJGIANT_HEDGEHOGJSQUIRREL_FLYINGJFLYING_SQUIRREL_MANJGIANT_FLYING_SQUIRRELJMUSSELJOYSTERJ FISH_SALMONJFISH_CLOWNFISHJ FISH_HAGFISHJFISH_LAMPREY_BROOKJ FISH_RAY_BATJFISH_RAY_THORNBACKJFISH_RATFISH_SPOTTEDJ FISH_HERRINGJ FISH_SHADJ FISH_ANCHOVYJFISH_TROUT_STEELHEADJ FISH_HAKEJ FISH_SEAHORSEJ FISH_GLASSEYEJFISH_PUFFER_WHITE_SPOTTEDJ FISH_SOLEJ FISH_FLOUNDERJ FISH_MACKERELJJELLYFISH_SEA_NETTLEJSQUIDJ SQUID MANJGIGANTIC SQUIDJ FISH_LUNGFISHJFISH_LOACH_CLOWNJFISH_BULLHEAD_BROWNJFISH_BULLHEAD_YELLOWJFISH_BULLHEAD_BLACKJFISH_KNIFEFISH_BANDEDJ FISH_CHARJFISH_TROUT_RAINBOWJFISH_MOLLY_SAILFINJ +FISH_GUPPYJ +FISH_PERCHJDWARFJHUMANJELFJGOBLINJKOBOLDJGREMLINJTROLLJOGREJUNICORNJDRAGONJSATYRJCOLOSSUS_BRONZEJGIANTJCYCLOPSJETTINJMINOTAURJYETIJ SASQUATCHJ BLIZZARD_MANJWOLF_ICEJFAIRYJPIXIEJBEAK_DOGJ GRIMELINGJ BLENDEC_FOULJ STRANGLERJ NIGHTWINGJHARPYJHYDRAJ MERPERSONJ SEA_SERPENTJ SEA_MONSTERJBIRD_ROCJCROCODILE_CAVEJTOAD_GIANT_CAVEJ OLM_GIANTJ BAT_GIANTJ RAT_GIANTJ RAT_LARGEJMOLE_DOG_NAKEDJ +TROGLODYTEJ +MOLE_GIANTJIMP_FIREJSPIDER_CAVE_GIANTJ SPIDER_CAVEJ FISH_CAVEJ CAVE_FISH_MANJ LOBSTER_CAVEJ +SNAKE_FIREJOLMJOLM_MANJBATJBAT_MANJMAGGOT_PURRINGJELEMENTMAN_FIREJELEMENTMAN_MAGMAJELEMENTMAN_IRONJELEMENTMAN_MUDJBIRD_SWALLOW_CAVEJCAVE_SWALLOW_MANJBIRD_SWALLOW_CAVE_GIANTJ AMPHIBIAN_MANJ REPTILE_MANJ SERPENT_MANJANT_MANJ +RODENT MANJ WILD_BOARJ WILD_BOAR_MANJGIANT_WILD_BOARJCOYOTEJ +COYOTE_MANJ GIANT_COYOTEJKANGAROOJ KANGAROO_MANJGIANT_KANGAROOJKOALAJ KOALA_MANJ GIANT_KOALAJADDERJ ADDER_MANJ GIANT_ADDERJECHIDNAJ ECHIDNA_MANJ GIANT_ECHIDNAJ PORCUPINEJ PORCUPINE_MANJGIANT_PORCUPINEJ KINGSNAKEJ KINGSNAKE_MANJGIANT_KINGSNAKEJ GRAY_LANGURJGRAY_LANGUR_MANJGIANT_GRAY_LANGURJBOBCATJ +BOBCAT_MANJ GIANT_BOBCATJSKUNKJ SKUNK_MANJ GIANT_SKUNKJGREEN_TREE_FROGJGREEN_TREE_FROG_MANJGIANT_GREEN_TREE_FROGJHAREJHARE_MANJ +GIANT_HAREJ RATTLESNAKEJRATTLESNAKE_MANJGIANT_RATTLESNAKEJWEASELJ +WEASEL_MANJ GIANT_WEASELJCOPPERHEAD_SNAKEJCOPPERHEAD_SNAKE_MANJGIANT_COPPERHEAD_SNAKEJIBEXJIBEX_MANJ +GIANT_IBEXJWOMBATJ +WOMBAT_MANJ GIANT_WOMBATJDINGOJ DINGO_MANJ GIANT_DINGOJCOATIJ COATI_MANJ GIANT_COATIJOPOSSUMJ OPOSSUM_MANJ GIANT_OPOSSUMJMONGOOSEJ MONGOOSE_MANJGIANT_MONGOOSEJHYENAJ HYENA_MANJ GIANT_HYENAJANACONDAJ ANACONDA_MANJGIANT_ANACONDAJMONITOR_LIZARDJMONITOR_LIZARD_MANJGIANT_MONITOR_LIZARDJ +KING_COBRAJKING_COBRA_MANJGIANT_KING_COBRAJOCELOTJ +OCELOT_MANJ GIANT_OCELOTJJACKALJ +JACKAL_MANJ GIANT_JACKALJCAPUCHINJ CAPUCHIN_MANJGIANT_CAPUCHINJSLOTHJ SLOTH_MANJ GIANT_SLOTHJ SPIDER_MONKEYJSPIDER_MONKEY_MANJGIANT_SPIDER_MONKEYJPANGOLINJ PANGOLIN_MANJGIANT_PANGOLINJ BLACK_MAMBAJBLACK_MAMBA_MANJGIANT_BLACK_MAMBAJ +BEAR_SLOTHJSLOTH_BEAR_MANJGIANT_SLOTH_BEARJAYE-AYEJ AYE-AYE_MANJ GIANT_AYE-AYEJ +BUSHMASTERJBUSHMASTER_MANJGIANT_BUSHMASTERJPYTHONJ +PYTHON_MANJ GIANT_PYTHONJTAPIRJ TAPIR_MANJ GIANT_TAPIRJIMPALAJ +IMPALA_MANJ GIANT_IMPALAJAARDVARKJ AARDVARK_MANJGIANT_AARDVARKJ LION_TAMARINJLION_TAMARIN_MANJGIANT_LION_TAMARINJSTOATJ STOAT_MANJ GIANT_STOATJLYNXJLYNX_MANJ +GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/usablehair.dfstock b/data/stockpiles/usablehair.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..97528d893bbc238bd0a2fbfd51ed4c0d3eb941da GIT binary patch literal 759 zcmXw1F_POb43x%_Y{R+C|1&Nqi(<|1?$Wuo3JY}0cK)ZZNEI}NjrP709?xun%gD0C?Lm4?lR zNjK^Y&qBkU_HB%I8t92Eod-$yV*cPE?ic`n{ZSBaq?i(=S3ysb+O_= zaS`W%?(`kEhfzPU@Qej5=uv_`VIq3ANo?+PG>m7!pKf+u6OJ^nUn~D(HWG^@>ezdQ z$fR4K4mmR&1?21DG@^~v^usJ3%)+~MbWGwI@~QK}g}R%~OGU`bo_M3-U6Kc>LquJl zMW#%Bbc{h=^4*3VGy}%_>2bHu04@s+Wl6$y=uQ##MN37DD9hjhHL)rh-d2nL^kTSh zTwRHrknl3Ha~1=5hUaW$mWQw}|6d?Ux1~ays|(M>Q#~5>FlB<`J6)n~lce3-Y~au$ zk_8IuC%HHa@ag%~8HL0Sc8m~WqwHBsv9Ry5a52kn;1f)oQ~gf;+bSv0*<0sw(P~4E z&SD~iNh{LnGl)Fu_fZ1kuBbZdvg45sGSAeFs`vUS1XCU03$m_{FkXA-*zQHow~Dcia4Fn}3Fku+IPh literal 0 HcmV?d00001 From 9b7860125d2328176e40e63670efe1a1338ad905 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 02:01:50 -0800 Subject: [PATCH 0797/2222] ensure elements in deepest dir are added to output list --- library/modules/Filesystem.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/modules/Filesystem.cpp b/library/modules/Filesystem.cpp index a182ac062..2c275ceba 100644 --- a/library/modules/Filesystem.cpp +++ b/library/modules/Filesystem.cpp @@ -255,13 +255,14 @@ static int listdir_recursive_impl (std::string prefix, std::string path, std::string path_file = path + *file; if (Filesystem::isdir(prefixed_file)) { + files.insert(std::pair(include_prefix ? prefixed_file : path_file, true)); + if (depth == 0) { out_of_depth = true; continue; } - files.insert(std::pair(include_prefix ? prefixed_file : path_file, true)); err = listdir_recursive_impl(prefix, path_file + "/", files, depth - 1, include_prefix); if (err) return err; @@ -277,5 +278,7 @@ static int listdir_recursive_impl (std::string prefix, std::string path, int Filesystem::listdir_recursive (std::string dir, std::map &files, int depth /* = 10 */, bool include_prefix /* = true */) { + if (dir.size() && dir[dir.size()-1] == '/') + dir.resize(dir.size()-1); return listdir_recursive_impl(dir, "", files, depth, include_prefix); } From 887cc57eb9dfa2401631d51b06c3f97ddcad2e6d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 11 Mar 2023 21:40:38 +0000 Subject: [PATCH 0798/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 89a345d66..4d5bb5551 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 89a345d66d70eb02293bd2e46dcfad65f0229ee6 +Subproject commit 4d5bb5551d7e8142b65a064ca9adcccbbb8d4534 From a5de0fef3ea25f784983f31ca94559c1e02f4b3e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 13:56:55 -0800 Subject: [PATCH 0799/2222] bump to 50.07-alpha3 --- CMakeLists.txt | 2 +- docs/changelog.txt | 17 +++++++++++++++-- docs/plugins/buildingplan.rst | 2 +- library/xml | 2 +- scripts | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fb43fa07..674d9b60d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "alpha2") +set(DFHACK_RELEASE "alpha3") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index f3592fc4a..89cd14064 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,20 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.07-alpha3 + ## Fixes -@ ``widgets.HotkeyLabel``: don't trigger on click if the widget is disabled - ``dfhack.job.isSuitableMaterial``: now properly detects lack of fire and magma safety for vulnerable materials with high melting points @@ -42,8 +56,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `dig-now`: added handling of dig designations that have been converted into active jobs - -## Documentation +- `buildingplan`: entirely new UI for building placement, item selection, and materials filtering! ## API - Gui focus strings will no longer get the "dfhack/" prefix if the string "dfhack/" already exists in the focus string diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index fe3683f77..8b1256b53 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -3,7 +3,7 @@ buildingplan .. dfhack-tool:: :summary: Plan building layouts with or without materials. - :tags: fort design buildings + :tags: fort design productivity buildings Buildingplan allows you to place furniture, constructions, and other buildings, regardless of whether the required materials are available. This allows you to diff --git a/library/xml b/library/xml index 267eb20f7..751065f42 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 267eb20f75e891fc0ade166e2d316498f454d913 +Subproject commit 751065f42a55504197990f9458826c5f5604bcff diff --git a/scripts b/scripts index 4d5bb5551..5896b316f 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 4d5bb5551d7e8142b65a064ca9adcccbbb8d4534 +Subproject commit 5896b316f6ebb3d0abf3316a2ba895c173051b36 From 097c333f91770656e096636a0e532add3568f907 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 14:18:30 -0800 Subject: [PATCH 0800/2222] move misplaced changelog entries from alpha2 to alpha3 --- docs/changelog.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 89cd14064..b0ed8b981 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -60,6 +60,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - Gui focus strings will no longer get the "dfhack/" prefix if the string "dfhack/" already exists in the focus string +- ``Military``: New module for military functionality +- ``Military``: new ``makeSquad`` to create a squad +- ``Military``: changed ``getSquadName`` to take a squad identifier +- ``Military``: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range +- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency +- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - ``dfhack.job.attachJobItem()``: allows you to attach specific items to a job @@ -68,6 +74,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widgets.CycleHotkeyLabel``: add ``label_below`` attribute for compact 2-line output -@ ``widgets.FilteredList``: search key matching is now case insensitive by default -@ ``gui.INTERIOR_FRAME``: a panel frame style for use in highlighting off interior areas of a UI +- ``maps.getBiomeType``: exposed preexisting function to Lua ## Removed -@ ``gui.THIN_FRAME``: replaced by ``gui.INTERIOR_FRAME`` @@ -97,18 +104,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API - ``Gui::any_civzone_hotkey``, ``Gui::getAnyCivZone``, ``Gui::getSelectedCivZone``: new functions to operate on the new zone system - Units module: added new predicates for ``isGeldable()``, ``isMarkedForGelding()``, and ``isPet()`` -- ``Military``: New module for military functionality -- ``Military``: new ``makeSquad`` to create a squad -- ``Military``: changed ``getSquadName`` to take a squad identifier -- ``Military``: new ``updateRoomAssignments`` for assigning a squad to a barracks and archery range -- ``Maps::GetBiomeType`` renamed to ``Maps::getBiomeType`` for consistency -- ``Maps::GetBiomeTypeRef`` renamed to ``Maps::getBiomeTypeRef`` for consistency ## Lua - ``dfhack.gui.getSelectedCivZone``: returns the Zone that the user has selected currently - ``widgets.FilteredList``: Added ``edit_on_change`` optional parameter to allow a custom callback on filter edit change. - ``widgets.TabBar``: new library widget (migrated from control-panel.lua) -- ``maps.getBiomeType``: exposed preexisting function to Lua # 50.07-alpha1 From 468b3b54d4f9be44d31ed1dfc19d4686ba4931c7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 15:36:47 -0800 Subject: [PATCH 0801/2222] fix buildingplan typo --- plugins/buildingplan/buildingplan.cpp | 2 +- plugins/lua/buildingplan/planneroverlay.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index abc3c6ebd..2010b5f12 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -971,7 +971,7 @@ static string getDescString(color_ostream &out, df::building *bld, int index) { } static int getQueuePosition(color_ostream &out, df::building *bld, int index) { - DEBUG(status,out).print("entering getQueuePosition\n"); + TRACE(status,out).print("entering getQueuePosition\n"); if (!validate_pb(out, bld, index)) return 0; diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 924080ec3..a45ab8ca0 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -462,7 +462,7 @@ function PlannerOverlay:set_filter(idx) end function PlannerOverlay:clear_filter(idx) - clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) + desc=require('plugins.buildingplan').clearFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, idx-1) end local function get_placement_data() From 3798a7cb5e0868fb6dc7ec82b94f1ec9ef8a6e54 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Mar 2023 15:13:57 -0700 Subject: [PATCH 0802/2222] allow clay to be chosen as a filter material --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 17 +++++++++++++---- plugins/lua/buildingplan/filterselection.lua | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b0ed8b981..5799dc70b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +-@ `buildingplan`: can now filter by clay materials ## Documentation diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 2010b5f12..b4f3839f4 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -148,22 +148,26 @@ static const df::dfhack_material_category stone_cat(df::dfhack_material_category static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); static const df::dfhack_material_category glass_cat(df::dfhack_material_category::mask_glass); +static const df::dfhack_material_category clay_cat(df::dfhack_material_category::mask_clay); static void cache_matched(int16_t type, int32_t index) { MaterialInfo mi; mi.decode(type, index); if (mi.matches(stone_cat)) { - DEBUG(status).print("cached stone material: %s\n", mi.toString().c_str()); + DEBUG(status).print("cached stone material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "stone")); } else if (mi.matches(wood_cat)) { - DEBUG(status).print("cached wood material: %s\n", mi.toString().c_str()); + DEBUG(status).print("cached wood material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "wood")); } else if (mi.matches(metal_cat)) { - DEBUG(status).print("cached metal material: %s\n", mi.toString().c_str()); + DEBUG(status).print("cached metal material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "metal")); } else if (mi.matches(glass_cat)) { - DEBUG(status).print("cached glass material: %s\n", mi.toString().c_str()); + DEBUG(status).print("cached glass material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "glass")); + } else if (mi.matches(clay_cat)) { + DEBUG(status).print("cached clay material: %s (%d, %d)\n", mi.toString().c_str(), type, index); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "clay")); } else TRACE(status).print("not matched: %s\n", mi.toString().c_str()); @@ -734,6 +738,8 @@ static int setMaterialMaskFilter(lua_State *L) { mask |= metal_cat.whole; else if (cat == "glass") mask |= glass_cat.whole; + else if (cat == "clay") + mask |= clay_cat.whole; } DEBUG(status,*out).print( "setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n", @@ -778,6 +784,7 @@ static int getMaterialMaskFilter(lua_State *L) { ret.emplace("wood", !bits || bits & wood_cat.whole); ret.emplace("metal", !bits || bits & metal_cat.whole); ret.emplace("glass", !bits || bits & glass_cat.whole); + ret.emplace("clay", !bits || bits & clay_cat.whole); Lua::Push(L, ret); return 1; } @@ -822,6 +829,8 @@ static int setMaterialFilter(lua_State *L) { mask.whole |= metal_cat.whole; else if (mat.matches(glass_cat)) mask.whole |= glass_cat.whole; + else if (mat.matches(clay_cat)) + mask.whole |= clay_cat.whole; } filter.setMaterialMask(mask.whole); get_item_filters(*out, key).setItemFilter(*out, filter, index); diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index d043406f3..e923c4b5b 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -443,6 +443,7 @@ function QualityAndMaterialsPage:refresh() make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), + make_cat_choice('Clay', 'clay', 'CUSTOM_SHIFT_C', cats), } self.subviews.materials_categories:setChoices(category_choices) From c48c9c77c0bee1aa123c463b60c5926760b1b5d6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 17:54:58 -0800 Subject: [PATCH 0803/2222] use new data directories for blueprints --- data/CMakeLists.txt | 12 +- data/blueprints/{library => }/aquifer_tap.csv | 0 .../28-3-Modified_Windmill_Villas.csv | 0 .../48-4-Raynard_Whirlpool_Housing.csv | 0 .../bedrooms/95-9-Hactar1_3_Branch_Tree.csv | 0 data/blueprints/{library => }/dreamfort.csv | 0 data/blueprints/{library => }/embark.csv | 0 .../connected-mineshafts.csv | 0 .../exploratory-mining/tunnels.csv | 0 .../vertical-mineshafts.csv | 0 .../layout-helpers/mark_down_left.csv | 0 .../layout-helpers/mark_down_right.csv | 0 .../layout-helpers/mark_up_left.csv | 0 .../layout-helpers/mark_up_right.csv | 0 data/blueprints/{library => }/pump_stack.csv | 0 .../{library => }/quickfortress.csv | 0 .../ecosystem/golden/gui_quantum-2-build.csv | 0 .../ecosystem/golden/gui_quantum-3-place.csv | 0 .../ecosystem/golden/gui_quantum-4-query.csv | 0 .../test/ecosystem/golden/meta-1-dig.csv | 0 .../test/ecosystem/golden/tracks-2-carve.csv | 0 .../test/ecosystem/golden/transform-1-dig.csv | 0 .../golden/transform-2-construct.csv | 0 .../ecosystem/golden/transform-3-build.csv | 0 .../test/ecosystem/in/basic-1-dig.csv | 0 .../test/ecosystem/in/basic-2-carve.csv | 0 .../test/ecosystem/in/basic-3-build.csv | 0 .../test/ecosystem/in/basic-4-place.csv | 0 .../test/ecosystem/in/basic-5-zone.csv | 0 .../test/ecosystem/in/basic-spec.csv | 0 .../test/ecosystem/in/buildings-1-dig.csv | 0 .../ecosystem/in/buildings-2-construct.csv | 0 .../test/ecosystem/in/buildings-3-build.csv | 0 .../test/ecosystem/in/buildings-spec.csv | 0 .../ecosystem/in/fortifications-1-dig.csv | 0 .../ecosystem/in/fortifications-2-smooth.csv | 0 .../ecosystem/in/fortifications-3-carve.csv | 0 .../test/ecosystem/in/fortifications-spec.csv | 0 .../test/ecosystem/in/gui_quantum-2-build.csv | 0 .../test/ecosystem/in/gui_quantum-3-place.csv | 0 .../test/ecosystem/in/gui_quantum-4-query.csv | 0 .../test/ecosystem/in/gui_quantum-spec.csv | 0 .../test/ecosystem/in/meta-1-dig.csv | 0 .../test/ecosystem/in/meta-spec.csv | 0 .../test/ecosystem/in/stockpiles-2-place.csv | 0 .../test/ecosystem/in/stockpiles-spec.csv | 0 .../test/ecosystem/in/tracks-1-dig.csv | 0 .../test/ecosystem/in/tracks-2-carve.csv | 0 .../test/ecosystem/in/tracks-3-build.csv | 0 .../test/ecosystem/in/tracks-spec.csv | 0 .../test/ecosystem/in/transform-1-dig.csv | 0 .../ecosystem/in/transform-2-construct.csv | 0 .../test/ecosystem/in/transform-3-build.csv | 0 .../test/ecosystem/in/transform-spec.csv | 0 .../test/ecosystem/in/zones-2-zone.csv | 0 .../test/ecosystem/in/zones-spec.csv | 0 .../test/quickfort/list/all_modes.csv | 0 .../list/all_modes_separate_sheets.xlsx | Bin .../list/all_modes_single_sheet.xlsx | Bin .../{library => }/tombs/Mini_Saracen.csv | 0 .../tombs/The_Saracen_Crypts.csv | 0 data/{ => dfhack-config}/blueprints/README.md | 0 data/dfhack-config/quickfort/aliases.txt | 17 - data/dfhack-config/quickfort/quickfort.txt | 37 -- data/quickfort/aliases-common.txt | 509 ------------------ docs/plugins/blueprint.rst | 2 +- plugins/blueprint.cpp | 4 +- plugins/lua/blueprint.lua | 2 +- 68 files changed, 9 insertions(+), 574 deletions(-) rename data/blueprints/{library => }/aquifer_tap.csv (100%) rename data/blueprints/{library => }/bedrooms/28-3-Modified_Windmill_Villas.csv (100%) rename data/blueprints/{library => }/bedrooms/48-4-Raynard_Whirlpool_Housing.csv (100%) rename data/blueprints/{library => }/bedrooms/95-9-Hactar1_3_Branch_Tree.csv (100%) rename data/blueprints/{library => }/dreamfort.csv (100%) rename data/blueprints/{library => }/embark.csv (100%) rename data/blueprints/{library => }/exploratory-mining/connected-mineshafts.csv (100%) rename data/blueprints/{library => }/exploratory-mining/tunnels.csv (100%) rename data/blueprints/{library => }/exploratory-mining/vertical-mineshafts.csv (100%) rename data/blueprints/{library => }/layout-helpers/mark_down_left.csv (100%) rename data/blueprints/{library => }/layout-helpers/mark_down_right.csv (100%) rename data/blueprints/{library => }/layout-helpers/mark_up_left.csv (100%) rename data/blueprints/{library => }/layout-helpers/mark_up_right.csv (100%) rename data/blueprints/{library => }/pump_stack.csv (100%) rename data/blueprints/{library => }/quickfortress.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/gui_quantum-2-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/gui_quantum-3-place.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/gui_quantum-4-query.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/meta-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/tracks-2-carve.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/transform-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/transform-2-construct.csv (100%) rename data/blueprints/{library => }/test/ecosystem/golden/transform-3-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/basic-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/basic-2-carve.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/basic-3-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/basic-4-place.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/basic-5-zone.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/basic-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/buildings-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/buildings-2-construct.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/buildings-3-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/buildings-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/fortifications-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/fortifications-2-smooth.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/fortifications-3-carve.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/fortifications-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/gui_quantum-2-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/gui_quantum-3-place.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/gui_quantum-4-query.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/gui_quantum-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/meta-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/meta-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/stockpiles-2-place.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/stockpiles-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/tracks-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/tracks-2-carve.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/tracks-3-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/tracks-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/transform-1-dig.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/transform-2-construct.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/transform-3-build.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/transform-spec.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/zones-2-zone.csv (100%) rename data/blueprints/{library => }/test/ecosystem/in/zones-spec.csv (100%) rename data/blueprints/{library => }/test/quickfort/list/all_modes.csv (100%) rename data/blueprints/{library => }/test/quickfort/list/all_modes_separate_sheets.xlsx (100%) rename data/blueprints/{library => }/test/quickfort/list/all_modes_single_sheet.xlsx (100%) rename data/blueprints/{library => }/tombs/Mini_Saracen.csv (100%) rename data/blueprints/{library => }/tombs/The_Saracen_Crypts.csv (100%) rename data/{ => dfhack-config}/blueprints/README.md (100%) delete mode 100644 data/dfhack-config/quickfort/aliases.txt delete mode 100644 data/dfhack-config/quickfort/quickfort.txt delete mode 100644 data/quickfort/aliases-common.txt diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 8cbd910cb..89abe3aaf 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -7,9 +7,6 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/init/ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/base_command_counts.json DESTINATION "${DFHACK_DATA_DESTINATION}/data") -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/quickfort/ - DESTINATION "${DFHACK_DATA_DESTINATION}/data/quickfort") - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/orders/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/orders") @@ -20,12 +17,11 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/professions/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/professions") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/blueprints/ - DESTINATION blueprints + DESTINATION "${DFHACK_DATA_DESTINATION}/data/blueprints" FILES_MATCHING PATTERN "*" - PATTERN blueprints/library/test EXCLUDE) + PATTERN blueprints/test EXCLUDE) if(BUILD_TESTS) - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/blueprints/library/test/ - DESTINATION blueprints/library/test - ) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/blueprints/test/ + DESTINATION "${DFHACK_DATA_DESTINATION}/data/blueprints/test") endif() diff --git a/data/blueprints/library/aquifer_tap.csv b/data/blueprints/aquifer_tap.csv similarity index 100% rename from data/blueprints/library/aquifer_tap.csv rename to data/blueprints/aquifer_tap.csv diff --git a/data/blueprints/library/bedrooms/28-3-Modified_Windmill_Villas.csv b/data/blueprints/bedrooms/28-3-Modified_Windmill_Villas.csv similarity index 100% rename from data/blueprints/library/bedrooms/28-3-Modified_Windmill_Villas.csv rename to data/blueprints/bedrooms/28-3-Modified_Windmill_Villas.csv diff --git a/data/blueprints/library/bedrooms/48-4-Raynard_Whirlpool_Housing.csv b/data/blueprints/bedrooms/48-4-Raynard_Whirlpool_Housing.csv similarity index 100% rename from data/blueprints/library/bedrooms/48-4-Raynard_Whirlpool_Housing.csv rename to data/blueprints/bedrooms/48-4-Raynard_Whirlpool_Housing.csv diff --git a/data/blueprints/library/bedrooms/95-9-Hactar1_3_Branch_Tree.csv b/data/blueprints/bedrooms/95-9-Hactar1_3_Branch_Tree.csv similarity index 100% rename from data/blueprints/library/bedrooms/95-9-Hactar1_3_Branch_Tree.csv rename to data/blueprints/bedrooms/95-9-Hactar1_3_Branch_Tree.csv diff --git a/data/blueprints/library/dreamfort.csv b/data/blueprints/dreamfort.csv similarity index 100% rename from data/blueprints/library/dreamfort.csv rename to data/blueprints/dreamfort.csv diff --git a/data/blueprints/library/embark.csv b/data/blueprints/embark.csv similarity index 100% rename from data/blueprints/library/embark.csv rename to data/blueprints/embark.csv diff --git a/data/blueprints/library/exploratory-mining/connected-mineshafts.csv b/data/blueprints/exploratory-mining/connected-mineshafts.csv similarity index 100% rename from data/blueprints/library/exploratory-mining/connected-mineshafts.csv rename to data/blueprints/exploratory-mining/connected-mineshafts.csv diff --git a/data/blueprints/library/exploratory-mining/tunnels.csv b/data/blueprints/exploratory-mining/tunnels.csv similarity index 100% rename from data/blueprints/library/exploratory-mining/tunnels.csv rename to data/blueprints/exploratory-mining/tunnels.csv diff --git a/data/blueprints/library/exploratory-mining/vertical-mineshafts.csv b/data/blueprints/exploratory-mining/vertical-mineshafts.csv similarity index 100% rename from data/blueprints/library/exploratory-mining/vertical-mineshafts.csv rename to data/blueprints/exploratory-mining/vertical-mineshafts.csv diff --git a/data/blueprints/library/layout-helpers/mark_down_left.csv b/data/blueprints/layout-helpers/mark_down_left.csv similarity index 100% rename from data/blueprints/library/layout-helpers/mark_down_left.csv rename to data/blueprints/layout-helpers/mark_down_left.csv diff --git a/data/blueprints/library/layout-helpers/mark_down_right.csv b/data/blueprints/layout-helpers/mark_down_right.csv similarity index 100% rename from data/blueprints/library/layout-helpers/mark_down_right.csv rename to data/blueprints/layout-helpers/mark_down_right.csv diff --git a/data/blueprints/library/layout-helpers/mark_up_left.csv b/data/blueprints/layout-helpers/mark_up_left.csv similarity index 100% rename from data/blueprints/library/layout-helpers/mark_up_left.csv rename to data/blueprints/layout-helpers/mark_up_left.csv diff --git a/data/blueprints/library/layout-helpers/mark_up_right.csv b/data/blueprints/layout-helpers/mark_up_right.csv similarity index 100% rename from data/blueprints/library/layout-helpers/mark_up_right.csv rename to data/blueprints/layout-helpers/mark_up_right.csv diff --git a/data/blueprints/library/pump_stack.csv b/data/blueprints/pump_stack.csv similarity index 100% rename from data/blueprints/library/pump_stack.csv rename to data/blueprints/pump_stack.csv diff --git a/data/blueprints/library/quickfortress.csv b/data/blueprints/quickfortress.csv similarity index 100% rename from data/blueprints/library/quickfortress.csv rename to data/blueprints/quickfortress.csv diff --git a/data/blueprints/library/test/ecosystem/golden/gui_quantum-2-build.csv b/data/blueprints/test/ecosystem/golden/gui_quantum-2-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/gui_quantum-2-build.csv rename to data/blueprints/test/ecosystem/golden/gui_quantum-2-build.csv diff --git a/data/blueprints/library/test/ecosystem/golden/gui_quantum-3-place.csv b/data/blueprints/test/ecosystem/golden/gui_quantum-3-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/gui_quantum-3-place.csv rename to data/blueprints/test/ecosystem/golden/gui_quantum-3-place.csv diff --git a/data/blueprints/library/test/ecosystem/golden/gui_quantum-4-query.csv b/data/blueprints/test/ecosystem/golden/gui_quantum-4-query.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/gui_quantum-4-query.csv rename to data/blueprints/test/ecosystem/golden/gui_quantum-4-query.csv diff --git a/data/blueprints/library/test/ecosystem/golden/meta-1-dig.csv b/data/blueprints/test/ecosystem/golden/meta-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/meta-1-dig.csv rename to data/blueprints/test/ecosystem/golden/meta-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/golden/tracks-2-carve.csv b/data/blueprints/test/ecosystem/golden/tracks-2-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/tracks-2-carve.csv rename to data/blueprints/test/ecosystem/golden/tracks-2-carve.csv diff --git a/data/blueprints/library/test/ecosystem/golden/transform-1-dig.csv b/data/blueprints/test/ecosystem/golden/transform-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/transform-1-dig.csv rename to data/blueprints/test/ecosystem/golden/transform-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/golden/transform-2-construct.csv b/data/blueprints/test/ecosystem/golden/transform-2-construct.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/transform-2-construct.csv rename to data/blueprints/test/ecosystem/golden/transform-2-construct.csv diff --git a/data/blueprints/library/test/ecosystem/golden/transform-3-build.csv b/data/blueprints/test/ecosystem/golden/transform-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/golden/transform-3-build.csv rename to data/blueprints/test/ecosystem/golden/transform-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-1-dig.csv b/data/blueprints/test/ecosystem/in/basic-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-1-dig.csv rename to data/blueprints/test/ecosystem/in/basic-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-2-carve.csv b/data/blueprints/test/ecosystem/in/basic-2-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-2-carve.csv rename to data/blueprints/test/ecosystem/in/basic-2-carve.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-3-build.csv b/data/blueprints/test/ecosystem/in/basic-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-3-build.csv rename to data/blueprints/test/ecosystem/in/basic-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-4-place.csv b/data/blueprints/test/ecosystem/in/basic-4-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-4-place.csv rename to data/blueprints/test/ecosystem/in/basic-4-place.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-5-zone.csv b/data/blueprints/test/ecosystem/in/basic-5-zone.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-5-zone.csv rename to data/blueprints/test/ecosystem/in/basic-5-zone.csv diff --git a/data/blueprints/library/test/ecosystem/in/basic-spec.csv b/data/blueprints/test/ecosystem/in/basic-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/basic-spec.csv rename to data/blueprints/test/ecosystem/in/basic-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-1-dig.csv b/data/blueprints/test/ecosystem/in/buildings-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-1-dig.csv rename to data/blueprints/test/ecosystem/in/buildings-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-2-construct.csv b/data/blueprints/test/ecosystem/in/buildings-2-construct.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-2-construct.csv rename to data/blueprints/test/ecosystem/in/buildings-2-construct.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-3-build.csv b/data/blueprints/test/ecosystem/in/buildings-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-3-build.csv rename to data/blueprints/test/ecosystem/in/buildings-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/buildings-spec.csv b/data/blueprints/test/ecosystem/in/buildings-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/buildings-spec.csv rename to data/blueprints/test/ecosystem/in/buildings-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-1-dig.csv b/data/blueprints/test/ecosystem/in/fortifications-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-1-dig.csv rename to data/blueprints/test/ecosystem/in/fortifications-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-2-smooth.csv b/data/blueprints/test/ecosystem/in/fortifications-2-smooth.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-2-smooth.csv rename to data/blueprints/test/ecosystem/in/fortifications-2-smooth.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-3-carve.csv b/data/blueprints/test/ecosystem/in/fortifications-3-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-3-carve.csv rename to data/blueprints/test/ecosystem/in/fortifications-3-carve.csv diff --git a/data/blueprints/library/test/ecosystem/in/fortifications-spec.csv b/data/blueprints/test/ecosystem/in/fortifications-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/fortifications-spec.csv rename to data/blueprints/test/ecosystem/in/fortifications-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-2-build.csv b/data/blueprints/test/ecosystem/in/gui_quantum-2-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/gui_quantum-2-build.csv rename to data/blueprints/test/ecosystem/in/gui_quantum-2-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-3-place.csv b/data/blueprints/test/ecosystem/in/gui_quantum-3-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/gui_quantum-3-place.csv rename to data/blueprints/test/ecosystem/in/gui_quantum-3-place.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-4-query.csv b/data/blueprints/test/ecosystem/in/gui_quantum-4-query.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/gui_quantum-4-query.csv rename to data/blueprints/test/ecosystem/in/gui_quantum-4-query.csv diff --git a/data/blueprints/library/test/ecosystem/in/gui_quantum-spec.csv b/data/blueprints/test/ecosystem/in/gui_quantum-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/gui_quantum-spec.csv rename to data/blueprints/test/ecosystem/in/gui_quantum-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/meta-1-dig.csv b/data/blueprints/test/ecosystem/in/meta-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/meta-1-dig.csv rename to data/blueprints/test/ecosystem/in/meta-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/meta-spec.csv b/data/blueprints/test/ecosystem/in/meta-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/meta-spec.csv rename to data/blueprints/test/ecosystem/in/meta-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/stockpiles-2-place.csv b/data/blueprints/test/ecosystem/in/stockpiles-2-place.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/stockpiles-2-place.csv rename to data/blueprints/test/ecosystem/in/stockpiles-2-place.csv diff --git a/data/blueprints/library/test/ecosystem/in/stockpiles-spec.csv b/data/blueprints/test/ecosystem/in/stockpiles-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/stockpiles-spec.csv rename to data/blueprints/test/ecosystem/in/stockpiles-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-1-dig.csv b/data/blueprints/test/ecosystem/in/tracks-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-1-dig.csv rename to data/blueprints/test/ecosystem/in/tracks-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-2-carve.csv b/data/blueprints/test/ecosystem/in/tracks-2-carve.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-2-carve.csv rename to data/blueprints/test/ecosystem/in/tracks-2-carve.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-3-build.csv b/data/blueprints/test/ecosystem/in/tracks-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-3-build.csv rename to data/blueprints/test/ecosystem/in/tracks-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/tracks-spec.csv b/data/blueprints/test/ecosystem/in/tracks-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/tracks-spec.csv rename to data/blueprints/test/ecosystem/in/tracks-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-1-dig.csv b/data/blueprints/test/ecosystem/in/transform-1-dig.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-1-dig.csv rename to data/blueprints/test/ecosystem/in/transform-1-dig.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-2-construct.csv b/data/blueprints/test/ecosystem/in/transform-2-construct.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-2-construct.csv rename to data/blueprints/test/ecosystem/in/transform-2-construct.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-3-build.csv b/data/blueprints/test/ecosystem/in/transform-3-build.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-3-build.csv rename to data/blueprints/test/ecosystem/in/transform-3-build.csv diff --git a/data/blueprints/library/test/ecosystem/in/transform-spec.csv b/data/blueprints/test/ecosystem/in/transform-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/transform-spec.csv rename to data/blueprints/test/ecosystem/in/transform-spec.csv diff --git a/data/blueprints/library/test/ecosystem/in/zones-2-zone.csv b/data/blueprints/test/ecosystem/in/zones-2-zone.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/zones-2-zone.csv rename to data/blueprints/test/ecosystem/in/zones-2-zone.csv diff --git a/data/blueprints/library/test/ecosystem/in/zones-spec.csv b/data/blueprints/test/ecosystem/in/zones-spec.csv similarity index 100% rename from data/blueprints/library/test/ecosystem/in/zones-spec.csv rename to data/blueprints/test/ecosystem/in/zones-spec.csv diff --git a/data/blueprints/library/test/quickfort/list/all_modes.csv b/data/blueprints/test/quickfort/list/all_modes.csv similarity index 100% rename from data/blueprints/library/test/quickfort/list/all_modes.csv rename to data/blueprints/test/quickfort/list/all_modes.csv diff --git a/data/blueprints/library/test/quickfort/list/all_modes_separate_sheets.xlsx b/data/blueprints/test/quickfort/list/all_modes_separate_sheets.xlsx similarity index 100% rename from data/blueprints/library/test/quickfort/list/all_modes_separate_sheets.xlsx rename to data/blueprints/test/quickfort/list/all_modes_separate_sheets.xlsx diff --git a/data/blueprints/library/test/quickfort/list/all_modes_single_sheet.xlsx b/data/blueprints/test/quickfort/list/all_modes_single_sheet.xlsx similarity index 100% rename from data/blueprints/library/test/quickfort/list/all_modes_single_sheet.xlsx rename to data/blueprints/test/quickfort/list/all_modes_single_sheet.xlsx diff --git a/data/blueprints/library/tombs/Mini_Saracen.csv b/data/blueprints/tombs/Mini_Saracen.csv similarity index 100% rename from data/blueprints/library/tombs/Mini_Saracen.csv rename to data/blueprints/tombs/Mini_Saracen.csv diff --git a/data/blueprints/library/tombs/The_Saracen_Crypts.csv b/data/blueprints/tombs/The_Saracen_Crypts.csv similarity index 100% rename from data/blueprints/library/tombs/The_Saracen_Crypts.csv rename to data/blueprints/tombs/The_Saracen_Crypts.csv diff --git a/data/blueprints/README.md b/data/dfhack-config/blueprints/README.md similarity index 100% rename from data/blueprints/README.md rename to data/dfhack-config/blueprints/README.md diff --git a/data/dfhack-config/quickfort/aliases.txt b/data/dfhack-config/quickfort/aliases.txt deleted file mode 100644 index a3e52052d..000000000 --- a/data/dfhack-config/quickfort/aliases.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Custom aliases for quickfort query mode blueprints -# -# This file defines custom key sequence shortcuts for query mode blueprints. -# Definitions in this file take precedence over any definitions in the -# baseline aliases configuration file in -# hack/data/quickfort/aliases-common.txt -# -# Please see -# https://docs.dfhack.org/en/latest/docs/guides/quickfort-alias-guide.html -# or -# hack/docs/docs/guides/quickfort-alias-guide.html -# in your DF installation directory for alias syntax documentation and an -# overview of the DFHack alias standard library. -# -# -# Add your custom aliases here. Example: -# food_stash: {foodprefix}b{Right}{Down 11}p^{permitplants} diff --git a/data/dfhack-config/quickfort/quickfort.txt b/data/dfhack-config/quickfort/quickfort.txt deleted file mode 100644 index 92af2909e..000000000 --- a/data/dfhack-config/quickfort/quickfort.txt +++ /dev/null @@ -1,37 +0,0 @@ -# quickfort main configuration file -# -# Set startup defaults for the quickfort script in this file. Settings can be -# temporarily overridden in the active session with the `quickfort set` command. -# -# If you have edited this file but want to revert to "factory defaults", delete -# this file and a default one will be regenerated for you the next time you -# start DFHack. - -# Directory tree to search for blueprints. Can be set to an absolute or relative -# path. If set to a relative path, resolves to a directory under the DF folder. -# Note that if you change this directory, you will not automatically pick up -# blueprints written by the DFHack "blueprint" plugin (which always writes to -# the "blueprints" dir). -blueprints_dir=blueprints - -# Set to "true" or "false". If true, will designate all dig blueprints in marker -# mode. If false, only cells with dig codes explicitly prefixed with an "m" will -# be designated in marker mode. -force_marker_mode=false - -# Skip query blueprint sanity checks that detect common blueprint errors and -# halt or skip keycode playback. Checks include ensuring a configurable building -# exists at the designated cursor position and verifying the active UI screen is -# the same before and after sending keys for the cursor position. If you find -# you need to enable this for one of your own blueprints, you should probably be -# using a config blueprint, not a query blueprint. -query_unsafe=false - -# Set to the maximum number of resources you want assigned to stockpiles of the -# relevant types. Set to -1 for DF defaults (number of stockpile tiles for -# stockpiles that take barrels and bins, 1 wheelbarrow for stone stockpiles). -# The default here for wheelbarrows is 0 since restricting stockpiles to a -# single wheelbarrow can drastically *decrease* the efficiency of your fort. -stockpiles_max_barrels=-1 -stockpiles_max_bins=-1 -stockpiles_max_wheelbarrows=0 diff --git a/data/quickfort/aliases-common.txt b/data/quickfort/aliases-common.txt deleted file mode 100644 index f000b5bd1..000000000 --- a/data/quickfort/aliases-common.txt +++ /dev/null @@ -1,509 +0,0 @@ -# Standard library of aliases for quickfort query mode blueprints. -# -# Please DO NOT EDIT this file directly. It will get overwritten when DFHack -# is updated. Instead, custom aliases should be added to -# dfhack-config/quickfort/aliases.txt -# Custom alias definitions will take precedence over aliases in this file. -# -# Please see -# https://docs.dfhack.org/en/latest/docs/guides/quickfort-alias-guide.html -# or -# hack/docs/docs/guides/quickfort-alias-guide.html -# in your DF installation directory for alias syntax documentation and -# documentation for the aliases in this file. - -################################## -# naming aliases -################################## - -name: {Empty} -givename: !n{name}& -namezone: ^i{givename}^q - - -################################## -# quantum stockpile aliases -################################## - -# Allows the standard stockpile config aliases to also be used to configure -# hauling routes. -enter_sp_config: {enter_sp_config_default} -enter_sp_config_default: s -enter_sp_config_hauling: & - -quantum_enable: {enableanimals}{enablefood}{enablefurniture}{enablestone}{enableammo}{enablecoins}{enablebars}{enablegems}{enablefinishedgoods}{enableleather}{enablecloth}{enablewood}{enableweapons}{enablearmor}{enablesheet} -quantum: {linksonly}{nocontainers}{quantum_enable}{givename} - -stop_name: {Empty} -route_enable: {quantum_enable}{enablecorpses}{enablerefuse} -sp_link: s{move}p{move_back} -sp_links: {sp_link} -quantumstop: ^hrn{name}&sn{stop_name}&&xxx{route_enable enter_sp_config={enter_sp_config_hauling}}{sp_links}^^q -quantumstopfromeast: {quantumstop move={Right} move_back={Left}} -quantumstopfromsouth: {quantumstop move={Down} move_back={Up}} -quantumstopfromwest: {quantumstop move={Left} move_back={Right}} -quantumstopfromnorth: {quantumstop move={Up} move_back={Down}} - - -################################## -# farm plots -################################## - -growlastcropall: a/&b/&c/&d/& -growfirstcropall: a&b&c&d& - - -######################################## -# stockpile utility aliases -######################################## - -linksonly: a -maxbins: V -maxbarrels: R -nobins: C -nobarrels: E -nocontainers: {nobins}{nobarrels} - -give: g{move}& -give2up: {give move={Up 2}} -give2down: {give move={Down 2}} -give2left: {give move={Left 2}} -give2right: {give move={Right 2}} -give10up: {give move={Up 10}} -give10down: {give move={Down 10}} -give10left: {give move={Left 10}} -give10right: {give move={Right 10}} - -togglesequence: &{Down} -togglesequence2: &{Down 2} - -# these aliases use the DFHack "search" plugin to filter the right column -forbidsearch: s{search}&f{Left}{Right} -permitsearch: s{search}&p{Left}{Right} -togglesearch: s{search}&&{Left}{Right} - -masterworkonly: {prefix}{Right}{Up 2}f{Right}{Up 2}&^ -artifactonly: {prefix}{Right}{Up 2}f{Right}{Up}&^ - -togglemasterwork: {prefix}{Right}{Up 2}{Right}{Up 2}&^ -toggleartifact: {prefix}{Right}{Up 2}{Right}{Up}&^ - - -################################## -# animal stockpile adjustments -################################## - -animalsprefix: {enter_sp_config} -enableanimals: {animalsprefix}e^ -disableanimals: {animalsprefix}d^ - -cages: {animalsprefix}bu^ -traps: {animalsprefix}bj^ - -forbidcages: {animalsprefix}u^ -forbidtraps: {animalsprefix}j^ - -permitcages: {forbidcages} -permittraps: {forbidtraps} - - -################################## -# food stockpile adjustments -################################## - -foodprefix: {enter_sp_config}{Down} -enablefood: {foodprefix}e^ -disablefood: {foodprefix}d^ - -preparedfood: {foodprefix}bu^ -unpreparedfish: {foodprefix}b{Right}{Down 2}p^ -plants: {foodprefix}b{Right}{Down 4}p^ -booze: {foodprefix}b{Right}{Down 5}p{Down}p^ -seeds: {foodprefix}b{Right}{Down 9}p^ -dye: {foodprefix}b{Right}{Down 11}{Right}{Down 28}{togglesequence 4}^ -tallow: {foodprefix}b{Right}{Down 13}{Right}{permitsearch search=tallow}^ -miscliquid: {foodprefix}b{Right}{Down 18}p^ -wax: {foodprefix}b{Right}{Down 15}{Right}{Down 6}&^ - -forbidpreparedfood: {foodprefix}u^ -forbidunpreparedfish: {foodprefix}{Right}{Down 2}f^ -forbidplants: {foodprefix}{Right}{Down 4}f^ -forbidbooze: {foodprefix}{Right}{Down 5}f{Down}f^ -forbidseeds: {foodprefix}{Right}{Down 9}f^ -forbiddye: {foodprefix}{Right}{Down 11}{Right}{Down 28}{togglesequence 4}^ -forbidtallow: {foodprefix}{Right}{Down 13}{Right}{forbidsearch search=tallow}^ -forbidmiscliquid: {foodprefix}{Right}{Down 18}f^ -forbidwax: {foodprefix}{Right}{Down 15}{Right}{Down 6}&^ - -permitpreparedfood: {forbidpreparedfood} -permitunpreparedfish: {foodprefix}{Right}{Down 2}p^ -permitplants: {foodprefix}{Right}{Down 4}p^ -permitbooze: {foodprefix}{Right}{Down 5}p{Down}p^ -permitseeds: {foodprefix}{Right}{Down 9}p^ -permitdye: {forbiddye} -permittallow: {foodprefix}{Right}{Down 13}{Right}{permitsearch search=tallow}^ -permitmiscliquid: {foodprefix}{Right}{Down 18}p^ -permitwax: {forbidwax} - -# the next two aliases are for compatibility with previous implementations of -# Quickfort and are not documented. -# enables everything but seeds -noseeds: {disablefood}{enablefood}{forbidseeds} -# enables all food except for the types listed above -food: {noseeds}{forbidpreparedfood}{forbidunpreparedfish}{forbidplants}{forbidbooze}{forbiddye}{forbidtallow}{forbidmiscliquid} - - -################################## -# furniture stockpile adjustments -################################## - -furnitureprefix: {enter_sp_config}{Down 2} -enablefurniture: {furnitureprefix}e^ -disablefurniture: {furnitureprefix}d^ - -pots: {furnitureprefix}de{Right}f{Right}{Up 5}&^ -bags: {furnitureprefix}de{Right}f{Right}{Up 10}&{Left}{Down}f{Down}f{Down}f{Right}{Down}&{Down 6}&{Down}&{Down 6}&^ -buckets: {furnitureprefix}de{Right}f{Right}{Up 12}&^ -sand: {furnitureprefix}de{Right}f{Right}{Up}&^ - -forbidpots: {furnitureprefix}{Right 2}{Up 5}&^ -forbidbuckets: {furnitureprefix}{Right 2}{Up 12}&^ -forbidsand: {furnitureprefix}{Right 2}{Up}&^ - -permitpots: {forbidpots} -permitbuckets: {forbidbuckets} -permitsand: {forbidsand} - -masterworkfurniture: {masterworkonly prefix={furnitureprefix}} -artifactfurniture: {artifactonly prefix={furnitureprefix}} - -forbidmasterworkfurniture: {togglemasterwork prefix={furnitureprefix}} -forbidartifactfurniture: {toggleartifact prefix={furnitureprefix}} - -permitmasterworkfurniture: {togglemasterwork prefix={furnitureprefix}} -permitartifactfurniture: {toggleartifact prefix={furnitureprefix}} - - -########################################### -# corpses and refuse stockpile adjustments -########################################### - -corpsesprefix: {enter_sp_config}{Down 3} -enablecorpses: {corpsesprefix}e^ -disablecorpses: {corpsesprefix}d{Up}d^ - -refuseprefix: {enter_sp_config}{Down 4} -enablerefuse: {refuseprefix}e^ -disablerefuse: {refuseprefix}d^ - -corpses: {refuseprefix}b{Right}{Down}p^ -rawhides: {refuseprefix}b{Right 2}{Down}&^ -tannedhides: {refuseprefix}b{Right 2}{Down 53}&^ -skulls: {refuseprefix}b{Right}{Down 3}p^ -bones: {refuseprefix}b{Right}{Down 4}p^ -shells: {refuseprefix}b{Right}{Down 5}p^ -teeth: {refuseprefix}b{Right}{Down 6}p^ -horns: {refuseprefix}b{Right}{Down 7}p^ -hair: {refuseprefix}b{Right}{Down 8}p^ -usablehair: {refuseprefix}b{Right}{Down 8}{Right}{togglesearch search=sheep}{togglesearch search=llama}{togglesearch search=alpaca}{togglesearch search=troll}^ -craftrefuse: {skulls}{permitbones}{permitshells}{permitteeth}{permithorns}{permitusablehair} - -forbidcorpses: {refuseprefix}{Right}{Down}f^ -forbidrawhides: {refuseprefix}{Right 2}{Down}&^ -forbidtannedhides: {refuseprefix}{Right 2}{Down 53}&^ -forbidskulls: {refuseprefix}{Right}{Down 3}f^ -forbidbones: {refuseprefix}{Right}{Down 4}f^ -forbidshells: {refuseprefix}{Right}{Down 5}f^ -forbidteeth: {refuseprefix}{Right}{Down 6}f^ -forbidhorns: {refuseprefix}{Right}{Down 7}f^ -forbidhair: {refuseprefix}{Right}{Down 8}f^ -forbidusablehair: {refuseprefix}{Right}{Down 8}{Right}{forbidsearch search=sheep}{forbidsearch search=llama}{forbidsearch search=alpaca}{forbidsearch search=troll}^ -forbidcraftrefuse: {forbidskulls}{forbidbones}{forbidshells}{forbidteeth}{forbidhorns}{forbidusablehair} - -permitcorpses: {refuseprefix}{Right}{Down}p^ -permitrawhides: {forbidrawhides} -permittannedhides: {forbidtannedhides} -permitskulls: {refuseprefix}{Right}{Down 3}p^ -permitbones: {refuseprefix}{Right}{Down 4}p^ -permitshells: {refuseprefix}{Right}{Down 5}p^ -permitteeth: {refuseprefix}{Right}{Down 6}p^ -permithorns: {refuseprefix}{Right}{Down 7}p^ -permithair: {refuseprefix}{Right}{Down 8}p^ -permitusablehair: {refuseprefix}{Right}{Down 8}{Right}{permitsearch search=sheep}{permitsearch search=llama}{permitsearch search=alpaca}{permitsearch search=troll}^ -permitcraftrefuse: {permitskulls}{permitbones}{permitshells}{permitteeth}{permithorns}{permitusablehair} - - -################################## -# stone stockpile adjustments -################################## - -stoneprefix: {enter_sp_config}{Down 5} -enablestone: {stoneprefix}e^ -disablestone: {stoneprefix}d^ - -metal: {stoneprefix}b{Right}p^ -iron: {stoneprefix}b{Right}{Right}&{Down}&{Down 13}&^ -economic: {stoneprefix}b{Right}{Down}p^ -flux: {stoneprefix}b{Right}{Down}{Right}{togglesequence 4}{Down 4}&^ -plaster: {stoneprefix}b{Right}{Down}{Right}{Down 6}&{Down 3}{togglesequence 3}^ -coalproducing: {stoneprefix}b{Right}{Down}{Right}{Down 4}{togglesequence 2}^ -otherstone: {stoneprefix}b{Right}{Down 2}p^ -bauxite: {stoneprefix}b{Right}{Down 2}{Right}{Down 42}&^ -clay: {stoneprefix}b{Right}{Down 3}p^ - -forbidmetal: {stoneprefix}{Right}f^ -forbidiron: {stoneprefix}{Right}{Right}&{Down}&{Down 13}&^ -forbideconomic: {stoneprefix}{Right}{Down}f^ -forbidflux: {stoneprefix}{Right}{Down}{Right}{togglesequence 4}{Down 4}&^ -forbidplaster: {stoneprefix}{Right}{Down}{Right}{Down 6}&{Down 3}{togglesequence 3}^ -forbidcoalproducing: {stoneprefix}{Right}{Down}{Right}{Down 4}{togglesequence 2}^ -forbidotherstone: {stoneprefix}{Right}{Down 2}f^ -forbidbauxite: {stoneprefix}{Right}{Down 2}{Right}{Down 42}&^ -forbidclay: {stoneprefix}{Right}{Down 3}f^ - -permitmetal: {stoneprefix}{Right}p^ -permitiron: {forbidiron} -permiteconomic: {stoneprefix}{Right}{Down}p^ -permitflux: {forbidflux} -permitplaster: {forbidplaster} -permitcoalproducing: {forbidcoalproducing} -permitotherstone: {stoneprefix}{Right}{Down 2}p^ -permitbauxite: {forbidbauxite} -permitclay: {stoneprefix}{Right}{Down 3}p^ - - -################################## -# ammo stockpile adjustments -################################## - -ammoprefix: {enter_sp_config}{Down 6} -enableammo: {ammoprefix}e^ -disableammo: {ammoprefix}d^ - -bolts: {ammoprefix}a{Right 2}f&^ - -forbidmetalbolts: {ammoprefix}{Right}{Down}f^ -forbidwoodenbolts: {ammoprefix}{Right}{Down 2}{Right}&^ -forbidbonebolts: {ammoprefix}{Right}{Down 2}{Right}{Down}&^ - -masterworkammo: {masterworkonly prefix={ammoprefix}} -artifactammo: {artifactonly prefix={ammoprefix}} - -forbidmasterworkammo: {togglemasterwork prefix={ammoprefix}} -forbidartifactammo: {toggleartifact prefix={ammoprefix}} - -permitmasterworkammo: {togglemasterwork prefix={ammoprefix}} -permitartifactammo: {toggleartifact prefix={ammoprefix}} - - -################################## -# bar stockpile adjustments -################################## - -barsprefix: {enter_sp_config}{Down 8} -enablebars: {barsprefix}e^ -disablebars: {barsprefix}d^ - -bars: {barsprefix}b{Right}p{Down}p^ -metalbars: {barsprefix}b{Right}p^ -ironbars: {barsprefix}b{Right 2}&^ -steelbars: {barsprefix}b{Right 2}{Down 8}&^ -pigironbars: {barsprefix}b{Right 2}{Down 9}&^ -otherbars: {barsprefix}b{Right}{Down}p^ -coal: {barsprefix}b{Right}{Down}{Right}&^ -potash: {barsprefix}b{Right}{Down}{Right}{Down}&^ -ash: {barsprefix}b{Right}{Down}{Right}{Down 2}&^ -pearlash: {barsprefix}b{Right}{Down}{Right}{Down 3}&^ -soap: {barsprefix}b{Right}{Down}{Right}{Down 4}&^ -blocks: {barsprefix}b{Down 2}p{Down}p{Down}p^ - -forbidbars: {barsprefix}{Right}f{Down}f^ -forbidmetalbars: {barsprefix}{Right}f^ -forbidironbars: {barsprefix}{Right 2}&^ -forbidsteelbars: {barsprefix}{Right 2}{Down 8}&^ -forbidpigironbars: {barsprefix}{Right 2}{Down 9}&^ -forbidotherbars: {barsprefix}{Right}{Down}f^ -forbidcoal: {barsprefix}{Right}{Down}{Right}&^ -forbidpotash: {barsprefix}{Right}{Down}{Right}{Down}&^ -forbidash: {barsprefix}{Right}{Down}{Right}{Down 2}&^ -forbidpearlash: {barsprefix}{Right}{Down}{Right}{Down 3}&^ -forbidsoap: {barsprefix}{Right}{Down}{Right}{Down 4}&^ -forbidblocks: {barsprefix}{Down 2}f{Down}f{Down}f^ - - -################################## -# gem stockpile adjustments -################################## - -gemsprefix: {enter_sp_config}{Down 9} -enablegems: {gemsprefix}e^ -disablegems: {gemsprefix}d^ - -roughgems: {gemsprefix}b{Right}p^ -roughglass: {gemsprefix}b{Right}{Down}p^ -cutgems: {gemsprefix}b{Right}{Down 2}p^ -cutglass: {gemsprefix}b{Right}{Down 3}p^ -cutstone: {gemsprefix}b{Right}{Down 4}p^ - -forbidroughgems: {gemsprefix}{Right}f^ -forbidroughglass: {gemsprefix}{Right}{Down}f^ -forbidcutgems: {gemsprefix}{Right}{Down 2}f^ -forbidcutglass: {gemsprefix}{Right}{Down 3}f^ -forbidcutstone: {gemsprefix}{Right}{Down 4}f^ - - -####################################### -# finished goods stockpile adjustments -####################################### - -finishedgoodsprefix: {enter_sp_config}{Down 10} -enablefinishedgoods: {finishedgoodsprefix}e^ -disablefinishedgoods: {finishedgoodsprefix}d^ - -crafts: {finishedgoodsprefix}{Right}f{Right}{Down 9}{togglesequence 9}^ -goblets: {finishedgoodsprefix}{Right}f{Right}{Down 2}&^ -jugs: {finishedgoodsprefix}{Right}f{Right}{Up 2}&{Left}{Down 2}f{Down}f{Down}f^ -stonetools: {finishedgoodsprefix}{Right}f{Right}{Up 2}&{Left}{Down 2}f{Down}f{Down}f^ -woodentools: {finishedgoodsprefix}{Right}f{Right}{Up 2}&{Left}{Down}f{Down}f{Down}f{Down}f{Right}&^ - -forbidcrafts: {finishedgoodsprefix}{Right 2}{Down 9}{togglesequence 9}^ -forbidgoblets: {finishedgoodsprefix}{Right 2}{Down 2}&^ - -permitcrafts: {forbidcrafts} -permitgoblets: {forbidgoblets} - -masterworkfinishedgoods: {masterworkonly prefix={finishedgoodsprefix}} -artifactfinishedgoods: {artifactonly prefix={finishedgoodsprefix}} - -forbidmasterworkfinishedgoods: {togglemasterwork prefix={finishedgoodsprefix}} -forbidartifactfinishedgoods: {toggleartifact prefix={finishedgoodsprefix}} - -permitmasterworkfinishedgoods: {togglemasterwork prefix={finishedgoodsprefix}} -permitartifactfinishedgoods: {toggleartifact prefix={finishedgoodsprefix}} - - -################################## -# cloth -################################## - -clothprefix: {enter_sp_config}{Down 12} -enablecloth: {clothprefix}e^ -disablecloth: {clothprefix}d^ - -thread: {clothprefix}b{Right}p{Down}p{Down}p^ -adamantinethread: {clothprefix}b{Right}{Down 3}p^ -cloth: {clothprefix}b{Right}{Down 4}p{Down}p{Down}p^ -adamantinecloth: {clothprefix}b{Right}{Up}p^ - -forbidthread: {clothprefix}{Right}f{Down}f{Down}f^ -forbidadamantinethread: {clothprefix}{Right}{Down 3}f^ -forbidcloth: {clothprefix}{Right}{Down 4}f{Down}f{Down}f^ -forbidadamantinecloth: {clothprefix}{Right}{Up}f^ - -permitthread: {clothprefix}{Right}p{Down}p{Down}p^ -permitadamantinethread: {clothprefix}{Right}{Down 3}p^ -permitcloth: {clothprefix}{Right}{Down 4}p{Down}p{Down}p^ -permitadamantinecloth: {clothprefix}{Right}{Up}p^ - -################################## -# weapon stockpile adjustments -################################## - -weaponsprefix: {enter_sp_config}{Down 14} -enableweapons: {weaponsprefix}e^ -disableweapons: {weaponsprefix}d^ - -metalweapons: {forbidtrapcomponents}{forbidstoneweapons}{forbidotherweapons} -ironweapons: {metalweapons}{forbidmetalweapons}{permitironweapons} -bronzeweapons: {metalweapons}{forbidmetalweapons}{permitbronzeweapons} -copperweapons: {metalweapons}{forbidmetalweapons}{permitcopperweapons} -steelweapons: {metalweapons}{forbidmetalweapons}{permitsteelweapons} - -forbidweapons: {weaponsprefix}{Right}f^ -forbidtrapcomponents: {weaponsprefix}{Right}{Down}f^ -forbidmetalweapons: {weaponsprefix}{Right}{Down 2}f^ -forbidstoneweapons: {weaponsprefix}{Right}{Down 3}f^ -forbidotherweapons: {weaponsprefix}{Right}{Down 4}f^ -forbidironweapons: {weaponsprefix}{Right}{Down 2}{Right}&^ -forbidbronzeweapons: {weaponsprefix}{Right}{Down 2}{Right}{Down 6}&^ -forbidcopperweapons: {weaponsprefix}{Right}{Down 2}{Right}{Down 3}&^ -forbidsteelweapons: {weaponsprefix}{Right}{Down 2}{Right}{Down 8}&^ - -permitweapons: {weaponsprefix}{Right}p^ -permittrapcomponents: {weaponsprefix}{Right}{Down}p^ -permitmetalweapons: {weaponsprefix}{Right}{Down 2}p^ -permitstoneweapons: {weaponsprefix}{Right}{Down 3}p^ -permitotherweapons: {weaponsprefix}{Right}{Down 4}p^ -permitironweapons: {forbidironweapons} -permitbronzeweapons: {forbidbronzeweapons} -permitcopperweapons: {forbidcopperweapons} -permitsteelweapons: {forbidsteelweapons} - -masterworkweapons: {masterworkonly prefix={weaponsprefix}} -artifactweapons: {artifactonly prefix={weaponsprefix}} - -forbidmasterworkweapons: {togglemasterwork prefix={weaponsprefix}} -forbidartifactweapons: {toggleartifact prefix={weaponsprefix}} - -permitmasterworkweapons: {togglemasterwork prefix={weaponsprefix}} -permitartifactweapons: {toggleartifact prefix={weaponsprefix}} - - -################################## -# armor stockpile adjustments -################################## - -armorprefix: {enter_sp_config}{Down 15} -enablearmor: {armorprefix}e^ -disablearmor: {armorprefix}d^ - -metalarmor: {forbidotherarmor} -otherarmor: {forbidmetalarmor} -ironarmor: {metalarmor}{forbidmetalarmor}{permitironarmor} -bronzearmor: {metalarmor}{forbidmetalarmor}{permitbronzearmor} -copperarmor: {metalarmor}{forbidmetalarmor}{permitcopperarmor} -steelarmor: {metalarmor}{forbidmetalarmor}{permitsteelarmor} - -forbidmetalarmor: {armorprefix}{Right}{Down 6}f^ -forbidotherarmor: {armorprefix}{Right}{Down 7}f^ -forbidironarmor: {armorprefix}{Right}{Down 6}{Right}&^ -forbidbronzearmor: {armorprefix}{Right}{Down 6}{Right}{Down 6}&^ -forbidcopperarmor: {armorprefix}{Right}{Down 6}{Right}{Down 3}&^ -forbidsteelarmor: {armorprefix}{Right}{Down 6}{Right}{Down 8}&^ - -permitmetalarmor: {armorprefix}{Right}{Down 6}p^ -permitotherarmor: {armorprefix}{Right}{Down 7}p^ -permitironarmor: {forbidironarmor} -permitbronzearmor: {forbidbronzearmor} -permitcopperarmor: {forbidcopperarmor} -permitsteelarmor: {forbidsteelarmor} - -masterworkarmor: {masterworkonly prefix={armorprefix}} -artifactarmor: {artifactonly prefix={armorprefix}} - -forbidmasterworkarmor: {togglemasterwork prefix={armorprefix}} -forbidartifactarmor: {toggleartifact prefix={armorprefix}} - -permitmasterworkarmor: {togglemasterwork prefix={armorprefix}} -permitartifactarmor: {toggleartifact prefix={armorprefix}} - - -################################## -# others -################################## - -coinsprefix: {enter_sp_config}{Down 7} -enablecoins: {coinsprefix}e^ -disablecoins: {coinsprefix}d^ - -leatherprefix: {enter_sp_config}{Down 11} -enableleather: {leatherprefix}e^ -disableleather: {leatherprefix}d^ - -woodprefix: {enter_sp_config}{Down 13} -enablewood: {woodprefix}e^ -disablewood: {woodprefix}d^ - -sheetprefix: {enter_sp_config}{Down 16} -enablesheet: {sheetprefix}e^ -disablesheet: {sheetprefix}d^ diff --git a/docs/plugins/blueprint.rst b/docs/plugins/blueprint.rst index b10921d30..6d2e9154f 100644 --- a/docs/plugins/blueprint.rst +++ b/docs/plugins/blueprint.rst @@ -9,7 +9,7 @@ With ``blueprint``, you can export the structure of a portion of your fortress in a blueprint file that you (or anyone else) can later play back with `gui/quickfort`. -Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` +Blueprints are ``.csv`` or ``.xlsx`` files created in the ``dfhack-config/blueprints`` subdirectory of your DF folder. The map area to turn into a blueprint is either selected interactively with the ``gui/blueprint`` command or, if the GUI is not used, starts at the active cursor location and extends right and down for the diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 3d34f592e..b09c4d497 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -46,6 +46,8 @@ using namespace DFHack; DFHACK_PLUGIN("blueprint"); REQUIRE_GLOBAL(world); +static const string BLUEPRINT_USER_DIR = "dfhack-config/blueprints/"; + namespace DFHack { DBG_DECLARE(blueprint,log); } @@ -1091,7 +1093,7 @@ static const char * get_tile_rooms(const df::coord &, const tile_context &ctx) { static bool create_output_dir(color_ostream &out, const blueprint_options &opts) { - string basename = "blueprints/" + opts.name; + string basename = BLUEPRINT_USER_DIR + opts.name; size_t last_slash = basename.find_last_of("/"); string parent_path = basename.substr(0, last_slash); diff --git a/plugins/lua/blueprint.lua b/plugins/lua/blueprint.lua index 718b211e8..375cfc1b4 100644 --- a/plugins/lua/blueprint.lua +++ b/plugins/lua/blueprint.lua @@ -206,7 +206,7 @@ end -- returns the name of the output file for the given context function get_filename(opts, phase, ordinal) - local fullname = 'blueprints/' .. opts.name + local fullname = 'dfhack-config/blueprints/' .. opts.name local _,_,basename = fullname:find('/([^/]+)/?$') if not basename then -- should not happen since opts.name should already be validated From c7a058c308732ea6f0ba01f4343c4c8199f7ebb9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 10 Mar 2023 18:00:10 -0800 Subject: [PATCH 0804/2222] fix library paths in docs --- docs/guides/quickfort-library-guide.rst | 36 ++++++++++++------------- docs/guides/quickfort-user-guide.rst | 10 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/guides/quickfort-library-guide.rst b/docs/guides/quickfort-library-guide.rst index 74b384cc0..47d86cd4f 100644 --- a/docs/guides/quickfort-library-guide.rst +++ b/docs/guides/quickfort-library-guide.rst @@ -5,7 +5,7 @@ Quickfort blueprint library =========================== This guide contains a high-level overview of the blueprints available in the -:source:`quickfort blueprint library `. +:source:`quickfort blueprint library `. Each file is hyperlinked to its online version so you can see exactly what the blueprints do before you run them. Also, if you use `gui/quickfort`, you will @@ -18,8 +18,8 @@ Whole fort blueprint sets These files contain the plans for entire fortresses. Each file has one or more help sections that walk you through how to build the fort, step by step. -- :source:`library/dreamfort.csv ` -- :source:`library/quickfortress.csv ` +- :source:`library/dreamfort.csv ` +- :source:`library/quickfortress.csv ` .. _dreamfort: @@ -149,10 +149,10 @@ these ``#dig`` blueprints can only mark undug wall tiles for mining, they are best used underground. They won't do much on the surface, where there aren't many walls. -- :source:`library/layout-helpers/mark_up_left.csv ` -- :source:`library/layout-helpers/mark_up_right.csv ` -- :source:`library/layout-helpers/mark_down_right.csv ` -- :source:`library/layout-helpers/mark_down_left.csv ` +- :source:`library/layout-helpers/mark_up_left.csv ` +- :source:`library/layout-helpers/mark_up_right.csv ` +- :source:`library/layout-helpers/mark_down_right.csv ` +- :source:`library/layout-helpers/mark_down_left.csv ` Bedrooms -------- @@ -161,9 +161,9 @@ These are popular bedroom layouts from the :wiki:`Bedroom design` page on the wiki. Each file has ``#dig``, ``#build``, and ``#query`` blueprints to dig the rooms, build the furniture, and configure the beds as bedrooms, respectively. -- :source:`library/bedrooms/48-4-Raynard_Whirlpool_Housing.csv ` -- :source:`library/bedrooms/95-9-Hactar1_3_Branch_Tree.csv ` -- :source:`library/bedrooms/28-3-Modified_Windmill_Villas.csv ` +- :source:`library/bedrooms/48-4-Raynard_Whirlpool_Housing.csv ` +- :source:`library/bedrooms/95-9-Hactar1_3_Branch_Tree.csv ` +- :source:`library/bedrooms/28-3-Modified_Windmill_Villas.csv ` Tombs ----- @@ -171,8 +171,8 @@ Tombs These blueprints have burial plot layouts for fortress that expect a lot of casualties. -- :source:`library/tombs/Mini_Saracen.csv ` -- :source:`library/tombs/The_Saracen_Crypts.csv ` +- :source:`library/tombs/Mini_Saracen.csv ` +- :source:`library/tombs/The_Saracen_Crypts.csv ` Exploratory mining ------------------ @@ -181,18 +181,18 @@ Several mining patterns to choose from when searching for gems or ores. The patterns can be repeated up or down z-levels (via quickfort's ``--repeat`` commandline option) for exploring through the depths. -- :source:`library/exploratory-mining/tunnels.csv ` -- :source:`library/exploratory-mining/vertical-mineshafts.csv ` -- :source:`library/exploratory-mining/connected-mineshafts.csv ` +- :source:`library/exploratory-mining/tunnels.csv ` +- :source:`library/exploratory-mining/vertical-mineshafts.csv ` +- :source:`library/exploratory-mining/connected-mineshafts.csv ` Miscellaneous ------------- Extra blueprints that are useful in specific situations. -- :source:`library/aquifer_tap.csv ` -- :source:`library/embark.csv ` -- :source:`library/pump_stack.csv ` +- :source:`library/aquifer_tap.csv ` +- :source:`library/embark.csv ` +- :source:`library/pump_stack.csv ` Light aquifer tap ~~~~~~~~~~~~~~~~~ diff --git a/docs/guides/quickfort-user-guide.rst b/docs/guides/quickfort-user-guide.rst index ecee46918..ec416f18f 100644 --- a/docs/guides/quickfort-user-guide.rst +++ b/docs/guides/quickfort-user-guide.rst @@ -27,9 +27,9 @@ written by Joel Thornton, reused here with his permission. For those just looking to apply existing blueprints, check out the `quickfort command's documentation ` for syntax. There are also many -ready-to-use blueprints available in the ``blueprints/library`` subfolder in +ready-to-use blueprints available in the ``hack/data/blueprints`` subfolder in your DFHack installation. Browse them on your computer or -:source:`online `, or run `gui/quickfort` to browse +:source:`online `, or run `gui/quickfort` to browse and apply them to your fort! Before you become an expert at writing blueprints, though, you should know that @@ -248,7 +248,7 @@ You can save a lot of time and effort by using aliases instead of adding all key sequences directly to your blueprints. For more details, check out the `quickfort-alias-guide`. You can also see examples of aliases being used in the query blueprints in the -:source:`DFHack blueprint library `. You can create +:source:`DFHack blueprint library `. You can create your own aliases by adding them to :source:`dfhack-config/quickfort/aliases.txt` in your DFHack folder or you can package them `together with your blueprint files `. @@ -1467,7 +1467,7 @@ Dreamfort organization and packaging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Dreamfort blueprints are distributed with DFHack as -:source:`one large .csv file `, but +:source:`one large .csv file `, but editing in that format would be frustrating. Instead, the blueprints are edited `online as Google drive spreadsheets `__. @@ -1825,7 +1825,7 @@ Links - `blueprint-library-guide` - :forums:`Quickfort forum thread <176889>` - :issue:`DFHack issue tracker <>` -- :source:`Blueprint library source ` +- :source:`Blueprint library source ` - :source-scripts:`Quickfort source code ` **Related tools:** From f549b7178d015334231b9377e7fb533c53560482 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Mar 2023 15:43:23 -0700 Subject: [PATCH 0805/2222] update changelog --- docs/changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b0ed8b981..f0e606856 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory +- `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` +- player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. ## Documentation From 78e0e02f31329c8e48bc41376effc642225e5147 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Mar 2023 15:57:36 -0700 Subject: [PATCH 0806/2222] update scripts ref --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 5896b316f..b17ca9443 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5896b316f6ebb3d0abf3316a2ba895c173051b36 +Subproject commit b17ca9443e797431c2f3594556658beea4d7c378 From a8a447c0e1d9e962dec3e4facf4be90cc006281b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Mar 2023 20:02:03 -0700 Subject: [PATCH 0807/2222] ignore new package directory build output --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9b78fa31f..e24f11252 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ build*/ nix buntu -build/VC2010 #except for the real one !build/ @@ -37,6 +36,7 @@ build/lua build/bin build/depends build/library +build/package build/plugins build/scripts build/install_manifest.txt From 543f9ade0def3b14a4867a6c66ca15036c1277dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 13 Mar 2023 13:35:12 -0700 Subject: [PATCH 0808/2222] account for reverse ordering in job_items vector --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 49 ++++++++++++--------- plugins/buildingplan/buildingplan_cycle.cpp | 13 ++++-- plugins/buildingplan/itemfilter.cpp | 13 +++++- plugins/buildingplan/plannedbuilding.cpp | 7 +-- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 50f586687..91c1168dd 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +-@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings ## Misc Improvements -@ `buildingplan`: can now filter by clay materials diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index b4f3839f4..ba9cbf5fb 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -372,7 +372,7 @@ static command_result do_command(color_ostream &out, vector ¶meters) // static string getBucket(const df::job_item & ji, const PlannedBuilding & pb, int idx) { - if (idx < 0 || (size_t)idx < pb.item_filters.size()) + if (idx < 0 || (size_t)idx >= pb.item_filters.size()) return "INVALID"; std::ostringstream ser; @@ -428,7 +428,7 @@ vector getVectorIds(color_ostream &out, const df::job_it return ret; } - // if the filer is for building material, refer to our global settings for + // if the filter is for building material, refer to our global settings for // which vectors to search if (job_item->flags2.bits.building_material) { @@ -465,10 +465,11 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { return true; } - int num_job_items = job_items.size(); + int num_job_items = (int)job_items.size(); int32_t id = bld->id; for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx) { - auto job_item = job_items[job_item_idx]; + int rev_jitem_index = num_job_items - (job_item_idx+1); + auto job_item = job_items[rev_jitem_index]; auto bucket = getBucket(*job_item, pb, job_item_idx); // if there are multiple vector_ids, schedule duplicate tasks. after @@ -476,11 +477,11 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { // as invalid for (auto vector_id : pb.vector_ids[job_item_idx]) { for (int item_num = 0; item_num < job_item->quantity; ++item_num) { - tasks[vector_id][bucket].emplace_back(id, job_item_idx); + tasks[vector_id][bucket].emplace_back(id, rev_jitem_index); DEBUG(status,out).print("added task: %s/%s/%d,%d; " "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), - bucket.c_str(), id, job_item_idx, tasks.size(), + bucket.c_str(), id, rev_jitem_index, tasks.size(), tasks[vector_id].size(), tasks[vector_id][bucket].size()); } } @@ -531,17 +532,19 @@ static void printStatus(color_ostream &out) { if (!bld || bld->jobs.size() != 1) continue; auto &job_items = bld->jobs[0]->job_items; - if (job_items.size() != pb.vector_ids.size()) + const size_t num_job_items = job_items.size(); + if (num_job_items != pb.vector_ids.size()) continue; ++bld_count; int job_item_idx = 0; for (auto &vec_ids : pb.vector_ids) { - auto &jitem = job_items[job_item_idx++]; + auto &jitem = job_items[num_job_items - (job_item_idx+1)]; int32_t quantity = jitem->quantity; if (quantity) { counts[get_desc_string(out, jitem, vec_ids)] += quantity; total += quantity; } + ++job_item_idx; } } @@ -692,11 +695,9 @@ static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtyp TRACE(status,out).print("entering hasFilter\n"); BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(out, key); - for (auto &filter : filters.getItemFilters()) { - if (!filter.isEmpty()) - return true; - } - return false; + if (index < 0 || filters.getItemFilters().size() <= (size_t)index) + return false; + return !filters.getItemFilters()[index].isEmpty(); } static void clearFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { @@ -972,10 +973,13 @@ static bool validate_pb(color_ostream &out, df::building *bld, int index) { static string getDescString(color_ostream &out, df::building *bld, int index) { DEBUG(status,out).print("entering getDescString\n"); if (!validate_pb(out, bld, index)) - return 0; + return "INVALID"; PlannedBuilding &pb = planned_buildings.at(bld->id); - auto &jitem = bld->jobs[0]->job_items[index]; + auto & jitems = bld->jobs[0]->job_items; + const size_t num_job_items = jitems.size(); + int rev_index = num_job_items - (index + 1); + auto &jitem = jitems[rev_index]; return int_to_string(jitem->quantity) + " " + get_desc_string(out, jitem, pb.vector_ids[index]); } @@ -985,7 +989,10 @@ static int getQueuePosition(color_ostream &out, df::building *bld, int index) { return 0; PlannedBuilding &pb = planned_buildings.at(bld->id); - auto &job_item = bld->jobs[0]->job_items[index]; + auto & jitems = bld->jobs[0]->job_items; + const size_t num_job_items = jitems.size(); + int rev_index = num_job_items - (index + 1); + auto &job_item = jitems[rev_index]; if (job_item->quantity <= 0) return 0; @@ -1001,7 +1008,7 @@ static int getQueuePosition(color_ostream &out, df::building *bld, int index) { int bucket_pos = -1; for (auto &task : buckets.at(bucket_id)) { ++bucket_pos; - if (bld->id == task.first && index == task.second) + if (bld->id == task.first && rev_index == task.second) break; } if (bucket_pos++ >= 0) @@ -1018,18 +1025,20 @@ static void makeTopPriority(color_ostream &out, df::building *bld) { PlannedBuilding &pb = planned_buildings.at(bld->id); auto &job_items = bld->jobs[0]->job_items; + const int num_job_items = (int)job_items.size(); - for (int index = 0; index < (int)job_items.size(); ++index) { + for (int index = 0; index < num_job_items; ++index) { + int rev_index = num_job_items - (index + 1); for (auto &vec_id : pb.vector_ids[index]) { if (!tasks.count(vec_id)) continue; auto &buckets = tasks.at(vec_id); - string bucket_id = getBucket(*job_items[index], pb, index); + string bucket_id = getBucket(*job_items[rev_index], pb, index); if (!buckets.count(bucket_id)) continue; auto &bucket = buckets.at(bucket_id); for (auto taskit = bucket.begin(); taskit != bucket.end(); ++taskit) { - if (bld->id == taskit->first && index == taskit->second) { + if (bld->id == taskit->first && rev_index == taskit->second) { auto task_bld_id = taskit->first; auto task_job_item_idx = taskit->second; bucket.erase(taskit); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index c08324e8a..41825c7da 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -190,6 +190,8 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, if (!itemPassesScreen(item)) continue; for (auto bucket_it = buckets.begin(); bucket_it != buckets.end(); ) { + TRACE(cycle,out).print("scanning bucket: %s/%s\n", + ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), bucket_it->first.c_str()); auto & task_queue = bucket_it->second; auto bld = popInvalidTasks(out, task_queue, planned_buildings); if (!bld) { @@ -203,11 +205,14 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto & task = task_queue.front(); auto id = task.first; auto job = bld->jobs[0]; + auto & jitems = job->job_items; + const size_t num_filters = jitems.size(); auto filter_idx = task.second; + const int rev_filter_idx = num_filters - (filter_idx+1); auto &pb = planned_buildings.at(id); if (isAccessibleFrom(out, item, job) - && matchesFilters(item, job->job_items[filter_idx], pb.heat_safety, - pb.item_filters[filter_idx]) + && matchesFilters(item, jitems[filter_idx], pb.heat_safety, + pb.item_filters[rev_filter_idx]) && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) { @@ -226,9 +231,9 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, // keep quantity aligned with the actual number of remaining // items so if buildingplan is turned off, the building will // be completed with the correct number of items. - --job->job_items[filter_idx]->quantity; + --jitems[filter_idx]->quantity; task_queue.pop_front(); - if (isJobReady(out, job->job_items)) { + if (isJobReady(out, jitems)) { finalizeBuilding(out, bld); planned_buildings.at(id).remove(out); } diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index e9639f281..8e66c3ed7 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -6,6 +6,7 @@ namespace DFHack { DBG_EXTERN(buildingplan, status); + DBG_EXTERN(buildingplan, cycle); } using std::set; @@ -153,11 +154,19 @@ bool ItemFilter::matches(DFHack::MaterialInfo &material) const { } bool ItemFilter::matches(df::item *item) const { - if (item->getQuality() < min_quality || item->getQuality() > max_quality) + if (item->getQuality() < min_quality || item->getQuality() > max_quality) { + TRACE(cycle).print("item outside of quality range (%d not between %d and %d)\n", + item->getQuality(), min_quality, max_quality); return false; + } - if (decorated_only && !item->hasImprovements()) + if (decorated_only && !item->hasImprovements()) { + TRACE(cycle).print("item needs improvements and doesn't have any\n"); return false; + } + + if (!mat_mask.whole) + return true; auto imattype = item->getActualMaterial(); auto imatindex = item->getActualMaterialIndex(); diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index e678c850f..aef55edc3 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -26,9 +26,10 @@ static vector> get_vector_ids(color_ostream &out, if (!bld || bld->jobs.size() != 1) return ret; - auto &job = bld->jobs[0]; - for (auto &jitem : job->job_items) { - ret.emplace_back(getVectorIds(out, jitem)); + auto &jitems = bld->jobs[0]->job_items; + int num_job_items = (int)jitems.size(); + for (int jitem_idx = num_job_items - 1; jitem_idx >= 0; --jitem_idx) { + ret.emplace_back(getVectorIds(out, jitems[jitem_idx])); } return ret; } From 96c7c952cf84ff55cf1dc539f2990f2dcfad04f2 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:16:34 +0000 Subject: [PATCH 0809/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 751065f42..13514f507 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 751065f42a55504197990f9458826c5f5604bcff +Subproject commit 13514f507b39dc256490d7b387331016e4e2b89f diff --git a/scripts b/scripts index b17ca9443..940e08208 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit b17ca9443e797431c2f3594556658beea4d7c378 +Subproject commit 940e0820877018ddf0e770f8378097fa34f98348 From 039171e01de81a6ebd47b676d61c8964ddeb91e2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 14 Mar 2023 21:36:41 -0700 Subject: [PATCH 0810/2222] support multiple upright spikes in a spike trap --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 91c1168dd..09ad4dd83 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings +-@ `buildingplan`: you can now attach multiple weapons to spike traps ## Misc Improvements -@ `buildingplan`: can now filter by clay materials diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index a45ab8ca0..0554752ca 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -42,6 +42,14 @@ local function is_weapon_trap() and uibs.building_subtype == df.trap_type.WeaponTrap end +local function is_spike_trap() + return uibs.building_type == df.building_type.Weapon +end + +local function is_weapon_or_spike_trap() + return is_weapon_trap() or is_spike_trap() +end + -- adjusted from CycleHotkeyLabel on the planner panel local weapon_quantity = 1 @@ -50,7 +58,7 @@ local function get_quantity(filter, hollow, placement_data) local flags = uibs.plate_info.flags return (flags.units and 1 or 0) + (flags.water and 1 or 0) + (flags.magma and 1 or 0) + (flags.track and 1 or 0) - elseif is_weapon_trap() and filter.vector_id == df.job_item_vector_id.ANY_WEAPON then + elseif (is_weapon_trap() and filter.vector_id == df.job_item_vector_id.ANY_WEAPON) or is_spike_trap() then return weapon_quantity end local quantity = filter.quantity or 1 @@ -334,7 +342,7 @@ function PlannerOverlay:init() key='CUSTOM_T', key_back='CUSTOM_SHIFT_T', label='Num weapons:', - visible=is_weapon_trap, + visible=is_weapon_or_spike_trap, options={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, on_change=function(val) weapon_quantity = val end, }, @@ -697,7 +705,7 @@ function PlannerOverlay:place_building(placement_data, chosen_items) local hollow = self.subviews.hollow:getOptionValue() local subtype = uibs.building_subtype local filters = get_cur_filters() - if is_pressure_plate() then + if is_pressure_plate() or is_spike_trap() then filters[1].quantity = get_quantity(filters[1]) elseif is_weapon_trap() then filters[2].quantity = get_quantity(filters[2]) From 4d8580d9e7284a670756933076cc8822d3e20ae3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 14 Mar 2023 21:55:43 -0700 Subject: [PATCH 0811/2222] remember "choose items" choice per building type --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 26 +++++++++++++++++++++ plugins/lua/buildingplan/planneroverlay.lua | 6 ++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 91c1168dd..af9dba787 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +-@ `buildingplan`: remember choice per building type for whether the player wants to choose specific items -@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings ## Misc Improvements diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index ba9cbf5fb..ef808c09b 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -55,6 +55,7 @@ static PersistentDataItem config; // for use in counting available materials for the UI static map> mat_cache; static unordered_map, BuildingTypeKeyHash> job_item_cache; +static unordered_map cur_choose_items; static unordered_map cur_heat_safety; static unordered_map cur_item_filters; // building id -> PlannedBuilding @@ -266,6 +267,7 @@ static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "reload_pens"); planned_buildings.clear(); tasks.clear(); + cur_choose_items.clear(); cur_heat_safety.clear(); cur_item_filters.clear(); for (auto &entry : job_item_cache ) { @@ -890,6 +892,28 @@ static int getMaterialFilter(lua_State *L) { return 1; } +static void setChooseItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, bool choose) { + DEBUG(status,out).print("entering setChooseItems\n"); + BuildingTypeKey key(type, subtype, custom); + cur_choose_items[key] = choose; + // no need to reset signal; no change to the state of any other UI element +} + +static int getChooseItems(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + DEBUG(status,*out).print( + "entering getChooseItems building_type=%d subtype=%d custom=%d\n", + type, subtype, custom); + BuildingTypeKey key(type, subtype, custom); + Lua::Push(L, cur_choose_items[key]); + return 1; +} + static void setHeatSafetyFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int heat) { DEBUG(status,out).print("entering setHeatSafetyFilter\n"); BuildingTypeKey key(type, subtype, custom); @@ -1061,6 +1085,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(countAvailableItems), DFHACK_LUA_FUNCTION(hasFilter), DFHACK_LUA_FUNCTION(clearFilter), + DFHACK_LUA_FUNCTION(setChooseItems), DFHACK_LUA_FUNCTION(setHeatSafetyFilter), DFHACK_LUA_FUNCTION(setQualityFilter), DFHACK_LUA_FUNCTION(getDescString), @@ -1076,6 +1101,7 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getMaterialMaskFilter), DFHACK_LUA_COMMAND(setMaterialFilter), DFHACK_LUA_COMMAND(getMaterialFilter), + DFHACK_LUA_COMMAND(getChooseItems), DFHACK_LUA_COMMAND(getHeatSafetyFilter), DFHACK_LUA_COMMAND(getQualityFilter), DFHACK_LUA_END diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index a45ab8ca0..0276f3a0d 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -401,6 +401,9 @@ function PlannerOverlay:init() end end end, + on_change=function(choose) + buildingplan.setChooseItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, choose) + end, }, widgets.CycleHotkeyLabel{ view_id='safety', @@ -541,7 +544,6 @@ function PlannerOverlay:onInput(keys) end self.selected = 1 self.subviews.hollow:setOption(false) - self.subviews.choose:setOption(false) self:reset() reset_counts_flag = true return false @@ -627,6 +629,8 @@ function PlannerOverlay:onRenderFrame(dc, rect) if reset_counts_flag then self:reset() + self.subviews.choose:setOption(require('plugins.buildingplan').getChooseItems( + uibs.building_type, uibs.building_subtype, uibs.custom_type)) self.subviews.safety:setOption(require('plugins.buildingplan').getHeatSafetyFilter( uibs.building_type, uibs.building_subtype, uibs.custom_type)) end From 306d300edfbb5595d0a14330fce29096d39566ea Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 14 Mar 2023 22:26:56 -0700 Subject: [PATCH 0812/2222] persist choose items choice per building type --- plugins/buildingplan/buildingplan.cpp | 7 +++---- plugins/buildingplan/buildingplan.h | 1 + plugins/buildingplan/defaultitemfilters.cpp | 9 ++++++++- plugins/buildingplan/defaultitemfilters.h | 3 +++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index ef808c09b..304b7001e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -55,7 +55,6 @@ static PersistentDataItem config; // for use in counting available materials for the UI static map> mat_cache; static unordered_map, BuildingTypeKeyHash> job_item_cache; -static unordered_map cur_choose_items; static unordered_map cur_heat_safety; static unordered_map cur_item_filters; // building id -> PlannedBuilding @@ -267,7 +266,6 @@ static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "reload_pens"); planned_buildings.clear(); tasks.clear(); - cur_choose_items.clear(); cur_heat_safety.clear(); cur_item_filters.clear(); for (auto &entry : job_item_cache ) { @@ -895,7 +893,8 @@ static int getMaterialFilter(lua_State *L) { static void setChooseItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, bool choose) { DEBUG(status,out).print("entering setChooseItems\n"); BuildingTypeKey key(type, subtype, custom); - cur_choose_items[key] = choose; + auto &filters = get_item_filters(out, key); + filters.setChooseItems(choose); // no need to reset signal; no change to the state of any other UI element } @@ -910,7 +909,7 @@ static int getChooseItems(lua_State *L) { "entering getChooseItems building_type=%d subtype=%d custom=%d\n", type, subtype, custom); BuildingTypeKey key(type, subtype, custom); - Lua::Push(L, cur_choose_items[key]); + Lua::Push(L, get_item_filters(*out, key).getChooseItems()); return 1; } diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 0d0d5b45f..6aa5e544f 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -27,6 +27,7 @@ enum FilterConfigValues { FILTER_CONFIG_TYPE = 0, FILTER_CONFIG_SUBTYPE = 1, FILTER_CONFIG_CUSTOM = 2, + FILTER_CONFIG_CHOOSE_ITEMS = 3, }; enum BuildingConfigValues { diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index 3c3b2f3a9..e866808f8 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -32,13 +32,14 @@ static int get_max_quality(const df::job_item *jitem) { } DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, const std::vector &jitems) - : key(key) { + : key(key), choose_items(false) { DEBUG(status,out).print("creating persistent data for filter key %d,%d,%d\n", std::get<0>(key), std::get<1>(key), std::get<2>(key)); filter_config = World::AddPersistentData(FILTER_CONFIG_KEY); set_config_val(filter_config, FILTER_CONFIG_TYPE, std::get<0>(key)); set_config_val(filter_config, FILTER_CONFIG_SUBTYPE, std::get<1>(key)); set_config_val(filter_config, FILTER_CONFIG_CUSTOM, std::get<2>(key)); + set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose_items); item_filters.resize(jitems.size()); for (size_t idx = 0; idx < jitems.size(); ++idx) { item_filters[idx].setMaxQuality(get_max_quality(jitems[idx]), true); @@ -48,6 +49,7 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &filter_config, const std::vector &jitems) : key(getKey(filter_config)), filter_config(filter_config) { + choose_items = get_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS); auto &serialized = filter_config.val(); DEBUG(status,out).print("deserializing item filters for key %d,%d,%d: %s\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); @@ -60,6 +62,11 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &f item_filters = filters; } +void DefaultItemFilters::setChooseItems(bool choose) { + choose_items = choose; + set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose); +} + void DefaultItemFilters::setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index) { if (index < 0 || item_filters.size() <= (size_t)index) { WARN(status,out).print("invalid index for filter key %d,%d,%d: %d\n", diff --git a/plugins/buildingplan/defaultitemfilters.h b/plugins/buildingplan/defaultitemfilters.h index 4d1d5cbd2..37ebdcaae 100644 --- a/plugins/buildingplan/defaultitemfilters.h +++ b/plugins/buildingplan/defaultitemfilters.h @@ -14,11 +14,14 @@ public: DefaultItemFilters(DFHack::color_ostream &out, BuildingTypeKey key, const std::vector &jitems); DefaultItemFilters(DFHack::color_ostream &out, DFHack::PersistentDataItem &filter_config, const std::vector &jitems); + void setChooseItems(bool choose); void setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index); + bool getChooseItems() const { return choose_items; } const std::vector & getItemFilters() const { return item_filters; } private: DFHack::PersistentDataItem filter_config; + bool choose_items; std::vector item_filters; }; From a578614cdd3c91d2a4f917d1e6f770ddcd886d1e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 14 Mar 2023 22:28:27 -0700 Subject: [PATCH 0813/2222] update docs --- docs/plugins/buildingplan.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 8b1256b53..68eb18f69 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -151,7 +151,9 @@ or hit :kbd:`i` before placing the building. When you click to place the building, a dialog will come up that allows you choose which items to use. The list is sorted by most recently used materials for that building type by default, but you can change to sort by name or by available quantity by -clicking on the "Sort by" selector or hitting :kbd:`R`. +clicking on the "Sort by" selector or hitting :kbd:`R`. The configuration for +whether you would like to choose specific items is saved per building type and +will be restored when you plan more of that building type. You can select the maximum quantity of a specified item by clicking on the item name or selecting it with the arrow keys and hitting :kbd:`Enter`. You can From ba3911718bcdd85cb44a40457577b00375e38486 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 15 Mar 2023 07:14:31 +0000 Subject: [PATCH 0814/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 940e08208..8bf468565 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 940e0820877018ddf0e770f8378097fa34f98348 +Subproject commit 8bf4685655e9be3646796cf203f40045f3e263c3 From c47f0687696778cb63d4289d0f0e2f5112e3fc22 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 00:28:18 -0700 Subject: [PATCH 0815/2222] add Lua::Push for sets --- library/include/LuaTools.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 689d82260..b19fc31de 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -31,6 +31,7 @@ distribution. #include #include #include +#include #include "df/interfacest.h" @@ -364,6 +365,26 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos); DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos); + template + void Push(lua_State *L, const std::set &pset) { + lua_createtable(L, 0, pset.size()); + for (auto &entry : pset) { + Lua::Push(L, entry); + Lua::Push(L, true); + lua_settable(L, -3); + } + } + + template + void Push(lua_State *L, const std::unordered_set &pset) { + lua_createtable(L, 0, pset.size()); + for (auto &entry : pset) { + Lua::Push(L, entry); + Lua::Push(L, true); + lua_settable(L, -3); + } + } + template void Push(lua_State *L, const std::map &pmap) { lua_createtable(L, 0, pmap.size()); From 4be5ca4e816fa41694b3085f725e87fb1aa6d41e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 00:29:27 -0700 Subject: [PATCH 0816/2222] filter by whether a slab is engraved this actually adds an entirely new "specials" filter system that can be extended later for other types --- docs/changelog.txt | 5 ++- plugins/buildingplan/buildingplan.cpp | 40 +++++++++++++++++--- plugins/buildingplan/buildingplan.h | 3 +- plugins/buildingplan/buildingplan_cycle.cpp | 9 ++++- plugins/buildingplan/defaultitemfilters.cpp | 31 +++++++++++++-- plugins/buildingplan/defaultitemfilters.h | 3 ++ plugins/buildingplan/plannedbuilding.cpp | 25 ++++++++++-- plugins/buildingplan/plannedbuilding.h | 5 ++- plugins/lua/buildingplan/filterselection.lua | 9 +++++ plugins/lua/buildingplan/planneroverlay.lua | 31 +++++++++++---- 10 files changed, 135 insertions(+), 26 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4a32c9b57..e4588e328 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,12 +36,13 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes --@ `buildingplan`: remember choice per building type for whether the player wants to choose specific items -@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings --@ `buildingplan`: you can now attach multiple weapons to spike traps ## Misc Improvements -@ `buildingplan`: can now filter by clay materials +-@ `buildingplan`: remember choice per building type for whether the player wants to choose specific items +-@ `buildingplan`: you can now attach multiple weapons to spike traps +-@ `buildingplan`: can now filter by whether a slab is engraved - `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory - `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` - player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 304b7001e..c2b4431d9 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -394,7 +394,7 @@ static string getBucket(const df::job_item & ji, const PlannedBuilding & pb, int ser << "Hc"; size_t num_materials = item_filter.getMaterials().size(); - if (num_materials == 0 || num_materials >= 9 || item_filter.getMaterialMask().whole) + if (num_materials == 0 || num_materials >= 9 || !item_filter.getMaterialMask().whole) ser << "M9"; else ser << "M" << num_materials; @@ -412,6 +412,9 @@ static string getBucket(const df::job_item & ji, const PlannedBuilding & pb, int ser << ':' << item_filter.serialize(); + for (auto &special : pb.specials) + ser << ':' << special; + return ser.str(); } @@ -596,7 +599,7 @@ static bool addPlannedBuilding(color_ostream &out, df::building *bld) { bld->getCustomType())) return false; BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); - PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key).getItemFilters()); + PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key)); return registerPlannedBuilding(out, pb); } @@ -621,7 +624,9 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ auto &job_items = get_job_items(out, key); if (index < 0 || job_items.size() <= (size_t)index) return 0; - auto &item_filters = get_item_filters(out, key).getItemFilters(); + auto &item_filters = get_item_filters(out, key); + auto &filters = item_filters.getItemFilters(); + auto &specials = item_filters.getSpecials(); auto &jitem = job_items[index]; auto vector_ids = getVectorIds(out, jitem); @@ -630,13 +635,13 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ for (auto vector_id : vector_ids) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { - ItemFilter filter = item_filters[index]; + ItemFilter filter = filters[index]; if (counts) { // don't filter by material; we want counts for all materials filter.setMaterialMask(0); filter.setMaterials(set()); } - if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter)) { + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter, specials)) { if (item_ids) item_ids->emplace_back(item->id); if (counts) { @@ -939,6 +944,29 @@ static int getHeatSafetyFilter(lua_State *L) { return 1; } +static void setSpecial(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, string special, bool val) { + DEBUG(status,out).print("entering setSpecial\n"); + BuildingTypeKey key(type, subtype, custom); + auto &filters = get_item_filters(out, key); + filters.setSpecial(special, val); + call_buildingplan_lua(&out, "signal_reset"); +} + +static int getSpecials(lua_State *L) { + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + df::building_type type = (df::building_type)luaL_checkint(L, 1); + int16_t subtype = luaL_checkint(L, 2); + int32_t custom = luaL_checkint(L, 3); + DEBUG(status,*out).print( + "entering getSpecials building_type=%d subtype=%d custom=%d\n", + type, subtype, custom); + BuildingTypeKey key(type, subtype, custom); + Lua::Push(L, get_item_filters(*out, key).getSpecials()); + return 1; +} + static void setQualityFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index, int decorated, int min_quality, int max_quality) { DEBUG(status,out).print("entering setQualityFilter\n"); @@ -1086,6 +1114,7 @@ DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(clearFilter), DFHACK_LUA_FUNCTION(setChooseItems), DFHACK_LUA_FUNCTION(setHeatSafetyFilter), + DFHACK_LUA_FUNCTION(setSpecial), DFHACK_LUA_FUNCTION(setQualityFilter), DFHACK_LUA_FUNCTION(getDescString), DFHACK_LUA_FUNCTION(getQueuePosition), @@ -1102,6 +1131,7 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(getMaterialFilter), DFHACK_LUA_COMMAND(getChooseItems), DFHACK_LUA_COMMAND(getHeatSafetyFilter), + DFHACK_LUA_COMMAND(getSpecials), DFHACK_LUA_COMMAND(getQualityFilter), DFHACK_LUA_END }; diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 6aa5e544f..495602b0b 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -9,6 +9,7 @@ #include "df/job_item_vector_id.h" #include +#include typedef std::deque> Bucket; typedef std::map> Tasks; @@ -49,6 +50,6 @@ void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item); bool itemPassesScreen(df::item * item); df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat); -bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter); +bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter, const std::set &special); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 41825c7da..803f1f130 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -10,6 +10,7 @@ #include "df/building_design.h" #include "df/item.h" +#include "df/item_slabst.h" #include "df/job.h" #include "df/map_block.h" #include "df/world.h" @@ -58,7 +59,7 @@ df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety h return jitem; } -bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter) { +bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter, const std::set &specials) { // check the properties that are not checked by Job::isSuitableItem() if (job_item->item_type > -1 && job_item->item_type != item->getType()) return false; @@ -77,6 +78,10 @@ bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety h && !item->hasToolUse(job_item->has_tool_use)) return false; + if (item->getType() == df::item_type::SLAB && specials.count("engraved") + && static_cast(item)->engraving_type != df::slab_engraving_type::Memorial) + return false; + df::job_item jitem = getJobItemWithHeatSafety(job_item, heat); return Job::isSuitableItem( @@ -212,7 +217,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto &pb = planned_buildings.at(id); if (isAccessibleFrom(out, item, job) && matchesFilters(item, jitems[filter_idx], pb.heat_safety, - pb.item_filters[rev_filter_idx]) + pb.item_filters[rev_filter_idx], pb.specials) && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) { diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index e866808f8..8af74eff2 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -31,6 +31,13 @@ static int get_max_quality(const df::job_item *jitem) { return df::item_quality::Masterful; } +static string serialize(const std::vector &item_filters, const std::set &specials) { + std::ostringstream out; + out << serialize_item_filters(item_filters); + out << "|" << join_strings(",", specials); + return out.str(); +} + DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, const std::vector &jitems) : key(key), choose_items(false) { DEBUG(status,out).print("creating persistent data for filter key %d,%d,%d\n", @@ -44,22 +51,30 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, for (size_t idx = 0; idx < jitems.size(); ++idx) { item_filters[idx].setMaxQuality(get_max_quality(jitems[idx]), true); } - filter_config.val() = serialize_item_filters(item_filters); + filter_config.val() = serialize(item_filters, specials); } DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &filter_config, const std::vector &jitems) : key(getKey(filter_config)), filter_config(filter_config) { choose_items = get_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS); auto &serialized = filter_config.val(); - DEBUG(status,out).print("deserializing item filters for key %d,%d,%d: %s\n", + DEBUG(status,out).print("deserializing default item filters for key %d,%d,%d: %s\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); - std::vector filters = deserialize_item_filters(out, serialized); + std::vector elems; + split_string(&elems, serialized, "|"); + std::vector filters = deserialize_item_filters(out, elems[0]); if (filters.size() != jitems.size()) { WARN(status,out).print("ignoring invalid filters_str for key %d,%d,%d: '%s'\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); item_filters.resize(jitems.size()); } else item_filters = filters; + if (elems.size() > 1) { + vector specs; + split_string(&specs, elems[1], ","); + for (auto & special : specs) + specials.emplace(special); + } } void DefaultItemFilters::setChooseItems(bool choose) { @@ -67,6 +82,14 @@ void DefaultItemFilters::setChooseItems(bool choose) { set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose); } +void DefaultItemFilters::setSpecial(const std::string &special, bool val) { + if (val) + specials.emplace(special); + else + specials.erase(special); + filter_config.val() = serialize(item_filters, specials); +} + void DefaultItemFilters::setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index) { if (index < 0 || item_filters.size() <= (size_t)index) { WARN(status,out).print("invalid index for filter key %d,%d,%d: %d\n", @@ -75,7 +98,7 @@ void DefaultItemFilters::setItemFilter(DFHack::color_ostream &out, const ItemFil } item_filters[index] = filter; - filter_config.val() = serialize_item_filters(item_filters); + filter_config.val() = serialize(item_filters, specials); DEBUG(status,out).print("updated item filter and persisted for key %d,%d,%d: %s\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), filter_config.val().c_str()); } diff --git a/plugins/buildingplan/defaultitemfilters.h b/plugins/buildingplan/defaultitemfilters.h index 37ebdcaae..d7ed12a7b 100644 --- a/plugins/buildingplan/defaultitemfilters.h +++ b/plugins/buildingplan/defaultitemfilters.h @@ -16,12 +16,15 @@ public: void setChooseItems(bool choose); void setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index); + void setSpecial(const std::string &special, bool val); bool getChooseItems() const { return choose_items; } const std::vector & getItemFilters() const { return item_filters; } + const std::set & getSpecials() const { return specials; } private: DFHack::PersistentDataItem filter_config; bool choose_items; std::vector item_filters; + std::set specials; }; diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index aef55edc3..60e99e073 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -69,19 +69,35 @@ static vector get_item_filters(color_ostream &out, PersistentDataIte return deserialize_item_filters(out, rawstrs[1]); } -static string serialize(const vector> &vector_ids, const vector &item_filters) { +static set get_specials(color_ostream &out, PersistentDataItem &bld_config) { + vector rawstrs; + split_string(&rawstrs, bld_config.val(), "|"); + set ret; + if (rawstrs.size() < 3) + return ret; + vector specials; + split_string(&specials, rawstrs[2], ","); + for (auto & special : specials) + ret.emplace(special); + return ret; +} + +static string serialize(const vector> &vector_ids, const DefaultItemFilters &item_filters) { vector joined; for (auto &vec_list : vector_ids) { joined.emplace_back(join_strings(",", vec_list)); } std::ostringstream out; - out << join_strings(";", joined) << "|" << serialize_item_filters(item_filters); + out << join_strings(";", joined); + out << "|" << serialize_item_filters(item_filters.getItemFilters()); + out << "|" << join_strings(",", item_filters.getSpecials()); return out.str(); } -PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafety heat, const vector &item_filters) +PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafety heat, const DefaultItemFilters &item_filters) : id(bld->id), vector_ids(get_vector_ids(out, id)), heat_safety(heat), - item_filters(item_filters) { + item_filters(item_filters.getItemFilters()), + specials(item_filters.getSpecials()) { DEBUG(status,out).print("creating persistent data for building %d\n", id); bld_config = World::AddPersistentData(BLD_CONFIG_KEY); set_config_val(bld_config, BLD_CONFIG_ID, id); @@ -95,6 +111,7 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_con vector_ids(deserialize_vector_ids(out, bld_config)), heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), item_filters(get_item_filters(out, bld_config)), + specials(get_specials(out, bld_config)), bld_config(bld_config) { } // Ensure the building still exists and is in a valid state. It can disappear diff --git a/plugins/buildingplan/plannedbuilding.h b/plugins/buildingplan/plannedbuilding.h index 59dc24a79..703d4246d 100644 --- a/plugins/buildingplan/plannedbuilding.h +++ b/plugins/buildingplan/plannedbuilding.h @@ -1,6 +1,7 @@ #pragma once #include "buildingplan.h" +#include "defaultitemfilters.h" #include "itemfilter.h" #include "Core.h" @@ -21,7 +22,9 @@ public: const std::vector item_filters; - PlannedBuilding(DFHack::color_ostream &out, df::building *bld, HeatSafety heat, const std::vector &item_filters); + const std::set specials; + + PlannedBuilding(DFHack::color_ostream &out, df::building *bld, HeatSafety heat, const DefaultItemFilters &item_filters); PlannedBuilding(DFHack::color_ostream &out, DFHack::PersistentDataItem &bld_config); void remove(DFHack::color_ostream &out); diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index e923c4b5b..84b5c46ea 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -432,6 +432,15 @@ function QualityAndMaterialsPage:refresh() else summary = 'Any ' .. summary end + local specials = buildingplan.getSpecials(uibs.building_type, uibs.building_subtype, uibs.custom_type) + if next(specials) then + local specials_list = {} + for special in pairs(specials) do + table.insert(specials_list, special) + end + summary = summary .. ' [' .. table.concat(specials_list, ', ') .. ']' + end + local quality = buildingplan.getQualityFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.index-1) subviews.decorated:setOption(quality.decorated ~= 0) subviews.min_quality:setOption(quality.min_quality) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index d4a94a01d..df49fe287 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -81,14 +81,18 @@ local function cur_building_has_no_area() return filters and filters[1] and (not filters[1].quantity or filters[1].quantity > 0) end +local function is_construction() + return uibs.building_type == df.building_type.Construction +end + local function is_plannable() return get_cur_filters() and - not (uibs.building_type == df.building_type.Construction - and uibs.building_subtype == df.construction_type.TrackNSEW) + not (is_construction() and + uibs.building_subtype == df.construction_type.TrackNSEW) end -local function is_construction() - return uibs.building_type == df.building_type.Construction +local function is_slab() + return uibs.building_type == df.building_type.Slab end local function is_stairs() @@ -346,6 +350,16 @@ function PlannerOverlay:init() options={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, on_change=function(val) weapon_quantity = val end, }, + widgets.ToggleHotkeyLabel { + view_id='engraved', + frame={t=5, l=4}, + key='CUSTOM_T', + label='Engraved only:', + visible=is_slab, + on_change=function(val) + buildingplan.setSpecial(uibs.building_type, uibs.building_subtype, uibs.custom_type, 'engraved', val) + end, + }, widgets.Label{ frame={b=3, l=17}, text={ @@ -637,10 +651,13 @@ function PlannerOverlay:onRenderFrame(dc, rect) if reset_counts_flag then self:reset() - self.subviews.choose:setOption(require('plugins.buildingplan').getChooseItems( + local buildingplan = require('plugins.buildingplan') + self.subviews.engraved:setOption(buildingplan.getSpecials( + uibs.building_type, uibs.building_subtype, uibs.custom_type).engraved or false) + self.subviews.choose:setOption(buildingplan.getChooseItems( + uibs.building_type, uibs.building_subtype, uibs.custom_type)) + self.subviews.safety:setOption(buildingplan.getHeatSafetyFilter( uibs.building_type, uibs.building_subtype, uibs.custom_type)) - self.subviews.safety:setOption(require('plugins.buildingplan').getHeatSafetyFilter( - uibs.building_type, uibs.building_subtype, uibs.custom_type)) end local selection_pos = self.saved_selection_pos or uibs.selection_pos From 208a3e4ae8b94730c2f4e506aae85fad84b76e21 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 01:19:34 -0700 Subject: [PATCH 0817/2222] add minimize/restore button --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 25 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index e4588e328..0b313c951 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `buildingplan`: remember choice per building type for whether the player wants to choose specific items -@ `buildingplan`: you can now attach multiple weapons to spike traps -@ `buildingplan`: can now filter by whether a slab is engraved +-@ `buildingplan`: add "minimize" button to temporarily get the planner overlay out of the way if you would rather use the vanilla UI for placing the current building - `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory - `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` - player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index df49fe287..255611178 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -260,12 +260,17 @@ PlannerOverlay.ATTRS{ function PlannerOverlay:init() self.selected = 1 + self.minimized = false + + local function is_minimized() return self.minimized end + local function not_is_minimized() return not self.minimized end local main_panel = widgets.Panel{ view_id='main', frame={t=0, l=0, r=0, h=14}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, + visible=function() return not self.minimized end, } local function make_is_selected_fn(idx) @@ -451,6 +456,7 @@ function PlannerOverlay:init() frame={t=14, l=0, r=0}, frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, + visible=function() return not self.minimized end, } error_panel:addviews{ @@ -565,11 +571,16 @@ function PlannerOverlay:onInput(keys) return true end self.selected = 1 + self.minimized = false self.subviews.hollow:setOption(false) self:reset() reset_counts_flag = true return false end + if keys.CUSTOM_ALT_M then + self.minimized = not self.minimized + return true + end if PlannerOverlay.super.onInput(self, keys) then return true end @@ -579,10 +590,15 @@ function PlannerOverlay:onInput(keys) detect_rect.height = self.subviews.main.frame_rect.height + self.subviews.errors.frame_rect.height detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 - if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) - or self.subviews.errors:getMousePos() then - return true + local x, y = self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) + if x or self.subviews.errors:getMousePos() then + if x and x == detect_rect.width-2 and y == 0 then + self.minimized = not self.minimized + return true + end + return not self.minimized end + if self.minimized then return false end if not is_construction() and #uibs.errors > 0 then return true end if dfhack.gui.getMousePos() then if is_choosing_area() or cur_building_has_no_area() then @@ -642,6 +658,9 @@ function PlannerOverlay:render(dc) if not is_plannable() then return end self.subviews.errors:updateLayout() PlannerOverlay.super.render(self, dc) + -- render "minimize" button + dc:seek(self.frame_rect.x2-1, self.frame_rect.y1) + dc:char(string.char(self.minimized and 31 or 30), COLOR_RED) end local ONE_BY_ONE = xy2pos(1, 1) From 0e1909bad1db3a7f6ad9c7c36242c5f2dc0abee4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 01:42:06 -0700 Subject: [PATCH 0818/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0b313c951..e32538566 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings +-@ `buildingplan`: buildings with different material filters will no longer get "stuck" if one of the filters currently matches no items ## Misc Improvements -@ `buildingplan`: can now filter by clay materials From a4365f47f5ec6ca266ce51e3cc0b33826762befe Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 13:40:08 -0700 Subject: [PATCH 0819/2222] don't pick up empty specials --- plugins/buildingplan/defaultitemfilters.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index 8af74eff2..fc7dd9f56 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -72,8 +72,10 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &f if (elems.size() > 1) { vector specs; split_string(&specs, elems[1], ","); - for (auto & special : specs) - specials.emplace(special); + for (auto & special : specs) { + if (special.size()) + specials.emplace(special); + } } } From 73e65f2d940f8e3933b93ff17c4761bd295974ee Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 13:40:34 -0700 Subject: [PATCH 0820/2222] use proper widgets for the minimize button --- plugins/lua/buildingplan/planneroverlay.lua | 30 +++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 255611178..9b3383443 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -262,9 +262,6 @@ function PlannerOverlay:init() self.selected = 1 self.minimized = false - local function is_minimized() return self.minimized end - local function not_is_minimized() return not self.minimized end - local main_panel = widgets.Panel{ view_id='main', frame={t=0, l=0, r=0, h=14}, @@ -273,6 +270,17 @@ function PlannerOverlay:init() visible=function() return not self.minimized end, } + local minimized_panel = widgets.Label{ + frame={t=0, r=1, w=1, h=1}, + text={ + { + text=function() return string.char(self.minimized and 31 or 30) end, + pen=COLOR_RED, + }, + }, + on_click=function() self.minimized = not self.minimized end, + } + local function make_is_selected_fn(idx) return function() return self.selected == idx end end @@ -476,6 +484,7 @@ function PlannerOverlay:init() self:addviews{ main_panel, + minimized_panel, error_panel, } end @@ -582,7 +591,7 @@ function PlannerOverlay:onInput(keys) return true end if PlannerOverlay.super.onInput(self, keys) then - return true + return not self.minimized end if keys._MOUSE_L_DOWN then if is_over_options_panel() then return false end @@ -590,13 +599,9 @@ function PlannerOverlay:onInput(keys) detect_rect.height = self.subviews.main.frame_rect.height + self.subviews.errors.frame_rect.height detect_rect.y2 = detect_rect.y1 + detect_rect.height - 1 - local x, y = self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) - if x or self.subviews.errors:getMousePos() then - if x and x == detect_rect.width-2 and y == 0 then - self.minimized = not self.minimized - return true - end - return not self.minimized + if self.subviews.main:getMousePos(gui.ViewRect{rect=detect_rect}) + or self.subviews.errors:getMousePos() then + return true end if self.minimized then return false end if not is_construction() and #uibs.errors > 0 then return true end @@ -658,9 +663,6 @@ function PlannerOverlay:render(dc) if not is_plannable() then return end self.subviews.errors:updateLayout() PlannerOverlay.super.render(self, dc) - -- render "minimize" button - dc:seek(self.frame_rect.x2-1, self.frame_rect.y1) - dc:char(string.char(self.minimized and 31 or 30), COLOR_RED) end local ONE_BY_ONE = xy2pos(1, 1) From b0f9ad644958485b16ccf2add6151e17ef13dbca Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 14:02:59 -0700 Subject: [PATCH 0821/2222] add buildingplan reset for resetting all filters --- docs/changelog.txt | 1 + docs/plugins/buildingplan.rst | 5 +++++ plugins/buildingplan/buildingplan.cpp | 18 ++++++++++++++---- plugins/lua/buildingplan.lua | 2 ++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index e32538566..c98d727f6 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,6 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `buildingplan`: you can now attach multiple weapons to spike traps -@ `buildingplan`: can now filter by whether a slab is engraved -@ `buildingplan`: add "minimize" button to temporarily get the planner overlay out of the way if you would rather use the vanilla UI for placing the current building +-@ `buildingplan`: add ``buildingplan reset`` command for resetting all filters to defaults - `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory - `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` - player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 68eb18f69..378d3b140 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -60,6 +60,7 @@ Usage buildingplan [status] buildingplan set (true|false) + buildingplan reset Examples -------- @@ -72,6 +73,10 @@ Examples When finding items to satisfy "building materials" requirements, don't select boulders. Use blocks or logs (if enabled) instead. +``buildingplan reset`` + Reset all settings and filters to their defaults. This command does not affect + existing planned buildings. + .. _buildingplan-settings: Global settings diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index c2b4431d9..d28a0aa31 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -261,13 +261,15 @@ static void validate_config(color_ostream &out, bool verbose = false) { set_config_bool(config, CONFIG_BARS, false); } -static void clear_state(color_ostream &out) { +static void reset_filters(color_ostream &out) { + cur_heat_safety.clear(); + cur_item_filters.clear(); call_buildingplan_lua(&out, "signal_reset"); - call_buildingplan_lua(&out, "reload_pens"); +} + +static void clear_state(color_ostream &out) { planned_buildings.clear(); tasks.clear(); - cur_heat_safety.clear(); - cur_item_filters.clear(); for (auto &entry : job_item_cache ) { for (auto &jitem : entry.second) { delete jitem; @@ -275,6 +277,8 @@ static void clear_state(color_ostream &out) { } job_item_cache.clear(); mat_cache.clear(); + reset_filters(out); + call_buildingplan_lua(&out, "reload_pens"); } DFhackCExport command_result plugin_load_data (color_ostream &out) { @@ -582,6 +586,11 @@ static bool setSetting(color_ostream &out, string name, bool value) { return true; } +static void resetFilters(color_ostream &out) { + DEBUG(status,out).print("entering resetFilters\n"); + reset_filters(out); +} + static bool isPlannableBuilding(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom) { DEBUG(status,out).print("entering isPlannableBuilding\n"); return get_num_filters(out, BuildingTypeKey(type, subtype, custom)) >= 1; @@ -1104,6 +1113,7 @@ static void makeTopPriority(color_ostream &out, df::building *bld) { DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(printStatus), DFHACK_LUA_FUNCTION(setSetting), + DFHACK_LUA_FUNCTION(resetFilters), DFHACK_LUA_FUNCTION(isPlannableBuilding), DFHACK_LUA_FUNCTION(isPlannedBuilding), DFHACK_LUA_FUNCTION(addPlannedBuilding), diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index d606101be..3492e2ccd 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -42,6 +42,8 @@ function parse_commandline(...) printStatus() elseif command == 'set' and positionals then setSetting(positionals[1], positionals[2] == 'true') + elseif command == 'reset' then + resetFilters() else return false end From d0752764be3783442b297626ed215d0df89e4a64 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 16 Mar 2023 07:15:12 +0000 Subject: [PATCH 0822/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 8bf468565..a6f4b8713 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 8bf4685655e9be3646796cf203f40045f3e263c3 +Subproject commit a6f4b87133e4a9f7eefdbd7f6f52d082d1ddbe61 From e95b6805d0f2c07416ab7277c9a05a2e9388ce0e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Mar 2023 19:55:12 -0700 Subject: [PATCH 0823/2222] comment out heat safety feature --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 7 ++++--- plugins/buildingplan/plannedbuilding.cpp | 3 ++- plugins/lua/buildingplan/planneroverlay.lua | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c98d727f6..5a668f344 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,6 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua ## Removed +-@ `buildingplan`: "heat safety" setting is temporarily removed while we investigate incorrect item matching # 50.07-alpha3 diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index d28a0aa31..b472c7390 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -196,8 +196,9 @@ static void load_material_cache() { } static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { - if (cur_heat_safety.count(key)) - return cur_heat_safety.at(key); + // comment out until we can get heat safety working as intended + // if (cur_heat_safety.count(key)) + // return cur_heat_safety.at(key); return HEAT_SAFETY_ANY; } @@ -486,7 +487,7 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { for (int item_num = 0; item_num < job_item->quantity; ++item_num) { tasks[vector_id][bucket].emplace_back(id, rev_jitem_index); DEBUG(status,out).print("added task: %s/%s/%d,%d; " - "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket", + "%zu vector(s), %zu filter bucket(s), %zu task(s) in bucket\n", ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), bucket.c_str(), id, rev_jitem_index, tasks.size(), tasks[vector_id].size(), tasks[vector_id][bucket].size()); diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index 60e99e073..2be2cf26f 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -109,7 +109,8 @@ PlannedBuilding::PlannedBuilding(color_ostream &out, df::building *bld, HeatSafe PlannedBuilding::PlannedBuilding(color_ostream &out, PersistentDataItem &bld_config) : id(get_config_val(bld_config, BLD_CONFIG_ID)), vector_ids(deserialize_vector_ids(out, bld_config)), - heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), + //heat_safety((HeatSafety)get_config_val(bld_config, BLD_CONFIG_HEAT)), // until this works + heat_safety(HEAT_SAFETY_ANY), item_filters(get_item_filters(out, bld_config)), specials(get_specials(out, bld_config)), bld_config(bld_config) { } diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 9b3383443..2f0bb7825 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -454,6 +454,7 @@ function PlannerOverlay:init() on_change=function(heat) buildingplan.setHeatSafetyFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, heat) end, + visible=false, -- until we can make this work the way it's intended }, }, }, From 52dec0114f890d03e6ba7e92822208bdebac75ce Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 16 Mar 2023 23:45:22 -0700 Subject: [PATCH 0824/2222] properly count required bars/cloth and don't output confusing total quantities --- docs/changelog.txt | 2 ++ plugins/showmood.cpp | 24 ++++++------------------ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5a668f344..b0ebc87f1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings -@ `buildingplan`: buildings with different material filters will no longer get "stuck" if one of the filters currently matches no items +- `showmood` properly count required number of bars and cloth when they aren't the main item for the strange mood ## Misc Improvements -@ `buildingplan`: can now filter by clay materials @@ -49,6 +50,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory - `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` - player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. +-@ `showmood`: clarify how many bars and/or cloth items are actually needed for the mood ## Documentation diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 79a733b7b..3ef6a6fb2 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -275,29 +275,17 @@ command_result df_showmood (color_ostream &out, vector & parameters) // count how many items of this type the crafter already collected { int count_got = 0; - int dimension_got = 0; int divisor = 1; - bool has_dims = false; - if (item->item_type == item_type::BAR) { + if (item->item_type == item_type::BAR) divisor = 150; - has_dims = true; - } else if (item->item_type == item_type::CLOTH) { + else if (item->item_type == item_type::CLOTH) divisor = 10000; - has_dims = true; - } - for (size_t j = 0; j < job->items.size(); j++) - { - if(job->items[j]->job_item_idx == int32_t(i)) - { - if (has_dims) - dimension_got += job->items[j]->item->getTotalDimension(); + for (size_t j = 0; j < job->items.size(); j++) { + if (job->items[j]->job_item_idx == int32_t(i)) count_got += 1; - } } - out.print(", got %i of %i", count_got, item->quantity/divisor); - if (has_dims) - out.print(" (%i of %i sub-units)", dimension_got, item->quantity); - out.print("\n"); + out.print(", got %i of %i\n", count_got, + item->quantity < divisor ? item->quantity : item->quantity/divisor); } } } From 41617556b57da89c10368a1ab0657df1fd2a8586 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 17 Mar 2023 07:14:45 +0000 Subject: [PATCH 0825/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index a6f4b8713..21aceeb24 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a6f4b87133e4a9f7eefdbd7f6f52d082d1ddbe61 +Subproject commit 21aceeb241df6ddfb6b276bb64ff6512842c2115 From a9e853188cc986a854ecc325d05b010127511398 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 08:33:55 -0700 Subject: [PATCH 0826/2222] better minimize widget for the planner panel --- plugins/lua/buildingplan/pens.lua | 5 ++++ plugins/lua/buildingplan/planneroverlay.lua | 26 +++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index d2198706f..973bb7bc6 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -4,6 +4,7 @@ GOOD_TILE_PEN, BAD_TILE_PEN = nil, nil VERT_TOP_PEN, VERT_MID_PEN, VERT_BOT_PEN = nil, nil, nil BUTTON_START_PEN, BUTTON_END_PEN = nil, nil SELECTED_ITEM_PEN = nil +MINIMIZED_LEFT_PEN, MINIMIZED_RIGHT_PEN = nil, nil local to_pen = dfhack.pen.parse @@ -25,6 +26,10 @@ function reload_pens() BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} BUTTON_END_PEN = to_pen{tile=tp(cp_texpos, 15), ch=']', fg=COLOR_YELLOW} SELECTED_ITEM_PEN = to_pen{tile=tp(cp_texpos, 9), ch=string.char(251), fg=COLOR_YELLOW} + + local wb_texpos = dfhack.textures.getWindowBordersTexposStart() + MINIMIZED_LEFT_PEN = to_pen{tile=tp(wb_texpos, 0), ch=199, fg=COLOR_WHITE} + MINIMIZED_RIGHT_PEN = to_pen{tile=tp(wb_texpos, 2), ch=182, fg=COLOR_WHITE} end reload_pens() diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 2f0bb7825..edc258f00 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -270,15 +270,27 @@ function PlannerOverlay:init() visible=function() return not self.minimized end, } - local minimized_panel = widgets.Label{ - frame={t=0, r=1, w=1, h=1}, - text={ - { - text=function() return string.char(self.minimized and 31 or 30) end, - pen=COLOR_RED, + local minimized_panel = widgets.Panel{ + frame={t=0, r=0, w=4, h=1}, + subviews={ + widgets.Label{ + frame={t=0, l=0, w=1, h=1}, + text={{tile=pens.MINIMIZED_LEFT_PEN}}, + visible=function() return self.minimized end, + }, + widgets.Label{ + frame={t=0, l=1, w=2, h=1}, + text=string.char(31)..string.char(30), + text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, + text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, + on_click=function() self.minimized = not self.minimized end, + }, + widgets.Label{ + frame={t=0, r=0, w=1, h=1}, + text={{tile=pens.MINIMIZED_RIGHT_PEN}}, + visible=function() return self.minimized end, }, }, - on_click=function() self.minimized = not self.minimized end, } local function make_is_selected_fn(idx) From 79dd5a313f4f23418a835ab7a43ab6cbb49d5553 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 09:55:04 -0700 Subject: [PATCH 0827/2222] rename Build button to Confirm and update docs --- docs/changelog.txt | 1 + docs/plugins/buildingplan.rst | 23 +++++++++++----------- plugins/lua/buildingplan/itemselection.lua | 12 +++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b0ebc87f1..28f3ab904 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `buildingplan`: can now filter by whether a slab is engraved -@ `buildingplan`: add "minimize" button to temporarily get the planner overlay out of the way if you would rather use the vanilla UI for placing the current building -@ `buildingplan`: add ``buildingplan reset`` command for resetting all filters to defaults +-@ `buildingplan`: rename "Build" button to "Confirm" on the item selection dialog and change the hotkey from "B" to "C" - `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory - `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` - player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 378d3b140..ca33106f2 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -47,11 +47,14 @@ to build the planned buildings as they are produced, with minimal space dedicated to stockpiles. The DFHack `orders` library can help with setting these manager workorders up for you. -If you do not wish to use the ``buildingplan`` interface, you can turn off the +IF you don't want to use the ``buildingplan`` interface for the building you're +currently trying to place, you can hit :kbd:`Alt`:kbd:`M` or click on the +minimize toggle in the upper left corner of the panel. If you do not wish to +ever use the ``buildingplan`` interface, you can turn off the ``buildingplan.planner`` overlay in `gui/control-panel` (on the "Overlays" -tab). You should not disable the ``buildingplan`` "System service" in -`gui/control-panel` since existing planned buildings in loaded forts will stop -functioning. +tab). Be sure to keep the ``buildingplan`` "System service" itself enabled in +`gui/control-panel` since if you turn it off, existing planned buildings in +saved forts will stop functioning. Usage ----- @@ -119,12 +122,8 @@ tiles selected in the construction area are not appropriate for building. For example, if you want to fill an area with flooring, you can select the entire area, and any tiles with existing buildings or walls will simply be skipped. -Setting heat safety filters -+++++++++++++++++++++++++++ - -If you specifically need the building to be magma- or fire-safe, click on the -"Building safety" button or hit :kbd:`g` until the desired heat safety is -displayed. This filter applies to all items used to construct the building. +For weapon and spike traps, you can choose how many weapons will be included +on this panel. Setting quality and material filters ++++++++++++++++++++++++++++++++++++ @@ -165,8 +164,8 @@ name or selecting it with the arrow keys and hitting :kbd:`Enter`. You can instead select items one at a time by Ctrl-clicking (:kbd:`Shift`:kbd:`Right`) to increment or Ctrl-Shift-clicking (:kbd:`Shift`:kbd:`Left`) to decrement. -Once you are satisfied with your choices, click on the "Build" button or hit -:kbd:`B` to continue building. Note that you don't have to select all the items +Once you are satisfied with your choices, click on the "Confirm" button or hit +:kbd:`C` to continue building. Note that you don't have to select all the items that the building needs. Any remaining items will be automatically chosen from other available items (or future items if not all items are available yet). If there are multiple item types to choose for the current building, one dialog diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index 7e6567d04..8134b9455 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -76,13 +76,13 @@ function ItemSelection:init() }, }, widgets.Label{ - frame={r=0, w=9, t=0, h=3}, + frame={r=0, w=11, t=0, h=3}, text_pen=BUILD_TEXT_PEN, text_hpen=BUILD_TEXT_HPEN, text={ - ' ', NEWLINE, - ' Build ', NEWLINE, - ' ', + ' ', NEWLINE, + ' Confirm ', NEWLINE, + ' ', }, on_click=self:callback('submit'), }, @@ -115,8 +115,8 @@ function ItemSelection:init() }, widgets.HotkeyLabel{ frame={l=22, b=1}, - key='CUSTOM_SHIFT_B', - label='Build', + key='CUSTOM_SHIFT_C', + label='Confirm', auto_width=true, on_activate=self:callback('submit'), }, From 779b9bfb6aca0042439c99e7015b1b2e99128728 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 10:08:16 -0700 Subject: [PATCH 0828/2222] bump to 50.07-beta1 --- CMakeLists.txt | 2 +- docs/changelog.txt | 20 ++++++++++++++------ library/xml | 2 +- scripts | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 674d9b60d..790e8f59b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "alpha3") +set(DFHACK_RELEASE "beta1") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 28f3ab904..0a5bfd1a1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,20 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.07-beta1 + ## Fixes -@ `buildingplan`: items are now attached correctly to screw pumps and other multi-item buildings -@ `buildingplan`: buildings with different material filters will no longer get "stuck" if one of the filters currently matches no items @@ -53,12 +67,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. -@ `showmood`: clarify how many bars and/or cloth items are actually needed for the mood -## Documentation - -## API - -## Lua - ## Removed -@ `buildingplan`: "heat safety" setting is temporarily removed while we investigate incorrect item matching diff --git a/library/xml b/library/xml index 13514f507..dbba09514 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 13514f507b39dc256490d7b387331016e4e2b89f +Subproject commit dbba095144e631b111d793aae0af2601fffe8e96 diff --git a/scripts b/scripts index 21aceeb24..cffac20a4 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 21aceeb241df6ddfb6b276bb64ff6512842c2115 +Subproject commit cffac20a47c80c73d9084cb1eac0a0063a042f6f From 2bceff76e08a7dab18465747f81ec6fa74c49951 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 10:14:11 -0700 Subject: [PATCH 0829/2222] changelog fix --- docs/changelog.txt | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0a5bfd1a1..b565f533f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -64,7 +64,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: -@ `buildingplan`: rename "Build" button to "Confirm" on the item selection dialog and change the hotkey from "B" to "C" - `blueprint`: now writes blueprints to the ``dfhack-config/blueprints`` directory - `blueprint-library-guide`: library blueprints have moved from ``blueprints`` to ``hack/data/blueprints`` -- player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. +- `blueprint-library-guide`: player-created blueprints should now go in the ``dfhack-config/blueprints`` folder. please move your existing blueprints from ``blueprints`` to ``dfhack-config/blueprints``. you don't need to move the library blueprints -- those can be safely deleted from the old ``blueprints`` directory. -@ `showmood`: clarify how many bars and/or cloth items are actually needed for the mood ## Removed diff --git a/scripts b/scripts index cffac20a4..2baf2a293 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit cffac20a47c80c73d9084cb1eac0a0063a042f6f +Subproject commit 2baf2a293b022e74bda169904182386ed414c84f From 181b930070f02113a4f191bd69bb587ea343a166 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 14:59:39 -0700 Subject: [PATCH 0830/2222] allow inputs to pass through when minimized --- plugins/lua/buildingplan/planneroverlay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index edc258f00..19f17ea62 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -603,8 +603,9 @@ function PlannerOverlay:onInput(keys) self.minimized = not self.minimized return true end + if self.minimized then return false end if PlannerOverlay.super.onInput(self, keys) then - return not self.minimized + return true end if keys._MOUSE_L_DOWN then if is_over_options_panel() then return false end @@ -616,7 +617,6 @@ function PlannerOverlay:onInput(keys) or self.subviews.errors:getMousePos() then return true end - if self.minimized then return false end if not is_construction() and #uibs.errors > 0 then return true end if dfhack.gui.getMousePos() then if is_choosing_area() or cur_building_has_no_area() then From 4a3363da9c6f4cf064d9c0aad39962ee61e44f74 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 17:43:58 -0700 Subject: [PATCH 0831/2222] anchor revflood at a unit's position makes it more user friendly and reduces chance of mishap by placing the cursor in an unforgiving location (like in a closed cavity) --- docs/plugins/reveal.rst | 9 +++++---- plugins/reveal.cpp | 30 +++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/plugins/reveal.rst b/docs/plugins/reveal.rst index bbcc07b6b..2f4ebb5d8 100644 --- a/docs/plugins/reveal.rst +++ b/docs/plugins/reveal.rst @@ -17,7 +17,7 @@ reveal :summary: Switch between reveal and unreveal. .. dfhack-command:: revflood - :summary: Hide everything, then reveal tiles with a path to the cursor. + :summary: Hide everything, then reveal tiles with a path to a unit. .. dfhack-command:: nopause :summary: Disable pausing. @@ -44,9 +44,10 @@ Usage where (for example) you abandoned with the fort revealed and no longer need the saved map data when you load a new fort. ``revflood`` - Hide everything, then reveal tiles with a path to the cursor. This allows - reparing maps that you accidentally saved while they were revealed. Note - that tiles behind constructed walls are also revealed as a workaround for + Hide everything, then reveal tiles with a path to the keyboard cursor (if + enabled) or the selected unit (if a unit is selected) or else a random citizen. + This allows reparing maps that you accidentally saved while they were revealed. + Note that tiles behind constructed walls are also revealed as a workaround for :bug:`1871`. ``nopause 1|0`` Disables pausing (both manual and automatic) with the exception of the pause diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 2820c9f54..b8a4ae902 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -12,6 +12,7 @@ #include "modules/World.h" #include "modules/MapCache.h" #include "modules/Gui.h" +#include "modules/Units.h" #include "modules/Screen.h" #include "df/block_square_event_frozen_liquidst.h" @@ -502,21 +503,32 @@ command_result revflood(color_ostream &out, vector & params) out.printerr("Only in proper dwarf mode.\n"); return CR_FAILURE; } - int32_t cx, cy, cz; + df::coord pos; Maps::getSize(x_max,y_max,z_max); - Gui::getCursorCoords(cx,cy,cz); - if(cx == -30000) - { - out.printerr("Cursor is not active. Point the cursor at some empty space you want to be unhidden.\n"); + Gui::getCursorCoords(pos); + if (!pos.isValid()) { + df::unit *unit = Gui::getSelectedUnit(out, true); + if (unit) + pos = Units::getPosition(unit); + } + + if (!pos.isValid()) { + vector citizens; + Units::getCitizens(citizens); + if (citizens.size()) + pos = Units::getPosition(citizens[0]); + } + + if(!pos.isValid()) { + out.printerr("Please select a unit or place the keyboard cursor at some empty space you want to be unhidden.\n"); return CR_FAILURE; } - DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz); MapCache * MCache = new MapCache; - df::tiletype tt = MCache->tiletypeAt(xy); + df::tiletype tt = MCache->tiletypeAt(pos); if(isWallTerrain(tt)) { - out.printerr("Point the cursor at some empty space you want to be unhidden.\n"); + out.printerr("Please select a unit or place the keyboard cursor at some empty space you want to be unhidden.\n"); delete MCache; return CR_FAILURE; } @@ -534,7 +546,7 @@ command_result revflood(color_ostream &out, vector & params) } MCache->trash(); - unhideFlood_internal(MCache, xy); + unhideFlood_internal(MCache, pos); MCache->WriteAll(); delete MCache; From b55a19f85945241934acc961afed8dfdcba836f1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 21:07:25 -0700 Subject: [PATCH 0832/2222] make the planner panel un-minimizable again --- plugins/lua/buildingplan/planneroverlay.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 19f17ea62..a60b2ebcd 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -603,10 +603,10 @@ function PlannerOverlay:onInput(keys) self.minimized = not self.minimized return true end - if self.minimized then return false end if PlannerOverlay.super.onInput(self, keys) then return true end + if self.minimized then return false end if keys._MOUSE_L_DOWN then if is_over_options_panel() then return false end local detect_rect = copyall(self.frame_rect) From 1984cf44694d2d277dab1439eb1a7f08c191fd14 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 21:07:38 -0700 Subject: [PATCH 0833/2222] remove unnecessary reload --- plugins/lua/buildingplan.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 3492e2ccd..077470409 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -114,9 +114,7 @@ end -- for use during development to reload all buildingplan modules function reload_modules() - -- ensure circular deps are refreshed reload('plugins.buildingplan.pens') - reload('plugins.buildingplan') reload('plugins.buildingplan.filterselection') reload('plugins.buildingplan.itemselection') reload('plugins.buildingplan.planneroverlay') From 3e0b37e922beb5ff6cfa691fdefffc7438e05b85 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 23:41:02 -0700 Subject: [PATCH 0834/2222] fix typo in strangemod docs --- docs/plugins/strangemood.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/strangemood.rst b/docs/plugins/strangemood.rst index a863943e9..12e814c55 100644 --- a/docs/plugins/strangemood.rst +++ b/docs/plugins/strangemood.rst @@ -10,7 +10,7 @@ Usage :: - stangemood [] + strangemood [] Examples -------- From dbe2b6753813d765b3533b267e90bab2de975b23 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 18 Mar 2023 07:13:45 +0000 Subject: [PATCH 0835/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 2baf2a293..142477779 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 2baf2a293b022e74bda169904182386ed414c84f +Subproject commit 1424777793b672b105a1785434518233eb0692da From 490ffe19bae7fdfc84e6931f9a717308dd0f549a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 18 Mar 2023 00:48:39 -0700 Subject: [PATCH 0836/2222] fix typos in buildingplan docs --- docs/plugins/buildingplan.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index ca33106f2..71951f386 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -47,9 +47,9 @@ to build the planned buildings as they are produced, with minimal space dedicated to stockpiles. The DFHack `orders` library can help with setting these manager workorders up for you. -IF you don't want to use the ``buildingplan`` interface for the building you're +If you don't want to use the ``buildingplan`` interface for the building you're currently trying to place, you can hit :kbd:`Alt`:kbd:`M` or click on the -minimize toggle in the upper left corner of the panel. If you do not wish to +minimize toggle in the upper right corner of the panel. If you do not wish to ever use the ``buildingplan`` interface, you can turn off the ``buildingplan.planner`` overlay in `gui/control-panel` (on the "Overlays" tab). Be sure to keep the ``buildingplan`` "System service" itself enabled in From 4d1e0da1b87e7871ad0fa9e62bc924bbf52e0b5f Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sun, 19 Mar 2023 18:38:11 -0400 Subject: [PATCH 0837/2222] add alias for gui/dig -> gui/design and add default hotkey --- data/init/dfhack.keybindings.init | 4 ++++ data/init/dfhack.tools.init | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index 3a94aea2a..8ce0c3696 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -153,6 +153,10 @@ keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort #keybinding add Ctrl-Shift-N@dwarfmode|unit|unitlist|joblist|dungeon_monsterstatus|layer_unit_relationship|item|workshop_profile|layer_noblelist|locations|pets|layer_overall_health|textviewer|reportlist|announcelist|layer_military|layer_unit_health|customize_unit|buildinglist gui/rename #keybinding add Ctrl-Shift-T@dwarfmode|unit|unitlist|joblist|dungeon_monsterstatus|layer_unit_relationship|item|workshop_profile|layer_noblelist|locations|pets|layer_overall_health|textviewer|reportlist|announcelist|layer_military|layer_unit_health|customize_unit "gui/rename unit-profession" +# gui/design +keybinding add Ctrl-D@dwarfmode gui/design + + ##################### # adv mode bindings # diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 50272cfa2..3b7fe7285 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -137,3 +137,9 @@ enable \ # a replacement for the "load game" screen #gui/load-screen enable + +################### +# Default Aliases # +################### + +alias gui/dig gui/design From e8b9a3acfb68fc1bdefb7dfc299e8bfd781633fe Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sun, 19 Mar 2023 19:43:23 -0400 Subject: [PATCH 0838/2222] Add default alias for gui/dig -> gui/design --- data/init/dfhack.tools.init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 3b7fe7285..0ca764e8e 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -142,4 +142,4 @@ enable \ # Default Aliases # ################### -alias gui/dig gui/design +alias add gui/dig gui/design From c9493dba7eb81c7134de9052ccd3bbf75dc1954a Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sun, 19 Mar 2023 21:50:28 -0400 Subject: [PATCH 0839/2222] Added entry to Removed.rst --- docs/about/Removed.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index eea11b088..47a7c8e76 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -192,3 +192,9 @@ Replaced with a GUI version: `gui/unit-syndromes`. warn-stuck-trees ================ The corresponding DF :bug:`9252` was fixed in DF 0.44.01. + +.. gui/dig: + +gui/dig +======= +Renamed to gui/design From fadf4cf2cd4acbd49cf74bc3e48b3c67644b3459 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 20 Mar 2023 02:56:57 +0000 Subject: [PATCH 0840/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 142477779..3e425ad66 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 1424777793b672b105a1785434518233eb0692da +Subproject commit 3e425ad66a2749c5a3c684cd2cbfaea97921bada From fa67b7c38fe5d957db3130bc129ddbb7bc7b297b Mon Sep 17 00:00:00 2001 From: John Cosker Date: Sun, 19 Mar 2023 22:58:38 -0400 Subject: [PATCH 0841/2222] fix underscore --- docs/about/Removed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 47a7c8e76..c427b3f8d 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -193,7 +193,7 @@ warn-stuck-trees ================ The corresponding DF :bug:`9252` was fixed in DF 0.44.01. -.. gui/dig: +.. _gui/dig: gui/dig ======= From 1ed8c81ac2205c6841c1882a4290007cee14bb00 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 20 Mar 2023 03:19:40 +0000 Subject: [PATCH 0842/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 3e425ad66..f3a8d2df7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 3e425ad66a2749c5a3c684cd2cbfaea97921bada +Subproject commit f3a8d2df72fa10311c4c52471b01216e6da90d9b From bdd4163d982fe007281b736d48db80ea3f2251c2 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 19 Mar 2023 20:21:06 -0700 Subject: [PATCH 0843/2222] Update Removed.rst --- docs/about/Removed.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index c427b3f8d..b7bb1fa3f 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -146,6 +146,12 @@ This script is no longer useful in current DF versions. The script required a binpatch `, which has not been available since DF 0.34.11. +.. _gui/dig: + +gui/dig +======= +Renamed to gui/design + .. _gui/hack-wish: gui/hack-wish @@ -193,8 +199,3 @@ warn-stuck-trees ================ The corresponding DF :bug:`9252` was fixed in DF 0.44.01. -.. _gui/dig: - -gui/dig -======= -Renamed to gui/design From d2f97747ded879559c93cefd7609edf48db92f92 Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 19 Mar 2023 20:22:05 -0700 Subject: [PATCH 0844/2222] Update Removed.rst --- docs/about/Removed.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index b7bb1fa3f..01669fac4 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -198,4 +198,3 @@ Replaced with a GUI version: `gui/unit-syndromes`. warn-stuck-trees ================ The corresponding DF :bug:`9252` was fixed in DF 0.44.01. - From 42e21bf946772f986de978b944c09e8858221057 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 20 Mar 2023 11:00:25 -0500 Subject: [PATCH 0845/2222] reenable prospector in fort mode fort mode prospect works. embark mode prospect doesn't crash but generates nonsense data, so that code has been disabled for now --- docs/changelog.txt | 1 + plugins/CMakeLists.txt | 2 +- plugins/prospector.cpp | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b565f533f..73e2709b1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. ## Fixes diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 477e83436..8fac3d467 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -140,7 +140,7 @@ dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #dfhack_plugin(petcapRemover petcapRemover.cpp) #dfhack_plugin(plants plants.cpp) dfhack_plugin(probe probe.cpp) -#dfhack_plugin(prospector prospector.cpp LINK_LIBRARIES lua) +dfhack_plugin(prospector prospector.cpp LINK_LIBRARIES lua) #dfhack_plugin(power-meter power-meter.cpp LINK_LIBRARIES lua) dfhack_plugin(regrass regrass.cpp) add_subdirectory(remotefortressreader) diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index e75c967dc..86d77e643 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -565,6 +565,10 @@ static command_result embark_prospector(color_ostream &out, df::viewscreen_choose_start_sitest *screen, const prospect_options &options) { + out.printerr("prospector at embark is not currently available.\n"); + return CR_FAILURE; + +/* if (!world || !world->world_data) { out.printerr("World data is not available.\n"); @@ -621,6 +625,7 @@ static command_result embark_prospector(color_ostream &out, out << std::endl << "Warning: the above data is only a very rough estimate." << std::endl; return CR_OK; +*/ } static command_result map_prospector(color_ostream &con, From df93aceb9de1739d2fb0b7fc5c7e7a96c453439a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 21 Mar 2023 00:54:38 +0000 Subject: [PATCH 0846/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index dbba09514..a86161521 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit dbba095144e631b111d793aae0af2601fffe8e96 +Subproject commit a86161521427e98d4e814bf4e080e74121edb966 From 5ed1e4ece7036a131f20f4b495e3344eeed6be7d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 22 Mar 2023 07:13:59 +0000 Subject: [PATCH 0847/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index a86161521..0dc8ae877 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a86161521427e98d4e814bf4e080e74121edb966 +Subproject commit 0dc8ae87746e287538be01f01dc628662e756794 diff --git a/scripts b/scripts index f3a8d2df7..7f388c0c1 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f3a8d2df72fa10311c4c52471b01216e6da90d9b +Subproject commit 7f388c0c1bf9ed5fa3f7abf1153da54921a93e15 From 959b4b317103dad07d78dcf765f7e7b0ee98e3b7 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Wed, 22 Mar 2023 18:29:47 -0500 Subject: [PATCH 0848/2222] update tailor `tailor` now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine --- docs/changelog.txt | 1 + plugins/tailor.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 73e2709b1..0abd926d2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. ## Fixes +- `tailor` now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine ## Misc Improvements diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 19237429e..66e54dd60 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -124,7 +124,8 @@ static const MatType M_ADAMANTINE = MatType("adamantine", df::job_material_category::mask_strand, df::armor_general_flags::SOFT); static const std::list all_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER, M_ADAMANTINE }; -static std::list material_order = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; // M_ADAMANTINE is not included by default +static const std::list default_materials = { M_SILK, M_CLOTH, M_YARN, M_LEATHER }; // adamantine not included by default +static std::list material_order = default_materials; static struct BadFlags { uint32_t whole; @@ -191,7 +192,7 @@ public: if (i->flags.whole & badFlags.whole) continue; - if (require_dyed && !i->hasImprovements()) + if (require_dyed && (!i->isDyed())) { // only count dyed std::string d; @@ -589,7 +590,7 @@ static void set_material_order() { material_order.push_back(M_ADAMANTINE); } if (!material_order.size()) - std::copy(all_materials.begin(), all_materials.end(), std::back_inserter(material_order)); + std::copy(default_materials.begin(), default_materials.end(), std::back_inserter(material_order)); } DFhackCExport command_result plugin_load_data (color_ostream &out) { From 29c17b407c7a9f59a7a8d8a74ee7c79fd3d98beb Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 23 Mar 2023 07:13:59 +0000 Subject: [PATCH 0849/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 0dc8ae877..8b8ac2de0 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0dc8ae87746e287538be01f01dc628662e756794 +Subproject commit 8b8ac2de03ab5f8e5506864a751933059abfd03e From 03e44f1e7ff9d285e1df5af53f1618b3b6d378af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 00:25:29 -0700 Subject: [PATCH 0850/2222] fix initial extended/retracted position for upright spike traps --- docs/changelog.txt | 3 ++- library/modules/Buildings.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0abd926d2..d68900d12 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,7 +37,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. ## Fixes -- `tailor` now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine +- `tailor`: now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine +- `buildingplan`: upright spike traps are now placed extended rather than retracted ## Misc Improvements diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 2b6fc8ec8..1d1ba9318 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -68,6 +68,7 @@ using namespace DFHack; #include "df/building_stockpilest.h" #include "df/building_trapst.h" #include "df/building_water_wheelst.h" +#include "df/building_weaponst.h" #include "df/building_wellst.h" #include "df/building_workshopst.h" #include "df/buildings_other_id.h" @@ -591,6 +592,12 @@ df::building *Buildings::allocInstance(df::coord pos, df::building_type type, in obj->gate_flags.bits.closed = false; break; } + case building_type::Weapon: + { + if (VIRTUAL_CAST_VAR(obj, df::building_weaponst, bld)) + obj->gate_flags.bits.closed = false; + break; + } default: break; } From 0b72b42b7e6e202675e2a8d8f98ecf76d50ced39 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 00:59:05 -0700 Subject: [PATCH 0851/2222] allow metal chests to be melted --- docs/changelog.txt | 1 + plugins/automelt.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0abd926d2..df909c0b1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `tailor` now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine ## Misc Improvements +- `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) ## Documentation diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index 0cd9e8131..beb4cba35 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -293,7 +293,7 @@ static inline bool can_melt(df::item *item) df::item_type t = item->getType(); - if (t == df::enums::item_type::BOX || t == df::enums::item_type::BAR) + if (t == df::enums::item_type::BAR) return false; for (auto &g : item->general_refs) From 90aed848bb5390278663ffad7b0cdd9fb7d17d85 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 02:17:36 -0700 Subject: [PATCH 0852/2222] add minimize button to orders overlay panel --- docs/changelog.txt | 1 + docs/plugins/orders.rst | 7 ++- plugins/lua/orders.lua | 100 ++++++++++++++++++++++++++++++---------- 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2b88261f8..cbc38fab7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) +- `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen ## Documentation diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 6fe159899..08e05c6ca 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -48,8 +48,11 @@ is open via an `overlay` widget. There are hotkeys assigned to export, import, sort, and clear. You can also click on the hotkey hints as if they were buttons. Clearing will ask for confirmation before acting. -If you want to change where the hotkey hints appear, you can move them via -`gui/overlay`. +If you want to change where the overlay panel appears, you can move it via +`gui/overlay`. If you just need to get the overlay out of the way temporarily, +for example to read a long description of a historical figure when choosing a +subject for a statue, click on the small arrow in the upper right corner of the +overlay panel. Click on the arrow again to restore the panel. The orders library ------------------ diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index 72bb6185e..6f5bc677a 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -46,37 +46,87 @@ OrdersOverlay.ATTRS{ default_enabled=true, viewscreens='dwarfmode/Info/WORK_ORDERS', frame={w=30, h=4}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, } function OrdersOverlay:init() - self:addviews{ - widgets.HotkeyLabel{ - frame={t=0, l=0}, - label='import', - key='CUSTOM_CTRL_I', - on_activate=do_import, - }, - widgets.HotkeyLabel{ - frame={t=1, l=0}, - label='export', - key='CUSTOM_CTRL_E', - on_activate=do_export, - }, - widgets.HotkeyLabel{ - frame={t=0, l=15}, - label='sort', - key='CUSTOM_CTRL_O', - on_activate=do_sort, + self.minimized = false + + local main_panel = widgets.Panel{ + frame={t=0, l=0, r=0, h=4}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, + visible=function() return not self.minimized end, + subviews={ + widgets.HotkeyLabel{ + frame={t=0, l=0}, + label='import', + key='CUSTOM_CTRL_I', + auto_width=true, + on_activate=do_import, + }, + widgets.HotkeyLabel{ + frame={t=1, l=0}, + label='export', + key='CUSTOM_CTRL_E', + auto_width=true, + on_activate=do_export, + }, + widgets.HotkeyLabel{ + frame={t=0, l=15}, + label='sort', + key='CUSTOM_CTRL_O', + auto_width=true, + on_activate=do_sort, + }, + widgets.HotkeyLabel{ + frame={t=1, l=15}, + label='clear', + key='CUSTOM_CTRL_C', + auto_width=true, + on_activate=do_clear, + }, }, - widgets.HotkeyLabel{ - frame={t=1, l=15}, - label='clear', - key='CUSTOM_CTRL_C', - on_activate=do_clear, + } + + local minimized_panel = widgets.Panel{ + frame={t=0, r=0, w=3, h=1}, + subviews={ + widgets.Label{ + frame={t=0, l=0, w=1, h=1}, + text='[', + text_pen=COLOR_RED, + visible=function() return self.minimized end, + }, + widgets.Label{ + frame={t=0, l=1, w=1, h=1}, + text={{text=function() return self.minimized and string.char(31) or string.char(30) end}}, + text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, + text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, + on_click=function() self.minimized = not self.minimized end, + }, + widgets.Label{ + frame={t=0, r=0, w=1, h=1}, + text=']', + text_pen=COLOR_RED, + visible=function() return self.minimized end, + }, }, } + + self:addviews{ + main_panel, + minimized_panel, + } +end + +function OrdersOverlay:onInput(keys) + if keys.CUSTOM_ALT_M then + self.minimized = not self.minimized + return true + end + if OrdersOverlay.super.onInput(self, keys) then + return true + end end OVERLAY_WIDGETS = { From a4c9a5116fef314b58101e48a690f847c7a9acff Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 17:15:48 -0700 Subject: [PATCH 0853/2222] install the stockpiles settings library --- data/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 89abe3aaf..355182bfb 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -10,6 +10,9 @@ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/base_command_counts.json install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/orders/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/orders") +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/stockpiles/ + DESTINATION "${DFHACK_DATA_DESTINATION}/data/stockpiles") + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/art/ DESTINATION "${DFHACK_DATA_DESTINATION}/data/art") From 8c0b59c5486b67b8f9f97c7d194914e710d3070a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 17:16:18 -0700 Subject: [PATCH 0854/2222] add the stockpiles user data directory --- data/dfhack-config/stockpiles/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/dfhack-config/stockpiles/README.md diff --git a/data/dfhack-config/stockpiles/README.md b/data/dfhack-config/stockpiles/README.md new file mode 100644 index 000000000..593d45fb6 --- /dev/null +++ b/data/dfhack-config/stockpiles/README.md @@ -0,0 +1,5 @@ +This folder contains stockpile settings that can be applied by `stockpiles` and +`quickfort` tools. For more information, see: + +* [stockpiles documentation](https://docs.dfhack.org/en/latest/docs/tools/stockpiles.html) +* [quickfort documentation](https://docs.dfhack.org/en/latest/docs/guides/quickfort-user-guide.html) From e21c55d6ffc1d5ac3a0fb608f3eadeb7ab1dd944 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 17:16:42 -0700 Subject: [PATCH 0855/2222] update stockpiles command and use new data paths --- docs/plugins/stockpiles.rst | 79 +++++--- plugins/lua/stockpiles.lua | 299 ++++++++++-------------------- plugins/stockpiles/stockpiles.cpp | 167 +++++++++-------- 3 files changed, 239 insertions(+), 306 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 01cd3159b..7ca0c5039 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -1,41 +1,68 @@ -.. _stocksettings: - stockpiles ========== .. dfhack-tool:: - :summary: Import and export stockpile settings. + :summary: Import, export, or modify stockpile settings and features. :tags: fort design productivity stockpiles - :no-command: - -.. dfhack-command:: savestock - :summary: Exports the configuration of the selected stockpile. - -.. dfhack-command:: loadstock - :summary: Imports configuration for the selected stockpile. -Select a stockpile in the UI first to use these commands. +If you are importing or exporting setting and don't want to specify a building +ID, select a stockpile in the UI before running the command. Usage ----- -``savestock `` - Saves the currently highlighted stockpile's settings to a file in your - Dwarf Fortress folder. This file can be used to copy settings between game - saves or players. -``loadstock `` - Loads a saved stockpile settings file and applies it to the currently - selected stockpile. +:: -Filenames with spaces are not supported. Generated materials, divine metals, -etc. are not saved as they are different in every world. + stockpiles [status] + stockpiles list [] + stockpiles export [] + stockpiles import [] + +Exported stockpile settings are saved in the ``dfhack-config/stockpiles`` +folder, where you can view and delete them, if desired. Names can only +contain numbers, letters, periods, underscores, dashes, and spaces. If +the name has spaces, be sure to surround it with double quotes (:kbd:`"`). + +The names of library settings files are all prefixed by the string ``library/``. +You can specify library files explicitly by including the prefix, or you can +just write the short name to use a player-exported file by that name if it +exists, and the library file if it doesn't. Examples -------- -``savestock food`` - Export the stockpile settings for the currently selected stockpile to a - file named ``food.dfstock``. -``loadstock food`` - Set the selected stockpile settings to those saved in the ``food.dfstock`` - file. +``stockpiles`` + Shows the list of all your stockpiles and some relevant statistics. +``stockpiles list`` + Shows the list of previously exported stockpile settings files, including + the stockpile configuration library. +``stockpiles list plants`` + Shows the list of exported stockpile settings files that include the + substring ``plants``. +``stockpiles import library/plants`` + Imports the library ``plants`` settings file into the currently selected + stockpile. +``stockpiles import plants`` + Imports a player-exported settings file named ``plants``, or the library + ``plants`` settings file if a player-exported file by that name doesn't + exist. +``stockpiles export mysettings`` + Export the settings for the currently selected stockpile to a file named + ``dfhack-config/stockpiles/mysettings.dfstock``. + +Options +------- + +``-s``, ``--stockpile `` + Specify a specific stockpile ID instead of using the one currently selected + in the UI. + +.. _stockpiles-library: + +The stockpiles settings library +------------------------------- + +DFHack comes with a library of useful stockpile settings files that are ready +for import: + +TODO: port alias library here diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index ca8c28cd4..723ff97f6 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -1,244 +1,135 @@ local _ENV = mkmodule('plugins.stockpiles') ---[[ +local argparse = require('argparse') - Native functions: +local STOCKPILES_DIR = "dfhack-config/stockpiles"; +local STOCKPILES_LIBRARY_DIR = "hack/data/stockpiles"; - * stockpiles_list_settings(dir_path), list files in directory - * stockpiles_load(file), with full path - * stockpiles_save(file), with full path - * isEnabled() - ---]] --- -function safe_require(module) - local status, module = pcall(require, module) - return status and module or nil +local function get_sp_name(name, num) + if #name > 0 then return name end + return ('Stockpile %d'):format(num) end - -local gui = require 'gui' -local widgets = require('gui.widgets') -local dlg = require('gui.dialogs') -local script = require 'gui.script' -local persist = safe_require('persist-table') - - -function ListFilterDialog(args) - args.text = args.prompt or 'Type or select an option' - args.text_pen = COLOR_WHITE - args.with_filter = true - args.icon_width = 2 - - local choices = {} - - if not args.hide_none then - table.insert(choices, { - icon = '?', text = args.none_caption or 'none', - index = -1, name = -1 - }) +local STATUS_FMT = '%6s %s' +local function print_status() + local sps = df.global.world.buildings.other.STOCKPILE + print(('Current stockpiles: %d'):format(#sps)) + if #sps > 0 then + print() + print(STATUS_FMT:format('ID', 'Name')) + print(STATUS_FMT:format('------', '----------')) end - - local filter = args.item_filter - - for i,v in ipairs(args.items) do - if not filter or filter(v,-1) then - local name = v - local icon - table.insert(choices, { - icon = icon, search_key = string.lower(name), text = name, index = i - }) - end + for _,sp in ipairs(sps) do + print(STATUS_FMT:format(sp.id, get_sp_name(sp.name, sp.stockpile_number))) end +end - args.choices = choices - - if args.on_select then - local cb = args.on_select - args.on_select = function(idx, obj) - return cb(obj.index, args.items[obj.index]) +local function list_dir(path, prefix, filters) + local paths = dfhack.filesystem.listdir_recursive(path, 0, false) + if not paths then + dfhack.printerr(('Cannot find stockpile settings directory: "%s"'):format(path)) + return + end + local normalized_filters = {} + for _,filter in ipairs(filters or {}) do + table.insert(normalized_filters, filter:lower()) + end + for _,v in ipairs(paths) do + local normalized_path = prefix .. v.path:lower() + if v.isdir or not normalized_path:endswith('.dfstock') then goto continue end + normalized_path = normalized_path:sub(1, -9) + if #normalized_filters > 0 then + local matched = false + for _,filter in ipairs(normalized_filters) do + if normalized_path:find(filter, 1, true) then + matched = true + break + end + end + if not matched then goto continue end end + print(('%s%s'):format(prefix, v.path:sub(1, -9))) + ::continue:: end - - return dlg.ListBox(args) end -function showFilterPrompt(title, list, text,item_filter,hide_none) - ListFilterDialog{ - frame_title=title, - items=list, - prompt=text, - item_filter=item_filter, - hide_none=hide_none, - on_select=script.mkresume(true), - on_cancel=script.mkresume(false), - on_close=script.qresume(nil) - }:show() - - return script.wait() +local function list_settings_files(filters) + list_dir(STOCKPILES_DIR, '', filters) + list_dir(STOCKPILES_LIBRARY_DIR, 'library/', filters) end -function init() - if persist == nil then return end - if dfhack.isMapLoaded() then - if persist.GlobalTable.stockpiles == nil then - persist.GlobalTable.stockpiles = {} - persist.GlobalTable.stockpiles['settings_path'] = './stocksettings' - end +local function assert_safe_name(name) + if not name or #name == 0 then + qerror('name missing or empty') end -end - -function tablify(iterableObject) - t={} - for k,v in ipairs(iterableObject) do - t[k] = v~=nil and v or 'nil' + if name:find('[^%a ._-]') then + qerror('name can only contain numbers, letters, periods, underscores, dashes, and spaces') end - return t end -local filename_invalid_regex = '[^A-Za-z0-9 ._-]' - -function valid_filename(filename) - return not filename:match(filename_invalid_regex) +local function get_sp_id(opts) + if opts.id then return opts.id end + local sp = dfhack.gui.getSelectedStockpile() + if sp then return sp.id end + return nil end -function sanitize_filename(filename) - local ret = '' - for i = 1, #filename do - local ch = filename:sub(i, i) - if valid_filename(ch) then - ret = ret .. ch - else - ret = ret .. '-' - end - end - return ret +local function export_stockpile(name, opts) + assert_safe_name(name) + name = STOCKPILES_DIR .. '/' .. name + stockpiles_export(name, get_sp_id(opts)) end -FilenameInputBox = defclass(FilenameInputBox, dlg.InputBox) -function FilenameInputBox:onInput(keys) - if not valid_filename(string.char(keys._STRING or 0)) and not keys.STRING_A000 then - keys._STRING = nil +local function import_stockpile(name, opts) + local is_library = false + if name:startswith('library/') then + name = name:sub(9) + is_library = true end - FilenameInputBox.super.onInput(self, keys) -end - -function showFilenameInputPrompt(title, text, tcolor, input, min_width) - FilenameInputBox{ - frame_title = title, - text = text, - text_pen = tcolor, - input = input, - frame_width = min_width, - on_input = script.mkresume(true), - on_cancel = script.mkresume(false), - on_close = script.qresume(nil) - }:show() - - return script.wait() + assert_safe_name(name) + if not is_library and dfhack.filesystem.exists(STOCKPILES_DIR .. '/' .. name .. '.dfstock') then + name = STOCKPILES_DIR .. '/' .. name + else + name = STOCKPILES_LIBRARY_DIR .. '/' .. name + end + stockpiles_import(name, get_sp_id(opts)) end -function load_settings() - init() - local path = get_path() - local ok, list = pcall(stockpiles_list_settings, path) - if not ok then - show_message_box("Stockpile Settings", "The stockpile settings folder doesn't exist.", true) - return - end - if #list == 0 then - show_message_box("Stockpile Settings", "There are no saved stockpile settings.", true) +local function process_args(opts, args) + if args[1] == 'help' then + opts.help = true return end - local choice_list = {} - for i,v in ipairs(list) do - choice_list[i] = string.gsub(v, "/", "/ ") - choice_list[i] = string.gsub(choice_list[i], "-", " - ") - choice_list[i] = string.gsub(choice_list[i], "_", " ") - end - - script.start(function() - local ok2,index,name=showFilterPrompt('Stockpile Settings', choice_list, 'Choose a stockpile', function(item) return true end, true) - if ok2 then - local filename = list[index]; - stockpiles_load(path..'/'..filename) - end - end) -end - -function save_settings(stockpile) - init() - script.start(function() - local suggested = stockpile.name - if #suggested == 0 then - suggested = 'Stock1' - end - suggested = sanitize_filename(suggested) - local path = get_path() - local sok,filename = showFilenameInputPrompt('Stockpile Settings', 'Enter filename', COLOR_WHITE, suggested) - if sok then - if filename == nil or filename == '' or not valid_filename(filename) then - script.showMessage('Stockpile Settings', 'Invalid File Name', COLOR_RED) - else - if not dfhack.filesystem.exists(path) then - dfhack.filesystem.mkdir(path) - end - stockpiles_save(path..'/'..filename) - end - end - end) -end - -function manage_settings(sp) - init() - if not guard() then return false end - script.start(function() - local list = {'Load', 'Save'} - local tok,i = script.showListPrompt('Stockpile Settings','Load or Save Settings?',COLOR_WHITE,tablify(list)) - if tok then - if i == 1 then - load_settings() - else - save_settings(sp) - end - end - end) + return argparse.processArgsGetopt(args, { + {'h', 'help', handler=function() opts.help = true end}, + {'s', 'stockpile', has_arg=true, + handler=function(arg) opts.id = argparse.nonnegativeInt(art, 'stockpile') end}, + }) end -function show_message_box(title, msg, iserror) - local color = COLOR_WHITE - if iserror then - color = COLOR_RED - end - script.start(function() - script.showMessage(title, msg, color) - end) -end +function parse_commandline(args) + local opts = {} + local positionals = process_args(opts, args) -function guard() - if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Stockpile') then - qerror("This script requires a stockpile selected in the 'q' mode") + if opts.help or not positionals then return false end - return true -end -function set_path(path) - init() - if persist == nil then - qerror("This version of DFHack doesn't support setting the stockpile settings path. Sorry.") - return + local command = table.remove(positionals, 1) + if not command or command == 'status' then + print_status() + elseif command == 'list' then + list_settings_files(positionals) + elseif command == 'export' then + export_stockpile(positionals[1], opts) + elseif command == 'import' then + import_stockpile(positionals[1], opts) + else + return false end - persist.GlobalTable.stockpiles['settings_path'] = path -end -function get_path() - init() - if persist == nil then - return "stocksettings" - end - return persist.GlobalTable.stockpiles['settings_path'] + return true end return _ENV diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 98c94ace0..e7b3525ef 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -1,13 +1,19 @@ #include "Debug.h" +#include "LuaTools.h" #include "PluginManager.h" #include "StockpileUtils.h" #include "StockpileSerializer.h" #include "modules/Filesystem.h" -#include "modules/Gui.h" -using std::vector; +#include "df/building.h" +#include "df/building_stockpilest.h" + +#include +#include + using std::string; +using std::vector; using namespace DFHack; @@ -19,112 +25,121 @@ namespace DFHack { DBG_DECLARE(stockpiles, log, DebugCategory::LINFO); } -static command_result savestock(color_ostream& out, vector & parameters); -static command_result loadstock(color_ostream& out, vector & parameters); +static command_result do_command(color_ostream &out, vector ¶meters); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + DEBUG(log,out).print("initializing %s\n", plugin_name); -DFhackCExport command_result plugin_init(color_ostream& out, std::vector & commands) { - commands.push_back(PluginCommand( - "savestock", - "Save the active stockpile's settings to a file.", - savestock, - Gui::any_stockpile_hotkey)); commands.push_back(PluginCommand( - "loadstock", - "Load and apply stockpile settings from a file.", - loadstock, - Gui::any_stockpile_hotkey)); + plugin_name, + "Import, export, or modify stockpile settings and features.", + do_command)); return CR_OK; } -DFhackCExport command_result plugin_shutdown(color_ostream& out) { - return CR_OK; +static bool call_stockpiles_lua(color_ostream *out, const char *fn_name, + int nargs = 0, int nres = 0, + Lua::LuaLambda && args_lambda = Lua::DEFAULT_LUA_LAMBDA, + Lua::LuaLambda && res_lambda = Lua::DEFAULT_LUA_LAMBDA) { + DEBUG(log).print("calling stockpiles lua function: '%s'\n", fn_name); + + CoreSuspender guard; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + + if (!out) + out = &Core::getInstance().getConsole(); + + return Lua::CallLuaModuleFunction(*out, L, "plugins.stockpiles", fn_name, + nargs, nres, + std::forward(args_lambda), + std::forward(res_lambda)); } -// exporting -static command_result savestock(color_ostream& out, vector & parameters) { - df::building_stockpilest* sp = Gui::getSelectedStockpile(out, true); - if (!sp) { - out.printerr("Selected building isn't a stockpile.\n"); - return CR_WRONG_USAGE; +static command_result do_command(color_ostream &out, vector ¶meters) { + CoreSuspender suspend; + + bool show_help = false; + if (!call_stockpiles_lua(&out, "parse_commandline", 1, 1, + [&](lua_State *L) { + Lua::PushVector(L, parameters); + }, + [&](lua_State *L) { + show_help = !lua_toboolean(L, -1); + })) { + return CR_FAILURE; } - if (parameters.size() > 2) { - out.printerr("Invalid parameters\n"); - return CR_WRONG_USAGE; - } + return show_help ? CR_WRONG_USAGE : CR_OK; +} - std::string file; - for (size_t i = 0; i < parameters.size(); ++i) { - const std::string o = parameters.at(i); - if (!o.empty() && o[0] != '-') { - file = o; - } - } - if (file.empty()) { - out.printerr("You must supply a valid filename.\n"); - return CR_WRONG_USAGE; +///////////////////////////////////////////////////// +// Lua API +// + +static df::building_stockpilest* get_stockpile(int id) { + return virtual_cast(df::building::find(id)); +} + +static bool stockpiles_export(color_ostream& out, string fname, int id) { + df::building_stockpilest* sp = get_stockpile(id); + if (!sp) { + out.printerr("Specified building isn't a stockpile: %d.\n", id); + return false; } - StockpileSerializer cereal(sp); + if (!is_dfstockfile(fname)) + fname += ".dfstock"; - if (!is_dfstockfile(file)) file += ".dfstock"; try { - if (!cereal.serialize_to_file(file)) { - out.printerr("could not save to %s\n", file.c_str()); - return CR_FAILURE; + StockpileSerializer cereal(sp); + if (!cereal.serialize_to_file(fname)) { + out.printerr("could not save to '%s'\n", fname.c_str()); + return false; } } catch (std::exception& e) { out.printerr("serialization failed: protobuf exception: %s\n", e.what()); - return CR_FAILURE; + return false; } - return CR_OK; + return true; } - -// importing -static command_result loadstock(color_ostream& out, vector & parameters) { - df::building_stockpilest* sp = Gui::getSelectedStockpile(out, true); +static bool stockpiles_import(color_ostream& out, string fname, int id) { + df::building_stockpilest* sp = get_stockpile(id); if (!sp) { - out.printerr("Selected building isn't a stockpile.\n"); - return CR_WRONG_USAGE; + out.printerr("Specified building isn't a stockpile: %d.\n", id); + return false; } - if (parameters.size() < 1 || parameters.size() > 2) { - out.printerr("Invalid parameters\n"); - return CR_WRONG_USAGE; - } + if (!is_dfstockfile(fname)) + fname += ".dfstock"; - std::string file; - for (size_t i = 0; i < parameters.size(); ++i) { - const std::string o = parameters.at(i); - if (!o.empty() && o[0] != '-') { - file = o; - } - } - if (file.empty()) { - out.printerr("ERROR: missing .dfstock file parameter\n"); - return DFHack::CR_WRONG_USAGE; - } - if (!is_dfstockfile(file)) - file += ".dfstock"; - if (!Filesystem::exists(file)) { - out.printerr("ERROR: the .dfstock file doesn't exist: %s\n", file.c_str()); - return CR_WRONG_USAGE; + if (!Filesystem::exists(fname)) { + out.printerr("ERROR: file doesn't exist: '%s'\n", fname.c_str()); + return false; } - StockpileSerializer cereal(sp); try { - if (!cereal.unserialize_from_file(file)) { - out.printerr("unserialization failed: %s\n", file.c_str()); - return CR_FAILURE; + StockpileSerializer cereal(sp); + if (!cereal.unserialize_from_file(fname)) { + out.printerr("deserialization failed: '%s'\n", fname.c_str()); + return false; } } catch (std::exception& e) { - out.printerr("unserialization failed: protobuf exception: %s\n", e.what()); - return CR_FAILURE; + out.printerr("deserialization failed: protobuf exception: %s\n", e.what()); + return false; } - return CR_OK; + + return true; } + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(stockpiles_export), + DFHACK_LUA_FUNCTION(stockpiles_import), + DFHACK_LUA_END +}; From 13cd825fedcda1421641a6430f0c98e7edfe76e9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 18:04:17 -0700 Subject: [PATCH 0856/2222] rename category files to identify them more clearly --- .../{ammo.dfstock => category_ammo.dfstock} | Bin ...imalsprefix.dfstock => category_animals.dfstock} | 0 .../{armorprefix.dfstock => category_armor.dfstock} | Bin ...sprefix.dfstock => category_bars_blocks.dfstock} | Bin .../{clothprefix.dfstock => category_cloth.dfstock} | 0 .../{coinsprefix.dfstock => category_coins.dfstock} | Bin ...rpsesprefix.dfstock => category_corpses.dfstock} | Bin ...efix.dfstock => category_finished_goods.dfstock} | Bin .../{foodprefix.dfstock => category_food.dfstock} | 0 ...fstock => category_furniture_siege_ammo.dfstock} | Bin .../{gemsprefix.dfstock => category_gems.dfstock} | Bin ...atherprefix.dfstock => category_leather.dfstock} | 0 .../{refuse.dfstock => category_refuse.dfstock} | 0 .../{sheetprefix.dfstock => category_sheet.dfstock} | Bin .../{stone.dfstock => category_stone.dfstock} | Bin ...ock => category_weapons_trap_components.dfstock} | Bin 16 files changed, 0 insertions(+), 0 deletions(-) rename data/stockpiles/{ammo.dfstock => category_ammo.dfstock} (100%) rename data/stockpiles/{animalsprefix.dfstock => category_animals.dfstock} (100%) rename data/stockpiles/{armorprefix.dfstock => category_armor.dfstock} (100%) rename data/stockpiles/{barsprefix.dfstock => category_bars_blocks.dfstock} (100%) rename data/stockpiles/{clothprefix.dfstock => category_cloth.dfstock} (100%) rename data/stockpiles/{coinsprefix.dfstock => category_coins.dfstock} (100%) rename data/stockpiles/{corpsesprefix.dfstock => category_corpses.dfstock} (100%) rename data/stockpiles/{finishedgoodsprefix.dfstock => category_finished_goods.dfstock} (100%) rename data/stockpiles/{foodprefix.dfstock => category_food.dfstock} (100%) rename data/stockpiles/{furnitureprefix.dfstock => category_furniture_siege_ammo.dfstock} (100%) rename data/stockpiles/{gemsprefix.dfstock => category_gems.dfstock} (100%) rename data/stockpiles/{leatherprefix.dfstock => category_leather.dfstock} (100%) rename data/stockpiles/{refuse.dfstock => category_refuse.dfstock} (100%) rename data/stockpiles/{sheetprefix.dfstock => category_sheet.dfstock} (100%) rename data/stockpiles/{stone.dfstock => category_stone.dfstock} (100%) rename data/stockpiles/{weaponsprefix.dfstock => category_weapons_trap_components.dfstock} (100%) diff --git a/data/stockpiles/ammo.dfstock b/data/stockpiles/category_ammo.dfstock similarity index 100% rename from data/stockpiles/ammo.dfstock rename to data/stockpiles/category_ammo.dfstock diff --git a/data/stockpiles/animalsprefix.dfstock b/data/stockpiles/category_animals.dfstock similarity index 100% rename from data/stockpiles/animalsprefix.dfstock rename to data/stockpiles/category_animals.dfstock diff --git a/data/stockpiles/armorprefix.dfstock b/data/stockpiles/category_armor.dfstock similarity index 100% rename from data/stockpiles/armorprefix.dfstock rename to data/stockpiles/category_armor.dfstock diff --git a/data/stockpiles/barsprefix.dfstock b/data/stockpiles/category_bars_blocks.dfstock similarity index 100% rename from data/stockpiles/barsprefix.dfstock rename to data/stockpiles/category_bars_blocks.dfstock diff --git a/data/stockpiles/clothprefix.dfstock b/data/stockpiles/category_cloth.dfstock similarity index 100% rename from data/stockpiles/clothprefix.dfstock rename to data/stockpiles/category_cloth.dfstock diff --git a/data/stockpiles/coinsprefix.dfstock b/data/stockpiles/category_coins.dfstock similarity index 100% rename from data/stockpiles/coinsprefix.dfstock rename to data/stockpiles/category_coins.dfstock diff --git a/data/stockpiles/corpsesprefix.dfstock b/data/stockpiles/category_corpses.dfstock similarity index 100% rename from data/stockpiles/corpsesprefix.dfstock rename to data/stockpiles/category_corpses.dfstock diff --git a/data/stockpiles/finishedgoodsprefix.dfstock b/data/stockpiles/category_finished_goods.dfstock similarity index 100% rename from data/stockpiles/finishedgoodsprefix.dfstock rename to data/stockpiles/category_finished_goods.dfstock diff --git a/data/stockpiles/foodprefix.dfstock b/data/stockpiles/category_food.dfstock similarity index 100% rename from data/stockpiles/foodprefix.dfstock rename to data/stockpiles/category_food.dfstock diff --git a/data/stockpiles/furnitureprefix.dfstock b/data/stockpiles/category_furniture_siege_ammo.dfstock similarity index 100% rename from data/stockpiles/furnitureprefix.dfstock rename to data/stockpiles/category_furniture_siege_ammo.dfstock diff --git a/data/stockpiles/gemsprefix.dfstock b/data/stockpiles/category_gems.dfstock similarity index 100% rename from data/stockpiles/gemsprefix.dfstock rename to data/stockpiles/category_gems.dfstock diff --git a/data/stockpiles/leatherprefix.dfstock b/data/stockpiles/category_leather.dfstock similarity index 100% rename from data/stockpiles/leatherprefix.dfstock rename to data/stockpiles/category_leather.dfstock diff --git a/data/stockpiles/refuse.dfstock b/data/stockpiles/category_refuse.dfstock similarity index 100% rename from data/stockpiles/refuse.dfstock rename to data/stockpiles/category_refuse.dfstock diff --git a/data/stockpiles/sheetprefix.dfstock b/data/stockpiles/category_sheet.dfstock similarity index 100% rename from data/stockpiles/sheetprefix.dfstock rename to data/stockpiles/category_sheet.dfstock diff --git a/data/stockpiles/stone.dfstock b/data/stockpiles/category_stone.dfstock similarity index 100% rename from data/stockpiles/stone.dfstock rename to data/stockpiles/category_stone.dfstock diff --git a/data/stockpiles/weaponsprefix.dfstock b/data/stockpiles/category_weapons_trap_components.dfstock similarity index 100% rename from data/stockpiles/weaponsprefix.dfstock rename to data/stockpiles/category_weapons_trap_components.dfstock From 3f0a93a9b117c8e9f76f79cecd470795f1d1be61 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 18:04:46 -0700 Subject: [PATCH 0857/2222] document my aspirations --- docs/plugins/stockpiles.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 7ca0c5039..c0b697c4b 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -53,10 +53,39 @@ Examples Options ------- +``-i``, ``--include `` + When exporting, you can include this option to select only specific elements + of the stockpile to record. If not specified, everything is included. When + the file is later imported, only the included settings will be modified. The + options are explained below in the next section. +``-d``, ``--disable`` + When importing, treat the settings in the file as elements to *remove** from + the current stockpile configuration. Elements that are enabled in the file + will be *disabled* on the stockpile. No other stockpile configuration will + be changed. +``-e``, ``--enable`` + When importing, treat the settings in the file as elements to *add* to the + current stockpile configuration. Elements that are enabled in the file will + be enabled on the stockpile, but nothing currently enabled on the stockpile + will be disabled. ``-s``, ``--stockpile `` Specify a specific stockpile ID instead of using the one currently selected in the UI. +Configuration elements +---------------------- + +The different configuration elements you can include in an exported settings file +are: + +:general: Max bins, barrels, and wheelbarrows; whether the stockpile takes from + links only; whether organic and/or inorganic materials are allowed. +:categories: The top-level categories of items that are enabled for the stockpile, + like Ammo, Finished goods, or Stone. +:types: The elements below the categories, which include the sub-categories, the + specific item types, and any toggles the category might have (like Prepared + meals for the Food category). + .. _stockpiles-library: The stockpiles settings library From 5a317820f96e19d32f84869b6c96f16b314c1e3c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 15 Mar 2023 21:54:48 -0700 Subject: [PATCH 0858/2222] new logic for deserialization --- docs/about/Removed.rst | 7 + docs/plugins/stockpiles.rst | 34 +- plugins/lua/stockpiles.lua | 52 +- plugins/stockpiles/StockpileSerializer.cpp | 1213 ++++++++++++-------- plugins/stockpiles/StockpileSerializer.h | 264 ++--- plugins/stockpiles/proto/stockpiles.proto | 72 +- plugins/stockpiles/stockpiles.cpp | 14 +- 7 files changed, 972 insertions(+), 684 deletions(-) diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 01669fac4..4a8cdbccf 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -193,6 +193,13 @@ show-unit-syndromes =================== Replaced with a GUI version: `gui/unit-syndromes`. +.. _stocksettings: + +stocksettings +============= +Along with ``copystock``, ``loadstock`` and ``savestock``, replaced with the new +`stockpiles` API. + .. _warn-stuck-trees: warn-stuck-trees diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index c0b697c4b..83cfada02 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -53,24 +53,23 @@ Examples Options ------- +``-s``, ``--stockpile `` + Specify a specific stockpile ID instead of using the one currently selected + in the UI. ``-i``, ``--include `` When exporting, you can include this option to select only specific elements of the stockpile to record. If not specified, everything is included. When the file is later imported, only the included settings will be modified. The options are explained below in the next section. -``-d``, ``--disable`` - When importing, treat the settings in the file as elements to *remove** from - the current stockpile configuration. Elements that are enabled in the file - will be *disabled* on the stockpile. No other stockpile configuration will - be changed. -``-e``, ``--enable`` - When importing, treat the settings in the file as elements to *add* to the - current stockpile configuration. Elements that are enabled in the file will - be enabled on the stockpile, but nothing currently enabled on the stockpile - will be disabled. -``-s``, ``--stockpile `` - Specify a specific stockpile ID instead of using the one currently selected - in the UI. +``-m``, ``--mode (set|enable|disable)`` + When importing, choose the algorithm used to apply the settings. In ``set`` + mode (the default), the stockpile is cleared and the settings in the file + are enabled. In ``enable`` mode, enabled settings in the file are *added* + to the stockpile, but no other settings are changed. In ``disable`` mode, + enabled settings in the file are *removed* from the current stockpile + configuration, and nothing else is changed. +``-f``, ``--filter `` + When importing, only modify the settings that contain the given substring. Configuration elements ---------------------- @@ -78,10 +77,11 @@ Configuration elements The different configuration elements you can include in an exported settings file are: -:general: Max bins, barrels, and wheelbarrows; whether the stockpile takes from - links only; whether organic and/or inorganic materials are allowed. -:categories: The top-level categories of items that are enabled for the stockpile, - like Ammo, Finished goods, or Stone. +:containers: Max bins, max barrels, and num wheelbarrows. +:general: Whether the stockpile takes from links only and whether organic + and/or inorganic materials are allowed. +:categories: The top-level categories of items that are enabled for the + stockpile, like Ammo, Finished goods, or Stone. :types: The elements below the categories, which include the sub-categories, the specific item types, and any toggles the category might have (like Prepared meals for the Food category). diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 723ff97f6..6770c044e 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -74,10 +74,25 @@ local function get_sp_id(opts) return nil end +local included_elements = { + containers=1, + general=2, + categories=4, + types=8, +} + local function export_stockpile(name, opts) assert_safe_name(name) name = STOCKPILES_DIR .. '/' .. name - stockpiles_export(name, get_sp_id(opts)) + + local includedElements = 0 + for _,inc in ipairs(opts.includes) do + if included_elements[inc] then + includedElements = includedElements | included_elements[inc] + end + end + + stockpiles_export(name, get_sp_id(opts), includedElements) end local function import_stockpile(name, opts) @@ -92,7 +107,28 @@ local function import_stockpile(name, opts) else name = STOCKPILES_LIBRARY_DIR .. '/' .. name end - stockpiles_import(name, get_sp_id(opts)) + stockpiles_import(name, get_sp_id(opts), opts.mode, opts.filter) +end + +local valid_includes = {general=true, categories=true, types=true} + +local function parse_include(arg) + local includes = argparse.stringList(arg, 'include') + for _,v in ipairs(includes) do + if not valid_includes[v] then + qerror(('invalid included element: "%s"'):format(v)) + end + end + return includes +end + +local valid_modes = {set=true, enable=true, disable=true} + +local function parse_mode(arg) + if not valid_modes[arg] then + qerror(('invalid mode: "%s"'):format(arg)) + end + return arg end local function process_args(opts, args) @@ -101,10 +137,20 @@ local function process_args(opts, args) return end + opts.includes = {} + opts.mode = 'set' + opts.filter = '' + return argparse.processArgsGetopt(args, { + {'f', 'filter', has_arg=true, + handler=function(arg) opts.filter = arg end}, {'h', 'help', handler=function() opts.help = true end}, + {'i', 'include', has_arg=true, + handler=function(arg) opts.includes = parse_include(arg) end}, + {'m', 'mode', has_arg=true, + handler=function(arg) opts.mode = parse_mode(arg) end}, {'s', 'stockpile', has_arg=true, - handler=function(arg) opts.id = argparse.nonnegativeInt(art, 'stockpile') end}, + handler=function(arg) opts.id = argparse.nonnegativeInt(arg, 'stockpile') end}, }) end diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 65feb2711..04e7cb9fa 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -44,23 +44,121 @@ namespace DFHack { DBG_EXTERN(stockpiles, log); } -StockpileSerializer::StockpileSerializer(df::building_stockpilest* stockpile) - : mPile(stockpile) { +static struct OtherMatsFurniture { + const std::map mats; + + OtherMatsFurniture() : mats(getMats()) {} + + std::map getMats() { + std::map ret; + ret.emplace(0, "WOOD"); + ret.emplace(1, "PLANT_CLOTH"); + ret.emplace(2, "BONE"); + ret.emplace(3, "TOOTH"); + ret.emplace(4, "HORN"); + ret.emplace(5, "PEARL"); + ret.emplace(6, "SHELL"); + ret.emplace(7, "LEATHER"); + ret.emplace(8, "SILK"); + ret.emplace(9, "AMBER"); + ret.emplace(10, "CORAL"); + ret.emplace(11, "GREEN_GLASS"); + ret.emplace(12, "CLEAR_GLASS"); + ret.emplace(13, "CRYSTAL_GLASS"); + ret.emplace(14, "YARN"); + return ret; + } +} mOtherMatsFurniture; + +static struct OtherMatsBars { + const std::map mats; + + OtherMatsBars() : mats(getMats()) {} + + std::map getMats() { + std::map ret; + ret.emplace(0, "COAL"); + ret.emplace(1, "POTASH"); + ret.emplace(2, "ASH"); + ret.emplace(3, "PEARLASH"); + ret.emplace(4, "SOAP"); + return ret; + } +} mOtherMatsBars; + +static struct OtherMatsBlocks { + const std::map mats; + + OtherMatsBlocks() : mats(getMats()) {} + + std::map getMats() { + std::map ret; + ret.emplace(0, "GREEN_GLASS"); + ret.emplace(1, "CLEAR_GLASS"); + ret.emplace(2, "CRYSTAL_GLASS"); + ret.emplace(3, "WOOD"); + return ret; + } +} mOtherMatsBlocks; + +static struct OtherMatsFinishedGoods { + const std::map mats; + + OtherMatsFinishedGoods() : mats(getMats()) {} + + std::map getMats() { + std::map ret; + ret.emplace(0, "WOOD"); + ret.emplace(1, "PLANT_CLOTH"); + ret.emplace(2, "BONE"); + ret.emplace(3, "TOOTH"); + ret.emplace(4, "HORN"); + ret.emplace(5, "PEARL"); + ret.emplace(6, "SHELL"); + ret.emplace(7, "LEATHER"); + ret.emplace(8, "SILK"); + ret.emplace(9, "AMBER"); + ret.emplace(10, "CORAL"); + ret.emplace(11, "GREEN_GLASS"); + ret.emplace(12, "CLEAR_GLASS"); + ret.emplace(13, "CRYSTAL_GLASS"); + ret.emplace(14, "YARN"); + ret.emplace(15, "WAX"); + return ret; + } +} mOtherMatsFinishedGoods; + +static struct OtherMatsWeaponsArmor { + const std::map mats; + + OtherMatsWeaponsArmor() : mats(getMats()) {} + + std::map getMats() { + std::map ret; + ret.emplace(0, "WOOD"); + ret.emplace(1, "PLANT_CLOTH"); + ret.emplace(2, "BONE"); + ret.emplace(3, "SHELL"); + ret.emplace(4, "LEATHER"); + ret.emplace(5, "SILK"); + ret.emplace(6, "GREEN_GLASS"); + ret.emplace(7, "CLEAR_GLASS"); + ret.emplace(8, "CRYSTAL_GLASS"); + ret.emplace(9, "YARN"); + return ret; + } +} mOtherMatsWeaponsArmor; - // build other_mats indices - furniture_setup_other_mats(); - bars_blocks_setup_other_mats(); - finished_goods_setup_other_mats(); - weapons_armor_setup_other_mats(); -} +StockpileSerializer::StockpileSerializer(df::building_stockpilest* stockpile) + : mPile(stockpile) { } StockpileSerializer::~StockpileSerializer() { } -bool StockpileSerializer::serialize_to_ostream(std::ostream* output) { +bool StockpileSerializer::serialize_to_ostream(std::ostream* output, uint32_t includedElements) { if (output->fail()) return false; mBuffer.Clear(); - write(); + write(includedElements); { io::OstreamOutputStream zero_copy_output(output); if (!mBuffer.SerializeToZeroCopyStream(&zero_copy_output)) @@ -69,109 +167,246 @@ bool StockpileSerializer::serialize_to_ostream(std::ostream* output) { return output->good(); } -bool StockpileSerializer::serialize_to_file(const std::string& file) { +bool StockpileSerializer::serialize_to_file(const std::string& file, uint32_t includedElements) { std::fstream output(file, std::ios::out | std::ios::binary | std::ios::trunc); if (output.fail()) { - WARN(log).print("ERROR: failed to open file for writing: '%s'\n", file.c_str()); + WARN(log).print("ERROR: failed to open file for writing: '%s'\n", + file.c_str()); return false; } - return serialize_to_ostream(&output); + return serialize_to_ostream(&output, includedElements); } -bool StockpileSerializer::parse_from_istream(std::istream* input) { +bool StockpileSerializer::parse_from_istream(std::istream* input, DeserializeMode mode, const std::string& filter) { if (input->fail()) return false; mBuffer.Clear(); io::IstreamInputStream zero_copy_input(input); - const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); + const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) + && input->eof(); if (res) - read(); + read(mode, filter); return res; } -bool StockpileSerializer::unserialize_from_file(const std::string& file) { +bool StockpileSerializer::unserialize_from_file(const std::string& file, DeserializeMode mode, const string& filter) { std::fstream input(file, std::ios::in | std::ios::binary); if (input.fail()) { - WARN(log).print("failed to open file for reading: '%s'\n", file.c_str()); + WARN(log).print("failed to open file for reading: '%s'\n", + file.c_str()); return false; } - return parse_from_istream(&input); -} - -void StockpileSerializer::write() { - DEBUG(log).print("GROUP SET %s\n", bitfield_to_string(mPile->settings.flags).c_str()); - write_general(); - if (mPile->settings.flags.bits.animals) - write_animals(); - if (mPile->settings.flags.bits.food) - write_food(); - if (mPile->settings.flags.bits.furniture) - write_furniture(); - if (mPile->settings.flags.bits.refuse) - write_refuse(); - if (mPile->settings.flags.bits.stone) - write_stone(); - if (mPile->settings.flags.bits.ammo) - write_ammo(); - if (mPile->settings.flags.bits.coins) - write_coins(); - if (mPile->settings.flags.bits.bars_blocks) - write_bars_blocks(); - if (mPile->settings.flags.bits.gems) - write_gems(); - if (mPile->settings.flags.bits.finished_goods) - write_finished_goods(); - if (mPile->settings.flags.bits.leather) - write_leather(); - if (mPile->settings.flags.bits.cloth) - write_cloth(); - if (mPile->settings.flags.bits.wood) - write_wood(); - if (mPile->settings.flags.bits.weapons) - write_weapons(); - if (mPile->settings.flags.bits.armor) - write_armor(); -} - -void StockpileSerializer::read() { - DEBUG(log).print("==READ==\n"); - read_general(); - read_animals(); - read_food(); - read_furniture(); - read_refuse(); - read_stone(); - read_ammo(); - read_coins(); - read_bars_blocks(); - read_gems(); - read_finished_goods(); - read_leather(); - read_cloth(); - read_wood(); - read_weapons(); - read_armor(); -} - -void StockpileSerializer::serialize_list_organic_mat(FuncWriteExport add_value, const std::vector* list, organic_mat_category::organic_mat_category cat) { + return parse_from_istream(&input, mode, filter); +} + +/** + * Find an enum's value based off the string label. + * @param traits the enum's trait struct + * @param token the string value in key_table + * @return the enum's value, -1 if not found + */ +template +static typename df::enum_traits::base_type linear_index(df::enum_traits traits, const std::string& token) { + auto j = traits.first_item_value; + auto limit = traits.last_item_value; + // sometimes enums start at -1, which is bad news for array indexing + if (j < 0) { + j += abs(traits.first_item_value); + limit += abs(traits.first_item_value); + } + for (; j <= limit; ++j) { + if (token.compare(traits.key_table[j]) == 0) + return j; + } + return -1; +} + +/** + * There are many repeated (un)serialization cases throughout the stockpile_settings structure, + * so the most common cases have been generalized into generic functions using lambdas. + * + * The basic process to serialize a stockpile_settings structure is: + * 1. loop through the list + * 2. for every element that is TRUE: + * 3. map the specific stockpile_settings index into a general material, creature, etc index + * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) + * 5. add it to the protobuf using FuncWriteExport + * + * The unserialization process is the same in reverse. + */ +static bool serialize_list_itemdef(FuncWriteExport add_value, + std::vector list, + std::vector items, + item_type::item_type type) { + bool all = true; + for (size_t i = 0; i < list.size(); ++i) { + if (!list.at(i)) { + all = false; + continue; + } + const df::itemdef* a = items.at(i); + // skip procedurally generated items + if (a->base_flags.is_set(df::itemdef_flags::GENERATED)) + continue; + ItemTypeInfo ii; + if (!ii.decode(type, i)) + continue; + add_value(ii.getToken()); + DEBUG(log).print("itemdef type %zd is %s\n", i, ii.getToken().c_str()); + } + return all; +} + +static void unserialize_list_itemdef(FuncReadImport read_value, + int32_t list_size, + std::vector* pile_list, + item_type::item_type type) { + pile_list->clear(); + pile_list->resize(Items::getSubtypeCount(type), '\0'); + for (int i = 0; i < list_size; ++i) { + std::string token = read_value(i); + ItemTypeInfo ii; + if (!ii.find(token)) + continue; + DEBUG(log).print("itemdef %d is %s\n", ii.subtype, token.c_str()); + if (size_t(ii.subtype) >= pile_list->size()) { + WARN(log).print("itemdef index too large! idx[%d] max_size[%zd]\n", ii.subtype, pile_list->size()); + continue; + } + pile_list->at(ii.subtype) = 1; + } +} + +static bool serialize_list_quality(FuncWriteExport add_value, + const bool(&quality_list)[7]) { + using df::enums::item_quality::item_quality; + using quality_traits = df::enum_traits; + + bool all = true; + for (size_t i = 0; i < 7; ++i) { + if (!quality_list[i]) { + all = false; + continue; + } + const std::string f_type(quality_traits::key_table[i]); + add_value(f_type); + DEBUG(log).print("quality: %zd is %s\n", i, f_type.c_str()); + } + return all; +} + +static void quality_clear(bool(&pile_list)[7]) { + std::fill(pile_list, pile_list + 7, false); +} + +static void unserialize_list_quality(FuncReadImport read_value, + int32_t list_size, + bool(&pile_list)[7]) { + quality_clear(pile_list); + if (list_size > 0 && list_size <= 7) { + using df::enums::item_quality::item_quality; + df::enum_traits quality_traits; + for (int i = 0; i < list_size; ++i) { + const std::string quality = read_value(i); + df::enum_traits::base_type idx = linear_index(quality_traits, quality); + if (idx < 0) { + WARN(log).print("invalid quality token: %s\n", quality.c_str()); + continue; + } + DEBUG(log).print("quality: %d is %s\n", idx, quality.c_str()); + pile_list[idx] = true; + } + } +} + +static string other_mats_index(const std::map other_mats, + int idx) { + auto it = other_mats.find(idx); + if (it == other_mats.end()) + return std::string(); + return it->second; +} + +static int other_mats_token(const std::map other_mats, + const std::string& token) { + for (auto it = other_mats.begin(); it != other_mats.end(); ++it) { + if (it->second == token) + return it->first; + } + return -1; +} + +static bool serialize_list_other_mats( + const std::map other_mats, + FuncWriteExport add_value, + std::vector list) { + bool all = true; + for (size_t i = 0; i < list.size(); ++i) { + if (!list.at(i)) { + all = false; + continue; + } + const std::string token = other_mats_index(other_mats, i); + if (token.empty()) { + WARN(log).print("invalid other material with index %zd\n", i); + continue; + } + add_value(token); + DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); + } + return all; +} + +static void unserialize_list_other_mats( + const std::map other_mats, + FuncReadImport read_value, + int32_t list_size, + std::vector* pile_list) { + pile_list->clear(); + pile_list->resize(other_mats.size(), '\0'); + for (int i = 0; i < list_size; ++i) { + const std::string token = read_value(i); + size_t idx = other_mats_token(other_mats, token); + if (idx < 0) { + WARN(log).print("invalid other mat with token %s\n", token.c_str()); + continue; + } + DEBUG(log).print("other_mats %zd is %s\n", idx, token.c_str()); + if (idx >= pile_list->size()) { + WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, pile_list->size()); + continue; + } + pile_list->at(idx) = 1; + } +} + +static bool serialize_list_organic_mat(FuncWriteExport add_value, + const std::vector* list, + organic_mat_category::organic_mat_category cat) { + bool all = true; if (!list) { DEBUG(log).print("serialize_list_organic_mat: list null\n"); - return; + return all; } for (size_t i = 0; i < list->size(); ++i) { - if (list->at(i)) { - std::string token = OrganicMatLookup::food_token_by_idx(cat, i); - if (token.empty()) { - DEBUG(log).print("food mat invalid :(\n"); - continue; - } - DEBUG(log).print("organic_material %zd is %s\n", i, token.c_str()); - add_value(token); + if (!list->at(i)) { + all = false; + continue; + } + std::string token = OrganicMatLookup::food_token_by_idx(cat, i); + if (token.empty()) { + DEBUG(log).print("food mat invalid :(\n"); + continue; } + DEBUG(log).print("organic_material %zd is %s\n", i, token.c_str()); + add_value(token); } + return all; } -void StockpileSerializer::unserialize_list_organic_mat(FuncReadImport get_value, size_t list_size, std::vector* pile_list, organic_mat_category::organic_mat_category cat) { +static void unserialize_list_organic_mat(FuncReadImport get_value, + size_t list_size, std::vector* pile_list, + organic_mat_category::organic_mat_category cat) { pile_list->clear(); pile_list->resize(OrganicMatLookup::food_max_size(cat), '\0'); for (size_t i = 0; i < list_size; ++i) { @@ -186,14 +421,21 @@ void StockpileSerializer::unserialize_list_organic_mat(FuncReadImport get_value, } } -void StockpileSerializer::serialize_list_item_type(FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector& list) { +static bool serialize_list_item_type(FuncItemAllowed is_allowed, + FuncWriteExport add_value, const std::vector& list) { using df::enums::item_type::item_type; using type_traits = df::enum_traits; + + bool all = true; size_t num_item_types = list.size(); - DEBUG(log).print("item_type size = %zd size limit = %d typecasted: %zd\n", num_item_types, type_traits::last_item_value, (size_t)type_traits::last_item_value); + DEBUG(log).print("item_type size = %zd size limit = %d typecasted: %zd\n", + num_item_types, type_traits::last_item_value, + (size_t)type_traits::last_item_value); for (size_t i = 0; i <= (size_t)type_traits::last_item_value; ++i) { - if (i < num_item_types && !list.at(i)) + if (i < num_item_types && !list.at(i)) { + all = false; continue; + } const item_type type = (item_type)((df::enum_traits::base_type)i); std::string r_type(type_traits::key_table[i + 1]); if (!is_allowed(type)) @@ -201,9 +443,12 @@ void StockpileSerializer::serialize_list_item_type(FuncItemAllowed is_allowed, F add_value(r_type); DEBUG(log).print("item_type key_table[%zd] type[%d] is %s\n", i + 1, (int16_t)type, r_type.c_str()); } + return all; } -void StockpileSerializer::unserialize_list_item_type(FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { +static void unserialize_list_item_type(FuncItemAllowed is_allowed, + FuncReadImport read_value, int32_t list_size, + std::vector* pile_list) { pile_list->clear(); pile_list->resize(112, '\0'); // TODO remove hardcoded list size value for (size_t i = 0; i < pile_list->size(); ++i) { @@ -227,20 +472,27 @@ void StockpileSerializer::unserialize_list_item_type(FuncItemAllowed is_allowed, } } -void StockpileSerializer::serialize_list_material(FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector& list) { +static bool serialize_list_material(FuncMaterialAllowed is_allowed, + FuncWriteExport add_value, const std::vector& list) { + bool all = true; MaterialInfo mi; for (size_t i = 0; i < list.size(); ++i) { - if (list.at(i)) { - mi.decode(0, i); - if (!is_allowed(mi)) - continue; - DEBUG(log).print("material %zd is %s\n", i, mi.getToken().c_str()); - add_value(mi.getToken()); + if (!list.at(i)) { + all = false; + continue; } + mi.decode(0, i); + if (!is_allowed(mi)) + continue; + DEBUG(log).print("material %zd is %s\n", i, mi.getToken().c_str()); + add_value(mi.getToken()); } + return all; } -void StockpileSerializer::unserialize_list_material(FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { +static void unserialize_list_material(FuncMaterialAllowed is_allowed, + FuncReadImport read_value, int32_t list_size, + std::vector* pile_list) { // we initialize all possible (allowed) values to 0, // then all other not-allowed values to 1 // why? because that's how the memory is in DF before @@ -267,150 +519,346 @@ void StockpileSerializer::unserialize_list_material(FuncMaterialAllowed is_allow } } -void StockpileSerializer::serialize_list_quality(FuncWriteExport add_value, const bool(&quality_list)[7]) { - using df::enums::item_quality::item_quality; - using quality_traits = df::enum_traits; - for (size_t i = 0; i < 7; ++i) { - if (quality_list[i]) { - const std::string f_type(quality_traits::key_table[i]); - add_value(f_type); - DEBUG(log).print("quality: %zd is %s\n", i, f_type.c_str()); - } +template +static void write_cat(bool include_types, uint32_t cat_flags, + enum df::stockpile_group_set::Mask cat_mask, + std::function mutable_cat_fn, + std::function write_cat_fn) { + if (!(cat_flags & cat_mask)) + return; + + T_cat_set* cat_set = mutable_cat_fn(); + if (!include_types) { + cat_set->set_all(true); + return; } -} -void StockpileSerializer::quality_clear(bool(&pile_list)[7]) { - std::fill(pile_list, pile_list + 7, false); + if (write_cat_fn(cat_set)) { + // all fields were set. might as well clear them and use the "all" flag + cat_set->Clear(); + cat_set->set_all(true); + } } -void StockpileSerializer::unserialize_list_quality(FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { - quality_clear(pile_list); - if (list_size > 0 && list_size <= 7) { - using df::enums::item_quality::item_quality; - df::enum_traits quality_traits; - for (int i = 0; i < list_size; ++i) { - const std::string quality = read_value(i); - df::enum_traits::base_type idx = linear_index(quality_traits, quality); - if (idx < 0) { - WARN(log).print("invalid quality token: %s\n", quality.c_str()); - continue; - } - DEBUG(log).print("quality: %d is %s\n", idx, quality.c_str()); - pile_list[idx] = true; - } +void StockpileSerializer::write(uint32_t includedElements) { + if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) + write_containers(); + if (includedElements & INCLUDED_ELEMENTS_GENERAL) + write_general(); + + if (!(includedElements & INCLUDED_ELEMENTS_CATEGORIES)) + return; + + DEBUG(log).print("GROUP SET %s\n", + bitfield_to_string(mPile->settings.flags).c_str()); + + bool include_types = 0 != (includedElements & INCLUDED_ELEMENTS_TYPES); + + write_cat(include_types, mPile->settings.flags.whole, + mPile->settings.flags.mask_ammo, + std::bind(&StockpileSettings::mutable_ammo, mBuffer), + std::bind(&StockpileSerializer::write_ammo, this, _1)); + + if (mPile->settings.flags.bits.animals) { + StockpileSettings::AnimalsSet* animals = mBuffer.mutable_animals(); + if (include_types) write_animals(animals); else animals->set_all(true); + } + if (mPile->settings.flags.bits.armor) { + StockpileSettings::ArmorSet* armor = mBuffer.mutable_armor(); + if (include_types) write_armor(armor); else armor->set_all(true); + } + if (mPile->settings.flags.bits.bars_blocks) { + StockpileSettings::BarsBlocksSet* bars_blocks = mBuffer.mutable_barsblocks(); + if (include_types) write_bars_blocks(bars_blocks); else bars_blocks->set_all(true); + } + if (mPile->settings.flags.bits.cloth) { + StockpileSettings::ClothSet* cloth = mBuffer.mutable_cloth(); + if (include_types) write_cloth(cloth); else cloth->set_all(true); + } + if (mPile->settings.flags.bits.coins) { + StockpileSettings::CoinSet* coins = mBuffer.mutable_coin(); + if (include_types) write_coins(coins); else coins->set_all(true); + } + if (mPile->settings.flags.bits.finished_goods) { + StockpileSettings::FinishedGoodsSet* finished_goods = mBuffer.mutable_finished_goods(); + if (include_types) write_finished_goods(finished_goods); else finished_goods->set_all(true); + } + if (mPile->settings.flags.bits.food) { + StockpileSettings::FoodSet* food = mBuffer.mutable_food(); + if (include_types) write_food(food); else food->set_all(true); + } + if (mPile->settings.flags.bits.furniture) { + StockpileSettings::FurnitureSet* furniture = mBuffer.mutable_furniture(); + if (include_types) write_furniture(furniture); else furniture->set_all(true); + } + if (mPile->settings.flags.bits.gems) { + StockpileSettings::GemsSet* gems = mBuffer.mutable_gems(); + if (include_types) write_gems(gems); else gems->set_all(true); + } + if (mPile->settings.flags.bits.leather) { + StockpileSettings::LeatherSet* leather = mBuffer.mutable_leather(); + if (include_types) write_leather(leather); else leather->set_all(true); + } + if (mPile->settings.flags.bits.corpses) { + StockpileSettings::CorpsesSet* corpses = mBuffer.mutable_corpses_v50(); + if (include_types) write_corpses(corpses); else corpses->set_all(true); + } + if (mPile->settings.flags.bits.refuse) { + StockpileSettings::RefuseSet* refuse = mBuffer.mutable_refuse(); + if (include_types) write_refuse(refuse); else refuse->set_all(true); + } + if (mPile->settings.flags.bits.sheet) { + StockpileSettings::SheetSet* sheet = mBuffer.mutable_sheet(); + if (include_types) write_sheet(sheet); else sheet->set_all(true); + } + if (mPile->settings.flags.bits.stone) { + StockpileSettings::StoneSet* stone = mBuffer.mutable_stone(); + if (include_types) write_stone(stone); else stone->set_all(true); + } + if (mPile->settings.flags.bits.weapons) { + StockpileSettings::WeaponsSet* weapons = mBuffer.mutable_weapons(); + if (include_types) write_weapons(weapons); else weapons->set_all(true); + } + if (mPile->settings.flags.bits.wood) { + StockpileSettings::WoodSet* wood = mBuffer.mutable_wood(); + if (include_types) write_wood(wood); else wood->set_all(true); } } -void StockpileSerializer::serialize_list_other_mats(const std::map other_mats, FuncWriteExport add_value, std::vector list) { - for (size_t i = 0; i < list.size(); ++i) { - if (list.at(i)) { - const std::string token = other_mats_index(other_mats, i); - if (token.empty()) { - WARN(log).print("invalid other material with index %zd\n", i); - continue; - } - add_value(token); - DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); - } - } +void StockpileSerializer::read(DeserializeMode mode, const std::string& filter) { + DEBUG(log).print("==READ==\n"); + read_containers(mode); + read_general(mode); + read_ammo(mode, filter); + read_animals(mode, filter); + read_armor(mode, filter); + read_bars_blocks(mode, filter); + read_cloth(mode, filter); + read_coins(mode, filter); + read_finished_goods(mode, filter); + read_food(mode, filter); + read_furniture(mode, filter); + read_gems(mode, filter); + read_leather(mode, filter); + + // support for old versions before corpses had a set + if (mBuffer.has_corpses()) { + StockpileSettings::CorpsesSet* corpses = mBuffer.mutable_corpses_v50(); + corpses->set_all(true); + } + read_corpses(mode, filter); + + read_refuse(mode, filter); + read_sheet(mode, filter); + read_stone(mode, filter); + read_weapons(mode, filter); + read_wood(mode, filter); +} + +void StockpileSerializer::write_containers() { + mBuffer.set_max_bins(mPile->max_bins); + mBuffer.set_max_barrels(mPile->max_barrels); + mBuffer.set_max_wheelbarrows(mPile->max_wheelbarrows); } -void StockpileSerializer::unserialize_list_other_mats(const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector* pile_list) { - pile_list->clear(); - pile_list->resize(other_mats.size(), '\0'); - for (int i = 0; i < list_size; ++i) { - const std::string token = read_value(i); - size_t idx = other_mats_token(other_mats, token); - if (idx < 0) { - WARN(log).print("invalid other mat with token %s\n", token.c_str()); - continue; - } - DEBUG(log).print("other_mats %zd is %s\n", idx, token.c_str()); - if (idx >= pile_list->size()) { - WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, pile_list->size()); - continue; - } - pile_list->at(idx) = 1; +template +static void read_elem(const char *name, DeserializeMode mode, + std::function has_elem_fn, + std::function elem_fn, + T_elem &setting) { + bool has_elem = has_elem_fn(); + bool is_set = has_elem && elem_fn() != 0; + bool just_disable = is_set && mode == DESERIALIZE_MODE_DISABLE; + + if (mode == DESERIALIZE_MODE_SET || just_disable) { + DEBUG(log).print("clearing %s\n", name); + setting = 0; } -} -void StockpileSerializer::serialize_list_itemdef(FuncWriteExport add_value, std::vector list, std::vector items, item_type::item_type type) { - for (size_t i = 0; i < list.size(); ++i) { - if (list.at(i)) { - const df::itemdef* a = items.at(i); - // skip procedurally generated items - if (a->base_flags.is_set(df::itemdef_flags::GENERATED)) - continue; - ItemTypeInfo ii; - if (!ii.decode(type, i)) - continue; - add_value(ii.getToken()); - DEBUG(log).print("itemdef type %zd is %s\n", i, ii.getToken().c_str()); - } + if (!has_elem || just_disable) + return; + + if (mode == DESERIALIZE_MODE_SET || is_set) { + T_elem val = (mode == DESERIALIZE_MODE_DISABLE) ? 0 : elem_fn(); + DEBUG(log).print("setting %s=%d\n", name, val); + setting = val; } } -void StockpileSerializer::unserialize_list_itemdef(FuncReadImport read_value, int32_t list_size, std::vector* pile_list, item_type::item_type type) { - pile_list->clear(); - pile_list->resize(Items::getSubtypeCount(type), '\0'); - for (int i = 0; i < list_size; ++i) { - std::string token = read_value(i); - ItemTypeInfo ii; - if (!ii.find(token)) - continue; - DEBUG(log).print("itemdef %d is %s\n", ii.subtype, token.c_str()); - if (size_t(ii.subtype) >= pile_list->size()) { - WARN(log).print("itemdef index too large! idx[%d] max_size[%zd]\n", ii.subtype, pile_list->size()); - continue; - } - pile_list->at(ii.subtype) = 1; +template +static void read_category(const char *name, DeserializeMode mode, + std::function has_cat_fn, + std::function cat_fn, + uint32_t & cat_flags, + enum df::stockpile_group_set::Mask cat_mask, + std::function clear_fn, + std::function set_fn) { + bool has_cat = has_cat_fn(); + bool all = has_cat && cat_fn().has_all() && cat_fn().all(); + bool just_disable = all && mode == DESERIALIZE_MODE_DISABLE; + + if (mode == DESERIALIZE_MODE_SET || just_disable) { + DEBUG(log).print("clearing %s\n", name); + cat_flags &= ~cat_mask; + clear_fn(); } -} -std::string StockpileSerializer::other_mats_index(const std::map other_mats, int idx) { - auto it = other_mats.find(idx); - if (it == other_mats.end()) - return std::string(); - return it->second; + if (!has_cat || just_disable) + return; + + if (mode == DESERIALIZE_MODE_DISABLE && !(cat_flags & cat_mask)) + return; + + if (mode == DESERIALIZE_MODE_SET || mode == DESERIALIZE_MODE_ENABLE) + cat_flags |= cat_mask; + + bool force = mode == DESERIALIZE_MODE_SET || all; + char val = (mode == DESERIALIZE_MODE_DISABLE) ? (char)0 : (char)1; + DEBUG(log).print("setting %s %s elements to %d\n", + all ? "all" : "marked", name, val); + set_fn(force, val); } -int StockpileSerializer::other_mats_token(const std::map other_mats, const std::string& token) { - for (auto it = other_mats.begin(); it != other_mats.end(); ++it) { - if (it->second == token) - return it->first; - } - return -1; +void StockpileSerializer::read_containers(DeserializeMode mode) { + read_elem("max_bins", mode, + std::bind(&StockpileSettings::has_max_bins, mBuffer), + std::bind(&StockpileSettings::max_bins, mBuffer), + mPile->max_bins); + read_elem("max_barrels", mode, + std::bind(&StockpileSettings::has_max_barrels, mBuffer), + std::bind(&StockpileSettings::max_barrels, mBuffer), + mPile->max_barrels); + read_elem("max_wheelbarrows", mode, + std::bind(&StockpileSettings::has_max_wheelbarrows, mBuffer), + std::bind(&StockpileSettings::max_wheelbarrows, mBuffer), + mPile->max_wheelbarrows); } void StockpileSerializer::write_general() { - mBuffer.set_max_bins(mPile->max_bins); - mBuffer.set_max_wheelbarrows(mPile->max_wheelbarrows); - mBuffer.set_max_barrels(mPile->max_barrels); mBuffer.set_use_links_only(mPile->use_links_only); mBuffer.set_allow_inorganic(mPile->settings.allow_inorganic); mBuffer.set_allow_organic(mPile->settings.allow_organic); - mBuffer.set_corpses(mPile->settings.flags.bits.corpses); -} - -void StockpileSerializer::read_general() { - if (mBuffer.has_max_bins()) - mPile->max_bins = mBuffer.max_bins(); - if (mBuffer.has_max_wheelbarrows()) - mPile->max_wheelbarrows = mBuffer.max_wheelbarrows(); - if (mBuffer.has_max_barrels()) - mPile->max_barrels = mBuffer.max_barrels(); - if (mBuffer.has_use_links_only()) - mPile->use_links_only = mBuffer.use_links_only(); - if (mBuffer.has_allow_inorganic()) - mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); - if (mBuffer.has_allow_organic()) - mPile->settings.allow_organic = mBuffer.allow_organic(); - if (mBuffer.has_corpses()) - mPile->settings.flags.bits.corpses = mBuffer.corpses(); -} - -void StockpileSerializer::write_animals() { - StockpileSettings::AnimalsSet* animals = mBuffer.mutable_animals(); +} + +void StockpileSerializer::read_general(DeserializeMode mode) { + read_elem("use_links_only", mode, + std::bind(&StockpileSettings::has_use_links_only, mBuffer), + std::bind(&StockpileSettings::use_links_only, mBuffer), + mPile->use_links_only); + read_elem("allow_inorganic", mode, + std::bind(&StockpileSettings::has_allow_inorganic, mBuffer), + std::bind(&StockpileSettings::allow_inorganic, mBuffer), + mPile->settings.allow_inorganic); + read_elem("allow_organic", mode, + std::bind(&StockpileSettings::has_allow_organic, mBuffer), + std::bind(&StockpileSettings::allow_organic, mBuffer), + mPile->settings.allow_organic); +} + +static bool ammo_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); +} + +bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { + bool all = true; + + // ammo type + all = serialize_list_itemdef( + [=](const std::string& token) { ammo->add_type(token); }, + mPile->settings.ammo.type, + std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), + item_type::AMMO) && all; + + // metal + all = serialize_list_material( + ammo_mat_is_allowed, + [=](const std::string& token) { ammo->add_mats(token); }, + mPile->settings.ammo.mats) && all; + + // other mats - only wood and bone are expected + if (mPile->settings.ammo.other_mats.size() > 2) { + WARN(log).print("ammo other materials > 2: %zd\n", + mPile->settings.ammo.other_mats.size()); + } + + size_t num_other_mats = std::min(size_t(2), + mPile->settings.ammo.other_mats.size()); + for (size_t i = 0; i < num_other_mats; ++i) { + if (!mPile->settings.ammo.other_mats.at(i)) { + all = false; + continue; + } + const std::string token = i == 0 ? "WOOD" : "BONE"; + ammo->add_other_mats(token); + DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); + } + + // quality core + all = serialize_list_quality( + [=](const std::string& token) { ammo->add_quality_core(token); }, + mPile->settings.ammo.quality_core) && all; + + // quality total + all = serialize_list_quality( + [=](const std::string& token) { ammo->add_quality_total(token); }, + mPile->settings.ammo.quality_total) && all; + + return all; +} + +void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& filter) { + read_category("ammo", mode, + std::bind(&StockpileSettings::has_ammo, mBuffer), + std::bind(&StockpileSettings::ammo, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_ammo, + [&]() { + auto & pammo = mPile->settings.ammo; + pammo.type.clear(); + pammo.mats.clear(); + pammo.other_mats.clear(); + quality_clear(pammo.quality_core); + quality_clear(pammo.quality_total); + }, + [&](bool force, char val) { + auto & bammo = mBuffer.ammo(); + auto & pammo = mPile->settings.ammo; + // ammo type + unserialize_list_itemdef( + [=](const size_t& idx) -> const std::string& { return bammo.type(idx); }, + bammo.type_size(), &pammo.type, item_type::AMMO); + + // materials metals + unserialize_list_material(ammo_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, + bammo.mats_size(), &pammo.mats); + + // others + pammo.other_mats.clear(); + pammo.other_mats.resize(2, '\0'); + if (bammo.other_mats_size() > 0) { + // TODO remove hardcoded value + for (int i = 0; i < bammo.other_mats_size(); ++i) { + const std::string token = bammo.other_mats(i); + const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 + : -1; + DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); + if (idx != -1) + pammo.other_mats.at(idx) = 1; + } + } + + // core quality + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return bammo.quality_core(idx); }, + bammo.quality_core_size(), pammo.quality_core); + + // total quality + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return bammo.quality_total(idx); }, + bammo.quality_total_size(), pammo.quality_total); + }); +} + +void StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { animals->set_empty_cages(mPile->settings.animals.empty_cages); animals->set_empty_traps(mPile->settings.animals.empty_traps); for (size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i) { @@ -422,36 +870,42 @@ void StockpileSerializer::write_animals() { } } -void StockpileSerializer::read_animals() { - if (mBuffer.has_animals()) { - mPile->settings.flags.bits.animals = 1; - DEBUG(log).print("animals:\n"); - mPile->settings.animals.empty_cages = mBuffer.animals().empty_cages(); - mPile->settings.animals.empty_traps = mBuffer.animals().empty_traps(); - - mPile->settings.animals.enabled.clear(); - mPile->settings.animals.enabled.resize(world->raws.creatures.all.size(), '\0'); - DEBUG(log).print("pile has %zd\n", mPile->settings.animals.enabled.size()); - for (auto i = 0; i < mBuffer.animals().enabled_size(); ++i) { - std::string id = mBuffer.animals().enabled(i); - int idx = find_creature(id); - DEBUG(log).print("%s %d\n", id.c_str(), idx); - if (idx < 0 || size_t(idx) >= mPile->settings.animals.enabled.size()) { - WARN(log).print("animal index invalid: %d\n", idx); - continue; - } - mPile->settings.animals.enabled.at(idx) = (char)1; - } - } - else { - mPile->settings.animals.enabled.clear(); - mPile->settings.flags.bits.animals = 0; - mPile->settings.animals.empty_cages = false; - mPile->settings.animals.empty_traps = false; - } +void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& filter) { + read_category("animals", mode, + std::bind(&StockpileSettings::has_animals, mBuffer), + std::bind(&StockpileSettings::animals, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_animals, + [&]() { + auto & panimals = mPile->settings.animals; + panimals.empty_cages = false; + panimals.empty_traps = false; + panimals.enabled.clear(); + }, + [&](bool force, char val) { + auto & banimals = mBuffer.animals(); + auto & panimals = mPile->settings.animals; + if (force || banimals.empty_cages()) + panimals.empty_cages = banimals.empty_cages() && val; + if (force || banimals.empty_traps()) + panimals.empty_traps = banimals.empty_traps() && val; + + panimals.enabled.resize(world->raws.creatures.all.size(), '\0'); + for (auto i = 0; i < banimals.enabled_size(); ++i) { + const std::string& id = banimals.enabled(i); + int idx = find_creature(id); + DEBUG(log).print("%s %d\n", id.c_str(), idx); + if (idx < 0 || size_t(idx) >= panimals.enabled.size()) { + WARN(log).print("animal index invalid: %d\n", idx); + continue; + } + if (!filter.size() || id.find(filter) != id.npos) + panimals.enabled.at(idx) = val; + } + }); } -StockpileSerializer::food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { +food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { using df::enums::organic_mat_category::organic_mat_category; using namespace std::placeholders; switch (cat) { @@ -633,8 +1087,7 @@ StockpileSerializer::food_pair StockpileSerializer::food_map(organic_mat_categor return food_pair(); } -void StockpileSerializer::write_food() { - StockpileSettings::FoodSet* food = mBuffer.mutable_food(); +void StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { DEBUG(log).print("food:\n"); food->set_prepared_meals(mPile->settings.food.prepared_meals); @@ -649,7 +1102,7 @@ void StockpileSerializer::write_food() { } } -void StockpileSerializer::read_food() { +void StockpileSerializer::read_food(DeserializeMode mode, const std::string& filter) { using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; if (mBuffer.has_food()) { @@ -683,27 +1136,7 @@ void StockpileSerializer::read_food() { } } -void StockpileSerializer::furniture_setup_other_mats() { - mOtherMatsFurniture.insert(std::make_pair(0, "WOOD")); - mOtherMatsFurniture.insert(std::make_pair(1, "PLANT_CLOTH")); - mOtherMatsFurniture.insert(std::make_pair(2, "BONE")); - mOtherMatsFurniture.insert(std::make_pair(3, "TOOTH")); - mOtherMatsFurniture.insert(std::make_pair(4, "HORN")); - mOtherMatsFurniture.insert(std::make_pair(5, "PEARL")); - mOtherMatsFurniture.insert(std::make_pair(6, "SHELL")); - mOtherMatsFurniture.insert(std::make_pair(7, "LEATHER")); - mOtherMatsFurniture.insert(std::make_pair(8, "SILK")); - mOtherMatsFurniture.insert(std::make_pair(9, "AMBER")); - mOtherMatsFurniture.insert(std::make_pair(10, "CORAL")); - mOtherMatsFurniture.insert(std::make_pair(11, "GREEN_GLASS")); - mOtherMatsFurniture.insert(std::make_pair(12, "CLEAR_GLASS")); - mOtherMatsFurniture.insert(std::make_pair(13, "CRYSTAL_GLASS")); - mOtherMatsFurniture.insert(std::make_pair(14, "YARN")); -} - -void StockpileSerializer::write_furniture() { - StockpileSettings::FurnitureSet* furniture = mBuffer.mutable_furniture(); - +void StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furniture) { // FURNITURE type using df::enums::furniture_type::furniture_type; using type_traits = df::enum_traits; @@ -722,7 +1155,7 @@ void StockpileSerializer::write_furniture() { // other mats serialize_list_other_mats( - mOtherMatsFurniture, [=](const std::string& token) { furniture->add_other_mats(token); }, + mOtherMatsFurniture.mats, [=](const std::string& token) { furniture->add_other_mats(token); }, mPile->settings.furniture.other_mats); serialize_list_quality([=](const std::string& token) { furniture->add_quality_core(token); }, @@ -735,7 +1168,7 @@ bool StockpileSerializer::furniture_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::read_furniture() { +void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_furniture()) { mPile->settings.flags.bits.furniture = 1; const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); @@ -766,7 +1199,7 @@ void StockpileSerializer::read_furniture() { // other materials unserialize_list_other_mats( - mOtherMatsFurniture, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, + mOtherMatsFurniture.mats, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, furniture.other_mats_size(), &mPile->settings.furniture.other_mats); // core quality @@ -816,9 +1249,8 @@ bool StockpileSerializer::refuse_type_is_allowed(item_type::item_type type) { return true; } -void StockpileSerializer::write_refuse() { +void StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { DEBUG(log).print("refuse:\n"); - StockpileSettings::RefuseSet* refuse = mBuffer.mutable_refuse(); refuse->set_fresh_raw_hide(mPile->settings.refuse.fresh_raw_hide); refuse->set_rotten_raw_hide(mPile->settings.refuse.rotten_raw_hide); @@ -876,7 +1308,7 @@ void StockpileSerializer::refuse_read_helper(std::functionsettings.flags.bits.refuse = 1; const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); @@ -949,16 +1381,14 @@ bool StockpileSerializer::stone_is_allowed(const MaterialInfo& mi) { return is_allowed_soil || is_allowed_stone; } -void StockpileSerializer::write_stone() { - StockpileSettings::StoneSet* stone = mBuffer.mutable_stone(); - +void StockpileSerializer::write_stone(StockpileSettings::StoneSet* stone) { FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); serialize_list_material( filter, [=](const std::string& token) { stone->add_mats(token); }, mPile->settings.stone.mats); } -void StockpileSerializer::read_stone() { +void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_stone()) { mPile->settings.flags.bits.stone = 1; const StockpileSettings::StoneSet stone = mBuffer.stone(); @@ -975,109 +1405,18 @@ void StockpileSerializer::read_stone() { } } -bool StockpileSerializer::ammo_mat_is_allowed(const MaterialInfo& mi) { - return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); -} - -void StockpileSerializer::write_ammo() { - StockpileSettings::AmmoSet* ammo = mBuffer.mutable_ammo(); - - // ammo type - serialize_list_itemdef([=](const std::string& token) { ammo->add_type(token); }, - mPile->settings.ammo.type, - std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), - item_type::AMMO); - - // metal - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::ammo_mat_is_allowed, this, _1); - serialize_list_material( - filter, [=](const std::string& token) { ammo->add_mats(token); }, - mPile->settings.ammo.mats); - - // other mats - only wood and bone - if (mPile->settings.ammo.other_mats.size() > 2) { - WARN(log).print("ammo other materials > 2: %zd\n", mPile->settings.ammo.other_mats.size()); - } - - for (size_t i = 0; i < std::min(size_t(2), mPile->settings.ammo.other_mats.size()); ++i) { - if (!mPile->settings.ammo.other_mats.at(i)) - continue; - const std::string token = i == 0 ? "WOOD" : "BONE"; - ammo->add_other_mats(token); - DEBUG(log).print(" other mats %zd is %s\n", i, token.c_str()); - } - - // quality core - serialize_list_quality([=](const std::string& token) { ammo->add_quality_core(token); }, - mPile->settings.ammo.quality_core); - - // quality total - serialize_list_quality([=](const std::string& token) { ammo->add_quality_total(token); }, - mPile->settings.ammo.quality_total); -} - -void StockpileSerializer::read_ammo() { - if (mBuffer.has_ammo()) { - mPile->settings.flags.bits.ammo = 1; - const StockpileSettings::AmmoSet ammo = mBuffer.ammo(); - DEBUG(log).print("ammo:\n"); - - // ammo type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return ammo.type(idx); }, - ammo.type_size(), &mPile->settings.ammo.type, item_type::AMMO); - - // materials metals - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::ammo_mat_is_allowed, this, _1); - unserialize_list_material( - filter, [=](const size_t& idx) -> const std::string& { return ammo.mats(idx); }, - ammo.mats_size(), &mPile->settings.ammo.mats); - - // others - mPile->settings.ammo.other_mats.clear(); - mPile->settings.ammo.other_mats.resize(2, '\0'); - if (ammo.other_mats_size() > 0) { - // TODO remove hardcoded value - for (int i = 0; i < ammo.other_mats_size(); ++i) { - const std::string token = ammo.other_mats(i); - const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 - : -1; - DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); - if (idx != -1) - mPile->settings.ammo.other_mats.at(idx) = 1; - } - } - - // core quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return ammo.quality_core(idx); }, - ammo.quality_core_size(), mPile->settings.ammo.quality_core); - - // total quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return ammo.quality_total(idx); }, - ammo.quality_total_size(), mPile->settings.ammo.quality_total); - } - else { - mPile->settings.flags.bits.ammo = 0; - mPile->settings.ammo.type.clear(); - mPile->settings.ammo.mats.clear(); - mPile->settings.ammo.other_mats.clear(); - quality_clear(mPile->settings.ammo.quality_core); - quality_clear(mPile->settings.ammo.quality_total); - } -} - bool StockpileSerializer::coins_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid(); } -void StockpileSerializer::write_coins() { - StockpileSettings::CoinSet* coins = mBuffer.mutable_coin(); +void StockpileSerializer::write_coins(StockpileSettings::CoinSet* coins) { FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); serialize_list_material( filter, [=](const std::string& token) { coins->add_mats(token); }, mPile->settings.coins.mats); } -void StockpileSerializer::read_coins() { +void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_coin()) { mPile->settings.flags.bits.coins = 1; const StockpileSettings::CoinSet coins = mBuffer.coin(); @@ -1094,19 +1433,6 @@ void StockpileSerializer::read_coins() { } } -void StockpileSerializer::bars_blocks_setup_other_mats() { - mOtherMatsBars.insert(std::make_pair(0, "COAL")); - mOtherMatsBars.insert(std::make_pair(1, "POTASH")); - mOtherMatsBars.insert(std::make_pair(2, "ASH")); - mOtherMatsBars.insert(std::make_pair(3, "PEARLASH")); - mOtherMatsBars.insert(std::make_pair(4, "SOAP")); - - mOtherMatsBlocks.insert(std::make_pair(0, "GREEN_GLASS")); - mOtherMatsBlocks.insert(std::make_pair(1, "CLEAR_GLASS")); - mOtherMatsBlocks.insert(std::make_pair(2, "CRYSTAL_GLASS")); - mOtherMatsBlocks.insert(std::make_pair(3, "WOOD")); -} - bool StockpileSerializer::bars_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } @@ -1115,8 +1441,7 @@ bool StockpileSerializer::blocks_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_bars_blocks() { - StockpileSettings::BarsBlocksSet* bars_blocks = mBuffer.mutable_barsblocks(); +void StockpileSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { MaterialInfo mi; FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); serialize_list_material( @@ -1131,16 +1456,16 @@ void StockpileSerializer::write_bars_blocks() { // bars other mats serialize_list_other_mats( - mOtherMatsBars, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, + mOtherMatsBars.mats, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, mPile->settings.bars_blocks.bars_other_mats); // blocks other mats serialize_list_other_mats( - mOtherMatsBlocks, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, + mOtherMatsBlocks.mats, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, mPile->settings.bars_blocks.blocks_other_mats); } -void StockpileSerializer::read_bars_blocks() { +void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_barsblocks()) { mPile->settings.flags.bits.bars_blocks = 1; const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); @@ -1158,12 +1483,12 @@ void StockpileSerializer::read_bars_blocks() { bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats); // bars other mats unserialize_list_other_mats( - mOtherMatsBars, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, + mOtherMatsBars.mats, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats); // blocks other mats unserialize_list_other_mats( - mOtherMatsBlocks, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, + mOtherMatsBlocks.mats, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats); } else { @@ -1185,8 +1510,7 @@ bool StockpileSerializer::gem_other_mat_is_allowed(MaterialInfo& mi) { return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); } -void StockpileSerializer::write_gems() { - StockpileSettings::GemsSet* gems = mBuffer.mutable_gems(); +void StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { MaterialInfo mi; // rough mats FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); @@ -1222,7 +1546,7 @@ void StockpileSerializer::write_gems() { } } -void StockpileSerializer::read_gems() { +void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_gems()) { mPile->settings.flags.bits.gems = 1; const StockpileSettings::GemsSet gems = mBuffer.gems(); @@ -1312,32 +1636,11 @@ bool StockpileSerializer::finished_goods_type_is_allowed(item_type::item_type ty } } -void StockpileSerializer::finished_goods_setup_other_mats() { - mOtherMatsFinishedGoods.insert(std::make_pair(0, "WOOD")); - mOtherMatsFinishedGoods.insert(std::make_pair(1, "PLANT_CLOTH")); - mOtherMatsFinishedGoods.insert(std::make_pair(2, "BONE")); - mOtherMatsFinishedGoods.insert(std::make_pair(3, "TOOTH")); - mOtherMatsFinishedGoods.insert(std::make_pair(4, "HORN")); - mOtherMatsFinishedGoods.insert(std::make_pair(5, "PEARL")); - mOtherMatsFinishedGoods.insert(std::make_pair(6, "SHELL")); - mOtherMatsFinishedGoods.insert(std::make_pair(7, "LEATHER")); - mOtherMatsFinishedGoods.insert(std::make_pair(8, "SILK")); - mOtherMatsFinishedGoods.insert(std::make_pair(9, "AMBER")); - mOtherMatsFinishedGoods.insert(std::make_pair(10, "CORAL")); - mOtherMatsFinishedGoods.insert(std::make_pair(11, "GREEN_GLASS")); - mOtherMatsFinishedGoods.insert(std::make_pair(12, "CLEAR_GLASS")); - mOtherMatsFinishedGoods.insert(std::make_pair(13, "CRYSTAL_GLASS")); - mOtherMatsFinishedGoods.insert(std::make_pair(14, "YARN")); - mOtherMatsFinishedGoods.insert(std::make_pair(15, "WAX")); -} - bool StockpileSerializer::finished_goods_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_finished_goods() { - StockpileSettings::FinishedGoodsSet* finished_goods = mBuffer.mutable_finished_goods(); - +void StockpileSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { // type FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); serialize_list_item_type( @@ -1352,7 +1655,7 @@ void StockpileSerializer::write_finished_goods() { // other mats serialize_list_other_mats( - mOtherMatsFinishedGoods, [=](const std::string& token) { finished_goods->add_other_mats(token); }, + mOtherMatsFinishedGoods.mats, [=](const std::string& token) { finished_goods->add_other_mats(token); }, mPile->settings.finished_goods.other_mats); // quality core @@ -1364,7 +1667,7 @@ void StockpileSerializer::write_finished_goods() { mPile->settings.finished_goods.quality_total); } -void StockpileSerializer::read_finished_goods() { +void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_finished_goods()) { mPile->settings.flags.bits.finished_goods = 1; const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); @@ -1384,7 +1687,7 @@ void StockpileSerializer::read_finished_goods() { // other mats unserialize_list_other_mats( - mOtherMatsFinishedGoods, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, + mOtherMatsFinishedGoods.mats, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats); // core quality @@ -1405,15 +1708,13 @@ void StockpileSerializer::read_finished_goods() { } } -void StockpileSerializer::write_leather() { - StockpileSettings::LeatherSet* leather = mBuffer.mutable_leather(); - +void StockpileSerializer::write_leather(StockpileSettings::LeatherSet* leather) { FuncWriteExport setter = [=](const std::string& id) { leather->add_mats(id); }; serialize_list_organic_mat(setter, &mPile->settings.leather.mats, organic_mat_category::Leather); } -void StockpileSerializer::read_leather() { +void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_leather()) { mPile->settings.flags.bits.leather = 1; const StockpileSettings::LeatherSet leather = mBuffer.leather(); @@ -1428,9 +1729,7 @@ void StockpileSerializer::read_leather() { } } -void StockpileSerializer::write_cloth() { - StockpileSettings::ClothSet* cloth = mBuffer.mutable_cloth(); - +void StockpileSerializer::write_cloth(StockpileSettings::ClothSet* cloth) { serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_silk(token); }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); @@ -1455,7 +1754,7 @@ void StockpileSerializer::write_cloth() { serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_metal(token); }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); } -void StockpileSerializer::read_cloth() { +void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_cloth()) { mPile->settings.flags.bits.cloth = 1; const StockpileSettings::ClothSet cloth = mBuffer.cloth(); @@ -1502,8 +1801,7 @@ bool StockpileSerializer::wood_mat_is_allowed(const df::plant_raw* plant) { return plant && plant->flags.is_set(plant_raw_flags::TREE); } -void StockpileSerializer::write_wood() { - StockpileSettings::WoodSet* wood = mBuffer.mutable_wood(); +void StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { for (size_t i = 0; i < mPile->settings.wood.mats.size(); ++i) { if (mPile->settings.wood.mats.at(i)) { const df::plant_raw* plant = find_plant(i); @@ -1514,7 +1812,7 @@ void StockpileSerializer::write_wood() { } } } -void StockpileSerializer::read_wood() { +void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_wood()) { mPile->settings.flags.bits.wood = 1; const StockpileSettings::WoodSet wood = mBuffer.wood(); @@ -1543,9 +1841,7 @@ bool StockpileSerializer::weapons_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_weapons() { - StockpileSettings::WeaponsSet* weapons = mBuffer.mutable_weapons(); - +void StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) { weapons->set_unusable(mPile->settings.weapons.unusable); weapons->set_usable(mPile->settings.weapons.usable); @@ -1569,7 +1865,7 @@ void StockpileSerializer::write_weapons() { // other mats serialize_list_other_mats( - mOtherMatsWeaponsArmor, [=](const std::string& token) { weapons->add_other_mats(token); }, + mOtherMatsWeaponsArmor.mats, [=](const std::string& token) { weapons->add_other_mats(token); }, mPile->settings.weapons.other_mats); // quality core @@ -1581,7 +1877,7 @@ void StockpileSerializer::write_weapons() { mPile->settings.weapons.quality_total); } -void StockpileSerializer::read_weapons() { +void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_weapons()) { mPile->settings.flags.bits.weapons = 1; const StockpileSettings::WeaponsSet weapons = mBuffer.weapons(); @@ -1610,7 +1906,7 @@ void StockpileSerializer::read_weapons() { // other mats unserialize_list_other_mats( - mOtherMatsWeaponsArmor, [=](const size_t& idx) -> const std::string& { return weapons.other_mats(idx); }, + mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return weapons.other_mats(idx); }, weapons.other_mats_size(), &mPile->settings.weapons.other_mats); // core quality @@ -1631,26 +1927,11 @@ void StockpileSerializer::read_weapons() { } } -void StockpileSerializer::weapons_armor_setup_other_mats() { - mOtherMatsWeaponsArmor.insert(std::make_pair(0, "WOOD")); - mOtherMatsWeaponsArmor.insert(std::make_pair(1, "PLANT_CLOTH")); - mOtherMatsWeaponsArmor.insert(std::make_pair(2, "BONE")); - mOtherMatsWeaponsArmor.insert(std::make_pair(3, "SHELL")); - mOtherMatsWeaponsArmor.insert(std::make_pair(4, "LEATHER")); - mOtherMatsWeaponsArmor.insert(std::make_pair(5, "SILK")); - mOtherMatsWeaponsArmor.insert(std::make_pair(6, "GREEN_GLASS")); - mOtherMatsWeaponsArmor.insert(std::make_pair(7, "CLEAR_GLASS")); - mOtherMatsWeaponsArmor.insert(std::make_pair(8, "CRYSTAL_GLASS")); - mOtherMatsWeaponsArmor.insert(std::make_pair(9, "YARN")); -} - bool StockpileSerializer::armor_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::write_armor() { - StockpileSettings::ArmorSet* armor = mBuffer.mutable_armor(); - +void StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { armor->set_unusable(mPile->settings.armor.unusable); armor->set_usable(mPile->settings.armor.usable); @@ -1698,7 +1979,7 @@ void StockpileSerializer::write_armor() { // other mats serialize_list_other_mats( - mOtherMatsWeaponsArmor, [=](const std::string& token) { armor->add_other_mats(token); }, + mOtherMatsWeaponsArmor.mats, [=](const std::string& token) { armor->add_other_mats(token); }, mPile->settings.armor.other_mats); // quality core @@ -1710,7 +1991,7 @@ void StockpileSerializer::write_armor() { mPile->settings.armor.quality_total); } -void StockpileSerializer::read_armor() { +void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& filter) { if (mBuffer.has_armor()) { mPile->settings.flags.bits.armor = 1; const StockpileSettings::ArmorSet armor = mBuffer.armor(); @@ -1755,7 +2036,7 @@ void StockpileSerializer::read_armor() { // other mats unserialize_list_other_mats( - mOtherMatsWeaponsArmor, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, + mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, armor.other_mats_size(), &mPile->settings.armor.other_mats); // core quality @@ -1779,3 +2060,19 @@ void StockpileSerializer::read_armor() { quality_clear(mPile->settings.armor.quality_total); } } + +void StockpileSerializer::write_corpses(StockpileSettings::CorpsesSet* corpses) { + +} + +void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& filter) { + +} + +void StockpileSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { + +} + +void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& filter) { + +} diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 3fe2087de..24c6d5d98 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -14,16 +14,52 @@ namespace df struct building_stockpilest; } +enum IncludedElements { + INCLUDED_ELEMENTS_NONE = 0x00, + INCLUDED_ELEMENTS_CONTAINERS = 0x01, + INCLUDED_ELEMENTS_GENERAL = 0x02, + INCLUDED_ELEMENTS_CATEGORIES = 0x04, + INCLUDED_ELEMENTS_TYPES = 0x08, +}; + +enum DeserializeMode { + DESERIALIZE_MODE_SET = 0, + DESERIALIZE_MODE_ENABLE = 1, + DESERIALIZE_MODE_DISABLE = 2, +}; + +// read the token from the serialized list during import +typedef std::function FuncReadImport; +// add the token to the serialized list during export +typedef std::function FuncWriteExport; +// are item's of item_type allowed? +typedef std::function FuncItemAllowed; +// is this material allowed? +typedef std::function FuncMaterialAllowed; + +// convenient struct for parsing food stockpile items +struct food_pair { + // exporting + FuncWriteExport set_value; + std::vector* stockpile_values; + // importing + FuncReadImport get_value; + size_t serialized_count; + bool valid; + + food_pair(FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count) + : set_value(s), stockpile_values(sp_v), get_value(g), serialized_count(count), valid(true) { } + food_pair(): valid(false) { } +}; + /** * Class for serializing the stockpile_settings structure into a Google protobuf */ class StockpileSerializer { public: /** - * @param out for debugging * @param stockpile stockpile to read or write settings to */ - StockpileSerializer(df::building_stockpilest* stockpile); ~StockpileSerializer(); @@ -32,191 +68,64 @@ public: * Since we depend on protobuf-lite, not the full lib, we copy this function from * protobuf message.cc */ - bool serialize_to_ostream(std::ostream* output); + bool serialize_to_ostream(std::ostream* output, uint32_t includedElements); /** * Will serialize stockpile settings to a file (overwrites existing files) * @return success/failure */ - bool serialize_to_file(const std::string& file); + bool serialize_to_file(const std::string& file, uint32_t includedElements); /** * Again, copied from message.cc */ - bool parse_from_istream(std::istream* input); + bool parse_from_istream(std::istream* input, DeserializeMode mode, const std::string& filter); /** * Read stockpile settings from file */ - bool unserialize_from_file(const std::string& file); + bool unserialize_from_file(const std::string& file, DeserializeMode mode, const std::string& filter); private: df::building_stockpilest* mPile; dfstockpiles::StockpileSettings mBuffer; - std::map mOtherMatsFurniture; - std::map mOtherMatsFinishedGoods; - std::map mOtherMatsBars; - std::map mOtherMatsBlocks; - std::map mOtherMatsWeaponsArmor; - - /** - read memory structures and serialize to protobuf - */ - void write(); - - // parse serialized data into ui indices - void read(); - - /** - * Find an enum's value based off the string label. - * @param traits the enum's trait struct - * @param token the string value in key_table - * @return the enum's value, -1 if not found - */ - template - static typename df::enum_traits::base_type linear_index(df::enum_traits traits, const std::string& token) { - auto j = traits.first_item_value; - auto limit = traits.last_item_value; - // sometimes enums start at -1, which is bad news for array indexing - if (j < 0) { - j += abs(traits.first_item_value); - limit += abs(traits.first_item_value); - } - for (; j <= limit; ++j) { - if (token.compare(traits.key_table[j]) == 0) - return j; - } - return -1; - } - - // read the token from the serailized list during import - typedef std::function FuncReadImport; - // add the token to the serialized list during export - typedef std::function FuncWriteExport; - // are item's of item_type allowed? - typedef std::function FuncItemAllowed; - // is this material allowed? - typedef std::function FuncMaterialAllowed; - - // convenient struct for parsing food stockpile items - struct food_pair { - // exporting - FuncWriteExport set_value; - std::vector* stockpile_values; - // importing - FuncReadImport get_value; - size_t serialized_count; - bool valid; - - food_pair(FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count) - : set_value(s), stockpile_values(sp_v), get_value(g), serialized_count(count), valid(true) { } - food_pair(): valid(false) { } - }; - - /** - * There are many repeated (un)serialization cases throughout the stockpile_settings structure, - * so the most common cases have been generalized into generic functions using lambdas. - * - * The basic process to serialize a stockpile_settings structure is: - * 1. loop through the list - * 2. for every element that is TRUE: - * 3. map the specific stockpile_settings index into a general material, creature, etc index - * 4. verify that type is allowed in the list (e.g., no stone in gems stockpiles) - * 5. add it to the protobuf using FuncWriteExport - * - * The unserialization process is the same in reverse. - */ - void serialize_list_organic_mat(FuncWriteExport add_value, const std::vector* list, df::enums::organic_mat_category::organic_mat_category cat); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_organic_mat(FuncReadImport get_value, size_t list_size, std::vector* pile_list, df::enums::organic_mat_category::organic_mat_category cat); - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_item_type(FuncItemAllowed is_allowed, FuncWriteExport add_value, const std::vector& list); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_item_type(FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_material(FuncMaterialAllowed is_allowed, FuncWriteExport add_value, const std::vector& list); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_material(FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_quality(FuncWriteExport add_value, const bool(&quality_list)[7]); - /** - * Set all values in a bool[7] to false - */ - void quality_clear(bool(&pile_list)[7]); + // read memory structures and serialize to protobuf + void write(uint32_t includedElements); - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_quality(FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]); + // parse serialized data into ui indices + void read(DeserializeMode mode, const std::string& filter); /** - * @see serialize_list_organic_mat - */ - void serialize_list_other_mats(const std::map other_mats, FuncWriteExport add_value, std::vector list); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_other_mats(const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector* pile_list); - - /** - * @see serialize_list_organic_mat - */ - void serialize_list_itemdef(FuncWriteExport add_value, std::vector list, std::vector items, df::enums::item_type::item_type type); - - /** - * @see serialize_list_organic_mat - */ - void unserialize_list_itemdef(FuncReadImport read_value, int32_t list_size, std::vector* pile_list, df::enums::item_type::item_type type); - - /** - * Given a list of other_materials and an index, return its corresponding token + * Given a list of other_materials and an index, return its corresponding token * @return empty string if not found * @see other_mats_token */ std::string other_mats_index(const std::map other_mats, int idx); /** - * Given a list of other_materials and a token, return its corresponding index + * Given a list of other_materials and a token, return its corresponding index * @return -1 if not found * @see other_mats_index */ int other_mats_token(const std::map other_mats, const std::string& token); + void write_containers(); + void read_containers(DeserializeMode mode); void write_general(); - void read_general(); + void read_general(DeserializeMode mode); - void write_animals(); - void read_animals(); + void write_animals(dfstockpiles::StockpileSettings::AnimalsSet* animals); + void read_animals(DeserializeMode mode, const std::string& filter); food_pair food_map(df::enums::organic_mat_category::organic_mat_category cat); - void write_food(); - void read_food(); + void write_food(dfstockpiles::StockpileSettings::FoodSet* food); + void read_food(DeserializeMode mode, const std::string& filter); - void furniture_setup_other_mats(); - void write_furniture(); + void write_furniture(dfstockpiles::StockpileSettings::FurnitureSet* furniture); bool furniture_mat_is_allowed(const DFHack::MaterialInfo& mi); - void read_furniture(); + void read_furniture(DeserializeMode mode, const std::string& filter); bool refuse_creature_is_allowed(const df::creature_raw* raw); @@ -224,60 +133,57 @@ private: bool refuse_type_is_allowed(df::enums::item_type::item_type type); - void write_refuse(); + void write_refuse(dfstockpiles::StockpileSettings::RefuseSet* refuse); void refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list); - void read_refuse(); + void read_refuse(DeserializeMode mode, const std::string& filter); bool stone_is_allowed(const DFHack::MaterialInfo& mi); - void write_stone(); + void write_stone(dfstockpiles::StockpileSettings::StoneSet* stone); - void read_stone(); + void read_stone(DeserializeMode mode, const std::string& filter); - bool ammo_mat_is_allowed(const DFHack::MaterialInfo& mi); - - void write_ammo(); - void read_ammo(); + bool write_ammo(dfstockpiles::StockpileSettings::AmmoSet* ammo); + void read_ammo(DeserializeMode mode, const std::string& filter); bool coins_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_coins(); - - void read_coins(); - - void bars_blocks_setup_other_mats(); + void write_coins(dfstockpiles::StockpileSettings::CoinSet* coins); + void read_coins(DeserializeMode mode, const std::string& filter); bool bars_mat_is_allowed(const DFHack::MaterialInfo& mi); bool blocks_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_bars_blocks(); - void read_bars_blocks(); + void write_bars_blocks(dfstockpiles::StockpileSettings::BarsBlocksSet* bars_blocks); + void read_bars_blocks(DeserializeMode mode, const std::string& filter); bool gem_mat_is_allowed(const DFHack::MaterialInfo& mi); bool gem_cut_mat_is_allowed(const DFHack::MaterialInfo& mi); bool gem_other_mat_is_allowed(DFHack::MaterialInfo& mi); - void write_gems(); + void write_gems(dfstockpiles::StockpileSettings::GemsSet* gems); - void read_gems(); + void read_gems(DeserializeMode mode, const std::string& filter); bool finished_goods_type_is_allowed(df::enums::item_type::item_type type); - void finished_goods_setup_other_mats(); bool finished_goods_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_finished_goods(); - void read_finished_goods(); - void write_leather(); - void read_leather(); - void write_cloth(); - void read_cloth(); + void write_finished_goods(dfstockpiles::StockpileSettings::FinishedGoodsSet* finished_goods); + void read_finished_goods(DeserializeMode mode, const std::string& filter); + void write_leather(dfstockpiles::StockpileSettings::LeatherSet* leather); + void read_leather(DeserializeMode mode, const std::string& filter); + void write_cloth(dfstockpiles::StockpileSettings::ClothSet* cloth); + void read_cloth(DeserializeMode mode, const std::string& filter); bool wood_mat_is_allowed(const df::plant_raw* plant); - void write_wood(); - void read_wood(); + void write_wood(dfstockpiles::StockpileSettings::WoodSet* wood); + void read_wood(DeserializeMode mode, const std::string& filter); bool weapons_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_weapons(); - void read_weapons(); - void weapons_armor_setup_other_mats(); + void write_weapons(dfstockpiles::StockpileSettings::WeaponsSet* weapons); + void read_weapons(DeserializeMode mode, const std::string& filter); bool armor_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_armor(); - void read_armor(); + void write_armor(dfstockpiles::StockpileSettings::ArmorSet* armor); + void read_armor(DeserializeMode mode, const std::string& filter); + void write_corpses(dfstockpiles::StockpileSettings::CorpsesSet* corpses); + void read_corpses(DeserializeMode mode, const std::string& filter); + void write_sheet(dfstockpiles::StockpileSettings::SheetSet* sheet); + void read_sheet(DeserializeMode mode, const std::string& filter); }; diff --git a/plugins/stockpiles/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto index 90c95b93f..66815335b 100644 --- a/plugins/stockpiles/proto/stockpiles.proto +++ b/plugins/stockpiles/proto/stockpiles.proto @@ -7,12 +7,14 @@ option optimize_for = LITE_RUNTIME; message StockpileSettings { message AnimalsSet { + optional bool all = 4; optional bool empty_cages = 1; optional bool empty_traps = 2; repeated string enabled = 3; } message FoodSet { + optional bool all = 21; repeated string meat = 1; repeated string fish = 2; repeated string unprepared_fish = 20; @@ -36,6 +38,7 @@ message StockpileSettings { } message FurnitureSet { + optional bool all = 7; repeated string type = 1; repeated string other_mats = 2; repeated string mats = 3; @@ -44,6 +47,7 @@ message StockpileSettings { // UNUSED: optional bool sand_bags = 6; } message RefuseSet { + optional bool all = 12; repeated string type = 1; repeated string corpses = 2; repeated string body_parts = 3; @@ -57,12 +61,14 @@ message StockpileSettings { optional bool rotten_raw_hide = 11; } message StoneSet { - repeated string mats = 1; + optional bool all = 2; + repeated string mats = 1; } message OreSet { - repeated string mats = 1; + repeated string mats = 1; } message AmmoSet { + optional bool all = 6; repeated string type = 1; repeated string other_mats = 2; repeated string mats = 3; @@ -70,21 +76,25 @@ message StockpileSettings { repeated string quality_total = 5; } message CoinSet { + optional bool all = 2; repeated string mats = 1; } message BarsBlocksSet { + optional bool all = 5; repeated string bars_other_mats = 1; repeated string blocks_other_mats = 2; repeated string bars_mats = 3; repeated string blocks_mats = 4; } message GemsSet { + optional bool all = 5; repeated string rough_other_mats = 1; repeated string cut_other_mats = 2; repeated string rough_mats = 3; repeated string cut_mats = 4; } message FinishedGoodsSet { + optional bool all = 6; repeated string type = 1; repeated string other_mats = 2; repeated string mats = 3; @@ -92,9 +102,11 @@ message StockpileSettings { repeated string quality_total = 5; } message LeatherSet { + optional bool all = 2; repeated string mats = 1; } message ClothSet { + optional bool all = 9; repeated string thread_silk = 1; repeated string thread_plant = 2; repeated string thread_yarn = 3; @@ -105,9 +117,11 @@ message StockpileSettings { repeated string cloth_metal = 8; } message WoodSet { + optional bool all = 2; repeated string mats = 1; } message WeaponsSet { + optional bool all = 9; repeated string weapon_type = 1; repeated string trapcomp_type = 2; repeated string other_mats = 3; @@ -117,8 +131,8 @@ message StockpileSettings { optional bool usable = 7; optional bool unusable = 8; } - message ArmorSet { + optional bool all = 13; repeated string body = 1; repeated string head = 2; repeated string feet = 3; @@ -129,33 +143,45 @@ message StockpileSettings { repeated string mats = 8; repeated string quality_core = 9; repeated string quality_total = 10; - optional bool usable =11; + optional bool usable = 11; optional bool unusable = 12; } + message CorpsesSet { + optional bool all = 1; + } + message SheetSet { + optional bool all = 1; + } + + // general settings + optional int32 max_barrels = 20; + optional int32 max_bins = 21; + optional int32 max_wheelbarrows = 22; + optional bool use_links_only = 23; + optional bool allow_organic = 18; + optional bool allow_inorganic = 19; + // categories + optional AmmoSet ammo = 8; optional AnimalsSet animals = 1; + optional ArmorSet armor = 17; + optional BarsBlocksSet barsblocks = 10; + optional ClothSet cloth = 14; + optional CoinSet coin = 9; + optional FinishedGoodsSet finished_goods = 12; optional FoodSet food = 2; optional FurnitureSet furniture = 3; - optional int32 unknown1 = 4 [deprecated=true]; - optional RefuseSet refuse = 5; - optional StoneSet stone = 6; - optional OreSet ore = 7; - optional AmmoSet ammo = 8; - optional CoinSet coin = 9; - optional BarsBlocksSet barsblocks = 10; optional GemsSet gems = 11; - optional FinishedGoodsSet finished_goods = 12; optional LeatherSet leather = 13; - optional ClothSet cloth = 14; - optional WoodSet wood = 15; + optional CorpsesSet corpses_v50 = 25; + optional RefuseSet refuse = 5; + optional SheetSet sheet = 26; + optional StoneSet stone = 6; optional WeaponsSet weapons = 16; - optional ArmorSet armor = 17; - optional bool allow_organic = 18; - optional bool allow_inorganic = 19; - optional bool corpses = 24; - // extras - optional int32 max_barrels = 20; - optional int32 max_bins = 21; - optional int32 max_wheelbarrows = 22; - optional bool use_links_only = 23; + optional WoodSet wood = 15; + + // deprecated + optional bool corpses = 24; // not marked as deprecated since we still read it + optional OreSet ore = 7 [deprecated=true]; + optional int32 unknown1 = 4 [deprecated=true]; } diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index e7b3525ef..99cc16607 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -83,7 +83,7 @@ static df::building_stockpilest* get_stockpile(int id) { return virtual_cast(df::building::find(id)); } -static bool stockpiles_export(color_ostream& out, string fname, int id) { +static bool stockpiles_export(color_ostream& out, string fname, int id, uint32_t includedElements) { df::building_stockpilest* sp = get_stockpile(id); if (!sp) { out.printerr("Specified building isn't a stockpile: %d.\n", id); @@ -95,7 +95,7 @@ static bool stockpiles_export(color_ostream& out, string fname, int id) { try { StockpileSerializer cereal(sp); - if (!cereal.serialize_to_file(fname)) { + if (!cereal.serialize_to_file(fname, includedElements)) { out.printerr("could not save to '%s'\n", fname.c_str()); return false; } @@ -108,7 +108,7 @@ static bool stockpiles_export(color_ostream& out, string fname, int id) { return true; } -static bool stockpiles_import(color_ostream& out, string fname, int id) { +static bool stockpiles_import(color_ostream& out, string fname, int id, string mode_str, string filter) { df::building_stockpilest* sp = get_stockpile(id); if (!sp) { out.printerr("Specified building isn't a stockpile: %d.\n", id); @@ -123,9 +123,15 @@ static bool stockpiles_import(color_ostream& out, string fname, int id) { return false; } + DeserializeMode mode = DESERIALIZE_MODE_SET; + if (mode_str == "enable") + mode = DESERIALIZE_MODE_ENABLE; + else if (mode_str == "disable") + mode = DESERIALIZE_MODE_DISABLE; + try { StockpileSerializer cereal(sp); - if (!cereal.unserialize_from_file(fname)) { + if (!cereal.unserialize_from_file(fname, mode, filter)) { out.printerr("deserialization failed: '%s'\n", fname.c_str()); return false; } From 1c527ab3d855211738de748f78f0312122413bbc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 19 Mar 2023 17:31:35 -0700 Subject: [PATCH 0859/2222] rejiggered --- plugins/stockpiles/StockpileSerializer.cpp | 2056 ++++++++++---------- plugins/stockpiles/StockpileSerializer.h | 104 +- 2 files changed, 1081 insertions(+), 1079 deletions(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 04e7cb9fa..3b2849333 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -554,75 +554,91 @@ void StockpileSerializer::write(uint32_t includedElements) { bool include_types = 0 != (includedElements & INCLUDED_ELEMENTS_TYPES); - write_cat(include_types, mPile->settings.flags.whole, + write_cat(include_types, + mPile->settings.flags.whole, mPile->settings.flags.mask_ammo, std::bind(&StockpileSettings::mutable_ammo, mBuffer), std::bind(&StockpileSerializer::write_ammo, this, _1)); - - if (mPile->settings.flags.bits.animals) { - StockpileSettings::AnimalsSet* animals = mBuffer.mutable_animals(); - if (include_types) write_animals(animals); else animals->set_all(true); - } - if (mPile->settings.flags.bits.armor) { - StockpileSettings::ArmorSet* armor = mBuffer.mutable_armor(); - if (include_types) write_armor(armor); else armor->set_all(true); - } - if (mPile->settings.flags.bits.bars_blocks) { - StockpileSettings::BarsBlocksSet* bars_blocks = mBuffer.mutable_barsblocks(); - if (include_types) write_bars_blocks(bars_blocks); else bars_blocks->set_all(true); - } - if (mPile->settings.flags.bits.cloth) { - StockpileSettings::ClothSet* cloth = mBuffer.mutable_cloth(); - if (include_types) write_cloth(cloth); else cloth->set_all(true); - } - if (mPile->settings.flags.bits.coins) { - StockpileSettings::CoinSet* coins = mBuffer.mutable_coin(); - if (include_types) write_coins(coins); else coins->set_all(true); - } - if (mPile->settings.flags.bits.finished_goods) { - StockpileSettings::FinishedGoodsSet* finished_goods = mBuffer.mutable_finished_goods(); - if (include_types) write_finished_goods(finished_goods); else finished_goods->set_all(true); - } - if (mPile->settings.flags.bits.food) { - StockpileSettings::FoodSet* food = mBuffer.mutable_food(); - if (include_types) write_food(food); else food->set_all(true); - } - if (mPile->settings.flags.bits.furniture) { - StockpileSettings::FurnitureSet* furniture = mBuffer.mutable_furniture(); - if (include_types) write_furniture(furniture); else furniture->set_all(true); - } - if (mPile->settings.flags.bits.gems) { - StockpileSettings::GemsSet* gems = mBuffer.mutable_gems(); - if (include_types) write_gems(gems); else gems->set_all(true); - } - if (mPile->settings.flags.bits.leather) { - StockpileSettings::LeatherSet* leather = mBuffer.mutable_leather(); - if (include_types) write_leather(leather); else leather->set_all(true); - } - if (mPile->settings.flags.bits.corpses) { - StockpileSettings::CorpsesSet* corpses = mBuffer.mutable_corpses_v50(); - if (include_types) write_corpses(corpses); else corpses->set_all(true); - } - if (mPile->settings.flags.bits.refuse) { - StockpileSettings::RefuseSet* refuse = mBuffer.mutable_refuse(); - if (include_types) write_refuse(refuse); else refuse->set_all(true); - } - if (mPile->settings.flags.bits.sheet) { - StockpileSettings::SheetSet* sheet = mBuffer.mutable_sheet(); - if (include_types) write_sheet(sheet); else sheet->set_all(true); - } - if (mPile->settings.flags.bits.stone) { - StockpileSettings::StoneSet* stone = mBuffer.mutable_stone(); - if (include_types) write_stone(stone); else stone->set_all(true); - } - if (mPile->settings.flags.bits.weapons) { - StockpileSettings::WeaponsSet* weapons = mBuffer.mutable_weapons(); - if (include_types) write_weapons(weapons); else weapons->set_all(true); - } - if (mPile->settings.flags.bits.wood) { - StockpileSettings::WoodSet* wood = mBuffer.mutable_wood(); - if (include_types) write_wood(wood); else wood->set_all(true); - } + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_animals, + std::bind(&StockpileSettings::mutable_animals, mBuffer), + std::bind(&StockpileSerializer::write_animals, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_armor, + std::bind(&StockpileSettings::mutable_armor, mBuffer), + std::bind(&StockpileSerializer::write_armor, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_bars_blocks, + std::bind(&StockpileSettings::mutable_barsblocks, mBuffer), + std::bind(&StockpileSerializer::write_bars_blocks, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_cloth, + std::bind(&StockpileSettings::mutable_cloth, mBuffer), + std::bind(&StockpileSerializer::write_cloth, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_coins, + std::bind(&StockpileSettings::mutable_coin, mBuffer), + std::bind(&StockpileSerializer::write_coins, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_finished_goods, + std::bind(&StockpileSettings::mutable_finished_goods, mBuffer), + std::bind(&StockpileSerializer::write_finished_goods, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_food, + std::bind(&StockpileSettings::mutable_food, mBuffer), + std::bind(&StockpileSerializer::write_food, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_furniture, + std::bind(&StockpileSettings::mutable_furniture, mBuffer), + std::bind(&StockpileSerializer::write_furniture, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_gems, + std::bind(&StockpileSettings::mutable_gems, mBuffer), + std::bind(&StockpileSerializer::write_gems, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_leather, + std::bind(&StockpileSettings::mutable_leather, mBuffer), + std::bind(&StockpileSerializer::write_leather, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_corpses, + std::bind(&StockpileSettings::mutable_corpses_v50, mBuffer), + std::bind(&StockpileSerializer::write_corpses, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_refuse, + std::bind(&StockpileSettings::mutable_refuse, mBuffer), + std::bind(&StockpileSerializer::write_refuse, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_sheet, + std::bind(&StockpileSettings::mutable_sheet, mBuffer), + std::bind(&StockpileSerializer::write_sheet, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_stone, + std::bind(&StockpileSettings::mutable_stone, mBuffer), + std::bind(&StockpileSerializer::write_stone, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_weapons, + std::bind(&StockpileSettings::mutable_weapons, mBuffer), + std::bind(&StockpileSerializer::write_weapons, this, _1)); + write_cat(include_types, + mPile->settings.flags.whole, + mPile->settings.flags.mask_wood, + std::bind(&StockpileSettings::mutable_wood, mBuffer), + std::bind(&StockpileSerializer::write_wood, this, _1)); } void StockpileSerializer::read(DeserializeMode mode, const std::string& filter) { @@ -858,16 +874,22 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil }); } -void StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { - animals->set_empty_cages(mPile->settings.animals.empty_cages); - animals->set_empty_traps(mPile->settings.animals.empty_traps); - for (size_t i = 0; i < mPile->settings.animals.enabled.size(); ++i) { - if (mPile->settings.animals.enabled.at(i) == 1) { - df::creature_raw* r = find_creature(i); - DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); - animals->add_enabled(r->creature_id); +bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { + auto & panimals = mPile->settings.animals; + bool all = panimals.empty_cages && panimals.empty_traps; + + animals->set_empty_cages(panimals.empty_cages); + animals->set_empty_traps(panimals.empty_traps); + for (size_t i = 0; i < panimals.enabled.size(); ++i) { + if (!panimals.enabled.at(i)) { + all = false; + continue; } + df::creature_raw* r = find_creature(i); + DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + animals->add_enabled(r->creature_id); } + return all; } void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& filter) { @@ -905,645 +927,792 @@ void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& }); } -food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { - using df::enums::organic_mat_category::organic_mat_category; - using namespace std::placeholders; - switch (cat) { - case organic_mat_category::Meat: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_meat(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; - return food_pair(setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); - } - case organic_mat_category::Fish: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_fish(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; - return food_pair(setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); - } - case organic_mat_category::UnpreparedFish: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_unprepared_fish(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; - return food_pair(setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); - } - case organic_mat_category::Eggs: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_egg(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; - return food_pair(setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); - } - case organic_mat_category::Plants: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_plants(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; - return food_pair(setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); - } - case organic_mat_category::PlantDrink: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_drink_plant(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; - return food_pair(setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); - } - case organic_mat_category::CreatureDrink: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_drink_animal(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; - return food_pair(setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); - } - case organic_mat_category::PlantCheese: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_cheese_plant(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; - return food_pair(setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); - } - case organic_mat_category::CreatureCheese: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_cheese_animal(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; - return food_pair(setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); - } - case organic_mat_category::Seed: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_seeds(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; - return food_pair(setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); - } - case organic_mat_category::Leaf: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_leaves(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; - return food_pair(setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); - } - case organic_mat_category::PlantPowder: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_powder_plant(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; - return food_pair(setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); - } - case organic_mat_category::CreaturePowder: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_powder_creature(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; - return food_pair(setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); - } - case organic_mat_category::Glob: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_glob(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; - return food_pair(setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); - } - case organic_mat_category::PlantLiquid: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_liquid_plant(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; - return food_pair(setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); - } - case organic_mat_category::CreatureLiquid: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_liquid_animal(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; - return food_pair(setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); - } - case organic_mat_category::MiscLiquid: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_liquid_misc(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; - return food_pair(setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); - } - - case organic_mat_category::Paste: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_glob_paste(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; - return food_pair(setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); - } - case organic_mat_category::Pressed: - { - FuncWriteExport setter = [=](const std::string& id) { - mBuffer.mutable_food()->add_glob_pressed(id); - }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; - return food_pair(setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); - } - case organic_mat_category::Leather: - case organic_mat_category::Silk: - case organic_mat_category::PlantFiber: - case organic_mat_category::Bone: - case organic_mat_category::Shell: - case organic_mat_category::Wood: - case organic_mat_category::Horn: - case organic_mat_category::Pearl: - case organic_mat_category::Tooth: - case organic_mat_category::EdibleCheese: - case organic_mat_category::AnyDrink: - case organic_mat_category::EdiblePlant: - case organic_mat_category::CookableLiquid: - case organic_mat_category::CookablePowder: - case organic_mat_category::CookableSeed: - case organic_mat_category::CookableLeaf: - case organic_mat_category::Yarn: - case organic_mat_category::MetalThread: - default: - // not used in stockpile food menu - break; - } - return food_pair(); +static bool armor_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { - DEBUG(log).print("food:\n"); - food->set_prepared_meals(mPile->settings.food.prepared_meals); - - using df::enums::organic_mat_category::organic_mat_category; - using traits = df::enum_traits; - for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { - food_pair p = food_map((organic_mat_category)mat_category); - if (!p.valid) - continue; - DEBUG(log).print("food: %s\n", traits::key_table[mat_category]); - serialize_list_organic_mat(p.set_value, p.stockpile_values, (organic_mat_category)mat_category); - } -} +bool StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { -void StockpileSerializer::read_food(DeserializeMode mode, const std::string& filter) { - using df::enums::organic_mat_category::organic_mat_category; - using traits = df::enum_traits; - if (mBuffer.has_food()) { - mPile->settings.flags.bits.food = 1; - const StockpileSettings::FoodSet food = mBuffer.food(); - DEBUG(log).print("food:\n"); + auto & parmor = mPile->settings.armor; + bool all = parmor.unusable && parmor.usable; - if (food.has_prepared_meals()) - mPile->settings.food.prepared_meals = food.prepared_meals(); - else - mPile->settings.food.prepared_meals = true; + armor->set_unusable(parmor.unusable); + armor->set_usable(parmor.usable); - DEBUG(log).print("prepared_meals: %d\n", mPile->settings.food.prepared_meals); + // armor type + all = serialize_list_itemdef( + [=](const std::string& token) { armor->add_body(token); }, + parmor.body, + std::vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), + item_type::ARMOR) && all; - for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { - food_pair p = food_map((organic_mat_category)mat_category); - if (!p.valid) - continue; - unserialize_list_organic_mat(p.get_value, p.serialized_count, p.stockpile_values, (organic_mat_category)mat_category); - } - } - else { - for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { - food_pair p = food_map((organic_mat_category)mat_category); - if (!p.valid) - continue; - p.stockpile_values->clear(); - } - mPile->settings.flags.bits.food = 0; - mPile->settings.food.prepared_meals = false; - } -} + // helm type + all = serialize_list_itemdef( + [=](const std::string& token) { armor->add_head(token); }, + parmor.head, + std::vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), + item_type::HELM) && all; -void StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furniture) { - // FURNITURE type - using df::enums::furniture_type::furniture_type; - using type_traits = df::enum_traits; - for (size_t i = 0; i < mPile->settings.furniture.type.size(); ++i) { - if (mPile->settings.furniture.type.at(i)) { - std::string f_type(type_traits::key_table[i]); - furniture->add_type(f_type); - DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); - } - } - // metal, stone/clay materials - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::furniture_mat_is_allowed, this, _1); - serialize_list_material( - filter, [=](const std::string& token) { furniture->add_mats(token); }, - mPile->settings.furniture.mats); + // shoes type + all = serialize_list_itemdef( + [=](const std::string& token) { armor->add_feet(token); }, + parmor.feet, + std::vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), + item_type::SHOES) && all; + + // gloves type + all = serialize_list_itemdef( + [=](const std::string& token) { armor->add_hands(token); }, + parmor.hands, + std::vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), + item_type::GLOVES) && all; + + // pant type + all = serialize_list_itemdef( + [=](const std::string& token) { armor->add_legs(token); }, + parmor.legs, + std::vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), + item_type::PANTS) && all; + + // shield type + all = serialize_list_itemdef( + [=](const std::string& token) { armor->add_shield(token); }, + parmor.shield, + std::vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), + item_type::SHIELD) && all; + + // materials + all = serialize_list_material( + armor_mat_is_allowed, + [=](const std::string& token) { armor->add_mats(token); }, + parmor.mats) && all; // other mats - serialize_list_other_mats( - mOtherMatsFurniture.mats, [=](const std::string& token) { furniture->add_other_mats(token); }, - mPile->settings.furniture.other_mats); + all = serialize_list_other_mats( + mOtherMatsWeaponsArmor.mats, [=](const std::string& token) { armor->add_other_mats(token); }, + parmor.other_mats) && all; - serialize_list_quality([=](const std::string& token) { furniture->add_quality_core(token); }, - mPile->settings.furniture.quality_core); - serialize_list_quality([=](const std::string& token) { furniture->add_quality_total(token); }, - mPile->settings.furniture.quality_total); -} + // quality core + all = serialize_list_quality([=](const std::string& token) { armor->add_quality_core(token); }, + parmor.quality_core) && all; -bool StockpileSerializer::furniture_mat_is_allowed(const MaterialInfo& mi) { - return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); + // quality total + all = serialize_list_quality([=](const std::string& token) { armor->add_quality_total(token); }, + parmor.quality_total) && all; + + return all; } -void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_furniture()) { - mPile->settings.flags.bits.furniture = 1; - const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); - DEBUG(log).print("furniture:\n"); +void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_armor()) { + mPile->settings.flags.bits.armor = 1; + const StockpileSettings::ArmorSet armor = mBuffer.armor(); + DEBUG(log).print("armor:\n"); - // type - using df::enums::furniture_type::furniture_type; - df::enum_traits type_traits; - mPile->settings.furniture.type.clear(); - mPile->settings.furniture.type.resize(type_traits.last_item_value + 1, '\0'); - if (furniture.type_size() > 0) { - for (int i = 0; i < furniture.type_size(); ++i) { - const std::string type = furniture.type(i); - df::enum_traits::base_type idx = linear_index(type_traits, type); - DEBUG(log).print("type %d is %s\n", idx, type.c_str()); - if (idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size()) { - WARN(log).print("furniture type index invalid %s, idx=%d\n", type.c_str(), idx); - continue; - } - mPile->settings.furniture.type.at(idx) = 1; - } - } + bool unusable = armor.unusable(); + bool usable = armor.usable(); + DEBUG(log).print("unusable %d\n", unusable); + DEBUG(log).print("usable %d\n", usable); + mPile->settings.armor.unusable = unusable; + mPile->settings.armor.usable = usable; + + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.body(idx); }, + armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR); + + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.head(idx); }, + armor.head_size(), &mPile->settings.armor.head, item_type::HELM); + + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.feet(idx); }, + armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES); + + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.hands(idx); }, + armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES); + + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.legs(idx); }, + armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS); + + unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.shield(idx); }, + armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD); - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::furniture_mat_is_allowed, this, _1); unserialize_list_material( - filter, [=](const size_t& idx) -> const std::string& { return furniture.mats(idx); }, - furniture.mats_size(), &mPile->settings.furniture.mats); + armor_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return armor.mats(idx); }, + armor.mats_size(), &mPile->settings.armor.mats); - // other materials unserialize_list_other_mats( - mOtherMatsFurniture.mats, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, - furniture.other_mats_size(), &mPile->settings.furniture.other_mats); + mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, + armor.other_mats_size(), &mPile->settings.armor.other_mats); - // core quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_core(idx); }, - furniture.quality_core_size(), mPile->settings.furniture.quality_core); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_core(idx); }, + armor.quality_core_size(), mPile->settings.armor.quality_core); - // total quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_total(idx); }, - furniture.quality_total_size(), mPile->settings.furniture.quality_total); + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_total(idx); }, + armor.quality_total_size(), mPile->settings.armor.quality_total); } else { - mPile->settings.flags.bits.furniture = 0; - mPile->settings.furniture.type.clear(); - mPile->settings.furniture.other_mats.clear(); - mPile->settings.furniture.mats.clear(); - quality_clear(mPile->settings.furniture.quality_core); - quality_clear(mPile->settings.furniture.quality_total); + mPile->settings.flags.bits.armor = 0; + mPile->settings.armor.body.clear(); + mPile->settings.armor.head.clear(); + mPile->settings.armor.feet.clear(); + mPile->settings.armor.hands.clear(); + mPile->settings.armor.legs.clear(); + mPile->settings.armor.shield.clear(); + mPile->settings.armor.other_mats.clear(); + mPile->settings.armor.mats.clear(); + quality_clear(mPile->settings.armor.quality_core); + quality_clear(mPile->settings.armor.quality_total); } } -bool StockpileSerializer::refuse_creature_is_allowed(const df::creature_raw* raw) { - if (!raw) - return false; - // wagon and generated creatures not allowed, except angels - const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; - const bool is_generated = raw->flags.is_set(creature_raw_flags::GENERATED); - const bool is_angel = is_generated && raw->creature_id.find("DIVINE_") != std::string::npos; - return !is_wagon && !(is_generated && !is_angel); +static bool bars_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -void StockpileSerializer::refuse_write_helper(std::function add_value, const vector& list) { - for (size_t i = 0; i < list.size(); ++i) { - if (list.at(i) == 1) { - df::creature_raw* r = find_creature(i); - // skip forgotten beasts, titans, demons, and night creatures - if (!refuse_creature_is_allowed(r)) - continue; - DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); - add_value(r->creature_id); - } - } +static bool blocks_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::refuse_type_is_allowed(item_type::item_type type) { - if (type == item_type::NONE || type == item_type::BAR || type == item_type::SMALLGEM || type == item_type::BLOCKS || type == item_type::ROUGH || type == item_type::BOULDER || type == item_type::CORPSE || type == item_type::CORPSEPIECE || type == item_type::ROCK || type == item_type::ORTHOPEDIC_CAST) - return false; - return true; -} +bool StockpileSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { + bool all = serialize_list_material( + bars_mat_is_allowed, + [=](const std::string& token) { bars_blocks->add_bars_mats(token); }, + mPile->settings.bars_blocks.bars_mats); -void StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { - DEBUG(log).print("refuse:\n"); - refuse->set_fresh_raw_hide(mPile->settings.refuse.fresh_raw_hide); - refuse->set_rotten_raw_hide(mPile->settings.refuse.rotten_raw_hide); + all = serialize_list_material( + blocks_mat_is_allowed, + [=](const std::string& token) { bars_blocks->add_blocks_mats(token); }, + mPile->settings.bars_blocks.blocks_mats) && all; - // type - DEBUG(log).print("getting types\n"); - FuncItemAllowed filter = std::bind(&StockpileSerializer::refuse_type_is_allowed, this, _1); - serialize_list_item_type( - filter, [=](const std::string& token) { - DEBUG(log).print("adding type: %s\n", token.c_str()); - refuse->add_type(token); - }, - mPile->settings.refuse.type); - - // corpses - refuse_write_helper([=](const std::string& id) { refuse->add_corpses(id); }, - mPile->settings.refuse.corpses); - // body_parts - refuse_write_helper([=](const std::string& id) { refuse->add_body_parts(id); }, - mPile->settings.refuse.body_parts); - // skulls - refuse_write_helper([=](const std::string& id) { refuse->add_skulls(id); }, - mPile->settings.refuse.skulls); - // bones - refuse_write_helper([=](const std::string& id) { refuse->add_bones(id); }, - mPile->settings.refuse.bones); - // hair - refuse_write_helper([=](const std::string& id) { refuse->add_hair(id); }, - mPile->settings.refuse.hair); - // shells - refuse_write_helper([=](const std::string& id) { refuse->add_shells(id); }, - mPile->settings.refuse.shells); - // teeth - refuse_write_helper([=](const std::string& id) { refuse->add_teeth(id); }, - mPile->settings.refuse.teeth); - // horns - refuse_write_helper([=](const std::string& id) { refuse->add_horns(id); }, - mPile->settings.refuse.horns); -} - -void StockpileSerializer::refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list) { - pile_list->clear(); - pile_list->resize(world->raws.creatures.all.size(), '\0'); - if (list_size > 0) { - for (size_t i = 0; i < list_size; ++i) { - const std::string creature_id = get_value(i); - const int idx = find_creature(creature_id); - const df::creature_raw* creature = find_creature(idx); - if (idx < 0 || !refuse_creature_is_allowed(creature) || size_t(idx) >= pile_list->size()) { - WARN(log).print("invalid refuse creature %s, idx=%d\n", creature_id.c_str(), idx); - continue; - } - DEBUG(log).print("creature %d is %s\n", idx, creature_id.c_str()); - pile_list->at(idx) = 1; - } - } + all = serialize_list_other_mats( + mOtherMatsBars.mats, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, + mPile->settings.bars_blocks.bars_other_mats) && all; + + all = serialize_list_other_mats( + mOtherMatsBlocks.mats, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, + mPile->settings.bars_blocks.blocks_other_mats) && all; + + return all; } -void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_refuse()) { - mPile->settings.flags.bits.refuse = 1; - const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); - DEBUG(log).print("refuse:\n"); - DEBUG(log).print(" fresh hide %d\n", refuse.fresh_raw_hide()); - DEBUG(log).print(" rotten hide %d\n", refuse.rotten_raw_hide()); - mPile->settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); - mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); +void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_barsblocks()) { + mPile->settings.flags.bits.bars_blocks = 1; + const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); + DEBUG(log).print("bars_blocks:\n"); - // type - FuncItemAllowed filter = std::bind(&StockpileSerializer::refuse_type_is_allowed, this, _1); - unserialize_list_item_type( - filter, [=](const size_t& idx) -> const std::string& { return refuse.type(idx); }, - refuse.type_size(), &mPile->settings.refuse.type); + unserialize_list_material( + bars_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_mats(idx); }, + bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats); - // corpses - DEBUG(log).print(" corpses\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.corpses(idx); }, - refuse.corpses_size(), &mPile->settings.refuse.corpses); - // body_parts - DEBUG(log).print(" body_parts\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.body_parts(idx); }, - refuse.body_parts_size(), &mPile->settings.refuse.body_parts); - // skulls - DEBUG(log).print(" skulls\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.skulls(idx); }, - refuse.skulls_size(), &mPile->settings.refuse.skulls); - // bones - DEBUG(log).print(" bones\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.bones(idx); }, - refuse.bones_size(), &mPile->settings.refuse.bones); - // hair - DEBUG(log).print(" hair\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.hair(idx); }, - refuse.hair_size(), &mPile->settings.refuse.hair); - // shells - DEBUG(log).print(" shells\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.shells(idx); }, - refuse.shells_size(), &mPile->settings.refuse.shells); - // teeth - DEBUG(log).print(" teeth\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.teeth(idx); }, - refuse.teeth_size(), &mPile->settings.refuse.teeth); - // horns - DEBUG(log).print(" horns\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.horns(idx); }, - refuse.horns_size(), &mPile->settings.refuse.horns); + unserialize_list_material( + blocks_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_mats(idx); }, + bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats); + + unserialize_list_other_mats( + mOtherMatsBars.mats, + [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, + bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats); + + unserialize_list_other_mats( + mOtherMatsBlocks.mats, + [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, + bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats); } else { - mPile->settings.flags.bits.refuse = 0; - mPile->settings.refuse.type.clear(); - mPile->settings.refuse.corpses.clear(); - mPile->settings.refuse.body_parts.clear(); - mPile->settings.refuse.skulls.clear(); - mPile->settings.refuse.bones.clear(); - mPile->settings.refuse.hair.clear(); - mPile->settings.refuse.shells.clear(); - mPile->settings.refuse.teeth.clear(); - mPile->settings.refuse.horns.clear(); - mPile->settings.refuse.fresh_raw_hide = false; - mPile->settings.refuse.rotten_raw_hide = false; + mPile->settings.flags.bits.bars_blocks = 0; + mPile->settings.bars_blocks.bars_other_mats.clear(); + mPile->settings.bars_blocks.bars_mats.clear(); + mPile->settings.bars_blocks.blocks_other_mats.clear(); + mPile->settings.bars_blocks.blocks_mats.clear(); } } -bool StockpileSerializer::stone_is_allowed(const MaterialInfo& mi) { - if (!mi.isValid()) - return false; - const bool is_allowed_soil = mi.inorganic->flags.is_set(inorganic_flags::SOIL) && !mi.inorganic->flags.is_set(inorganic_flags::AQUIFER); - const bool is_allowed_stone = mi.material->flags.is_set(material_flags::IS_STONE) && !mi.material->flags.is_set(material_flags::NO_STONE_STOCKPILE); - return is_allowed_soil || is_allowed_stone; -} +bool StockpileSerializer::write_cloth(StockpileSettings::ClothSet* cloth) { + bool all = true; -void StockpileSerializer::write_stone(StockpileSettings::StoneSet* stone) { - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); - serialize_list_material( - filter, [=](const std::string& token) { stone->add_mats(token); }, - mPile->settings.stone.mats); -} + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_thread_silk(token); }, + &mPile->settings.cloth.thread_silk, organic_mat_category::Silk) && all; -void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_stone()) { - mPile->settings.flags.bits.stone = 1; - const StockpileSettings::StoneSet stone = mBuffer.stone(); - DEBUG(log).print("stone:\n"); + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_thread_plant(token); }, + &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber) && all; - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::stone_is_allowed, this, _1); - unserialize_list_material( - filter, [=](const size_t& idx) -> const std::string& { return stone.mats(idx); }, - stone.mats_size(), &mPile->settings.stone.mats); - } - else { - mPile->settings.flags.bits.stone = 0; - mPile->settings.stone.mats.clear(); - } -} + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_thread_yarn(token); }, + &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn) && all; -bool StockpileSerializer::coins_mat_is_allowed(const MaterialInfo& mi) { - return mi.isValid(); -} + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_thread_metal(token); }, + &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread) && all; -void StockpileSerializer::write_coins(StockpileSettings::CoinSet* coins) { - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); - serialize_list_material( - filter, [=](const std::string& token) { coins->add_mats(token); }, - mPile->settings.coins.mats); + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_cloth_silk(token); }, + &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk) && all; + + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_cloth_plant(token); }, + &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber) && all; + + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_cloth_yarn(token); }, + &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn) && all; + + all = serialize_list_organic_mat( + [=](const std::string& token) { cloth->add_cloth_metal(token); }, + &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread) && all; + + return all; } -void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_coin()) { - mPile->settings.flags.bits.coins = 1; - const StockpileSettings::CoinSet coins = mBuffer.coin(); - DEBUG(log).print("coins:\n"); +void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_cloth()) { + mPile->settings.flags.bits.cloth = 1; + const StockpileSettings::ClothSet cloth = mBuffer.cloth(); + DEBUG(log).print("cloth:\n"); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_silk(idx); }, + cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_plant(idx); }, + cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_yarn(idx); }, + cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_metal(idx); }, + cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_silk(idx); }, + cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_plant(idx); }, + cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_yarn(idx); }, + cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + + unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_metal(idx); }, + cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); + } + else { + mPile->settings.cloth.thread_metal.clear(); + mPile->settings.cloth.thread_plant.clear(); + mPile->settings.cloth.thread_silk.clear(); + mPile->settings.cloth.thread_yarn.clear(); + mPile->settings.cloth.cloth_metal.clear(); + mPile->settings.cloth.cloth_plant.clear(); + mPile->settings.cloth.cloth_silk.clear(); + mPile->settings.cloth.cloth_yarn.clear(); + mPile->settings.flags.bits.cloth = 0; + } +} + +static bool coins_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid(); +} + +bool StockpileSerializer::write_coins(StockpileSettings::CoinSet* coins) { + return serialize_list_material( + coins_mat_is_allowed, + [=](const std::string& token) { coins->add_mats(token); }, + mPile->settings.coins.mats); +} + +void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_coin()) { + mPile->settings.flags.bits.coins = 1; + const StockpileSettings::CoinSet coins = mBuffer.coin(); + DEBUG(log).print("coins:\n"); + + unserialize_list_material( + coins_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return coins.mats(idx); }, + coins.mats_size(), &mPile->settings.coins.mats); + } + else { + mPile->settings.flags.bits.coins = 0; + mPile->settings.coins.mats.clear(); + } +} + +static bool finished_goods_type_is_allowed(item_type::item_type type) { + switch (type) { + case item_type::CHAIN: + case item_type::FLASK: + case item_type::GOBLET: + case item_type::INSTRUMENT: + case item_type::TOY: + case item_type::ARMOR: + case item_type::SHOES: + case item_type::HELM: + case item_type::GLOVES: + case item_type::FIGURINE: + case item_type::AMULET: + case item_type::SCEPTER: + case item_type::CROWN: + case item_type::RING: + case item_type::EARRING: + case item_type::BRACELET: + case item_type::GEM: + case item_type::TOTEM: + case item_type::PANTS: + case item_type::BACKPACK: + case item_type::QUIVER: + case item_type::SPLINT: + case item_type::CRUTCH: + case item_type::TOOL: + case item_type::BOOK: + return true; + default: + return false; + } +} + +static bool finished_goods_mat_is_allowed(const MaterialInfo& mi) { + return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); +} + +bool StockpileSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { + bool all = serialize_list_item_type( + finished_goods_type_is_allowed, + [=](const std::string& token) { finished_goods->add_type(token); }, + mPile->settings.finished_goods.type); + + all = serialize_list_material( + finished_goods_mat_is_allowed, + [=](const std::string& token) { finished_goods->add_mats(token); }, + mPile->settings.finished_goods.mats) && all; + + all = serialize_list_other_mats( + mOtherMatsFinishedGoods.mats, [=](const std::string& token) { finished_goods->add_other_mats(token); }, + mPile->settings.finished_goods.other_mats) && all; + + all = serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_core(token); }, + mPile->settings.finished_goods.quality_core) && all; + + all = serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_total(token); }, + mPile->settings.finished_goods.quality_total) && all; + + return all; +} + +void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_finished_goods()) { + mPile->settings.flags.bits.finished_goods = 1; + const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); + DEBUG(log).print("finished_goods:\n"); + + unserialize_list_item_type( + finished_goods_type_is_allowed, + [=](const size_t& idx) -> const std::string& { return finished_goods.type(idx); }, + finished_goods.type_size(), &mPile->settings.finished_goods.type); + + unserialize_list_material( + finished_goods_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return finished_goods.mats(idx); }, + finished_goods.mats_size(), &mPile->settings.finished_goods.mats); + + unserialize_list_other_mats( + mOtherMatsFinishedGoods.mats, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, + finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats); + + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_core(idx); }, + finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core); + + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_total(idx); }, + finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total); + } + else { + mPile->settings.flags.bits.finished_goods = 0; + mPile->settings.finished_goods.type.clear(); + mPile->settings.finished_goods.other_mats.clear(); + mPile->settings.finished_goods.mats.clear(); + quality_clear(mPile->settings.finished_goods.quality_core); + quality_clear(mPile->settings.finished_goods.quality_total); + } +} + +food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { + using df::enums::organic_mat_category::organic_mat_category; + + switch (cat) { + case organic_mat_category::Meat: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_meat(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; + return food_pair(setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); + } + case organic_mat_category::Fish: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_fish(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; + return food_pair(setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); + } + case organic_mat_category::UnpreparedFish: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_unprepared_fish(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; + return food_pair(setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); + } + case organic_mat_category::Eggs: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_egg(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; + return food_pair(setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); + } + case organic_mat_category::Plants: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_plants(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; + return food_pair(setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); + } + case organic_mat_category::PlantDrink: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_drink_plant(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; + return food_pair(setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); + } + case organic_mat_category::CreatureDrink: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_drink_animal(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; + return food_pair(setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); + } + case organic_mat_category::PlantCheese: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_cheese_plant(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; + return food_pair(setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); + } + case organic_mat_category::CreatureCheese: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_cheese_animal(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; + return food_pair(setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); + } + case organic_mat_category::Seed: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_seeds(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; + return food_pair(setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); + } + case organic_mat_category::Leaf: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_leaves(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; + return food_pair(setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); + } + case organic_mat_category::PlantPowder: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_powder_plant(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; + return food_pair(setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); + } + case organic_mat_category::CreaturePowder: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_powder_creature(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; + return food_pair(setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); + } + case organic_mat_category::Glob: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; + return food_pair(setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); + } + case organic_mat_category::PlantLiquid: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_plant(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); + } + case organic_mat_category::CreatureLiquid: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_animal(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); + } + case organic_mat_category::MiscLiquid: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_liquid_misc(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; + return food_pair(setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); + } + + case organic_mat_category::Paste: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob_paste(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; + return food_pair(setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); + } + case organic_mat_category::Pressed: + { + FuncWriteExport setter = [=](const std::string& id) { + mBuffer.mutable_food()->add_glob_pressed(id); + }; + FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; + return food_pair(setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); + } + case organic_mat_category::Leather: + case organic_mat_category::Silk: + case organic_mat_category::PlantFiber: + case organic_mat_category::Bone: + case organic_mat_category::Shell: + case organic_mat_category::Wood: + case organic_mat_category::Horn: + case organic_mat_category::Pearl: + case organic_mat_category::Tooth: + case organic_mat_category::EdibleCheese: + case organic_mat_category::AnyDrink: + case organic_mat_category::EdiblePlant: + case organic_mat_category::CookableLiquid: + case organic_mat_category::CookablePowder: + case organic_mat_category::CookableSeed: + case organic_mat_category::CookableLeaf: + case organic_mat_category::Yarn: + case organic_mat_category::MetalThread: + default: + // not used in stockpile food menu + break; + } + return food_pair(); +} + +bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { + DEBUG(log).print("food:\n"); + + auto & pfood = mPile->settings.food; + bool all = pfood.prepared_meals; + + food->set_prepared_meals(pfood.prepared_meals); + + using df::enums::organic_mat_category::organic_mat_category; + using traits = df::enum_traits; + for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { + food_pair p = food_map((organic_mat_category)mat_category); + if (!p.valid) + continue; + DEBUG(log).print("food: %s\n", traits::key_table[mat_category]); + all = serialize_list_organic_mat(p.set_value, p.stockpile_values, (organic_mat_category)mat_category) && all; + } + + return all; +} + +void StockpileSerializer::read_food(DeserializeMode mode, const std::string& filter) { + using df::enums::organic_mat_category::organic_mat_category; + using traits = df::enum_traits; + if (mBuffer.has_food()) { + mPile->settings.flags.bits.food = 1; + const StockpileSettings::FoodSet food = mBuffer.food(); + DEBUG(log).print("food:\n"); + + if (food.has_prepared_meals()) + mPile->settings.food.prepared_meals = food.prepared_meals(); + else + mPile->settings.food.prepared_meals = true; - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::coins_mat_is_allowed, this, _1); - unserialize_list_material( - filter, [=](const size_t& idx) -> const std::string& { return coins.mats(idx); }, - coins.mats_size(), &mPile->settings.coins.mats); + DEBUG(log).print("prepared_meals: %d\n", mPile->settings.food.prepared_meals); + + for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { + food_pair p = food_map((organic_mat_category)mat_category); + if (!p.valid) + continue; + unserialize_list_organic_mat(p.get_value, p.serialized_count, p.stockpile_values, (organic_mat_category)mat_category); + } } else { - mPile->settings.flags.bits.coins = 0; - mPile->settings.coins.mats.clear(); + for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { + food_pair p = food_map((organic_mat_category)mat_category); + if (!p.valid) + continue; + p.stockpile_values->clear(); + } + mPile->settings.flags.bits.food = 0; + mPile->settings.food.prepared_meals = false; } } -bool StockpileSerializer::bars_mat_is_allowed(const MaterialInfo& mi) { - return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); -} - -bool StockpileSerializer::blocks_mat_is_allowed(const MaterialInfo& mi) { +static bool furniture_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { - MaterialInfo mi; - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); - serialize_list_material( - filter, [=](const std::string& token) { bars_blocks->add_bars_mats(token); }, - mPile->settings.bars_blocks.bars_mats); +bool StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furniture) { + using df::enums::furniture_type::furniture_type; + using type_traits = df::enum_traits; - // blocks mats - filter = std::bind(&StockpileSerializer::blocks_mat_is_allowed, this, _1); - serialize_list_material( - filter, [=](const std::string& token) { bars_blocks->add_blocks_mats(token); }, - mPile->settings.bars_blocks.blocks_mats); + auto & pfurniture = mPile->settings.furniture; + bool all = true; - // bars other mats - serialize_list_other_mats( - mOtherMatsBars.mats, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, - mPile->settings.bars_blocks.bars_other_mats); + for (size_t i = 0; i < pfurniture.type.size(); ++i) { + if (!pfurniture.type.at(i)) { + all = false; + continue; + } + std::string f_type(type_traits::key_table[i]); + furniture->add_type(f_type); + DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); + } + all = serialize_list_material( + furniture_mat_is_allowed, + [=](const std::string& token) { furniture->add_mats(token); }, + pfurniture.mats) && all; + all = serialize_list_other_mats( + mOtherMatsFurniture.mats, + [=](const std::string& token) { furniture->add_other_mats(token); }, + pfurniture.other_mats) && all; + all = serialize_list_quality( + [=](const std::string& token) { furniture->add_quality_core(token); }, + pfurniture.quality_core) && all; + all = serialize_list_quality( + [=](const std::string& token) { furniture->add_quality_total(token); }, + pfurniture.quality_total) && all; - // blocks other mats - serialize_list_other_mats( - mOtherMatsBlocks.mats, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, - mPile->settings.bars_blocks.blocks_other_mats); + return all; } -void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_barsblocks()) { - mPile->settings.flags.bits.bars_blocks = 1; - const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); - DEBUG(log).print("bars_blocks:\n"); - // bars - FuncMaterialAllowed filter = std::bind(&StockpileSerializer::bars_mat_is_allowed, this, _1); - unserialize_list_material( - filter, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_mats(idx); }, - bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats); +void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_furniture()) { + mPile->settings.flags.bits.furniture = 1; + const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); + DEBUG(log).print("furniture:\n"); + + // type + using df::enums::furniture_type::furniture_type; + df::enum_traits type_traits; + mPile->settings.furniture.type.clear(); + mPile->settings.furniture.type.resize(type_traits.last_item_value + 1, '\0'); + if (furniture.type_size() > 0) { + for (int i = 0; i < furniture.type_size(); ++i) { + const std::string type = furniture.type(i); + df::enum_traits::base_type idx = linear_index(type_traits, type); + DEBUG(log).print("type %d is %s\n", idx, type.c_str()); + if (idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size()) { + WARN(log).print("furniture type index invalid %s, idx=%d\n", type.c_str(), idx); + continue; + } + mPile->settings.furniture.type.at(idx) = 1; + } + } - // blocks - filter = std::bind(&StockpileSerializer::blocks_mat_is_allowed, this, _1); unserialize_list_material( - filter, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_mats(idx); }, - bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats); - // bars other mats - unserialize_list_other_mats( - mOtherMatsBars.mats, [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, - bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats); + furniture_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return furniture.mats(idx); }, + furniture.mats_size(), &mPile->settings.furniture.mats); - // blocks other mats unserialize_list_other_mats( - mOtherMatsBlocks.mats, [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, - bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats); + mOtherMatsFurniture.mats, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, + furniture.other_mats_size(), &mPile->settings.furniture.other_mats); + + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_core(idx); }, + furniture.quality_core_size(), mPile->settings.furniture.quality_core); + + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_total(idx); }, + furniture.quality_total_size(), mPile->settings.furniture.quality_total); } else { - mPile->settings.flags.bits.bars_blocks = 0; - mPile->settings.bars_blocks.bars_other_mats.clear(); - mPile->settings.bars_blocks.bars_mats.clear(); - mPile->settings.bars_blocks.blocks_other_mats.clear(); - mPile->settings.bars_blocks.blocks_mats.clear(); + mPile->settings.flags.bits.furniture = 0; + mPile->settings.furniture.type.clear(); + mPile->settings.furniture.other_mats.clear(); + mPile->settings.furniture.mats.clear(); + quality_clear(mPile->settings.furniture.quality_core); + quality_clear(mPile->settings.furniture.quality_total); } } -bool StockpileSerializer::gem_mat_is_allowed(const MaterialInfo& mi) { +static bool gem_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_GEM); } -bool StockpileSerializer::gem_cut_mat_is_allowed(const MaterialInfo& mi) { + +static bool gem_cut_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::gem_other_mat_is_allowed(MaterialInfo& mi) { + +static bool gem_other_mat_is_allowed(MaterialInfo& mi) { return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); } -void StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { +bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { MaterialInfo mi; - // rough mats - FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); - serialize_list_material( - filter_rough, [=](const std::string& token) { gems->add_rough_mats(token); }, - mPile->settings.gems.rough_mats); - // cut mats - FuncMaterialAllowed filter_cut = std::bind(&StockpileSerializer::gem_cut_mat_is_allowed, this, _1); - serialize_list_material( - filter_cut, [=](const std::string& token) { gems->add_cut_mats(token); }, - mPile->settings.gems.cut_mats); - // rough other - for (size_t i = 0; i < mPile->settings.gems.rough_other_mats.size(); ++i) { - if (mPile->settings.gems.rough_other_mats.at(i)) { - mi.decode(i, -1); - if (!gem_other_mat_is_allowed(mi)) - continue; - DEBUG(log).print("gem rough_other mat %zd is %s\n", i, mi.getToken().c_str()); - gems->add_rough_other_mats(mi.getToken()); + + auto & pgems = mPile->settings.gems; + + bool all = serialize_list_material( + gem_mat_is_allowed, + [=](const std::string& token) { gems->add_rough_mats(token); }, + pgems.rough_mats); + + all = serialize_list_material( + gem_cut_mat_is_allowed, + [=](const std::string& token) { gems->add_cut_mats(token); }, + pgems.cut_mats) && all; + + for (size_t i = 0; i < pgems.rough_other_mats.size(); ++i) { + if (!pgems.rough_other_mats.at(i)) { + all = false; + continue; } + mi.decode(i, -1); + if (!gem_other_mat_is_allowed(mi)) + continue; + DEBUG(log).print("gem rough_other mat %zd is %s\n", i, mi.getToken().c_str()); + gems->add_rough_other_mats(mi.getToken()); } - // cut other - for (size_t i = 0; i < mPile->settings.gems.cut_other_mats.size(); ++i) { - if (mPile->settings.gems.cut_other_mats.at(i)) { - mi.decode(i, -1); - if (!mi.isValid()) - mi.decode(0, i); - if (!gem_other_mat_is_allowed(mi)) - continue; - DEBUG(log).print("gem cut_other mat %zd is %s\n", i, mi.getToken().c_str()); - gems->add_cut_other_mats(mi.getToken()); + + for (size_t i = 0; i < pgems.cut_other_mats.size(); ++i) { + if (!pgems.cut_other_mats.at(i)) { + all = false; + continue; } + mi.decode(i, -1); + if (!mi.isValid()) + mi.decode(0, i); + if (!gem_other_mat_is_allowed(mi)) + continue; + DEBUG(log).print("gem cut_other mat %zd is %s\n", i, mi.getToken().c_str()); + gems->add_cut_other_mats(mi.getToken()); } + + return all; } void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& filter) { @@ -1551,16 +1720,15 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil mPile->settings.flags.bits.gems = 1; const StockpileSettings::GemsSet gems = mBuffer.gems(); DEBUG(log).print("gems:\n"); - // rough - FuncMaterialAllowed filter_rough = std::bind(&StockpileSerializer::gem_mat_is_allowed, this, _1); + unserialize_list_material( - filter_rough, [=](const size_t& idx) -> const std::string& { return gems.rough_mats(idx); }, + gem_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return gems.rough_mats(idx); }, gems.rough_mats_size(), &mPile->settings.gems.rough_mats); - // cut - FuncMaterialAllowed filter_cut = std::bind(&StockpileSerializer::gem_cut_mat_is_allowed, this, _1); unserialize_list_material( - filter_cut, [=](const size_t& idx) -> const std::string& { return gems.cut_mats(idx); }, + gem_cut_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return gems.cut_mats(idx); }, gems.cut_mats_size(), &mPile->settings.gems.cut_mats); const size_t builtin_size = std::extentraws.mat_table.builtin)>::value; @@ -1603,278 +1771,272 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil } } -bool StockpileSerializer::finished_goods_type_is_allowed(item_type::item_type type) { - switch (type) { - case item_type::CHAIN: - case item_type::FLASK: - case item_type::GOBLET: - case item_type::INSTRUMENT: - case item_type::TOY: - case item_type::ARMOR: - case item_type::SHOES: - case item_type::HELM: - case item_type::GLOVES: - case item_type::FIGURINE: - case item_type::AMULET: - case item_type::SCEPTER: - case item_type::CROWN: - case item_type::RING: - case item_type::EARRING: - case item_type::BRACELET: - case item_type::GEM: - case item_type::TOTEM: - case item_type::PANTS: - case item_type::BACKPACK: - case item_type::QUIVER: - case item_type::SPLINT: - case item_type::CRUTCH: - case item_type::TOOL: - case item_type::BOOK: - return true; - default: - return false; - } +bool StockpileSerializer::write_leather(StockpileSettings::LeatherSet* leather) { + return serialize_list_organic_mat( + [=](const std::string& id) { leather->add_mats(id); }, + &mPile->settings.leather.mats, organic_mat_category::Leather); } -bool StockpileSerializer::finished_goods_mat_is_allowed(const MaterialInfo& mi) { - return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); -} +void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_leather()) { + mPile->settings.flags.bits.leather = 1; + const StockpileSettings::LeatherSet leather = mBuffer.leather(); + DEBUG(log).print("leather:\n"); -void StockpileSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { - // type - FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); - serialize_list_item_type( - filter, [=](const std::string& token) { finished_goods->add_type(token); }, - mPile->settings.finished_goods.type); + unserialize_list_organic_mat([=](size_t idx) -> std::string { return leather.mats(idx); }, + leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather); + } + else { + mPile->settings.flags.bits.leather = 0; + mPile->settings.leather.mats.clear(); + } +} - // materials - FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::finished_goods_mat_is_allowed, this, _1); - serialize_list_material( - mat_filter, [=](const std::string& token) { finished_goods->add_mats(token); }, - mPile->settings.finished_goods.mats); +bool StockpileSerializer::write_corpses(StockpileSettings::CorpsesSet* corpses) { + bool all = true; - // other mats - serialize_list_other_mats( - mOtherMatsFinishedGoods.mats, [=](const std::string& token) { finished_goods->add_other_mats(token); }, - mPile->settings.finished_goods.other_mats); + return all; +} - // quality core - serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_core(token); }, - mPile->settings.finished_goods.quality_core); +void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& filter) { - // quality total - serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_total(token); }, - mPile->settings.finished_goods.quality_total); } -void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_finished_goods()) { - mPile->settings.flags.bits.finished_goods = 1; - const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); - DEBUG(log).print("finished_goods:\n"); +static bool refuse_creature_is_allowed(const df::creature_raw* raw) { + if (!raw) + return false; + // wagon and generated creatures not allowed, except angels + const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; + const bool is_generated = raw->flags.is_set(creature_raw_flags::GENERATED); + const bool is_angel = is_generated && raw->creature_id.find("DIVINE_") != std::string::npos; + return !is_wagon && !(is_generated && !is_angel); +} - // type - FuncItemAllowed filter = std::bind(&StockpileSerializer::finished_goods_type_is_allowed, this, _1); - unserialize_list_item_type( - filter, [=](const size_t& idx) -> const std::string& { return finished_goods.type(idx); }, - finished_goods.type_size(), &mPile->settings.finished_goods.type); +static bool refuse_write_helper(std::function add_value, const vector& list) { + bool all = true; + for (size_t i = 0; i < list.size(); ++i) { + if (!list.at(i)) { + all = false; + continue; + } + df::creature_raw* r = find_creature(i); + // skip forgotten beasts, titans, demons, and night creatures + if (!refuse_creature_is_allowed(r)) + continue; + DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + add_value(r->creature_id); + } + return all; +} - // materials - FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::finished_goods_mat_is_allowed, this, _1); - unserialize_list_material( - mat_filter, [=](const size_t& idx) -> const std::string& { return finished_goods.mats(idx); }, - finished_goods.mats_size(), &mPile->settings.finished_goods.mats); +static bool refuse_type_is_allowed(item_type::item_type type) { + if (type == item_type::NONE || type == item_type::BAR || type == item_type::SMALLGEM || type == item_type::BLOCKS || type == item_type::ROUGH || type == item_type::BOULDER || type == item_type::CORPSE || type == item_type::CORPSEPIECE || type == item_type::ROCK || type == item_type::ORTHOPEDIC_CAST) + return false; + return true; +} - // other mats - unserialize_list_other_mats( - mOtherMatsFinishedGoods.mats, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, - finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats); +bool StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { + auto & prefuse = mPile->settings.refuse; + bool all = prefuse.fresh_raw_hide && prefuse.rotten_raw_hide; - // core quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_core(idx); }, - finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core); + DEBUG(log).print("refuse:\n"); + refuse->set_fresh_raw_hide(prefuse.fresh_raw_hide); + refuse->set_rotten_raw_hide(prefuse.rotten_raw_hide); - // total quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_total(idx); }, - finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total); - } - else { - mPile->settings.flags.bits.finished_goods = 0; - mPile->settings.finished_goods.type.clear(); - mPile->settings.finished_goods.other_mats.clear(); - mPile->settings.finished_goods.mats.clear(); - quality_clear(mPile->settings.finished_goods.quality_core); - quality_clear(mPile->settings.finished_goods.quality_total); - } -} + DEBUG(log).print("getting types\n"); + all = serialize_list_item_type( + refuse_type_is_allowed, + [=](const std::string& token) { + DEBUG(log).print("adding type: %s\n", token.c_str()); + refuse->add_type(token); + }, + prefuse.type) && all; + + all = refuse_write_helper([=](const std::string& id) { refuse->add_corpses(id); }, + prefuse.corpses) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_body_parts(id); }, + prefuse.body_parts) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_skulls(id); }, + prefuse.skulls) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_bones(id); }, + prefuse.bones) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_hair(id); }, + prefuse.hair) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_shells(id); }, + prefuse.shells) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_teeth(id); }, + prefuse.teeth) && all; + all = refuse_write_helper([=](const std::string& id) { refuse->add_horns(id); }, + prefuse.horns) && all; -void StockpileSerializer::write_leather(StockpileSettings::LeatherSet* leather) { - FuncWriteExport setter = [=](const std::string& id) { - leather->add_mats(id); - }; - serialize_list_organic_mat(setter, &mPile->settings.leather.mats, organic_mat_category::Leather); + return all; } -void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_leather()) { - mPile->settings.flags.bits.leather = 1; - const StockpileSettings::LeatherSet leather = mBuffer.leather(); - DEBUG(log).print("leather:\n"); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return leather.mats(idx); }, - leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather); - } - else { - mPile->settings.flags.bits.leather = 0; - mPile->settings.leather.mats.clear(); +static void refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list) { + pile_list->clear(); + pile_list->resize(world->raws.creatures.all.size(), '\0'); + if (list_size > 0) { + for (size_t i = 0; i < list_size; ++i) { + const std::string creature_id = get_value(i); + const int idx = find_creature(creature_id); + const df::creature_raw* creature = find_creature(idx); + if (idx < 0 || !refuse_creature_is_allowed(creature) || size_t(idx) >= pile_list->size()) { + WARN(log).print("invalid refuse creature %s, idx=%d\n", creature_id.c_str(), idx); + continue; + } + DEBUG(log).print("creature %d is %s\n", idx, creature_id.c_str()); + pile_list->at(idx) = 1; + } } } -void StockpileSerializer::write_cloth(StockpileSettings::ClothSet* cloth) { - serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_silk(token); }, - &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); - - serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_plant(token); }, - &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); - - serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_yarn(token); }, - &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); +void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_refuse()) { + mPile->settings.flags.bits.refuse = 1; + const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); + DEBUG(log).print("refuse:\n"); + DEBUG(log).print(" fresh hide %d\n", refuse.fresh_raw_hide()); + DEBUG(log).print(" rotten hide %d\n", refuse.rotten_raw_hide()); + mPile->settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); + mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); - serialize_list_organic_mat([=](const std::string& token) { cloth->add_thread_metal(token); }, - &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); + unserialize_list_item_type( + refuse_type_is_allowed, + [=](const size_t& idx) -> const std::string& { return refuse.type(idx); }, + refuse.type_size(), &mPile->settings.refuse.type); - serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_silk(token); }, - &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); + DEBUG(log).print(" corpses\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.corpses(idx); }, + refuse.corpses_size(), &mPile->settings.refuse.corpses); - serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_plant(token); }, - &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); + DEBUG(log).print(" body_parts\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.body_parts(idx); }, + refuse.body_parts_size(), &mPile->settings.refuse.body_parts); - serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_yarn(token); }, - &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + DEBUG(log).print(" skulls\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.skulls(idx); }, + refuse.skulls_size(), &mPile->settings.refuse.skulls); - serialize_list_organic_mat([=](const std::string& token) { cloth->add_cloth_metal(token); }, - &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); -} -void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_cloth()) { - mPile->settings.flags.bits.cloth = 1; - const StockpileSettings::ClothSet cloth = mBuffer.cloth(); - DEBUG(log).print("cloth:\n"); + DEBUG(log).print(" bones\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.bones(idx); }, + refuse.bones_size(), &mPile->settings.refuse.bones); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_silk(idx); }, - cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); + DEBUG(log).print(" hair\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.hair(idx); }, + refuse.hair_size(), &mPile->settings.refuse.hair); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_plant(idx); }, - cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); + DEBUG(log).print(" shells\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.shells(idx); }, + refuse.shells_size(), &mPile->settings.refuse.shells); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_yarn(idx); }, - cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); + DEBUG(log).print(" teeth\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.teeth(idx); }, + refuse.teeth_size(), &mPile->settings.refuse.teeth); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_metal(idx); }, - cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); + DEBUG(log).print(" horns\n"); + refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.horns(idx); }, + refuse.horns_size(), &mPile->settings.refuse.horns); + } + else { + mPile->settings.flags.bits.refuse = 0; + mPile->settings.refuse.type.clear(); + mPile->settings.refuse.corpses.clear(); + mPile->settings.refuse.body_parts.clear(); + mPile->settings.refuse.skulls.clear(); + mPile->settings.refuse.bones.clear(); + mPile->settings.refuse.hair.clear(); + mPile->settings.refuse.shells.clear(); + mPile->settings.refuse.teeth.clear(); + mPile->settings.refuse.horns.clear(); + mPile->settings.refuse.fresh_raw_hide = false; + mPile->settings.refuse.rotten_raw_hide = false; + } +} - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_silk(idx); }, - cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); +bool StockpileSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { + bool all = true; - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_plant(idx); }, - cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); + return all; +} - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_yarn(idx); }, - cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); +void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& filter) { - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_metal(idx); }, - cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); - } - else { - mPile->settings.cloth.thread_metal.clear(); - mPile->settings.cloth.thread_plant.clear(); - mPile->settings.cloth.thread_silk.clear(); - mPile->settings.cloth.thread_yarn.clear(); - mPile->settings.cloth.cloth_metal.clear(); - mPile->settings.cloth.cloth_plant.clear(); - mPile->settings.cloth.cloth_silk.clear(); - mPile->settings.cloth.cloth_yarn.clear(); - mPile->settings.flags.bits.cloth = 0; - } } -bool StockpileSerializer::wood_mat_is_allowed(const df::plant_raw* plant) { - return plant && plant->flags.is_set(plant_raw_flags::TREE); +static bool stone_is_allowed(const MaterialInfo& mi) { + if (!mi.isValid()) + return false; + const bool is_allowed_soil = mi.inorganic->flags.is_set(inorganic_flags::SOIL) && !mi.inorganic->flags.is_set(inorganic_flags::AQUIFER); + const bool is_allowed_stone = mi.material->flags.is_set(material_flags::IS_STONE) && !mi.material->flags.is_set(material_flags::NO_STONE_STOCKPILE); + return is_allowed_soil || is_allowed_stone; } -void StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { - for (size_t i = 0; i < mPile->settings.wood.mats.size(); ++i) { - if (mPile->settings.wood.mats.at(i)) { - const df::plant_raw* plant = find_plant(i); - if (!wood_mat_is_allowed(plant)) - continue; - wood->add_mats(plant->id); - DEBUG(log).print("plant %zd is %s\n", i, plant->id.c_str()); - } - } +bool StockpileSerializer::write_stone(StockpileSettings::StoneSet* stone) { + return serialize_list_material( + stone_is_allowed, + [=](const std::string& token) { stone->add_mats(token); }, + mPile->settings.stone.mats); } -void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_wood()) { - mPile->settings.flags.bits.wood = 1; - const StockpileSettings::WoodSet wood = mBuffer.wood(); - DEBUG(log).print("wood: \n"); - mPile->settings.wood.mats.clear(); - mPile->settings.wood.mats.resize(world->raws.plants.all.size(), '\0'); - for (int i = 0; i < wood.mats_size(); ++i) { - const std::string token = wood.mats(i); - const size_t idx = find_plant(token); - if (idx < 0 || idx >= mPile->settings.wood.mats.size()) { - WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); - continue; - } - DEBUG(log).print("plant %zd is %s\n", idx, token.c_str()); - mPile->settings.wood.mats.at(idx) = 1; - } +void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_stone()) { + mPile->settings.flags.bits.stone = 1; + const StockpileSettings::StoneSet stone = mBuffer.stone(); + DEBUG(log).print("stone:\n"); + + unserialize_list_material( + stone_is_allowed, + [=](const size_t& idx) -> const std::string& { return stone.mats(idx); }, + stone.mats_size(), &mPile->settings.stone.mats); } else { - mPile->settings.flags.bits.wood = 0; - mPile->settings.wood.mats.clear(); + mPile->settings.flags.bits.stone = 0; + mPile->settings.stone.mats.clear(); } } -bool StockpileSerializer::weapons_mat_is_allowed(const MaterialInfo& mi) { +static bool weapons_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -void StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) { - weapons->set_unusable(mPile->settings.weapons.unusable); - weapons->set_usable(mPile->settings.weapons.usable); +bool StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) { + auto & pweapons = mPile->settings.weapons; + bool all = pweapons.unusable && pweapons.usable; + + weapons->set_unusable(pweapons.unusable); + weapons->set_usable(pweapons.usable); - // weapon type - serialize_list_itemdef([=](const std::string& token) { weapons->add_weapon_type(token); }, - mPile->settings.weapons.weapon_type, + all = serialize_list_itemdef( + [=](const std::string& token) { weapons->add_weapon_type(token); }, + pweapons.weapon_type, std::vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), - item_type::WEAPON); + item_type::WEAPON) && all; - // trapcomp type - serialize_list_itemdef([=](const std::string& token) { weapons->add_trapcomp_type(token); }, - mPile->settings.weapons.trapcomp_type, + all = serialize_list_itemdef( + [=](const std::string& token) { weapons->add_trapcomp_type(token); }, + pweapons.trapcomp_type, std::vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), - item_type::TRAPCOMP); + item_type::TRAPCOMP) && all; - // materials - FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::weapons_mat_is_allowed, this, _1); - serialize_list_material( - mat_filter, [=](const std::string& token) { weapons->add_mats(token); }, - mPile->settings.weapons.mats); + all = serialize_list_material( + weapons_mat_is_allowed, + [=](const std::string& token) { weapons->add_mats(token); }, + pweapons.mats) && all; - // other mats - serialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [=](const std::string& token) { weapons->add_other_mats(token); }, - mPile->settings.weapons.other_mats); + all = serialize_list_other_mats( + mOtherMatsWeaponsArmor.mats, + [=](const std::string& token) { weapons->add_other_mats(token); }, + pweapons.other_mats) && all; - // quality core - serialize_list_quality([=](const std::string& token) { weapons->add_quality_core(token); }, - mPile->settings.weapons.quality_core); + all = serialize_list_quality( + [=](const std::string& token) { weapons->add_quality_core(token); }, + pweapons.quality_core) && all; - // quality total - serialize_list_quality([=](const std::string& token) { weapons->add_quality_total(token); }, - mPile->settings.weapons.quality_total); + all = serialize_list_quality( + [=](const std::string& token) { weapons->add_quality_total(token); }, + pweapons.quality_total) && all; + + return all; } void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& filter) { @@ -1890,29 +2052,24 @@ void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& mPile->settings.weapons.unusable = unusable; mPile->settings.weapons.usable = usable; - // weapon type unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.weapon_type(idx); }, weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON); - // trapcomp type unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.trapcomp_type(idx); }, weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP); - // materials - FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::weapons_mat_is_allowed, this, _1); unserialize_list_material( - mat_filter, [=](const size_t& idx) -> const std::string& { return weapons.mats(idx); }, + weapons_mat_is_allowed, + [=](const size_t& idx) -> const std::string& { return weapons.mats(idx); }, weapons.mats_size(), &mPile->settings.weapons.mats); - // other mats unserialize_list_other_mats( mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return weapons.other_mats(idx); }, weapons.other_mats_size(), &mPile->settings.weapons.other_mats); - // core quality unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_core(idx); }, weapons.quality_core_size(), mPile->settings.weapons.quality_core); - // total quality + unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_total(idx); }, weapons.quality_total_size(), mPile->settings.weapons.quality_total); } @@ -1927,152 +2084,47 @@ void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& } } -bool StockpileSerializer::armor_mat_is_allowed(const MaterialInfo& mi) { - return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); +static bool wood_mat_is_allowed(const df::plant_raw* plant) { + return plant && plant->flags.is_set(plant_raw_flags::TREE); } -void StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { - armor->set_unusable(mPile->settings.armor.unusable); - armor->set_usable(mPile->settings.armor.usable); - - // armor type - serialize_list_itemdef([=](const std::string& token) { armor->add_body(token); }, - mPile->settings.armor.body, - std::vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), - item_type::ARMOR); - - // helm type - serialize_list_itemdef([=](const std::string& token) { armor->add_head(token); }, - mPile->settings.armor.head, - std::vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), - item_type::HELM); - - // shoes type - serialize_list_itemdef([=](const std::string& token) { armor->add_feet(token); }, - mPile->settings.armor.feet, - std::vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), - item_type::SHOES); - - // gloves type - serialize_list_itemdef([=](const std::string& token) { armor->add_hands(token); }, - mPile->settings.armor.hands, - std::vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), - item_type::GLOVES); - - // pant type - serialize_list_itemdef([=](const std::string& token) { armor->add_legs(token); }, - mPile->settings.armor.legs, - std::vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), - item_type::PANTS); - - // shield type - serialize_list_itemdef([=](const std::string& token) { armor->add_shield(token); }, - mPile->settings.armor.shield, - std::vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), - item_type::SHIELD); - - // materials - FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::armor_mat_is_allowed, this, _1); - serialize_list_material( - mat_filter, [=](const std::string& token) { armor->add_mats(token); }, - mPile->settings.armor.mats); - - // other mats - serialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [=](const std::string& token) { armor->add_other_mats(token); }, - mPile->settings.armor.other_mats); - - // quality core - serialize_list_quality([=](const std::string& token) { armor->add_quality_core(token); }, - mPile->settings.armor.quality_core); - - // quality total - serialize_list_quality([=](const std::string& token) { armor->add_quality_total(token); }, - mPile->settings.armor.quality_total); +bool StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { + bool all = true; + for (size_t i = 0; i < mPile->settings.wood.mats.size(); ++i) { + if (!mPile->settings.wood.mats.at(i)) { + all = false; + continue; + } + const df::plant_raw* plant = find_plant(i); + if (!wood_mat_is_allowed(plant)) + continue; + wood->add_mats(plant->id); + DEBUG(log).print("plant %zd is %s\n", i, plant->id.c_str()); + } + return all; } -void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_armor()) { - mPile->settings.flags.bits.armor = 1; - const StockpileSettings::ArmorSet armor = mBuffer.armor(); - DEBUG(log).print("armor:\n"); - - bool unusable = armor.unusable(); - bool usable = armor.usable(); - DEBUG(log).print("unusable %d\n", unusable); - DEBUG(log).print("usable %d\n", usable); - mPile->settings.armor.unusable = unusable; - mPile->settings.armor.usable = usable; - - // body type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.body(idx); }, - armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR); - - // head type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.head(idx); }, - armor.head_size(), &mPile->settings.armor.head, item_type::HELM); - - // feet type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.feet(idx); }, - armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES); - - // hands type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.hands(idx); }, - armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES); - - // legs type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.legs(idx); }, - armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS); - - // shield type - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.shield(idx); }, - armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD); - - // materials - FuncMaterialAllowed mat_filter = std::bind(&StockpileSerializer::armor_mat_is_allowed, this, _1); - unserialize_list_material( - mat_filter, [=](const size_t& idx) -> const std::string& { return armor.mats(idx); }, - armor.mats_size(), &mPile->settings.armor.mats); - - // other mats - unserialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, - armor.other_mats_size(), &mPile->settings.armor.other_mats); +void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& filter) { + if (mBuffer.has_wood()) { + mPile->settings.flags.bits.wood = 1; + const StockpileSettings::WoodSet wood = mBuffer.wood(); + DEBUG(log).print("wood: \n"); - // core quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_core(idx); }, - armor.quality_core_size(), mPile->settings.armor.quality_core); - // total quality - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_total(idx); }, - armor.quality_total_size(), mPile->settings.armor.quality_total); + mPile->settings.wood.mats.clear(); + mPile->settings.wood.mats.resize(world->raws.plants.all.size(), '\0'); + for (int i = 0; i < wood.mats_size(); ++i) { + const std::string token = wood.mats(i); + const size_t idx = find_plant(token); + if (idx < 0 || idx >= mPile->settings.wood.mats.size()) { + WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); + continue; + } + DEBUG(log).print("plant %zd is %s\n", idx, token.c_str()); + mPile->settings.wood.mats.at(idx) = 1; + } } else { - mPile->settings.flags.bits.armor = 0; - mPile->settings.armor.body.clear(); - mPile->settings.armor.head.clear(); - mPile->settings.armor.feet.clear(); - mPile->settings.armor.hands.clear(); - mPile->settings.armor.legs.clear(); - mPile->settings.armor.shield.clear(); - mPile->settings.armor.other_mats.clear(); - mPile->settings.armor.mats.clear(); - quality_clear(mPile->settings.armor.quality_core); - quality_clear(mPile->settings.armor.quality_total); + mPile->settings.flags.bits.wood = 0; + mPile->settings.wood.mats.clear(); } } - -void StockpileSerializer::write_corpses(StockpileSettings::CorpsesSet* corpses) { - -} - -void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& filter) { - -} - -void StockpileSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { - -} - -void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& filter) { - -} diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 24c6d5d98..4085744d0 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -96,94 +96,44 @@ private: // parse serialized data into ui indices void read(DeserializeMode mode, const std::string& filter); - /** - * Given a list of other_materials and an index, return its corresponding token - * @return empty string if not found - * @see other_mats_token - */ - std::string other_mats_index(const std::map other_mats, int idx); - - /** - * Given a list of other_materials and a token, return its corresponding index - * @return -1 if not found - * @see other_mats_index - */ - int other_mats_token(const std::map other_mats, const std::string& token); - void write_containers(); void read_containers(DeserializeMode mode); void write_general(); void read_general(DeserializeMode mode); - void write_animals(dfstockpiles::StockpileSettings::AnimalsSet* animals); + bool write_ammo(dfstockpiles::StockpileSettings::AmmoSet* ammo); + void read_ammo(DeserializeMode mode, const std::string& filter); + bool write_animals(dfstockpiles::StockpileSettings::AnimalsSet* animals); void read_animals(DeserializeMode mode, const std::string& filter); - + bool write_armor(dfstockpiles::StockpileSettings::ArmorSet* armor); + void read_armor(DeserializeMode mode, const std::string& filter); + bool write_bars_blocks(dfstockpiles::StockpileSettings::BarsBlocksSet* bars_blocks); + void read_bars_blocks(DeserializeMode mode, const std::string& filter); + bool write_cloth(dfstockpiles::StockpileSettings::ClothSet* cloth); + void read_cloth(DeserializeMode mode, const std::string& filter); + bool write_coins(dfstockpiles::StockpileSettings::CoinSet* coins); + void read_coins(DeserializeMode mode, const std::string& filter); + bool write_finished_goods(dfstockpiles::StockpileSettings::FinishedGoodsSet* finished_goods); + void read_finished_goods(DeserializeMode mode, const std::string& filter); food_pair food_map(df::enums::organic_mat_category::organic_mat_category cat); - - void write_food(dfstockpiles::StockpileSettings::FoodSet* food); + bool write_food(dfstockpiles::StockpileSettings::FoodSet* food); void read_food(DeserializeMode mode, const std::string& filter); - - void write_furniture(dfstockpiles::StockpileSettings::FurnitureSet* furniture); - bool furniture_mat_is_allowed(const DFHack::MaterialInfo& mi); + bool write_furniture(dfstockpiles::StockpileSettings::FurnitureSet* furniture); void read_furniture(DeserializeMode mode, const std::string& filter); - - bool refuse_creature_is_allowed(const df::creature_raw* raw); - - void refuse_write_helper(std::function add_value, const std::vector& list); - - bool refuse_type_is_allowed(df::enums::item_type::item_type type); - - void write_refuse(dfstockpiles::StockpileSettings::RefuseSet* refuse); - void refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list); - - void read_refuse(DeserializeMode mode, const std::string& filter); - - bool stone_is_allowed(const DFHack::MaterialInfo& mi); - - void write_stone(dfstockpiles::StockpileSettings::StoneSet* stone); - - void read_stone(DeserializeMode mode, const std::string& filter); - - bool write_ammo(dfstockpiles::StockpileSettings::AmmoSet* ammo); - void read_ammo(DeserializeMode mode, const std::string& filter); - bool coins_mat_is_allowed(const DFHack::MaterialInfo& mi); - - void write_coins(dfstockpiles::StockpileSettings::CoinSet* coins); - - void read_coins(DeserializeMode mode, const std::string& filter); - bool bars_mat_is_allowed(const DFHack::MaterialInfo& mi); - - bool blocks_mat_is_allowed(const DFHack::MaterialInfo& mi); - - void write_bars_blocks(dfstockpiles::StockpileSettings::BarsBlocksSet* bars_blocks); - void read_bars_blocks(DeserializeMode mode, const std::string& filter); - bool gem_mat_is_allowed(const DFHack::MaterialInfo& mi); - bool gem_cut_mat_is_allowed(const DFHack::MaterialInfo& mi); - bool gem_other_mat_is_allowed(DFHack::MaterialInfo& mi); - - void write_gems(dfstockpiles::StockpileSettings::GemsSet* gems); - + bool write_gems(dfstockpiles::StockpileSettings::GemsSet* gems); void read_gems(DeserializeMode mode, const std::string& filter); - - bool finished_goods_type_is_allowed(df::enums::item_type::item_type type); - bool finished_goods_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_finished_goods(dfstockpiles::StockpileSettings::FinishedGoodsSet* finished_goods); - void read_finished_goods(DeserializeMode mode, const std::string& filter); - void write_leather(dfstockpiles::StockpileSettings::LeatherSet* leather); + bool write_leather(dfstockpiles::StockpileSettings::LeatherSet* leather); void read_leather(DeserializeMode mode, const std::string& filter); - void write_cloth(dfstockpiles::StockpileSettings::ClothSet* cloth); - void read_cloth(DeserializeMode mode, const std::string& filter); - bool wood_mat_is_allowed(const df::plant_raw* plant); - void write_wood(dfstockpiles::StockpileSettings::WoodSet* wood); - void read_wood(DeserializeMode mode, const std::string& filter); - bool weapons_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_weapons(dfstockpiles::StockpileSettings::WeaponsSet* weapons); - void read_weapons(DeserializeMode mode, const std::string& filter); - bool armor_mat_is_allowed(const DFHack::MaterialInfo& mi); - void write_armor(dfstockpiles::StockpileSettings::ArmorSet* armor); - void read_armor(DeserializeMode mode, const std::string& filter); - void write_corpses(dfstockpiles::StockpileSettings::CorpsesSet* corpses); + bool write_corpses(dfstockpiles::StockpileSettings::CorpsesSet* corpses); void read_corpses(DeserializeMode mode, const std::string& filter); - void write_sheet(dfstockpiles::StockpileSettings::SheetSet* sheet); + bool write_refuse(dfstockpiles::StockpileSettings::RefuseSet* refuse); + void read_refuse(DeserializeMode mode, const std::string& filter); + bool write_sheet(dfstockpiles::StockpileSettings::SheetSet* sheet); void read_sheet(DeserializeMode mode, const std::string& filter); + bool write_stone(dfstockpiles::StockpileSettings::StoneSet* stone); + void read_stone(DeserializeMode mode, const std::string& filter); + bool write_weapons(dfstockpiles::StockpileSettings::WeaponsSet* weapons); + void read_weapons(DeserializeMode mode, const std::string& filter); + bool write_wood(dfstockpiles::StockpileSettings::WoodSet* wood); + void read_wood(DeserializeMode mode, const std::string& filter); }; From 06fa43932cbea53b9bee289a526f891d0b25f2fa Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 00:32:19 -0700 Subject: [PATCH 0860/2222] animals works --- plugins/lua/stockpiles.lua | 14 +- plugins/stockpiles/StockpileSerializer.cpp | 185 +++++++++++---------- 2 files changed, 108 insertions(+), 91 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 6770c044e..1a92e2c0c 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -92,6 +92,12 @@ local function export_stockpile(name, opts) end end + if includedElements == 0 then + for _,v in pairs(included_elements) do + includedElements = includedElements | v + end + end + stockpiles_export(name, get_sp_id(opts), includedElements) end @@ -142,14 +148,14 @@ local function process_args(opts, args) opts.filter = '' return argparse.processArgsGetopt(args, { - {'f', 'filter', has_arg=true, + {'f', 'filter', hasArg=true, handler=function(arg) opts.filter = arg end}, {'h', 'help', handler=function() opts.help = true end}, - {'i', 'include', has_arg=true, + {'i', 'include', hasArg=true, handler=function(arg) opts.includes = parse_include(arg) end}, - {'m', 'mode', has_arg=true, + {'m', 'mode', hasArg=true, handler=function(arg) opts.mode = parse_mode(arg) end}, - {'s', 'stockpile', has_arg=true, + {'s', 'stockpile', hasArg=true, handler=function(arg) opts.id = argparse.nonnegativeInt(arg, 'stockpile') end}, }) end diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 3b2849333..2d72aa822 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -10,6 +10,7 @@ // df #include "df/building_stockpilest.h" +#include "df/creature_raw.h" #include "df/inorganic_raw.h" #include "df/item_quality.h" #include @@ -520,7 +521,7 @@ static void unserialize_list_material(FuncMaterialAllowed is_allowed, } template -static void write_cat(bool include_types, uint32_t cat_flags, +static void write_cat(const char *name, bool include_types, uint32_t cat_flags, enum df::stockpile_group_set::Mask cat_mask, std::function mutable_cat_fn, std::function write_cat_fn) { @@ -528,13 +529,17 @@ static void write_cat(bool include_types, uint32_t cat_flags, return; T_cat_set* cat_set = mutable_cat_fn(); + if (!include_types) { + DEBUG(log).print("including all for %s since only category is being recorded\n", name); cat_set->set_all(true); return; } if (write_cat_fn(cat_set)) { - // all fields were set. might as well clear them and use the "all" flag + // all fields were set. clear them and use the "all" flag instead so "all" can be applied + // to other worlds with other generated types + DEBUG(log).print("including all for %s since all fields were enabled\n", name); cat_set->Clear(); cat_set->set_all(true); } @@ -554,90 +559,90 @@ void StockpileSerializer::write(uint32_t includedElements) { bool include_types = 0 != (includedElements & INCLUDED_ELEMENTS_TYPES); - write_cat(include_types, + write_cat("ammo", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_ammo, - std::bind(&StockpileSettings::mutable_ammo, mBuffer), + std::bind(&StockpileSettings::mutable_ammo, &mBuffer), std::bind(&StockpileSerializer::write_ammo, this, _1)); - write_cat(include_types, + write_cat("animals", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_animals, - std::bind(&StockpileSettings::mutable_animals, mBuffer), + std::bind(&StockpileSettings::mutable_animals, &mBuffer), std::bind(&StockpileSerializer::write_animals, this, _1)); - write_cat(include_types, + write_cat("armor", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_armor, - std::bind(&StockpileSettings::mutable_armor, mBuffer), + std::bind(&StockpileSettings::mutable_armor, &mBuffer), std::bind(&StockpileSerializer::write_armor, this, _1)); - write_cat(include_types, + write_cat("bars_blocks", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_bars_blocks, - std::bind(&StockpileSettings::mutable_barsblocks, mBuffer), + std::bind(&StockpileSettings::mutable_barsblocks, &mBuffer), std::bind(&StockpileSerializer::write_bars_blocks, this, _1)); - write_cat(include_types, + write_cat("cloth", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_cloth, - std::bind(&StockpileSettings::mutable_cloth, mBuffer), + std::bind(&StockpileSettings::mutable_cloth, &mBuffer), std::bind(&StockpileSerializer::write_cloth, this, _1)); - write_cat(include_types, + write_cat("coin", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_coins, - std::bind(&StockpileSettings::mutable_coin, mBuffer), + std::bind(&StockpileSettings::mutable_coin, &mBuffer), std::bind(&StockpileSerializer::write_coins, this, _1)); - write_cat(include_types, + write_cat("finished_goods", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_finished_goods, - std::bind(&StockpileSettings::mutable_finished_goods, mBuffer), + std::bind(&StockpileSettings::mutable_finished_goods, &mBuffer), std::bind(&StockpileSerializer::write_finished_goods, this, _1)); - write_cat(include_types, + write_cat("food", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_food, - std::bind(&StockpileSettings::mutable_food, mBuffer), + std::bind(&StockpileSettings::mutable_food, &mBuffer), std::bind(&StockpileSerializer::write_food, this, _1)); - write_cat(include_types, + write_cat("furniture", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_furniture, - std::bind(&StockpileSettings::mutable_furniture, mBuffer), + std::bind(&StockpileSettings::mutable_furniture, &mBuffer), std::bind(&StockpileSerializer::write_furniture, this, _1)); - write_cat(include_types, + write_cat("gems", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_gems, - std::bind(&StockpileSettings::mutable_gems, mBuffer), + std::bind(&StockpileSettings::mutable_gems, &mBuffer), std::bind(&StockpileSerializer::write_gems, this, _1)); - write_cat(include_types, + write_cat("leather", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_leather, - std::bind(&StockpileSettings::mutable_leather, mBuffer), + std::bind(&StockpileSettings::mutable_leather, &mBuffer), std::bind(&StockpileSerializer::write_leather, this, _1)); - write_cat(include_types, + write_cat("corpses", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_corpses, - std::bind(&StockpileSettings::mutable_corpses_v50, mBuffer), + std::bind(&StockpileSettings::mutable_corpses_v50, &mBuffer), std::bind(&StockpileSerializer::write_corpses, this, _1)); - write_cat(include_types, + write_cat("refuse", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_refuse, - std::bind(&StockpileSettings::mutable_refuse, mBuffer), + std::bind(&StockpileSettings::mutable_refuse, &mBuffer), std::bind(&StockpileSerializer::write_refuse, this, _1)); - write_cat(include_types, + write_cat("sheet", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_sheet, - std::bind(&StockpileSettings::mutable_sheet, mBuffer), + std::bind(&StockpileSettings::mutable_sheet, &mBuffer), std::bind(&StockpileSerializer::write_sheet, this, _1)); - write_cat(include_types, + write_cat("stone", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_stone, - std::bind(&StockpileSettings::mutable_stone, mBuffer), + std::bind(&StockpileSettings::mutable_stone, &mBuffer), std::bind(&StockpileSerializer::write_stone, this, _1)); - write_cat(include_types, + write_cat("weapons", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_weapons, - std::bind(&StockpileSettings::mutable_weapons, mBuffer), + std::bind(&StockpileSettings::mutable_weapons, &mBuffer), std::bind(&StockpileSerializer::write_weapons, this, _1)); - write_cat(include_types, + write_cat("wood", include_types, mPile->settings.flags.whole, mPile->settings.flags.mask_wood, - std::bind(&StockpileSettings::mutable_wood, mBuffer), + std::bind(&StockpileSettings::mutable_wood, &mBuffer), std::bind(&StockpileSerializer::write_wood, this, _1)); } @@ -672,6 +677,7 @@ void StockpileSerializer::read(DeserializeMode mode, const std::string& filter) } void StockpileSerializer::write_containers() { + DEBUG(log).print("writing container settings\n"); mBuffer.set_max_bins(mPile->max_bins); mBuffer.set_max_barrels(mPile->max_barrels); mBuffer.set_max_wheelbarrows(mPile->max_wheelbarrows); @@ -682,18 +688,10 @@ static void read_elem(const char *name, DeserializeMode mode, std::function has_elem_fn, std::function elem_fn, T_elem &setting) { - bool has_elem = has_elem_fn(); - bool is_set = has_elem && elem_fn() != 0; - bool just_disable = is_set && mode == DESERIALIZE_MODE_DISABLE; - - if (mode == DESERIALIZE_MODE_SET || just_disable) { - DEBUG(log).print("clearing %s\n", name); - setting = 0; - } - - if (!has_elem || just_disable) + if (!has_elem_fn()) return; + bool is_set = elem_fn() != 0; if (mode == DESERIALIZE_MODE_SET || is_set) { T_elem val = (mode == DESERIALIZE_MODE_DISABLE) ? 0 : elem_fn(); DEBUG(log).print("setting %s=%d\n", name, val); @@ -728,11 +726,30 @@ static void read_category(const char *name, DeserializeMode mode, if (mode == DESERIALIZE_MODE_SET || mode == DESERIALIZE_MODE_ENABLE) cat_flags |= cat_mask; - bool force = mode == DESERIALIZE_MODE_SET || all; char val = (mode == DESERIALIZE_MODE_DISABLE) ? (char)0 : (char)1; DEBUG(log).print("setting %s %s elements to %d\n", all ? "all" : "marked", name, val); - set_fn(force, val); + set_fn(all, val); +} + +static void set_elem(bool all, char val, bool enabled, bool& elem) { + if (all || enabled) + elem = val; +} + +static bool matches_filter(const std::string& filter, const std::string& name) { + if (!filter.size()) + return true; + return std::search(name.begin(), name.end(), filter.begin(), filter.end(), + [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } + ) != name.end(); +} + +static void set_filter_elem(const std::string& filter, char val, df::creature_raw* r, char& elem) { + if (matches_filter(filter, r->name[0])) { + DEBUG(log).print("setting %s (%s) to %d\n", r->name[0].c_str(), r->creature_id.c_str(), val); + elem = val; + } } void StockpileSerializer::read_containers(DeserializeMode mode) { @@ -751,6 +768,7 @@ void StockpileSerializer::read_containers(DeserializeMode mode) { } void StockpileSerializer::write_general() { + DEBUG(log).print("writing general settings\n"); mBuffer.set_use_links_only(mPile->use_links_only); mBuffer.set_allow_inorganic(mPile->settings.allow_inorganic); mBuffer.set_allow_organic(mPile->settings.allow_organic); @@ -776,22 +794,17 @@ static bool ammo_mat_is_allowed(const MaterialInfo& mi) { } bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { - bool all = true; - - // ammo type - all = serialize_list_itemdef( + bool all = serialize_list_itemdef( [=](const std::string& token) { ammo->add_type(token); }, mPile->settings.ammo.type, std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), - item_type::AMMO) && all; + item_type::AMMO); - // metal all = serialize_list_material( ammo_mat_is_allowed, [=](const std::string& token) { ammo->add_mats(token); }, mPile->settings.ammo.mats) && all; - // other mats - only wood and bone are expected if (mPile->settings.ammo.other_mats.size() > 2) { WARN(log).print("ammo other materials > 2: %zd\n", mPile->settings.ammo.other_mats.size()); @@ -809,12 +822,10 @@ bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); } - // quality core all = serialize_list_quality( [=](const std::string& token) { ammo->add_quality_core(token); }, mPile->settings.ammo.quality_core) && all; - // quality total all = serialize_list_quality( [=](const std::string& token) { ammo->add_quality_total(token); }, mPile->settings.ammo.quality_total) && all; @@ -823,13 +834,13 @@ bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { } void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& filter) { + auto & pammo = mPile->settings.ammo; read_category("ammo", mode, std::bind(&StockpileSettings::has_ammo, mBuffer), std::bind(&StockpileSettings::ammo, mBuffer), mPile->settings.flags.whole, mPile->settings.flags.mask_ammo, [&]() { - auto & pammo = mPile->settings.ammo; pammo.type.clear(); pammo.mats.clear(); pammo.other_mats.clear(); @@ -838,18 +849,15 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil }, [&](bool force, char val) { auto & bammo = mBuffer.ammo(); - auto & pammo = mPile->settings.ammo; - // ammo type + unserialize_list_itemdef( - [=](const size_t& idx) -> const std::string& { return bammo.type(idx); }, + [&](const size_t& idx) -> const std::string& { return bammo.type(idx); }, bammo.type_size(), &pammo.type, item_type::AMMO); - // materials metals unserialize_list_material(ammo_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, + [&](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, bammo.mats_size(), &pammo.mats); - // others pammo.other_mats.clear(); pammo.other_mats.resize(2, '\0'); if (bammo.other_mats_size() > 0) { @@ -859,16 +867,15 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 : -1; DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); - if (idx != -1) - pammo.other_mats.at(idx) = 1; + if (idx == -1) + continue; + pammo.other_mats.at(idx) = 1; } } - // core quality unserialize_list_quality([=](const size_t& idx) -> const std::string& { return bammo.quality_core(idx); }, bammo.quality_core_size(), pammo.quality_core); - // total quality unserialize_list_quality([=](const size_t& idx) -> const std::string& { return bammo.quality_total(idx); }, bammo.quality_total_size(), pammo.quality_total); }); @@ -886,43 +893,47 @@ bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) continue; } df::creature_raw* r = find_creature(i); - DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); + if (r->flags.is_set(creature_raw_flags::GENERATED) + || r->creature_id == "EQUIPMENT_WAGON") + continue; + DEBUG(log).print("saving creature %s\n", r->creature_id.c_str()); animals->add_enabled(r->creature_id); } return all; } void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& filter) { + auto & panimals = mPile->settings.animals; read_category("animals", mode, std::bind(&StockpileSettings::has_animals, mBuffer), std::bind(&StockpileSettings::animals, mBuffer), mPile->settings.flags.whole, mPile->settings.flags.mask_animals, [&]() { - auto & panimals = mPile->settings.animals; panimals.empty_cages = false; panimals.empty_traps = false; panimals.enabled.clear(); }, - [&](bool force, char val) { + [&](bool all, char val) { auto & banimals = mBuffer.animals(); - auto & panimals = mPile->settings.animals; - if (force || banimals.empty_cages()) - panimals.empty_cages = banimals.empty_cages() && val; - if (force || banimals.empty_traps()) - panimals.empty_traps = banimals.empty_traps() && val; - - panimals.enabled.resize(world->raws.creatures.all.size(), '\0'); - for (auto i = 0; i < banimals.enabled_size(); ++i) { - const std::string& id = banimals.enabled(i); - int idx = find_creature(id); - DEBUG(log).print("%s %d\n", id.c_str(), idx); - if (idx < 0 || size_t(idx) >= panimals.enabled.size()) { - WARN(log).print("animal index invalid: %d\n", idx); - continue; + set_elem(all, val, banimals.empty_cages(), panimals.empty_cages); + set_elem(all, val, banimals.empty_traps(), panimals.empty_traps); + + size_t num_animals = world->raws.creatures.all.size(); + panimals.enabled.resize(num_animals, '\0'); + if (all) { + for (auto idx = 0; idx < num_animals; ++idx) + set_filter_elem(filter, val, find_creature(idx), panimals.enabled.at(idx)); + } else { + for (auto i = 0; i < banimals.enabled_size(); ++i) { + const std::string& id = banimals.enabled(i); + int idx = find_creature(id); + if (idx < 0 || size_t(idx) >= num_animals) { + WARN(log).print("animal index invalid: %d\n", idx); + continue; + } + set_filter_elem(filter, val, find_creature(idx), panimals.enabled.at(idx)); } - if (!filter.size() || id.find(filter) != id.npos) - panimals.enabled.at(idx) = val; } }); } From 0a9e81418e041b2c097a58149fa95c84262ae701 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 10:07:03 -0700 Subject: [PATCH 0861/2222] port two helper functions --- plugins/stockpiles/StockpileSerializer.cpp | 152 ++++++++++++--------- 1 file changed, 87 insertions(+), 65 deletions(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 2d72aa822..eb18576f0 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -222,6 +222,34 @@ static typename df::enum_traits::base_type linear_index(df::enum_traits tr return -1; } +static void set_elem(bool all, char val, bool enabled, bool& elem) { + if (all || enabled) + elem = val; +} + +static bool matches_filter(const string& filter, const string& name) { + if (!filter.size()) + return true; + return std::search(name.begin(), name.end(), filter.begin(), filter.end(), + [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } + ) != name.end(); +} + +static void set_filter_elem(const string& filter, char val, const string& name, const string& id, char& elem) { + if (matches_filter(filter, name)) { + DEBUG(log).print("setting %s (%s) to %d\n", name.c_str(), id.c_str(), val); + elem = val; + } +} + +template +static void set_filter_elem(const string& filter, char val, const string& name, T id, char& elem) { + if (matches_filter(filter, name)) { + DEBUG(log).print("setting %s (%d) to %d\n", name.c_str(), (int32_t)id, val); + elem = val; + } +} + /** * There are many repeated (un)serialization cases throughout the stockpile_settings structure, * so the most common cases have been generalized into generic functions using lambdas. @@ -252,29 +280,35 @@ static bool serialize_list_itemdef(FuncWriteExport add_value, ItemTypeInfo ii; if (!ii.decode(type, i)) continue; + DEBUG(log).print("adding itemdef type %s\n", ii.getToken().c_str()); add_value(ii.getToken()); - DEBUG(log).print("itemdef type %zd is %s\n", i, ii.getToken().c_str()); } return all; } -static void unserialize_list_itemdef(FuncReadImport read_value, - int32_t list_size, - std::vector* pile_list, - item_type::item_type type) { - pile_list->clear(); - pile_list->resize(Items::getSubtypeCount(type), '\0'); - for (int i = 0; i < list_size; ++i) { - std::string token = read_value(i); +static void unserialize_list_itemdef(bool all, char val, const string& filter, FuncReadImport read_value, + int32_t list_size, std::vector& pile_list, item_type::item_type type) { + int num_elems = Items::getSubtypeCount(type); + pile_list.resize(num_elems, '\0'); + if (all) { + for (auto idx = 0; idx < num_elems; ++idx) { + ItemTypeInfo ii; + ii.decode(type, idx); + set_filter_elem(filter, val, ii.toString(), idx, pile_list.at(idx)); + } + return; + } + + for (auto i = 0; i < list_size; ++i) { + string id = read_value(i); ItemTypeInfo ii; - if (!ii.find(token)) + if (!ii.find(id)) continue; - DEBUG(log).print("itemdef %d is %s\n", ii.subtype, token.c_str()); - if (size_t(ii.subtype) >= pile_list->size()) { - WARN(log).print("itemdef index too large! idx[%d] max_size[%zd]\n", ii.subtype, pile_list->size()); + if (ii.subtype < 0 || size_t(ii.subtype) >= pile_list.size()) { + WARN(log).print("item type index invalid: %d\n", ii.subtype); continue; } - pile_list->at(ii.subtype) = 1; + set_filter_elem(filter, val, id, ii.subtype, pile_list.at(ii.subtype)); } } @@ -291,7 +325,7 @@ static bool serialize_list_quality(FuncWriteExport add_value, } const std::string f_type(quality_traits::key_table[i]); add_value(f_type); - DEBUG(log).print("quality: %zd is %s\n", i, f_type.c_str()); + DEBUG(log).print("adding quality %s\n", f_type.c_str()); } return all; } @@ -485,38 +519,44 @@ static bool serialize_list_material(FuncMaterialAllowed is_allowed, mi.decode(0, i); if (!is_allowed(mi)) continue; - DEBUG(log).print("material %zd is %s\n", i, mi.getToken().c_str()); + DEBUG(log).print("adding material %s\n", mi.getToken().c_str()); add_value(mi.getToken()); } return all; } -static void unserialize_list_material(FuncMaterialAllowed is_allowed, - FuncReadImport read_value, int32_t list_size, - std::vector* pile_list) { - // we initialize all possible (allowed) values to 0, - // then all other not-allowed values to 1 - // why? because that's how the memory is in DF before - // we muck with it. - std::set idx_set; - pile_list->clear(); - pile_list->resize(world->raws.inorganics.size(), 0); - for (size_t i = 0; i < pile_list->size(); ++i) { +static void unserialize_list_material(bool all, char val, const string& filter, + FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, + std::vector& pile_list) { + // we initialize all disallowed values to 1 + // why? because that's how the memory is in DF before we muck with it. + size_t num_elems = world->raws.inorganics.size(); + pile_list.resize(num_elems, 0); + for (size_t i = 0; i < pile_list.size(); ++i) { MaterialInfo mi(0, i); - pile_list->at(i) = is_allowed(mi) ? 0 : 1; + if (!is_allowed(mi)) + pile_list.at(i) = 1; } - for (int i = 0; i < list_size; ++i) { - const std::string token = read_value(i); + + if (all) { + for (auto idx = 0; idx < num_elems; ++idx) { + MaterialInfo mi; + mi.decode(0, idx); + set_filter_elem(filter, val, mi.toString(), idx, pile_list.at(idx)); + } + return; + } + + for (auto i = 0; i < list_size; ++i) { + string id = read_value(i); MaterialInfo mi; - mi.find(token); - if (!is_allowed(mi)) + if (!mi.find(id) || !is_allowed(mi)) continue; - DEBUG(log).print("material %d is %s\n", mi.index, token.c_str()); - if (size_t(mi.index) >= pile_list->size()) { - WARN(log).print("material index too large! idx[%d] max_size[%zd]\n", mi.index, pile_list->size()); + if (mi.index < 0 || size_t(mi.index) >= pile_list.size()) { + WARN(log).print("material type index invalid: %d\n", mi.index); continue; } - pile_list->at(mi.index) = 1; + set_filter_elem(filter, val, id, mi.index, pile_list.at(mi.index)); } } @@ -732,26 +772,6 @@ static void read_category(const char *name, DeserializeMode mode, set_fn(all, val); } -static void set_elem(bool all, char val, bool enabled, bool& elem) { - if (all || enabled) - elem = val; -} - -static bool matches_filter(const std::string& filter, const std::string& name) { - if (!filter.size()) - return true; - return std::search(name.begin(), name.end(), filter.begin(), filter.end(), - [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } - ) != name.end(); -} - -static void set_filter_elem(const std::string& filter, char val, df::creature_raw* r, char& elem) { - if (matches_filter(filter, r->name[0])) { - DEBUG(log).print("setting %s (%s) to %d\n", r->name[0].c_str(), r->creature_id.c_str(), val); - elem = val; - } -} - void StockpileSerializer::read_containers(DeserializeMode mode) { read_elem("max_bins", mode, std::bind(&StockpileSettings::has_max_bins, mBuffer), @@ -847,18 +867,17 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil quality_clear(pammo.quality_core); quality_clear(pammo.quality_total); }, - [&](bool force, char val) { + [&](bool all, char val) { auto & bammo = mBuffer.ammo(); - unserialize_list_itemdef( + unserialize_list_itemdef(all, val, filter, [&](const size_t& idx) -> const std::string& { return bammo.type(idx); }, - bammo.type_size(), &pammo.type, item_type::AMMO); + bammo.type_size(), pammo.type, item_type::AMMO); - unserialize_list_material(ammo_mat_is_allowed, + unserialize_list_material(all, val, filter, ammo_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, - bammo.mats_size(), &pammo.mats); + bammo.mats_size(), pammo.mats); - pammo.other_mats.clear(); pammo.other_mats.resize(2, '\0'); if (bammo.other_mats_size() > 0) { // TODO remove hardcoded value @@ -922,8 +941,10 @@ void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& size_t num_animals = world->raws.creatures.all.size(); panimals.enabled.resize(num_animals, '\0'); if (all) { - for (auto idx = 0; idx < num_animals; ++idx) - set_filter_elem(filter, val, find_creature(idx), panimals.enabled.at(idx)); + for (auto idx = 0; idx < num_animals; ++idx) { + auto r = find_creature(idx); + set_filter_elem(filter, val, r->name[0], r->creature_id, panimals.enabled.at(idx)); + } } else { for (auto i = 0; i < banimals.enabled_size(); ++i) { const std::string& id = banimals.enabled(i); @@ -932,7 +953,8 @@ void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& WARN(log).print("animal index invalid: %d\n", idx); continue; } - set_filter_elem(filter, val, find_creature(idx), panimals.enabled.at(idx)); + auto r = find_creature(idx); + set_filter_elem(filter, val, r->name[0], r->creature_id, panimals.enabled.at(idx)); } } }); From a545bc8120d388ed85aaabf3224c962873c61544 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 15:14:28 -0700 Subject: [PATCH 0862/2222] it compiles! --- plugins/stockpiles/StockpileSerializer.cpp | 1502 +++++++++++--------- plugins/stockpiles/proto/stockpiles.proto | 3 + 2 files changed, 801 insertions(+), 704 deletions(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index eb18576f0..1ce6b65b1 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -242,8 +242,8 @@ static void set_filter_elem(const string& filter, char val, const string& name, } } -template -static void set_filter_elem(const string& filter, char val, const string& name, T id, char& elem) { +template +static void set_filter_elem(const string& filter, T_val val, const string& name, T_id id, T_val& elem) { if (matches_filter(filter, name)) { DEBUG(log).print("setting %s (%d) to %d\n", name.c_str(), (int32_t)id, val); elem = val; @@ -334,23 +334,26 @@ static void quality_clear(bool(&pile_list)[7]) { std::fill(pile_list, pile_list + 7, false); } -static void unserialize_list_quality(FuncReadImport read_value, - int32_t list_size, - bool(&pile_list)[7]) { - quality_clear(pile_list); - if (list_size > 0 && list_size <= 7) { - using df::enums::item_quality::item_quality; - df::enum_traits quality_traits; - for (int i = 0; i < list_size; ++i) { - const std::string quality = read_value(i); - df::enum_traits::base_type idx = linear_index(quality_traits, quality); - if (idx < 0) { - WARN(log).print("invalid quality token: %s\n", quality.c_str()); - continue; - } - DEBUG(log).print("quality: %d is %s\n", idx, quality.c_str()); - pile_list[idx] = true; +static void unserialize_list_quality(bool all, bool val, const string& filter, + FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { + if (all) { + for (auto idx = 0; idx < 7; ++idx) { + string id = ENUM_KEY_STR(item_quality, (df::item_quality)idx); + set_filter_elem(filter, val, id, idx, pile_list[idx]); } + return; + } + + using df::enums::item_quality::item_quality; + df::enum_traits quality_traits; + for (int i = 0; i < list_size; ++i) { + const std::string quality = read_value(i); + df::enum_traits::base_type idx = linear_index(quality_traits, quality); + if (idx < 0) { + WARN(log).print("invalid quality token: %s\n", quality.c_str()); + continue; + } + set_filter_elem(filter, val, quality, idx, pile_list[idx]); } } @@ -392,13 +395,17 @@ static bool serialize_list_other_mats( return all; } -static void unserialize_list_other_mats( - const std::map other_mats, - FuncReadImport read_value, - int32_t list_size, - std::vector* pile_list) { - pile_list->clear(); - pile_list->resize(other_mats.size(), '\0'); +static void unserialize_list_other_mats(bool all, char val, const string& filter, + const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { + size_t num_elems = other_mats.size(); + pile_list.resize(num_elems, '\0'); + + if (all) { + for (auto & entry : other_mats) + set_filter_elem(filter, val, entry.second, entry.first, pile_list.at(entry.first)); + return; + } + for (int i = 0; i < list_size; ++i) { const std::string token = read_value(i); size_t idx = other_mats_token(other_mats, token); @@ -406,12 +413,11 @@ static void unserialize_list_other_mats( WARN(log).print("invalid other mat with token %s\n", token.c_str()); continue; } - DEBUG(log).print("other_mats %zd is %s\n", idx, token.c_str()); - if (idx >= pile_list->size()) { - WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, pile_list->size()); + if (idx >= num_elems) { + WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, num_elems); continue; } - pile_list->at(idx) = 1; + set_filter_elem(filter, val, token, idx, pile_list.at(idx)); } } @@ -439,20 +445,26 @@ static bool serialize_list_organic_mat(FuncWriteExport add_value, return all; } -static void unserialize_list_organic_mat(FuncReadImport get_value, - size_t list_size, std::vector* pile_list, - organic_mat_category::organic_mat_category cat) { - pile_list->clear(); - pile_list->resize(OrganicMatLookup::food_max_size(cat), '\0'); +static void unserialize_list_organic_mat(bool all, char val, const string& filter, FuncReadImport read_value, + size_t list_size, std::vector& pile_list, organic_mat_category::organic_mat_category cat) { + size_t num_elems = OrganicMatLookup::food_max_size(cat); + pile_list.resize(num_elems, '\0'); + if (all) { + for (size_t idx = 0; idx < num_elems; ++idx) { + string token = OrganicMatLookup::food_token_by_idx(cat, idx); + set_filter_elem(filter, val, token, idx, pile_list.at(idx)); + } + return; + } + for (size_t i = 0; i < list_size; ++i) { - std::string token = get_value(i); + const std::string token = read_value(i); int16_t idx = OrganicMatLookup::food_idx_by_token(cat, token); - DEBUG(log).print(" organic_material %d is %s\n", idx, token.c_str()); - if (size_t(idx) >= pile_list->size()) { - WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, pile_list->size()); + if (idx < 0 || size_t(idx) >= num_elems) { + WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - pile_list->at(idx) = 1; + set_filter_elem(filter, val, token, idx, pile_list.at(idx)); } } @@ -481,29 +493,37 @@ static bool serialize_list_item_type(FuncItemAllowed is_allowed, return all; } -static void unserialize_list_item_type(FuncItemAllowed is_allowed, - FuncReadImport read_value, int32_t list_size, - std::vector* pile_list) { - pile_list->clear(); - pile_list->resize(112, '\0'); // TODO remove hardcoded list size value - for (size_t i = 0; i < pile_list->size(); ++i) { - pile_list->at(i) = is_allowed((item_type::item_type)i) ? 0 : 1; +static void unserialize_list_item_type(bool all, char val, const string& filter, FuncItemAllowed is_allowed, + FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { + // TODO can we remove the hardcoded list size? + size_t num_elems = 112; + pile_list.resize(num_elems, '\0'); + for (size_t i = 0; i < num_elems; ++i) { + if (!is_allowed((df::item_type)i)) + pile_list.at(i) = 1; } + + if (all) { + for (size_t idx = 0; idx < num_elems; ++idx) { + string id = ENUM_KEY_STR(item_type, (df::item_type)idx); + set_filter_elem(filter, val, id, idx, pile_list.at(idx)); + } + return; + } + using df::enums::item_type::item_type; df::enum_traits type_traits; - for (int32_t i = 0; i < list_size; ++i) { + for (int i = 0; i < list_size; ++i) { const std::string token = read_value(i); // subtract one because item_type starts at -1 const df::enum_traits::base_type idx = linear_index(type_traits, token) - 1; - const item_type type = (item_type)idx; - if (!is_allowed(type)) + if (!is_allowed((item_type)idx)) continue; - DEBUG(log).print("item_type %d is %s\n", idx, token.c_str()); - if (size_t(idx) >= pile_list->size()) { - WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, pile_list->size()); + if (idx < 0 || size_t(idx) >= num_elems) { + WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - pile_list->at(idx) = 1; + set_filter_elem(filter, val, token, idx, pile_list.at(idx)); } } @@ -539,7 +559,7 @@ static void unserialize_list_material(bool all, char val, const string& filter, } if (all) { - for (auto idx = 0; idx < num_elems; ++idx) { + for (size_t idx = 0; idx < num_elems; ++idx) { MaterialInfo mi; mi.decode(0, idx); set_filter_elem(filter, val, mi.toString(), idx, pile_list.at(idx)); @@ -560,6 +580,48 @@ static void unserialize_list_material(bool all, char val, const string& filter, } } +static bool serialize_list_creature(FuncWriteExport add_value, const std::vector& list) { + bool all = true; + + for (size_t i = 0; i < list.size(); ++i) { + if (!list.at(i)) { + all = false; + continue; + } + df::creature_raw* r = find_creature(i); + if (r->flags.is_set(creature_raw_flags::GENERATED) + || r->creature_id == "EQUIPMENT_WAGON") + continue; + DEBUG(log).print("adding creature %s\n", r->creature_id.c_str()); + add_value(r->creature_id); + } + return all; +} + +static void unserialize_list_creature(bool all, char val, const string& filter, + FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { + size_t num_elems = world->raws.creatures.all.size(); + pile_list.resize(num_elems, '\0'); + if (all) { + for (size_t idx = 0; idx < num_elems; ++idx) { + auto r = find_creature(idx); + set_filter_elem(filter, val, r->name[0], r->creature_id, pile_list.at(idx)); + } + return; + } + + for (auto i = 0; i < list_size; ++i) { + string id = read_value(i); + int idx = find_creature(id); + if (idx < 0 || size_t(idx) >= num_elems) { + WARN(log).print("animal index invalid: %d\n", idx); + continue; + } + auto r = find_creature(idx); + set_filter_elem(filter, val, r->name[0], r->creature_id, pile_list.at(idx)); + } +} + template static void write_cat(const char *name, bool include_types, uint32_t cat_flags, enum df::stockpile_group_set::Mask cat_mask, @@ -815,14 +877,14 @@ static bool ammo_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { bool all = serialize_list_itemdef( - [=](const std::string& token) { ammo->add_type(token); }, + [&](const std::string& token) { ammo->add_type(token); }, mPile->settings.ammo.type, std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), item_type::AMMO); all = serialize_list_material( ammo_mat_is_allowed, - [=](const std::string& token) { ammo->add_mats(token); }, + [&](const std::string& token) { ammo->add_mats(token); }, mPile->settings.ammo.mats) && all; if (mPile->settings.ammo.other_mats.size() > 2) { @@ -843,61 +905,78 @@ bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { } all = serialize_list_quality( - [=](const std::string& token) { ammo->add_quality_core(token); }, + [&](const std::string& token) { ammo->add_quality_core(token); }, mPile->settings.ammo.quality_core) && all; all = serialize_list_quality( - [=](const std::string& token) { ammo->add_quality_total(token); }, + [&](const std::string& token) { ammo->add_quality_total(token); }, mPile->settings.ammo.quality_total) && all; return all; } void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& filter) { - auto & pammo = mPile->settings.ammo; - read_category("ammo", mode, - std::bind(&StockpileSettings::has_ammo, mBuffer), - std::bind(&StockpileSettings::ammo, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_ammo, - [&]() { - pammo.type.clear(); - pammo.mats.clear(); - pammo.other_mats.clear(); - quality_clear(pammo.quality_core); - quality_clear(pammo.quality_total); - }, - [&](bool all, char val) { - auto & bammo = mBuffer.ammo(); - - unserialize_list_itemdef(all, val, filter, - [&](const size_t& idx) -> const std::string& { return bammo.type(idx); }, - bammo.type_size(), pammo.type, item_type::AMMO); - - unserialize_list_material(all, val, filter, ammo_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, - bammo.mats_size(), pammo.mats); - - pammo.other_mats.resize(2, '\0'); - if (bammo.other_mats_size() > 0) { - // TODO remove hardcoded value - for (int i = 0; i < bammo.other_mats_size(); ++i) { - const std::string token = bammo.other_mats(i); - const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 - : -1; - DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); - if (idx == -1) - continue; - pammo.other_mats.at(idx) = 1; - } + auto & pammo = mPile->settings.ammo; + read_category("ammo", mode, + std::bind(&StockpileSettings::has_ammo, mBuffer), + std::bind(&StockpileSettings::ammo, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_ammo, + [&]() { + pammo.type.clear(); + pammo.mats.clear(); + pammo.other_mats.clear(); + quality_clear(pammo.quality_core); + quality_clear(pammo.quality_total); + }, + [&](bool all, char val) { + auto & bammo = mBuffer.ammo(); + + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bammo.type(idx); }, + bammo.type_size(), pammo.type, item_type::AMMO); + + unserialize_list_material(all, val, filter, ammo_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, + bammo.mats_size(), pammo.mats); + + pammo.other_mats.resize(2, '\0'); + if (bammo.other_mats_size() > 0) { + // TODO remove hardcoded value + for (int i = 0; i < bammo.other_mats_size(); ++i) { + const std::string token = bammo.other_mats(i); + const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 + : -1; + DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); + if (idx == -1) + continue; + pammo.other_mats.at(idx) = 1; } + } - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return bammo.quality_core(idx); }, - bammo.quality_core_size(), pammo.quality_core); + pammo.other_mats.resize(2, '\0'); + if (all) { + set_filter_elem(filter, val, "WOOD", 0, pammo.other_mats.at(0)); + set_filter_elem(filter, val, "BONE", 1, pammo.other_mats.at(1)); + } else { + // TODO can we un-hardcode the values? + for (int i = 0; i < bammo.other_mats_size(); ++i) { + const std::string id = bammo.other_mats(i); + const int32_t idx = id == "WOOD" ? 0 : id == "BONE" ? 1 : -1; + if (idx == -1) + continue; + set_filter_elem(filter, val, id, idx, pammo.other_mats.at(idx)); + } + } - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return bammo.quality_total(idx); }, - bammo.quality_total_size(), pammo.quality_total); - }); + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bammo.quality_core(idx); }, + bammo.quality_core_size(), pammo.quality_core); + + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bammo.quality_total(idx); }, + bammo.quality_total_size(), pammo.quality_total); + }); } bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { @@ -906,58 +985,34 @@ bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) animals->set_empty_cages(panimals.empty_cages); animals->set_empty_traps(panimals.empty_traps); - for (size_t i = 0; i < panimals.enabled.size(); ++i) { - if (!panimals.enabled.at(i)) { - all = false; - continue; - } - df::creature_raw* r = find_creature(i); - if (r->flags.is_set(creature_raw_flags::GENERATED) - || r->creature_id == "EQUIPMENT_WAGON") - continue; - DEBUG(log).print("saving creature %s\n", r->creature_id.c_str()); - animals->add_enabled(r->creature_id); - } - return all; + + return serialize_list_creature( + [&](const std::string& token) { animals->add_enabled(token); }, + panimals.enabled) && all; } void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& filter) { auto & panimals = mPile->settings.animals; read_category("animals", mode, - std::bind(&StockpileSettings::has_animals, mBuffer), - std::bind(&StockpileSettings::animals, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_animals, - [&]() { - panimals.empty_cages = false; - panimals.empty_traps = false; - panimals.enabled.clear(); - }, - [&](bool all, char val) { - auto & banimals = mBuffer.animals(); - set_elem(all, val, banimals.empty_cages(), panimals.empty_cages); - set_elem(all, val, banimals.empty_traps(), panimals.empty_traps); - - size_t num_animals = world->raws.creatures.all.size(); - panimals.enabled.resize(num_animals, '\0'); - if (all) { - for (auto idx = 0; idx < num_animals; ++idx) { - auto r = find_creature(idx); - set_filter_elem(filter, val, r->name[0], r->creature_id, panimals.enabled.at(idx)); - } - } else { - for (auto i = 0; i < banimals.enabled_size(); ++i) { - const std::string& id = banimals.enabled(i); - int idx = find_creature(id); - if (idx < 0 || size_t(idx) >= num_animals) { - WARN(log).print("animal index invalid: %d\n", idx); - continue; - } - auto r = find_creature(idx); - set_filter_elem(filter, val, r->name[0], r->creature_id, panimals.enabled.at(idx)); - } - } - }); + std::bind(&StockpileSettings::has_animals, mBuffer), + std::bind(&StockpileSettings::animals, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_animals, + [&]() { + panimals.empty_cages = false; + panimals.empty_traps = false; + panimals.enabled.clear(); + }, + [&](bool all, char val) { + auto & banimals = mBuffer.animals(); + + set_elem(all, val, banimals.empty_cages(), panimals.empty_cages); + set_elem(all, val, banimals.empty_traps(), panimals.empty_traps); + + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const std::string& { return banimals.enabled(idx); }, + banimals.enabled_size(), panimals.enabled); + }); } static bool armor_mat_is_allowed(const MaterialInfo& mi) { @@ -974,42 +1029,42 @@ bool StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { // armor type all = serialize_list_itemdef( - [=](const std::string& token) { armor->add_body(token); }, + [&](const std::string& token) { armor->add_body(token); }, parmor.body, std::vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), item_type::ARMOR) && all; // helm type all = serialize_list_itemdef( - [=](const std::string& token) { armor->add_head(token); }, + [&](const std::string& token) { armor->add_head(token); }, parmor.head, std::vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), item_type::HELM) && all; // shoes type all = serialize_list_itemdef( - [=](const std::string& token) { armor->add_feet(token); }, + [&](const std::string& token) { armor->add_feet(token); }, parmor.feet, std::vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), item_type::SHOES) && all; // gloves type all = serialize_list_itemdef( - [=](const std::string& token) { armor->add_hands(token); }, + [&](const std::string& token) { armor->add_hands(token); }, parmor.hands, std::vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), item_type::GLOVES) && all; // pant type all = serialize_list_itemdef( - [=](const std::string& token) { armor->add_legs(token); }, + [&](const std::string& token) { armor->add_legs(token); }, parmor.legs, std::vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), item_type::PANTS) && all; // shield type all = serialize_list_itemdef( - [=](const std::string& token) { armor->add_shield(token); }, + [&](const std::string& token) { armor->add_shield(token); }, parmor.shield, std::vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), item_type::SHIELD) && all; @@ -1017,84 +1072,91 @@ bool StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { // materials all = serialize_list_material( armor_mat_is_allowed, - [=](const std::string& token) { armor->add_mats(token); }, + [&](const std::string& token) { armor->add_mats(token); }, parmor.mats) && all; // other mats all = serialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [=](const std::string& token) { armor->add_other_mats(token); }, + mOtherMatsWeaponsArmor.mats, [&](const std::string& token) { armor->add_other_mats(token); }, parmor.other_mats) && all; // quality core - all = serialize_list_quality([=](const std::string& token) { armor->add_quality_core(token); }, + all = serialize_list_quality([&](const std::string& token) { armor->add_quality_core(token); }, parmor.quality_core) && all; // quality total - all = serialize_list_quality([=](const std::string& token) { armor->add_quality_total(token); }, + all = serialize_list_quality([&](const std::string& token) { armor->add_quality_total(token); }, parmor.quality_total) && all; return all; } void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_armor()) { - mPile->settings.flags.bits.armor = 1; - const StockpileSettings::ArmorSet armor = mBuffer.armor(); - DEBUG(log).print("armor:\n"); + auto & parmor = mPile->settings.armor; + read_category("armor", mode, + std::bind(&StockpileSettings::has_armor, mBuffer), + std::bind(&StockpileSettings::armor, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_armor, + [&]() { + parmor.body.clear(); + parmor.head.clear(); + parmor.feet.clear(); + parmor.hands.clear(); + parmor.legs.clear(); + parmor.shield.clear(); + parmor.other_mats.clear(); + parmor.mats.clear(); + quality_clear(parmor.quality_core); + quality_clear(parmor.quality_total); + }, + [&](bool all, char val) { + auto & barmor = mBuffer.armor(); - bool unusable = armor.unusable(); - bool usable = armor.usable(); - DEBUG(log).print("unusable %d\n", unusable); - DEBUG(log).print("usable %d\n", usable); - mPile->settings.armor.unusable = unusable; - mPile->settings.armor.usable = usable; + set_elem(all, val, barmor.unusable(), parmor.unusable); + set_elem(all, val, barmor.usable(), parmor.usable); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.body(idx); }, - armor.body_size(), &mPile->settings.armor.body, item_type::ARMOR); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.body(idx); }, + barmor.body_size(), parmor.body, item_type::ARMOR); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.head(idx); }, - armor.head_size(), &mPile->settings.armor.head, item_type::HELM); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.head(idx); }, + barmor.head_size(), parmor.head, item_type::HELM); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.feet(idx); }, - armor.feet_size(), &mPile->settings.armor.feet, item_type::SHOES); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.feet(idx); }, + barmor.feet_size(), parmor.feet, item_type::SHOES); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.hands(idx); }, - armor.hands_size(), &mPile->settings.armor.hands, item_type::GLOVES); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.hands(idx); }, + barmor.hands_size(), parmor.hands, item_type::GLOVES); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.legs(idx); }, - armor.legs_size(), &mPile->settings.armor.legs, item_type::PANTS); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.legs(idx); }, + barmor.legs_size(), parmor.legs, item_type::PANTS); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return armor.shield(idx); }, - armor.shield_size(), &mPile->settings.armor.shield, item_type::SHIELD); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.shield(idx); }, + barmor.shield_size(), parmor.shield, item_type::SHIELD); - unserialize_list_material( - armor_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return armor.mats(idx); }, - armor.mats_size(), &mPile->settings.armor.mats); + unserialize_list_material(all, val, filter, + armor_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return barmor.mats(idx); }, + barmor.mats_size(), parmor.mats); - unserialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return armor.other_mats(idx); }, - armor.other_mats_size(), &mPile->settings.armor.other_mats); + unserialize_list_other_mats(all, val, filter, + mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const std::string& { return barmor.other_mats(idx); }, + barmor.other_mats_size(), parmor.other_mats); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_core(idx); }, - armor.quality_core_size(), mPile->settings.armor.quality_core); + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.quality_core(idx); }, + barmor.quality_core_size(), parmor.quality_core); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return armor.quality_total(idx); }, - armor.quality_total_size(), mPile->settings.armor.quality_total); - } - else { - mPile->settings.flags.bits.armor = 0; - mPile->settings.armor.body.clear(); - mPile->settings.armor.head.clear(); - mPile->settings.armor.feet.clear(); - mPile->settings.armor.hands.clear(); - mPile->settings.armor.legs.clear(); - mPile->settings.armor.shield.clear(); - mPile->settings.armor.other_mats.clear(); - mPile->settings.armor.mats.clear(); - quality_clear(mPile->settings.armor.quality_core); - quality_clear(mPile->settings.armor.quality_total); - } + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return barmor.quality_total(idx); }, + barmor.quality_total_size(), parmor.quality_total); + }); } static bool bars_mat_is_allowed(const MaterialInfo& mi) { @@ -1108,139 +1170,152 @@ static bool blocks_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { bool all = serialize_list_material( bars_mat_is_allowed, - [=](const std::string& token) { bars_blocks->add_bars_mats(token); }, + [&](const std::string& token) { bars_blocks->add_bars_mats(token); }, mPile->settings.bars_blocks.bars_mats); all = serialize_list_material( blocks_mat_is_allowed, - [=](const std::string& token) { bars_blocks->add_blocks_mats(token); }, + [&](const std::string& token) { bars_blocks->add_blocks_mats(token); }, mPile->settings.bars_blocks.blocks_mats) && all; all = serialize_list_other_mats( - mOtherMatsBars.mats, [=](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, + mOtherMatsBars.mats, [&](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, mPile->settings.bars_blocks.bars_other_mats) && all; all = serialize_list_other_mats( - mOtherMatsBlocks.mats, [=](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, + mOtherMatsBlocks.mats, [&](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, mPile->settings.bars_blocks.blocks_other_mats) && all; return all; } void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_barsblocks()) { - mPile->settings.flags.bits.bars_blocks = 1; - const StockpileSettings::BarsBlocksSet bars_blocks = mBuffer.barsblocks(); - DEBUG(log).print("bars_blocks:\n"); + auto & pbarsblocks = mPile->settings.bars_blocks; + read_category("bars_blocks", mode, + std::bind(&StockpileSettings::has_barsblocks, mBuffer), + std::bind(&StockpileSettings::barsblocks, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_bars_blocks, + [&]() { + pbarsblocks.bars_other_mats.clear(); + pbarsblocks.bars_mats.clear(); + pbarsblocks.blocks_other_mats.clear(); + pbarsblocks.blocks_mats.clear(); + }, + [&](bool all, char val) { + auto & bbarsblocks = mBuffer.barsblocks(); - unserialize_list_material( - bars_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_mats(idx); }, - bars_blocks.bars_mats_size(), &mPile->settings.bars_blocks.bars_mats); + unserialize_list_material(all, val, filter, bars_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bbarsblocks.bars_mats(idx); }, + bbarsblocks.bars_mats_size(), pbarsblocks.bars_mats); - unserialize_list_material( - blocks_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_mats(idx); }, - bars_blocks.blocks_mats_size(), &mPile->settings.bars_blocks.blocks_mats); + unserialize_list_material(all, val, filter, + blocks_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_mats(idx); }, + bbarsblocks.blocks_mats_size(), pbarsblocks.blocks_mats); - unserialize_list_other_mats( - mOtherMatsBars.mats, - [=](const size_t& idx) -> const std::string& { return bars_blocks.bars_other_mats(idx); }, - bars_blocks.bars_other_mats_size(), &mPile->settings.bars_blocks.bars_other_mats); + unserialize_list_other_mats(all, val, filter, + mOtherMatsBars.mats, + [&](const size_t& idx) -> const std::string& { return bbarsblocks.bars_other_mats(idx); }, + bbarsblocks.bars_other_mats_size(), pbarsblocks.bars_other_mats); - unserialize_list_other_mats( - mOtherMatsBlocks.mats, - [=](const size_t& idx) -> const std::string& { return bars_blocks.blocks_other_mats(idx); }, - bars_blocks.blocks_other_mats_size(), &mPile->settings.bars_blocks.blocks_other_mats); - } - else { - mPile->settings.flags.bits.bars_blocks = 0; - mPile->settings.bars_blocks.bars_other_mats.clear(); - mPile->settings.bars_blocks.bars_mats.clear(); - mPile->settings.bars_blocks.blocks_other_mats.clear(); - mPile->settings.bars_blocks.blocks_mats.clear(); - } + unserialize_list_other_mats(all, val, filter, + mOtherMatsBlocks.mats, + [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_other_mats(idx); }, + bbarsblocks.blocks_other_mats_size(), pbarsblocks.blocks_other_mats); + }); } bool StockpileSerializer::write_cloth(StockpileSettings::ClothSet* cloth) { bool all = true; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_thread_silk(token); }, + [&](const std::string& token) { cloth->add_thread_silk(token); }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_thread_plant(token); }, + [&](const std::string& token) { cloth->add_thread_plant(token); }, &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_thread_yarn(token); }, + [&](const std::string& token) { cloth->add_thread_yarn(token); }, &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_thread_metal(token); }, + [&](const std::string& token) { cloth->add_thread_metal(token); }, &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_cloth_silk(token); }, + [&](const std::string& token) { cloth->add_cloth_silk(token); }, &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_cloth_plant(token); }, + [&](const std::string& token) { cloth->add_cloth_plant(token); }, &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_cloth_yarn(token); }, + [&](const std::string& token) { cloth->add_cloth_yarn(token); }, &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn) && all; all = serialize_list_organic_mat( - [=](const std::string& token) { cloth->add_cloth_metal(token); }, + [&](const std::string& token) { cloth->add_cloth_metal(token); }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread) && all; return all; } void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_cloth()) { - mPile->settings.flags.bits.cloth = 1; - const StockpileSettings::ClothSet cloth = mBuffer.cloth(); - DEBUG(log).print("cloth:\n"); + auto & pcloth = mPile->settings.cloth; + read_category("cloth", mode, + std::bind(&StockpileSettings::has_cloth, mBuffer), + std::bind(&StockpileSettings::cloth, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_cloth, + [&]() { + pcloth.thread_metal.clear(); + pcloth.thread_plant.clear(); + pcloth.thread_silk.clear(); + pcloth.thread_yarn.clear(); + pcloth.cloth_metal.clear(); + pcloth.cloth_plant.clear(); + pcloth.cloth_silk.clear(); + pcloth.cloth_yarn.clear(); + }, + [&](bool all, char val) { + auto & bcloth = mBuffer.cloth(); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_silk(idx); }, - cloth.thread_silk_size(), &mPile->settings.cloth.thread_silk, organic_mat_category::Silk); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.thread_silk(idx); }, + bcloth.thread_silk_size(), pcloth.thread_silk, organic_mat_category::Silk); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_plant(idx); }, - cloth.thread_plant_size(), &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.thread_plant(idx); }, + bcloth.thread_plant_size(), pcloth.thread_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_yarn(idx); }, - cloth.thread_yarn_size(), &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.thread_yarn(idx); }, + bcloth.thread_yarn_size(), pcloth.thread_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.thread_metal(idx); }, - cloth.thread_metal_size(), &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.thread_metal(idx); }, + bcloth.thread_metal_size(), pcloth.thread_metal, organic_mat_category::MetalThread); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_silk(idx); }, - cloth.cloth_silk_size(), &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.cloth_silk(idx); }, + bcloth.cloth_silk_size(), pcloth.cloth_silk, organic_mat_category::Silk); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_plant(idx); }, - cloth.cloth_plant_size(), &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.cloth_plant(idx); }, + bcloth.cloth_plant_size(), pcloth.cloth_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_yarn(idx); }, - cloth.cloth_yarn_size(), &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.cloth_yarn(idx); }, + bcloth.cloth_yarn_size(), pcloth.cloth_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return cloth.cloth_metal(idx); }, - cloth.cloth_metal_size(), &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread); - } - else { - mPile->settings.cloth.thread_metal.clear(); - mPile->settings.cloth.thread_plant.clear(); - mPile->settings.cloth.thread_silk.clear(); - mPile->settings.cloth.thread_yarn.clear(); - mPile->settings.cloth.cloth_metal.clear(); - mPile->settings.cloth.cloth_plant.clear(); - mPile->settings.cloth.cloth_silk.clear(); - mPile->settings.cloth.cloth_yarn.clear(); - mPile->settings.flags.bits.cloth = 0; - } + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bcloth.cloth_metal(idx); }, + bcloth.cloth_metal_size(), pcloth.cloth_metal, organic_mat_category::MetalThread); + }); } static bool coins_mat_is_allowed(const MaterialInfo& mi) { @@ -1250,25 +1325,27 @@ static bool coins_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_coins(StockpileSettings::CoinSet* coins) { return serialize_list_material( coins_mat_is_allowed, - [=](const std::string& token) { coins->add_mats(token); }, + [&](const std::string& token) { coins->add_mats(token); }, mPile->settings.coins.mats); } void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_coin()) { - mPile->settings.flags.bits.coins = 1; - const StockpileSettings::CoinSet coins = mBuffer.coin(); - DEBUG(log).print("coins:\n"); + auto & pcoins = mPile->settings.coins; + read_category("coin", mode, + std::bind(&StockpileSettings::has_coin, mBuffer), + std::bind(&StockpileSettings::coin, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_coins, + [&]() { + pcoins.mats.clear(); + }, + [&](bool all, char val) { + auto & bcoin = mBuffer.coin(); - unserialize_list_material( - coins_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return coins.mats(idx); }, - coins.mats_size(), &mPile->settings.coins.mats); - } - else { - mPile->settings.flags.bits.coins = 0; - mPile->settings.coins.mats.clear(); - } + unserialize_list_material(all, val, filter, coins_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bcoin.mats(idx); }, + bcoin.mats_size(), pcoins.mats); + }); } static bool finished_goods_type_is_allowed(item_type::item_type type) { @@ -1311,61 +1388,64 @@ static bool finished_goods_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { bool all = serialize_list_item_type( finished_goods_type_is_allowed, - [=](const std::string& token) { finished_goods->add_type(token); }, + [&](const std::string& token) { finished_goods->add_type(token); }, mPile->settings.finished_goods.type); all = serialize_list_material( finished_goods_mat_is_allowed, - [=](const std::string& token) { finished_goods->add_mats(token); }, + [&](const std::string& token) { finished_goods->add_mats(token); }, mPile->settings.finished_goods.mats) && all; all = serialize_list_other_mats( - mOtherMatsFinishedGoods.mats, [=](const std::string& token) { finished_goods->add_other_mats(token); }, + mOtherMatsFinishedGoods.mats, [&](const std::string& token) { finished_goods->add_other_mats(token); }, mPile->settings.finished_goods.other_mats) && all; - all = serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_core(token); }, + all = serialize_list_quality([&](const std::string& token) { finished_goods->add_quality_core(token); }, mPile->settings.finished_goods.quality_core) && all; - all = serialize_list_quality([=](const std::string& token) { finished_goods->add_quality_total(token); }, + all = serialize_list_quality([&](const std::string& token) { finished_goods->add_quality_total(token); }, mPile->settings.finished_goods.quality_total) && all; return all; } void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_finished_goods()) { - mPile->settings.flags.bits.finished_goods = 1; - const StockpileSettings::FinishedGoodsSet finished_goods = mBuffer.finished_goods(); - DEBUG(log).print("finished_goods:\n"); + auto & pfinished_goods = mPile->settings.finished_goods; + read_category("finished_goods", mode, + std::bind(&StockpileSettings::has_finished_goods, mBuffer), + std::bind(&StockpileSettings::finished_goods, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_finished_goods, + [&]() { + pfinished_goods.type.clear(); + pfinished_goods.other_mats.clear(); + pfinished_goods.mats.clear(); + quality_clear(pfinished_goods.quality_core); + quality_clear(pfinished_goods.quality_total); + }, + [&](bool all, char val) { + auto & bfinished_goods = mBuffer.finished_goods(); - unserialize_list_item_type( - finished_goods_type_is_allowed, - [=](const size_t& idx) -> const std::string& { return finished_goods.type(idx); }, - finished_goods.type_size(), &mPile->settings.finished_goods.type); + unserialize_list_item_type(all, val, filter, finished_goods_type_is_allowed, + [&](const size_t& idx) -> const std::string& { return bfinished_goods.type(idx); }, + bfinished_goods.type_size(), pfinished_goods.type); - unserialize_list_material( - finished_goods_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return finished_goods.mats(idx); }, - finished_goods.mats_size(), &mPile->settings.finished_goods.mats); + unserialize_list_material(all, val, filter, finished_goods_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bfinished_goods.mats(idx); }, + bfinished_goods.mats_size(), pfinished_goods.mats); - unserialize_list_other_mats( - mOtherMatsFinishedGoods.mats, [=](const size_t& idx) -> const std::string& { return finished_goods.other_mats(idx); }, - finished_goods.other_mats_size(), &mPile->settings.finished_goods.other_mats); + unserialize_list_other_mats(all, val, filter, mOtherMatsFinishedGoods.mats, + [&](const size_t& idx) -> const std::string& { return bfinished_goods.other_mats(idx); }, + bfinished_goods.other_mats_size(), pfinished_goods.other_mats); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_core(idx); }, - finished_goods.quality_core_size(), mPile->settings.finished_goods.quality_core); + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bfinished_goods.quality_core(idx); }, + bfinished_goods.quality_core_size(), pfinished_goods.quality_core); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return finished_goods.quality_total(idx); }, - finished_goods.quality_total_size(), mPile->settings.finished_goods.quality_total); - } - else { - mPile->settings.flags.bits.finished_goods = 0; - mPile->settings.finished_goods.type.clear(); - mPile->settings.finished_goods.other_mats.clear(); - mPile->settings.finished_goods.mats.clear(); - quality_clear(mPile->settings.finished_goods.quality_core); - quality_clear(mPile->settings.finished_goods.quality_total); - } + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bfinished_goods.quality_total(idx); }, + bfinished_goods.quality_total_size(), pfinished_goods.quality_total); + }); } food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { @@ -1374,155 +1454,155 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego switch (cat) { case organic_mat_category::Meat: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_meat(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; return food_pair(setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); } case organic_mat_category::Fish: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_fish(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; return food_pair(setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_unprepared_fish(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; return food_pair(setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_egg(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; return food_pair(setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); } case organic_mat_category::Plants: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_plants(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; return food_pair(setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); } case organic_mat_category::PlantDrink: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_drink_plant(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; return food_pair(setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); } case organic_mat_category::CreatureDrink: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_drink_animal(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; return food_pair(setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); } case organic_mat_category::PlantCheese: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_cheese_plant(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; return food_pair(setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); } case organic_mat_category::CreatureCheese: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_cheese_animal(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; return food_pair(setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); } case organic_mat_category::Seed: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_seeds(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; return food_pair(setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); } case organic_mat_category::Leaf: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_leaves(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; return food_pair(setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); } case organic_mat_category::PlantPowder: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_powder_plant(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; return food_pair(setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); } case organic_mat_category::CreaturePowder: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_powder_creature(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; return food_pair(setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); } case organic_mat_category::Glob: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_glob(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; return food_pair(setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); } case organic_mat_category::PlantLiquid: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_liquid_plant(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; return food_pair(setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); } case organic_mat_category::CreatureLiquid: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_liquid_animal(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; return food_pair(setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); } case organic_mat_category::MiscLiquid: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_liquid_misc(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; return food_pair(setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); } case organic_mat_category::Paste: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_glob_paste(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; return food_pair(setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); } case organic_mat_category::Pressed: { - FuncWriteExport setter = [=](const std::string& id) { + FuncWriteExport setter = [&](const std::string& id) { mBuffer.mutable_food()->add_glob_pressed(id); }; - FuncReadImport getter = [=](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; + FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; return food_pair(setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); } case organic_mat_category::Leather: @@ -1551,8 +1631,6 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego } bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { - DEBUG(log).print("food:\n"); - auto & pfood = mPile->settings.food; bool all = pfood.prepared_meals; @@ -1574,35 +1652,37 @@ bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { void StockpileSerializer::read_food(DeserializeMode mode, const std::string& filter) { using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; - if (mBuffer.has_food()) { - mPile->settings.flags.bits.food = 1; - const StockpileSettings::FoodSet food = mBuffer.food(); - DEBUG(log).print("food:\n"); - - if (food.has_prepared_meals()) - mPile->settings.food.prepared_meals = food.prepared_meals(); - else - mPile->settings.food.prepared_meals = true; - - DEBUG(log).print("prepared_meals: %d\n", mPile->settings.food.prepared_meals); - - for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { - food_pair p = food_map((organic_mat_category)mat_category); - if (!p.valid) - continue; - unserialize_list_organic_mat(p.get_value, p.serialized_count, p.stockpile_values, (organic_mat_category)mat_category); - } - } - else { - for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { - food_pair p = food_map((organic_mat_category)mat_category); - if (!p.valid) - continue; - p.stockpile_values->clear(); - } - mPile->settings.flags.bits.food = 0; - mPile->settings.food.prepared_meals = false; - } + + auto & pfood = mPile->settings.food; + read_category("food", mode, + std::bind(&StockpileSettings::has_food, mBuffer), + std::bind(&StockpileSettings::food, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_food, + [&]() { + pfood.prepared_meals = false; + for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { + food_pair p = food_map((organic_mat_category)mat_category); + if (!p.valid) + continue; + p.stockpile_values->clear(); + } + }, + [&](bool all, char val) { + auto & bfood = mBuffer.food(); + + set_elem(all, val, bfood.prepared_meals(), pfood.prepared_meals); + + for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { + food_pair p = food_map((organic_mat_category)mat_category); + if (!p.valid) + continue; + unserialize_list_organic_mat(all, val, filter, + p.get_value, p.serialized_count, *p.stockpile_values, + (organic_mat_category)mat_category); + } + }); + } static bool furniture_mat_is_allowed(const MaterialInfo& mi) { @@ -1627,69 +1707,77 @@ bool StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furni } all = serialize_list_material( furniture_mat_is_allowed, - [=](const std::string& token) { furniture->add_mats(token); }, + [&](const std::string& token) { furniture->add_mats(token); }, pfurniture.mats) && all; all = serialize_list_other_mats( mOtherMatsFurniture.mats, - [=](const std::string& token) { furniture->add_other_mats(token); }, + [&](const std::string& token) { furniture->add_other_mats(token); }, pfurniture.other_mats) && all; all = serialize_list_quality( - [=](const std::string& token) { furniture->add_quality_core(token); }, + [&](const std::string& token) { furniture->add_quality_core(token); }, pfurniture.quality_core) && all; all = serialize_list_quality( - [=](const std::string& token) { furniture->add_quality_total(token); }, + [&](const std::string& token) { furniture->add_quality_total(token); }, pfurniture.quality_total) && all; return all; } void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_furniture()) { - mPile->settings.flags.bits.furniture = 1; - const StockpileSettings::FurnitureSet furniture = mBuffer.furniture(); - DEBUG(log).print("furniture:\n"); - - // type - using df::enums::furniture_type::furniture_type; - df::enum_traits type_traits; - mPile->settings.furniture.type.clear(); - mPile->settings.furniture.type.resize(type_traits.last_item_value + 1, '\0'); - if (furniture.type_size() > 0) { - for (int i = 0; i < furniture.type_size(); ++i) { - const std::string type = furniture.type(i); - df::enum_traits::base_type idx = linear_index(type_traits, type); - DEBUG(log).print("type %d is %s\n", idx, type.c_str()); - if (idx < 0 || size_t(idx) >= mPile->settings.furniture.type.size()) { - WARN(log).print("furniture type index invalid %s, idx=%d\n", type.c_str(), idx); - continue; + auto & pfurniture = mPile->settings.furniture; + read_category("furniture", mode, + std::bind(&StockpileSettings::has_furniture, mBuffer), + std::bind(&StockpileSettings::furniture, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_furniture, + [&]() { + pfurniture.type.clear(); + pfurniture.other_mats.clear(); + pfurniture.mats.clear(); + quality_clear(pfurniture.quality_core); + quality_clear(pfurniture.quality_total); + }, + [&](bool all, char val) { + auto & bfurniture = mBuffer.furniture(); + + using df::enums::furniture_type::furniture_type; + df::enum_traits type_traits; + size_t num_elems = type_traits.last_item_value + 1; + pfurniture.type.resize(num_elems, '\0'); + + if (all) { + for (size_t idx = 0; idx < num_elems; ++idx) { + string id = ENUM_KEY_STR(furniture_type, (df::furniture_type)idx); + set_filter_elem(filter, val, id, idx, pfurniture.type.at(idx)); + } + } else { + for (int i = 0; i < bfurniture.type_size(); ++i) { + const std::string token = bfurniture.type(i); + df::enum_traits::base_type idx = linear_index(type_traits, token); + if (idx < 0 || size_t(idx) >= pfurniture.type.size()) { + WARN(log).print("furniture type index invalid %s, idx=%d\n", token.c_str(), idx); + continue; + } + set_filter_elem(filter, val, token, idx, pfurniture.type.at(idx)); } - mPile->settings.furniture.type.at(idx) = 1; } - } - unserialize_list_material( - furniture_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return furniture.mats(idx); }, - furniture.mats_size(), &mPile->settings.furniture.mats); + unserialize_list_material(all, val, filter, furniture_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bfurniture.mats(idx); }, + bfurniture.mats_size(), pfurniture.mats); - unserialize_list_other_mats( - mOtherMatsFurniture.mats, [=](const size_t& idx) -> const std::string& { return furniture.other_mats(idx); }, - furniture.other_mats_size(), &mPile->settings.furniture.other_mats); + unserialize_list_other_mats(all, val, filter, + mOtherMatsFurniture.mats, [&](const size_t& idx) -> const std::string& { return bfurniture.other_mats(idx); }, + bfurniture.other_mats_size(), pfurniture.other_mats); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_core(idx); }, - furniture.quality_core_size(), mPile->settings.furniture.quality_core); + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bfurniture.quality_core(idx); }, + bfurniture.quality_core_size(), pfurniture.quality_core); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return furniture.quality_total(idx); }, - furniture.quality_total_size(), mPile->settings.furniture.quality_total); - } - else { - mPile->settings.flags.bits.furniture = 0; - mPile->settings.furniture.type.clear(); - mPile->settings.furniture.other_mats.clear(); - mPile->settings.furniture.mats.clear(); - quality_clear(mPile->settings.furniture.quality_core); - quality_clear(mPile->settings.furniture.quality_total); - } + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bfurniture.quality_total(idx); }, + bfurniture.quality_total_size(), pfurniture.quality_total); + }); } static bool gem_mat_is_allowed(const MaterialInfo& mi) { @@ -1711,12 +1799,12 @@ bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { bool all = serialize_list_material( gem_mat_is_allowed, - [=](const std::string& token) { gems->add_rough_mats(token); }, + [&](const std::string& token) { gems->add_rough_mats(token); }, pgems.rough_mats); all = serialize_list_material( gem_cut_mat_is_allowed, - [=](const std::string& token) { gems->add_cut_mats(token); }, + [&](const std::string& token) { gems->add_cut_mats(token); }, pgems.cut_mats) && all; for (size_t i = 0; i < pgems.rough_other_mats.size(); ++i) { @@ -1749,121 +1837,108 @@ bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { } void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_gems()) { - mPile->settings.flags.bits.gems = 1; - const StockpileSettings::GemsSet gems = mBuffer.gems(); - DEBUG(log).print("gems:\n"); - - unserialize_list_material( - gem_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return gems.rough_mats(idx); }, - gems.rough_mats_size(), &mPile->settings.gems.rough_mats); - - unserialize_list_material( - gem_cut_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return gems.cut_mats(idx); }, - gems.cut_mats_size(), &mPile->settings.gems.cut_mats); - - const size_t builtin_size = std::extentraws.mat_table.builtin)>::value; - // rough other - mPile->settings.gems.rough_other_mats.clear(); - mPile->settings.gems.rough_other_mats.resize(builtin_size, '\0'); - for (int i = 0; i < gems.rough_other_mats_size(); ++i) { - const std::string token = gems.rough_other_mats(i); - MaterialInfo mi; - mi.find(token); - if (!mi.isValid() || size_t(mi.type) >= builtin_size) { - WARN(log).print("invalid gem mat %s idx=%d\n", token.c_str(), mi.type); - continue; - } - DEBUG(log).print("rough_other mats %d is %s\n", mi.type, token.c_str()); - mPile->settings.gems.rough_other_mats.at(mi.type) = 1; - } - - // cut other - mPile->settings.gems.cut_other_mats.clear(); - mPile->settings.gems.cut_other_mats.resize(builtin_size, '\0'); - for (int i = 0; i < gems.cut_other_mats_size(); ++i) { - const std::string token = gems.cut_other_mats(i); - MaterialInfo mi; - mi.find(token); - if (!mi.isValid() || size_t(mi.type) >= builtin_size) { - WARN(log).print("invalid gem mat %s idx=%d\n", token.c_str(), mi.type); - continue; + auto & pgems = mPile->settings.gems; + read_category("gems", mode, + std::bind(&StockpileSettings::has_gems, mBuffer), + std::bind(&StockpileSettings::gems, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_gems, + [&]() { + pgems.cut_other_mats.clear(); + pgems.cut_mats.clear(); + pgems.rough_other_mats.clear(); + pgems.rough_mats.clear(); + }, + [&](bool all, char val) { + auto & bgems = mBuffer.gems(); + + unserialize_list_material(all, val, filter, gem_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bgems.rough_mats(idx); }, + bgems.rough_mats_size(),pgems.rough_mats); + + unserialize_list_material(all, val, filter, gem_cut_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bgems.cut_mats(idx); }, + bgems.cut_mats_size(), pgems.cut_mats); + + const size_t builtin_size = std::extentraws.mat_table.builtin)>::value; + pgems.rough_other_mats.resize(builtin_size, '\0'); + pgems.cut_other_mats.resize(builtin_size, '\0'); + if (all) { + for (size_t idx = 0; idx < builtin_size; ++idx) { + MaterialInfo mi; + mi.decode(0, idx); + set_filter_elem(filter, val, mi.toString(), idx, pgems.rough_other_mats.at(idx)); + set_filter_elem(filter, val, mi.toString(), idx, pgems.cut_other_mats.at(idx)); + } + return; + } else { + MaterialInfo mi; + for (size_t i = 0; i < builtin_size; ++i) { + string id = bgems.rough_other_mats(i); + if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) + set_filter_elem(filter, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); + id = bgems.cut_other_mats(i); + if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) + set_filter_elem(filter, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); + } } - DEBUG(log).print("cut_other mats %d is %s\n", mi.type, token.c_str()); - mPile->settings.gems.cut_other_mats.at(mi.type) = 1; - } - } - else { - mPile->settings.flags.bits.gems = 0; - mPile->settings.gems.cut_other_mats.clear(); - mPile->settings.gems.cut_mats.clear(); - mPile->settings.gems.rough_other_mats.clear(); - mPile->settings.gems.rough_mats.clear(); - } + }); } bool StockpileSerializer::write_leather(StockpileSettings::LeatherSet* leather) { return serialize_list_organic_mat( - [=](const std::string& id) { leather->add_mats(id); }, + [&](const std::string& id) { leather->add_mats(id); }, &mPile->settings.leather.mats, organic_mat_category::Leather); } void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_leather()) { - mPile->settings.flags.bits.leather = 1; - const StockpileSettings::LeatherSet leather = mBuffer.leather(); - DEBUG(log).print("leather:\n"); + auto & pleather = mPile->settings.leather; + read_category("leather", mode, + std::bind(&StockpileSettings::has_leather, mBuffer), + std::bind(&StockpileSettings::leather, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_leather, + [&]() { + pleather.mats.clear(); + }, + [&](bool all, char val) { + auto & bleather = mBuffer.leather(); - unserialize_list_organic_mat([=](size_t idx) -> std::string { return leather.mats(idx); }, - leather.mats_size(), &mPile->settings.leather.mats, organic_mat_category::Leather); - } - else { - mPile->settings.flags.bits.leather = 0; - mPile->settings.leather.mats.clear(); - } + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bleather.mats(idx); }, + bleather.mats_size(), pleather.mats, organic_mat_category::Leather); + }); } bool StockpileSerializer::write_corpses(StockpileSettings::CorpsesSet* corpses) { - bool all = true; - - return all; + return serialize_list_creature( + [&](const std::string& token) { corpses->add_corpses(token); }, + mPile->settings.corpses.corpses); } void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& filter) { - -} - -static bool refuse_creature_is_allowed(const df::creature_raw* raw) { - if (!raw) - return false; - // wagon and generated creatures not allowed, except angels - const bool is_wagon = raw->creature_id == "EQUIPMENT_WAGON"; - const bool is_generated = raw->flags.is_set(creature_raw_flags::GENERATED); - const bool is_angel = is_generated && raw->creature_id.find("DIVINE_") != std::string::npos; - return !is_wagon && !(is_generated && !is_angel); -} - -static bool refuse_write_helper(std::function add_value, const vector& list) { - bool all = true; - for (size_t i = 0; i < list.size(); ++i) { - if (!list.at(i)) { - all = false; - continue; - } - df::creature_raw* r = find_creature(i); - // skip forgotten beasts, titans, demons, and night creatures - if (!refuse_creature_is_allowed(r)) - continue; - DEBUG(log).print("creature %s %zd\n", r->creature_id.c_str(), i); - add_value(r->creature_id); - } - return all; + auto & pcorpses = mPile->settings.corpses; + read_category("corpses", mode, + std::bind(&StockpileSettings::has_corpses_v50, mBuffer), + std::bind(&StockpileSettings::corpses_v50, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_corpses, + [&]() { + pcorpses.corpses.clear(); + }, + [&](bool all, char val) { + auto & bcorpses = mBuffer.corpses_v50(); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bcorpses.corpses(idx); }, + bcorpses.corpses_size(), pcorpses.corpses); + }); } static bool refuse_type_is_allowed(item_type::item_type type) { - if (type == item_type::NONE || type == item_type::BAR || type == item_type::SMALLGEM || type == item_type::BLOCKS || type == item_type::ROUGH || type == item_type::BOULDER || type == item_type::CORPSE || type == item_type::CORPSEPIECE || type == item_type::ROCK || type == item_type::ORTHOPEDIC_CAST) + if (type == item_type::NONE || type == item_type::BAR || type == item_type::SMALLGEM + || type == item_type::BLOCKS || type == item_type::ROUGH || type == item_type::BOULDER + || type == item_type::CORPSE || type == item_type::CORPSEPIECE || type == item_type::ROCK + || type == item_type::ORTHOPEDIC_CAST) return false; return true; } @@ -1872,128 +1947,133 @@ bool StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { auto & prefuse = mPile->settings.refuse; bool all = prefuse.fresh_raw_hide && prefuse.rotten_raw_hide; - DEBUG(log).print("refuse:\n"); refuse->set_fresh_raw_hide(prefuse.fresh_raw_hide); refuse->set_rotten_raw_hide(prefuse.rotten_raw_hide); - DEBUG(log).print("getting types\n"); - all = serialize_list_item_type( - refuse_type_is_allowed, - [=](const std::string& token) { - DEBUG(log).print("adding type: %s\n", token.c_str()); - refuse->add_type(token); - }, + all = serialize_list_item_type(refuse_type_is_allowed, + [&](const std::string& token) { refuse->add_type(token); }, prefuse.type) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_corpses(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_corpses(token); }, prefuse.corpses) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_body_parts(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_body_parts(token); }, prefuse.body_parts) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_skulls(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_skulls(token); }, prefuse.skulls) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_bones(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_bones(token); }, prefuse.bones) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_hair(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_hair(token); }, prefuse.hair) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_shells(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_shells(token); }, prefuse.shells) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_teeth(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_teeth(token); }, prefuse.teeth) && all; - all = refuse_write_helper([=](const std::string& id) { refuse->add_horns(id); }, + all = serialize_list_creature( + [&](const std::string& token) { refuse->add_horns(token); }, prefuse.horns) && all; return all; } -static void refuse_read_helper(std::function get_value, size_t list_size, std::vector* pile_list) { - pile_list->clear(); - pile_list->resize(world->raws.creatures.all.size(), '\0'); - if (list_size > 0) { - for (size_t i = 0; i < list_size; ++i) { - const std::string creature_id = get_value(i); - const int idx = find_creature(creature_id); - const df::creature_raw* creature = find_creature(idx); - if (idx < 0 || !refuse_creature_is_allowed(creature) || size_t(idx) >= pile_list->size()) { - WARN(log).print("invalid refuse creature %s, idx=%d\n", creature_id.c_str(), idx); - continue; - } - DEBUG(log).print("creature %d is %s\n", idx, creature_id.c_str()); - pile_list->at(idx) = 1; - } - } -} - void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_refuse()) { - mPile->settings.flags.bits.refuse = 1; - const StockpileSettings::RefuseSet refuse = mBuffer.refuse(); - DEBUG(log).print("refuse:\n"); - DEBUG(log).print(" fresh hide %d\n", refuse.fresh_raw_hide()); - DEBUG(log).print(" rotten hide %d\n", refuse.rotten_raw_hide()); - mPile->settings.refuse.fresh_raw_hide = refuse.fresh_raw_hide(); - mPile->settings.refuse.rotten_raw_hide = refuse.rotten_raw_hide(); - - unserialize_list_item_type( - refuse_type_is_allowed, - [=](const size_t& idx) -> const std::string& { return refuse.type(idx); }, - refuse.type_size(), &mPile->settings.refuse.type); - - DEBUG(log).print(" corpses\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.corpses(idx); }, - refuse.corpses_size(), &mPile->settings.refuse.corpses); - - DEBUG(log).print(" body_parts\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.body_parts(idx); }, - refuse.body_parts_size(), &mPile->settings.refuse.body_parts); - - DEBUG(log).print(" skulls\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.skulls(idx); }, - refuse.skulls_size(), &mPile->settings.refuse.skulls); - - DEBUG(log).print(" bones\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.bones(idx); }, - refuse.bones_size(), &mPile->settings.refuse.bones); - - DEBUG(log).print(" hair\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.hair(idx); }, - refuse.hair_size(), &mPile->settings.refuse.hair); - - DEBUG(log).print(" shells\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.shells(idx); }, - refuse.shells_size(), &mPile->settings.refuse.shells); - - DEBUG(log).print(" teeth\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.teeth(idx); }, - refuse.teeth_size(), &mPile->settings.refuse.teeth); - - DEBUG(log).print(" horns\n"); - refuse_read_helper([=](const size_t& idx) -> const std::string& { return refuse.horns(idx); }, - refuse.horns_size(), &mPile->settings.refuse.horns); - } - else { - mPile->settings.flags.bits.refuse = 0; - mPile->settings.refuse.type.clear(); - mPile->settings.refuse.corpses.clear(); - mPile->settings.refuse.body_parts.clear(); - mPile->settings.refuse.skulls.clear(); - mPile->settings.refuse.bones.clear(); - mPile->settings.refuse.hair.clear(); - mPile->settings.refuse.shells.clear(); - mPile->settings.refuse.teeth.clear(); - mPile->settings.refuse.horns.clear(); - mPile->settings.refuse.fresh_raw_hide = false; - mPile->settings.refuse.rotten_raw_hide = false; - } + auto & prefuse = mPile->settings.refuse; + read_category("refuse", mode, + std::bind(&StockpileSettings::has_refuse, mBuffer), + std::bind(&StockpileSettings::refuse, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_refuse, + [&]() { + prefuse.fresh_raw_hide = false; + prefuse.rotten_raw_hide = false; + prefuse.type.clear(); + prefuse.corpses.clear(); + prefuse.body_parts.clear(); + prefuse.skulls.clear(); + prefuse.bones.clear(); + prefuse.hair.clear(); + prefuse.shells.clear(); + prefuse.teeth.clear(); + prefuse.horns.clear(); + }, + [&](bool all, char val) { + auto & brefuse = mBuffer.refuse(); + + set_elem(all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); + set_elem(all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); + + unserialize_list_item_type(all, val, filter, refuse_type_is_allowed, + [&](const size_t& idx) -> const string& { return brefuse.type(idx); }, + brefuse.type_size(), prefuse.type); + + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.corpses(idx); }, + brefuse.corpses_size(), prefuse.corpses); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.body_parts(idx); }, + brefuse.body_parts_size(), prefuse.body_parts); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.skulls(idx); }, + brefuse.skulls_size(), prefuse.skulls); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.bones(idx); }, + brefuse.bones_size(), prefuse.bones); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.hair(idx); }, + brefuse.hair_size(), prefuse.hair); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.shells(idx); }, + brefuse.shells_size(), prefuse.shells); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.teeth(idx); }, + brefuse.teeth_size(), prefuse.teeth); + unserialize_list_creature(all, val, filter, + [&](const size_t& idx) -> const string& { return brefuse.horns(idx); }, + brefuse.horns_size(), prefuse.horns); + }); + } bool StockpileSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { - bool all = true; + bool all = serialize_list_organic_mat( + [&](const std::string& token) { sheet->add_paper(token); }, + &mPile->settings.sheet.paper, organic_mat_category::Paper); + + all = serialize_list_organic_mat( + [&](const std::string& token) { sheet->add_parchment(token); }, + &mPile->settings.sheet.parchment, organic_mat_category::Parchment) && all; return all; } void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& filter) { + auto & psheet = mPile->settings.sheet; + read_category("sheet", mode, + std::bind(&StockpileSettings::has_sheet, mBuffer), + std::bind(&StockpileSettings::sheet, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_sheet, + [&]() { + psheet.paper.clear(); + psheet.parchment.clear(); + }, + [&](bool all, char val) { + auto & bsheet = mBuffer.sheet(); + + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bsheet.paper(idx); }, + bsheet.paper_size(), psheet.paper, organic_mat_category::Paper); + unserialize_list_organic_mat(all, val, filter, + [&](size_t idx) -> std::string { return bsheet.parchment(idx); }, + bsheet.parchment_size(), psheet.parchment, organic_mat_category::Parchment); + }); } static bool stone_is_allowed(const MaterialInfo& mi) { @@ -2007,25 +2087,27 @@ static bool stone_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_stone(StockpileSettings::StoneSet* stone) { return serialize_list_material( stone_is_allowed, - [=](const std::string& token) { stone->add_mats(token); }, + [&](const std::string& token) { stone->add_mats(token); }, mPile->settings.stone.mats); } void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_stone()) { - mPile->settings.flags.bits.stone = 1; - const StockpileSettings::StoneSet stone = mBuffer.stone(); - DEBUG(log).print("stone:\n"); + auto & pstone = mPile->settings.stone; + read_category("stone", mode, + std::bind(&StockpileSettings::has_stone, mBuffer), + std::bind(&StockpileSettings::stone, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_stone, + [&]() { + pstone.mats.clear(); + }, + [&](bool all, char val) { + auto & bstone = mBuffer.stone(); - unserialize_list_material( - stone_is_allowed, - [=](const size_t& idx) -> const std::string& { return stone.mats(idx); }, - stone.mats_size(), &mPile->settings.stone.mats); - } - else { - mPile->settings.flags.bits.stone = 0; - mPile->settings.stone.mats.clear(); - } + unserialize_list_material(all, val, filter, stone_is_allowed, + [&](const size_t& idx) -> const std::string& { return bstone.mats(idx); }, + bstone.mats_size(), pstone.mats); + }); } static bool weapons_mat_is_allowed(const MaterialInfo& mi) { @@ -2040,81 +2122,83 @@ bool StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) weapons->set_usable(pweapons.usable); all = serialize_list_itemdef( - [=](const std::string& token) { weapons->add_weapon_type(token); }, + [&](const std::string& token) { weapons->add_weapon_type(token); }, pweapons.weapon_type, std::vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), item_type::WEAPON) && all; all = serialize_list_itemdef( - [=](const std::string& token) { weapons->add_trapcomp_type(token); }, + [&](const std::string& token) { weapons->add_trapcomp_type(token); }, pweapons.trapcomp_type, std::vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), item_type::TRAPCOMP) && all; all = serialize_list_material( weapons_mat_is_allowed, - [=](const std::string& token) { weapons->add_mats(token); }, + [&](const std::string& token) { weapons->add_mats(token); }, pweapons.mats) && all; all = serialize_list_other_mats( mOtherMatsWeaponsArmor.mats, - [=](const std::string& token) { weapons->add_other_mats(token); }, + [&](const std::string& token) { weapons->add_other_mats(token); }, pweapons.other_mats) && all; all = serialize_list_quality( - [=](const std::string& token) { weapons->add_quality_core(token); }, + [&](const std::string& token) { weapons->add_quality_core(token); }, pweapons.quality_core) && all; all = serialize_list_quality( - [=](const std::string& token) { weapons->add_quality_total(token); }, + [&](const std::string& token) { weapons->add_quality_total(token); }, pweapons.quality_total) && all; return all; } void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_weapons()) { - mPile->settings.flags.bits.weapons = 1; - const StockpileSettings::WeaponsSet weapons = mBuffer.weapons(); - DEBUG(log).print("weapons: \n"); + auto & pweapons = mPile->settings.weapons; + read_category("weapons", mode, + std::bind(&StockpileSettings::has_weapons, mBuffer), + std::bind(&StockpileSettings::weapons, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_weapons, + [&]() { + pweapons.weapon_type.clear(); + pweapons.trapcomp_type.clear(); + pweapons.other_mats.clear(); + pweapons.mats.clear(); + quality_clear(pweapons.quality_core); + quality_clear(pweapons.quality_total); + }, + [&](bool all, char val) { + auto & bweapons = mBuffer.weapons(); - bool unusable = weapons.unusable(); - bool usable = weapons.usable(); - DEBUG(log).print("unusable %d\n", unusable); - DEBUG(log).print("usable %d\n", usable); - mPile->settings.weapons.unusable = unusable; - mPile->settings.weapons.usable = usable; + set_elem(all, val, bweapons.unusable(), pweapons.unusable); + set_elem(all, val, bweapons.usable(), pweapons.usable); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.weapon_type(idx); }, - weapons.weapon_type_size(), &mPile->settings.weapons.weapon_type, item_type::WEAPON); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bweapons.weapon_type(idx); }, + bweapons.weapon_type_size(), pweapons.weapon_type, item_type::WEAPON); - unserialize_list_itemdef([=](const size_t& idx) -> const std::string& { return weapons.trapcomp_type(idx); }, - weapons.trapcomp_type_size(), &mPile->settings.weapons.trapcomp_type, item_type::TRAPCOMP); + unserialize_list_itemdef(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bweapons.trapcomp_type(idx); }, + bweapons.trapcomp_type_size(), pweapons.trapcomp_type, item_type::TRAPCOMP); - unserialize_list_material( - weapons_mat_is_allowed, - [=](const size_t& idx) -> const std::string& { return weapons.mats(idx); }, - weapons.mats_size(), &mPile->settings.weapons.mats); + unserialize_list_material(all, val, filter, weapons_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bweapons.mats(idx); }, + bweapons.mats_size(), pweapons.mats); - unserialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [=](const size_t& idx) -> const std::string& { return weapons.other_mats(idx); }, - weapons.other_mats_size(), &mPile->settings.weapons.other_mats); + unserialize_list_other_mats(all, val, filter, mOtherMatsWeaponsArmor.mats, + [&](const size_t& idx) -> const std::string& { return bweapons.other_mats(idx); }, + bweapons.other_mats_size(), pweapons.other_mats); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_core(idx); }, - weapons.quality_core_size(), mPile->settings.weapons.quality_core); + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bweapons.quality_core(idx); }, + bweapons.quality_core_size(), pweapons.quality_core); - unserialize_list_quality([=](const size_t& idx) -> const std::string& { return weapons.quality_total(idx); }, - weapons.quality_total_size(), mPile->settings.weapons.quality_total); - } - else { - mPile->settings.flags.bits.weapons = 0; - mPile->settings.weapons.weapon_type.clear(); - mPile->settings.weapons.trapcomp_type.clear(); - mPile->settings.weapons.other_mats.clear(); - mPile->settings.weapons.mats.clear(); - quality_clear(mPile->settings.weapons.quality_core); - quality_clear(mPile->settings.weapons.quality_total); - } + unserialize_list_quality(all, val, filter, + [&](const size_t& idx) -> const std::string& { return bweapons.quality_total(idx); }, + bweapons.quality_total_size(), pweapons.quality_total); + }); } static bool wood_mat_is_allowed(const df::plant_raw* plant) { @@ -2138,26 +2222,36 @@ bool StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { } void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& filter) { - if (mBuffer.has_wood()) { - mPile->settings.flags.bits.wood = 1; - const StockpileSettings::WoodSet wood = mBuffer.wood(); - DEBUG(log).print("wood: \n"); - - mPile->settings.wood.mats.clear(); - mPile->settings.wood.mats.resize(world->raws.plants.all.size(), '\0'); - for (int i = 0; i < wood.mats_size(); ++i) { - const std::string token = wood.mats(i); - const size_t idx = find_plant(token); - if (idx < 0 || idx >= mPile->settings.wood.mats.size()) { - WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); - continue; + auto & pwood = mPile->settings.wood; + read_category("wood", mode, + std::bind(&StockpileSettings::has_wood, mBuffer), + std::bind(&StockpileSettings::wood, mBuffer), + mPile->settings.flags.whole, + mPile->settings.flags.mask_wood, + [&]() { + pwood.mats.clear(); + }, + [&](bool all, char val) { + auto & bwood = mBuffer.wood(); + + size_t num_elems = world->raws.plants.all.size(); + pwood.mats.resize(num_elems, '\0'); + + if (all) { + for (size_t idx = 0; idx < num_elems; ++idx) { + string id = world->raws.plants.all[idx]->id; + set_filter_elem(filter, val, id, idx, pwood.mats.at(idx)); + } + } else { + for (int i = 0; i < bwood.mats_size(); ++i) { + const std::string token = bwood.mats(i); + const size_t idx = find_plant(token); + if (idx < 0 || (size_t)idx >= num_elems) { + WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); + continue; + } + set_filter_elem(filter, val, token, idx, pwood.mats.at(idx)); + } } - DEBUG(log).print("plant %zd is %s\n", idx, token.c_str()); - mPile->settings.wood.mats.at(idx) = 1; - } - } - else { - mPile->settings.flags.bits.wood = 0; - mPile->settings.wood.mats.clear(); - } + }); } diff --git a/plugins/stockpiles/proto/stockpiles.proto b/plugins/stockpiles/proto/stockpiles.proto index 66815335b..681e7d927 100644 --- a/plugins/stockpiles/proto/stockpiles.proto +++ b/plugins/stockpiles/proto/stockpiles.proto @@ -148,9 +148,12 @@ message StockpileSettings { } message CorpsesSet { optional bool all = 1; + repeated string corpses = 2; } message SheetSet { optional bool all = 1; + repeated string paper = 2; + repeated string parchment = 3; } // general settings From f13df882b6f06978cd9c8c05b29ab3ecd1269fe0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 15:41:34 -0700 Subject: [PATCH 0863/2222] properly filter when setting gems --- plugins/stockpiles/StockpileSerializer.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 1ce6b65b1..04b046a3e 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1866,9 +1866,13 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil if (all) { for (size_t idx = 0; idx < builtin_size; ++idx) { MaterialInfo mi; - mi.decode(0, idx); - set_filter_elem(filter, val, mi.toString(), idx, pgems.rough_other_mats.at(idx)); - set_filter_elem(filter, val, mi.toString(), idx, pgems.cut_other_mats.at(idx)); + mi.decode(idx, -1); + if (gem_other_mat_is_allowed(mi)) + set_filter_elem(filter, val, mi.getToken(), idx, pgems.rough_other_mats.at(idx)); + if (!mi.isValid()) + mi.decode(0, idx); + if (gem_other_mat_is_allowed(mi)) + set_filter_elem(filter, val, mi.getToken(), idx, pgems.cut_other_mats.at(idx)); } return; } else { From 284d80b0765fd8937bb746564b4137f3e54c93f7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 15:45:07 -0700 Subject: [PATCH 0864/2222] rename some leftover categories --- ...iege_ammo.dfstock => category_furniture.dfstock} | Bin ...tegory_sheet.dfstock => category_sheets.dfstock} | Bin ..._components.dfstock => category_weapons.dfstock} | Bin .../{woodprefix.dfstock => category_wood.dfstock} | Bin 4 files changed, 0 insertions(+), 0 deletions(-) rename data/stockpiles/{category_furniture_siege_ammo.dfstock => category_furniture.dfstock} (100%) rename data/stockpiles/{category_sheet.dfstock => category_sheets.dfstock} (100%) rename data/stockpiles/{category_weapons_trap_components.dfstock => category_weapons.dfstock} (100%) rename data/stockpiles/{woodprefix.dfstock => category_wood.dfstock} (100%) diff --git a/data/stockpiles/category_furniture_siege_ammo.dfstock b/data/stockpiles/category_furniture.dfstock similarity index 100% rename from data/stockpiles/category_furniture_siege_ammo.dfstock rename to data/stockpiles/category_furniture.dfstock diff --git a/data/stockpiles/category_sheet.dfstock b/data/stockpiles/category_sheets.dfstock similarity index 100% rename from data/stockpiles/category_sheet.dfstock rename to data/stockpiles/category_sheets.dfstock diff --git a/data/stockpiles/category_weapons_trap_components.dfstock b/data/stockpiles/category_weapons.dfstock similarity index 100% rename from data/stockpiles/category_weapons_trap_components.dfstock rename to data/stockpiles/category_weapons.dfstock diff --git a/data/stockpiles/woodprefix.dfstock b/data/stockpiles/category_wood.dfstock similarity index 100% rename from data/stockpiles/woodprefix.dfstock rename to data/stockpiles/category_wood.dfstock From c2a7de232e15edd1c83bd1dfd39f7a73d1ba1acd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 15:45:29 -0700 Subject: [PATCH 0865/2222] use new "all" format categories --- data/stockpiles/category_ammo.dfstock | Bin 1015 -> 4 bytes data/stockpiles/category_animals.dfstock | 71 +- data/stockpiles/category_armor.dfstock | Bin 2040 -> 5 bytes data/stockpiles/category_bars_blocks.dfstock | Bin 3450 -> 4 bytes data/stockpiles/category_cloth.dfstock | 142 +- data/stockpiles/category_coins.dfstock | Bin 7467 -> 4 bytes data/stockpiles/category_corpses.dfstock | Bin 21 -> 5 bytes .../category_finished_goods.dfstock | Bin 5995 -> 4 bytes data/stockpiles/category_food.dfstock | 18620 +--------------- data/stockpiles/category_furniture.dfstock | Bin 3259 -> 4 bytes data/stockpiles/category_gems.dfstock | Bin 7728 -> 4 bytes data/stockpiles/category_leather.dfstock | 1364 +- data/stockpiles/category_refuse.dfstock | 584 +- data/stockpiles/category_sheets.dfstock | Bin 21 -> 5 bytes data/stockpiles/category_stone.dfstock | Bin 1832 -> 4 bytes data/stockpiles/category_weapons.dfstock | Bin 3838 -> 5 bytes data/stockpiles/category_wood.dfstock | Bin 674 -> 4 bytes 17 files changed, 5 insertions(+), 20776 deletions(-) diff --git a/data/stockpiles/category_ammo.dfstock b/data/stockpiles/category_ammo.dfstock index 744b76cb4ad5dddc46b0881fb390fbf943f0d263..55a20e7e65d1873e449810e03d7e7ce96f9141dd 100644 GIT binary patch literal 4 LcmZ=_GGGJ%0doL$ literal 1015 zcmZvZK~LK-6vt}`iLQukbS-o_=9pWB5Q3YXxUI!YT-k0TxI_`8QlzM$ES1K$*l|aW z969c|Z?dm4Q)umy-0Yv9f6xDC@^8P>!(qrTnWACxx=IKKB|H5O6br#)*%^Gm1b7@^ zp=5V2<~-=`5gt+JGmE$wY|9OceESa3 zU2drJ#dwLpQrbW&svTWZF=gXq{W_ZgRxFx@b{z%kE1}t;xvPtuzS&M`tf)vvOlLo- zVI|lIsMD(*0KT?E0mXsc7{e@FKgLqNjUuT}jW7T`>F=hA4lIC0<4y6gIU<1ltK@xX z-wYRA(q#QZM+Oom!7vT++{016%!|$d{W2}wPht9_%F4y6aND@7@5gU(KAMGYy5@KZ-kyu P>lqDRQ0otB{Y9<+l)4>` diff --git a/data/stockpiles/category_animals.dfstock b/data/stockpiles/category_animals.dfstock index e09193a2f..9796e4535 100644 --- a/data/stockpiles/category_animals.dfstock +++ b/data/stockpiles/category_animals.dfstock @@ -1,71 +1,2 @@ -™÷TOADTOAD_MAN -GIANT_TOADWORMWORM_MAN BIRD_BLUEJAY BLUEJAY_MAN GIANT_BLUEJAY BIRD_CARDINAL CARDINAL_MANGIANT_CARDINAL BIRD_GRACKLE GRACKLE_MAN GIANT_GRACKLE BIRD_ORIOLE -ORIOLE_MAN GIANT_ORIOLEBIRD_RW_BLACKBIRDRW_BLACKBIRD_MANGIANT_RW_BLACKBIRD BIRD_PENGUINBIRD_PENGUIN_LITTLEBIRD_PENGUIN_EMPEROR PENGUIN MANBIRD_PENGUIN_GIANTBIRD_FALCON_PEREGRINEPEREGRINE FALCON MANGIANT PEREGRINE FALCON BIRD_KIWIKIWI MANBIRD_KIWI_GIANT BIRD_OSTRICH OSTRICH MANBIRD_OSTRICH_GIANT BIRD_CROWCROW_MAN -GIANT_CROW -BIRD_RAVEN RAVEN_MAN GIANT_RAVENBIRD_CASSOWARY CASSOWARY_MANGIANT_CASSOWARYBIRD_KEAKEA_MAN GIANT_KEABIRD_OWL_SNOWY SNOWY_OWL_MANGIANT_SNOWY_OWLSPARROW SPARROW_MAN GIANT_SPARROWBIRD_STORK_WHITEWHITE_STORK_MANGIANT_WHITE_STORK BIRD_LOONLOON_MAN -GIANT_LOON BIRD_OWL_BARN BARN_OWL_MANGIANT_BARN_OWL BIRD_PARAKEET PARAKEET_MANGIANT_PARAKEET BIRD_KAKAPO -KAKAPO_MAN GIANT_KAKAPOBIRD_PARROT_GREYGREY_PARROT_MANGIANT_GREY_PARROT BIRD_PUFFIN -PUFFIN_MAN GIANT_PUFFIN BIRD_SWANSWAN_MAN -GIANT_SWAN BIRD_LORIKEET LORIKEET_MANGIANT_LORIKEET BIRD_WRENWREN_MAN -GIANT_WREN BIRD_OSPREY -OSPREY_MAN GIANT_OSPREYBIRD_EMUEMU_MAN GIANT_EMUBIRD_COCKATIEL COCKATIEL_MANGIANT_COCKATIELBIRD_LOVEBIRD_PEACH-FACEDPEACH-FACED_LOVEBIRD_MANGIANT_PEACH-FACED_LOVEBIRD BIRD_MAGPIE -MAGPIE_MAN GIANT_MAGPIE BIRD_KESTREL KESTREL_MAN GIANT_KESTRELBIRD_ALBATROSS ALBATROSS_MANGIANT_ALBATROSSBIRD_OWL_GREAT_HORNEDGREAT_HORNED_OWL_MANGIANT_GREAT_HORNED_OWL -BIRD_EAGLE EAGLE_MAN GIANT_EAGLE BIRD_HORNBILL HORNBILL_MANGIANT_HORNBILLBIRD_LOVEBIRD_MASKEDMASKED_LOVEBIRD_MANGIANT_MASKED_LOVEBIRD BIRD_BUSHTIT BUSHTIT_MAN GIANT_BUSHTIT DAMSELFLY DAMSELFLY_MANGIANT_DAMSELFLYMOTHMOTH_MAN -GIANT_MOTH GRASSHOPPERGRASSHOPPER_MANGIANT_GRASSHOPPER BARK_SCORPIONBARK_SCORPION_MANGIANT_BARK_SCORPIONMANTIS -MANTIS_MAN GIANT_MANTISTICKTICK_MAN -GIANT_TICKLOUSE LOUSE_MAN GIANT_LOUSETHRIPS -THRIPS_MAN GIANT_THRIPSSLUGSLUG_MAN -GIANT_SLUGMOSQUITO MOSQUITO_MANGIANT_MOSQUITOSPIDER_JUMPINGJUMPING_SPIDER_MANGIANT_JUMPING_SPIDERTERMITE -MOON_SNAILMOON_SNAIL_MANGIANT_MOON_SNAILSPIDER_BROWN_RECLUSEBROWN_RECLUSE_SPIDER_MANGIANT_BROWN_RECLUSE_SPIDERSNAIL SNAIL_MAN GIANT_SNAIL GECKO_LEOPARDLEOPARD_GECKO_MANGIANT_LEOPARD_GECKODESERT TORTOISEDESERT_TORTOISE_MANGIANT_DESERT_TORTOISE GILA_MONSTERGILA_MONSTER_MANGIANT_GILA_MONSTERDOGCATMULEDONKEYHORSECOWSHEEPPIGGOAT BIRD_CHICKENCAVY BIRD_DUCK WATER_BUFFALOREINDEER -BIRD_GOOSEYAKLLAMAALPACABIRD_GUINEAFOWLBIRD_PEAFOWL_BLUE BIRD_TURKEYRABBITEQUIPMENT_WAGONCHIMERACENTAURGRIFFONFLYFLY_MAN GIANT_FLY ROACH_LARGE ROACH_MAN GIANT_ROACHBEETLE -BEETLE_MAN GIANT_BEETLEANTBUTTERFLY_MONARCHBUTTERFLY_MONARCH_MANGIANT_BUTTERFLY_MONARCHFIREFLY FIREFLY_MAN GIANT_FIREFLY DRAGONFLY DRAGONFLY_MANGIANT_DRAGONFLY HONEY_BEE BUMBLEBEE GOAT_MOUNTAINGOAT_MOUNTAIN_MANGIANT_GOAT_MOUNTAIN MARMOT_HOARYMARMOT_HOARY_MANGIANT_MARMOT_HOARYGNOME_MOUNTAIN -GNOME_DARKWALRUS -WALRUS_MAN GIANT_WALRUSFISH_LAMPREY_SEASHARK_GREAT_WHITE SHARK_FRILLSHARK_SPINY_DOGFISHSHARK_WOBBEGONG_SPOTTED SHARK_WHALE SHARK_BASKING SHARK_NURSESHARK_MAKO_SHORTFINSHARK_MAKO_LONGFIN SHARK_TIGER -SHARK_BULLSHARK_REEF_BLACKTIPSHARK_REEF_WHITETIP -SHARK_BLUESHARK_HAMMERHEAD SHARK_ANGELFISH_SKATE_COMMONFISH_RAY_MANTA FISH_STINGRAYFISH_COELACANTH FISH_STURGEONFISH_CONGER_EEL FISH_MILKFISHFISH_COD FISH_OPAHFISH_GROUPER_GIANT FISH_BLUEFISHFISH_SUNFISH_OCEANFISH_SWORDFISH FISH_MARLIN FISH_HALIBUTFISH_BARRACUDA_GREATFISH_TUNA_BLUEFINNARWHAL NARWHAL MANNARWHAL, GIANTHIPPO HIPPO_MAN GIANT_HIPPOFISH_GAR_LONGNOSE FISH_CARPFISH_TIGERFISH FISH_PIKEPLATYPUS PLATYPUS MANPLATYPUS, GIANT BEAR_GRIZZLYBEAR_GRIZZLY_MANGIANT_BEAR_GRIZZLY -BEAR_BLACKBEAR_BLACK_MANGIANT_BEAR_BLACKDEERDEER_MAN -GIANT_DEERFOXFOX_MAN GIANT_FOXRACCOON RACCOON_MAN GIANT_RACCOONMACAQUE_RHESUSMACAQUE_RHESUS_MANGIANT_MACAQUE_RHESUSCOUGAR -COUGAR_MAN GIANT_COUGARWOLFWOLF_MAN -GIANT_WOLF GROUNDHOG GROUNDHOG_MANGIANT_GROUNDHOG ALLIGATOR ALLIGATOR_MANGIANT_ALLIGATOR BIRD_BUZZARD BUZZARD_MAN GIANT_BUZZARDPANDAPANDA, GIGANTIC PANDA MANCAPYBARACAPYBARA, GIANT CAPYBARA MANBADGER -BADGER MAN BADGER, GIANTMOOSE MOOSE MAN MOOSE, GIANT RED PANDA RED PANDA MANRED PANDA, GIANTELEPHANT ELEPHANT_MANGIANT_ELEPHANTWARTHOG WARTHOG_MAN GIANT_WARTHOGLIONLION_MAN -GIANT_LIONLEOPARD LEOPARD_MAN GIANT_LEOPARDJAGUAR -JAGUAR_MAN GIANT_JAGUARTIGER TIGER_MAN GIANT_TIGERCHEETAH CHEETAH_MAN GIANT_CHEETAHGAZELLE GAZELLE_MAN GIANT_GAZELLEMANDRILL MANDRILL_MANGIANT_MANDRILL -CHIMPANZEEBONOBOGORILLA ORANGUTANGIBBON_SIAMANGGIBBON_WHITE_HANDEDGIBBON_BLACK_HANDED GIBBON_GRAYGIBBON_SILVERYGIBBON_PILEATED GIBBON_BILOUGIBBON_WHITE_BROWEDGIBBON_BLACK_CRESTED CAMEL_1_HUMPCAMEL_1_HUMP_MANGIANT_CAMEL_1_HUMP CAMEL_2_HUMPCAMEL_2_HUMP_MANGIANT_CAMEL_2_HUMPCROCODILE_SALTWATERCROCODILE_SALTWATER_MANGIANT_CROCODILE_SALTWATER BIRD_VULTURE VULTURE_MAN GIANT_VULTURE -RHINOCEROSRHINOCEROS_MANGIANT_RHINOCEROSGIRAFFE GIRAFFE_MAN GIANT_GIRAFFE HONEY BADGERHONEY BADGER MANHONEY BADGER, GIANTGIANT TORTOISEGIANT TORTOISE MANGIGANTIC TORTOISE ARMADILLO ARMADILLO MANARMADILLO, GIANTMUSKOX -MUSKOX_MAN GIANT_MUSKOXELKELK_MAN GIANT_ELK -BEAR_POLARBEAR_POLAR_MANGIANT_BEAR_POLAR WOLVERINE WOLVERINE_MANGIANT_WOLVERINE -CHINCHILLACHINCHILLA_MANGIANT_CHINCHILLA FLOATING_GUTSDRUNIAN CREEPING_EYEVORACIOUS_CAVE_CRAWLERBLIND_CAVE_OGRE -CAP_HOPPER -MAGMA_CRABCRUNDLE HUNGRY_HEAD -FLESH_BALLELK_BIRD HELMET_SNAKEGREEN_DEVOURERRUTHERERCREEPY_CRAWLERDRALTHAGIANT_EARTHWORM BLOOD_MANBUGBATMANERA -MOLEMARIANJABBERER POND_GRABBERBLIND_CAVE_BEAR CAVE_DRAGONREACHERELEMENTMAN_GABBROGORLAK CAVE_FLOATERPLUMP_HELMET_MAN CAVE_BLOBELEMENTMAN_AMETHYSTOCTOPUS OCTOPUS_MAN GIANT_OCTOPUSCRABCRAB_MAN -GIANT_CRAB LEOPARD_SEALLEOPARD_SEAL_MANGIANT_LEOPARD_SEAL -CUTTLEFISHCUTTLEFISH_MANGIANT_CUTTLEFISHORCAORCA_MAN -GIANT_ORCASPONGE -SPONGE_MAN GIANT_SPONGEHORSESHOE_CRABHORSESHOE_CRAB_MANGIANT_HORSESHOE_CRAB SPERM_WHALESPERM_WHALE_MANGIANT_SPERM_WHALE ELEPHANT_SEALELEPHANT_SEAL_MANGIANT_ELEPHANT_SEAL HARP_SEAL HARP_SEAL_MANGIANT_HARP_SEALNAUTILUS NAUTILUS_MANGIANT_NAUTILUS FOXSQUIRREL MOGHOPPER RAT_DEMONWAMBLER_FLUFFYLIZARD_RHINO_TWO_LEGGED WORM_KNUCKLESPIDER_PHANTOM FLY_ACORN -GNAT_BLOODLIZARD -LIZARD_MAN GIANT_LIZARDSKINK SKINK_MAN GIANT_SKINK CHAMELEON CHAMELEON_MANGIANT_CHAMELEONANOLE ANOLE_MAN GIANT_ANOLEIGUANA -IGUANA_MAN GIANT_IGUANA RIVER OTTER SEA OTTER OTTER_MAN GIANT_OTTERSNAPPING TURTLEALLIGATOR SNAPPING TURTLESNAPPING_TURTLE_MANGIANT_SNAPPING_TURTLEBEAVER -BEAVER_MAN GIANT_BEAVERLEECH LEECH_MAN GIANT_LEECHAXOLOTL AXOLOTL_MAN GIANT_AXOLOTLMINKMINK_MAN -GIANT_MINK POND_TURTLEPOND_TURTLE_MANGIANT_POND_TURTLERATRAT_MAN SQUIRREL_GRAYSQUIRREL_GRAY_MANGIANT_SQUIRREL_GRAY SQUIRREL_REDSQUIRREL_RED_MANGIANT_SQUIRREL_REDCHIPMUNK CHIPMUNK_MANGIANT_CHIPMUNKHAMSTER HAMSTER_MAN GIANT_HAMSTERHEDGEHOG HEDGEHOG_MANGIANT_HEDGEHOGSQUIRREL_FLYINGFLYING_SQUIRREL_MANGIANT_FLYING_SQUIRRELMUSSELOYSTER FISH_SALMONFISH_CLOWNFISH FISH_HAGFISHFISH_LAMPREY_BROOK FISH_RAY_BATFISH_RAY_THORNBACKFISH_RATFISH_SPOTTED FISH_HERRING FISH_SHAD FISH_ANCHOVYFISH_TROUT_STEELHEAD FISH_HAKE FISH_SEAHORSE FISH_GLASSEYEFISH_PUFFER_WHITE_SPOTTED FISH_SOLE FISH_FLOUNDER FISH_MACKERELJELLYFISH_SEA_NETTLESQUID SQUID MANGIGANTIC SQUID FISH_LUNGFISHFISH_LOACH_CLOWNFISH_BULLHEAD_BROWNFISH_BULLHEAD_YELLOWFISH_BULLHEAD_BLACKFISH_KNIFEFISH_BANDED FISH_CHARFISH_TROUT_RAINBOWFISH_MOLLY_SAILFIN -FISH_GUPPY -FISH_PERCHDWARFHUMANELFGOBLINKOBOLDGREMLINTROLLOGREUNICORNDRAGONSATYRCOLOSSUS_BRONZEGIANTCYCLOPSETTINMINOTAURYETI SASQUATCH BLIZZARD_MANWOLF_ICEFAIRYPIXIEBEAK_DOG GRIMELING BLENDEC_FOUL STRANGLER NIGHTWINGHARPYHYDRA MERPERSON SEA_SERPENT SEA_MONSTERBIRD_ROCCROCODILE_CAVETOAD_GIANT_CAVE OLM_GIANT BAT_GIANT RAT_GIANT RAT_LARGEMOLE_DOG_NAKED -TROGLODYTE -MOLE_GIANTIMP_FIRESPIDER_CAVE_GIANT SPIDER_CAVE FISH_CAVE CAVE_FISH_MAN LOBSTER_CAVE -SNAKE_FIREOLMOLM_MANBATBAT_MANMAGGOT_PURRINGELEMENTMAN_FIREELEMENTMAN_MAGMAELEMENTMAN_IRONELEMENTMAN_MUDBIRD_SWALLOW_CAVECAVE_SWALLOW_MANBIRD_SWALLOW_CAVE_GIANT AMPHIBIAN_MAN REPTILE_MAN SERPENT_MANANT_MAN -RODENT MAN WILD_BOAR WILD_BOAR_MANGIANT_WILD_BOARCOYOTE -COYOTE_MAN GIANT_COYOTEKANGAROO KANGAROO_MANGIANT_KANGAROOKOALA KOALA_MAN GIANT_KOALAADDER ADDER_MAN GIANT_ADDERECHIDNA ECHIDNA_MAN GIANT_ECHIDNA PORCUPINE PORCUPINE_MANGIANT_PORCUPINE KINGSNAKE KINGSNAKE_MANGIANT_KINGSNAKE GRAY_LANGURGRAY_LANGUR_MANGIANT_GRAY_LANGURBOBCAT -BOBCAT_MAN GIANT_BOBCATSKUNK SKUNK_MAN GIANT_SKUNKGREEN_TREE_FROGGREEN_TREE_FROG_MANGIANT_GREEN_TREE_FROGHAREHARE_MAN -GIANT_HARE RATTLESNAKERATTLESNAKE_MANGIANT_RATTLESNAKEWEASEL -WEASEL_MAN GIANT_WEASELCOPPERHEAD_SNAKECOPPERHEAD_SNAKE_MANGIANT_COPPERHEAD_SNAKEIBEXIBEX_MAN -GIANT_IBEXWOMBAT -WOMBAT_MAN GIANT_WOMBATDINGO DINGO_MAN GIANT_DINGOCOATI COATI_MAN GIANT_COATIOPOSSUM OPOSSUM_MAN GIANT_OPOSSUMMONGOOSE MONGOOSE_MANGIANT_MONGOOSEHYENA HYENA_MAN GIANT_HYENAANACONDA ANACONDA_MANGIANT_ANACONDAMONITOR_LIZARDMONITOR_LIZARD_MANGIANT_MONITOR_LIZARD -KING_COBRAKING_COBRA_MANGIANT_KING_COBRAOCELOT -OCELOT_MAN GIANT_OCELOTJACKAL -JACKAL_MAN GIANT_JACKALCAPUCHIN CAPUCHIN_MANGIANT_CAPUCHINSLOTH SLOTH_MAN GIANT_SLOTH SPIDER_MONKEYSPIDER_MONKEY_MANGIANT_SPIDER_MONKEYPANGOLIN PANGOLIN_MANGIANT_PANGOLIN BLACK_MAMBABLACK_MAMBA_MANGIANT_BLACK_MAMBA -BEAR_SLOTHSLOTH_BEAR_MANGIANT_SLOTH_BEARAYE-AYE AYE-AYE_MAN GIANT_AYE-AYE -BUSHMASTERBUSHMASTER_MANGIANT_BUSHMASTERPYTHON -PYTHON_MAN GIANT_PYTHONTAPIR TAPIR_MAN GIANT_TAPIRIMPALA -IMPALA_MAN GIANT_IMPALAAARDVARK AARDVARK_MANGIANT_AARDVARK LION_TAMARINLION_TAMARIN_MANGIANT_LION_TAMARINSTOAT STOAT_MAN GIANT_STOATLYNXLYNX_MAN -GIANT_LYNXGNOLLNAGAFORGOTTEN_BEAST_1FORGOTTEN_BEAST_2FORGOTTEN_BEAST_3FORGOTTEN_BEAST_4FORGOTTEN_BEAST_5FORGOTTEN_BEAST_6FORGOTTEN_BEAST_7FORGOTTEN_BEAST_8FORGOTTEN_BEAST_9FORGOTTEN_BEAST_10FORGOTTEN_BEAST_11FORGOTTEN_BEAST_12FORGOTTEN_BEAST_13FORGOTTEN_BEAST_14FORGOTTEN_BEAST_15FORGOTTEN_BEAST_16FORGOTTEN_BEAST_17FORGOTTEN_BEAST_18FORGOTTEN_BEAST_19FORGOTTEN_BEAST_20FORGOTTEN_BEAST_21FORGOTTEN_BEAST_22FORGOTTEN_BEAST_23FORGOTTEN_BEAST_24FORGOTTEN_BEAST_25FORGOTTEN_BEAST_26FORGOTTEN_BEAST_27FORGOTTEN_BEAST_28FORGOTTEN_BEAST_29FORGOTTEN_BEAST_30FORGOTTEN_BEAST_31FORGOTTEN_BEAST_32FORGOTTEN_BEAST_33FORGOTTEN_BEAST_34FORGOTTEN_BEAST_35FORGOTTEN_BEAST_36FORGOTTEN_BEAST_37FORGOTTEN_BEAST_38FORGOTTEN_BEAST_39FORGOTTEN_BEAST_40FORGOTTEN_BEAST_41FORGOTTEN_BEAST_42FORGOTTEN_BEAST_43FORGOTTEN_BEAST_44FORGOTTEN_BEAST_45FORGOTTEN_BEAST_46FORGOTTEN_BEAST_47FORGOTTEN_BEAST_48FORGOTTEN_BEAST_49FORGOTTEN_BEAST_50FORGOTTEN_BEAST_51FORGOTTEN_BEAST_52FORGOTTEN_BEAST_53FORGOTTEN_BEAST_54FORGOTTEN_BEAST_55FORGOTTEN_BEAST_56FORGOTTEN_BEAST_57FORGOTTEN_BEAST_58FORGOTTEN_BEAST_59FORGOTTEN_BEAST_60FORGOTTEN_BEAST_61FORGOTTEN_BEAST_62FORGOTTEN_BEAST_63FORGOTTEN_BEAST_64FORGOTTEN_BEAST_65FORGOTTEN_BEAST_66FORGOTTEN_BEAST_67FORGOTTEN_BEAST_68FORGOTTEN_BEAST_69FORGOTTEN_BEAST_70FORGOTTEN_BEAST_71FORGOTTEN_BEAST_72FORGOTTEN_BEAST_73FORGOTTEN_BEAST_74FORGOTTEN_BEAST_75FORGOTTEN_BEAST_76FORGOTTEN_BEAST_77FORGOTTEN_BEAST_78FORGOTTEN_BEAST_79FORGOTTEN_BEAST_80FORGOTTEN_BEAST_81FORGOTTEN_BEAST_82FORGOTTEN_BEAST_83FORGOTTEN_BEAST_84FORGOTTEN_BEAST_85FORGOTTEN_BEAST_86FORGOTTEN_BEAST_87FORGOTTEN_BEAST_88FORGOTTEN_BEAST_89FORGOTTEN_BEAST_90FORGOTTEN_BEAST_91FORGOTTEN_BEAST_92FORGOTTEN_BEAST_93FORGOTTEN_BEAST_94FORGOTTEN_BEAST_95FORGOTTEN_BEAST_96FORGOTTEN_BEAST_97FORGOTTEN_BEAST_98FORGOTTEN_BEAST_99FORGOTTEN_BEAST_100FORGOTTEN_BEAST_101FORGOTTEN_BEAST_102FORGOTTEN_BEAST_103FORGOTTEN_BEAST_104FORGOTTEN_BEAST_105FORGOTTEN_BEAST_106FORGOTTEN_BEAST_107FORGOTTEN_BEAST_108FORGOTTEN_BEAST_109FORGOTTEN_BEAST_110FORGOTTEN_BEAST_111FORGOTTEN_BEAST_112FORGOTTEN_BEAST_113FORGOTTEN_BEAST_114FORGOTTEN_BEAST_115FORGOTTEN_BEAST_116FORGOTTEN_BEAST_117FORGOTTEN_BEAST_118FORGOTTEN_BEAST_119FORGOTTEN_BEAST_120FORGOTTEN_BEAST_121FORGOTTEN_BEAST_122FORGOTTEN_BEAST_123FORGOTTEN_BEAST_124FORGOTTEN_BEAST_125FORGOTTEN_BEAST_126FORGOTTEN_BEAST_127FORGOTTEN_BEAST_128FORGOTTEN_BEAST_129FORGOTTEN_BEAST_130FORGOTTEN_BEAST_131FORGOTTEN_BEAST_132FORGOTTEN_BEAST_133FORGOTTEN_BEAST_134FORGOTTEN_BEAST_135FORGOTTEN_BEAST_136FORGOTTEN_BEAST_137FORGOTTEN_BEAST_138FORGOTTEN_BEAST_139FORGOTTEN_BEAST_140FORGOTTEN_BEAST_141FORGOTTEN_BEAST_142FORGOTTEN_BEAST_143FORGOTTEN_BEAST_144FORGOTTEN_BEAST_145FORGOTTEN_BEAST_146FORGOTTEN_BEAST_147FORGOTTEN_BEAST_148FORGOTTEN_BEAST_149FORGOTTEN_BEAST_150FORGOTTEN_BEAST_151FORGOTTEN_BEAST_152FORGOTTEN_BEAST_153FORGOTTEN_BEAST_154FORGOTTEN_BEAST_155FORGOTTEN_BEAST_156FORGOTTEN_BEAST_157FORGOTTEN_BEAST_158FORGOTTEN_BEAST_159FORGOTTEN_BEAST_160FORGOTTEN_BEAST_161FORGOTTEN_BEAST_162FORGOTTEN_BEAST_163FORGOTTEN_BEAST_164FORGOTTEN_BEAST_165FORGOTTEN_BEAST_166FORGOTTEN_BEAST_167FORGOTTEN_BEAST_168FORGOTTEN_BEAST_169FORGOTTEN_BEAST_170FORGOTTEN_BEAST_171FORGOTTEN_BEAST_172FORGOTTEN_BEAST_173FORGOTTEN_BEAST_174FORGOTTEN_BEAST_175FORGOTTEN_BEAST_176FORGOTTEN_BEAST_177FORGOTTEN_BEAST_178FORGOTTEN_BEAST_179FORGOTTEN_BEAST_180FORGOTTEN_BEAST_181FORGOTTEN_BEAST_182FORGOTTEN_BEAST_183FORGOTTEN_BEAST_184FORGOTTEN_BEAST_185FORGOTTEN_BEAST_186FORGOTTEN_BEAST_187FORGOTTEN_BEAST_188FORGOTTEN_BEAST_189FORGOTTEN_BEAST_190FORGOTTEN_BEAST_191FORGOTTEN_BEAST_192FORGOTTEN_BEAST_193FORGOTTEN_BEAST_194FORGOTTEN_BEAST_195FORGOTTEN_BEAST_196FORGOTTEN_BEAST_197FORGOTTEN_BEAST_198FORGOTTEN_BEAST_199FORGOTTEN_BEAST_200FORGOTTEN_BEAST_201FORGOTTEN_BEAST_202FORGOTTEN_BEAST_203FORGOTTEN_BEAST_204FORGOTTEN_BEAST_205FORGOTTEN_BEAST_206FORGOTTEN_BEAST_207FORGOTTEN_BEAST_208FORGOTTEN_BEAST_209FORGOTTEN_BEAST_210FORGOTTEN_BEAST_211FORGOTTEN_BEAST_212FORGOTTEN_BEAST_213FORGOTTEN_BEAST_214FORGOTTEN_BEAST_215FORGOTTEN_BEAST_216FORGOTTEN_BEAST_217FORGOTTEN_BEAST_218FORGOTTEN_BEAST_219FORGOTTEN_BEAST_220FORGOTTEN_BEAST_221FORGOTTEN_BEAST_222FORGOTTEN_BEAST_223FORGOTTEN_BEAST_224FORGOTTEN_BEAST_225FORGOTTEN_BEAST_226FORGOTTEN_BEAST_227FORGOTTEN_BEAST_228FORGOTTEN_BEAST_229FORGOTTEN_BEAST_230FORGOTTEN_BEAST_231FORGOTTEN_BEAST_232FORGOTTEN_BEAST_233FORGOTTEN_BEAST_234FORGOTTEN_BEAST_235FORGOTTEN_BEAST_236FORGOTTEN_BEAST_237FORGOTTEN_BEAST_238FORGOTTEN_BEAST_239FORGOTTEN_BEAST_240FORGOTTEN_BEAST_241FORGOTTEN_BEAST_242FORGOTTEN_BEAST_243FORGOTTEN_BEAST_244FORGOTTEN_BEAST_245FORGOTTEN_BEAST_246FORGOTTEN_BEAST_247FORGOTTEN_BEAST_248FORGOTTEN_BEAST_249FORGOTTEN_BEAST_250FORGOTTEN_BEAST_251FORGOTTEN_BEAST_252FORGOTTEN_BEAST_253FORGOTTEN_BEAST_254FORGOTTEN_BEAST_255FORGOTTEN_BEAST_256FORGOTTEN_BEAST_257FORGOTTEN_BEAST_258FORGOTTEN_BEAST_259FORGOTTEN_BEAST_260FORGOTTEN_BEAST_261FORGOTTEN_BEAST_262FORGOTTEN_BEAST_263FORGOTTEN_BEAST_264FORGOTTEN_BEAST_265FORGOTTEN_BEAST_266FORGOTTEN_BEAST_267FORGOTTEN_BEAST_268FORGOTTEN_BEAST_269FORGOTTEN_BEAST_270FORGOTTEN_BEAST_271FORGOTTEN_BEAST_272FORGOTTEN_BEAST_273FORGOTTEN_BEAST_274FORGOTTEN_BEAST_275FORGOTTEN_BEAST_276FORGOTTEN_BEAST_277FORGOTTEN_BEAST_278FORGOTTEN_BEAST_279FORGOTTEN_BEAST_280FORGOTTEN_BEAST_281FORGOTTEN_BEAST_282FORGOTTEN_BEAST_283FORGOTTEN_BEAST_284FORGOTTEN_BEAST_285FORGOTTEN_BEAST_286FORGOTTEN_BEAST_287FORGOTTEN_BEAST_288FORGOTTEN_BEAST_289FORGOTTEN_BEAST_290FORGOTTEN_BEAST_291FORGOTTEN_BEAST_292FORGOTTEN_BEAST_293FORGOTTEN_BEAST_294FORGOTTEN_BEAST_295FORGOTTEN_BEAST_296FORGOTTEN_BEAST_297FORGOTTEN_BEAST_298FORGOTTEN_BEAST_299FORGOTTEN_BEAST_300FORGOTTEN_BEAST_301FORGOTTEN_BEAST_302FORGOTTEN_BEAST_303FORGOTTEN_BEAST_304FORGOTTEN_BEAST_305FORGOTTEN_BEAST_306FORGOTTEN_BEAST_307FORGOTTEN_BEAST_308FORGOTTEN_BEAST_309FORGOTTEN_BEAST_310FORGOTTEN_BEAST_311FORGOTTEN_BEAST_312FORGOTTEN_BEAST_313FORGOTTEN_BEAST_314FORGOTTEN_BEAST_315FORGOTTEN_BEAST_316FORGOTTEN_BEAST_317FORGOTTEN_BEAST_318FORGOTTEN_BEAST_319FORGOTTEN_BEAST_320FORGOTTEN_BEAST_321FORGOTTEN_BEAST_322FORGOTTEN_BEAST_323FORGOTTEN_BEAST_324FORGOTTEN_BEAST_325FORGOTTEN_BEAST_326FORGOTTEN_BEAST_327FORGOTTEN_BEAST_328FORGOTTEN_BEAST_329FORGOTTEN_BEAST_330FORGOTTEN_BEAST_331FORGOTTEN_BEAST_332FORGOTTEN_BEAST_333FORGOTTEN_BEAST_334FORGOTTEN_BEAST_335FORGOTTEN_BEAST_336FORGOTTEN_BEAST_337FORGOTTEN_BEAST_338FORGOTTEN_BEAST_339FORGOTTEN_BEAST_340FORGOTTEN_BEAST_341FORGOTTEN_BEAST_342FORGOTTEN_BEAST_343FORGOTTEN_BEAST_344FORGOTTEN_BEAST_345FORGOTTEN_BEAST_346FORGOTTEN_BEAST_347FORGOTTEN_BEAST_348FORGOTTEN_BEAST_349FORGOTTEN_BEAST_350FORGOTTEN_BEAST_351FORGOTTEN_BEAST_352FORGOTTEN_BEAST_353FORGOTTEN_BEAST_354FORGOTTEN_BEAST_355FORGOTTEN_BEAST_356FORGOTTEN_BEAST_357FORGOTTEN_BEAST_358FORGOTTEN_BEAST_359FORGOTTEN_BEAST_360FORGOTTEN_BEAST_361FORGOTTEN_BEAST_362FORGOTTEN_BEAST_363FORGOTTEN_BEAST_364FORGOTTEN_BEAST_365FORGOTTEN_BEAST_366FORGOTTEN_BEAST_367FORGOTTEN_BEAST_368FORGOTTEN_BEAST_369FORGOTTEN_BEAST_370FORGOTTEN_BEAST_371FORGOTTEN_BEAST_372FORGOTTEN_BEAST_373FORGOTTEN_BEAST_374FORGOTTEN_BEAST_375FORGOTTEN_BEAST_376FORGOTTEN_BEAST_377FORGOTTEN_BEAST_378FORGOTTEN_BEAST_379FORGOTTEN_BEAST_380FORGOTTEN_BEAST_381FORGOTTEN_BEAST_382FORGOTTEN_BEAST_383FORGOTTEN_BEAST_384FORGOTTEN_BEAST_385FORGOTTEN_BEAST_386FORGOTTEN_BEAST_387FORGOTTEN_BEAST_388FORGOTTEN_BEAST_389FORGOTTEN_BEAST_390FORGOTTEN_BEAST_391FORGOTTEN_BEAST_392FORGOTTEN_BEAST_393FORGOTTEN_BEAST_394FORGOTTEN_BEAST_395FORGOTTEN_BEAST_396FORGOTTEN_BEAST_397FORGOTTEN_BEAST_398FORGOTTEN_BEAST_399FORGOTTEN_BEAST_400FORGOTTEN_BEAST_401FORGOTTEN_BEAST_402FORGOTTEN_BEAST_403FORGOTTEN_BEAST_404FORGOTTEN_BEAST_405FORGOTTEN_BEAST_406FORGOTTEN_BEAST_407FORGOTTEN_BEAST_408FORGOTTEN_BEAST_409FORGOTTEN_BEAST_410FORGOTTEN_BEAST_411FORGOTTEN_BEAST_412FORGOTTEN_BEAST_413FORGOTTEN_BEAST_414FORGOTTEN_BEAST_415FORGOTTEN_BEAST_416FORGOTTEN_BEAST_417FORGOTTEN_BEAST_418FORGOTTEN_BEAST_419FORGOTTEN_BEAST_420FORGOTTEN_BEAST_421FORGOTTEN_BEAST_422FORGOTTEN_BEAST_423FORGOTTEN_BEAST_424FORGOTTEN_BEAST_425FORGOTTEN_BEAST_426FORGOTTEN_BEAST_427FORGOTTEN_BEAST_428FORGOTTEN_BEAST_429FORGOTTEN_BEAST_430FORGOTTEN_BEAST_431FORGOTTEN_BEAST_432FORGOTTEN_BEAST_433FORGOTTEN_BEAST_434FORGOTTEN_BEAST_435FORGOTTEN_BEAST_436FORGOTTEN_BEAST_437FORGOTTEN_BEAST_438FORGOTTEN_BEAST_439FORGOTTEN_BEAST_440FORGOTTEN_BEAST_441FORGOTTEN_BEAST_442FORGOTTEN_BEAST_443FORGOTTEN_BEAST_444FORGOTTEN_BEAST_445FORGOTTEN_BEAST_446FORGOTTEN_BEAST_447FORGOTTEN_BEAST_448FORGOTTEN_BEAST_449FORGOTTEN_BEAST_450FORGOTTEN_BEAST_451FORGOTTEN_BEAST_452FORGOTTEN_BEAST_453FORGOTTEN_BEAST_454FORGOTTEN_BEAST_455FORGOTTEN_BEAST_456FORGOTTEN_BEAST_457FORGOTTEN_BEAST_458FORGOTTEN_BEAST_459FORGOTTEN_BEAST_460FORGOTTEN_BEAST_461FORGOTTEN_BEAST_462FORGOTTEN_BEAST_463FORGOTTEN_BEAST_464FORGOTTEN_BEAST_465FORGOTTEN_BEAST_466FORGOTTEN_BEAST_467FORGOTTEN_BEAST_468FORGOTTEN_BEAST_469FORGOTTEN_BEAST_470FORGOTTEN_BEAST_471FORGOTTEN_BEAST_472FORGOTTEN_BEAST_473FORGOTTEN_BEAST_474FORGOTTEN_BEAST_475FORGOTTEN_BEAST_476FORGOTTEN_BEAST_477FORGOTTEN_BEAST_478FORGOTTEN_BEAST_479FORGOTTEN_BEAST_480FORGOTTEN_BEAST_481FORGOTTEN_BEAST_482FORGOTTEN_BEAST_483FORGOTTEN_BEAST_484FORGOTTEN_BEAST_485FORGOTTEN_BEAST_486FORGOTTEN_BEAST_487FORGOTTEN_BEAST_488FORGOTTEN_BEAST_489FORGOTTEN_BEAST_490FORGOTTEN_BEAST_491FORGOTTEN_BEAST_492FORGOTTEN_BEAST_493FORGOTTEN_BEAST_494FORGOTTEN_BEAST_495FORGOTTEN_BEAST_496FORGOTTEN_BEAST_497FORGOTTEN_BEAST_498FORGOTTEN_BEAST_499FORGOTTEN_BEAST_500FORGOTTEN_BEAST_501FORGOTTEN_BEAST_502FORGOTTEN_BEAST_503FORGOTTEN_BEAST_504FORGOTTEN_BEAST_505FORGOTTEN_BEAST_506FORGOTTEN_BEAST_507FORGOTTEN_BEAST_508FORGOTTEN_BEAST_509FORGOTTEN_BEAST_510FORGOTTEN_BEAST_511FORGOTTEN_BEAST_512FORGOTTEN_BEAST_513FORGOTTEN_BEAST_514FORGOTTEN_BEAST_515FORGOTTEN_BEAST_516FORGOTTEN_BEAST_517FORGOTTEN_BEAST_518FORGOTTEN_BEAST_519FORGOTTEN_BEAST_520FORGOTTEN_BEAST_521FORGOTTEN_BEAST_522FORGOTTEN_BEAST_523FORGOTTEN_BEAST_524FORGOTTEN_BEAST_525FORGOTTEN_BEAST_526FORGOTTEN_BEAST_527FORGOTTEN_BEAST_528FORGOTTEN_BEAST_529FORGOTTEN_BEAST_530FORGOTTEN_BEAST_531FORGOTTEN_BEAST_532FORGOTTEN_BEAST_533FORGOTTEN_BEAST_534FORGOTTEN_BEAST_535FORGOTTEN_BEAST_536FORGOTTEN_BEAST_537FORGOTTEN_BEAST_538FORGOTTEN_BEAST_539FORGOTTEN_BEAST_540FORGOTTEN_BEAST_541FORGOTTEN_BEAST_542FORGOTTEN_BEAST_543FORGOTTEN_BEAST_544FORGOTTEN_BEAST_545FORGOTTEN_BEAST_546FORGOTTEN_BEAST_547FORGOTTEN_BEAST_548FORGOTTEN_BEAST_549FORGOTTEN_BEAST_550FORGOTTEN_BEAST_551FORGOTTEN_BEAST_552FORGOTTEN_BEAST_553FORGOTTEN_BEAST_554FORGOTTEN_BEAST_555FORGOTTEN_BEAST_556FORGOTTEN_BEAST_557FORGOTTEN_BEAST_558FORGOTTEN_BEAST_559FORGOTTEN_BEAST_560FORGOTTEN_BEAST_561FORGOTTEN_BEAST_562FORGOTTEN_BEAST_563FORGOTTEN_BEAST_564FORGOTTEN_BEAST_565FORGOTTEN_BEAST_566FORGOTTEN_BEAST_567FORGOTTEN_BEAST_568FORGOTTEN_BEAST_569FORGOTTEN_BEAST_570FORGOTTEN_BEAST_571FORGOTTEN_BEAST_572FORGOTTEN_BEAST_573FORGOTTEN_BEAST_574FORGOTTEN_BEAST_575FORGOTTEN_BEAST_576FORGOTTEN_BEAST_577FORGOTTEN_BEAST_578FORGOTTEN_BEAST_579FORGOTTEN_BEAST_580FORGOTTEN_BEAST_581FORGOTTEN_BEAST_582FORGOTTEN_BEAST_583FORGOTTEN_BEAST_584FORGOTTEN_BEAST_585FORGOTTEN_BEAST_586FORGOTTEN_BEAST_587FORGOTTEN_BEAST_588FORGOTTEN_BEAST_589FORGOTTEN_BEAST_590FORGOTTEN_BEAST_591FORGOTTEN_BEAST_592FORGOTTEN_BEAST_593FORGOTTEN_BEAST_594FORGOTTEN_BEAST_595FORGOTTEN_BEAST_596FORGOTTEN_BEAST_597FORGOTTEN_BEAST_598FORGOTTEN_BEAST_599FORGOTTEN_BEAST_600FORGOTTEN_BEAST_601FORGOTTEN_BEAST_602FORGOTTEN_BEAST_603FORGOTTEN_BEAST_604FORGOTTEN_BEAST_605FORGOTTEN_BEAST_606FORGOTTEN_BEAST_607FORGOTTEN_BEAST_608FORGOTTEN_BEAST_609FORGOTTEN_BEAST_610FORGOTTEN_BEAST_611FORGOTTEN_BEAST_612FORGOTTEN_BEAST_613FORGOTTEN_BEAST_614FORGOTTEN_BEAST_615FORGOTTEN_BEAST_616FORGOTTEN_BEAST_617FORGOTTEN_BEAST_618FORGOTTEN_BEAST_619FORGOTTEN_BEAST_620FORGOTTEN_BEAST_621FORGOTTEN_BEAST_622FORGOTTEN_BEAST_623FORGOTTEN_BEAST_624FORGOTTEN_BEAST_625FORGOTTEN_BEAST_626FORGOTTEN_BEAST_627FORGOTTEN_BEAST_628FORGOTTEN_BEAST_629FORGOTTEN_BEAST_630FORGOTTEN_BEAST_631FORGOTTEN_BEAST_632FORGOTTEN_BEAST_633FORGOTTEN_BEAST_634FORGOTTEN_BEAST_635FORGOTTEN_BEAST_636FORGOTTEN_BEAST_637FORGOTTEN_BEAST_638FORGOTTEN_BEAST_639FORGOTTEN_BEAST_640FORGOTTEN_BEAST_641FORGOTTEN_BEAST_642FORGOTTEN_BEAST_643FORGOTTEN_BEAST_644FORGOTTEN_BEAST_645FORGOTTEN_BEAST_646FORGOTTEN_BEAST_647FORGOTTEN_BEAST_648FORGOTTEN_BEAST_649FORGOTTEN_BEAST_650FORGOTTEN_BEAST_651FORGOTTEN_BEAST_652FORGOTTEN_BEAST_653FORGOTTEN_BEAST_654FORGOTTEN_BEAST_655FORGOTTEN_BEAST_656FORGOTTEN_BEAST_657FORGOTTEN_BEAST_658FORGOTTEN_BEAST_659FORGOTTEN_BEAST_660FORGOTTEN_BEAST_661FORGOTTEN_BEAST_662FORGOTTEN_BEAST_663FORGOTTEN_BEAST_664FORGOTTEN_BEAST_665FORGOTTEN_BEAST_666FORGOTTEN_BEAST_667FORGOTTEN_BEAST_668FORGOTTEN_BEAST_669FORGOTTEN_BEAST_670FORGOTTEN_BEAST_671FORGOTTEN_BEAST_672FORGOTTEN_BEAST_673FORGOTTEN_BEAST_674FORGOTTEN_BEAST_675FORGOTTEN_BEAST_676FORGOTTEN_BEAST_677FORGOTTEN_BEAST_678FORGOTTEN_BEAST_679FORGOTTEN_BEAST_680FORGOTTEN_BEAST_681FORGOTTEN_BEAST_682FORGOTTEN_BEAST_683FORGOTTEN_BEAST_684FORGOTTEN_BEAST_685FORGOTTEN_BEAST_686FORGOTTEN_BEAST_687FORGOTTEN_BEAST_688FORGOTTEN_BEAST_689FORGOTTEN_BEAST_690FORGOTTEN_BEAST_691FORGOTTEN_BEAST_692FORGOTTEN_BEAST_693FORGOTTEN_BEAST_694FORGOTTEN_BEAST_695FORGOTTEN_BEAST_696FORGOTTEN_BEAST_697FORGOTTEN_BEAST_698FORGOTTEN_BEAST_699FORGOTTEN_BEAST_700FORGOTTEN_BEAST_701FORGOTTEN_BEAST_702FORGOTTEN_BEAST_703FORGOTTEN_BEAST_704FORGOTTEN_BEAST_705FORGOTTEN_BEAST_706FORGOTTEN_BEAST_707FORGOTTEN_BEAST_708FORGOTTEN_BEAST_709FORGOTTEN_BEAST_710FORGOTTEN_BEAST_711FORGOTTEN_BEAST_712FORGOTTEN_BEAST_713FORGOTTEN_BEAST_714FORGOTTEN_BEAST_715FORGOTTEN_BEAST_716FORGOTTEN_BEAST_717FORGOTTEN_BEAST_718FORGOTTEN_BEAST_719FORGOTTEN_BEAST_720FORGOTTEN_BEAST_721FORGOTTEN_BEAST_722FORGOTTEN_BEAST_723FORGOTTEN_BEAST_724FORGOTTEN_BEAST_725FORGOTTEN_BEAST_726FORGOTTEN_BEAST_727FORGOTTEN_BEAST_728FORGOTTEN_BEAST_729FORGOTTEN_BEAST_730FORGOTTEN_BEAST_731FORGOTTEN_BEAST_732FORGOTTEN_BEAST_733FORGOTTEN_BEAST_734FORGOTTEN_BEAST_735FORGOTTEN_BEAST_736FORGOTTEN_BEAST_737FORGOTTEN_BEAST_738FORGOTTEN_BEAST_739FORGOTTEN_BEAST_740FORGOTTEN_BEAST_741FORGOTTEN_BEAST_742FORGOTTEN_BEAST_743FORGOTTEN_BEAST_744FORGOTTEN_BEAST_745FORGOTTEN_BEAST_746FORGOTTEN_BEAST_747FORGOTTEN_BEAST_748FORGOTTEN_BEAST_749FORGOTTEN_BEAST_750FORGOTTEN_BEAST_751FORGOTTEN_BEAST_752FORGOTTEN_BEAST_753FORGOTTEN_BEAST_754FORGOTTEN_BEAST_755FORGOTTEN_BEAST_756FORGOTTEN_BEAST_757FORGOTTEN_BEAST_758FORGOTTEN_BEAST_759FORGOTTEN_BEAST_760FORGOTTEN_BEAST_761FORGOTTEN_BEAST_762FORGOTTEN_BEAST_763FORGOTTEN_BEAST_764FORGOTTEN_BEAST_765FORGOTTEN_BEAST_766FORGOTTEN_BEAST_767FORGOTTEN_BEAST_768FORGOTTEN_BEAST_769FORGOTTEN_BEAST_770FORGOTTEN_BEAST_771FORGOTTEN_BEAST_772FORGOTTEN_BEAST_773FORGOTTEN_BEAST_774FORGOTTEN_BEAST_775FORGOTTEN_BEAST_776FORGOTTEN_BEAST_777FORGOTTEN_BEAST_778FORGOTTEN_BEAST_779FORGOTTEN_BEAST_780FORGOTTEN_BEAST_781FORGOTTEN_BEAST_782FORGOTTEN_BEAST_783FORGOTTEN_BEAST_784FORGOTTEN_BEAST_785FORGOTTEN_BEAST_786FORGOTTEN_BEAST_787FORGOTTEN_BEAST_788FORGOTTEN_BEAST_789FORGOTTEN_BEAST_790FORGOTTEN_BEAST_791FORGOTTEN_BEAST_792FORGOTTEN_BEAST_793FORGOTTEN_BEAST_794FORGOTTEN_BEAST_795FORGOTTEN_BEAST_796FORGOTTEN_BEAST_797FORGOTTEN_BEAST_798FORGOTTEN_BEAST_799FORGOTTEN_BEAST_800FORGOTTEN_BEAST_801FORGOTTEN_BEAST_802FORGOTTEN_BEAST_803FORGOTTEN_BEAST_804FORGOTTEN_BEAST_805FORGOTTEN_BEAST_806FORGOTTEN_BEAST_807FORGOTTEN_BEAST_808FORGOTTEN_BEAST_809FORGOTTEN_BEAST_810FORGOTTEN_BEAST_811FORGOTTEN_BEAST_812FORGOTTEN_BEAST_813FORGOTTEN_BEAST_814FORGOTTEN_BEAST_815FORGOTTEN_BEAST_816FORGOTTEN_BEAST_817FORGOTTEN_BEAST_818FORGOTTEN_BEAST_819FORGOTTEN_BEAST_820FORGOTTEN_BEAST_821FORGOTTEN_BEAST_822FORGOTTEN_BEAST_823FORGOTTEN_BEAST_824FORGOTTEN_BEAST_825FORGOTTEN_BEAST_826FORGOTTEN_BEAST_827FORGOTTEN_BEAST_828FORGOTTEN_BEAST_829FORGOTTEN_BEAST_830FORGOTTEN_BEAST_831FORGOTTEN_BEAST_832FORGOTTEN_BEAST_833FORGOTTEN_BEAST_834FORGOTTEN_BEAST_835FORGOTTEN_BEAST_836FORGOTTEN_BEAST_837FORGOTTEN_BEAST_838FORGOTTEN_BEAST_839FORGOTTEN_BEAST_840FORGOTTEN_BEAST_841FORGOTTEN_BEAST_842FORGOTTEN_BEAST_843FORGOTTEN_BEAST_844FORGOTTEN_BEAST_845FORGOTTEN_BEAST_846FORGOTTEN_BEAST_847FORGOTTEN_BEAST_848FORGOTTEN_BEAST_849FORGOTTEN_BEAST_850FORGOTTEN_BEAST_851FORGOTTEN_BEAST_852FORGOTTEN_BEAST_853FORGOTTEN_BEAST_854FORGOTTEN_BEAST_855FORGOTTEN_BEAST_856FORGOTTEN_BEAST_857FORGOTTEN_BEAST_858FORGOTTEN_BEAST_859FORGOTTEN_BEAST_860FORGOTTEN_BEAST_861FORGOTTEN_BEAST_862FORGOTTEN_BEAST_863FORGOTTEN_BEAST_864FORGOTTEN_BEAST_865FORGOTTEN_BEAST_866FORGOTTEN_BEAST_867TITAN_1TITAN_2TITAN_3TITAN_4TITAN_5TITAN_6TITAN_7TITAN_8TITAN_9TITAN_10TITAN_11TITAN_12TITAN_13TITAN_14TITAN_15TITAN_16TITAN_17TITAN_18TITAN_19TITAN_20TITAN_21TITAN_22TITAN_23TITAN_24TITAN_25TITAN_26TITAN_27TITAN_28TITAN_29TITAN_30TITAN_31TITAN_32TITAN_33DEMON_1DEMON_2DEMON_3DEMON_4DEMON_5DEMON_6DEMON_7DEMON_8DEMON_9DEMON_10DEMON_11DEMON_12DEMON_13DEMON_14DEMON_15DEMON_16DEMON_17DEMON_18DEMON_19DEMON_20DEMON_21DEMON_22DEMON_23DEMON_24DEMON_25DEMON_26DEMON_27DEMON_28DEMON_29DEMON_30DEMON_31DEMON_32DEMON_33DEMON_34DEMON_35DEMON_36DEMON_37DEMON_38DEMON_39DEMON_40DEMON_41DEMON_42DEMON_43DEMON_44DEMON_45DEMON_46DEMON_47DEMON_48DEMON_49DEMON_50DEMON_51DEMON_52NIGHT_CREATURE_1NIGHT_CREATURE_2NIGHT_CREATURE_3NIGHT_CREATURE_4NIGHT_CREATURE_5NIGHT_CREATURE_6NIGHT_CREATURE_7NIGHT_CREATURE_8NIGHT_CREATURE_9NIGHT_CREATURE_10NIGHT_CREATURE_11NIGHT_CREATURE_12NIGHT_CREATURE_13NIGHT_CREATURE_14NIGHT_CREATURE_15NIGHT_CREATURE_16NIGHT_CREATURE_17NIGHT_CREATURE_18NIGHT_CREATURE_19NIGHT_CREATURE_20NIGHT_CREATURE_21NIGHT_CREATURE_22NIGHT_CREATURE_23NIGHT_CREATURE_24NIGHT_CREATURE_25NIGHT_CREATURE_26NIGHT_CREATURE_27NIGHT_CREATURE_28NIGHT_CREATURE_29NIGHT_CREATURE_30NIGHT_CREATURE_31NIGHT_CREATURE_32NIGHT_CREATURE_33NIGHT_CREATURE_34NIGHT_CREATURE_35NIGHT_CREATURE_36NIGHT_CREATURE_37NIGHT_CREATURE_38NIGHT_CREATURE_39NIGHT_CREATURE_40NIGHT_CREATURE_41NIGHT_CREATURE_42NIGHT_CREATURE_43NIGHT_CREATURE_44NIGHT_CREATURE_45NIGHT_CREATURE_46NIGHT_CREATURE_47NIGHT_CREATURE_48NIGHT_CREATURE_49NIGHT_CREATURE_50NIGHT_CREATURE_51NIGHT_CREATURE_52NIGHT_CREATURE_53NIGHT_CREATURE_54NIGHT_CREATURE_55NIGHT_CREATURE_56NIGHT_CREATURE_57NIGHT_CREATURE_58NIGHT_CREATURE_59NIGHT_CREATURE_60NIGHT_CREATURE_61NIGHT_CREATURE_62NIGHT_CREATURE_63NIGHT_CREATURE_64NIGHT_CREATURE_65NIGHT_CREATURE_66NIGHT_CREATURE_67NIGHT_CREATURE_68NIGHT_CREATURE_69NIGHT_CREATURE_70NIGHT_CREATURE_71NIGHT_CREATURE_72NIGHT_CREATURE_73NIGHT_CREATURE_74NIGHT_CREATURE_75NIGHT_CREATURE_76NIGHT_CREATURE_77NIGHT_CREATURE_78NIGHT_CREATURE_79NIGHT_CREATURE_80NIGHT_CREATURE_81NIGHT_CREATURE_82NIGHT_CREATURE_83NIGHT_CREATURE_84NIGHT_CREATURE_85NIGHT_CREATURE_86NIGHT_CREATURE_87NIGHT_CREATURE_88NIGHT_CREATURE_89NIGHT_CREATURE_90NIGHT_CREATURE_91NIGHT_CREATURE_92NIGHT_CREATURE_93NIGHT_CREATURE_94NIGHT_CREATURE_95NIGHT_CREATURE_96NIGHT_CREATURE_97NIGHT_CREATURE_98NIGHT_CREATURE_99NIGHT_CREATURE_100NIGHT_CREATURE_101NIGHT_CREATURE_102NIGHT_CREATURE_103NIGHT_CREATURE_104HF1248 DIVINE_1HF1248 DIVINE_2HF1248 DIVINE_3HF1108 DIVINE_1HF1108 DIVINE_2HF1108 DIVINE_3HF1249 DIVINE_1HF1249 DIVINE_2HF1249 DIVINE_3HF1345 DIVINE_1HF1345 DIVINE_2HF1345 DIVINE_3˜ ¨°¸À \ No newline at end of file +  \ No newline at end of file diff --git a/data/stockpiles/category_armor.dfstock b/data/stockpiles/category_armor.dfstock index 8d69cec678552608e310a1d01ab3fd05581ddda6..54a2d54eee3204330dc86b6d12ee8684d24cf1aa 100644 GIT binary patch literal 5 McmeBTWXfO!00WW$_W%F@ literal 2040 zcmZ{kL2ueH6vv6gv<}pDqbmj4Q;*!*s;Zisojei~Cq}k`wU-RFR6;^+#i+D?g`IZX zamO8Z+;QJzUuBN~O^Uq>*uVet|Jm=^`Re@LsT>j+3+XZqaheTNDuF27<3vN{z))kt zc&b7sb){j59Ec7?kk0th`X;>kJv3M#v`|im4YM< zI8n;2dML`(WZRRMN)x`CmM@7+=v$Tu84!G>E|cQLJP_Qc?{XdO2Ox{Y6TRL`f<6)O zqU#m5pty!&mMfq;xi6MSsUlDs(4&?|^jMH)yDKvM-m#<~7vq3~rzNuOd@Tmiy?ok( zp$3_DSORq4z*@2n2l~MfIJqny3ajcD6BFK#v&|GNm!(L}s!_)AqR?iZ1P?4Z-r;p{ zJ(nmaUWA|ctZ2JAmqJ0Bb?AK#DN}J@hqPg!wL{@##3wOcsYM|QG`mwq1AI2Xxb(Pq z<`p}&urmugx3CLZIMF)Cu??Qu;F%4c+u%iaOU!O2cca-;w>lh8r`~LIJ0IV4w>y)& z@$_ju-BOPa+!>Ua(_3PcHhLK&-3x@_Ho+XBD4AAc62?L&yKR_C|^3t lXGd9dl)0mPcKqmka=tmvcgOkRlzuvmUry<_Q~KkS{sDA}NDlx2 diff --git a/data/stockpiles/category_bars_blocks.dfstock b/data/stockpiles/category_bars_blocks.dfstock index fdc5bb346f0e1b6a1f16b3d6b568bfa023f03368..b7ae54bb1f020e87b6765ea97e5112c6b7d6b560 100644 GIT binary patch literal 4 LcmWGy(qIGt0iytZ literal 3450 zcmZ`*%WmB^5VcUF2ppts+`Ouy8~uS{7>2tWQMANdieyR3&hTo#EGc)x6=NIR~lD_!S*+`+e_#yqgaFV=E|IULYclomRQkuSIC|tf2 znop;XSHd4dBt3neKU!PmKPh8{UALe!aBD^SkFOf)bgZ^ zz&j;dd6N0hv)3v`P0eAD1Cpg zjLhF};ON!8mie2_fu0I@!3|1F!eMn?3M$54A&IbtWC7fYTO`#djzloS0&o)^G|>415TK_#A};Sus@*E?Zd5$U^&qwaM;*Ysig!I%Z(euQNjWmzsW%2v=I7DmYbBE4) zAvGz3G4OCsGxtd%$c?uJ1DR^jkCHq=;s^p&+HMjAf`x|k!iL~-Zq#|p10|4-XhW-3 zj*?IlVNI=V%V~)cSx{L<6=PsqG}6ZC0guRU)Wz65owl=TUM=BugX&OvLwIaY<3Q&2?mYT5^t7fm9d%$ zWC*(_xJ0V87lJo7UmalR@ANw9OBK^Hv|YebYgG>cg7g#$cOv&f*J6%@GKNa>NKs)+ zCT(&UC>mw-)W~Y>U}L?RwkcMlKDxk`U>WdxNZ=U#C?o23T~+a79$N#(jbpp&UdgIY zuPAh#Y3_!!Las}%+EE46_n_J`t^H*{(+XGXR`HTFGXgDH@3?DCL@Y#A4p+fCQ;_SG z>_F>jbB4Ht@G@DJAp>KT`)pGc>R2#V_x@i13Iv^45=&kMs z2Z88LMbU8Ua9^X}C3T;Y=`D}G?*D_!j4G-S>7Z(X<3Ivx-m-Vk6`+xaKLh!Y)miSO#pJU^2|#h6fE#NU|sBxnkl$t z>yT-VV~YR-IA6nMV6~^m9@Zr!lIJ1U0m7T#iRjVk4Yz|`?_f7O*zFE>hp9N(xY`4+ h_rRMy@OBTp`?Cihn>>H`1p^N|k diff --git a/data/stockpiles/category_cloth.dfstock b/data/stockpiles/category_cloth.dfstock index 62d84c402..d88106c6b 100644 --- a/data/stockpiles/category_cloth.dfstock +++ b/data/stockpiles/category_cloth.dfstock @@ -1,141 +1 @@ -rêL -"CREATURE:SPIDER_BROWN_RECLUSE:SILK -&CREATURE:BROWN_RECLUSE_SPIDER_MAN:SILK -(CREATURE:GIANT_BROWN_RECLUSE_SPIDER:SILK -CREATURE:SPIDER_PHANTOM:SILK -CREATURE:SPIDER_CAVE_GIANT:SILK -CREATURE:SPIDER_CAVE:SILK -INORGANIC:DIVINE_2 -INORGANIC:DIVINE_4 -INORGANIC:DIVINE_6 -INORGANIC:DIVINE_8 -INORGANIC:DIVINE_10 -INORGANIC:DIVINE_12 -INORGANIC:DIVINE_14 -INORGANIC:DIVINE_16 -INORGANIC:DIVINE_18 -INORGANIC:DIVINE_20 - CREATURE:FORGOTTEN_BEAST_10:SILK - CREATURE:FORGOTTEN_BEAST_12:SILK - CREATURE:FORGOTTEN_BEAST_17:SILK - CREATURE:FORGOTTEN_BEAST_20:SILK - CREATURE:FORGOTTEN_BEAST_29:SILK - CREATURE:FORGOTTEN_BEAST_31:SILK - CREATURE:FORGOTTEN_BEAST_42:SILK - CREATURE:FORGOTTEN_BEAST_55:SILK - CREATURE:FORGOTTEN_BEAST_65:SILK - CREATURE:FORGOTTEN_BEAST_68:SILK - CREATURE:FORGOTTEN_BEAST_71:SILK - CREATURE:FORGOTTEN_BEAST_74:SILK - CREATURE:FORGOTTEN_BEAST_91:SILK -!CREATURE:FORGOTTEN_BEAST_101:SILK -!CREATURE:FORGOTTEN_BEAST_109:SILK -!CREATURE:FORGOTTEN_BEAST_119:SILK -!CREATURE:FORGOTTEN_BEAST_121:SILK -!CREATURE:FORGOTTEN_BEAST_125:SILK -!CREATURE:FORGOTTEN_BEAST_128:SILK -!CREATURE:FORGOTTEN_BEAST_132:SILK -!CREATURE:FORGOTTEN_BEAST_142:SILK -!CREATURE:FORGOTTEN_BEAST_146:SILK -!CREATURE:FORGOTTEN_BEAST_168:SILK -!CREATURE:FORGOTTEN_BEAST_173:SILK -!CREATURE:FORGOTTEN_BEAST_186:SILK -!CREATURE:FORGOTTEN_BEAST_204:SILK -!CREATURE:FORGOTTEN_BEAST_213:SILK -!CREATURE:FORGOTTEN_BEAST_224:SILK -!CREATURE:FORGOTTEN_BEAST_235:SILK -!CREATURE:FORGOTTEN_BEAST_236:SILK -!CREATURE:FORGOTTEN_BEAST_239:SILK -!CREATURE:FORGOTTEN_BEAST_248:SILK -!CREATURE:FORGOTTEN_BEAST_275:SILK -!CREATURE:FORGOTTEN_BEAST_277:SILK -!CREATURE:FORGOTTEN_BEAST_281:SILK -!CREATURE:FORGOTTEN_BEAST_289:SILK -!CREATURE:FORGOTTEN_BEAST_290:SILK -!CREATURE:FORGOTTEN_BEAST_293:SILK -!CREATURE:FORGOTTEN_BEAST_297:SILK -!CREATURE:FORGOTTEN_BEAST_309:SILK -!CREATURE:FORGOTTEN_BEAST_311:SILK -!CREATURE:FORGOTTEN_BEAST_313:SILK -!CREATURE:FORGOTTEN_BEAST_326:SILK -!CREATURE:FORGOTTEN_BEAST_330:SILK -!CREATURE:FORGOTTEN_BEAST_341:SILK -!CREATURE:FORGOTTEN_BEAST_343:SILK -!CREATURE:FORGOTTEN_BEAST_357:SILK -!CREATURE:FORGOTTEN_BEAST_362:SILK -!CREATURE:FORGOTTEN_BEAST_371:SILK -!CREATURE:FORGOTTEN_BEAST_376:SILK -!CREATURE:FORGOTTEN_BEAST_379:SILK -!CREATURE:FORGOTTEN_BEAST_384:SILK -!CREATURE:FORGOTTEN_BEAST_397:SILK -!CREATURE:FORGOTTEN_BEAST_398:SILK -!CREATURE:FORGOTTEN_BEAST_403:SILK -!CREATURE:FORGOTTEN_BEAST_412:SILK -!CREATURE:FORGOTTEN_BEAST_426:SILK -!CREATURE:FORGOTTEN_BEAST_427:SILK -!CREATURE:FORGOTTEN_BEAST_432:SILK -!CREATURE:FORGOTTEN_BEAST_435:SILK -!CREATURE:FORGOTTEN_BEAST_438:SILK -!CREATURE:FORGOTTEN_BEAST_442:SILK -!CREATURE:FORGOTTEN_BEAST_456:SILK -!CREATURE:FORGOTTEN_BEAST_460:SILK -!CREATURE:FORGOTTEN_BEAST_465:SILK -!CREATURE:FORGOTTEN_BEAST_467:SILK -!CREATURE:FORGOTTEN_BEAST_472:SILK -!CREATURE:FORGOTTEN_BEAST_476:SILK -!CREATURE:FORGOTTEN_BEAST_486:SILK -!CREATURE:FORGOTTEN_BEAST_508:SILK -!CREATURE:FORGOTTEN_BEAST_549:SILK -!CREATURE:FORGOTTEN_BEAST_567:SILK -!CREATURE:FORGOTTEN_BEAST_568:SILK -!CREATURE:FORGOTTEN_BEAST_575:SILK -!CREATURE:FORGOTTEN_BEAST_580:SILK -!CREATURE:FORGOTTEN_BEAST_583:SILK -!CREATURE:FORGOTTEN_BEAST_588:SILK -!CREATURE:FORGOTTEN_BEAST_589:SILK -!CREATURE:FORGOTTEN_BEAST_600:SILK -!CREATURE:FORGOTTEN_BEAST_605:SILK -!CREATURE:FORGOTTEN_BEAST_606:SILK -!CREATURE:FORGOTTEN_BEAST_612:SILK -!CREATURE:FORGOTTEN_BEAST_613:SILK -!CREATURE:FORGOTTEN_BEAST_614:SILK -!CREATURE:FORGOTTEN_BEAST_630:SILK -!CREATURE:FORGOTTEN_BEAST_632:SILK -!CREATURE:FORGOTTEN_BEAST_639:SILK -!CREATURE:FORGOTTEN_BEAST_643:SILK -!CREATURE:FORGOTTEN_BEAST_650:SILK -!CREATURE:FORGOTTEN_BEAST_653:SILK -!CREATURE:FORGOTTEN_BEAST_661:SILK -!CREATURE:FORGOTTEN_BEAST_662:SILK -!CREATURE:FORGOTTEN_BEAST_676:SILK -!CREATURE:FORGOTTEN_BEAST_680:SILK -!CREATURE:FORGOTTEN_BEAST_697:SILK -!CREATURE:FORGOTTEN_BEAST_706:SILK -!CREATURE:FORGOTTEN_BEAST_720:SILK -!CREATURE:FORGOTTEN_BEAST_723:SILK -!CREATURE:FORGOTTEN_BEAST_730:SILK -!CREATURE:FORGOTTEN_BEAST_757:SILK -!CREATURE:FORGOTTEN_BEAST_764:SILK -!CREATURE:FORGOTTEN_BEAST_765:SILK -!CREATURE:FORGOTTEN_BEAST_768:SILK -!CREATURE:FORGOTTEN_BEAST_774:SILK -!CREATURE:FORGOTTEN_BEAST_791:SILK -!CREATURE:FORGOTTEN_BEAST_809:SILK -!CREATURE:FORGOTTEN_BEAST_814:SILK -!CREATURE:FORGOTTEN_BEAST_821:SILK -!CREATURE:FORGOTTEN_BEAST_827:SILK -!CREATURE:FORGOTTEN_BEAST_829:SILK -!CREATURE:FORGOTTEN_BEAST_843:SILK -!CREATURE:FORGOTTEN_BEAST_855:SILK -!CREATURE:FORGOTTEN_BEAST_863:SILK -CREATURE:TITAN_4:SILK -CREATURE:TITAN_9:SILK -CREATURE:TITAN_14:SILK -CREATURE:TITAN_15:SILK -CREATURE:TITAN_16:SILK -CREATURE:TITAN_21:SILK -CREATURE:TITAN_22:SILK -CREATURE:TITAN_23:SILK -CREATURE:DEMON_39:SILK -CREATURE:DEMON_42:SILK -CREATURE:DEMON_51:SILKPLANT:FLAX:THREADPLANT:JUTE:THREADPLANT:HEMP:THREADPLANT:COTTON:THREADPLANT:RAMIE:THREADPLANT:KENAF:THREADPLANT:GRASS_TAIL_PIG:THREADPLANT:REED_ROPE:THREADCREATURE:SHEEP:HAIRCREATURE:LLAMA:HAIRCREATURE:ALPACA:HAIRCREATURE:TROLL:HAIRCREATURE:GNOLL:HAIR"INORGANIC:ADAMANTINE*"CREATURE:SPIDER_BROWN_RECLUSE:SILK*&CREATURE:BROWN_RECLUSE_SPIDER_MAN:SILK*(CREATURE:GIANT_BROWN_RECLUSE_SPIDER:SILK*CREATURE:SPIDER_PHANTOM:SILK*CREATURE:SPIDER_CAVE_GIANT:SILK*CREATURE:SPIDER_CAVE:SILK*INORGANIC:DIVINE_2*INORGANIC:DIVINE_4*INORGANIC:DIVINE_6*INORGANIC:DIVINE_8*INORGANIC:DIVINE_10*INORGANIC:DIVINE_12*INORGANIC:DIVINE_14*INORGANIC:DIVINE_16*INORGANIC:DIVINE_18*INORGANIC:DIVINE_20* CREATURE:FORGOTTEN_BEAST_10:SILK* CREATURE:FORGOTTEN_BEAST_12:SILK* CREATURE:FORGOTTEN_BEAST_17:SILK* CREATURE:FORGOTTEN_BEAST_20:SILK* CREATURE:FORGOTTEN_BEAST_29:SILK* CREATURE:FORGOTTEN_BEAST_31:SILK* CREATURE:FORGOTTEN_BEAST_42:SILK* CREATURE:FORGOTTEN_BEAST_55:SILK* CREATURE:FORGOTTEN_BEAST_65:SILK* CREATURE:FORGOTTEN_BEAST_68:SILK* CREATURE:FORGOTTEN_BEAST_71:SILK* CREATURE:FORGOTTEN_BEAST_74:SILK* CREATURE:FORGOTTEN_BEAST_91:SILK*!CREATURE:FORGOTTEN_BEAST_101:SILK*!CREATURE:FORGOTTEN_BEAST_109:SILK*!CREATURE:FORGOTTEN_BEAST_119:SILK*!CREATURE:FORGOTTEN_BEAST_121:SILK*!CREATURE:FORGOTTEN_BEAST_125:SILK*!CREATURE:FORGOTTEN_BEAST_128:SILK*!CREATURE:FORGOTTEN_BEAST_132:SILK*!CREATURE:FORGOTTEN_BEAST_142:SILK*!CREATURE:FORGOTTEN_BEAST_146:SILK*!CREATURE:FORGOTTEN_BEAST_168:SILK*!CREATURE:FORGOTTEN_BEAST_173:SILK*!CREATURE:FORGOTTEN_BEAST_186:SILK*!CREATURE:FORGOTTEN_BEAST_204:SILK*!CREATURE:FORGOTTEN_BEAST_213:SILK*!CREATURE:FORGOTTEN_BEAST_224:SILK*!CREATURE:FORGOTTEN_BEAST_235:SILK*!CREATURE:FORGOTTEN_BEAST_236:SILK*!CREATURE:FORGOTTEN_BEAST_239:SILK*!CREATURE:FORGOTTEN_BEAST_248:SILK*!CREATURE:FORGOTTEN_BEAST_275:SILK*!CREATURE:FORGOTTEN_BEAST_277:SILK*!CREATURE:FORGOTTEN_BEAST_281:SILK*!CREATURE:FORGOTTEN_BEAST_289:SILK*!CREATURE:FORGOTTEN_BEAST_290:SILK*!CREATURE:FORGOTTEN_BEAST_293:SILK*!CREATURE:FORGOTTEN_BEAST_297:SILK*!CREATURE:FORGOTTEN_BEAST_309:SILK*!CREATURE:FORGOTTEN_BEAST_311:SILK*!CREATURE:FORGOTTEN_BEAST_313:SILK*!CREATURE:FORGOTTEN_BEAST_326:SILK*!CREATURE:FORGOTTEN_BEAST_330:SILK*!CREATURE:FORGOTTEN_BEAST_341:SILK*!CREATURE:FORGOTTEN_BEAST_343:SILK*!CREATURE:FORGOTTEN_BEAST_357:SILK*!CREATURE:FORGOTTEN_BEAST_362:SILK*!CREATURE:FORGOTTEN_BEAST_371:SILK*!CREATURE:FORGOTTEN_BEAST_376:SILK*!CREATURE:FORGOTTEN_BEAST_379:SILK*!CREATURE:FORGOTTEN_BEAST_384:SILK*!CREATURE:FORGOTTEN_BEAST_397:SILK*!CREATURE:FORGOTTEN_BEAST_398:SILK*!CREATURE:FORGOTTEN_BEAST_403:SILK*!CREATURE:FORGOTTEN_BEAST_412:SILK*!CREATURE:FORGOTTEN_BEAST_426:SILK*!CREATURE:FORGOTTEN_BEAST_427:SILK*!CREATURE:FORGOTTEN_BEAST_432:SILK*!CREATURE:FORGOTTEN_BEAST_435:SILK*!CREATURE:FORGOTTEN_BEAST_438:SILK*!CREATURE:FORGOTTEN_BEAST_442:SILK*!CREATURE:FORGOTTEN_BEAST_456:SILK*!CREATURE:FORGOTTEN_BEAST_460:SILK*!CREATURE:FORGOTTEN_BEAST_465:SILK*!CREATURE:FORGOTTEN_BEAST_467:SILK*!CREATURE:FORGOTTEN_BEAST_472:SILK*!CREATURE:FORGOTTEN_BEAST_476:SILK*!CREATURE:FORGOTTEN_BEAST_486:SILK*!CREATURE:FORGOTTEN_BEAST_508:SILK*!CREATURE:FORGOTTEN_BEAST_549:SILK*!CREATURE:FORGOTTEN_BEAST_567:SILK*!CREATURE:FORGOTTEN_BEAST_568:SILK*!CREATURE:FORGOTTEN_BEAST_575:SILK*!CREATURE:FORGOTTEN_BEAST_580:SILK*!CREATURE:FORGOTTEN_BEAST_583:SILK*!CREATURE:FORGOTTEN_BEAST_588:SILK*!CREATURE:FORGOTTEN_BEAST_589:SILK*!CREATURE:FORGOTTEN_BEAST_600:SILK*!CREATURE:FORGOTTEN_BEAST_605:SILK*!CREATURE:FORGOTTEN_BEAST_606:SILK*!CREATURE:FORGOTTEN_BEAST_612:SILK*!CREATURE:FORGOTTEN_BEAST_613:SILK*!CREATURE:FORGOTTEN_BEAST_614:SILK*!CREATURE:FORGOTTEN_BEAST_630:SILK*!CREATURE:FORGOTTEN_BEAST_632:SILK*!CREATURE:FORGOTTEN_BEAST_639:SILK*!CREATURE:FORGOTTEN_BEAST_643:SILK*!CREATURE:FORGOTTEN_BEAST_650:SILK*!CREATURE:FORGOTTEN_BEAST_653:SILK*!CREATURE:FORGOTTEN_BEAST_661:SILK*!CREATURE:FORGOTTEN_BEAST_662:SILK*!CREATURE:FORGOTTEN_BEAST_676:SILK*!CREATURE:FORGOTTEN_BEAST_680:SILK*!CREATURE:FORGOTTEN_BEAST_697:SILK*!CREATURE:FORGOTTEN_BEAST_706:SILK*!CREATURE:FORGOTTEN_BEAST_720:SILK*!CREATURE:FORGOTTEN_BEAST_723:SILK*!CREATURE:FORGOTTEN_BEAST_730:SILK*!CREATURE:FORGOTTEN_BEAST_757:SILK*!CREATURE:FORGOTTEN_BEAST_764:SILK*!CREATURE:FORGOTTEN_BEAST_765:SILK*!CREATURE:FORGOTTEN_BEAST_768:SILK*!CREATURE:FORGOTTEN_BEAST_774:SILK*!CREATURE:FORGOTTEN_BEAST_791:SILK*!CREATURE:FORGOTTEN_BEAST_809:SILK*!CREATURE:FORGOTTEN_BEAST_814:SILK*!CREATURE:FORGOTTEN_BEAST_821:SILK*!CREATURE:FORGOTTEN_BEAST_827:SILK*!CREATURE:FORGOTTEN_BEAST_829:SILK*!CREATURE:FORGOTTEN_BEAST_843:SILK*!CREATURE:FORGOTTEN_BEAST_855:SILK*!CREATURE:FORGOTTEN_BEAST_863:SILK*CREATURE:TITAN_4:SILK*CREATURE:TITAN_9:SILK*CREATURE:TITAN_14:SILK*CREATURE:TITAN_15:SILK*CREATURE:TITAN_16:SILK*CREATURE:TITAN_21:SILK*CREATURE:TITAN_22:SILK*CREATURE:TITAN_23:SILK*CREATURE:DEMON_39:SILK*CREATURE:DEMON_42:SILK*CREATURE:DEMON_51:SILK2PLANT:FLAX:THREAD2PLANT:JUTE:THREAD2PLANT:HEMP:THREAD2PLANT:COTTON:THREAD2PLANT:RAMIE:THREAD2PLANT:KENAF:THREAD2PLANT:GRASS_TAIL_PIG:THREAD2PLANT:REED_ROPE:THREAD:CREATURE:SHEEP:HAIR:CREATURE:LLAMA:HAIR:CREATURE:ALPACA:HAIR:CREATURE:TROLL:HAIR:CREATURE:GNOLL:HAIRBINORGANIC:ADAMANTINE˜ ¨°¸À \ No newline at end of file +rH \ No newline at end of file diff --git a/data/stockpiles/category_coins.dfstock b/data/stockpiles/category_coins.dfstock index 4f17935dae690a5aa517f6459fb3bef185464a95..a48133ac400899723d0a7ba9616a9c311dcc5114 100644 GIT binary patch literal 4 LcmeZb5?}-X0aE~8 literal 7467 zcmZ`;xsv3#6`XP_Lej2Q$7qj;^5jlepEDjvqDgdt1P36io9*zI6d_0Enlfd|+)vvC zz?w=Dq}d7Z;T`!B57jUK{<8n0YK$#Kqw+7xnuh-_jV|`zUDut`XK5M9P20{9*f~kf zR^C;U^EVkzd6)Y?UF~L8ILCnNBy9Zox}#O)7`$Uli%vCt&HJR5d1rzB;rcB$?f-iH zvz4!%wBxgCSwY1Z=fx z%FE`zU6(Q~^3yma?T0IFjgw=j(Dzp`Q?Bm2YX32TE*2;ig_ZlC(q-3~Ms6*(#^zEB)PiCVt{O`%zy<>0PzrwS!7Ht5xu?|B zN}opaJO-S2Y?|SX8CM&tkZ)#kUKy*o%*e7lh*ESjUozdx!;2_p$7<0kcYxcyR?ONK zG@et(0|(QJ*-4o;rU<#>^K0W=dVWBI0ZPzWSmkmBPl?ufWd~=X3kjSXtp(H930rW^ zb732)m1sD`GOioeb`Hw@2XXi+3hZrlhfEH&7BR1xcVrW%!iXaiP>$6J@2z^%e zg3^I48twuUCD13Y4GcRkOVkCe%DWzRrWN|a)#fxDzV_huJ9qS}c#GkX*RQBtQcqE5 z2yj^>-mTY^AaVm#ashj(Rjh#WCH;;6Z4B{RE* z4mX|$q~WS5RB4z9*_t z7eSJ50&a3BAV(>Dt79d_l2EYHI1mzsMpNUDHWQ3I7~_x^FX#STI!dhVGlkIxl_B@@ zpsQ)mlm_}3sz4SXa{qJ(ul0$ADNgDDQOmA^5U|q%icw=KK=~-K6Ky~~E@~VUk}7y2 zj;-e;GcTYp@Qk6bV%uRM2uy+ox#dX0-buWY-8s6*B&e2WOp4UH!A2#lmY4)*Di>D3 z)K%C02on}4Ers$-Ek-@?u^04tU}=#xMqfOFXt+eV2Ub&u+oD>e?P4sjfRle&@HHV) zU*Mp5oVv?`S71)Z1&_R3GZkw?XI%UoafeveAsOB71=Ez;_*y`|=xj)(sl61@*?A>xHwT11aZxeX zgbpN0D^$G5zPn0eDKZR`e44F>C=9eK6CnWd35mn(1)peFA;P#$yG-U3ia2`>#cO{N zj>9W=7IRw-I$RKB-{qBpA`ntGQsGTa2=pOCQOJi1*Ky~MBMj*`Avx4AUd0sdSnQq+DlK)zJji_oVy_ zCUA-loPJ7B%T}>cXfT3LGPXt6HXUQGQ(5?wP6q{1FYgkyo*VSpLqtN{6|vFiTfFBK zR0I<-ZB!b^`j08Iw(rzjd}@JiSP~uAV3ZC@gr5L)9~`6#wyMi3v<~{3{mw-H=8sN! z^zFAGQl-pT@y4Gc!1Ln1_Eee9p}#sRVN6iS@`O?_e%GjtsJV*%>Wf>`rEj+561fqqP}ZZBfYO2f z292N6R=){1FplN8{MK2Hu|8t5XASn>jMEH21_K<&uq7JD0xq35;l~k*rIli?J$Nvd z`28;=mH6&CgWm!nu%j{f5swN8?=cjATcH&AaV#y5|Ir}%)wc*OGTv|+ChWL^-L7DF zE7<)C_OOCIu3%3q*k|sKi_f3dz?(JjxCY*?fp=@*{TleN20pHVPix>>M8{8j9brD# zsG{i$l!tB&!jBvH?FN3gf!}Z74;%R72L7~xf9CO-{J;6MwQ#ewaNJtB-CDTYTDad@ zc-UHa+*)|rTG)ylrKZ7+QgYmi{J0hQaVzrUR^-R6$d3;!eZeb>|0jTdbXi~F<2ro1 z4&SZA_v`S6Cy(p9pVr~e2_IuPX`e+r?X!rdeHQVw&mx}oS;W&mi+I{+5l{Op;%T2n zd?Vf!T?zZawWY2^d?Q{kZGHbnyx`gT{)d0gA38D3*;uRF%J>9kGLX$uT|Dd0nZAP9o=Mp3lH9ZJ-a)QsiJz)ie>0b>)m z4$%CI9&^kw#~yp^f9N0TLrSyrXyhIi-g!m7pU?ZeB>(o`PiH5x6uO?BTp8h4v(v(6 zMupi-*FLzmQgxWU47Qt{2v=D*JMpDees)|cQ(>{OYy3L9(nafZt!Ag9YUB1-UaBT2 zte4Ji>e(^A7PD7Mxb&a1%n7OD!k2}rW+%ah_@@zdfVND?RfB)C)3>c&L&>RcjD{np z(zQXBv*Tc`nH^`=uI9%ZYxDVeW3YECjSb}-A8Iv+()6RWuAZMXFm2}WUKulgWt0e| za`R)a&1!xksti9)q;V_Y$rR#hUX83vB321(~5!Z`f>e{+O z)LQ;kJ6rSb!kT>X(f&IuA|#eb+caYd^sJO&EjPNByeET|8@2e=UN180JqvtL&@tby zXmqhn-m%PxpzF5cb*PJsrC+?>@5Q3U@Af|%wLx*WS19@7_Y-k&`pT%qZzms&=r|~( zHfO`av{iD3RQ9RZPP3M2W02`T?2RB`2bdJQ#UJlKWd?BC4oUmXUhb?{+f<=n?}eH6 zRU691Clk?514SlsQ2}`|5u^fWjNUB%yjLNW6P1=*zzNW+H^QmKAE%#v0CxZ1$HqEz z4Z3wfF$iBqKmrXUghPXnplR#wnn8qq)<_|PQ=(f8R}P6&OHqh0QlFVtE#HV-Ehx!G zG}ms2Xo!NPZN$h@X3)qfe70X%@2B>YG%28%*TQKpH9RGx$`U#~$_J;Ym*VD>;*lNYT-V1+s5WA)pv3h#q!=1quX-V2 zEVS9K1|+@GZYL#!4SHxNrrH4dn;MFJWEui?qh)BJXZS)pd}UYN@@)&a-f)wG?)q@Z z>qxnB2m2}`~yV?0gSq{2TPIV(dCE48l zeiB#m7!|6-Bb_g9;B03XuN-XN3TA6LrI^dFlB8<9*hp7f+E{OW3uP_NQ z1mnRCn*X$K|FyO_oG!tZ9;_Z>s8sh9(NK3{u~l2&9O#j{u0@4L-DifZlxY5LIWS$H z2g>~$FyJsb7`%zd2Ut)p1}#&xN5WP0kY+MIY~T>-G#OBU8%9nN^c5hc8kFp#qr|#4 z(>Sf5GUR?bPA{iDQ&{k0ssd>M$^FGIBiBzf3~^Eih)RVL$8&aCfH5kZ3dS>FfDORM zMePp?lPW|YZkvcnMqZLaLA*1SY%zUE1T&E)y5&gH+JU?#t0lU~PNYVyarmRwO*%?k zZ4|CbBbG~FfYfJQk4KpFf$)~=h}3**IySqLUUf_@s={d&_Zm7}qTC&`smHV^57M?% zmQcXS-+hQRAyDtciSsyhyAP3pIUPHBkIUy&^|c`zJ3qI$**WH+7|=xMk+}X)K{&xqF@Wg-PYJ|S^9d%-7Km5X#- zr&Tud8buskaQd--5RS*ibrf?`b|wu7((h%d!3ZQNYNc^aCJFcf4;~y(iE$jFIr0nX zw<$T)FpYE6vLoXyA z1F$uT>542Z*euf_-m`7%x%x&IDL`61_>9XeCgAvuAPu%`+%VE+0Bv-bAR1I~qE!7X zrZmM@ncYR5s!?=}hb7__ythbxiFkv<7U^%=94C>YZw(^G26k~yR{1twPPmkSIfw~f zQm!+nt6l}wcctUAP{I@$99{?XVXHVL3=qMVtZUGe! z`1y0fFP;;A`JC{pi!=N9&Hek^#~&`vH+K&Y^6~cF)7>{0udnXk-#z@W`+4TS|L*Sb z{@cfkbM=p}@4kDw|Mvau!^JPE+rK~EJ-++?;o?j@KHa~&{rc(hgT_nNcsbU1`9b3) kYrNcRy!@c?pGQYO9v%I3^y25EPyTiE;@?Lv{&V!=f7onI82|tP diff --git a/data/stockpiles/category_food.dfstock b/data/stockpiles/category_food.dfstock index 9333d0c61..32829f677 100644 --- a/data/stockpiles/category_food.dfstock +++ b/data/stockpiles/category_food.dfstock @@ -1,18619 +1 @@ -©À- -PLANT:WORMY TENDRILS:STRUCTURAL -PLANT:EYEBALL:STRUCTURAL -CREATURE:TOAD:MUSCLE -CREATURE:TOAD:EYE -CREATURE:TOAD:BRAIN -CREATURE:TOAD:LUNG -CREATURE:TOAD:HEART -CREATURE:TOAD:LIVER -CREATURE:TOAD:GUT -CREATURE:TOAD:STOMACH -CREATURE:TOAD:GIZZARD -CREATURE:TOAD:PANCREAS -CREATURE:TOAD:SPLEEN -CREATURE:TOAD:KIDNEY -CREATURE:TOAD_MAN:MUSCLE -CREATURE:TOAD_MAN:EYE -CREATURE:TOAD_MAN:BRAIN -CREATURE:TOAD_MAN:LUNG -CREATURE:TOAD_MAN:HEART -CREATURE:TOAD_MAN:LIVER -CREATURE:TOAD_MAN:GUT -CREATURE:TOAD_MAN:STOMACH -CREATURE:TOAD_MAN:GIZZARD -CREATURE:TOAD_MAN:PANCREAS -CREATURE:TOAD_MAN:SPLEEN -CREATURE:TOAD_MAN:KIDNEY -CREATURE:GIANT_TOAD:MUSCLE -CREATURE:GIANT_TOAD:EYE -CREATURE:GIANT_TOAD:BRAIN -CREATURE:GIANT_TOAD:LUNG -CREATURE:GIANT_TOAD:HEART -CREATURE:GIANT_TOAD:LIVER -CREATURE:GIANT_TOAD:GUT -CREATURE:GIANT_TOAD:STOMACH -CREATURE:GIANT_TOAD:GIZZARD -CREATURE:GIANT_TOAD:PANCREAS -CREATURE:GIANT_TOAD:SPLEEN -CREATURE:GIANT_TOAD:KIDNEY -CREATURE:WORM:MUSCLE -CREATURE:WORM:EYE -CREATURE:WORM:BRAIN -CREATURE:WORM:LUNG -CREATURE:WORM:HEART -CREATURE:WORM:LIVER -CREATURE:WORM:GUT -CREATURE:WORM:STOMACH -CREATURE:WORM:GIZZARD -CREATURE:WORM:PANCREAS -CREATURE:WORM:SPLEEN -CREATURE:WORM:KIDNEY -CREATURE:WORM_MAN:MUSCLE -CREATURE:WORM_MAN:EYE -CREATURE:WORM_MAN:BRAIN -CREATURE:WORM_MAN:LUNG -CREATURE:WORM_MAN:HEART -CREATURE:WORM_MAN:LIVER -CREATURE:WORM_MAN:GUT -CREATURE:WORM_MAN:STOMACH -CREATURE:WORM_MAN:GIZZARD -CREATURE:WORM_MAN:PANCREAS -CREATURE:WORM_MAN:SPLEEN -CREATURE:WORM_MAN:KIDNEY -CREATURE:BIRD_BLUEJAY:MUSCLE -CREATURE:BIRD_BLUEJAY:EYE -CREATURE:BIRD_BLUEJAY:BRAIN -CREATURE:BIRD_BLUEJAY:LUNG -CREATURE:BIRD_BLUEJAY:HEART -CREATURE:BIRD_BLUEJAY:LIVER -CREATURE:BIRD_BLUEJAY:GUT -CREATURE:BIRD_BLUEJAY:STOMACH -CREATURE:BIRD_BLUEJAY:GIZZARD -CREATURE:BIRD_BLUEJAY:PANCREAS -CREATURE:BIRD_BLUEJAY:SPLEEN -CREATURE:BIRD_BLUEJAY:KIDNEY -CREATURE:BLUEJAY_MAN:MUSCLE -CREATURE:BLUEJAY_MAN:EYE -CREATURE:BLUEJAY_MAN:BRAIN -CREATURE:BLUEJAY_MAN:LUNG -CREATURE:BLUEJAY_MAN:HEART -CREATURE:BLUEJAY_MAN:LIVER -CREATURE:BLUEJAY_MAN:GUT -CREATURE:BLUEJAY_MAN:STOMACH -CREATURE:BLUEJAY_MAN:GIZZARD -CREATURE:BLUEJAY_MAN:PANCREAS -CREATURE:BLUEJAY_MAN:SPLEEN -CREATURE:BLUEJAY_MAN:KIDNEY -CREATURE:GIANT_BLUEJAY:MUSCLE -CREATURE:GIANT_BLUEJAY:EYE -CREATURE:GIANT_BLUEJAY:BRAIN -CREATURE:GIANT_BLUEJAY:LUNG -CREATURE:GIANT_BLUEJAY:HEART -CREATURE:GIANT_BLUEJAY:LIVER -CREATURE:GIANT_BLUEJAY:GUT -CREATURE:GIANT_BLUEJAY:STOMACH -CREATURE:GIANT_BLUEJAY:GIZZARD -CREATURE:GIANT_BLUEJAY:PANCREAS -CREATURE:GIANT_BLUEJAY:SPLEEN -CREATURE:GIANT_BLUEJAY:KIDNEY -CREATURE:BIRD_CARDINAL:MUSCLE -CREATURE:BIRD_CARDINAL:EYE -CREATURE:BIRD_CARDINAL:BRAIN -CREATURE:BIRD_CARDINAL:LUNG -CREATURE:BIRD_CARDINAL:HEART -CREATURE:BIRD_CARDINAL:LIVER -CREATURE:BIRD_CARDINAL:GUT -CREATURE:BIRD_CARDINAL:STOMACH -CREATURE:BIRD_CARDINAL:GIZZARD -CREATURE:BIRD_CARDINAL:PANCREAS -CREATURE:BIRD_CARDINAL:SPLEEN -CREATURE:BIRD_CARDINAL:KIDNEY -CREATURE:CARDINAL_MAN:MUSCLE -CREATURE:CARDINAL_MAN:EYE -CREATURE:CARDINAL_MAN:BRAIN -CREATURE:CARDINAL_MAN:LUNG -CREATURE:CARDINAL_MAN:HEART -CREATURE:CARDINAL_MAN:LIVER -CREATURE:CARDINAL_MAN:GUT -CREATURE:CARDINAL_MAN:STOMACH -CREATURE:CARDINAL_MAN:GIZZARD -CREATURE:CARDINAL_MAN:PANCREAS -CREATURE:CARDINAL_MAN:SPLEEN -CREATURE:CARDINAL_MAN:KIDNEY -CREATURE:GIANT_CARDINAL:MUSCLE -CREATURE:GIANT_CARDINAL:EYE -CREATURE:GIANT_CARDINAL:BRAIN -CREATURE:GIANT_CARDINAL:LUNG -CREATURE:GIANT_CARDINAL:HEART -CREATURE:GIANT_CARDINAL:LIVER -CREATURE:GIANT_CARDINAL:GUT -CREATURE:GIANT_CARDINAL:STOMACH -CREATURE:GIANT_CARDINAL:GIZZARD - CREATURE:GIANT_CARDINAL:PANCREAS -CREATURE:GIANT_CARDINAL:SPLEEN -CREATURE:GIANT_CARDINAL:KIDNEY -CREATURE:BIRD_GRACKLE:MUSCLE -CREATURE:BIRD_GRACKLE:EYE -CREATURE:BIRD_GRACKLE:BRAIN -CREATURE:BIRD_GRACKLE:LUNG -CREATURE:BIRD_GRACKLE:HEART -CREATURE:BIRD_GRACKLE:LIVER -CREATURE:BIRD_GRACKLE:GUT -CREATURE:BIRD_GRACKLE:STOMACH -CREATURE:BIRD_GRACKLE:GIZZARD -CREATURE:BIRD_GRACKLE:PANCREAS -CREATURE:BIRD_GRACKLE:SPLEEN -CREATURE:BIRD_GRACKLE:KIDNEY -CREATURE:GRACKLE_MAN:MUSCLE -CREATURE:GRACKLE_MAN:EYE -CREATURE:GRACKLE_MAN:BRAIN -CREATURE:GRACKLE_MAN:LUNG -CREATURE:GRACKLE_MAN:HEART -CREATURE:GRACKLE_MAN:LIVER -CREATURE:GRACKLE_MAN:GUT -CREATURE:GRACKLE_MAN:STOMACH -CREATURE:GRACKLE_MAN:GIZZARD -CREATURE:GRACKLE_MAN:PANCREAS -CREATURE:GRACKLE_MAN:SPLEEN -CREATURE:GRACKLE_MAN:KIDNEY -CREATURE:GIANT_GRACKLE:MUSCLE -CREATURE:GIANT_GRACKLE:EYE -CREATURE:GIANT_GRACKLE:BRAIN -CREATURE:GIANT_GRACKLE:LUNG -CREATURE:GIANT_GRACKLE:HEART -CREATURE:GIANT_GRACKLE:LIVER -CREATURE:GIANT_GRACKLE:GUT -CREATURE:GIANT_GRACKLE:STOMACH -CREATURE:GIANT_GRACKLE:GIZZARD -CREATURE:GIANT_GRACKLE:PANCREAS -CREATURE:GIANT_GRACKLE:SPLEEN -CREATURE:GIANT_GRACKLE:KIDNEY -CREATURE:BIRD_ORIOLE:MUSCLE -CREATURE:BIRD_ORIOLE:EYE -CREATURE:BIRD_ORIOLE:BRAIN -CREATURE:BIRD_ORIOLE:LUNG -CREATURE:BIRD_ORIOLE:HEART -CREATURE:BIRD_ORIOLE:LIVER -CREATURE:BIRD_ORIOLE:GUT -CREATURE:BIRD_ORIOLE:STOMACH -CREATURE:BIRD_ORIOLE:GIZZARD -CREATURE:BIRD_ORIOLE:PANCREAS -CREATURE:BIRD_ORIOLE:SPLEEN -CREATURE:BIRD_ORIOLE:KIDNEY -CREATURE:ORIOLE_MAN:MUSCLE -CREATURE:ORIOLE_MAN:EYE -CREATURE:ORIOLE_MAN:BRAIN -CREATURE:ORIOLE_MAN:LUNG -CREATURE:ORIOLE_MAN:HEART -CREATURE:ORIOLE_MAN:LIVER -CREATURE:ORIOLE_MAN:GUT -CREATURE:ORIOLE_MAN:STOMACH -CREATURE:ORIOLE_MAN:GIZZARD -CREATURE:ORIOLE_MAN:PANCREAS -CREATURE:ORIOLE_MAN:SPLEEN -CREATURE:ORIOLE_MAN:KIDNEY -CREATURE:GIANT_ORIOLE:MUSCLE -CREATURE:GIANT_ORIOLE:EYE -CREATURE:GIANT_ORIOLE:BRAIN -CREATURE:GIANT_ORIOLE:LUNG -CREATURE:GIANT_ORIOLE:HEART -CREATURE:GIANT_ORIOLE:LIVER -CREATURE:GIANT_ORIOLE:GUT -CREATURE:GIANT_ORIOLE:STOMACH -CREATURE:GIANT_ORIOLE:GIZZARD -CREATURE:GIANT_ORIOLE:PANCREAS -CREATURE:GIANT_ORIOLE:SPLEEN -CREATURE:GIANT_ORIOLE:KIDNEY -!CREATURE:BIRD_RW_BLACKBIRD:MUSCLE -CREATURE:BIRD_RW_BLACKBIRD:EYE - CREATURE:BIRD_RW_BLACKBIRD:BRAIN -CREATURE:BIRD_RW_BLACKBIRD:LUNG - CREATURE:BIRD_RW_BLACKBIRD:HEART - CREATURE:BIRD_RW_BLACKBIRD:LIVER -CREATURE:BIRD_RW_BLACKBIRD:GUT -"CREATURE:BIRD_RW_BLACKBIRD:STOMACH -"CREATURE:BIRD_RW_BLACKBIRD:GIZZARD -#CREATURE:BIRD_RW_BLACKBIRD:PANCREAS -!CREATURE:BIRD_RW_BLACKBIRD:SPLEEN -!CREATURE:BIRD_RW_BLACKBIRD:KIDNEY - CREATURE:RW_BLACKBIRD_MAN:MUSCLE -CREATURE:RW_BLACKBIRD_MAN:EYE -CREATURE:RW_BLACKBIRD_MAN:BRAIN -CREATURE:RW_BLACKBIRD_MAN:LUNG -CREATURE:RW_BLACKBIRD_MAN:HEART -CREATURE:RW_BLACKBIRD_MAN:LIVER -CREATURE:RW_BLACKBIRD_MAN:GUT -!CREATURE:RW_BLACKBIRD_MAN:STOMACH -!CREATURE:RW_BLACKBIRD_MAN:GIZZARD -"CREATURE:RW_BLACKBIRD_MAN:PANCREAS - CREATURE:RW_BLACKBIRD_MAN:SPLEEN - CREATURE:RW_BLACKBIRD_MAN:KIDNEY -"CREATURE:GIANT_RW_BLACKBIRD:MUSCLE -CREATURE:GIANT_RW_BLACKBIRD:EYE -!CREATURE:GIANT_RW_BLACKBIRD:BRAIN - CREATURE:GIANT_RW_BLACKBIRD:LUNG -!CREATURE:GIANT_RW_BLACKBIRD:HEART -!CREATURE:GIANT_RW_BLACKBIRD:LIVER -CREATURE:GIANT_RW_BLACKBIRD:GUT -#CREATURE:GIANT_RW_BLACKBIRD:STOMACH -#CREATURE:GIANT_RW_BLACKBIRD:GIZZARD -$CREATURE:GIANT_RW_BLACKBIRD:PANCREAS -"CREATURE:GIANT_RW_BLACKBIRD:SPLEEN -"CREATURE:GIANT_RW_BLACKBIRD:KIDNEY -CREATURE:BIRD_PENGUIN:MUSCLE -CREATURE:BIRD_PENGUIN:EYE -CREATURE:BIRD_PENGUIN:BRAIN -CREATURE:BIRD_PENGUIN:LUNG -CREATURE:BIRD_PENGUIN:HEART -CREATURE:BIRD_PENGUIN:LIVER -CREATURE:BIRD_PENGUIN:GUT -CREATURE:BIRD_PENGUIN:STOMACH -CREATURE:BIRD_PENGUIN:GIZZARD -CREATURE:BIRD_PENGUIN:PANCREAS -CREATURE:BIRD_PENGUIN:SPLEEN -CREATURE:BIRD_PENGUIN:KIDNEY -#CREATURE:BIRD_PENGUIN_LITTLE:MUSCLE - CREATURE:BIRD_PENGUIN_LITTLE:EYE -"CREATURE:BIRD_PENGUIN_LITTLE:BRAIN -!CREATURE:BIRD_PENGUIN_LITTLE:LUNG -"CREATURE:BIRD_PENGUIN_LITTLE:HEART -"CREATURE:BIRD_PENGUIN_LITTLE:LIVER - CREATURE:BIRD_PENGUIN_LITTLE:GUT -$CREATURE:BIRD_PENGUIN_LITTLE:STOMACH -$CREATURE:BIRD_PENGUIN_LITTLE:GIZZARD -%CREATURE:BIRD_PENGUIN_LITTLE:PANCREAS -#CREATURE:BIRD_PENGUIN_LITTLE:SPLEEN -#CREATURE:BIRD_PENGUIN_LITTLE:KIDNEY -$CREATURE:BIRD_PENGUIN_EMPEROR:MUSCLE -!CREATURE:BIRD_PENGUIN_EMPEROR:EYE -#CREATURE:BIRD_PENGUIN_EMPEROR:BRAIN -"CREATURE:BIRD_PENGUIN_EMPEROR:LUNG -#CREATURE:BIRD_PENGUIN_EMPEROR:HEART -#CREATURE:BIRD_PENGUIN_EMPEROR:LIVER -!CREATURE:BIRD_PENGUIN_EMPEROR:GUT -%CREATURE:BIRD_PENGUIN_EMPEROR:STOMACH -%CREATURE:BIRD_PENGUIN_EMPEROR:GIZZARD -&CREATURE:BIRD_PENGUIN_EMPEROR:PANCREAS -$CREATURE:BIRD_PENGUIN_EMPEROR:SPLEEN -$CREATURE:BIRD_PENGUIN_EMPEROR:KIDNEY -CREATURE:PENGUIN MAN:MUSCLE -CREATURE:PENGUIN MAN:EYE -CREATURE:PENGUIN MAN:BRAIN -CREATURE:PENGUIN MAN:LUNG -CREATURE:PENGUIN MAN:HEART -CREATURE:PENGUIN MAN:LIVER -CREATURE:PENGUIN MAN:GUT -CREATURE:PENGUIN MAN:STOMACH -CREATURE:PENGUIN MAN:GIZZARD -CREATURE:PENGUIN MAN:PANCREAS -CREATURE:PENGUIN MAN:SPLEEN -CREATURE:PENGUIN MAN:KIDNEY -"CREATURE:BIRD_PENGUIN_GIANT:MUSCLE -CREATURE:BIRD_PENGUIN_GIANT:EYE -!CREATURE:BIRD_PENGUIN_GIANT:BRAIN - CREATURE:BIRD_PENGUIN_GIANT:LUNG -!CREATURE:BIRD_PENGUIN_GIANT:HEART -!CREATURE:BIRD_PENGUIN_GIANT:LIVER -CREATURE:BIRD_PENGUIN_GIANT:GUT -#CREATURE:BIRD_PENGUIN_GIANT:STOMACH -#CREATURE:BIRD_PENGUIN_GIANT:GIZZARD -$CREATURE:BIRD_PENGUIN_GIANT:PANCREAS -"CREATURE:BIRD_PENGUIN_GIANT:SPLEEN -"CREATURE:BIRD_PENGUIN_GIANT:KIDNEY -%CREATURE:BIRD_FALCON_PEREGRINE:MUSCLE -"CREATURE:BIRD_FALCON_PEREGRINE:EYE -$CREATURE:BIRD_FALCON_PEREGRINE:BRAIN -#CREATURE:BIRD_FALCON_PEREGRINE:LUNG -$CREATURE:BIRD_FALCON_PEREGRINE:HEART -$CREATURE:BIRD_FALCON_PEREGRINE:LIVER -"CREATURE:BIRD_FALCON_PEREGRINE:GUT -&CREATURE:BIRD_FALCON_PEREGRINE:STOMACH -&CREATURE:BIRD_FALCON_PEREGRINE:GIZZARD -'CREATURE:BIRD_FALCON_PEREGRINE:PANCREAS -%CREATURE:BIRD_FALCON_PEREGRINE:SPLEEN -%CREATURE:BIRD_FALCON_PEREGRINE:KIDNEY -$CREATURE:PEREGRINE FALCON MAN:MUSCLE -!CREATURE:PEREGRINE FALCON MAN:EYE -#CREATURE:PEREGRINE FALCON MAN:BRAIN -"CREATURE:PEREGRINE FALCON MAN:LUNG -#CREATURE:PEREGRINE FALCON MAN:HEART -#CREATURE:PEREGRINE FALCON MAN:LIVER -!CREATURE:PEREGRINE FALCON MAN:GUT -%CREATURE:PEREGRINE FALCON MAN:STOMACH -%CREATURE:PEREGRINE FALCON MAN:GIZZARD -&CREATURE:PEREGRINE FALCON MAN:PANCREAS -$CREATURE:PEREGRINE FALCON MAN:SPLEEN -$CREATURE:PEREGRINE FALCON MAN:KIDNEY -&CREATURE:GIANT PEREGRINE FALCON:MUSCLE -#CREATURE:GIANT PEREGRINE FALCON:EYE -%CREATURE:GIANT PEREGRINE FALCON:BRAIN -$CREATURE:GIANT PEREGRINE FALCON:LUNG -%CREATURE:GIANT PEREGRINE FALCON:HEART -%CREATURE:GIANT PEREGRINE FALCON:LIVER -#CREATURE:GIANT PEREGRINE FALCON:GUT -'CREATURE:GIANT PEREGRINE FALCON:STOMACH -'CREATURE:GIANT PEREGRINE FALCON:GIZZARD -(CREATURE:GIANT PEREGRINE FALCON:PANCREAS -&CREATURE:GIANT PEREGRINE FALCON:SPLEEN -&CREATURE:GIANT PEREGRINE FALCON:KIDNEY -CREATURE:BIRD_KIWI:MUSCLE -CREATURE:BIRD_KIWI:EYE -CREATURE:BIRD_KIWI:BRAIN -CREATURE:BIRD_KIWI:LUNG -CREATURE:BIRD_KIWI:HEART -CREATURE:BIRD_KIWI:LIVER -CREATURE:BIRD_KIWI:GUT -CREATURE:BIRD_KIWI:STOMACH -CREATURE:BIRD_KIWI:GIZZARD -CREATURE:BIRD_KIWI:PANCREAS -CREATURE:BIRD_KIWI:SPLEEN -CREATURE:BIRD_KIWI:KIDNEY -CREATURE:KIWI MAN:MUSCLE -CREATURE:KIWI MAN:EYE -CREATURE:KIWI MAN:BRAIN -CREATURE:KIWI MAN:LUNG -CREATURE:KIWI MAN:HEART -CREATURE:KIWI MAN:LIVER -CREATURE:KIWI MAN:GUT -CREATURE:KIWI MAN:STOMACH -CREATURE:KIWI MAN:GIZZARD -CREATURE:KIWI MAN:PANCREAS -CREATURE:KIWI MAN:SPLEEN -CREATURE:KIWI MAN:KIDNEY -CREATURE:BIRD_KIWI_GIANT:MUSCLE -CREATURE:BIRD_KIWI_GIANT:EYE -CREATURE:BIRD_KIWI_GIANT:BRAIN -CREATURE:BIRD_KIWI_GIANT:LUNG -CREATURE:BIRD_KIWI_GIANT:HEART -CREATURE:BIRD_KIWI_GIANT:LIVER -CREATURE:BIRD_KIWI_GIANT:GUT - CREATURE:BIRD_KIWI_GIANT:STOMACH - CREATURE:BIRD_KIWI_GIANT:GIZZARD -!CREATURE:BIRD_KIWI_GIANT:PANCREAS -CREATURE:BIRD_KIWI_GIANT:SPLEEN -CREATURE:BIRD_KIWI_GIANT:KIDNEY -CREATURE:BIRD_OSTRICH:MUSCLE -CREATURE:BIRD_OSTRICH:EYE -CREATURE:BIRD_OSTRICH:BRAIN -CREATURE:BIRD_OSTRICH:LUNG -CREATURE:BIRD_OSTRICH:HEART -CREATURE:BIRD_OSTRICH:LIVER -CREATURE:BIRD_OSTRICH:GUT -CREATURE:BIRD_OSTRICH:STOMACH -CREATURE:BIRD_OSTRICH:GIZZARD -CREATURE:BIRD_OSTRICH:PANCREAS -CREATURE:BIRD_OSTRICH:SPLEEN -CREATURE:BIRD_OSTRICH:KIDNEY -CREATURE:OSTRICH MAN:MUSCLE -CREATURE:OSTRICH MAN:EYE -CREATURE:OSTRICH MAN:BRAIN -CREATURE:OSTRICH MAN:LUNG -CREATURE:OSTRICH MAN:HEART -CREATURE:OSTRICH MAN:LIVER -CREATURE:OSTRICH MAN:GUT -CREATURE:OSTRICH MAN:STOMACH -CREATURE:OSTRICH MAN:GIZZARD -CREATURE:OSTRICH MAN:PANCREAS -CREATURE:OSTRICH MAN:SPLEEN -CREATURE:OSTRICH MAN:KIDNEY -"CREATURE:BIRD_OSTRICH_GIANT:MUSCLE -CREATURE:BIRD_OSTRICH_GIANT:EYE -!CREATURE:BIRD_OSTRICH_GIANT:BRAIN - CREATURE:BIRD_OSTRICH_GIANT:LUNG -!CREATURE:BIRD_OSTRICH_GIANT:HEART -!CREATURE:BIRD_OSTRICH_GIANT:LIVER -CREATURE:BIRD_OSTRICH_GIANT:GUT -#CREATURE:BIRD_OSTRICH_GIANT:STOMACH -#CREATURE:BIRD_OSTRICH_GIANT:GIZZARD -$CREATURE:BIRD_OSTRICH_GIANT:PANCREAS -"CREATURE:BIRD_OSTRICH_GIANT:SPLEEN -"CREATURE:BIRD_OSTRICH_GIANT:KIDNEY -CREATURE:BIRD_CROW:MUSCLE -CREATURE:BIRD_CROW:EYE -CREATURE:BIRD_CROW:BRAIN -CREATURE:BIRD_CROW:LUNG -CREATURE:BIRD_CROW:HEART -CREATURE:BIRD_CROW:LIVER -CREATURE:BIRD_CROW:GUT -CREATURE:BIRD_CROW:STOMACH -CREATURE:BIRD_CROW:GIZZARD -CREATURE:BIRD_CROW:PANCREAS -CREATURE:BIRD_CROW:SPLEEN -CREATURE:BIRD_CROW:KIDNEY -CREATURE:CROW_MAN:MUSCLE -CREATURE:CROW_MAN:EYE -CREATURE:CROW_MAN:BRAIN -CREATURE:CROW_MAN:LUNG -CREATURE:CROW_MAN:HEART -CREATURE:CROW_MAN:LIVER -CREATURE:CROW_MAN:GUT -CREATURE:CROW_MAN:STOMACH -CREATURE:CROW_MAN:GIZZARD -CREATURE:CROW_MAN:PANCREAS -CREATURE:CROW_MAN:SPLEEN -CREATURE:CROW_MAN:KIDNEY -CREATURE:GIANT_CROW:MUSCLE -CREATURE:GIANT_CROW:EYE -CREATURE:GIANT_CROW:BRAIN -CREATURE:GIANT_CROW:LUNG -CREATURE:GIANT_CROW:HEART -CREATURE:GIANT_CROW:LIVER -CREATURE:GIANT_CROW:GUT -CREATURE:GIANT_CROW:STOMACH -CREATURE:GIANT_CROW:GIZZARD -CREATURE:GIANT_CROW:PANCREAS -CREATURE:GIANT_CROW:SPLEEN -CREATURE:GIANT_CROW:KIDNEY -CREATURE:BIRD_RAVEN:MUSCLE -CREATURE:BIRD_RAVEN:EYE -CREATURE:BIRD_RAVEN:BRAIN -CREATURE:BIRD_RAVEN:LUNG -CREATURE:BIRD_RAVEN:HEART -CREATURE:BIRD_RAVEN:LIVER -CREATURE:BIRD_RAVEN:GUT -CREATURE:BIRD_RAVEN:STOMACH -CREATURE:BIRD_RAVEN:GIZZARD -CREATURE:BIRD_RAVEN:PANCREAS -CREATURE:BIRD_RAVEN:SPLEEN -CREATURE:BIRD_RAVEN:KIDNEY -CREATURE:RAVEN_MAN:MUSCLE -CREATURE:RAVEN_MAN:EYE -CREATURE:RAVEN_MAN:BRAIN -CREATURE:RAVEN_MAN:LUNG -CREATURE:RAVEN_MAN:HEART -CREATURE:RAVEN_MAN:LIVER -CREATURE:RAVEN_MAN:GUT -CREATURE:RAVEN_MAN:STOMACH -CREATURE:RAVEN_MAN:GIZZARD -CREATURE:RAVEN_MAN:PANCREAS -CREATURE:RAVEN_MAN:SPLEEN -CREATURE:RAVEN_MAN:KIDNEY -CREATURE:GIANT_RAVEN:MUSCLE -CREATURE:GIANT_RAVEN:EYE -CREATURE:GIANT_RAVEN:BRAIN -CREATURE:GIANT_RAVEN:LUNG -CREATURE:GIANT_RAVEN:HEART -CREATURE:GIANT_RAVEN:LIVER -CREATURE:GIANT_RAVEN:GUT -CREATURE:GIANT_RAVEN:STOMACH -CREATURE:GIANT_RAVEN:GIZZARD -CREATURE:GIANT_RAVEN:PANCREAS -CREATURE:GIANT_RAVEN:SPLEEN -CREATURE:GIANT_RAVEN:KIDNEY -CREATURE:BIRD_CASSOWARY:MUSCLE -CREATURE:BIRD_CASSOWARY:EYE -CREATURE:BIRD_CASSOWARY:BRAIN -CREATURE:BIRD_CASSOWARY:LUNG -CREATURE:BIRD_CASSOWARY:HEART -CREATURE:BIRD_CASSOWARY:LIVER -CREATURE:BIRD_CASSOWARY:GUT -CREATURE:BIRD_CASSOWARY:STOMACH -CREATURE:BIRD_CASSOWARY:GIZZARD - CREATURE:BIRD_CASSOWARY:PANCREAS -CREATURE:BIRD_CASSOWARY:SPLEEN -CREATURE:BIRD_CASSOWARY:KIDNEY -CREATURE:CASSOWARY_MAN:MUSCLE -CREATURE:CASSOWARY_MAN:EYE -CREATURE:CASSOWARY_MAN:BRAIN -CREATURE:CASSOWARY_MAN:LUNG -CREATURE:CASSOWARY_MAN:HEART -CREATURE:CASSOWARY_MAN:LIVER -CREATURE:CASSOWARY_MAN:GUT -CREATURE:CASSOWARY_MAN:STOMACH -CREATURE:CASSOWARY_MAN:GIZZARD -CREATURE:CASSOWARY_MAN:PANCREAS -CREATURE:CASSOWARY_MAN:SPLEEN -CREATURE:CASSOWARY_MAN:KIDNEY -CREATURE:GIANT_CASSOWARY:MUSCLE -CREATURE:GIANT_CASSOWARY:EYE -CREATURE:GIANT_CASSOWARY:BRAIN -CREATURE:GIANT_CASSOWARY:LUNG -CREATURE:GIANT_CASSOWARY:HEART -CREATURE:GIANT_CASSOWARY:LIVER -CREATURE:GIANT_CASSOWARY:GUT - CREATURE:GIANT_CASSOWARY:STOMACH - CREATURE:GIANT_CASSOWARY:GIZZARD -!CREATURE:GIANT_CASSOWARY:PANCREAS -CREATURE:GIANT_CASSOWARY:SPLEEN -CREATURE:GIANT_CASSOWARY:KIDNEY -CREATURE:BIRD_KEA:MUSCLE -CREATURE:BIRD_KEA:EYE -CREATURE:BIRD_KEA:BRAIN -CREATURE:BIRD_KEA:LUNG -CREATURE:BIRD_KEA:HEART -CREATURE:BIRD_KEA:LIVER -CREATURE:BIRD_KEA:GUT -CREATURE:BIRD_KEA:STOMACH -CREATURE:BIRD_KEA:GIZZARD -CREATURE:BIRD_KEA:PANCREAS -CREATURE:BIRD_KEA:SPLEEN -CREATURE:BIRD_KEA:KIDNEY -CREATURE:KEA_MAN:MUSCLE -CREATURE:KEA_MAN:EYE -CREATURE:KEA_MAN:BRAIN -CREATURE:KEA_MAN:LUNG -CREATURE:KEA_MAN:HEART -CREATURE:KEA_MAN:LIVER -CREATURE:KEA_MAN:GUT -CREATURE:KEA_MAN:STOMACH -CREATURE:KEA_MAN:GIZZARD -CREATURE:KEA_MAN:PANCREAS -CREATURE:KEA_MAN:SPLEEN -CREATURE:KEA_MAN:KIDNEY -CREATURE:GIANT_KEA:MUSCLE -CREATURE:GIANT_KEA:EYE -CREATURE:GIANT_KEA:BRAIN -CREATURE:GIANT_KEA:LUNG -CREATURE:GIANT_KEA:HEART -CREATURE:GIANT_KEA:LIVER -CREATURE:GIANT_KEA:GUT -CREATURE:GIANT_KEA:STOMACH -CREATURE:GIANT_KEA:GIZZARD -CREATURE:GIANT_KEA:PANCREAS -CREATURE:GIANT_KEA:SPLEEN -CREATURE:GIANT_KEA:KIDNEY -CREATURE:BIRD_OWL_SNOWY:MUSCLE -CREATURE:BIRD_OWL_SNOWY:EYE -CREATURE:BIRD_OWL_SNOWY:BRAIN -CREATURE:BIRD_OWL_SNOWY:LUNG -CREATURE:BIRD_OWL_SNOWY:HEART -CREATURE:BIRD_OWL_SNOWY:LIVER -CREATURE:BIRD_OWL_SNOWY:GUT -CREATURE:BIRD_OWL_SNOWY:STOMACH -CREATURE:BIRD_OWL_SNOWY:GIZZARD - CREATURE:BIRD_OWL_SNOWY:PANCREAS -CREATURE:BIRD_OWL_SNOWY:SPLEEN -CREATURE:BIRD_OWL_SNOWY:KIDNEY -CREATURE:SNOWY_OWL_MAN:MUSCLE -CREATURE:SNOWY_OWL_MAN:EYE -CREATURE:SNOWY_OWL_MAN:BRAIN -CREATURE:SNOWY_OWL_MAN:LUNG -CREATURE:SNOWY_OWL_MAN:HEART -CREATURE:SNOWY_OWL_MAN:LIVER -CREATURE:SNOWY_OWL_MAN:GUT -CREATURE:SNOWY_OWL_MAN:STOMACH -CREATURE:SNOWY_OWL_MAN:GIZZARD -CREATURE:SNOWY_OWL_MAN:PANCREAS -CREATURE:SNOWY_OWL_MAN:SPLEEN -CREATURE:SNOWY_OWL_MAN:KIDNEY -CREATURE:GIANT_SNOWY_OWL:MUSCLE -CREATURE:GIANT_SNOWY_OWL:EYE -CREATURE:GIANT_SNOWY_OWL:BRAIN -CREATURE:GIANT_SNOWY_OWL:LUNG -CREATURE:GIANT_SNOWY_OWL:HEART -CREATURE:GIANT_SNOWY_OWL:LIVER -CREATURE:GIANT_SNOWY_OWL:GUT - CREATURE:GIANT_SNOWY_OWL:STOMACH - CREATURE:GIANT_SNOWY_OWL:GIZZARD -!CREATURE:GIANT_SNOWY_OWL:PANCREAS -CREATURE:GIANT_SNOWY_OWL:SPLEEN -CREATURE:GIANT_SNOWY_OWL:KIDNEY -CREATURE:SPARROW:MUSCLE -CREATURE:SPARROW:EYE -CREATURE:SPARROW:BRAIN -CREATURE:SPARROW:LUNG -CREATURE:SPARROW:HEART -CREATURE:SPARROW:LIVER -CREATURE:SPARROW:GUT -CREATURE:SPARROW:STOMACH -CREATURE:SPARROW:GIZZARD -CREATURE:SPARROW:PANCREAS -CREATURE:SPARROW:SPLEEN -CREATURE:SPARROW:KIDNEY -CREATURE:SPARROW_MAN:MUSCLE -CREATURE:SPARROW_MAN:EYE -CREATURE:SPARROW_MAN:BRAIN -CREATURE:SPARROW_MAN:LUNG -CREATURE:SPARROW_MAN:HEART -CREATURE:SPARROW_MAN:LIVER -CREATURE:SPARROW_MAN:GUT -CREATURE:SPARROW_MAN:STOMACH -CREATURE:SPARROW_MAN:GIZZARD -CREATURE:SPARROW_MAN:PANCREAS -CREATURE:SPARROW_MAN:SPLEEN -CREATURE:SPARROW_MAN:KIDNEY -CREATURE:GIANT_SPARROW:MUSCLE -CREATURE:GIANT_SPARROW:EYE -CREATURE:GIANT_SPARROW:BRAIN -CREATURE:GIANT_SPARROW:LUNG -CREATURE:GIANT_SPARROW:HEART -CREATURE:GIANT_SPARROW:LIVER -CREATURE:GIANT_SPARROW:GUT -CREATURE:GIANT_SPARROW:STOMACH -CREATURE:GIANT_SPARROW:GIZZARD -CREATURE:GIANT_SPARROW:PANCREAS -CREATURE:GIANT_SPARROW:SPLEEN -CREATURE:GIANT_SPARROW:KIDNEY - CREATURE:BIRD_STORK_WHITE:MUSCLE -CREATURE:BIRD_STORK_WHITE:EYE -CREATURE:BIRD_STORK_WHITE:BRAIN -CREATURE:BIRD_STORK_WHITE:LUNG -CREATURE:BIRD_STORK_WHITE:HEART -CREATURE:BIRD_STORK_WHITE:LIVER -CREATURE:BIRD_STORK_WHITE:GUT -!CREATURE:BIRD_STORK_WHITE:STOMACH -!CREATURE:BIRD_STORK_WHITE:GIZZARD -"CREATURE:BIRD_STORK_WHITE:PANCREAS - CREATURE:BIRD_STORK_WHITE:SPLEEN - CREATURE:BIRD_STORK_WHITE:KIDNEY -CREATURE:WHITE_STORK_MAN:MUSCLE -CREATURE:WHITE_STORK_MAN:EYE -CREATURE:WHITE_STORK_MAN:BRAIN -CREATURE:WHITE_STORK_MAN:LUNG -CREATURE:WHITE_STORK_MAN:HEART -CREATURE:WHITE_STORK_MAN:LIVER -CREATURE:WHITE_STORK_MAN:GUT - CREATURE:WHITE_STORK_MAN:STOMACH - CREATURE:WHITE_STORK_MAN:GIZZARD -!CREATURE:WHITE_STORK_MAN:PANCREAS -CREATURE:WHITE_STORK_MAN:SPLEEN -CREATURE:WHITE_STORK_MAN:KIDNEY -!CREATURE:GIANT_WHITE_STORK:MUSCLE -CREATURE:GIANT_WHITE_STORK:EYE - CREATURE:GIANT_WHITE_STORK:BRAIN -CREATURE:GIANT_WHITE_STORK:LUNG - CREATURE:GIANT_WHITE_STORK:HEART - CREATURE:GIANT_WHITE_STORK:LIVER -CREATURE:GIANT_WHITE_STORK:GUT -"CREATURE:GIANT_WHITE_STORK:STOMACH -"CREATURE:GIANT_WHITE_STORK:GIZZARD -#CREATURE:GIANT_WHITE_STORK:PANCREAS -!CREATURE:GIANT_WHITE_STORK:SPLEEN -!CREATURE:GIANT_WHITE_STORK:KIDNEY -CREATURE:BIRD_LOON:MUSCLE -CREATURE:BIRD_LOON:EYE -CREATURE:BIRD_LOON:BRAIN -CREATURE:BIRD_LOON:LUNG -CREATURE:BIRD_LOON:HEART -CREATURE:BIRD_LOON:LIVER -CREATURE:BIRD_LOON:GUT -CREATURE:BIRD_LOON:STOMACH -CREATURE:BIRD_LOON:GIZZARD -CREATURE:BIRD_LOON:PANCREAS -CREATURE:BIRD_LOON:SPLEEN -CREATURE:BIRD_LOON:KIDNEY -CREATURE:LOON_MAN:MUSCLE -CREATURE:LOON_MAN:EYE -CREATURE:LOON_MAN:BRAIN -CREATURE:LOON_MAN:LUNG -CREATURE:LOON_MAN:HEART -CREATURE:LOON_MAN:LIVER -CREATURE:LOON_MAN:GUT -CREATURE:LOON_MAN:STOMACH -CREATURE:LOON_MAN:GIZZARD -CREATURE:LOON_MAN:PANCREAS -CREATURE:LOON_MAN:SPLEEN -CREATURE:LOON_MAN:KIDNEY -CREATURE:GIANT_LOON:MUSCLE -CREATURE:GIANT_LOON:EYE -CREATURE:GIANT_LOON:BRAIN -CREATURE:GIANT_LOON:LUNG -CREATURE:GIANT_LOON:HEART -CREATURE:GIANT_LOON:LIVER -CREATURE:GIANT_LOON:GUT -CREATURE:GIANT_LOON:STOMACH -CREATURE:GIANT_LOON:GIZZARD -CREATURE:GIANT_LOON:PANCREAS -CREATURE:GIANT_LOON:SPLEEN -CREATURE:GIANT_LOON:KIDNEY -CREATURE:BIRD_OWL_BARN:MUSCLE -CREATURE:BIRD_OWL_BARN:EYE -CREATURE:BIRD_OWL_BARN:BRAIN -CREATURE:BIRD_OWL_BARN:LUNG -CREATURE:BIRD_OWL_BARN:HEART -CREATURE:BIRD_OWL_BARN:LIVER -CREATURE:BIRD_OWL_BARN:GUT -CREATURE:BIRD_OWL_BARN:STOMACH -CREATURE:BIRD_OWL_BARN:GIZZARD -CREATURE:BIRD_OWL_BARN:PANCREAS -CREATURE:BIRD_OWL_BARN:SPLEEN -CREATURE:BIRD_OWL_BARN:KIDNEY -CREATURE:BARN_OWL_MAN:MUSCLE -CREATURE:BARN_OWL_MAN:EYE -CREATURE:BARN_OWL_MAN:BRAIN -CREATURE:BARN_OWL_MAN:LUNG -CREATURE:BARN_OWL_MAN:HEART -CREATURE:BARN_OWL_MAN:LIVER -CREATURE:BARN_OWL_MAN:GUT -CREATURE:BARN_OWL_MAN:STOMACH -CREATURE:BARN_OWL_MAN:GIZZARD -CREATURE:BARN_OWL_MAN:PANCREAS -CREATURE:BARN_OWL_MAN:SPLEEN -CREATURE:BARN_OWL_MAN:KIDNEY -CREATURE:GIANT_BARN_OWL:MUSCLE -CREATURE:GIANT_BARN_OWL:EYE -CREATURE:GIANT_BARN_OWL:BRAIN -CREATURE:GIANT_BARN_OWL:LUNG -CREATURE:GIANT_BARN_OWL:HEART -CREATURE:GIANT_BARN_OWL:LIVER -CREATURE:GIANT_BARN_OWL:GUT -CREATURE:GIANT_BARN_OWL:STOMACH -CREATURE:GIANT_BARN_OWL:GIZZARD - CREATURE:GIANT_BARN_OWL:PANCREAS -CREATURE:GIANT_BARN_OWL:SPLEEN -CREATURE:GIANT_BARN_OWL:KIDNEY -CREATURE:BIRD_PARAKEET:MUSCLE -CREATURE:BIRD_PARAKEET:EYE -CREATURE:BIRD_PARAKEET:BRAIN -CREATURE:BIRD_PARAKEET:LUNG -CREATURE:BIRD_PARAKEET:HEART -CREATURE:BIRD_PARAKEET:LIVER -CREATURE:BIRD_PARAKEET:GUT -CREATURE:BIRD_PARAKEET:STOMACH -CREATURE:BIRD_PARAKEET:GIZZARD -CREATURE:BIRD_PARAKEET:PANCREAS -CREATURE:BIRD_PARAKEET:SPLEEN -CREATURE:BIRD_PARAKEET:KIDNEY -CREATURE:PARAKEET_MAN:MUSCLE -CREATURE:PARAKEET_MAN:EYE -CREATURE:PARAKEET_MAN:BRAIN -CREATURE:PARAKEET_MAN:LUNG -CREATURE:PARAKEET_MAN:HEART -CREATURE:PARAKEET_MAN:LIVER -CREATURE:PARAKEET_MAN:GUT -CREATURE:PARAKEET_MAN:STOMACH -CREATURE:PARAKEET_MAN:GIZZARD -CREATURE:PARAKEET_MAN:PANCREAS -CREATURE:PARAKEET_MAN:SPLEEN -CREATURE:PARAKEET_MAN:KIDNEY -CREATURE:GIANT_PARAKEET:MUSCLE -CREATURE:GIANT_PARAKEET:EYE -CREATURE:GIANT_PARAKEET:BRAIN -CREATURE:GIANT_PARAKEET:LUNG -CREATURE:GIANT_PARAKEET:HEART -CREATURE:GIANT_PARAKEET:LIVER -CREATURE:GIANT_PARAKEET:GUT -CREATURE:GIANT_PARAKEET:STOMACH -CREATURE:GIANT_PARAKEET:GIZZARD - CREATURE:GIANT_PARAKEET:PANCREAS -CREATURE:GIANT_PARAKEET:SPLEEN -CREATURE:GIANT_PARAKEET:KIDNEY -CREATURE:BIRD_KAKAPO:MUSCLE -CREATURE:BIRD_KAKAPO:EYE -CREATURE:BIRD_KAKAPO:BRAIN -CREATURE:BIRD_KAKAPO:LUNG -CREATURE:BIRD_KAKAPO:HEART -CREATURE:BIRD_KAKAPO:LIVER -CREATURE:BIRD_KAKAPO:GUT -CREATURE:BIRD_KAKAPO:STOMACH -CREATURE:BIRD_KAKAPO:GIZZARD -CREATURE:BIRD_KAKAPO:PANCREAS -CREATURE:BIRD_KAKAPO:SPLEEN -CREATURE:BIRD_KAKAPO:KIDNEY -CREATURE:KAKAPO_MAN:MUSCLE -CREATURE:KAKAPO_MAN:EYE -CREATURE:KAKAPO_MAN:BRAIN -CREATURE:KAKAPO_MAN:LUNG -CREATURE:KAKAPO_MAN:HEART -CREATURE:KAKAPO_MAN:LIVER -CREATURE:KAKAPO_MAN:GUT -CREATURE:KAKAPO_MAN:STOMACH -CREATURE:KAKAPO_MAN:GIZZARD -CREATURE:KAKAPO_MAN:PANCREAS -CREATURE:KAKAPO_MAN:SPLEEN -CREATURE:KAKAPO_MAN:KIDNEY -CREATURE:GIANT_KAKAPO:MUSCLE -CREATURE:GIANT_KAKAPO:EYE -CREATURE:GIANT_KAKAPO:BRAIN -CREATURE:GIANT_KAKAPO:LUNG -CREATURE:GIANT_KAKAPO:HEART -CREATURE:GIANT_KAKAPO:LIVER -CREATURE:GIANT_KAKAPO:GUT -CREATURE:GIANT_KAKAPO:STOMACH -CREATURE:GIANT_KAKAPO:GIZZARD -CREATURE:GIANT_KAKAPO:PANCREAS -CREATURE:GIANT_KAKAPO:SPLEEN -CREATURE:GIANT_KAKAPO:KIDNEY - CREATURE:BIRD_PARROT_GREY:MUSCLE -CREATURE:BIRD_PARROT_GREY:EYE -CREATURE:BIRD_PARROT_GREY:BRAIN -CREATURE:BIRD_PARROT_GREY:LUNG -CREATURE:BIRD_PARROT_GREY:HEART -CREATURE:BIRD_PARROT_GREY:LIVER -CREATURE:BIRD_PARROT_GREY:GUT -!CREATURE:BIRD_PARROT_GREY:STOMACH -!CREATURE:BIRD_PARROT_GREY:GIZZARD -"CREATURE:BIRD_PARROT_GREY:PANCREAS - CREATURE:BIRD_PARROT_GREY:SPLEEN - CREATURE:BIRD_PARROT_GREY:KIDNEY -CREATURE:GREY_PARROT_MAN:MUSCLE -CREATURE:GREY_PARROT_MAN:EYE -CREATURE:GREY_PARROT_MAN:BRAIN -CREATURE:GREY_PARROT_MAN:LUNG -CREATURE:GREY_PARROT_MAN:HEART -CREATURE:GREY_PARROT_MAN:LIVER -CREATURE:GREY_PARROT_MAN:GUT - CREATURE:GREY_PARROT_MAN:STOMACH - CREATURE:GREY_PARROT_MAN:GIZZARD -!CREATURE:GREY_PARROT_MAN:PANCREAS -CREATURE:GREY_PARROT_MAN:SPLEEN -CREATURE:GREY_PARROT_MAN:KIDNEY -!CREATURE:GIANT_GREY_PARROT:MUSCLE -CREATURE:GIANT_GREY_PARROT:EYE - CREATURE:GIANT_GREY_PARROT:BRAIN -CREATURE:GIANT_GREY_PARROT:LUNG - CREATURE:GIANT_GREY_PARROT:HEART - CREATURE:GIANT_GREY_PARROT:LIVER -CREATURE:GIANT_GREY_PARROT:GUT -"CREATURE:GIANT_GREY_PARROT:STOMACH -"CREATURE:GIANT_GREY_PARROT:GIZZARD -#CREATURE:GIANT_GREY_PARROT:PANCREAS -!CREATURE:GIANT_GREY_PARROT:SPLEEN -!CREATURE:GIANT_GREY_PARROT:KIDNEY -CREATURE:BIRD_PUFFIN:MUSCLE -CREATURE:BIRD_PUFFIN:EYE -CREATURE:BIRD_PUFFIN:BRAIN -CREATURE:BIRD_PUFFIN:LUNG -CREATURE:BIRD_PUFFIN:HEART -CREATURE:BIRD_PUFFIN:LIVER -CREATURE:BIRD_PUFFIN:GUT -CREATURE:BIRD_PUFFIN:STOMACH -CREATURE:BIRD_PUFFIN:GIZZARD -CREATURE:BIRD_PUFFIN:PANCREAS -CREATURE:BIRD_PUFFIN:SPLEEN -CREATURE:BIRD_PUFFIN:KIDNEY -CREATURE:PUFFIN_MAN:MUSCLE -CREATURE:PUFFIN_MAN:EYE -CREATURE:PUFFIN_MAN:BRAIN -CREATURE:PUFFIN_MAN:LUNG -CREATURE:PUFFIN_MAN:HEART -CREATURE:PUFFIN_MAN:LIVER -CREATURE:PUFFIN_MAN:GUT -CREATURE:PUFFIN_MAN:STOMACH -CREATURE:PUFFIN_MAN:GIZZARD -CREATURE:PUFFIN_MAN:PANCREAS -CREATURE:PUFFIN_MAN:SPLEEN -CREATURE:PUFFIN_MAN:KIDNEY -CREATURE:GIANT_PUFFIN:MUSCLE -CREATURE:GIANT_PUFFIN:EYE -CREATURE:GIANT_PUFFIN:BRAIN -CREATURE:GIANT_PUFFIN:LUNG -CREATURE:GIANT_PUFFIN:HEART -CREATURE:GIANT_PUFFIN:LIVER -CREATURE:GIANT_PUFFIN:GUT -CREATURE:GIANT_PUFFIN:STOMACH -CREATURE:GIANT_PUFFIN:GIZZARD -CREATURE:GIANT_PUFFIN:PANCREAS -CREATURE:GIANT_PUFFIN:SPLEEN -CREATURE:GIANT_PUFFIN:KIDNEY -CREATURE:BIRD_SWAN:MUSCLE -CREATURE:BIRD_SWAN:EYE -CREATURE:BIRD_SWAN:BRAIN -CREATURE:BIRD_SWAN:LUNG -CREATURE:BIRD_SWAN:HEART -CREATURE:BIRD_SWAN:LIVER -CREATURE:BIRD_SWAN:GUT -CREATURE:BIRD_SWAN:STOMACH -CREATURE:BIRD_SWAN:GIZZARD -CREATURE:BIRD_SWAN:PANCREAS -CREATURE:BIRD_SWAN:SPLEEN -CREATURE:BIRD_SWAN:KIDNEY -CREATURE:SWAN_MAN:MUSCLE -CREATURE:SWAN_MAN:EYE -CREATURE:SWAN_MAN:BRAIN -CREATURE:SWAN_MAN:LUNG -CREATURE:SWAN_MAN:HEART -CREATURE:SWAN_MAN:LIVER -CREATURE:SWAN_MAN:GUT -CREATURE:SWAN_MAN:STOMACH -CREATURE:SWAN_MAN:GIZZARD -CREATURE:SWAN_MAN:PANCREAS -CREATURE:SWAN_MAN:SPLEEN -CREATURE:SWAN_MAN:KIDNEY -CREATURE:GIANT_SWAN:MUSCLE -CREATURE:GIANT_SWAN:EYE -CREATURE:GIANT_SWAN:BRAIN -CREATURE:GIANT_SWAN:LUNG -CREATURE:GIANT_SWAN:HEART -CREATURE:GIANT_SWAN:LIVER -CREATURE:GIANT_SWAN:GUT -CREATURE:GIANT_SWAN:STOMACH -CREATURE:GIANT_SWAN:GIZZARD -CREATURE:GIANT_SWAN:PANCREAS -CREATURE:GIANT_SWAN:SPLEEN -CREATURE:GIANT_SWAN:KIDNEY -CREATURE:BIRD_LORIKEET:MUSCLE -CREATURE:BIRD_LORIKEET:EYE -CREATURE:BIRD_LORIKEET:BRAIN -CREATURE:BIRD_LORIKEET:LUNG -CREATURE:BIRD_LORIKEET:HEART -CREATURE:BIRD_LORIKEET:LIVER -CREATURE:BIRD_LORIKEET:GUT -CREATURE:BIRD_LORIKEET:STOMACH -CREATURE:BIRD_LORIKEET:GIZZARD -CREATURE:BIRD_LORIKEET:PANCREAS -CREATURE:BIRD_LORIKEET:SPLEEN -CREATURE:BIRD_LORIKEET:KIDNEY -CREATURE:LORIKEET_MAN:MUSCLE -CREATURE:LORIKEET_MAN:EYE -CREATURE:LORIKEET_MAN:BRAIN -CREATURE:LORIKEET_MAN:LUNG -CREATURE:LORIKEET_MAN:HEART -CREATURE:LORIKEET_MAN:LIVER -CREATURE:LORIKEET_MAN:GUT -CREATURE:LORIKEET_MAN:STOMACH -CREATURE:LORIKEET_MAN:GIZZARD -CREATURE:LORIKEET_MAN:PANCREAS -CREATURE:LORIKEET_MAN:SPLEEN -CREATURE:LORIKEET_MAN:KIDNEY -CREATURE:GIANT_LORIKEET:MUSCLE -CREATURE:GIANT_LORIKEET:EYE -CREATURE:GIANT_LORIKEET:BRAIN -CREATURE:GIANT_LORIKEET:LUNG -CREATURE:GIANT_LORIKEET:HEART -CREATURE:GIANT_LORIKEET:LIVER -CREATURE:GIANT_LORIKEET:GUT -CREATURE:GIANT_LORIKEET:STOMACH -CREATURE:GIANT_LORIKEET:GIZZARD - CREATURE:GIANT_LORIKEET:PANCREAS -CREATURE:GIANT_LORIKEET:SPLEEN -CREATURE:GIANT_LORIKEET:KIDNEY -CREATURE:BIRD_WREN:MUSCLE -CREATURE:BIRD_WREN:EYE -CREATURE:BIRD_WREN:BRAIN -CREATURE:BIRD_WREN:LUNG -CREATURE:BIRD_WREN:HEART -CREATURE:BIRD_WREN:LIVER -CREATURE:BIRD_WREN:GUT -CREATURE:BIRD_WREN:STOMACH -CREATURE:BIRD_WREN:GIZZARD -CREATURE:BIRD_WREN:PANCREAS -CREATURE:BIRD_WREN:SPLEEN -CREATURE:BIRD_WREN:KIDNEY -CREATURE:WREN_MAN:MUSCLE -CREATURE:WREN_MAN:EYE -CREATURE:WREN_MAN:BRAIN -CREATURE:WREN_MAN:LUNG -CREATURE:WREN_MAN:HEART -CREATURE:WREN_MAN:LIVER -CREATURE:WREN_MAN:GUT -CREATURE:WREN_MAN:STOMACH -CREATURE:WREN_MAN:GIZZARD -CREATURE:WREN_MAN:PANCREAS -CREATURE:WREN_MAN:SPLEEN -CREATURE:WREN_MAN:KIDNEY -CREATURE:GIANT_WREN:MUSCLE -CREATURE:GIANT_WREN:EYE -CREATURE:GIANT_WREN:BRAIN -CREATURE:GIANT_WREN:LUNG -CREATURE:GIANT_WREN:HEART -CREATURE:GIANT_WREN:LIVER -CREATURE:GIANT_WREN:GUT -CREATURE:GIANT_WREN:STOMACH -CREATURE:GIANT_WREN:GIZZARD -CREATURE:GIANT_WREN:PANCREAS -CREATURE:GIANT_WREN:SPLEEN -CREATURE:GIANT_WREN:KIDNEY -CREATURE:BIRD_OSPREY:MUSCLE -CREATURE:BIRD_OSPREY:EYE -CREATURE:BIRD_OSPREY:BRAIN -CREATURE:BIRD_OSPREY:LUNG -CREATURE:BIRD_OSPREY:HEART -CREATURE:BIRD_OSPREY:LIVER -CREATURE:BIRD_OSPREY:GUT -CREATURE:BIRD_OSPREY:STOMACH -CREATURE:BIRD_OSPREY:GIZZARD -CREATURE:BIRD_OSPREY:PANCREAS -CREATURE:BIRD_OSPREY:SPLEEN -CREATURE:BIRD_OSPREY:KIDNEY -CREATURE:OSPREY_MAN:MUSCLE -CREATURE:OSPREY_MAN:EYE -CREATURE:OSPREY_MAN:BRAIN -CREATURE:OSPREY_MAN:LUNG -CREATURE:OSPREY_MAN:HEART -CREATURE:OSPREY_MAN:LIVER -CREATURE:OSPREY_MAN:GUT -CREATURE:OSPREY_MAN:STOMACH -CREATURE:OSPREY_MAN:GIZZARD -CREATURE:OSPREY_MAN:PANCREAS -CREATURE:OSPREY_MAN:SPLEEN -CREATURE:OSPREY_MAN:KIDNEY -CREATURE:GIANT_OSPREY:MUSCLE -CREATURE:GIANT_OSPREY:EYE -CREATURE:GIANT_OSPREY:BRAIN -CREATURE:GIANT_OSPREY:LUNG -CREATURE:GIANT_OSPREY:HEART -CREATURE:GIANT_OSPREY:LIVER -CREATURE:GIANT_OSPREY:GUT -CREATURE:GIANT_OSPREY:STOMACH -CREATURE:GIANT_OSPREY:GIZZARD -CREATURE:GIANT_OSPREY:PANCREAS -CREATURE:GIANT_OSPREY:SPLEEN -CREATURE:GIANT_OSPREY:KIDNEY -CREATURE:BIRD_EMU:MUSCLE -CREATURE:BIRD_EMU:EYE -CREATURE:BIRD_EMU:BRAIN -CREATURE:BIRD_EMU:LUNG -CREATURE:BIRD_EMU:HEART -CREATURE:BIRD_EMU:LIVER -CREATURE:BIRD_EMU:GUT -CREATURE:BIRD_EMU:STOMACH -CREATURE:BIRD_EMU:GIZZARD -CREATURE:BIRD_EMU:PANCREAS -CREATURE:BIRD_EMU:SPLEEN -CREATURE:BIRD_EMU:KIDNEY -CREATURE:EMU_MAN:MUSCLE -CREATURE:EMU_MAN:EYE -CREATURE:EMU_MAN:BRAIN -CREATURE:EMU_MAN:LUNG -CREATURE:EMU_MAN:HEART -CREATURE:EMU_MAN:LIVER -CREATURE:EMU_MAN:GUT -CREATURE:EMU_MAN:STOMACH -CREATURE:EMU_MAN:GIZZARD -CREATURE:EMU_MAN:PANCREAS -CREATURE:EMU_MAN:SPLEEN -CREATURE:EMU_MAN:KIDNEY -CREATURE:GIANT_EMU:MUSCLE -CREATURE:GIANT_EMU:EYE -CREATURE:GIANT_EMU:BRAIN -CREATURE:GIANT_EMU:LUNG -CREATURE:GIANT_EMU:HEART -CREATURE:GIANT_EMU:LIVER -CREATURE:GIANT_EMU:GUT -CREATURE:GIANT_EMU:STOMACH -CREATURE:GIANT_EMU:GIZZARD -CREATURE:GIANT_EMU:PANCREAS -CREATURE:GIANT_EMU:SPLEEN -CREATURE:GIANT_EMU:KIDNEY -CREATURE:BIRD_COCKATIEL:MUSCLE -CREATURE:BIRD_COCKATIEL:EYE -CREATURE:BIRD_COCKATIEL:BRAIN -CREATURE:BIRD_COCKATIEL:LUNG -CREATURE:BIRD_COCKATIEL:HEART -CREATURE:BIRD_COCKATIEL:LIVER -CREATURE:BIRD_COCKATIEL:GUT -CREATURE:BIRD_COCKATIEL:STOMACH -CREATURE:BIRD_COCKATIEL:GIZZARD - CREATURE:BIRD_COCKATIEL:PANCREAS -CREATURE:BIRD_COCKATIEL:SPLEEN -CREATURE:BIRD_COCKATIEL:KIDNEY -CREATURE:COCKATIEL_MAN:MUSCLE -CREATURE:COCKATIEL_MAN:EYE -CREATURE:COCKATIEL_MAN:BRAIN -CREATURE:COCKATIEL_MAN:LUNG -CREATURE:COCKATIEL_MAN:HEART -CREATURE:COCKATIEL_MAN:LIVER -CREATURE:COCKATIEL_MAN:GUT -CREATURE:COCKATIEL_MAN:STOMACH -CREATURE:COCKATIEL_MAN:GIZZARD -CREATURE:COCKATIEL_MAN:PANCREAS -CREATURE:COCKATIEL_MAN:SPLEEN -CREATURE:COCKATIEL_MAN:KIDNEY -CREATURE:GIANT_COCKATIEL:MUSCLE -CREATURE:GIANT_COCKATIEL:EYE -CREATURE:GIANT_COCKATIEL:BRAIN -CREATURE:GIANT_COCKATIEL:LUNG -CREATURE:GIANT_COCKATIEL:HEART -CREATURE:GIANT_COCKATIEL:LIVER -CREATURE:GIANT_COCKATIEL:GUT - CREATURE:GIANT_COCKATIEL:STOMACH - CREATURE:GIANT_COCKATIEL:GIZZARD -!CREATURE:GIANT_COCKATIEL:PANCREAS -CREATURE:GIANT_COCKATIEL:SPLEEN -CREATURE:GIANT_COCKATIEL:KIDNEY -)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:MUSCLE -&CREATURE:BIRD_LOVEBIRD_PEACH-FACED:EYE -(CREATURE:BIRD_LOVEBIRD_PEACH-FACED:BRAIN -'CREATURE:BIRD_LOVEBIRD_PEACH-FACED:LUNG -(CREATURE:BIRD_LOVEBIRD_PEACH-FACED:HEART -(CREATURE:BIRD_LOVEBIRD_PEACH-FACED:LIVER -&CREATURE:BIRD_LOVEBIRD_PEACH-FACED:GUT -*CREATURE:BIRD_LOVEBIRD_PEACH-FACED:STOMACH -*CREATURE:BIRD_LOVEBIRD_PEACH-FACED:GIZZARD -+CREATURE:BIRD_LOVEBIRD_PEACH-FACED:PANCREAS -)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:SPLEEN -)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:KIDNEY -(CREATURE:PEACH-FACED_LOVEBIRD_MAN:MUSCLE -%CREATURE:PEACH-FACED_LOVEBIRD_MAN:EYE -'CREATURE:PEACH-FACED_LOVEBIRD_MAN:BRAIN -&CREATURE:PEACH-FACED_LOVEBIRD_MAN:LUNG -'CREATURE:PEACH-FACED_LOVEBIRD_MAN:HEART -'CREATURE:PEACH-FACED_LOVEBIRD_MAN:LIVER -%CREATURE:PEACH-FACED_LOVEBIRD_MAN:GUT -)CREATURE:PEACH-FACED_LOVEBIRD_MAN:STOMACH -)CREATURE:PEACH-FACED_LOVEBIRD_MAN:GIZZARD -*CREATURE:PEACH-FACED_LOVEBIRD_MAN:PANCREAS -(CREATURE:PEACH-FACED_LOVEBIRD_MAN:SPLEEN -(CREATURE:PEACH-FACED_LOVEBIRD_MAN:KIDNEY -*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:MUSCLE -'CREATURE:GIANT_PEACH-FACED_LOVEBIRD:EYE -)CREATURE:GIANT_PEACH-FACED_LOVEBIRD:BRAIN -(CREATURE:GIANT_PEACH-FACED_LOVEBIRD:LUNG -)CREATURE:GIANT_PEACH-FACED_LOVEBIRD:HEART -)CREATURE:GIANT_PEACH-FACED_LOVEBIRD:LIVER -'CREATURE:GIANT_PEACH-FACED_LOVEBIRD:GUT -+CREATURE:GIANT_PEACH-FACED_LOVEBIRD:STOMACH -+CREATURE:GIANT_PEACH-FACED_LOVEBIRD:GIZZARD -,CREATURE:GIANT_PEACH-FACED_LOVEBIRD:PANCREAS -*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:SPLEEN -*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:KIDNEY -CREATURE:BIRD_MAGPIE:MUSCLE -CREATURE:BIRD_MAGPIE:EYE -CREATURE:BIRD_MAGPIE:BRAIN -CREATURE:BIRD_MAGPIE:LUNG -CREATURE:BIRD_MAGPIE:HEART -CREATURE:BIRD_MAGPIE:LIVER -CREATURE:BIRD_MAGPIE:GUT -CREATURE:BIRD_MAGPIE:STOMACH -CREATURE:BIRD_MAGPIE:GIZZARD -CREATURE:BIRD_MAGPIE:PANCREAS -CREATURE:BIRD_MAGPIE:SPLEEN -CREATURE:BIRD_MAGPIE:KIDNEY -CREATURE:MAGPIE_MAN:MUSCLE -CREATURE:MAGPIE_MAN:EYE -CREATURE:MAGPIE_MAN:BRAIN -CREATURE:MAGPIE_MAN:LUNG -CREATURE:MAGPIE_MAN:HEART -CREATURE:MAGPIE_MAN:LIVER -CREATURE:MAGPIE_MAN:GUT -CREATURE:MAGPIE_MAN:STOMACH -CREATURE:MAGPIE_MAN:GIZZARD -CREATURE:MAGPIE_MAN:PANCREAS -CREATURE:MAGPIE_MAN:SPLEEN -CREATURE:MAGPIE_MAN:KIDNEY -CREATURE:GIANT_MAGPIE:MUSCLE -CREATURE:GIANT_MAGPIE:EYE -CREATURE:GIANT_MAGPIE:BRAIN -CREATURE:GIANT_MAGPIE:LUNG -CREATURE:GIANT_MAGPIE:HEART -CREATURE:GIANT_MAGPIE:LIVER -CREATURE:GIANT_MAGPIE:GUT -CREATURE:GIANT_MAGPIE:STOMACH -CREATURE:GIANT_MAGPIE:GIZZARD -CREATURE:GIANT_MAGPIE:PANCREAS -CREATURE:GIANT_MAGPIE:SPLEEN -CREATURE:GIANT_MAGPIE:KIDNEY -CREATURE:BIRD_KESTREL:MUSCLE -CREATURE:BIRD_KESTREL:EYE -CREATURE:BIRD_KESTREL:BRAIN -CREATURE:BIRD_KESTREL:LUNG -CREATURE:BIRD_KESTREL:HEART -CREATURE:BIRD_KESTREL:LIVER -CREATURE:BIRD_KESTREL:GUT -CREATURE:BIRD_KESTREL:STOMACH -CREATURE:BIRD_KESTREL:GIZZARD -CREATURE:BIRD_KESTREL:PANCREAS -CREATURE:BIRD_KESTREL:SPLEEN -CREATURE:BIRD_KESTREL:KIDNEY -CREATURE:KESTREL_MAN:MUSCLE -CREATURE:KESTREL_MAN:EYE -CREATURE:KESTREL_MAN:BRAIN -CREATURE:KESTREL_MAN:LUNG -CREATURE:KESTREL_MAN:HEART -CREATURE:KESTREL_MAN:LIVER -CREATURE:KESTREL_MAN:GUT -CREATURE:KESTREL_MAN:STOMACH -CREATURE:KESTREL_MAN:GIZZARD -CREATURE:KESTREL_MAN:PANCREAS -CREATURE:KESTREL_MAN:SPLEEN -CREATURE:KESTREL_MAN:KIDNEY -CREATURE:GIANT_KESTREL:MUSCLE -CREATURE:GIANT_KESTREL:EYE -CREATURE:GIANT_KESTREL:BRAIN -CREATURE:GIANT_KESTREL:LUNG -CREATURE:GIANT_KESTREL:HEART -CREATURE:GIANT_KESTREL:LIVER -CREATURE:GIANT_KESTREL:GUT -CREATURE:GIANT_KESTREL:STOMACH -CREATURE:GIANT_KESTREL:GIZZARD -CREATURE:GIANT_KESTREL:PANCREAS -CREATURE:GIANT_KESTREL:SPLEEN -CREATURE:GIANT_KESTREL:KIDNEY -CREATURE:BIRD_ALBATROSS:MUSCLE -CREATURE:BIRD_ALBATROSS:EYE -CREATURE:BIRD_ALBATROSS:BRAIN -CREATURE:BIRD_ALBATROSS:LUNG -CREATURE:BIRD_ALBATROSS:HEART -CREATURE:BIRD_ALBATROSS:LIVER -CREATURE:BIRD_ALBATROSS:GUT -CREATURE:BIRD_ALBATROSS:STOMACH -CREATURE:BIRD_ALBATROSS:GIZZARD - CREATURE:BIRD_ALBATROSS:PANCREAS -CREATURE:BIRD_ALBATROSS:SPLEEN -CREATURE:BIRD_ALBATROSS:KIDNEY -CREATURE:ALBATROSS_MAN:MUSCLE -CREATURE:ALBATROSS_MAN:EYE -CREATURE:ALBATROSS_MAN:BRAIN -CREATURE:ALBATROSS_MAN:LUNG -CREATURE:ALBATROSS_MAN:HEART -CREATURE:ALBATROSS_MAN:LIVER -CREATURE:ALBATROSS_MAN:GUT -CREATURE:ALBATROSS_MAN:STOMACH -CREATURE:ALBATROSS_MAN:GIZZARD -CREATURE:ALBATROSS_MAN:PANCREAS -CREATURE:ALBATROSS_MAN:SPLEEN -CREATURE:ALBATROSS_MAN:KIDNEY -CREATURE:GIANT_ALBATROSS:MUSCLE -CREATURE:GIANT_ALBATROSS:EYE -CREATURE:GIANT_ALBATROSS:BRAIN -CREATURE:GIANT_ALBATROSS:LUNG -CREATURE:GIANT_ALBATROSS:HEART -CREATURE:GIANT_ALBATROSS:LIVER -CREATURE:GIANT_ALBATROSS:GUT - CREATURE:GIANT_ALBATROSS:STOMACH - CREATURE:GIANT_ALBATROSS:GIZZARD -!CREATURE:GIANT_ALBATROSS:PANCREAS -CREATURE:GIANT_ALBATROSS:SPLEEN -CREATURE:GIANT_ALBATROSS:KIDNEY -%CREATURE:BIRD_OWL_GREAT_HORNED:MUSCLE -"CREATURE:BIRD_OWL_GREAT_HORNED:EYE -$CREATURE:BIRD_OWL_GREAT_HORNED:BRAIN -#CREATURE:BIRD_OWL_GREAT_HORNED:LUNG -$CREATURE:BIRD_OWL_GREAT_HORNED:HEART -$CREATURE:BIRD_OWL_GREAT_HORNED:LIVER -"CREATURE:BIRD_OWL_GREAT_HORNED:GUT -&CREATURE:BIRD_OWL_GREAT_HORNED:STOMACH -&CREATURE:BIRD_OWL_GREAT_HORNED:GIZZARD -'CREATURE:BIRD_OWL_GREAT_HORNED:PANCREAS -%CREATURE:BIRD_OWL_GREAT_HORNED:SPLEEN -%CREATURE:BIRD_OWL_GREAT_HORNED:KIDNEY -$CREATURE:GREAT_HORNED_OWL_MAN:MUSCLE -!CREATURE:GREAT_HORNED_OWL_MAN:EYE -#CREATURE:GREAT_HORNED_OWL_MAN:BRAIN -"CREATURE:GREAT_HORNED_OWL_MAN:LUNG -#CREATURE:GREAT_HORNED_OWL_MAN:HEART -#CREATURE:GREAT_HORNED_OWL_MAN:LIVER -!CREATURE:GREAT_HORNED_OWL_MAN:GUT -%CREATURE:GREAT_HORNED_OWL_MAN:STOMACH -%CREATURE:GREAT_HORNED_OWL_MAN:GIZZARD -&CREATURE:GREAT_HORNED_OWL_MAN:PANCREAS -$CREATURE:GREAT_HORNED_OWL_MAN:SPLEEN -$CREATURE:GREAT_HORNED_OWL_MAN:KIDNEY -&CREATURE:GIANT_GREAT_HORNED_OWL:MUSCLE -#CREATURE:GIANT_GREAT_HORNED_OWL:EYE -%CREATURE:GIANT_GREAT_HORNED_OWL:BRAIN -$CREATURE:GIANT_GREAT_HORNED_OWL:LUNG -%CREATURE:GIANT_GREAT_HORNED_OWL:HEART -%CREATURE:GIANT_GREAT_HORNED_OWL:LIVER -#CREATURE:GIANT_GREAT_HORNED_OWL:GUT -'CREATURE:GIANT_GREAT_HORNED_OWL:STOMACH -'CREATURE:GIANT_GREAT_HORNED_OWL:GIZZARD -(CREATURE:GIANT_GREAT_HORNED_OWL:PANCREAS -&CREATURE:GIANT_GREAT_HORNED_OWL:SPLEEN -&CREATURE:GIANT_GREAT_HORNED_OWL:KIDNEY -CREATURE:BIRD_EAGLE:MUSCLE -CREATURE:BIRD_EAGLE:EYE -CREATURE:BIRD_EAGLE:BRAIN -CREATURE:BIRD_EAGLE:LUNG -CREATURE:BIRD_EAGLE:HEART -CREATURE:BIRD_EAGLE:LIVER -CREATURE:BIRD_EAGLE:GUT -CREATURE:BIRD_EAGLE:STOMACH -CREATURE:BIRD_EAGLE:GIZZARD -CREATURE:BIRD_EAGLE:PANCREAS -CREATURE:BIRD_EAGLE:SPLEEN -CREATURE:BIRD_EAGLE:KIDNEY -CREATURE:EAGLE_MAN:MUSCLE -CREATURE:EAGLE_MAN:EYE -CREATURE:EAGLE_MAN:BRAIN -CREATURE:EAGLE_MAN:LUNG -CREATURE:EAGLE_MAN:HEART -CREATURE:EAGLE_MAN:LIVER -CREATURE:EAGLE_MAN:GUT -CREATURE:EAGLE_MAN:STOMACH -CREATURE:EAGLE_MAN:GIZZARD -CREATURE:EAGLE_MAN:PANCREAS -CREATURE:EAGLE_MAN:SPLEEN -CREATURE:EAGLE_MAN:KIDNEY -CREATURE:GIANT_EAGLE:MUSCLE -CREATURE:GIANT_EAGLE:EYE -CREATURE:GIANT_EAGLE:BRAIN -CREATURE:GIANT_EAGLE:LUNG -CREATURE:GIANT_EAGLE:HEART -CREATURE:GIANT_EAGLE:LIVER -CREATURE:GIANT_EAGLE:GUT -CREATURE:GIANT_EAGLE:STOMACH -CREATURE:GIANT_EAGLE:GIZZARD -CREATURE:GIANT_EAGLE:PANCREAS -CREATURE:GIANT_EAGLE:SPLEEN -CREATURE:GIANT_EAGLE:KIDNEY -CREATURE:BIRD_HORNBILL:MUSCLE -CREATURE:BIRD_HORNBILL:EYE -CREATURE:BIRD_HORNBILL:BRAIN -CREATURE:BIRD_HORNBILL:LUNG -CREATURE:BIRD_HORNBILL:HEART -CREATURE:BIRD_HORNBILL:LIVER -CREATURE:BIRD_HORNBILL:GUT -CREATURE:BIRD_HORNBILL:STOMACH -CREATURE:BIRD_HORNBILL:GIZZARD -CREATURE:BIRD_HORNBILL:PANCREAS -CREATURE:BIRD_HORNBILL:SPLEEN -CREATURE:BIRD_HORNBILL:KIDNEY -CREATURE:HORNBILL_MAN:MUSCLE -CREATURE:HORNBILL_MAN:EYE -CREATURE:HORNBILL_MAN:BRAIN -CREATURE:HORNBILL_MAN:LUNG -CREATURE:HORNBILL_MAN:HEART -CREATURE:HORNBILL_MAN:LIVER -CREATURE:HORNBILL_MAN:GUT -CREATURE:HORNBILL_MAN:STOMACH -CREATURE:HORNBILL_MAN:GIZZARD -CREATURE:HORNBILL_MAN:PANCREAS -CREATURE:HORNBILL_MAN:SPLEEN -CREATURE:HORNBILL_MAN:KIDNEY -CREATURE:GIANT_HORNBILL:MUSCLE -CREATURE:GIANT_HORNBILL:EYE -CREATURE:GIANT_HORNBILL:BRAIN -CREATURE:GIANT_HORNBILL:LUNG -CREATURE:GIANT_HORNBILL:HEART -CREATURE:GIANT_HORNBILL:LIVER -CREATURE:GIANT_HORNBILL:GUT -CREATURE:GIANT_HORNBILL:STOMACH -CREATURE:GIANT_HORNBILL:GIZZARD - CREATURE:GIANT_HORNBILL:PANCREAS -CREATURE:GIANT_HORNBILL:SPLEEN -CREATURE:GIANT_HORNBILL:KIDNEY -$CREATURE:BIRD_LOVEBIRD_MASKED:MUSCLE -!CREATURE:BIRD_LOVEBIRD_MASKED:EYE -#CREATURE:BIRD_LOVEBIRD_MASKED:BRAIN -"CREATURE:BIRD_LOVEBIRD_MASKED:LUNG -#CREATURE:BIRD_LOVEBIRD_MASKED:HEART -#CREATURE:BIRD_LOVEBIRD_MASKED:LIVER -!CREATURE:BIRD_LOVEBIRD_MASKED:GUT -%CREATURE:BIRD_LOVEBIRD_MASKED:STOMACH -%CREATURE:BIRD_LOVEBIRD_MASKED:GIZZARD -&CREATURE:BIRD_LOVEBIRD_MASKED:PANCREAS -$CREATURE:BIRD_LOVEBIRD_MASKED:SPLEEN -$CREATURE:BIRD_LOVEBIRD_MASKED:KIDNEY -#CREATURE:MASKED_LOVEBIRD_MAN:MUSCLE - CREATURE:MASKED_LOVEBIRD_MAN:EYE -"CREATURE:MASKED_LOVEBIRD_MAN:BRAIN -!CREATURE:MASKED_LOVEBIRD_MAN:LUNG -"CREATURE:MASKED_LOVEBIRD_MAN:HEART -"CREATURE:MASKED_LOVEBIRD_MAN:LIVER - CREATURE:MASKED_LOVEBIRD_MAN:GUT -$CREATURE:MASKED_LOVEBIRD_MAN:STOMACH -$CREATURE:MASKED_LOVEBIRD_MAN:GIZZARD -%CREATURE:MASKED_LOVEBIRD_MAN:PANCREAS -#CREATURE:MASKED_LOVEBIRD_MAN:SPLEEN -#CREATURE:MASKED_LOVEBIRD_MAN:KIDNEY -%CREATURE:GIANT_MASKED_LOVEBIRD:MUSCLE -"CREATURE:GIANT_MASKED_LOVEBIRD:EYE -$CREATURE:GIANT_MASKED_LOVEBIRD:BRAIN -#CREATURE:GIANT_MASKED_LOVEBIRD:LUNG -$CREATURE:GIANT_MASKED_LOVEBIRD:HEART -$CREATURE:GIANT_MASKED_LOVEBIRD:LIVER -"CREATURE:GIANT_MASKED_LOVEBIRD:GUT -&CREATURE:GIANT_MASKED_LOVEBIRD:STOMACH -&CREATURE:GIANT_MASKED_LOVEBIRD:GIZZARD -'CREATURE:GIANT_MASKED_LOVEBIRD:PANCREAS -%CREATURE:GIANT_MASKED_LOVEBIRD:SPLEEN -%CREATURE:GIANT_MASKED_LOVEBIRD:KIDNEY -CREATURE:BIRD_BUSHTIT:MUSCLE -CREATURE:BIRD_BUSHTIT:EYE -CREATURE:BIRD_BUSHTIT:BRAIN -CREATURE:BIRD_BUSHTIT:LUNG -CREATURE:BIRD_BUSHTIT:HEART -CREATURE:BIRD_BUSHTIT:LIVER -CREATURE:BIRD_BUSHTIT:GUT -CREATURE:BIRD_BUSHTIT:STOMACH -CREATURE:BIRD_BUSHTIT:GIZZARD -CREATURE:BIRD_BUSHTIT:PANCREAS -CREATURE:BIRD_BUSHTIT:SPLEEN -CREATURE:BIRD_BUSHTIT:KIDNEY -CREATURE:BUSHTIT_MAN:MUSCLE -CREATURE:BUSHTIT_MAN:EYE -CREATURE:BUSHTIT_MAN:BRAIN -CREATURE:BUSHTIT_MAN:LUNG -CREATURE:BUSHTIT_MAN:HEART -CREATURE:BUSHTIT_MAN:LIVER -CREATURE:BUSHTIT_MAN:GUT -CREATURE:BUSHTIT_MAN:STOMACH -CREATURE:BUSHTIT_MAN:GIZZARD -CREATURE:BUSHTIT_MAN:PANCREAS -CREATURE:BUSHTIT_MAN:SPLEEN -CREATURE:BUSHTIT_MAN:KIDNEY -CREATURE:GIANT_BUSHTIT:MUSCLE -CREATURE:GIANT_BUSHTIT:EYE -CREATURE:GIANT_BUSHTIT:BRAIN -CREATURE:GIANT_BUSHTIT:LUNG -CREATURE:GIANT_BUSHTIT:HEART -CREATURE:GIANT_BUSHTIT:LIVER -CREATURE:GIANT_BUSHTIT:GUT -CREATURE:GIANT_BUSHTIT:STOMACH -CREATURE:GIANT_BUSHTIT:GIZZARD -CREATURE:GIANT_BUSHTIT:PANCREAS -CREATURE:GIANT_BUSHTIT:SPLEEN -CREATURE:GIANT_BUSHTIT:KIDNEY -CREATURE:DAMSELFLY:MUSCLE -CREATURE:DAMSELFLY:EYE -CREATURE:DAMSELFLY:BRAIN -CREATURE:DAMSELFLY:LUNG -CREATURE:DAMSELFLY:HEART -CREATURE:DAMSELFLY:LIVER -CREATURE:DAMSELFLY:GUT -CREATURE:DAMSELFLY:STOMACH -CREATURE:DAMSELFLY:GIZZARD -CREATURE:DAMSELFLY:PANCREAS -CREATURE:DAMSELFLY:SPLEEN -CREATURE:DAMSELFLY:KIDNEY -CREATURE:DAMSELFLY_MAN:MUSCLE -CREATURE:DAMSELFLY_MAN:EYE -CREATURE:DAMSELFLY_MAN:BRAIN -CREATURE:DAMSELFLY_MAN:LUNG -CREATURE:DAMSELFLY_MAN:HEART -CREATURE:DAMSELFLY_MAN:LIVER -CREATURE:DAMSELFLY_MAN:GUT -CREATURE:DAMSELFLY_MAN:STOMACH -CREATURE:DAMSELFLY_MAN:GIZZARD -CREATURE:DAMSELFLY_MAN:PANCREAS -CREATURE:DAMSELFLY_MAN:SPLEEN -CREATURE:DAMSELFLY_MAN:KIDNEY -CREATURE:GIANT_DAMSELFLY:MUSCLE -CREATURE:GIANT_DAMSELFLY:EYE -CREATURE:GIANT_DAMSELFLY:BRAIN -CREATURE:GIANT_DAMSELFLY:LUNG -CREATURE:GIANT_DAMSELFLY:HEART -CREATURE:GIANT_DAMSELFLY:LIVER -CREATURE:GIANT_DAMSELFLY:GUT - CREATURE:GIANT_DAMSELFLY:STOMACH - CREATURE:GIANT_DAMSELFLY:GIZZARD -!CREATURE:GIANT_DAMSELFLY:PANCREAS -CREATURE:GIANT_DAMSELFLY:SPLEEN -CREATURE:GIANT_DAMSELFLY:KIDNEY -CREATURE:MOTH:MUSCLE -CREATURE:MOTH:EYE -CREATURE:MOTH:BRAIN -CREATURE:MOTH:LUNG -CREATURE:MOTH:HEART -CREATURE:MOTH:LIVER -CREATURE:MOTH:GUT -CREATURE:MOTH:STOMACH -CREATURE:MOTH:GIZZARD -CREATURE:MOTH:PANCREAS -CREATURE:MOTH:SPLEEN -CREATURE:MOTH:KIDNEY -CREATURE:MOTH_MAN:MUSCLE -CREATURE:MOTH_MAN:EYE -CREATURE:MOTH_MAN:BRAIN -CREATURE:MOTH_MAN:LUNG -CREATURE:MOTH_MAN:HEART -CREATURE:MOTH_MAN:LIVER -CREATURE:MOTH_MAN:GUT -CREATURE:MOTH_MAN:STOMACH -CREATURE:MOTH_MAN:GIZZARD -CREATURE:MOTH_MAN:PANCREAS -CREATURE:MOTH_MAN:SPLEEN -CREATURE:MOTH_MAN:KIDNEY -CREATURE:GIANT_MOTH:MUSCLE -CREATURE:GIANT_MOTH:EYE -CREATURE:GIANT_MOTH:BRAIN -CREATURE:GIANT_MOTH:LUNG -CREATURE:GIANT_MOTH:HEART -CREATURE:GIANT_MOTH:LIVER -CREATURE:GIANT_MOTH:GUT -CREATURE:GIANT_MOTH:STOMACH -CREATURE:GIANT_MOTH:GIZZARD -CREATURE:GIANT_MOTH:PANCREAS -CREATURE:GIANT_MOTH:SPLEEN -CREATURE:GIANT_MOTH:KIDNEY -CREATURE:GRASSHOPPER:MUSCLE -CREATURE:GRASSHOPPER:EYE -CREATURE:GRASSHOPPER:BRAIN -CREATURE:GRASSHOPPER:LUNG -CREATURE:GRASSHOPPER:HEART -CREATURE:GRASSHOPPER:LIVER -CREATURE:GRASSHOPPER:GUT -CREATURE:GRASSHOPPER:STOMACH -CREATURE:GRASSHOPPER:GIZZARD -CREATURE:GRASSHOPPER:PANCREAS -CREATURE:GRASSHOPPER:SPLEEN -CREATURE:GRASSHOPPER:KIDNEY -CREATURE:GRASSHOPPER_MAN:MUSCLE -CREATURE:GRASSHOPPER_MAN:EYE -CREATURE:GRASSHOPPER_MAN:BRAIN -CREATURE:GRASSHOPPER_MAN:LUNG -CREATURE:GRASSHOPPER_MAN:HEART -CREATURE:GRASSHOPPER_MAN:LIVER -CREATURE:GRASSHOPPER_MAN:GUT - CREATURE:GRASSHOPPER_MAN:STOMACH - CREATURE:GRASSHOPPER_MAN:GIZZARD -!CREATURE:GRASSHOPPER_MAN:PANCREAS -CREATURE:GRASSHOPPER_MAN:SPLEEN -CREATURE:GRASSHOPPER_MAN:KIDNEY -!CREATURE:GIANT_GRASSHOPPER:MUSCLE -CREATURE:GIANT_GRASSHOPPER:EYE - CREATURE:GIANT_GRASSHOPPER:BRAIN -CREATURE:GIANT_GRASSHOPPER:LUNG - CREATURE:GIANT_GRASSHOPPER:HEART - CREATURE:GIANT_GRASSHOPPER:LIVER -CREATURE:GIANT_GRASSHOPPER:GUT -"CREATURE:GIANT_GRASSHOPPER:STOMACH -"CREATURE:GIANT_GRASSHOPPER:GIZZARD -#CREATURE:GIANT_GRASSHOPPER:PANCREAS -!CREATURE:GIANT_GRASSHOPPER:SPLEEN -!CREATURE:GIANT_GRASSHOPPER:KIDNEY -CREATURE:BARK_SCORPION:MUSCLE -CREATURE:BARK_SCORPION:EYE -CREATURE:BARK_SCORPION:BRAIN -CREATURE:BARK_SCORPION:LUNG -CREATURE:BARK_SCORPION:HEART -CREATURE:BARK_SCORPION:LIVER -CREATURE:BARK_SCORPION:GUT -CREATURE:BARK_SCORPION:STOMACH -CREATURE:BARK_SCORPION:GIZZARD -CREATURE:BARK_SCORPION:PANCREAS -CREATURE:BARK_SCORPION:SPLEEN -CREATURE:BARK_SCORPION:KIDNEY -!CREATURE:BARK_SCORPION_MAN:MUSCLE -CREATURE:BARK_SCORPION_MAN:EYE - CREATURE:BARK_SCORPION_MAN:BRAIN -CREATURE:BARK_SCORPION_MAN:LUNG - CREATURE:BARK_SCORPION_MAN:HEART - CREATURE:BARK_SCORPION_MAN:LIVER -CREATURE:BARK_SCORPION_MAN:GUT -"CREATURE:BARK_SCORPION_MAN:STOMACH -"CREATURE:BARK_SCORPION_MAN:GIZZARD -#CREATURE:BARK_SCORPION_MAN:PANCREAS -!CREATURE:BARK_SCORPION_MAN:SPLEEN -!CREATURE:BARK_SCORPION_MAN:KIDNEY -#CREATURE:GIANT_BARK_SCORPION:MUSCLE - CREATURE:GIANT_BARK_SCORPION:EYE -"CREATURE:GIANT_BARK_SCORPION:BRAIN -!CREATURE:GIANT_BARK_SCORPION:LUNG -"CREATURE:GIANT_BARK_SCORPION:HEART -"CREATURE:GIANT_BARK_SCORPION:LIVER - CREATURE:GIANT_BARK_SCORPION:GUT -$CREATURE:GIANT_BARK_SCORPION:STOMACH -$CREATURE:GIANT_BARK_SCORPION:GIZZARD -%CREATURE:GIANT_BARK_SCORPION:PANCREAS -#CREATURE:GIANT_BARK_SCORPION:SPLEEN -#CREATURE:GIANT_BARK_SCORPION:KIDNEY -CREATURE:MANTIS:MUSCLE -CREATURE:MANTIS:EYE -CREATURE:MANTIS:BRAIN -CREATURE:MANTIS:LUNG -CREATURE:MANTIS:HEART -CREATURE:MANTIS:LIVER -CREATURE:MANTIS:GUT -CREATURE:MANTIS:STOMACH -CREATURE:MANTIS:GIZZARD -CREATURE:MANTIS:PANCREAS -CREATURE:MANTIS:SPLEEN -CREATURE:MANTIS:KIDNEY -CREATURE:MANTIS_MAN:MUSCLE -CREATURE:MANTIS_MAN:EYE -CREATURE:MANTIS_MAN:BRAIN -CREATURE:MANTIS_MAN:LUNG -CREATURE:MANTIS_MAN:HEART -CREATURE:MANTIS_MAN:LIVER -CREATURE:MANTIS_MAN:GUT -CREATURE:MANTIS_MAN:STOMACH -CREATURE:MANTIS_MAN:GIZZARD -CREATURE:MANTIS_MAN:PANCREAS -CREATURE:MANTIS_MAN:SPLEEN -CREATURE:MANTIS_MAN:KIDNEY -CREATURE:GIANT_MANTIS:MUSCLE -CREATURE:GIANT_MANTIS:EYE -CREATURE:GIANT_MANTIS:BRAIN -CREATURE:GIANT_MANTIS:LUNG -CREATURE:GIANT_MANTIS:HEART -CREATURE:GIANT_MANTIS:LIVER -CREATURE:GIANT_MANTIS:GUT -CREATURE:GIANT_MANTIS:STOMACH -CREATURE:GIANT_MANTIS:GIZZARD -CREATURE:GIANT_MANTIS:PANCREAS -CREATURE:GIANT_MANTIS:SPLEEN -CREATURE:GIANT_MANTIS:KIDNEY -CREATURE:TICK:MUSCLE -CREATURE:TICK:EYE -CREATURE:TICK:BRAIN -CREATURE:TICK:LUNG -CREATURE:TICK:HEART -CREATURE:TICK:LIVER -CREATURE:TICK:GUT -CREATURE:TICK:STOMACH -CREATURE:TICK:GIZZARD -CREATURE:TICK:PANCREAS -CREATURE:TICK:SPLEEN -CREATURE:TICK:KIDNEY -CREATURE:TICK_MAN:MUSCLE -CREATURE:TICK_MAN:EYE -CREATURE:TICK_MAN:BRAIN -CREATURE:TICK_MAN:LUNG -CREATURE:TICK_MAN:HEART -CREATURE:TICK_MAN:LIVER -CREATURE:TICK_MAN:GUT -CREATURE:TICK_MAN:STOMACH -CREATURE:TICK_MAN:GIZZARD -CREATURE:TICK_MAN:PANCREAS -CREATURE:TICK_MAN:SPLEEN -CREATURE:TICK_MAN:KIDNEY -CREATURE:GIANT_TICK:MUSCLE -CREATURE:GIANT_TICK:EYE -CREATURE:GIANT_TICK:BRAIN -CREATURE:GIANT_TICK:LUNG -CREATURE:GIANT_TICK:HEART -CREATURE:GIANT_TICK:LIVER -CREATURE:GIANT_TICK:GUT -CREATURE:GIANT_TICK:STOMACH -CREATURE:GIANT_TICK:GIZZARD -CREATURE:GIANT_TICK:PANCREAS -CREATURE:GIANT_TICK:SPLEEN -CREATURE:GIANT_TICK:KIDNEY -CREATURE:LOUSE:MUSCLE -CREATURE:LOUSE:EYE -CREATURE:LOUSE:BRAIN -CREATURE:LOUSE:LUNG -CREATURE:LOUSE:HEART -CREATURE:LOUSE:LIVER -CREATURE:LOUSE:GUT -CREATURE:LOUSE:STOMACH -CREATURE:LOUSE:GIZZARD -CREATURE:LOUSE:PANCREAS -CREATURE:LOUSE:SPLEEN -CREATURE:LOUSE:KIDNEY -CREATURE:LOUSE_MAN:MUSCLE -CREATURE:LOUSE_MAN:EYE -CREATURE:LOUSE_MAN:BRAIN -CREATURE:LOUSE_MAN:LUNG -CREATURE:LOUSE_MAN:HEART -CREATURE:LOUSE_MAN:LIVER -CREATURE:LOUSE_MAN:GUT -CREATURE:LOUSE_MAN:STOMACH -CREATURE:LOUSE_MAN:GIZZARD -CREATURE:LOUSE_MAN:PANCREAS -CREATURE:LOUSE_MAN:SPLEEN -CREATURE:LOUSE_MAN:KIDNEY -CREATURE:GIANT_LOUSE:MUSCLE -CREATURE:GIANT_LOUSE:EYE -CREATURE:GIANT_LOUSE:BRAIN -CREATURE:GIANT_LOUSE:LUNG -CREATURE:GIANT_LOUSE:HEART -CREATURE:GIANT_LOUSE:LIVER -CREATURE:GIANT_LOUSE:GUT -CREATURE:GIANT_LOUSE:STOMACH -CREATURE:GIANT_LOUSE:GIZZARD -CREATURE:GIANT_LOUSE:PANCREAS -CREATURE:GIANT_LOUSE:SPLEEN -CREATURE:GIANT_LOUSE:KIDNEY -CREATURE:THRIPS:MUSCLE -CREATURE:THRIPS:EYE -CREATURE:THRIPS:BRAIN -CREATURE:THRIPS:LUNG -CREATURE:THRIPS:HEART -CREATURE:THRIPS:LIVER -CREATURE:THRIPS:GUT -CREATURE:THRIPS:STOMACH -CREATURE:THRIPS:GIZZARD -CREATURE:THRIPS:PANCREAS -CREATURE:THRIPS:SPLEEN -CREATURE:THRIPS:KIDNEY -CREATURE:THRIPS_MAN:MUSCLE -CREATURE:THRIPS_MAN:EYE -CREATURE:THRIPS_MAN:BRAIN -CREATURE:THRIPS_MAN:LUNG -CREATURE:THRIPS_MAN:HEART -CREATURE:THRIPS_MAN:LIVER -CREATURE:THRIPS_MAN:GUT -CREATURE:THRIPS_MAN:STOMACH -CREATURE:THRIPS_MAN:GIZZARD -CREATURE:THRIPS_MAN:PANCREAS -CREATURE:THRIPS_MAN:SPLEEN -CREATURE:THRIPS_MAN:KIDNEY -CREATURE:GIANT_THRIPS:MUSCLE -CREATURE:GIANT_THRIPS:EYE -CREATURE:GIANT_THRIPS:BRAIN -CREATURE:GIANT_THRIPS:LUNG -CREATURE:GIANT_THRIPS:HEART -CREATURE:GIANT_THRIPS:LIVER -CREATURE:GIANT_THRIPS:GUT -CREATURE:GIANT_THRIPS:STOMACH -CREATURE:GIANT_THRIPS:GIZZARD -CREATURE:GIANT_THRIPS:PANCREAS -CREATURE:GIANT_THRIPS:SPLEEN -CREATURE:GIANT_THRIPS:KIDNEY -CREATURE:SLUG:MUSCLE -CREATURE:SLUG:EYE -CREATURE:SLUG:BRAIN -CREATURE:SLUG:LUNG -CREATURE:SLUG:HEART -CREATURE:SLUG:LIVER -CREATURE:SLUG:GUT -CREATURE:SLUG:STOMACH -CREATURE:SLUG:GIZZARD -CREATURE:SLUG:PANCREAS -CREATURE:SLUG:SPLEEN -CREATURE:SLUG:KIDNEY -CREATURE:SLUG_MAN:MUSCLE -CREATURE:SLUG_MAN:EYE -CREATURE:SLUG_MAN:BRAIN -CREATURE:SLUG_MAN:LUNG -CREATURE:SLUG_MAN:HEART -CREATURE:SLUG_MAN:LIVER -CREATURE:SLUG_MAN:GUT -CREATURE:SLUG_MAN:STOMACH -CREATURE:SLUG_MAN:GIZZARD -CREATURE:SLUG_MAN:PANCREAS -CREATURE:SLUG_MAN:SPLEEN -CREATURE:SLUG_MAN:KIDNEY -CREATURE:GIANT_SLUG:MUSCLE -CREATURE:GIANT_SLUG:EYE -CREATURE:GIANT_SLUG:BRAIN -CREATURE:GIANT_SLUG:LUNG -CREATURE:GIANT_SLUG:HEART -CREATURE:GIANT_SLUG:LIVER -CREATURE:GIANT_SLUG:GUT -CREATURE:GIANT_SLUG:STOMACH -CREATURE:GIANT_SLUG:GIZZARD -CREATURE:GIANT_SLUG:PANCREAS -CREATURE:GIANT_SLUG:SPLEEN -CREATURE:GIANT_SLUG:KIDNEY -CREATURE:MOSQUITO:MUSCLE -CREATURE:MOSQUITO:EYE -CREATURE:MOSQUITO:BRAIN -CREATURE:MOSQUITO:LUNG -CREATURE:MOSQUITO:HEART -CREATURE:MOSQUITO:LIVER -CREATURE:MOSQUITO:GUT -CREATURE:MOSQUITO:STOMACH -CREATURE:MOSQUITO:GIZZARD -CREATURE:MOSQUITO:PANCREAS -CREATURE:MOSQUITO:SPLEEN -CREATURE:MOSQUITO:KIDNEY -CREATURE:MOSQUITO_MAN:MUSCLE -CREATURE:MOSQUITO_MAN:EYE -CREATURE:MOSQUITO_MAN:BRAIN -CREATURE:MOSQUITO_MAN:LUNG -CREATURE:MOSQUITO_MAN:HEART -CREATURE:MOSQUITO_MAN:LIVER -CREATURE:MOSQUITO_MAN:GUT -CREATURE:MOSQUITO_MAN:STOMACH -CREATURE:MOSQUITO_MAN:GIZZARD -CREATURE:MOSQUITO_MAN:PANCREAS -CREATURE:MOSQUITO_MAN:SPLEEN -CREATURE:MOSQUITO_MAN:KIDNEY -CREATURE:GIANT_MOSQUITO:MUSCLE -CREATURE:GIANT_MOSQUITO:EYE -CREATURE:GIANT_MOSQUITO:BRAIN -CREATURE:GIANT_MOSQUITO:LUNG -CREATURE:GIANT_MOSQUITO:HEART -CREATURE:GIANT_MOSQUITO:LIVER -CREATURE:GIANT_MOSQUITO:GUT -CREATURE:GIANT_MOSQUITO:STOMACH -CREATURE:GIANT_MOSQUITO:GIZZARD - CREATURE:GIANT_MOSQUITO:PANCREAS -CREATURE:GIANT_MOSQUITO:SPLEEN -CREATURE:GIANT_MOSQUITO:KIDNEY -CREATURE:SPIDER_JUMPING:MUSCLE -CREATURE:SPIDER_JUMPING:EYE -CREATURE:SPIDER_JUMPING:BRAIN -CREATURE:SPIDER_JUMPING:LUNG -CREATURE:SPIDER_JUMPING:HEART -CREATURE:SPIDER_JUMPING:LIVER -CREATURE:SPIDER_JUMPING:GUT -CREATURE:SPIDER_JUMPING:STOMACH -CREATURE:SPIDER_JUMPING:GIZZARD - CREATURE:SPIDER_JUMPING:PANCREAS -CREATURE:SPIDER_JUMPING:SPLEEN -CREATURE:SPIDER_JUMPING:KIDNEY -"CREATURE:JUMPING_SPIDER_MAN:MUSCLE -CREATURE:JUMPING_SPIDER_MAN:EYE -!CREATURE:JUMPING_SPIDER_MAN:BRAIN - CREATURE:JUMPING_SPIDER_MAN:LUNG -!CREATURE:JUMPING_SPIDER_MAN:HEART -!CREATURE:JUMPING_SPIDER_MAN:LIVER -CREATURE:JUMPING_SPIDER_MAN:GUT -#CREATURE:JUMPING_SPIDER_MAN:STOMACH -#CREATURE:JUMPING_SPIDER_MAN:GIZZARD -$CREATURE:JUMPING_SPIDER_MAN:PANCREAS -"CREATURE:JUMPING_SPIDER_MAN:SPLEEN -"CREATURE:JUMPING_SPIDER_MAN:KIDNEY -$CREATURE:GIANT_JUMPING_SPIDER:MUSCLE -!CREATURE:GIANT_JUMPING_SPIDER:EYE -#CREATURE:GIANT_JUMPING_SPIDER:BRAIN -"CREATURE:GIANT_JUMPING_SPIDER:LUNG -#CREATURE:GIANT_JUMPING_SPIDER:HEART -#CREATURE:GIANT_JUMPING_SPIDER:LIVER -!CREATURE:GIANT_JUMPING_SPIDER:GUT -%CREATURE:GIANT_JUMPING_SPIDER:STOMACH -%CREATURE:GIANT_JUMPING_SPIDER:GIZZARD -&CREATURE:GIANT_JUMPING_SPIDER:PANCREAS -$CREATURE:GIANT_JUMPING_SPIDER:SPLEEN -$CREATURE:GIANT_JUMPING_SPIDER:KIDNEY -CREATURE:TERMITE:MUSCLE -CREATURE:TERMITE:EYE -CREATURE:TERMITE:BRAIN -CREATURE:TERMITE:LUNG -CREATURE:TERMITE:HEART -CREATURE:TERMITE:LIVER -CREATURE:TERMITE:GUT -CREATURE:TERMITE:STOMACH -CREATURE:TERMITE:GIZZARD -CREATURE:TERMITE:PANCREAS -CREATURE:TERMITE:SPLEEN -CREATURE:TERMITE:KIDNEY -CREATURE:MOON_SNAIL:MUSCLE -CREATURE:MOON_SNAIL:EYE -CREATURE:MOON_SNAIL:BRAIN -CREATURE:MOON_SNAIL:LUNG -CREATURE:MOON_SNAIL:HEART -CREATURE:MOON_SNAIL:LIVER -CREATURE:MOON_SNAIL:GUT -CREATURE:MOON_SNAIL:STOMACH -CREATURE:MOON_SNAIL:GIZZARD -CREATURE:MOON_SNAIL:PANCREAS -CREATURE:MOON_SNAIL:SPLEEN -CREATURE:MOON_SNAIL:KIDNEY -CREATURE:MOON_SNAIL_MAN:MUSCLE -CREATURE:MOON_SNAIL_MAN:EYE -CREATURE:MOON_SNAIL_MAN:BRAIN -CREATURE:MOON_SNAIL_MAN:LUNG -CREATURE:MOON_SNAIL_MAN:HEART -CREATURE:MOON_SNAIL_MAN:LIVER -CREATURE:MOON_SNAIL_MAN:GUT -CREATURE:MOON_SNAIL_MAN:STOMACH -CREATURE:MOON_SNAIL_MAN:GIZZARD - CREATURE:MOON_SNAIL_MAN:PANCREAS -CREATURE:MOON_SNAIL_MAN:SPLEEN -CREATURE:MOON_SNAIL_MAN:KIDNEY - CREATURE:GIANT_MOON_SNAIL:MUSCLE -CREATURE:GIANT_MOON_SNAIL:EYE -CREATURE:GIANT_MOON_SNAIL:BRAIN -CREATURE:GIANT_MOON_SNAIL:LUNG -CREATURE:GIANT_MOON_SNAIL:HEART -CREATURE:GIANT_MOON_SNAIL:LIVER -CREATURE:GIANT_MOON_SNAIL:GUT -!CREATURE:GIANT_MOON_SNAIL:STOMACH -!CREATURE:GIANT_MOON_SNAIL:GIZZARD -"CREATURE:GIANT_MOON_SNAIL:PANCREAS - CREATURE:GIANT_MOON_SNAIL:SPLEEN - CREATURE:GIANT_MOON_SNAIL:KIDNEY -$CREATURE:SPIDER_BROWN_RECLUSE:MUSCLE -!CREATURE:SPIDER_BROWN_RECLUSE:EYE -#CREATURE:SPIDER_BROWN_RECLUSE:BRAIN -"CREATURE:SPIDER_BROWN_RECLUSE:LUNG -#CREATURE:SPIDER_BROWN_RECLUSE:HEART -#CREATURE:SPIDER_BROWN_RECLUSE:LIVER -!CREATURE:SPIDER_BROWN_RECLUSE:GUT -%CREATURE:SPIDER_BROWN_RECLUSE:STOMACH -%CREATURE:SPIDER_BROWN_RECLUSE:GIZZARD -&CREATURE:SPIDER_BROWN_RECLUSE:PANCREAS -$CREATURE:SPIDER_BROWN_RECLUSE:SPLEEN -$CREATURE:SPIDER_BROWN_RECLUSE:KIDNEY -(CREATURE:BROWN_RECLUSE_SPIDER_MAN:MUSCLE -%CREATURE:BROWN_RECLUSE_SPIDER_MAN:EYE -'CREATURE:BROWN_RECLUSE_SPIDER_MAN:BRAIN -&CREATURE:BROWN_RECLUSE_SPIDER_MAN:LUNG -'CREATURE:BROWN_RECLUSE_SPIDER_MAN:HEART -'CREATURE:BROWN_RECLUSE_SPIDER_MAN:LIVER -%CREATURE:BROWN_RECLUSE_SPIDER_MAN:GUT -)CREATURE:BROWN_RECLUSE_SPIDER_MAN:STOMACH -)CREATURE:BROWN_RECLUSE_SPIDER_MAN:GIZZARD -*CREATURE:BROWN_RECLUSE_SPIDER_MAN:PANCREAS -(CREATURE:BROWN_RECLUSE_SPIDER_MAN:SPLEEN -(CREATURE:BROWN_RECLUSE_SPIDER_MAN:KIDNEY -*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:MUSCLE -'CREATURE:GIANT_BROWN_RECLUSE_SPIDER:EYE -)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:BRAIN -(CREATURE:GIANT_BROWN_RECLUSE_SPIDER:LUNG -)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:HEART -)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:LIVER -'CREATURE:GIANT_BROWN_RECLUSE_SPIDER:GUT -+CREATURE:GIANT_BROWN_RECLUSE_SPIDER:STOMACH -+CREATURE:GIANT_BROWN_RECLUSE_SPIDER:GIZZARD -,CREATURE:GIANT_BROWN_RECLUSE_SPIDER:PANCREAS -*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:SPLEEN -*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:KIDNEY -CREATURE:SNAIL:MUSCLE -CREATURE:SNAIL:EYE -CREATURE:SNAIL:BRAIN -CREATURE:SNAIL:LUNG -CREATURE:SNAIL:HEART -CREATURE:SNAIL:LIVER -CREATURE:SNAIL:GUT -CREATURE:SNAIL:STOMACH -CREATURE:SNAIL:GIZZARD -CREATURE:SNAIL:PANCREAS -CREATURE:SNAIL:SPLEEN -CREATURE:SNAIL:KIDNEY -CREATURE:SNAIL_MAN:MUSCLE -CREATURE:SNAIL_MAN:EYE -CREATURE:SNAIL_MAN:BRAIN -CREATURE:SNAIL_MAN:LUNG -CREATURE:SNAIL_MAN:HEART -CREATURE:SNAIL_MAN:LIVER -CREATURE:SNAIL_MAN:GUT -CREATURE:SNAIL_MAN:STOMACH -CREATURE:SNAIL_MAN:GIZZARD -CREATURE:SNAIL_MAN:PANCREAS -CREATURE:SNAIL_MAN:SPLEEN -CREATURE:SNAIL_MAN:KIDNEY -CREATURE:GIANT_SNAIL:MUSCLE -CREATURE:GIANT_SNAIL:EYE -CREATURE:GIANT_SNAIL:BRAIN -CREATURE:GIANT_SNAIL:LUNG -CREATURE:GIANT_SNAIL:HEART -CREATURE:GIANT_SNAIL:LIVER -CREATURE:GIANT_SNAIL:GUT -CREATURE:GIANT_SNAIL:STOMACH -CREATURE:GIANT_SNAIL:GIZZARD -CREATURE:GIANT_SNAIL:PANCREAS -CREATURE:GIANT_SNAIL:SPLEEN -CREATURE:GIANT_SNAIL:KIDNEY -CREATURE:GECKO_LEOPARD:MUSCLE -CREATURE:GECKO_LEOPARD:EYE -CREATURE:GECKO_LEOPARD:BRAIN -CREATURE:GECKO_LEOPARD:LUNG -CREATURE:GECKO_LEOPARD:HEART -CREATURE:GECKO_LEOPARD:LIVER -CREATURE:GECKO_LEOPARD:GUT -CREATURE:GECKO_LEOPARD:STOMACH -CREATURE:GECKO_LEOPARD:GIZZARD -CREATURE:GECKO_LEOPARD:PANCREAS -CREATURE:GECKO_LEOPARD:SPLEEN -CREATURE:GECKO_LEOPARD:KIDNEY -!CREATURE:LEOPARD_GECKO_MAN:MUSCLE -CREATURE:LEOPARD_GECKO_MAN:EYE - CREATURE:LEOPARD_GECKO_MAN:BRAIN -CREATURE:LEOPARD_GECKO_MAN:LUNG - CREATURE:LEOPARD_GECKO_MAN:HEART - CREATURE:LEOPARD_GECKO_MAN:LIVER -CREATURE:LEOPARD_GECKO_MAN:GUT -"CREATURE:LEOPARD_GECKO_MAN:STOMACH -"CREATURE:LEOPARD_GECKO_MAN:GIZZARD -#CREATURE:LEOPARD_GECKO_MAN:PANCREAS -!CREATURE:LEOPARD_GECKO_MAN:SPLEEN -!CREATURE:LEOPARD_GECKO_MAN:KIDNEY -#CREATURE:GIANT_LEOPARD_GECKO:MUSCLE - CREATURE:GIANT_LEOPARD_GECKO:EYE -"CREATURE:GIANT_LEOPARD_GECKO:BRAIN -!CREATURE:GIANT_LEOPARD_GECKO:LUNG -"CREATURE:GIANT_LEOPARD_GECKO:HEART -"CREATURE:GIANT_LEOPARD_GECKO:LIVER - CREATURE:GIANT_LEOPARD_GECKO:GUT -$CREATURE:GIANT_LEOPARD_GECKO:STOMACH -$CREATURE:GIANT_LEOPARD_GECKO:GIZZARD -%CREATURE:GIANT_LEOPARD_GECKO:PANCREAS -#CREATURE:GIANT_LEOPARD_GECKO:SPLEEN -#CREATURE:GIANT_LEOPARD_GECKO:KIDNEY -CREATURE:DESERT TORTOISE:MUSCLE -CREATURE:DESERT TORTOISE:EYE -CREATURE:DESERT TORTOISE:BRAIN -CREATURE:DESERT TORTOISE:LUNG -CREATURE:DESERT TORTOISE:HEART -CREATURE:DESERT TORTOISE:LIVER -CREATURE:DESERT TORTOISE:GUT - CREATURE:DESERT TORTOISE:STOMACH - CREATURE:DESERT TORTOISE:GIZZARD -!CREATURE:DESERT TORTOISE:PANCREAS -CREATURE:DESERT TORTOISE:SPLEEN -CREATURE:DESERT TORTOISE:KIDNEY -#CREATURE:DESERT_TORTOISE_MAN:MUSCLE - CREATURE:DESERT_TORTOISE_MAN:EYE -"CREATURE:DESERT_TORTOISE_MAN:BRAIN -!CREATURE:DESERT_TORTOISE_MAN:LUNG -"CREATURE:DESERT_TORTOISE_MAN:HEART -"CREATURE:DESERT_TORTOISE_MAN:LIVER - CREATURE:DESERT_TORTOISE_MAN:GUT -$CREATURE:DESERT_TORTOISE_MAN:STOMACH -$CREATURE:DESERT_TORTOISE_MAN:GIZZARD -%CREATURE:DESERT_TORTOISE_MAN:PANCREAS -#CREATURE:DESERT_TORTOISE_MAN:SPLEEN -#CREATURE:DESERT_TORTOISE_MAN:KIDNEY -%CREATURE:GIANT_DESERT_TORTOISE:MUSCLE -"CREATURE:GIANT_DESERT_TORTOISE:EYE -$CREATURE:GIANT_DESERT_TORTOISE:BRAIN -#CREATURE:GIANT_DESERT_TORTOISE:LUNG -$CREATURE:GIANT_DESERT_TORTOISE:HEART -$CREATURE:GIANT_DESERT_TORTOISE:LIVER -"CREATURE:GIANT_DESERT_TORTOISE:GUT -&CREATURE:GIANT_DESERT_TORTOISE:STOMACH -&CREATURE:GIANT_DESERT_TORTOISE:GIZZARD -'CREATURE:GIANT_DESERT_TORTOISE:PANCREAS -%CREATURE:GIANT_DESERT_TORTOISE:SPLEEN -%CREATURE:GIANT_DESERT_TORTOISE:KIDNEY -CREATURE:GILA_MONSTER:MUSCLE -CREATURE:GILA_MONSTER:EYE -CREATURE:GILA_MONSTER:BRAIN -CREATURE:GILA_MONSTER:LUNG -CREATURE:GILA_MONSTER:HEART -CREATURE:GILA_MONSTER:LIVER -CREATURE:GILA_MONSTER:GUT -CREATURE:GILA_MONSTER:STOMACH -CREATURE:GILA_MONSTER:GIZZARD -CREATURE:GILA_MONSTER:PANCREAS -CREATURE:GILA_MONSTER:SPLEEN -CREATURE:GILA_MONSTER:KIDNEY - CREATURE:GILA_MONSTER_MAN:MUSCLE -CREATURE:GILA_MONSTER_MAN:EYE -CREATURE:GILA_MONSTER_MAN:BRAIN -CREATURE:GILA_MONSTER_MAN:LUNG -CREATURE:GILA_MONSTER_MAN:HEART -CREATURE:GILA_MONSTER_MAN:LIVER -CREATURE:GILA_MONSTER_MAN:GUT -!CREATURE:GILA_MONSTER_MAN:STOMACH -!CREATURE:GILA_MONSTER_MAN:GIZZARD -"CREATURE:GILA_MONSTER_MAN:PANCREAS - CREATURE:GILA_MONSTER_MAN:SPLEEN - CREATURE:GILA_MONSTER_MAN:KIDNEY -"CREATURE:GIANT_GILA_MONSTER:MUSCLE -CREATURE:GIANT_GILA_MONSTER:EYE -!CREATURE:GIANT_GILA_MONSTER:BRAIN - CREATURE:GIANT_GILA_MONSTER:LUNG -!CREATURE:GIANT_GILA_MONSTER:HEART -!CREATURE:GIANT_GILA_MONSTER:LIVER -CREATURE:GIANT_GILA_MONSTER:GUT -#CREATURE:GIANT_GILA_MONSTER:STOMACH -#CREATURE:GIANT_GILA_MONSTER:GIZZARD -$CREATURE:GIANT_GILA_MONSTER:PANCREAS -"CREATURE:GIANT_GILA_MONSTER:SPLEEN -"CREATURE:GIANT_GILA_MONSTER:KIDNEY -CREATURE:DOG:MUSCLE -CREATURE:DOG:EYE -CREATURE:DOG:BRAIN -CREATURE:DOG:LUNG -CREATURE:DOG:HEART -CREATURE:DOG:LIVER -CREATURE:DOG:GUT -CREATURE:DOG:STOMACH -CREATURE:DOG:GIZZARD -CREATURE:DOG:PANCREAS -CREATURE:DOG:SPLEEN -CREATURE:DOG:KIDNEY -CREATURE:CAT:MUSCLE -CREATURE:CAT:EYE -CREATURE:CAT:BRAIN -CREATURE:CAT:LUNG -CREATURE:CAT:HEART -CREATURE:CAT:LIVER -CREATURE:CAT:GUT -CREATURE:CAT:STOMACH -CREATURE:CAT:GIZZARD -CREATURE:CAT:PANCREAS -CREATURE:CAT:SPLEEN -CREATURE:CAT:KIDNEY -CREATURE:MULE:MUSCLE -CREATURE:MULE:EYE -CREATURE:MULE:BRAIN -CREATURE:MULE:LUNG -CREATURE:MULE:HEART -CREATURE:MULE:LIVER -CREATURE:MULE:GUT -CREATURE:MULE:STOMACH -CREATURE:MULE:GIZZARD -CREATURE:MULE:PANCREAS -CREATURE:MULE:SPLEEN -CREATURE:MULE:KIDNEY -CREATURE:DONKEY:MUSCLE -CREATURE:DONKEY:EYE -CREATURE:DONKEY:BRAIN -CREATURE:DONKEY:LUNG -CREATURE:DONKEY:HEART -CREATURE:DONKEY:LIVER -CREATURE:DONKEY:GUT -CREATURE:DONKEY:STOMACH -CREATURE:DONKEY:GIZZARD -CREATURE:DONKEY:PANCREAS -CREATURE:DONKEY:SPLEEN -CREATURE:DONKEY:KIDNEY -CREATURE:HORSE:MUSCLE -CREATURE:HORSE:EYE -CREATURE:HORSE:BRAIN -CREATURE:HORSE:LUNG -CREATURE:HORSE:HEART -CREATURE:HORSE:LIVER -CREATURE:HORSE:GUT -CREATURE:HORSE:STOMACH -CREATURE:HORSE:GIZZARD -CREATURE:HORSE:PANCREAS -CREATURE:HORSE:SPLEEN -CREATURE:HORSE:KIDNEY -CREATURE:COW:MUSCLE -CREATURE:COW:EYE -CREATURE:COW:BRAIN -CREATURE:COW:LUNG -CREATURE:COW:HEART -CREATURE:COW:LIVER -CREATURE:COW:GUT -CREATURE:COW:STOMACH -CREATURE:COW:GIZZARD -CREATURE:COW:PANCREAS -CREATURE:COW:SPLEEN -CREATURE:COW:KIDNEY -CREATURE:SHEEP:MUSCLE -CREATURE:SHEEP:EYE -CREATURE:SHEEP:BRAIN -CREATURE:SHEEP:LUNG -CREATURE:SHEEP:HEART -CREATURE:SHEEP:LIVER -CREATURE:SHEEP:GUT -CREATURE:SHEEP:STOMACH -CREATURE:SHEEP:GIZZARD -CREATURE:SHEEP:PANCREAS -CREATURE:SHEEP:SPLEEN -CREATURE:SHEEP:KIDNEY -CREATURE:PIG:MUSCLE -CREATURE:PIG:EYE -CREATURE:PIG:BRAIN -CREATURE:PIG:LUNG -CREATURE:PIG:HEART -CREATURE:PIG:LIVER -CREATURE:PIG:GUT -CREATURE:PIG:STOMACH -CREATURE:PIG:GIZZARD -CREATURE:PIG:PANCREAS -CREATURE:PIG:SPLEEN -CREATURE:PIG:KIDNEY -CREATURE:GOAT:MUSCLE -CREATURE:GOAT:EYE -CREATURE:GOAT:BRAIN -CREATURE:GOAT:LUNG -CREATURE:GOAT:HEART -CREATURE:GOAT:LIVER -CREATURE:GOAT:GUT -CREATURE:GOAT:STOMACH -CREATURE:GOAT:GIZZARD -CREATURE:GOAT:PANCREAS -CREATURE:GOAT:SPLEEN -CREATURE:GOAT:KIDNEY -CREATURE:BIRD_CHICKEN:MUSCLE -CREATURE:BIRD_CHICKEN:EYE -CREATURE:BIRD_CHICKEN:BRAIN -CREATURE:BIRD_CHICKEN:LUNG -CREATURE:BIRD_CHICKEN:HEART -CREATURE:BIRD_CHICKEN:LIVER -CREATURE:BIRD_CHICKEN:GUT -CREATURE:BIRD_CHICKEN:STOMACH -CREATURE:BIRD_CHICKEN:GIZZARD -CREATURE:BIRD_CHICKEN:PANCREAS -CREATURE:BIRD_CHICKEN:SPLEEN -CREATURE:BIRD_CHICKEN:KIDNEY -CREATURE:CAVY:MUSCLE -CREATURE:CAVY:EYE -CREATURE:CAVY:BRAIN -CREATURE:CAVY:LUNG -CREATURE:CAVY:HEART -CREATURE:CAVY:LIVER -CREATURE:CAVY:GUT -CREATURE:CAVY:STOMACH -CREATURE:CAVY:GIZZARD -CREATURE:CAVY:PANCREAS -CREATURE:CAVY:SPLEEN -CREATURE:CAVY:KIDNEY -CREATURE:BIRD_DUCK:MUSCLE -CREATURE:BIRD_DUCK:EYE -CREATURE:BIRD_DUCK:BRAIN -CREATURE:BIRD_DUCK:LUNG -CREATURE:BIRD_DUCK:HEART -CREATURE:BIRD_DUCK:LIVER -CREATURE:BIRD_DUCK:GUT -CREATURE:BIRD_DUCK:STOMACH -CREATURE:BIRD_DUCK:GIZZARD -CREATURE:BIRD_DUCK:PANCREAS -CREATURE:BIRD_DUCK:SPLEEN -CREATURE:BIRD_DUCK:KIDNEY -CREATURE:WATER_BUFFALO:MUSCLE -CREATURE:WATER_BUFFALO:EYE -CREATURE:WATER_BUFFALO:BRAIN -CREATURE:WATER_BUFFALO:LUNG -CREATURE:WATER_BUFFALO:HEART -CREATURE:WATER_BUFFALO:LIVER -CREATURE:WATER_BUFFALO:GUT -CREATURE:WATER_BUFFALO:STOMACH -CREATURE:WATER_BUFFALO:GIZZARD -CREATURE:WATER_BUFFALO:PANCREAS -CREATURE:WATER_BUFFALO:SPLEEN -CREATURE:WATER_BUFFALO:KIDNEY -CREATURE:REINDEER:MUSCLE -CREATURE:REINDEER:EYE -CREATURE:REINDEER:BRAIN -CREATURE:REINDEER:LUNG -CREATURE:REINDEER:HEART -CREATURE:REINDEER:LIVER -CREATURE:REINDEER:GUT -CREATURE:REINDEER:STOMACH -CREATURE:REINDEER:GIZZARD -CREATURE:REINDEER:PANCREAS -CREATURE:REINDEER:SPLEEN -CREATURE:REINDEER:KIDNEY -CREATURE:BIRD_GOOSE:MUSCLE -CREATURE:BIRD_GOOSE:EYE -CREATURE:BIRD_GOOSE:BRAIN -CREATURE:BIRD_GOOSE:LUNG -CREATURE:BIRD_GOOSE:HEART -CREATURE:BIRD_GOOSE:LIVER -CREATURE:BIRD_GOOSE:GUT -CREATURE:BIRD_GOOSE:STOMACH -CREATURE:BIRD_GOOSE:GIZZARD -CREATURE:BIRD_GOOSE:PANCREAS -CREATURE:BIRD_GOOSE:SPLEEN -CREATURE:BIRD_GOOSE:KIDNEY -CREATURE:YAK:MUSCLE -CREATURE:YAK:EYE -CREATURE:YAK:BRAIN -CREATURE:YAK:LUNG -CREATURE:YAK:HEART -CREATURE:YAK:LIVER -CREATURE:YAK:GUT -CREATURE:YAK:STOMACH -CREATURE:YAK:GIZZARD -CREATURE:YAK:PANCREAS -CREATURE:YAK:SPLEEN -CREATURE:YAK:KIDNEY -CREATURE:LLAMA:MUSCLE -CREATURE:LLAMA:EYE -CREATURE:LLAMA:BRAIN -CREATURE:LLAMA:LUNG -CREATURE:LLAMA:HEART -CREATURE:LLAMA:LIVER -CREATURE:LLAMA:GUT -CREATURE:LLAMA:STOMACH -CREATURE:LLAMA:GIZZARD -CREATURE:LLAMA:PANCREAS -CREATURE:LLAMA:SPLEEN -CREATURE:LLAMA:KIDNEY -CREATURE:ALPACA:MUSCLE -CREATURE:ALPACA:EYE -CREATURE:ALPACA:BRAIN -CREATURE:ALPACA:LUNG -CREATURE:ALPACA:HEART -CREATURE:ALPACA:LIVER -CREATURE:ALPACA:GUT -CREATURE:ALPACA:STOMACH -CREATURE:ALPACA:GIZZARD -CREATURE:ALPACA:PANCREAS -CREATURE:ALPACA:SPLEEN -CREATURE:ALPACA:KIDNEY -CREATURE:BIRD_GUINEAFOWL:MUSCLE -CREATURE:BIRD_GUINEAFOWL:EYE -CREATURE:BIRD_GUINEAFOWL:BRAIN -CREATURE:BIRD_GUINEAFOWL:LUNG -CREATURE:BIRD_GUINEAFOWL:HEART -CREATURE:BIRD_GUINEAFOWL:LIVER -CREATURE:BIRD_GUINEAFOWL:GUT - CREATURE:BIRD_GUINEAFOWL:STOMACH - CREATURE:BIRD_GUINEAFOWL:GIZZARD -!CREATURE:BIRD_GUINEAFOWL:PANCREAS -CREATURE:BIRD_GUINEAFOWL:SPLEEN -CREATURE:BIRD_GUINEAFOWL:KIDNEY -!CREATURE:BIRD_PEAFOWL_BLUE:MUSCLE -CREATURE:BIRD_PEAFOWL_BLUE:EYE - CREATURE:BIRD_PEAFOWL_BLUE:BRAIN -CREATURE:BIRD_PEAFOWL_BLUE:LUNG - CREATURE:BIRD_PEAFOWL_BLUE:HEART - CREATURE:BIRD_PEAFOWL_BLUE:LIVER -CREATURE:BIRD_PEAFOWL_BLUE:GUT -"CREATURE:BIRD_PEAFOWL_BLUE:STOMACH -"CREATURE:BIRD_PEAFOWL_BLUE:GIZZARD -#CREATURE:BIRD_PEAFOWL_BLUE:PANCREAS -!CREATURE:BIRD_PEAFOWL_BLUE:SPLEEN -!CREATURE:BIRD_PEAFOWL_BLUE:KIDNEY -CREATURE:BIRD_TURKEY:MUSCLE -CREATURE:BIRD_TURKEY:EYE -CREATURE:BIRD_TURKEY:BRAIN -CREATURE:BIRD_TURKEY:LUNG -CREATURE:BIRD_TURKEY:HEART -CREATURE:BIRD_TURKEY:LIVER -CREATURE:BIRD_TURKEY:GUT -CREATURE:BIRD_TURKEY:STOMACH -CREATURE:BIRD_TURKEY:GIZZARD -CREATURE:BIRD_TURKEY:PANCREAS -CREATURE:BIRD_TURKEY:SPLEEN -CREATURE:BIRD_TURKEY:KIDNEY -CREATURE:RABBIT:MUSCLE -CREATURE:RABBIT:EYE -CREATURE:RABBIT:BRAIN -CREATURE:RABBIT:LUNG -CREATURE:RABBIT:HEART -CREATURE:RABBIT:LIVER -CREATURE:RABBIT:GUT -CREATURE:RABBIT:STOMACH -CREATURE:RABBIT:GIZZARD -CREATURE:RABBIT:PANCREAS -CREATURE:RABBIT:SPLEEN -CREATURE:RABBIT:KIDNEY -CREATURE:FLY:MUSCLE -CREATURE:FLY:EYE -CREATURE:FLY:BRAIN -CREATURE:FLY:LUNG -CREATURE:FLY:HEART -CREATURE:FLY:LIVER -CREATURE:FLY:GUT -CREATURE:FLY:STOMACH -CREATURE:FLY:GIZZARD -CREATURE:FLY:PANCREAS -CREATURE:FLY:SPLEEN -CREATURE:FLY:KIDNEY -CREATURE:FLY_MAN:MUSCLE -CREATURE:FLY_MAN:EYE -CREATURE:FLY_MAN:BRAIN -CREATURE:FLY_MAN:LUNG -CREATURE:FLY_MAN:HEART -CREATURE:FLY_MAN:LIVER -CREATURE:FLY_MAN:GUT -CREATURE:FLY_MAN:STOMACH -CREATURE:FLY_MAN:GIZZARD -CREATURE:FLY_MAN:PANCREAS -CREATURE:FLY_MAN:SPLEEN -CREATURE:FLY_MAN:KIDNEY -CREATURE:GIANT_FLY:MUSCLE -CREATURE:GIANT_FLY:EYE -CREATURE:GIANT_FLY:BRAIN -CREATURE:GIANT_FLY:LUNG -CREATURE:GIANT_FLY:HEART -CREATURE:GIANT_FLY:LIVER -CREATURE:GIANT_FLY:GUT -CREATURE:GIANT_FLY:STOMACH -CREATURE:GIANT_FLY:GIZZARD -CREATURE:GIANT_FLY:PANCREAS -CREATURE:GIANT_FLY:SPLEEN -CREATURE:GIANT_FLY:KIDNEY -CREATURE:ROACH_LARGE:MUSCLE -CREATURE:ROACH_LARGE:EYE -CREATURE:ROACH_LARGE:BRAIN -CREATURE:ROACH_LARGE:LUNG -CREATURE:ROACH_LARGE:HEART -CREATURE:ROACH_LARGE:LIVER -CREATURE:ROACH_LARGE:GUT -CREATURE:ROACH_LARGE:STOMACH -CREATURE:ROACH_LARGE:GIZZARD -CREATURE:ROACH_LARGE:PANCREAS -CREATURE:ROACH_LARGE:SPLEEN -CREATURE:ROACH_LARGE:KIDNEY -CREATURE:ROACH_MAN:MUSCLE -CREATURE:ROACH_MAN:EYE -CREATURE:ROACH_MAN:BRAIN -CREATURE:ROACH_MAN:LUNG -CREATURE:ROACH_MAN:HEART -CREATURE:ROACH_MAN:LIVER -CREATURE:ROACH_MAN:GUT -CREATURE:ROACH_MAN:STOMACH -CREATURE:ROACH_MAN:GIZZARD -CREATURE:ROACH_MAN:PANCREAS -CREATURE:ROACH_MAN:SPLEEN -CREATURE:ROACH_MAN:KIDNEY -CREATURE:GIANT_ROACH:MUSCLE -CREATURE:GIANT_ROACH:EYE -CREATURE:GIANT_ROACH:BRAIN -CREATURE:GIANT_ROACH:LUNG -CREATURE:GIANT_ROACH:HEART -CREATURE:GIANT_ROACH:LIVER -CREATURE:GIANT_ROACH:GUT -CREATURE:GIANT_ROACH:STOMACH -CREATURE:GIANT_ROACH:GIZZARD -CREATURE:GIANT_ROACH:PANCREAS -CREATURE:GIANT_ROACH:SPLEEN -CREATURE:GIANT_ROACH:KIDNEY -CREATURE:BEETLE:MUSCLE -CREATURE:BEETLE:EYE -CREATURE:BEETLE:BRAIN -CREATURE:BEETLE:LUNG -CREATURE:BEETLE:HEART -CREATURE:BEETLE:LIVER -CREATURE:BEETLE:GUT -CREATURE:BEETLE:STOMACH -CREATURE:BEETLE:GIZZARD -CREATURE:BEETLE:PANCREAS -CREATURE:BEETLE:SPLEEN -CREATURE:BEETLE:KIDNEY -CREATURE:BEETLE_MAN:MUSCLE -CREATURE:BEETLE_MAN:EYE -CREATURE:BEETLE_MAN:BRAIN -CREATURE:BEETLE_MAN:LUNG -CREATURE:BEETLE_MAN:HEART -CREATURE:BEETLE_MAN:LIVER -CREATURE:BEETLE_MAN:GUT -CREATURE:BEETLE_MAN:STOMACH -CREATURE:BEETLE_MAN:GIZZARD -CREATURE:BEETLE_MAN:PANCREAS -CREATURE:BEETLE_MAN:SPLEEN -CREATURE:BEETLE_MAN:KIDNEY -CREATURE:GIANT_BEETLE:MUSCLE -CREATURE:GIANT_BEETLE:EYE -CREATURE:GIANT_BEETLE:BRAIN -CREATURE:GIANT_BEETLE:LUNG -CREATURE:GIANT_BEETLE:HEART -CREATURE:GIANT_BEETLE:LIVER -CREATURE:GIANT_BEETLE:GUT -CREATURE:GIANT_BEETLE:STOMACH -CREATURE:GIANT_BEETLE:GIZZARD -CREATURE:GIANT_BEETLE:PANCREAS -CREATURE:GIANT_BEETLE:SPLEEN -CREATURE:GIANT_BEETLE:KIDNEY -CREATURE:ANT:MUSCLE -CREATURE:ANT:EYE -CREATURE:ANT:BRAIN -CREATURE:ANT:LUNG -CREATURE:ANT:HEART -CREATURE:ANT:LIVER -CREATURE:ANT:GUT -CREATURE:ANT:STOMACH -CREATURE:ANT:GIZZARD -CREATURE:ANT:PANCREAS -CREATURE:ANT:SPLEEN -CREATURE:ANT:KIDNEY -!CREATURE:BUTTERFLY_MONARCH:MUSCLE -CREATURE:BUTTERFLY_MONARCH:EYE - CREATURE:BUTTERFLY_MONARCH:BRAIN -CREATURE:BUTTERFLY_MONARCH:LUNG - CREATURE:BUTTERFLY_MONARCH:HEART - CREATURE:BUTTERFLY_MONARCH:LIVER -CREATURE:BUTTERFLY_MONARCH:GUT -"CREATURE:BUTTERFLY_MONARCH:STOMACH -"CREATURE:BUTTERFLY_MONARCH:GIZZARD -#CREATURE:BUTTERFLY_MONARCH:PANCREAS -!CREATURE:BUTTERFLY_MONARCH:SPLEEN -!CREATURE:BUTTERFLY_MONARCH:KIDNEY -%CREATURE:BUTTERFLY_MONARCH_MAN:MUSCLE -"CREATURE:BUTTERFLY_MONARCH_MAN:EYE -$CREATURE:BUTTERFLY_MONARCH_MAN:BRAIN -#CREATURE:BUTTERFLY_MONARCH_MAN:LUNG -$CREATURE:BUTTERFLY_MONARCH_MAN:HEART -$CREATURE:BUTTERFLY_MONARCH_MAN:LIVER -"CREATURE:BUTTERFLY_MONARCH_MAN:GUT -&CREATURE:BUTTERFLY_MONARCH_MAN:STOMACH -&CREATURE:BUTTERFLY_MONARCH_MAN:GIZZARD -'CREATURE:BUTTERFLY_MONARCH_MAN:PANCREAS -%CREATURE:BUTTERFLY_MONARCH_MAN:SPLEEN -%CREATURE:BUTTERFLY_MONARCH_MAN:KIDNEY -'CREATURE:GIANT_BUTTERFLY_MONARCH:MUSCLE -$CREATURE:GIANT_BUTTERFLY_MONARCH:EYE -&CREATURE:GIANT_BUTTERFLY_MONARCH:BRAIN -%CREATURE:GIANT_BUTTERFLY_MONARCH:LUNG -&CREATURE:GIANT_BUTTERFLY_MONARCH:HEART -&CREATURE:GIANT_BUTTERFLY_MONARCH:LIVER -$CREATURE:GIANT_BUTTERFLY_MONARCH:GUT -(CREATURE:GIANT_BUTTERFLY_MONARCH:STOMACH -(CREATURE:GIANT_BUTTERFLY_MONARCH:GIZZARD -)CREATURE:GIANT_BUTTERFLY_MONARCH:PANCREAS -'CREATURE:GIANT_BUTTERFLY_MONARCH:SPLEEN -'CREATURE:GIANT_BUTTERFLY_MONARCH:KIDNEY -CREATURE:FIREFLY:MUSCLE -CREATURE:FIREFLY:EYE -CREATURE:FIREFLY:BRAIN -CREATURE:FIREFLY:LUNG -CREATURE:FIREFLY:HEART -CREATURE:FIREFLY:LIVER -CREATURE:FIREFLY:GUT -CREATURE:FIREFLY:STOMACH -CREATURE:FIREFLY:GIZZARD -CREATURE:FIREFLY:PANCREAS -CREATURE:FIREFLY:SPLEEN -CREATURE:FIREFLY:KIDNEY -CREATURE:FIREFLY_MAN:MUSCLE -CREATURE:FIREFLY_MAN:EYE -CREATURE:FIREFLY_MAN:BRAIN -CREATURE:FIREFLY_MAN:LUNG -CREATURE:FIREFLY_MAN:HEART -CREATURE:FIREFLY_MAN:LIVER -CREATURE:FIREFLY_MAN:GUT -CREATURE:FIREFLY_MAN:STOMACH -CREATURE:FIREFLY_MAN:GIZZARD -CREATURE:FIREFLY_MAN:PANCREAS -CREATURE:FIREFLY_MAN:SPLEEN -CREATURE:FIREFLY_MAN:KIDNEY -CREATURE:GIANT_FIREFLY:MUSCLE -CREATURE:GIANT_FIREFLY:EYE -CREATURE:GIANT_FIREFLY:BRAIN -CREATURE:GIANT_FIREFLY:LUNG -CREATURE:GIANT_FIREFLY:HEART -CREATURE:GIANT_FIREFLY:LIVER -CREATURE:GIANT_FIREFLY:GUT -CREATURE:GIANT_FIREFLY:STOMACH -CREATURE:GIANT_FIREFLY:GIZZARD -CREATURE:GIANT_FIREFLY:PANCREAS -CREATURE:GIANT_FIREFLY:SPLEEN -CREATURE:GIANT_FIREFLY:KIDNEY -CREATURE:DRAGONFLY:MUSCLE -CREATURE:DRAGONFLY:EYE -CREATURE:DRAGONFLY:BRAIN -CREATURE:DRAGONFLY:LUNG -CREATURE:DRAGONFLY:HEART -CREATURE:DRAGONFLY:LIVER -CREATURE:DRAGONFLY:GUT -CREATURE:DRAGONFLY:STOMACH -CREATURE:DRAGONFLY:GIZZARD -CREATURE:DRAGONFLY:PANCREAS -CREATURE:DRAGONFLY:SPLEEN -CREATURE:DRAGONFLY:KIDNEY -CREATURE:DRAGONFLY_MAN:MUSCLE -CREATURE:DRAGONFLY_MAN:EYE -CREATURE:DRAGONFLY_MAN:BRAIN -CREATURE:DRAGONFLY_MAN:LUNG -CREATURE:DRAGONFLY_MAN:HEART -CREATURE:DRAGONFLY_MAN:LIVER -CREATURE:DRAGONFLY_MAN:GUT -CREATURE:DRAGONFLY_MAN:STOMACH -CREATURE:DRAGONFLY_MAN:GIZZARD -CREATURE:DRAGONFLY_MAN:PANCREAS -CREATURE:DRAGONFLY_MAN:SPLEEN -CREATURE:DRAGONFLY_MAN:KIDNEY -CREATURE:GIANT_DRAGONFLY:MUSCLE -CREATURE:GIANT_DRAGONFLY:EYE -CREATURE:GIANT_DRAGONFLY:BRAIN -CREATURE:GIANT_DRAGONFLY:LUNG -CREATURE:GIANT_DRAGONFLY:HEART -CREATURE:GIANT_DRAGONFLY:LIVER -CREATURE:GIANT_DRAGONFLY:GUT - CREATURE:GIANT_DRAGONFLY:STOMACH - CREATURE:GIANT_DRAGONFLY:GIZZARD -!CREATURE:GIANT_DRAGONFLY:PANCREAS -CREATURE:GIANT_DRAGONFLY:SPLEEN -CREATURE:GIANT_DRAGONFLY:KIDNEY -CREATURE:HONEY_BEE:MUSCLE -CREATURE:HONEY_BEE:EYE -CREATURE:HONEY_BEE:BRAIN -CREATURE:HONEY_BEE:LUNG -CREATURE:HONEY_BEE:HEART -CREATURE:HONEY_BEE:LIVER -CREATURE:HONEY_BEE:GUT -CREATURE:HONEY_BEE:STOMACH -CREATURE:HONEY_BEE:GIZZARD -CREATURE:HONEY_BEE:PANCREAS -CREATURE:HONEY_BEE:SPLEEN -CREATURE:HONEY_BEE:KIDNEY -CREATURE:BUMBLEBEE:MUSCLE -CREATURE:BUMBLEBEE:EYE -CREATURE:BUMBLEBEE:BRAIN -CREATURE:BUMBLEBEE:LUNG -CREATURE:BUMBLEBEE:HEART -CREATURE:BUMBLEBEE:LIVER -CREATURE:BUMBLEBEE:GUT -CREATURE:BUMBLEBEE:STOMACH -CREATURE:BUMBLEBEE:GIZZARD -CREATURE:BUMBLEBEE:PANCREAS -CREATURE:BUMBLEBEE:SPLEEN -CREATURE:BUMBLEBEE:KIDNEY -CREATURE:GOAT_MOUNTAIN:MUSCLE -CREATURE:GOAT_MOUNTAIN:EYE -CREATURE:GOAT_MOUNTAIN:BRAIN -CREATURE:GOAT_MOUNTAIN:LUNG -CREATURE:GOAT_MOUNTAIN:HEART -CREATURE:GOAT_MOUNTAIN:LIVER -CREATURE:GOAT_MOUNTAIN:GUT -CREATURE:GOAT_MOUNTAIN:STOMACH -CREATURE:GOAT_MOUNTAIN:GIZZARD -CREATURE:GOAT_MOUNTAIN:PANCREAS -CREATURE:GOAT_MOUNTAIN:SPLEEN -CREATURE:GOAT_MOUNTAIN:KIDNEY -!CREATURE:GOAT_MOUNTAIN_MAN:MUSCLE -CREATURE:GOAT_MOUNTAIN_MAN:EYE - CREATURE:GOAT_MOUNTAIN_MAN:BRAIN -CREATURE:GOAT_MOUNTAIN_MAN:LUNG - CREATURE:GOAT_MOUNTAIN_MAN:HEART - CREATURE:GOAT_MOUNTAIN_MAN:LIVER -CREATURE:GOAT_MOUNTAIN_MAN:GUT -"CREATURE:GOAT_MOUNTAIN_MAN:STOMACH -"CREATURE:GOAT_MOUNTAIN_MAN:GIZZARD -#CREATURE:GOAT_MOUNTAIN_MAN:PANCREAS -!CREATURE:GOAT_MOUNTAIN_MAN:SPLEEN -!CREATURE:GOAT_MOUNTAIN_MAN:KIDNEY -#CREATURE:GIANT_GOAT_MOUNTAIN:MUSCLE - CREATURE:GIANT_GOAT_MOUNTAIN:EYE -"CREATURE:GIANT_GOAT_MOUNTAIN:BRAIN -!CREATURE:GIANT_GOAT_MOUNTAIN:LUNG -"CREATURE:GIANT_GOAT_MOUNTAIN:HEART -"CREATURE:GIANT_GOAT_MOUNTAIN:LIVER - CREATURE:GIANT_GOAT_MOUNTAIN:GUT -$CREATURE:GIANT_GOAT_MOUNTAIN:STOMACH -$CREATURE:GIANT_GOAT_MOUNTAIN:GIZZARD -%CREATURE:GIANT_GOAT_MOUNTAIN:PANCREAS -#CREATURE:GIANT_GOAT_MOUNTAIN:SPLEEN -#CREATURE:GIANT_GOAT_MOUNTAIN:KIDNEY -CREATURE:MARMOT_HOARY:MUSCLE -CREATURE:MARMOT_HOARY:EYE -CREATURE:MARMOT_HOARY:BRAIN -CREATURE:MARMOT_HOARY:LUNG -CREATURE:MARMOT_HOARY:HEART -CREATURE:MARMOT_HOARY:LIVER -CREATURE:MARMOT_HOARY:GUT -CREATURE:MARMOT_HOARY:STOMACH -CREATURE:MARMOT_HOARY:GIZZARD -CREATURE:MARMOT_HOARY:PANCREAS -CREATURE:MARMOT_HOARY:SPLEEN -CREATURE:MARMOT_HOARY:KIDNEY - CREATURE:MARMOT_HOARY_MAN:MUSCLE -CREATURE:MARMOT_HOARY_MAN:EYE -CREATURE:MARMOT_HOARY_MAN:BRAIN -CREATURE:MARMOT_HOARY_MAN:LUNG -CREATURE:MARMOT_HOARY_MAN:HEART -CREATURE:MARMOT_HOARY_MAN:LIVER -CREATURE:MARMOT_HOARY_MAN:GUT -!CREATURE:MARMOT_HOARY_MAN:STOMACH -!CREATURE:MARMOT_HOARY_MAN:GIZZARD -"CREATURE:MARMOT_HOARY_MAN:PANCREAS - CREATURE:MARMOT_HOARY_MAN:SPLEEN - CREATURE:MARMOT_HOARY_MAN:KIDNEY -"CREATURE:GIANT_MARMOT_HOARY:MUSCLE -CREATURE:GIANT_MARMOT_HOARY:EYE -!CREATURE:GIANT_MARMOT_HOARY:BRAIN - CREATURE:GIANT_MARMOT_HOARY:LUNG -!CREATURE:GIANT_MARMOT_HOARY:HEART -!CREATURE:GIANT_MARMOT_HOARY:LIVER -CREATURE:GIANT_MARMOT_HOARY:GUT -#CREATURE:GIANT_MARMOT_HOARY:STOMACH -#CREATURE:GIANT_MARMOT_HOARY:GIZZARD -$CREATURE:GIANT_MARMOT_HOARY:PANCREAS -"CREATURE:GIANT_MARMOT_HOARY:SPLEEN -"CREATURE:GIANT_MARMOT_HOARY:KIDNEY -CREATURE:GNOME_MOUNTAIN:MUSCLE -CREATURE:GNOME_MOUNTAIN:EYE -CREATURE:GNOME_MOUNTAIN:BRAIN -CREATURE:GNOME_MOUNTAIN:LUNG -CREATURE:GNOME_MOUNTAIN:HEART -CREATURE:GNOME_MOUNTAIN:LIVER -CREATURE:GNOME_MOUNTAIN:GUT -CREATURE:GNOME_MOUNTAIN:STOMACH -CREATURE:GNOME_MOUNTAIN:GIZZARD - CREATURE:GNOME_MOUNTAIN:PANCREAS -CREATURE:GNOME_MOUNTAIN:SPLEEN -CREATURE:GNOME_MOUNTAIN:KIDNEY -CREATURE:GNOME_DARK:MUSCLE -CREATURE:GNOME_DARK:EYE -CREATURE:GNOME_DARK:BRAIN -CREATURE:GNOME_DARK:LUNG -CREATURE:GNOME_DARK:HEART -CREATURE:GNOME_DARK:LIVER -CREATURE:GNOME_DARK:GUT -CREATURE:GNOME_DARK:STOMACH -CREATURE:GNOME_DARK:GIZZARD -CREATURE:GNOME_DARK:PANCREAS -CREATURE:GNOME_DARK:SPLEEN -CREATURE:GNOME_DARK:KIDNEY -CREATURE:WALRUS:MUSCLE -CREATURE:WALRUS:EYE -CREATURE:WALRUS:BRAIN -CREATURE:WALRUS:LUNG -CREATURE:WALRUS:HEART -CREATURE:WALRUS:LIVER -CREATURE:WALRUS:GUT -CREATURE:WALRUS:STOMACH -CREATURE:WALRUS:GIZZARD -CREATURE:WALRUS:PANCREAS -CREATURE:WALRUS:SPLEEN -CREATURE:WALRUS:KIDNEY -CREATURE:WALRUS_MAN:MUSCLE -CREATURE:WALRUS_MAN:EYE -CREATURE:WALRUS_MAN:BRAIN -CREATURE:WALRUS_MAN:LUNG -CREATURE:WALRUS_MAN:HEART -CREATURE:WALRUS_MAN:LIVER -CREATURE:WALRUS_MAN:GUT -CREATURE:WALRUS_MAN:STOMACH -CREATURE:WALRUS_MAN:GIZZARD -CREATURE:WALRUS_MAN:PANCREAS -CREATURE:WALRUS_MAN:SPLEEN -CREATURE:WALRUS_MAN:KIDNEY -CREATURE:GIANT_WALRUS:MUSCLE -CREATURE:GIANT_WALRUS:EYE -CREATURE:GIANT_WALRUS:BRAIN -CREATURE:GIANT_WALRUS:LUNG -CREATURE:GIANT_WALRUS:HEART -CREATURE:GIANT_WALRUS:LIVER -CREATURE:GIANT_WALRUS:GUT -CREATURE:GIANT_WALRUS:STOMACH -CREATURE:GIANT_WALRUS:GIZZARD -CREATURE:GIANT_WALRUS:PANCREAS -CREATURE:GIANT_WALRUS:SPLEEN -CREATURE:GIANT_WALRUS:KIDNEY - CREATURE:FISH_LAMPREY_SEA:MUSCLE -CREATURE:FISH_LAMPREY_SEA:EYE -CREATURE:FISH_LAMPREY_SEA:BRAIN -CREATURE:FISH_LAMPREY_SEA:LUNG -CREATURE:FISH_LAMPREY_SEA:HEART -CREATURE:FISH_LAMPREY_SEA:LIVER -CREATURE:FISH_LAMPREY_SEA:GUT -!CREATURE:FISH_LAMPREY_SEA:STOMACH -!CREATURE:FISH_LAMPREY_SEA:GIZZARD -"CREATURE:FISH_LAMPREY_SEA:PANCREAS - CREATURE:FISH_LAMPREY_SEA:SPLEEN - CREATURE:FISH_LAMPREY_SEA:KIDNEY -!CREATURE:SHARK_GREAT_WHITE:MUSCLE -CREATURE:SHARK_GREAT_WHITE:EYE - CREATURE:SHARK_GREAT_WHITE:BRAIN -CREATURE:SHARK_GREAT_WHITE:LUNG - CREATURE:SHARK_GREAT_WHITE:HEART - CREATURE:SHARK_GREAT_WHITE:LIVER -CREATURE:SHARK_GREAT_WHITE:GUT -"CREATURE:SHARK_GREAT_WHITE:STOMACH -"CREATURE:SHARK_GREAT_WHITE:GIZZARD -#CREATURE:SHARK_GREAT_WHITE:PANCREAS -!CREATURE:SHARK_GREAT_WHITE:SPLEEN -!CREATURE:SHARK_GREAT_WHITE:KIDNEY -CREATURE:SHARK_FRILL:MUSCLE -CREATURE:SHARK_FRILL:EYE -CREATURE:SHARK_FRILL:BRAIN -CREATURE:SHARK_FRILL:LUNG -CREATURE:SHARK_FRILL:HEART -CREATURE:SHARK_FRILL:LIVER -CREATURE:SHARK_FRILL:GUT -CREATURE:SHARK_FRILL:STOMACH -CREATURE:SHARK_FRILL:GIZZARD -CREATURE:SHARK_FRILL:PANCREAS -CREATURE:SHARK_FRILL:SPLEEN -CREATURE:SHARK_FRILL:KIDNEY -#CREATURE:SHARK_SPINY_DOGFISH:MUSCLE - CREATURE:SHARK_SPINY_DOGFISH:EYE -"CREATURE:SHARK_SPINY_DOGFISH:BRAIN -!CREATURE:SHARK_SPINY_DOGFISH:LUNG -"CREATURE:SHARK_SPINY_DOGFISH:HEART -"CREATURE:SHARK_SPINY_DOGFISH:LIVER - CREATURE:SHARK_SPINY_DOGFISH:GUT -$CREATURE:SHARK_SPINY_DOGFISH:STOMACH -$CREATURE:SHARK_SPINY_DOGFISH:GIZZARD -%CREATURE:SHARK_SPINY_DOGFISH:PANCREAS -#CREATURE:SHARK_SPINY_DOGFISH:SPLEEN -#CREATURE:SHARK_SPINY_DOGFISH:KIDNEY -'CREATURE:SHARK_WOBBEGONG_SPOTTED:MUSCLE -$CREATURE:SHARK_WOBBEGONG_SPOTTED:EYE -&CREATURE:SHARK_WOBBEGONG_SPOTTED:BRAIN -%CREATURE:SHARK_WOBBEGONG_SPOTTED:LUNG -&CREATURE:SHARK_WOBBEGONG_SPOTTED:HEART -&CREATURE:SHARK_WOBBEGONG_SPOTTED:LIVER -$CREATURE:SHARK_WOBBEGONG_SPOTTED:GUT -(CREATURE:SHARK_WOBBEGONG_SPOTTED:STOMACH -(CREATURE:SHARK_WOBBEGONG_SPOTTED:GIZZARD -)CREATURE:SHARK_WOBBEGONG_SPOTTED:PANCREAS -'CREATURE:SHARK_WOBBEGONG_SPOTTED:SPLEEN -'CREATURE:SHARK_WOBBEGONG_SPOTTED:KIDNEY -CREATURE:SHARK_WHALE:MUSCLE -CREATURE:SHARK_WHALE:EYE -CREATURE:SHARK_WHALE:BRAIN -CREATURE:SHARK_WHALE:LUNG -CREATURE:SHARK_WHALE:HEART -CREATURE:SHARK_WHALE:LIVER -CREATURE:SHARK_WHALE:GUT -CREATURE:SHARK_WHALE:STOMACH -CREATURE:SHARK_WHALE:GIZZARD -CREATURE:SHARK_WHALE:PANCREAS -CREATURE:SHARK_WHALE:SPLEEN -CREATURE:SHARK_WHALE:KIDNEY -CREATURE:SHARK_BASKING:MUSCLE -CREATURE:SHARK_BASKING:EYE -CREATURE:SHARK_BASKING:BRAIN -CREATURE:SHARK_BASKING:LUNG -CREATURE:SHARK_BASKING:HEART -CREATURE:SHARK_BASKING:LIVER -CREATURE:SHARK_BASKING:GUT -CREATURE:SHARK_BASKING:STOMACH -CREATURE:SHARK_BASKING:GIZZARD -CREATURE:SHARK_BASKING:PANCREAS -CREATURE:SHARK_BASKING:SPLEEN -CREATURE:SHARK_BASKING:KIDNEY -CREATURE:SHARK_NURSE:MUSCLE -CREATURE:SHARK_NURSE:EYE -CREATURE:SHARK_NURSE:BRAIN -CREATURE:SHARK_NURSE:LUNG -CREATURE:SHARK_NURSE:HEART -CREATURE:SHARK_NURSE:LIVER -CREATURE:SHARK_NURSE:GUT -CREATURE:SHARK_NURSE:STOMACH -CREATURE:SHARK_NURSE:GIZZARD -CREATURE:SHARK_NURSE:PANCREAS -CREATURE:SHARK_NURSE:SPLEEN -CREATURE:SHARK_NURSE:KIDNEY -#CREATURE:SHARK_MAKO_SHORTFIN:MUSCLE - CREATURE:SHARK_MAKO_SHORTFIN:EYE -"CREATURE:SHARK_MAKO_SHORTFIN:BRAIN -!CREATURE:SHARK_MAKO_SHORTFIN:LUNG -"CREATURE:SHARK_MAKO_SHORTFIN:HEART -"CREATURE:SHARK_MAKO_SHORTFIN:LIVER - CREATURE:SHARK_MAKO_SHORTFIN:GUT -$CREATURE:SHARK_MAKO_SHORTFIN:STOMACH -$CREATURE:SHARK_MAKO_SHORTFIN:GIZZARD -%CREATURE:SHARK_MAKO_SHORTFIN:PANCREAS -#CREATURE:SHARK_MAKO_SHORTFIN:SPLEEN -#CREATURE:SHARK_MAKO_SHORTFIN:KIDNEY -"CREATURE:SHARK_MAKO_LONGFIN:MUSCLE -CREATURE:SHARK_MAKO_LONGFIN:EYE -!CREATURE:SHARK_MAKO_LONGFIN:BRAIN - CREATURE:SHARK_MAKO_LONGFIN:LUNG -!CREATURE:SHARK_MAKO_LONGFIN:HEART -!CREATURE:SHARK_MAKO_LONGFIN:LIVER -CREATURE:SHARK_MAKO_LONGFIN:GUT -#CREATURE:SHARK_MAKO_LONGFIN:STOMACH -#CREATURE:SHARK_MAKO_LONGFIN:GIZZARD -$CREATURE:SHARK_MAKO_LONGFIN:PANCREAS -"CREATURE:SHARK_MAKO_LONGFIN:SPLEEN -"CREATURE:SHARK_MAKO_LONGFIN:KIDNEY -CREATURE:SHARK_TIGER:MUSCLE -CREATURE:SHARK_TIGER:EYE -CREATURE:SHARK_TIGER:BRAIN -CREATURE:SHARK_TIGER:LUNG -CREATURE:SHARK_TIGER:HEART -CREATURE:SHARK_TIGER:LIVER -CREATURE:SHARK_TIGER:GUT -CREATURE:SHARK_TIGER:STOMACH -CREATURE:SHARK_TIGER:GIZZARD -CREATURE:SHARK_TIGER:PANCREAS -CREATURE:SHARK_TIGER:SPLEEN -CREATURE:SHARK_TIGER:KIDNEY -CREATURE:SHARK_BULL:MUSCLE -CREATURE:SHARK_BULL:EYE -CREATURE:SHARK_BULL:BRAIN -CREATURE:SHARK_BULL:LUNG -CREATURE:SHARK_BULL:HEART -CREATURE:SHARK_BULL:LIVER -CREATURE:SHARK_BULL:GUT -CREATURE:SHARK_BULL:STOMACH -CREATURE:SHARK_BULL:GIZZARD -CREATURE:SHARK_BULL:PANCREAS -CREATURE:SHARK_BULL:SPLEEN -CREATURE:SHARK_BULL:KIDNEY -#CREATURE:SHARK_REEF_BLACKTIP:MUSCLE - CREATURE:SHARK_REEF_BLACKTIP:EYE -"CREATURE:SHARK_REEF_BLACKTIP:BRAIN -!CREATURE:SHARK_REEF_BLACKTIP:LUNG -"CREATURE:SHARK_REEF_BLACKTIP:HEART -"CREATURE:SHARK_REEF_BLACKTIP:LIVER - CREATURE:SHARK_REEF_BLACKTIP:GUT -$CREATURE:SHARK_REEF_BLACKTIP:STOMACH -$CREATURE:SHARK_REEF_BLACKTIP:GIZZARD -%CREATURE:SHARK_REEF_BLACKTIP:PANCREAS -#CREATURE:SHARK_REEF_BLACKTIP:SPLEEN -#CREATURE:SHARK_REEF_BLACKTIP:KIDNEY -#CREATURE:SHARK_REEF_WHITETIP:MUSCLE - CREATURE:SHARK_REEF_WHITETIP:EYE -"CREATURE:SHARK_REEF_WHITETIP:BRAIN -!CREATURE:SHARK_REEF_WHITETIP:LUNG -"CREATURE:SHARK_REEF_WHITETIP:HEART -"CREATURE:SHARK_REEF_WHITETIP:LIVER - CREATURE:SHARK_REEF_WHITETIP:GUT -$CREATURE:SHARK_REEF_WHITETIP:STOMACH -$CREATURE:SHARK_REEF_WHITETIP:GIZZARD -%CREATURE:SHARK_REEF_WHITETIP:PANCREAS -#CREATURE:SHARK_REEF_WHITETIP:SPLEEN -#CREATURE:SHARK_REEF_WHITETIP:KIDNEY -CREATURE:SHARK_BLUE:MUSCLE -CREATURE:SHARK_BLUE:EYE -CREATURE:SHARK_BLUE:BRAIN -CREATURE:SHARK_BLUE:LUNG -CREATURE:SHARK_BLUE:HEART -CREATURE:SHARK_BLUE:LIVER -CREATURE:SHARK_BLUE:GUT -CREATURE:SHARK_BLUE:STOMACH -CREATURE:SHARK_BLUE:GIZZARD -CREATURE:SHARK_BLUE:PANCREAS -CREATURE:SHARK_BLUE:SPLEEN -CREATURE:SHARK_BLUE:KIDNEY - CREATURE:SHARK_HAMMERHEAD:MUSCLE -CREATURE:SHARK_HAMMERHEAD:EYE -CREATURE:SHARK_HAMMERHEAD:BRAIN -CREATURE:SHARK_HAMMERHEAD:LUNG -CREATURE:SHARK_HAMMERHEAD:HEART -CREATURE:SHARK_HAMMERHEAD:LIVER -CREATURE:SHARK_HAMMERHEAD:GUT -!CREATURE:SHARK_HAMMERHEAD:STOMACH -!CREATURE:SHARK_HAMMERHEAD:GIZZARD -"CREATURE:SHARK_HAMMERHEAD:PANCREAS - CREATURE:SHARK_HAMMERHEAD:SPLEEN - CREATURE:SHARK_HAMMERHEAD:KIDNEY -CREATURE:SHARK_ANGEL:MUSCLE -CREATURE:SHARK_ANGEL:EYE -CREATURE:SHARK_ANGEL:BRAIN -CREATURE:SHARK_ANGEL:LUNG -CREATURE:SHARK_ANGEL:HEART -CREATURE:SHARK_ANGEL:LIVER -CREATURE:SHARK_ANGEL:GUT -CREATURE:SHARK_ANGEL:STOMACH -CREATURE:SHARK_ANGEL:GIZZARD -CREATURE:SHARK_ANGEL:PANCREAS -CREATURE:SHARK_ANGEL:SPLEEN -CREATURE:SHARK_ANGEL:KIDNEY -!CREATURE:FISH_SKATE_COMMON:MUSCLE -CREATURE:FISH_SKATE_COMMON:EYE - CREATURE:FISH_SKATE_COMMON:BRAIN -CREATURE:FISH_SKATE_COMMON:LUNG - CREATURE:FISH_SKATE_COMMON:HEART - CREATURE:FISH_SKATE_COMMON:LIVER -CREATURE:FISH_SKATE_COMMON:GUT -"CREATURE:FISH_SKATE_COMMON:STOMACH -"CREATURE:FISH_SKATE_COMMON:GIZZARD -#CREATURE:FISH_SKATE_COMMON:PANCREAS -!CREATURE:FISH_SKATE_COMMON:SPLEEN -!CREATURE:FISH_SKATE_COMMON:KIDNEY -CREATURE:FISH_RAY_MANTA:MUSCLE -CREATURE:FISH_RAY_MANTA:EYE -CREATURE:FISH_RAY_MANTA:BRAIN -CREATURE:FISH_RAY_MANTA:LUNG -CREATURE:FISH_RAY_MANTA:HEART -CREATURE:FISH_RAY_MANTA:LIVER -CREATURE:FISH_RAY_MANTA:GUT -CREATURE:FISH_RAY_MANTA:STOMACH -CREATURE:FISH_RAY_MANTA:GIZZARD - CREATURE:FISH_RAY_MANTA:PANCREAS -CREATURE:FISH_RAY_MANTA:SPLEEN -CREATURE:FISH_RAY_MANTA:KIDNEY -CREATURE:FISH_STINGRAY:MUSCLE -CREATURE:FISH_STINGRAY:EYE -CREATURE:FISH_STINGRAY:BRAIN -CREATURE:FISH_STINGRAY:LUNG -CREATURE:FISH_STINGRAY:HEART -CREATURE:FISH_STINGRAY:LIVER -CREATURE:FISH_STINGRAY:GUT -CREATURE:FISH_STINGRAY:STOMACH -CREATURE:FISH_STINGRAY:GIZZARD -CREATURE:FISH_STINGRAY:PANCREAS -CREATURE:FISH_STINGRAY:SPLEEN -CREATURE:FISH_STINGRAY:KIDNEY -CREATURE:FISH_COELACANTH:MUSCLE -CREATURE:FISH_COELACANTH:EYE -CREATURE:FISH_COELACANTH:BRAIN -CREATURE:FISH_COELACANTH:LUNG -CREATURE:FISH_COELACANTH:HEART -CREATURE:FISH_COELACANTH:LIVER -CREATURE:FISH_COELACANTH:GUT - CREATURE:FISH_COELACANTH:STOMACH - CREATURE:FISH_COELACANTH:GIZZARD -!CREATURE:FISH_COELACANTH:PANCREAS -CREATURE:FISH_COELACANTH:SPLEEN -CREATURE:FISH_COELACANTH:KIDNEY -CREATURE:FISH_STURGEON:MUSCLE -CREATURE:FISH_STURGEON:EYE -CREATURE:FISH_STURGEON:BRAIN -CREATURE:FISH_STURGEON:LUNG -CREATURE:FISH_STURGEON:HEART -CREATURE:FISH_STURGEON:LIVER -CREATURE:FISH_STURGEON:GUT -CREATURE:FISH_STURGEON:STOMACH -CREATURE:FISH_STURGEON:GIZZARD -CREATURE:FISH_STURGEON:PANCREAS -CREATURE:FISH_STURGEON:SPLEEN -CREATURE:FISH_STURGEON:KIDNEY -CREATURE:FISH_CONGER_EEL:MUSCLE -CREATURE:FISH_CONGER_EEL:EYE -CREATURE:FISH_CONGER_EEL:BRAIN -CREATURE:FISH_CONGER_EEL:LUNG -CREATURE:FISH_CONGER_EEL:HEART -CREATURE:FISH_CONGER_EEL:LIVER -CREATURE:FISH_CONGER_EEL:GUT - CREATURE:FISH_CONGER_EEL:STOMACH - CREATURE:FISH_CONGER_EEL:GIZZARD -!CREATURE:FISH_CONGER_EEL:PANCREAS -CREATURE:FISH_CONGER_EEL:SPLEEN -CREATURE:FISH_CONGER_EEL:KIDNEY -CREATURE:FISH_MILKFISH:MUSCLE -CREATURE:FISH_MILKFISH:EYE -CREATURE:FISH_MILKFISH:BRAIN -CREATURE:FISH_MILKFISH:LUNG -CREATURE:FISH_MILKFISH:HEART -CREATURE:FISH_MILKFISH:LIVER -CREATURE:FISH_MILKFISH:GUT -CREATURE:FISH_MILKFISH:STOMACH -CREATURE:FISH_MILKFISH:GIZZARD -CREATURE:FISH_MILKFISH:PANCREAS -CREATURE:FISH_MILKFISH:SPLEEN -CREATURE:FISH_MILKFISH:KIDNEY -CREATURE:FISH_COD:MUSCLE -CREATURE:FISH_COD:EYE -CREATURE:FISH_COD:BRAIN -CREATURE:FISH_COD:LUNG -CREATURE:FISH_COD:HEART -CREATURE:FISH_COD:LIVER -CREATURE:FISH_COD:GUT -CREATURE:FISH_COD:STOMACH -CREATURE:FISH_COD:GIZZARD -CREATURE:FISH_COD:PANCREAS -CREATURE:FISH_COD:SPLEEN -CREATURE:FISH_COD:KIDNEY -CREATURE:FISH_OPAH:MUSCLE -CREATURE:FISH_OPAH:EYE -CREATURE:FISH_OPAH:BRAIN -CREATURE:FISH_OPAH:LUNG -CREATURE:FISH_OPAH:HEART -CREATURE:FISH_OPAH:LIVER -CREATURE:FISH_OPAH:GUT -CREATURE:FISH_OPAH:STOMACH -CREATURE:FISH_OPAH:GIZZARD -CREATURE:FISH_OPAH:PANCREAS -CREATURE:FISH_OPAH:SPLEEN -CREATURE:FISH_OPAH:KIDNEY -"CREATURE:FISH_GROUPER_GIANT:MUSCLE -CREATURE:FISH_GROUPER_GIANT:EYE -!CREATURE:FISH_GROUPER_GIANT:BRAIN - CREATURE:FISH_GROUPER_GIANT:LUNG -!CREATURE:FISH_GROUPER_GIANT:HEART -!CREATURE:FISH_GROUPER_GIANT:LIVER -CREATURE:FISH_GROUPER_GIANT:GUT -#CREATURE:FISH_GROUPER_GIANT:STOMACH -#CREATURE:FISH_GROUPER_GIANT:GIZZARD -$CREATURE:FISH_GROUPER_GIANT:PANCREAS -"CREATURE:FISH_GROUPER_GIANT:SPLEEN -"CREATURE:FISH_GROUPER_GIANT:KIDNEY -CREATURE:FISH_BLUEFISH:MUSCLE -CREATURE:FISH_BLUEFISH:EYE -CREATURE:FISH_BLUEFISH:BRAIN -CREATURE:FISH_BLUEFISH:LUNG -CREATURE:FISH_BLUEFISH:HEART -CREATURE:FISH_BLUEFISH:LIVER -CREATURE:FISH_BLUEFISH:GUT -CREATURE:FISH_BLUEFISH:STOMACH -CREATURE:FISH_BLUEFISH:GIZZARD -CREATURE:FISH_BLUEFISH:PANCREAS -CREATURE:FISH_BLUEFISH:SPLEEN -CREATURE:FISH_BLUEFISH:KIDNEY -"CREATURE:FISH_SUNFISH_OCEAN:MUSCLE -CREATURE:FISH_SUNFISH_OCEAN:EYE -!CREATURE:FISH_SUNFISH_OCEAN:BRAIN - CREATURE:FISH_SUNFISH_OCEAN:LUNG -!CREATURE:FISH_SUNFISH_OCEAN:HEART -!CREATURE:FISH_SUNFISH_OCEAN:LIVER -CREATURE:FISH_SUNFISH_OCEAN:GUT -#CREATURE:FISH_SUNFISH_OCEAN:STOMACH -#CREATURE:FISH_SUNFISH_OCEAN:GIZZARD -$CREATURE:FISH_SUNFISH_OCEAN:PANCREAS -"CREATURE:FISH_SUNFISH_OCEAN:SPLEEN -"CREATURE:FISH_SUNFISH_OCEAN:KIDNEY -CREATURE:FISH_SWORDFISH:MUSCLE -CREATURE:FISH_SWORDFISH:EYE -CREATURE:FISH_SWORDFISH:BRAIN -CREATURE:FISH_SWORDFISH:LUNG -CREATURE:FISH_SWORDFISH:HEART -CREATURE:FISH_SWORDFISH:LIVER -CREATURE:FISH_SWORDFISH:GUT -CREATURE:FISH_SWORDFISH:STOMACH -CREATURE:FISH_SWORDFISH:GIZZARD - CREATURE:FISH_SWORDFISH:PANCREAS -CREATURE:FISH_SWORDFISH:SPLEEN -CREATURE:FISH_SWORDFISH:KIDNEY -CREATURE:FISH_MARLIN:MUSCLE -CREATURE:FISH_MARLIN:EYE -CREATURE:FISH_MARLIN:BRAIN -CREATURE:FISH_MARLIN:LUNG -CREATURE:FISH_MARLIN:HEART -CREATURE:FISH_MARLIN:LIVER -CREATURE:FISH_MARLIN:GUT -CREATURE:FISH_MARLIN:STOMACH -CREATURE:FISH_MARLIN:GIZZARD -CREATURE:FISH_MARLIN:PANCREAS -CREATURE:FISH_MARLIN:SPLEEN -CREATURE:FISH_MARLIN:KIDNEY -CREATURE:FISH_HALIBUT:MUSCLE -CREATURE:FISH_HALIBUT:EYE -CREATURE:FISH_HALIBUT:BRAIN -CREATURE:FISH_HALIBUT:LUNG -CREATURE:FISH_HALIBUT:HEART -CREATURE:FISH_HALIBUT:LIVER -CREATURE:FISH_HALIBUT:GUT -CREATURE:FISH_HALIBUT:STOMACH -CREATURE:FISH_HALIBUT:GIZZARD -CREATURE:FISH_HALIBUT:PANCREAS -CREATURE:FISH_HALIBUT:SPLEEN -CREATURE:FISH_HALIBUT:KIDNEY -$CREATURE:FISH_BARRACUDA_GREAT:MUSCLE -!CREATURE:FISH_BARRACUDA_GREAT:EYE -#CREATURE:FISH_BARRACUDA_GREAT:BRAIN -"CREATURE:FISH_BARRACUDA_GREAT:LUNG -#CREATURE:FISH_BARRACUDA_GREAT:HEART -#CREATURE:FISH_BARRACUDA_GREAT:LIVER -!CREATURE:FISH_BARRACUDA_GREAT:GUT -%CREATURE:FISH_BARRACUDA_GREAT:STOMACH -%CREATURE:FISH_BARRACUDA_GREAT:GIZZARD -&CREATURE:FISH_BARRACUDA_GREAT:PANCREAS -$CREATURE:FISH_BARRACUDA_GREAT:SPLEEN -$CREATURE:FISH_BARRACUDA_GREAT:KIDNEY -!CREATURE:FISH_TUNA_BLUEFIN:MUSCLE -CREATURE:FISH_TUNA_BLUEFIN:EYE - CREATURE:FISH_TUNA_BLUEFIN:BRAIN -CREATURE:FISH_TUNA_BLUEFIN:LUNG - CREATURE:FISH_TUNA_BLUEFIN:HEART - CREATURE:FISH_TUNA_BLUEFIN:LIVER -CREATURE:FISH_TUNA_BLUEFIN:GUT -"CREATURE:FISH_TUNA_BLUEFIN:STOMACH -"CREATURE:FISH_TUNA_BLUEFIN:GIZZARD -#CREATURE:FISH_TUNA_BLUEFIN:PANCREAS -!CREATURE:FISH_TUNA_BLUEFIN:SPLEEN -!CREATURE:FISH_TUNA_BLUEFIN:KIDNEY -CREATURE:NARWHAL:MUSCLE -CREATURE:NARWHAL:EYE -CREATURE:NARWHAL:BRAIN -CREATURE:NARWHAL:LUNG -CREATURE:NARWHAL:HEART -CREATURE:NARWHAL:LIVER -CREATURE:NARWHAL:GUT -CREATURE:NARWHAL:STOMACH -CREATURE:NARWHAL:GIZZARD -CREATURE:NARWHAL:PANCREAS -CREATURE:NARWHAL:SPLEEN -CREATURE:NARWHAL:KIDNEY -CREATURE:NARWHAL MAN:MUSCLE -CREATURE:NARWHAL MAN:EYE -CREATURE:NARWHAL MAN:BRAIN -CREATURE:NARWHAL MAN:LUNG -CREATURE:NARWHAL MAN:HEART -CREATURE:NARWHAL MAN:LIVER -CREATURE:NARWHAL MAN:GUT -CREATURE:NARWHAL MAN:STOMACH -CREATURE:NARWHAL MAN:GIZZARD -CREATURE:NARWHAL MAN:PANCREAS -CREATURE:NARWHAL MAN:SPLEEN -CREATURE:NARWHAL MAN:KIDNEY -CREATURE:NARWHAL, GIANT:MUSCLE -CREATURE:NARWHAL, GIANT:EYE -CREATURE:NARWHAL, GIANT:BRAIN -CREATURE:NARWHAL, GIANT:LUNG -CREATURE:NARWHAL, GIANT:HEART -CREATURE:NARWHAL, GIANT:LIVER -CREATURE:NARWHAL, GIANT:GUT -CREATURE:NARWHAL, GIANT:STOMACH -CREATURE:NARWHAL, GIANT:GIZZARD - CREATURE:NARWHAL, GIANT:PANCREAS -CREATURE:NARWHAL, GIANT:SPLEEN -CREATURE:NARWHAL, GIANT:KIDNEY -CREATURE:HIPPO:MUSCLE -CREATURE:HIPPO:EYE -CREATURE:HIPPO:BRAIN -CREATURE:HIPPO:LUNG -CREATURE:HIPPO:HEART -CREATURE:HIPPO:LIVER -CREATURE:HIPPO:GUT -CREATURE:HIPPO:STOMACH -CREATURE:HIPPO:GIZZARD -CREATURE:HIPPO:PANCREAS -CREATURE:HIPPO:SPLEEN -CREATURE:HIPPO:KIDNEY -CREATURE:HIPPO_MAN:MUSCLE -CREATURE:HIPPO_MAN:EYE -CREATURE:HIPPO_MAN:BRAIN -CREATURE:HIPPO_MAN:LUNG -CREATURE:HIPPO_MAN:HEART -CREATURE:HIPPO_MAN:LIVER -CREATURE:HIPPO_MAN:GUT -CREATURE:HIPPO_MAN:STOMACH -CREATURE:HIPPO_MAN:GIZZARD -CREATURE:HIPPO_MAN:PANCREAS -CREATURE:HIPPO_MAN:SPLEEN -CREATURE:HIPPO_MAN:KIDNEY -CREATURE:GIANT_HIPPO:MUSCLE -CREATURE:GIANT_HIPPO:EYE -CREATURE:GIANT_HIPPO:BRAIN -CREATURE:GIANT_HIPPO:LUNG -CREATURE:GIANT_HIPPO:HEART -CREATURE:GIANT_HIPPO:LIVER -CREATURE:GIANT_HIPPO:GUT -CREATURE:GIANT_HIPPO:STOMACH -CREATURE:GIANT_HIPPO:GIZZARD -CREATURE:GIANT_HIPPO:PANCREAS -CREATURE:GIANT_HIPPO:SPLEEN -CREATURE:GIANT_HIPPO:KIDNEY -!CREATURE:FISH_GAR_LONGNOSE:MUSCLE -CREATURE:FISH_GAR_LONGNOSE:EYE - CREATURE:FISH_GAR_LONGNOSE:BRAIN -CREATURE:FISH_GAR_LONGNOSE:LUNG - CREATURE:FISH_GAR_LONGNOSE:HEART - CREATURE:FISH_GAR_LONGNOSE:LIVER -CREATURE:FISH_GAR_LONGNOSE:GUT -"CREATURE:FISH_GAR_LONGNOSE:STOMACH -"CREATURE:FISH_GAR_LONGNOSE:GIZZARD -#CREATURE:FISH_GAR_LONGNOSE:PANCREAS -!CREATURE:FISH_GAR_LONGNOSE:SPLEEN -!CREATURE:FISH_GAR_LONGNOSE:KIDNEY -CREATURE:FISH_CARP:MUSCLE -CREATURE:FISH_CARP:EYE -CREATURE:FISH_CARP:BRAIN -CREATURE:FISH_CARP:LUNG -CREATURE:FISH_CARP:HEART -CREATURE:FISH_CARP:LIVER -CREATURE:FISH_CARP:GUT -CREATURE:FISH_CARP:STOMACH -CREATURE:FISH_CARP:GIZZARD -CREATURE:FISH_CARP:PANCREAS -CREATURE:FISH_CARP:SPLEEN -CREATURE:FISH_CARP:KIDNEY -CREATURE:FISH_TIGERFISH:MUSCLE -CREATURE:FISH_TIGERFISH:EYE -CREATURE:FISH_TIGERFISH:BRAIN -CREATURE:FISH_TIGERFISH:LUNG -CREATURE:FISH_TIGERFISH:HEART -CREATURE:FISH_TIGERFISH:LIVER -CREATURE:FISH_TIGERFISH:GUT -CREATURE:FISH_TIGERFISH:STOMACH -CREATURE:FISH_TIGERFISH:GIZZARD - CREATURE:FISH_TIGERFISH:PANCREAS -CREATURE:FISH_TIGERFISH:SPLEEN -CREATURE:FISH_TIGERFISH:KIDNEY -CREATURE:FISH_PIKE:MUSCLE -CREATURE:FISH_PIKE:EYE -CREATURE:FISH_PIKE:BRAIN -CREATURE:FISH_PIKE:LUNG -CREATURE:FISH_PIKE:HEART -CREATURE:FISH_PIKE:LIVER -CREATURE:FISH_PIKE:GUT -CREATURE:FISH_PIKE:STOMACH -CREATURE:FISH_PIKE:GIZZARD -CREATURE:FISH_PIKE:PANCREAS -CREATURE:FISH_PIKE:SPLEEN -CREATURE:FISH_PIKE:KIDNEY -CREATURE:PLATYPUS:MUSCLE -CREATURE:PLATYPUS:EYE -CREATURE:PLATYPUS:BRAIN -CREATURE:PLATYPUS:LUNG -CREATURE:PLATYPUS:HEART -CREATURE:PLATYPUS:LIVER -CREATURE:PLATYPUS:GUT -CREATURE:PLATYPUS:STOMACH -CREATURE:PLATYPUS:GIZZARD -CREATURE:PLATYPUS:PANCREAS -CREATURE:PLATYPUS:SPLEEN -CREATURE:PLATYPUS:KIDNEY -CREATURE:PLATYPUS MAN:MUSCLE -CREATURE:PLATYPUS MAN:EYE -CREATURE:PLATYPUS MAN:BRAIN -CREATURE:PLATYPUS MAN:LUNG -CREATURE:PLATYPUS MAN:HEART -CREATURE:PLATYPUS MAN:LIVER -CREATURE:PLATYPUS MAN:GUT -CREATURE:PLATYPUS MAN:STOMACH -CREATURE:PLATYPUS MAN:GIZZARD -CREATURE:PLATYPUS MAN:PANCREAS -CREATURE:PLATYPUS MAN:SPLEEN -CREATURE:PLATYPUS MAN:KIDNEY -CREATURE:PLATYPUS, GIANT:MUSCLE -CREATURE:PLATYPUS, GIANT:EYE -CREATURE:PLATYPUS, GIANT:BRAIN -CREATURE:PLATYPUS, GIANT:LUNG -CREATURE:PLATYPUS, GIANT:HEART -CREATURE:PLATYPUS, GIANT:LIVER -CREATURE:PLATYPUS, GIANT:GUT - CREATURE:PLATYPUS, GIANT:STOMACH - CREATURE:PLATYPUS, GIANT:GIZZARD -!CREATURE:PLATYPUS, GIANT:PANCREAS -CREATURE:PLATYPUS, GIANT:SPLEEN -CREATURE:PLATYPUS, GIANT:KIDNEY -CREATURE:BEAR_GRIZZLY:MUSCLE -CREATURE:BEAR_GRIZZLY:EYE -CREATURE:BEAR_GRIZZLY:BRAIN -CREATURE:BEAR_GRIZZLY:LUNG -CREATURE:BEAR_GRIZZLY:HEART -CREATURE:BEAR_GRIZZLY:LIVER -CREATURE:BEAR_GRIZZLY:GUT -CREATURE:BEAR_GRIZZLY:STOMACH -CREATURE:BEAR_GRIZZLY:GIZZARD -CREATURE:BEAR_GRIZZLY:PANCREAS -CREATURE:BEAR_GRIZZLY:SPLEEN -CREATURE:BEAR_GRIZZLY:KIDNEY - CREATURE:BEAR_GRIZZLY_MAN:MUSCLE -CREATURE:BEAR_GRIZZLY_MAN:EYE -CREATURE:BEAR_GRIZZLY_MAN:BRAIN -CREATURE:BEAR_GRIZZLY_MAN:LUNG -CREATURE:BEAR_GRIZZLY_MAN:HEART -CREATURE:BEAR_GRIZZLY_MAN:LIVER -CREATURE:BEAR_GRIZZLY_MAN:GUT -!CREATURE:BEAR_GRIZZLY_MAN:STOMACH -!CREATURE:BEAR_GRIZZLY_MAN:GIZZARD -"CREATURE:BEAR_GRIZZLY_MAN:PANCREAS - CREATURE:BEAR_GRIZZLY_MAN:SPLEEN - CREATURE:BEAR_GRIZZLY_MAN:KIDNEY -"CREATURE:GIANT_BEAR_GRIZZLY:MUSCLE -CREATURE:GIANT_BEAR_GRIZZLY:EYE -!CREATURE:GIANT_BEAR_GRIZZLY:BRAIN - CREATURE:GIANT_BEAR_GRIZZLY:LUNG -!CREATURE:GIANT_BEAR_GRIZZLY:HEART -!CREATURE:GIANT_BEAR_GRIZZLY:LIVER -CREATURE:GIANT_BEAR_GRIZZLY:GUT -#CREATURE:GIANT_BEAR_GRIZZLY:STOMACH -#CREATURE:GIANT_BEAR_GRIZZLY:GIZZARD -$CREATURE:GIANT_BEAR_GRIZZLY:PANCREAS -"CREATURE:GIANT_BEAR_GRIZZLY:SPLEEN -"CREATURE:GIANT_BEAR_GRIZZLY:KIDNEY -CREATURE:BEAR_BLACK:MUSCLE -CREATURE:BEAR_BLACK:EYE -CREATURE:BEAR_BLACK:BRAIN -CREATURE:BEAR_BLACK:LUNG -CREATURE:BEAR_BLACK:HEART -CREATURE:BEAR_BLACK:LIVER -CREATURE:BEAR_BLACK:GUT -CREATURE:BEAR_BLACK:STOMACH -CREATURE:BEAR_BLACK:GIZZARD -CREATURE:BEAR_BLACK:PANCREAS -CREATURE:BEAR_BLACK:SPLEEN -CREATURE:BEAR_BLACK:KIDNEY -CREATURE:BEAR_BLACK_MAN:MUSCLE -CREATURE:BEAR_BLACK_MAN:EYE -CREATURE:BEAR_BLACK_MAN:BRAIN -CREATURE:BEAR_BLACK_MAN:LUNG -CREATURE:BEAR_BLACK_MAN:HEART -CREATURE:BEAR_BLACK_MAN:LIVER -CREATURE:BEAR_BLACK_MAN:GUT -CREATURE:BEAR_BLACK_MAN:STOMACH -CREATURE:BEAR_BLACK_MAN:GIZZARD - CREATURE:BEAR_BLACK_MAN:PANCREAS -CREATURE:BEAR_BLACK_MAN:SPLEEN -CREATURE:BEAR_BLACK_MAN:KIDNEY - CREATURE:GIANT_BEAR_BLACK:MUSCLE -CREATURE:GIANT_BEAR_BLACK:EYE -CREATURE:GIANT_BEAR_BLACK:BRAIN -CREATURE:GIANT_BEAR_BLACK:LUNG -CREATURE:GIANT_BEAR_BLACK:HEART -CREATURE:GIANT_BEAR_BLACK:LIVER -CREATURE:GIANT_BEAR_BLACK:GUT -!CREATURE:GIANT_BEAR_BLACK:STOMACH -!CREATURE:GIANT_BEAR_BLACK:GIZZARD -"CREATURE:GIANT_BEAR_BLACK:PANCREAS - CREATURE:GIANT_BEAR_BLACK:SPLEEN - CREATURE:GIANT_BEAR_BLACK:KIDNEY -CREATURE:DEER:MUSCLE -CREATURE:DEER:EYE -CREATURE:DEER:BRAIN -CREATURE:DEER:LUNG -CREATURE:DEER:HEART -CREATURE:DEER:LIVER -CREATURE:DEER:GUT -CREATURE:DEER:STOMACH -CREATURE:DEER:GIZZARD -CREATURE:DEER:PANCREAS -CREATURE:DEER:SPLEEN -CREATURE:DEER:KIDNEY -CREATURE:DEER_MAN:MUSCLE -CREATURE:DEER_MAN:EYE -CREATURE:DEER_MAN:BRAIN -CREATURE:DEER_MAN:LUNG -CREATURE:DEER_MAN:HEART -CREATURE:DEER_MAN:LIVER -CREATURE:DEER_MAN:GUT -CREATURE:DEER_MAN:STOMACH -CREATURE:DEER_MAN:GIZZARD -CREATURE:DEER_MAN:PANCREAS -CREATURE:DEER_MAN:SPLEEN -CREATURE:DEER_MAN:KIDNEY -CREATURE:GIANT_DEER:MUSCLE -CREATURE:GIANT_DEER:EYE -CREATURE:GIANT_DEER:BRAIN -CREATURE:GIANT_DEER:LUNG -CREATURE:GIANT_DEER:HEART -CREATURE:GIANT_DEER:LIVER -CREATURE:GIANT_DEER:GUT -CREATURE:GIANT_DEER:STOMACH -CREATURE:GIANT_DEER:GIZZARD -CREATURE:GIANT_DEER:PANCREAS -CREATURE:GIANT_DEER:SPLEEN -CREATURE:GIANT_DEER:KIDNEY -CREATURE:FOX:MUSCLE -CREATURE:FOX:EYE -CREATURE:FOX:BRAIN -CREATURE:FOX:LUNG -CREATURE:FOX:HEART -CREATURE:FOX:LIVER -CREATURE:FOX:GUT -CREATURE:FOX:STOMACH -CREATURE:FOX:GIZZARD -CREATURE:FOX:PANCREAS -CREATURE:FOX:SPLEEN -CREATURE:FOX:KIDNEY -CREATURE:FOX_MAN:MUSCLE -CREATURE:FOX_MAN:EYE -CREATURE:FOX_MAN:BRAIN -CREATURE:FOX_MAN:LUNG -CREATURE:FOX_MAN:HEART -CREATURE:FOX_MAN:LIVER -CREATURE:FOX_MAN:GUT -CREATURE:FOX_MAN:STOMACH -CREATURE:FOX_MAN:GIZZARD -CREATURE:FOX_MAN:PANCREAS -CREATURE:FOX_MAN:SPLEEN -CREATURE:FOX_MAN:KIDNEY -CREATURE:GIANT_FOX:MUSCLE -CREATURE:GIANT_FOX:EYE -CREATURE:GIANT_FOX:BRAIN -CREATURE:GIANT_FOX:LUNG -CREATURE:GIANT_FOX:HEART -CREATURE:GIANT_FOX:LIVER -CREATURE:GIANT_FOX:GUT -CREATURE:GIANT_FOX:STOMACH -CREATURE:GIANT_FOX:GIZZARD -CREATURE:GIANT_FOX:PANCREAS -CREATURE:GIANT_FOX:SPLEEN -CREATURE:GIANT_FOX:KIDNEY -CREATURE:RACCOON:MUSCLE -CREATURE:RACCOON:EYE -CREATURE:RACCOON:BRAIN -CREATURE:RACCOON:LUNG -CREATURE:RACCOON:HEART -CREATURE:RACCOON:LIVER -CREATURE:RACCOON:GUT -CREATURE:RACCOON:STOMACH -CREATURE:RACCOON:GIZZARD -CREATURE:RACCOON:PANCREAS -CREATURE:RACCOON:SPLEEN -CREATURE:RACCOON:KIDNEY -CREATURE:RACCOON_MAN:MUSCLE -CREATURE:RACCOON_MAN:EYE -CREATURE:RACCOON_MAN:BRAIN -CREATURE:RACCOON_MAN:LUNG -CREATURE:RACCOON_MAN:HEART -CREATURE:RACCOON_MAN:LIVER -CREATURE:RACCOON_MAN:GUT -CREATURE:RACCOON_MAN:STOMACH -CREATURE:RACCOON_MAN:GIZZARD -CREATURE:RACCOON_MAN:PANCREAS -CREATURE:RACCOON_MAN:SPLEEN -CREATURE:RACCOON_MAN:KIDNEY -CREATURE:GIANT_RACCOON:MUSCLE -CREATURE:GIANT_RACCOON:EYE -CREATURE:GIANT_RACCOON:BRAIN -CREATURE:GIANT_RACCOON:LUNG -CREATURE:GIANT_RACCOON:HEART -CREATURE:GIANT_RACCOON:LIVER -CREATURE:GIANT_RACCOON:GUT -CREATURE:GIANT_RACCOON:STOMACH -CREATURE:GIANT_RACCOON:GIZZARD -CREATURE:GIANT_RACCOON:PANCREAS -CREATURE:GIANT_RACCOON:SPLEEN -CREATURE:GIANT_RACCOON:KIDNEY -CREATURE:MACAQUE_RHESUS:MUSCLE -CREATURE:MACAQUE_RHESUS:EYE -CREATURE:MACAQUE_RHESUS:BRAIN -CREATURE:MACAQUE_RHESUS:LUNG -CREATURE:MACAQUE_RHESUS:HEART -CREATURE:MACAQUE_RHESUS:LIVER -CREATURE:MACAQUE_RHESUS:GUT -CREATURE:MACAQUE_RHESUS:STOMACH -CREATURE:MACAQUE_RHESUS:GIZZARD - CREATURE:MACAQUE_RHESUS:PANCREAS -CREATURE:MACAQUE_RHESUS:SPLEEN -CREATURE:MACAQUE_RHESUS:KIDNEY -"CREATURE:MACAQUE_RHESUS_MAN:MUSCLE -CREATURE:MACAQUE_RHESUS_MAN:EYE -!CREATURE:MACAQUE_RHESUS_MAN:BRAIN - CREATURE:MACAQUE_RHESUS_MAN:LUNG -!CREATURE:MACAQUE_RHESUS_MAN:HEART -!CREATURE:MACAQUE_RHESUS_MAN:LIVER -CREATURE:MACAQUE_RHESUS_MAN:GUT -#CREATURE:MACAQUE_RHESUS_MAN:STOMACH -#CREATURE:MACAQUE_RHESUS_MAN:GIZZARD -$CREATURE:MACAQUE_RHESUS_MAN:PANCREAS -"CREATURE:MACAQUE_RHESUS_MAN:SPLEEN -"CREATURE:MACAQUE_RHESUS_MAN:KIDNEY -$CREATURE:GIANT_MACAQUE_RHESUS:MUSCLE -!CREATURE:GIANT_MACAQUE_RHESUS:EYE -#CREATURE:GIANT_MACAQUE_RHESUS:BRAIN -"CREATURE:GIANT_MACAQUE_RHESUS:LUNG -#CREATURE:GIANT_MACAQUE_RHESUS:HEART -#CREATURE:GIANT_MACAQUE_RHESUS:LIVER -!CREATURE:GIANT_MACAQUE_RHESUS:GUT -%CREATURE:GIANT_MACAQUE_RHESUS:STOMACH -%CREATURE:GIANT_MACAQUE_RHESUS:GIZZARD -&CREATURE:GIANT_MACAQUE_RHESUS:PANCREAS -$CREATURE:GIANT_MACAQUE_RHESUS:SPLEEN -$CREATURE:GIANT_MACAQUE_RHESUS:KIDNEY -CREATURE:COUGAR:MUSCLE -CREATURE:COUGAR:EYE -CREATURE:COUGAR:BRAIN -CREATURE:COUGAR:LUNG -CREATURE:COUGAR:HEART -CREATURE:COUGAR:LIVER -CREATURE:COUGAR:GUT -CREATURE:COUGAR:STOMACH -CREATURE:COUGAR:GIZZARD -CREATURE:COUGAR:PANCREAS -CREATURE:COUGAR:SPLEEN -CREATURE:COUGAR:KIDNEY -CREATURE:COUGAR_MAN:MUSCLE -CREATURE:COUGAR_MAN:EYE -CREATURE:COUGAR_MAN:BRAIN -CREATURE:COUGAR_MAN:LUNG -CREATURE:COUGAR_MAN:HEART -CREATURE:COUGAR_MAN:LIVER -CREATURE:COUGAR_MAN:GUT -CREATURE:COUGAR_MAN:STOMACH -CREATURE:COUGAR_MAN:GIZZARD -CREATURE:COUGAR_MAN:PANCREAS -CREATURE:COUGAR_MAN:SPLEEN -CREATURE:COUGAR_MAN:KIDNEY -CREATURE:GIANT_COUGAR:MUSCLE -CREATURE:GIANT_COUGAR:EYE -CREATURE:GIANT_COUGAR:BRAIN -CREATURE:GIANT_COUGAR:LUNG -CREATURE:GIANT_COUGAR:HEART -CREATURE:GIANT_COUGAR:LIVER -CREATURE:GIANT_COUGAR:GUT -CREATURE:GIANT_COUGAR:STOMACH -CREATURE:GIANT_COUGAR:GIZZARD -CREATURE:GIANT_COUGAR:PANCREAS -CREATURE:GIANT_COUGAR:SPLEEN -CREATURE:GIANT_COUGAR:KIDNEY -CREATURE:WOLF:MUSCLE -CREATURE:WOLF:EYE -CREATURE:WOLF:BRAIN -CREATURE:WOLF:LUNG -CREATURE:WOLF:HEART -CREATURE:WOLF:LIVER -CREATURE:WOLF:GUT -CREATURE:WOLF:STOMACH -CREATURE:WOLF:GIZZARD -CREATURE:WOLF:PANCREAS -CREATURE:WOLF:SPLEEN -CREATURE:WOLF:KIDNEY -CREATURE:WOLF_MAN:MUSCLE -CREATURE:WOLF_MAN:EYE -CREATURE:WOLF_MAN:BRAIN -CREATURE:WOLF_MAN:LUNG -CREATURE:WOLF_MAN:HEART -CREATURE:WOLF_MAN:LIVER -CREATURE:WOLF_MAN:GUT -CREATURE:WOLF_MAN:STOMACH -CREATURE:WOLF_MAN:GIZZARD -CREATURE:WOLF_MAN:PANCREAS -CREATURE:WOLF_MAN:SPLEEN -CREATURE:WOLF_MAN:KIDNEY -CREATURE:GIANT_WOLF:MUSCLE -CREATURE:GIANT_WOLF:EYE -CREATURE:GIANT_WOLF:BRAIN -CREATURE:GIANT_WOLF:LUNG -CREATURE:GIANT_WOLF:HEART -CREATURE:GIANT_WOLF:LIVER -CREATURE:GIANT_WOLF:GUT -CREATURE:GIANT_WOLF:STOMACH -CREATURE:GIANT_WOLF:GIZZARD -CREATURE:GIANT_WOLF:PANCREAS -CREATURE:GIANT_WOLF:SPLEEN -CREATURE:GIANT_WOLF:KIDNEY -CREATURE:GROUNDHOG:MUSCLE -CREATURE:GROUNDHOG:EYE -CREATURE:GROUNDHOG:BRAIN -CREATURE:GROUNDHOG:LUNG -CREATURE:GROUNDHOG:HEART -CREATURE:GROUNDHOG:LIVER -CREATURE:GROUNDHOG:GUT -CREATURE:GROUNDHOG:STOMACH -CREATURE:GROUNDHOG:GIZZARD -CREATURE:GROUNDHOG:PANCREAS -CREATURE:GROUNDHOG:SPLEEN -CREATURE:GROUNDHOG:KIDNEY -CREATURE:GROUNDHOG_MAN:MUSCLE -CREATURE:GROUNDHOG_MAN:EYE -CREATURE:GROUNDHOG_MAN:BRAIN -CREATURE:GROUNDHOG_MAN:LUNG -CREATURE:GROUNDHOG_MAN:HEART -CREATURE:GROUNDHOG_MAN:LIVER -CREATURE:GROUNDHOG_MAN:GUT -CREATURE:GROUNDHOG_MAN:STOMACH -CREATURE:GROUNDHOG_MAN:GIZZARD -CREATURE:GROUNDHOG_MAN:PANCREAS -CREATURE:GROUNDHOG_MAN:SPLEEN -CREATURE:GROUNDHOG_MAN:KIDNEY -CREATURE:GIANT_GROUNDHOG:MUSCLE -CREATURE:GIANT_GROUNDHOG:EYE -CREATURE:GIANT_GROUNDHOG:BRAIN -CREATURE:GIANT_GROUNDHOG:LUNG -CREATURE:GIANT_GROUNDHOG:HEART -CREATURE:GIANT_GROUNDHOG:LIVER -CREATURE:GIANT_GROUNDHOG:GUT - CREATURE:GIANT_GROUNDHOG:STOMACH - CREATURE:GIANT_GROUNDHOG:GIZZARD -!CREATURE:GIANT_GROUNDHOG:PANCREAS -CREATURE:GIANT_GROUNDHOG:SPLEEN -CREATURE:GIANT_GROUNDHOG:KIDNEY -CREATURE:ALLIGATOR:MUSCLE -CREATURE:ALLIGATOR:EYE -CREATURE:ALLIGATOR:BRAIN -CREATURE:ALLIGATOR:LUNG -CREATURE:ALLIGATOR:HEART -CREATURE:ALLIGATOR:LIVER -CREATURE:ALLIGATOR:GUT -CREATURE:ALLIGATOR:STOMACH -CREATURE:ALLIGATOR:GIZZARD -CREATURE:ALLIGATOR:PANCREAS -CREATURE:ALLIGATOR:SPLEEN -CREATURE:ALLIGATOR:KIDNEY -CREATURE:ALLIGATOR_MAN:MUSCLE -CREATURE:ALLIGATOR_MAN:EYE -CREATURE:ALLIGATOR_MAN:BRAIN -CREATURE:ALLIGATOR_MAN:LUNG -CREATURE:ALLIGATOR_MAN:HEART -CREATURE:ALLIGATOR_MAN:LIVER -CREATURE:ALLIGATOR_MAN:GUT -CREATURE:ALLIGATOR_MAN:STOMACH -CREATURE:ALLIGATOR_MAN:GIZZARD -CREATURE:ALLIGATOR_MAN:PANCREAS -CREATURE:ALLIGATOR_MAN:SPLEEN -CREATURE:ALLIGATOR_MAN:KIDNEY -CREATURE:GIANT_ALLIGATOR:MUSCLE -CREATURE:GIANT_ALLIGATOR:EYE -CREATURE:GIANT_ALLIGATOR:BRAIN -CREATURE:GIANT_ALLIGATOR:LUNG -CREATURE:GIANT_ALLIGATOR:HEART -CREATURE:GIANT_ALLIGATOR:LIVER -CREATURE:GIANT_ALLIGATOR:GUT - CREATURE:GIANT_ALLIGATOR:STOMACH - CREATURE:GIANT_ALLIGATOR:GIZZARD -!CREATURE:GIANT_ALLIGATOR:PANCREAS -CREATURE:GIANT_ALLIGATOR:SPLEEN -CREATURE:GIANT_ALLIGATOR:KIDNEY -CREATURE:BIRD_BUZZARD:MUSCLE -CREATURE:BIRD_BUZZARD:EYE -CREATURE:BIRD_BUZZARD:BRAIN -CREATURE:BIRD_BUZZARD:LUNG -CREATURE:BIRD_BUZZARD:HEART -CREATURE:BIRD_BUZZARD:LIVER -CREATURE:BIRD_BUZZARD:GUT -CREATURE:BIRD_BUZZARD:STOMACH -CREATURE:BIRD_BUZZARD:GIZZARD -CREATURE:BIRD_BUZZARD:PANCREAS -CREATURE:BIRD_BUZZARD:SPLEEN -CREATURE:BIRD_BUZZARD:KIDNEY -CREATURE:BUZZARD_MAN:MUSCLE -CREATURE:BUZZARD_MAN:EYE -CREATURE:BUZZARD_MAN:BRAIN -CREATURE:BUZZARD_MAN:LUNG -CREATURE:BUZZARD_MAN:HEART -CREATURE:BUZZARD_MAN:LIVER -CREATURE:BUZZARD_MAN:GUT -CREATURE:BUZZARD_MAN:STOMACH -CREATURE:BUZZARD_MAN:GIZZARD -CREATURE:BUZZARD_MAN:PANCREAS -CREATURE:BUZZARD_MAN:SPLEEN -CREATURE:BUZZARD_MAN:KIDNEY -CREATURE:GIANT_BUZZARD:MUSCLE -CREATURE:GIANT_BUZZARD:EYE -CREATURE:GIANT_BUZZARD:BRAIN -CREATURE:GIANT_BUZZARD:LUNG -CREATURE:GIANT_BUZZARD:HEART -CREATURE:GIANT_BUZZARD:LIVER -CREATURE:GIANT_BUZZARD:GUT -CREATURE:GIANT_BUZZARD:STOMACH -CREATURE:GIANT_BUZZARD:GIZZARD -CREATURE:GIANT_BUZZARD:PANCREAS -CREATURE:GIANT_BUZZARD:SPLEEN -CREATURE:GIANT_BUZZARD:KIDNEY -CREATURE:PANDA:MUSCLE -CREATURE:PANDA:EYE -CREATURE:PANDA:BRAIN -CREATURE:PANDA:LUNG -CREATURE:PANDA:HEART -CREATURE:PANDA:LIVER -CREATURE:PANDA:GUT -CREATURE:PANDA:STOMACH -CREATURE:PANDA:GIZZARD -CREATURE:PANDA:PANCREAS -CREATURE:PANDA:SPLEEN -CREATURE:PANDA:KIDNEY -CREATURE:PANDA, GIGANTIC:MUSCLE -CREATURE:PANDA, GIGANTIC:EYE -CREATURE:PANDA, GIGANTIC:BRAIN -CREATURE:PANDA, GIGANTIC:LUNG -CREATURE:PANDA, GIGANTIC:HEART -CREATURE:PANDA, GIGANTIC:LIVER -CREATURE:PANDA, GIGANTIC:GUT - CREATURE:PANDA, GIGANTIC:STOMACH - CREATURE:PANDA, GIGANTIC:GIZZARD -!CREATURE:PANDA, GIGANTIC:PANCREAS -CREATURE:PANDA, GIGANTIC:SPLEEN -CREATURE:PANDA, GIGANTIC:KIDNEY -CREATURE:PANDA MAN:MUSCLE -CREATURE:PANDA MAN:EYE -CREATURE:PANDA MAN:BRAIN -CREATURE:PANDA MAN:LUNG -CREATURE:PANDA MAN:HEART -CREATURE:PANDA MAN:LIVER -CREATURE:PANDA MAN:GUT -CREATURE:PANDA MAN:STOMACH -CREATURE:PANDA MAN:GIZZARD -CREATURE:PANDA MAN:PANCREAS -CREATURE:PANDA MAN:SPLEEN -CREATURE:PANDA MAN:KIDNEY -CREATURE:CAPYBARA:MUSCLE -CREATURE:CAPYBARA:EYE -CREATURE:CAPYBARA:BRAIN -CREATURE:CAPYBARA:LUNG -CREATURE:CAPYBARA:HEART -CREATURE:CAPYBARA:LIVER -CREATURE:CAPYBARA:GUT -CREATURE:CAPYBARA:STOMACH -CREATURE:CAPYBARA:GIZZARD -CREATURE:CAPYBARA:PANCREAS -CREATURE:CAPYBARA:SPLEEN -CREATURE:CAPYBARA:KIDNEY -CREATURE:CAPYBARA, GIANT:MUSCLE -CREATURE:CAPYBARA, GIANT:EYE -CREATURE:CAPYBARA, GIANT:BRAIN -CREATURE:CAPYBARA, GIANT:LUNG -CREATURE:CAPYBARA, GIANT:HEART -CREATURE:CAPYBARA, GIANT:LIVER -CREATURE:CAPYBARA, GIANT:GUT - CREATURE:CAPYBARA, GIANT:STOMACH - CREATURE:CAPYBARA, GIANT:GIZZARD -!CREATURE:CAPYBARA, GIANT:PANCREAS -CREATURE:CAPYBARA, GIANT:SPLEEN -CREATURE:CAPYBARA, GIANT:KIDNEY -CREATURE:CAPYBARA MAN:MUSCLE -CREATURE:CAPYBARA MAN:EYE -CREATURE:CAPYBARA MAN:BRAIN -CREATURE:CAPYBARA MAN:LUNG -CREATURE:CAPYBARA MAN:HEART -CREATURE:CAPYBARA MAN:LIVER -CREATURE:CAPYBARA MAN:GUT -CREATURE:CAPYBARA MAN:STOMACH -CREATURE:CAPYBARA MAN:GIZZARD -CREATURE:CAPYBARA MAN:PANCREAS -CREATURE:CAPYBARA MAN:SPLEEN -CREATURE:CAPYBARA MAN:KIDNEY -CREATURE:BADGER:MUSCLE -CREATURE:BADGER:EYE -CREATURE:BADGER:BRAIN -CREATURE:BADGER:LUNG -CREATURE:BADGER:HEART -CREATURE:BADGER:LIVER -CREATURE:BADGER:GUT -CREATURE:BADGER:STOMACH -CREATURE:BADGER:GIZZARD -CREATURE:BADGER:PANCREAS -CREATURE:BADGER:SPLEEN -CREATURE:BADGER:KIDNEY -CREATURE:BADGER MAN:MUSCLE -CREATURE:BADGER MAN:EYE -CREATURE:BADGER MAN:BRAIN -CREATURE:BADGER MAN:LUNG -CREATURE:BADGER MAN:HEART -CREATURE:BADGER MAN:LIVER -CREATURE:BADGER MAN:GUT -CREATURE:BADGER MAN:STOMACH -CREATURE:BADGER MAN:GIZZARD -CREATURE:BADGER MAN:PANCREAS -CREATURE:BADGER MAN:SPLEEN -CREATURE:BADGER MAN:KIDNEY -CREATURE:BADGER, GIANT:MUSCLE -CREATURE:BADGER, GIANT:EYE -CREATURE:BADGER, GIANT:BRAIN -CREATURE:BADGER, GIANT:LUNG -CREATURE:BADGER, GIANT:HEART -CREATURE:BADGER, GIANT:LIVER -CREATURE:BADGER, GIANT:GUT -CREATURE:BADGER, GIANT:STOMACH -CREATURE:BADGER, GIANT:GIZZARD -CREATURE:BADGER, GIANT:PANCREAS -CREATURE:BADGER, GIANT:SPLEEN -CREATURE:BADGER, GIANT:KIDNEY -CREATURE:MOOSE:MUSCLE -CREATURE:MOOSE:EYE -CREATURE:MOOSE:BRAIN -CREATURE:MOOSE:LUNG -CREATURE:MOOSE:HEART -CREATURE:MOOSE:LIVER -CREATURE:MOOSE:GUT -CREATURE:MOOSE:STOMACH -CREATURE:MOOSE:GIZZARD -CREATURE:MOOSE:PANCREAS -CREATURE:MOOSE:SPLEEN -CREATURE:MOOSE:KIDNEY -CREATURE:MOOSE MAN:MUSCLE -CREATURE:MOOSE MAN:EYE -CREATURE:MOOSE MAN:BRAIN -CREATURE:MOOSE MAN:LUNG -CREATURE:MOOSE MAN:HEART -CREATURE:MOOSE MAN:LIVER -CREATURE:MOOSE MAN:GUT -CREATURE:MOOSE MAN:STOMACH -CREATURE:MOOSE MAN:GIZZARD -CREATURE:MOOSE MAN:PANCREAS -CREATURE:MOOSE MAN:SPLEEN -CREATURE:MOOSE MAN:KIDNEY -CREATURE:MOOSE, GIANT:MUSCLE -CREATURE:MOOSE, GIANT:EYE -CREATURE:MOOSE, GIANT:BRAIN -CREATURE:MOOSE, GIANT:LUNG -CREATURE:MOOSE, GIANT:HEART -CREATURE:MOOSE, GIANT:LIVER -CREATURE:MOOSE, GIANT:GUT -CREATURE:MOOSE, GIANT:STOMACH -CREATURE:MOOSE, GIANT:GIZZARD -CREATURE:MOOSE, GIANT:PANCREAS -CREATURE:MOOSE, GIANT:SPLEEN -CREATURE:MOOSE, GIANT:KIDNEY -CREATURE:RED PANDA:MUSCLE -CREATURE:RED PANDA:EYE -CREATURE:RED PANDA:BRAIN -CREATURE:RED PANDA:LUNG -CREATURE:RED PANDA:HEART -CREATURE:RED PANDA:LIVER -CREATURE:RED PANDA:GUT -CREATURE:RED PANDA:STOMACH -CREATURE:RED PANDA:GIZZARD -CREATURE:RED PANDA:PANCREAS -CREATURE:RED PANDA:SPLEEN -CREATURE:RED PANDA:KIDNEY -CREATURE:RED PANDA MAN:MUSCLE -CREATURE:RED PANDA MAN:EYE -CREATURE:RED PANDA MAN:BRAIN -CREATURE:RED PANDA MAN:LUNG -CREATURE:RED PANDA MAN:HEART -CREATURE:RED PANDA MAN:LIVER -CREATURE:RED PANDA MAN:GUT -CREATURE:RED PANDA MAN:STOMACH -CREATURE:RED PANDA MAN:GIZZARD -CREATURE:RED PANDA MAN:PANCREAS -CREATURE:RED PANDA MAN:SPLEEN -CREATURE:RED PANDA MAN:KIDNEY - CREATURE:RED PANDA, GIANT:MUSCLE -CREATURE:RED PANDA, GIANT:EYE -CREATURE:RED PANDA, GIANT:BRAIN -CREATURE:RED PANDA, GIANT:LUNG -CREATURE:RED PANDA, GIANT:HEART -CREATURE:RED PANDA, GIANT:LIVER -CREATURE:RED PANDA, GIANT:GUT -!CREATURE:RED PANDA, GIANT:STOMACH -!CREATURE:RED PANDA, GIANT:GIZZARD -"CREATURE:RED PANDA, GIANT:PANCREAS - CREATURE:RED PANDA, GIANT:SPLEEN - CREATURE:RED PANDA, GIANT:KIDNEY -CREATURE:ELEPHANT:MUSCLE -CREATURE:ELEPHANT:EYE -CREATURE:ELEPHANT:BRAIN -CREATURE:ELEPHANT:LUNG -CREATURE:ELEPHANT:HEART -CREATURE:ELEPHANT:LIVER -CREATURE:ELEPHANT:GUT -CREATURE:ELEPHANT:STOMACH -CREATURE:ELEPHANT:GIZZARD -CREATURE:ELEPHANT:PANCREAS -CREATURE:ELEPHANT:SPLEEN -CREATURE:ELEPHANT:KIDNEY -CREATURE:ELEPHANT_MAN:MUSCLE -CREATURE:ELEPHANT_MAN:EYE -CREATURE:ELEPHANT_MAN:BRAIN -CREATURE:ELEPHANT_MAN:LUNG -CREATURE:ELEPHANT_MAN:HEART -CREATURE:ELEPHANT_MAN:LIVER -CREATURE:ELEPHANT_MAN:GUT -CREATURE:ELEPHANT_MAN:STOMACH -CREATURE:ELEPHANT_MAN:GIZZARD -CREATURE:ELEPHANT_MAN:PANCREAS -CREATURE:ELEPHANT_MAN:SPLEEN -CREATURE:ELEPHANT_MAN:KIDNEY -CREATURE:GIANT_ELEPHANT:MUSCLE -CREATURE:GIANT_ELEPHANT:EYE -CREATURE:GIANT_ELEPHANT:BRAIN -CREATURE:GIANT_ELEPHANT:LUNG -CREATURE:GIANT_ELEPHANT:HEART -CREATURE:GIANT_ELEPHANT:LIVER -CREATURE:GIANT_ELEPHANT:GUT -CREATURE:GIANT_ELEPHANT:STOMACH -CREATURE:GIANT_ELEPHANT:GIZZARD - CREATURE:GIANT_ELEPHANT:PANCREAS -CREATURE:GIANT_ELEPHANT:SPLEEN -CREATURE:GIANT_ELEPHANT:KIDNEY -CREATURE:WARTHOG:MUSCLE -CREATURE:WARTHOG:EYE -CREATURE:WARTHOG:BRAIN -CREATURE:WARTHOG:LUNG -CREATURE:WARTHOG:HEART -CREATURE:WARTHOG:LIVER -CREATURE:WARTHOG:GUT -CREATURE:WARTHOG:STOMACH -CREATURE:WARTHOG:GIZZARD -CREATURE:WARTHOG:PANCREAS -CREATURE:WARTHOG:SPLEEN -CREATURE:WARTHOG:KIDNEY -CREATURE:WARTHOG_MAN:MUSCLE -CREATURE:WARTHOG_MAN:EYE -CREATURE:WARTHOG_MAN:BRAIN -CREATURE:WARTHOG_MAN:LUNG -CREATURE:WARTHOG_MAN:HEART -CREATURE:WARTHOG_MAN:LIVER -CREATURE:WARTHOG_MAN:GUT -CREATURE:WARTHOG_MAN:STOMACH -CREATURE:WARTHOG_MAN:GIZZARD -CREATURE:WARTHOG_MAN:PANCREAS -CREATURE:WARTHOG_MAN:SPLEEN -CREATURE:WARTHOG_MAN:KIDNEY -CREATURE:GIANT_WARTHOG:MUSCLE -CREATURE:GIANT_WARTHOG:EYE -CREATURE:GIANT_WARTHOG:BRAIN -CREATURE:GIANT_WARTHOG:LUNG -CREATURE:GIANT_WARTHOG:HEART -CREATURE:GIANT_WARTHOG:LIVER -CREATURE:GIANT_WARTHOG:GUT -CREATURE:GIANT_WARTHOG:STOMACH -CREATURE:GIANT_WARTHOG:GIZZARD -CREATURE:GIANT_WARTHOG:PANCREAS -CREATURE:GIANT_WARTHOG:SPLEEN -CREATURE:GIANT_WARTHOG:KIDNEY -CREATURE:LION:MUSCLE -CREATURE:LION:EYE -CREATURE:LION:BRAIN -CREATURE:LION:LUNG -CREATURE:LION:HEART -CREATURE:LION:LIVER -CREATURE:LION:GUT -CREATURE:LION:STOMACH -CREATURE:LION:GIZZARD -CREATURE:LION:PANCREAS -CREATURE:LION:SPLEEN -CREATURE:LION:KIDNEY -CREATURE:LION_MAN:MUSCLE -CREATURE:LION_MAN:EYE -CREATURE:LION_MAN:BRAIN -CREATURE:LION_MAN:LUNG -CREATURE:LION_MAN:HEART -CREATURE:LION_MAN:LIVER -CREATURE:LION_MAN:GUT -CREATURE:LION_MAN:STOMACH -CREATURE:LION_MAN:GIZZARD -CREATURE:LION_MAN:PANCREAS -CREATURE:LION_MAN:SPLEEN -CREATURE:LION_MAN:KIDNEY -CREATURE:GIANT_LION:MUSCLE -CREATURE:GIANT_LION:EYE -CREATURE:GIANT_LION:BRAIN -CREATURE:GIANT_LION:LUNG -CREATURE:GIANT_LION:HEART -CREATURE:GIANT_LION:LIVER -CREATURE:GIANT_LION:GUT -CREATURE:GIANT_LION:STOMACH -CREATURE:GIANT_LION:GIZZARD -CREATURE:GIANT_LION:PANCREAS -CREATURE:GIANT_LION:SPLEEN -CREATURE:GIANT_LION:KIDNEY -CREATURE:LEOPARD:MUSCLE -CREATURE:LEOPARD:EYE -CREATURE:LEOPARD:BRAIN -CREATURE:LEOPARD:LUNG -CREATURE:LEOPARD:HEART -CREATURE:LEOPARD:LIVER -CREATURE:LEOPARD:GUT -CREATURE:LEOPARD:STOMACH -CREATURE:LEOPARD:GIZZARD -CREATURE:LEOPARD:PANCREAS -CREATURE:LEOPARD:SPLEEN -CREATURE:LEOPARD:KIDNEY -CREATURE:LEOPARD_MAN:MUSCLE -CREATURE:LEOPARD_MAN:EYE -CREATURE:LEOPARD_MAN:BRAIN -CREATURE:LEOPARD_MAN:LUNG -CREATURE:LEOPARD_MAN:HEART -CREATURE:LEOPARD_MAN:LIVER -CREATURE:LEOPARD_MAN:GUT -CREATURE:LEOPARD_MAN:STOMACH -CREATURE:LEOPARD_MAN:GIZZARD -CREATURE:LEOPARD_MAN:PANCREAS -CREATURE:LEOPARD_MAN:SPLEEN -CREATURE:LEOPARD_MAN:KIDNEY -CREATURE:GIANT_LEOPARD:MUSCLE -CREATURE:GIANT_LEOPARD:EYE -CREATURE:GIANT_LEOPARD:BRAIN -CREATURE:GIANT_LEOPARD:LUNG -CREATURE:GIANT_LEOPARD:HEART -CREATURE:GIANT_LEOPARD:LIVER -CREATURE:GIANT_LEOPARD:GUT -CREATURE:GIANT_LEOPARD:STOMACH -CREATURE:GIANT_LEOPARD:GIZZARD -CREATURE:GIANT_LEOPARD:PANCREAS -CREATURE:GIANT_LEOPARD:SPLEEN -CREATURE:GIANT_LEOPARD:KIDNEY -CREATURE:JAGUAR:MUSCLE -CREATURE:JAGUAR:EYE -CREATURE:JAGUAR:BRAIN -CREATURE:JAGUAR:LUNG -CREATURE:JAGUAR:HEART -CREATURE:JAGUAR:LIVER -CREATURE:JAGUAR:GUT -CREATURE:JAGUAR:STOMACH -CREATURE:JAGUAR:GIZZARD -CREATURE:JAGUAR:PANCREAS -CREATURE:JAGUAR:SPLEEN -CREATURE:JAGUAR:KIDNEY -CREATURE:JAGUAR_MAN:MUSCLE -CREATURE:JAGUAR_MAN:EYE -CREATURE:JAGUAR_MAN:BRAIN -CREATURE:JAGUAR_MAN:LUNG -CREATURE:JAGUAR_MAN:HEART -CREATURE:JAGUAR_MAN:LIVER -CREATURE:JAGUAR_MAN:GUT -CREATURE:JAGUAR_MAN:STOMACH -CREATURE:JAGUAR_MAN:GIZZARD -CREATURE:JAGUAR_MAN:PANCREAS -CREATURE:JAGUAR_MAN:SPLEEN -CREATURE:JAGUAR_MAN:KIDNEY -CREATURE:GIANT_JAGUAR:MUSCLE -CREATURE:GIANT_JAGUAR:EYE -CREATURE:GIANT_JAGUAR:BRAIN -CREATURE:GIANT_JAGUAR:LUNG -CREATURE:GIANT_JAGUAR:HEART -CREATURE:GIANT_JAGUAR:LIVER -CREATURE:GIANT_JAGUAR:GUT -CREATURE:GIANT_JAGUAR:STOMACH -CREATURE:GIANT_JAGUAR:GIZZARD -CREATURE:GIANT_JAGUAR:PANCREAS -CREATURE:GIANT_JAGUAR:SPLEEN -CREATURE:GIANT_JAGUAR:KIDNEY -CREATURE:TIGER:MUSCLE -CREATURE:TIGER:EYE -CREATURE:TIGER:BRAIN -CREATURE:TIGER:LUNG -CREATURE:TIGER:HEART -CREATURE:TIGER:LIVER -CREATURE:TIGER:GUT -CREATURE:TIGER:STOMACH -CREATURE:TIGER:GIZZARD -CREATURE:TIGER:PANCREAS -CREATURE:TIGER:SPLEEN -CREATURE:TIGER:KIDNEY -CREATURE:TIGER_MAN:MUSCLE -CREATURE:TIGER_MAN:EYE -CREATURE:TIGER_MAN:BRAIN -CREATURE:TIGER_MAN:LUNG -CREATURE:TIGER_MAN:HEART -CREATURE:TIGER_MAN:LIVER -CREATURE:TIGER_MAN:GUT -CREATURE:TIGER_MAN:STOMACH -CREATURE:TIGER_MAN:GIZZARD -CREATURE:TIGER_MAN:PANCREAS -CREATURE:TIGER_MAN:SPLEEN -CREATURE:TIGER_MAN:KIDNEY -CREATURE:GIANT_TIGER:MUSCLE -CREATURE:GIANT_TIGER:EYE -CREATURE:GIANT_TIGER:BRAIN -CREATURE:GIANT_TIGER:LUNG -CREATURE:GIANT_TIGER:HEART -CREATURE:GIANT_TIGER:LIVER -CREATURE:GIANT_TIGER:GUT -CREATURE:GIANT_TIGER:STOMACH -CREATURE:GIANT_TIGER:GIZZARD -CREATURE:GIANT_TIGER:PANCREAS -CREATURE:GIANT_TIGER:SPLEEN -CREATURE:GIANT_TIGER:KIDNEY -CREATURE:CHEETAH:MUSCLE -CREATURE:CHEETAH:EYE -CREATURE:CHEETAH:BRAIN -CREATURE:CHEETAH:LUNG -CREATURE:CHEETAH:HEART -CREATURE:CHEETAH:LIVER -CREATURE:CHEETAH:GUT -CREATURE:CHEETAH:STOMACH -CREATURE:CHEETAH:GIZZARD -CREATURE:CHEETAH:PANCREAS -CREATURE:CHEETAH:SPLEEN -CREATURE:CHEETAH:KIDNEY -CREATURE:CHEETAH_MAN:MUSCLE -CREATURE:CHEETAH_MAN:EYE -CREATURE:CHEETAH_MAN:BRAIN -CREATURE:CHEETAH_MAN:LUNG -CREATURE:CHEETAH_MAN:HEART -CREATURE:CHEETAH_MAN:LIVER -CREATURE:CHEETAH_MAN:GUT -CREATURE:CHEETAH_MAN:STOMACH -CREATURE:CHEETAH_MAN:GIZZARD -CREATURE:CHEETAH_MAN:PANCREAS -CREATURE:CHEETAH_MAN:SPLEEN -CREATURE:CHEETAH_MAN:KIDNEY -CREATURE:GIANT_CHEETAH:MUSCLE -CREATURE:GIANT_CHEETAH:EYE -CREATURE:GIANT_CHEETAH:BRAIN -CREATURE:GIANT_CHEETAH:LUNG -CREATURE:GIANT_CHEETAH:HEART -CREATURE:GIANT_CHEETAH:LIVER -CREATURE:GIANT_CHEETAH:GUT -CREATURE:GIANT_CHEETAH:STOMACH -CREATURE:GIANT_CHEETAH:GIZZARD -CREATURE:GIANT_CHEETAH:PANCREAS -CREATURE:GIANT_CHEETAH:SPLEEN -CREATURE:GIANT_CHEETAH:KIDNEY -CREATURE:GAZELLE:MUSCLE -CREATURE:GAZELLE:EYE -CREATURE:GAZELLE:BRAIN -CREATURE:GAZELLE:LUNG -CREATURE:GAZELLE:HEART -CREATURE:GAZELLE:LIVER -CREATURE:GAZELLE:GUT -CREATURE:GAZELLE:STOMACH -CREATURE:GAZELLE:GIZZARD -CREATURE:GAZELLE:PANCREAS -CREATURE:GAZELLE:SPLEEN -CREATURE:GAZELLE:KIDNEY -CREATURE:GAZELLE_MAN:MUSCLE -CREATURE:GAZELLE_MAN:EYE -CREATURE:GAZELLE_MAN:BRAIN -CREATURE:GAZELLE_MAN:LUNG -CREATURE:GAZELLE_MAN:HEART -CREATURE:GAZELLE_MAN:LIVER -CREATURE:GAZELLE_MAN:GUT -CREATURE:GAZELLE_MAN:STOMACH -CREATURE:GAZELLE_MAN:GIZZARD -CREATURE:GAZELLE_MAN:PANCREAS -CREATURE:GAZELLE_MAN:SPLEEN -CREATURE:GAZELLE_MAN:KIDNEY -CREATURE:GIANT_GAZELLE:MUSCLE -CREATURE:GIANT_GAZELLE:EYE -CREATURE:GIANT_GAZELLE:BRAIN -CREATURE:GIANT_GAZELLE:LUNG -CREATURE:GIANT_GAZELLE:HEART -CREATURE:GIANT_GAZELLE:LIVER -CREATURE:GIANT_GAZELLE:GUT -CREATURE:GIANT_GAZELLE:STOMACH -CREATURE:GIANT_GAZELLE:GIZZARD -CREATURE:GIANT_GAZELLE:PANCREAS -CREATURE:GIANT_GAZELLE:SPLEEN -CREATURE:GIANT_GAZELLE:KIDNEY -CREATURE:MANDRILL:MUSCLE -CREATURE:MANDRILL:EYE -CREATURE:MANDRILL:BRAIN -CREATURE:MANDRILL:LUNG -CREATURE:MANDRILL:HEART -CREATURE:MANDRILL:LIVER -CREATURE:MANDRILL:GUT -CREATURE:MANDRILL:STOMACH -CREATURE:MANDRILL:GIZZARD -CREATURE:MANDRILL:PANCREAS -CREATURE:MANDRILL:SPLEEN -CREATURE:MANDRILL:KIDNEY -CREATURE:MANDRILL_MAN:MUSCLE -CREATURE:MANDRILL_MAN:EYE -CREATURE:MANDRILL_MAN:BRAIN -CREATURE:MANDRILL_MAN:LUNG -CREATURE:MANDRILL_MAN:HEART -CREATURE:MANDRILL_MAN:LIVER -CREATURE:MANDRILL_MAN:GUT -CREATURE:MANDRILL_MAN:STOMACH -CREATURE:MANDRILL_MAN:GIZZARD -CREATURE:MANDRILL_MAN:PANCREAS -CREATURE:MANDRILL_MAN:SPLEEN -CREATURE:MANDRILL_MAN:KIDNEY -CREATURE:GIANT_MANDRILL:MUSCLE -CREATURE:GIANT_MANDRILL:EYE -CREATURE:GIANT_MANDRILL:BRAIN -CREATURE:GIANT_MANDRILL:LUNG -CREATURE:GIANT_MANDRILL:HEART -CREATURE:GIANT_MANDRILL:LIVER -CREATURE:GIANT_MANDRILL:GUT -CREATURE:GIANT_MANDRILL:STOMACH -CREATURE:GIANT_MANDRILL:GIZZARD - CREATURE:GIANT_MANDRILL:PANCREAS -CREATURE:GIANT_MANDRILL:SPLEEN -CREATURE:GIANT_MANDRILL:KIDNEY -CREATURE:CHIMPANZEE:MUSCLE -CREATURE:CHIMPANZEE:EYE -CREATURE:CHIMPANZEE:BRAIN -CREATURE:CHIMPANZEE:LUNG -CREATURE:CHIMPANZEE:HEART -CREATURE:CHIMPANZEE:LIVER -CREATURE:CHIMPANZEE:GUT -CREATURE:CHIMPANZEE:STOMACH -CREATURE:CHIMPANZEE:GIZZARD -CREATURE:CHIMPANZEE:PANCREAS -CREATURE:CHIMPANZEE:SPLEEN -CREATURE:CHIMPANZEE:KIDNEY -CREATURE:BONOBO:MUSCLE -CREATURE:BONOBO:EYE -CREATURE:BONOBO:BRAIN -CREATURE:BONOBO:LUNG -CREATURE:BONOBO:HEART -CREATURE:BONOBO:LIVER -CREATURE:BONOBO:GUT -CREATURE:BONOBO:STOMACH -CREATURE:BONOBO:GIZZARD -CREATURE:BONOBO:PANCREAS -CREATURE:BONOBO:SPLEEN -CREATURE:BONOBO:KIDNEY -CREATURE:GORILLA:MUSCLE -CREATURE:GORILLA:EYE -CREATURE:GORILLA:BRAIN -CREATURE:GORILLA:LUNG -CREATURE:GORILLA:HEART -CREATURE:GORILLA:LIVER -CREATURE:GORILLA:GUT -CREATURE:GORILLA:STOMACH -CREATURE:GORILLA:GIZZARD -CREATURE:GORILLA:PANCREAS -CREATURE:GORILLA:SPLEEN -CREATURE:GORILLA:KIDNEY -CREATURE:ORANGUTAN:MUSCLE -CREATURE:ORANGUTAN:EYE -CREATURE:ORANGUTAN:BRAIN -CREATURE:ORANGUTAN:LUNG -CREATURE:ORANGUTAN:HEART -CREATURE:ORANGUTAN:LIVER -CREATURE:ORANGUTAN:GUT -CREATURE:ORANGUTAN:STOMACH -CREATURE:ORANGUTAN:GIZZARD -CREATURE:ORANGUTAN:PANCREAS -CREATURE:ORANGUTAN:SPLEEN -CREATURE:ORANGUTAN:KIDNEY -CREATURE:GIBBON_SIAMANG:MUSCLE -CREATURE:GIBBON_SIAMANG:EYE -CREATURE:GIBBON_SIAMANG:BRAIN -CREATURE:GIBBON_SIAMANG:LUNG -CREATURE:GIBBON_SIAMANG:HEART -CREATURE:GIBBON_SIAMANG:LIVER -CREATURE:GIBBON_SIAMANG:GUT -CREATURE:GIBBON_SIAMANG:STOMACH -CREATURE:GIBBON_SIAMANG:GIZZARD - CREATURE:GIBBON_SIAMANG:PANCREAS -CREATURE:GIBBON_SIAMANG:SPLEEN -CREATURE:GIBBON_SIAMANG:KIDNEY -#CREATURE:GIBBON_WHITE_HANDED:MUSCLE - CREATURE:GIBBON_WHITE_HANDED:EYE -"CREATURE:GIBBON_WHITE_HANDED:BRAIN -!CREATURE:GIBBON_WHITE_HANDED:LUNG -"CREATURE:GIBBON_WHITE_HANDED:HEART -"CREATURE:GIBBON_WHITE_HANDED:LIVER - CREATURE:GIBBON_WHITE_HANDED:GUT -$CREATURE:GIBBON_WHITE_HANDED:STOMACH -$CREATURE:GIBBON_WHITE_HANDED:GIZZARD -%CREATURE:GIBBON_WHITE_HANDED:PANCREAS -#CREATURE:GIBBON_WHITE_HANDED:SPLEEN -#CREATURE:GIBBON_WHITE_HANDED:KIDNEY -#CREATURE:GIBBON_BLACK_HANDED:MUSCLE - CREATURE:GIBBON_BLACK_HANDED:EYE -"CREATURE:GIBBON_BLACK_HANDED:BRAIN -!CREATURE:GIBBON_BLACK_HANDED:LUNG -"CREATURE:GIBBON_BLACK_HANDED:HEART -"CREATURE:GIBBON_BLACK_HANDED:LIVER - CREATURE:GIBBON_BLACK_HANDED:GUT -$CREATURE:GIBBON_BLACK_HANDED:STOMACH -$CREATURE:GIBBON_BLACK_HANDED:GIZZARD -%CREATURE:GIBBON_BLACK_HANDED:PANCREAS -#CREATURE:GIBBON_BLACK_HANDED:SPLEEN -#CREATURE:GIBBON_BLACK_HANDED:KIDNEY -CREATURE:GIBBON_GRAY:MUSCLE -CREATURE:GIBBON_GRAY:EYE -CREATURE:GIBBON_GRAY:BRAIN -CREATURE:GIBBON_GRAY:LUNG -CREATURE:GIBBON_GRAY:HEART -CREATURE:GIBBON_GRAY:LIVER -CREATURE:GIBBON_GRAY:GUT -CREATURE:GIBBON_GRAY:STOMACH -CREATURE:GIBBON_GRAY:GIZZARD -CREATURE:GIBBON_GRAY:PANCREAS -CREATURE:GIBBON_GRAY:SPLEEN -CREATURE:GIBBON_GRAY:KIDNEY -CREATURE:GIBBON_SILVERY:MUSCLE -CREATURE:GIBBON_SILVERY:EYE -CREATURE:GIBBON_SILVERY:BRAIN -CREATURE:GIBBON_SILVERY:LUNG -CREATURE:GIBBON_SILVERY:HEART -CREATURE:GIBBON_SILVERY:LIVER -CREATURE:GIBBON_SILVERY:GUT -CREATURE:GIBBON_SILVERY:STOMACH -CREATURE:GIBBON_SILVERY:GIZZARD - CREATURE:GIBBON_SILVERY:PANCREAS -CREATURE:GIBBON_SILVERY:SPLEEN -CREATURE:GIBBON_SILVERY:KIDNEY -CREATURE:GIBBON_PILEATED:MUSCLE -CREATURE:GIBBON_PILEATED:EYE -CREATURE:GIBBON_PILEATED:BRAIN -CREATURE:GIBBON_PILEATED:LUNG -CREATURE:GIBBON_PILEATED:HEART -CREATURE:GIBBON_PILEATED:LIVER -CREATURE:GIBBON_PILEATED:GUT - CREATURE:GIBBON_PILEATED:STOMACH - CREATURE:GIBBON_PILEATED:GIZZARD -!CREATURE:GIBBON_PILEATED:PANCREAS -CREATURE:GIBBON_PILEATED:SPLEEN -CREATURE:GIBBON_PILEATED:KIDNEY -CREATURE:GIBBON_BILOU:MUSCLE -CREATURE:GIBBON_BILOU:EYE -CREATURE:GIBBON_BILOU:BRAIN -CREATURE:GIBBON_BILOU:LUNG -CREATURE:GIBBON_BILOU:HEART -CREATURE:GIBBON_BILOU:LIVER -CREATURE:GIBBON_BILOU:GUT -CREATURE:GIBBON_BILOU:STOMACH -CREATURE:GIBBON_BILOU:GIZZARD -CREATURE:GIBBON_BILOU:PANCREAS -CREATURE:GIBBON_BILOU:SPLEEN -CREATURE:GIBBON_BILOU:KIDNEY -#CREATURE:GIBBON_WHITE_BROWED:MUSCLE - CREATURE:GIBBON_WHITE_BROWED:EYE -"CREATURE:GIBBON_WHITE_BROWED:BRAIN -!CREATURE:GIBBON_WHITE_BROWED:LUNG -"CREATURE:GIBBON_WHITE_BROWED:HEART -"CREATURE:GIBBON_WHITE_BROWED:LIVER - CREATURE:GIBBON_WHITE_BROWED:GUT -$CREATURE:GIBBON_WHITE_BROWED:STOMACH -$CREATURE:GIBBON_WHITE_BROWED:GIZZARD -%CREATURE:GIBBON_WHITE_BROWED:PANCREAS -#CREATURE:GIBBON_WHITE_BROWED:SPLEEN -#CREATURE:GIBBON_WHITE_BROWED:KIDNEY -$CREATURE:GIBBON_BLACK_CRESTED:MUSCLE -!CREATURE:GIBBON_BLACK_CRESTED:EYE -#CREATURE:GIBBON_BLACK_CRESTED:BRAIN -"CREATURE:GIBBON_BLACK_CRESTED:LUNG -#CREATURE:GIBBON_BLACK_CRESTED:HEART -#CREATURE:GIBBON_BLACK_CRESTED:LIVER -!CREATURE:GIBBON_BLACK_CRESTED:GUT -%CREATURE:GIBBON_BLACK_CRESTED:STOMACH -%CREATURE:GIBBON_BLACK_CRESTED:GIZZARD -&CREATURE:GIBBON_BLACK_CRESTED:PANCREAS -$CREATURE:GIBBON_BLACK_CRESTED:SPLEEN -$CREATURE:GIBBON_BLACK_CRESTED:KIDNEY -CREATURE:CAMEL_1_HUMP:MUSCLE -CREATURE:CAMEL_1_HUMP:EYE -CREATURE:CAMEL_1_HUMP:BRAIN -CREATURE:CAMEL_1_HUMP:LUNG -CREATURE:CAMEL_1_HUMP:HEART -CREATURE:CAMEL_1_HUMP:LIVER -CREATURE:CAMEL_1_HUMP:GUT -CREATURE:CAMEL_1_HUMP:STOMACH -CREATURE:CAMEL_1_HUMP:GIZZARD -CREATURE:CAMEL_1_HUMP:PANCREAS -CREATURE:CAMEL_1_HUMP:SPLEEN -CREATURE:CAMEL_1_HUMP:KIDNEY - CREATURE:CAMEL_1_HUMP_MAN:MUSCLE -CREATURE:CAMEL_1_HUMP_MAN:EYE -CREATURE:CAMEL_1_HUMP_MAN:BRAIN -CREATURE:CAMEL_1_HUMP_MAN:LUNG -CREATURE:CAMEL_1_HUMP_MAN:HEART -CREATURE:CAMEL_1_HUMP_MAN:LIVER -CREATURE:CAMEL_1_HUMP_MAN:GUT -!CREATURE:CAMEL_1_HUMP_MAN:STOMACH -!CREATURE:CAMEL_1_HUMP_MAN:GIZZARD -"CREATURE:CAMEL_1_HUMP_MAN:PANCREAS - CREATURE:CAMEL_1_HUMP_MAN:SPLEEN - CREATURE:CAMEL_1_HUMP_MAN:KIDNEY -"CREATURE:GIANT_CAMEL_1_HUMP:MUSCLE -CREATURE:GIANT_CAMEL_1_HUMP:EYE -!CREATURE:GIANT_CAMEL_1_HUMP:BRAIN - CREATURE:GIANT_CAMEL_1_HUMP:LUNG -!CREATURE:GIANT_CAMEL_1_HUMP:HEART -!CREATURE:GIANT_CAMEL_1_HUMP:LIVER -CREATURE:GIANT_CAMEL_1_HUMP:GUT -#CREATURE:GIANT_CAMEL_1_HUMP:STOMACH -#CREATURE:GIANT_CAMEL_1_HUMP:GIZZARD -$CREATURE:GIANT_CAMEL_1_HUMP:PANCREAS -"CREATURE:GIANT_CAMEL_1_HUMP:SPLEEN -"CREATURE:GIANT_CAMEL_1_HUMP:KIDNEY -CREATURE:CAMEL_2_HUMP:MUSCLE -CREATURE:CAMEL_2_HUMP:EYE -CREATURE:CAMEL_2_HUMP:BRAIN -CREATURE:CAMEL_2_HUMP:LUNG -CREATURE:CAMEL_2_HUMP:HEART -CREATURE:CAMEL_2_HUMP:LIVER -CREATURE:CAMEL_2_HUMP:GUT -CREATURE:CAMEL_2_HUMP:STOMACH -CREATURE:CAMEL_2_HUMP:GIZZARD -CREATURE:CAMEL_2_HUMP:PANCREAS -CREATURE:CAMEL_2_HUMP:SPLEEN -CREATURE:CAMEL_2_HUMP:KIDNEY - CREATURE:CAMEL_2_HUMP_MAN:MUSCLE -CREATURE:CAMEL_2_HUMP_MAN:EYE -CREATURE:CAMEL_2_HUMP_MAN:BRAIN -CREATURE:CAMEL_2_HUMP_MAN:LUNG -CREATURE:CAMEL_2_HUMP_MAN:HEART -CREATURE:CAMEL_2_HUMP_MAN:LIVER -CREATURE:CAMEL_2_HUMP_MAN:GUT -!CREATURE:CAMEL_2_HUMP_MAN:STOMACH -!CREATURE:CAMEL_2_HUMP_MAN:GIZZARD -"CREATURE:CAMEL_2_HUMP_MAN:PANCREAS - CREATURE:CAMEL_2_HUMP_MAN:SPLEEN - CREATURE:CAMEL_2_HUMP_MAN:KIDNEY -"CREATURE:GIANT_CAMEL_2_HUMP:MUSCLE -CREATURE:GIANT_CAMEL_2_HUMP:EYE -!CREATURE:GIANT_CAMEL_2_HUMP:BRAIN - CREATURE:GIANT_CAMEL_2_HUMP:LUNG -!CREATURE:GIANT_CAMEL_2_HUMP:HEART -!CREATURE:GIANT_CAMEL_2_HUMP:LIVER -CREATURE:GIANT_CAMEL_2_HUMP:GUT -#CREATURE:GIANT_CAMEL_2_HUMP:STOMACH -#CREATURE:GIANT_CAMEL_2_HUMP:GIZZARD -$CREATURE:GIANT_CAMEL_2_HUMP:PANCREAS -"CREATURE:GIANT_CAMEL_2_HUMP:SPLEEN -"CREATURE:GIANT_CAMEL_2_HUMP:KIDNEY -#CREATURE:CROCODILE_SALTWATER:MUSCLE - CREATURE:CROCODILE_SALTWATER:EYE -"CREATURE:CROCODILE_SALTWATER:BRAIN -!CREATURE:CROCODILE_SALTWATER:LUNG -"CREATURE:CROCODILE_SALTWATER:HEART -"CREATURE:CROCODILE_SALTWATER:LIVER - CREATURE:CROCODILE_SALTWATER:GUT -$CREATURE:CROCODILE_SALTWATER:STOMACH -$CREATURE:CROCODILE_SALTWATER:GIZZARD -%CREATURE:CROCODILE_SALTWATER:PANCREAS -#CREATURE:CROCODILE_SALTWATER:SPLEEN -#CREATURE:CROCODILE_SALTWATER:KIDNEY -'CREATURE:CROCODILE_SALTWATER_MAN:MUSCLE -$CREATURE:CROCODILE_SALTWATER_MAN:EYE -&CREATURE:CROCODILE_SALTWATER_MAN:BRAIN -%CREATURE:CROCODILE_SALTWATER_MAN:LUNG -&CREATURE:CROCODILE_SALTWATER_MAN:HEART -&CREATURE:CROCODILE_SALTWATER_MAN:LIVER -$CREATURE:CROCODILE_SALTWATER_MAN:GUT -(CREATURE:CROCODILE_SALTWATER_MAN:STOMACH -(CREATURE:CROCODILE_SALTWATER_MAN:GIZZARD -)CREATURE:CROCODILE_SALTWATER_MAN:PANCREAS -'CREATURE:CROCODILE_SALTWATER_MAN:SPLEEN -'CREATURE:CROCODILE_SALTWATER_MAN:KIDNEY -)CREATURE:GIANT_CROCODILE_SALTWATER:MUSCLE -&CREATURE:GIANT_CROCODILE_SALTWATER:EYE -(CREATURE:GIANT_CROCODILE_SALTWATER:BRAIN -'CREATURE:GIANT_CROCODILE_SALTWATER:LUNG -(CREATURE:GIANT_CROCODILE_SALTWATER:HEART -(CREATURE:GIANT_CROCODILE_SALTWATER:LIVER -&CREATURE:GIANT_CROCODILE_SALTWATER:GUT -*CREATURE:GIANT_CROCODILE_SALTWATER:STOMACH -*CREATURE:GIANT_CROCODILE_SALTWATER:GIZZARD -+CREATURE:GIANT_CROCODILE_SALTWATER:PANCREAS -)CREATURE:GIANT_CROCODILE_SALTWATER:SPLEEN -)CREATURE:GIANT_CROCODILE_SALTWATER:KIDNEY -CREATURE:BIRD_VULTURE:MUSCLE -CREATURE:BIRD_VULTURE:EYE -CREATURE:BIRD_VULTURE:BRAIN -CREATURE:BIRD_VULTURE:LUNG -CREATURE:BIRD_VULTURE:HEART -CREATURE:BIRD_VULTURE:LIVER -CREATURE:BIRD_VULTURE:GUT -CREATURE:BIRD_VULTURE:STOMACH -CREATURE:BIRD_VULTURE:GIZZARD -CREATURE:BIRD_VULTURE:PANCREAS -CREATURE:BIRD_VULTURE:SPLEEN -CREATURE:BIRD_VULTURE:KIDNEY -CREATURE:VULTURE_MAN:MUSCLE -CREATURE:VULTURE_MAN:EYE -CREATURE:VULTURE_MAN:BRAIN -CREATURE:VULTURE_MAN:LUNG -CREATURE:VULTURE_MAN:HEART -CREATURE:VULTURE_MAN:LIVER -CREATURE:VULTURE_MAN:GUT -CREATURE:VULTURE_MAN:STOMACH -CREATURE:VULTURE_MAN:GIZZARD -CREATURE:VULTURE_MAN:PANCREAS -CREATURE:VULTURE_MAN:SPLEEN -CREATURE:VULTURE_MAN:KIDNEY -CREATURE:GIANT_VULTURE:MUSCLE -CREATURE:GIANT_VULTURE:EYE -CREATURE:GIANT_VULTURE:BRAIN -CREATURE:GIANT_VULTURE:LUNG -CREATURE:GIANT_VULTURE:HEART -CREATURE:GIANT_VULTURE:LIVER -CREATURE:GIANT_VULTURE:GUT -CREATURE:GIANT_VULTURE:STOMACH -CREATURE:GIANT_VULTURE:GIZZARD -CREATURE:GIANT_VULTURE:PANCREAS -CREATURE:GIANT_VULTURE:SPLEEN -CREATURE:GIANT_VULTURE:KIDNEY -CREATURE:RHINOCEROS:MUSCLE -CREATURE:RHINOCEROS:EYE -CREATURE:RHINOCEROS:BRAIN -CREATURE:RHINOCEROS:LUNG -CREATURE:RHINOCEROS:HEART -CREATURE:RHINOCEROS:LIVER -CREATURE:RHINOCEROS:GUT -CREATURE:RHINOCEROS:STOMACH -CREATURE:RHINOCEROS:GIZZARD -CREATURE:RHINOCEROS:PANCREAS -CREATURE:RHINOCEROS:SPLEEN -CREATURE:RHINOCEROS:KIDNEY -CREATURE:RHINOCEROS_MAN:MUSCLE -CREATURE:RHINOCEROS_MAN:EYE -CREATURE:RHINOCEROS_MAN:BRAIN -CREATURE:RHINOCEROS_MAN:LUNG -CREATURE:RHINOCEROS_MAN:HEART -CREATURE:RHINOCEROS_MAN:LIVER -CREATURE:RHINOCEROS_MAN:GUT -CREATURE:RHINOCEROS_MAN:STOMACH -CREATURE:RHINOCEROS_MAN:GIZZARD - CREATURE:RHINOCEROS_MAN:PANCREAS -CREATURE:RHINOCEROS_MAN:SPLEEN -CREATURE:RHINOCEROS_MAN:KIDNEY - CREATURE:GIANT_RHINOCEROS:MUSCLE -CREATURE:GIANT_RHINOCEROS:EYE -CREATURE:GIANT_RHINOCEROS:BRAIN -CREATURE:GIANT_RHINOCEROS:LUNG -CREATURE:GIANT_RHINOCEROS:HEART -CREATURE:GIANT_RHINOCEROS:LIVER -CREATURE:GIANT_RHINOCEROS:GUT -!CREATURE:GIANT_RHINOCEROS:STOMACH -!CREATURE:GIANT_RHINOCEROS:GIZZARD -"CREATURE:GIANT_RHINOCEROS:PANCREAS - CREATURE:GIANT_RHINOCEROS:SPLEEN - CREATURE:GIANT_RHINOCEROS:KIDNEY -CREATURE:GIRAFFE:MUSCLE -CREATURE:GIRAFFE:EYE -CREATURE:GIRAFFE:BRAIN -CREATURE:GIRAFFE:LUNG -CREATURE:GIRAFFE:HEART -CREATURE:GIRAFFE:LIVER -CREATURE:GIRAFFE:GUT -CREATURE:GIRAFFE:STOMACH -CREATURE:GIRAFFE:GIZZARD -CREATURE:GIRAFFE:PANCREAS -CREATURE:GIRAFFE:SPLEEN -CREATURE:GIRAFFE:KIDNEY -CREATURE:GIRAFFE_MAN:MUSCLE -CREATURE:GIRAFFE_MAN:EYE -CREATURE:GIRAFFE_MAN:BRAIN -CREATURE:GIRAFFE_MAN:LUNG -CREATURE:GIRAFFE_MAN:HEART -CREATURE:GIRAFFE_MAN:LIVER -CREATURE:GIRAFFE_MAN:GUT -CREATURE:GIRAFFE_MAN:STOMACH -CREATURE:GIRAFFE_MAN:GIZZARD -CREATURE:GIRAFFE_MAN:PANCREAS -CREATURE:GIRAFFE_MAN:SPLEEN -CREATURE:GIRAFFE_MAN:KIDNEY -CREATURE:GIANT_GIRAFFE:MUSCLE -CREATURE:GIANT_GIRAFFE:EYE -CREATURE:GIANT_GIRAFFE:BRAIN -CREATURE:GIANT_GIRAFFE:LUNG -CREATURE:GIANT_GIRAFFE:HEART -CREATURE:GIANT_GIRAFFE:LIVER -CREATURE:GIANT_GIRAFFE:GUT -CREATURE:GIANT_GIRAFFE:STOMACH -CREATURE:GIANT_GIRAFFE:GIZZARD -CREATURE:GIANT_GIRAFFE:PANCREAS -CREATURE:GIANT_GIRAFFE:SPLEEN -CREATURE:GIANT_GIRAFFE:KIDNEY -CREATURE:HONEY BADGER:MUSCLE -CREATURE:HONEY BADGER:EYE -CREATURE:HONEY BADGER:BRAIN -CREATURE:HONEY BADGER:LUNG -CREATURE:HONEY BADGER:HEART -CREATURE:HONEY BADGER:LIVER -CREATURE:HONEY BADGER:GUT -CREATURE:HONEY BADGER:STOMACH -CREATURE:HONEY BADGER:GIZZARD -CREATURE:HONEY BADGER:PANCREAS -CREATURE:HONEY BADGER:SPLEEN -CREATURE:HONEY BADGER:KIDNEY - CREATURE:HONEY BADGER MAN:MUSCLE -CREATURE:HONEY BADGER MAN:EYE -CREATURE:HONEY BADGER MAN:BRAIN -CREATURE:HONEY BADGER MAN:LUNG -CREATURE:HONEY BADGER MAN:HEART -CREATURE:HONEY BADGER MAN:LIVER -CREATURE:HONEY BADGER MAN:GUT -!CREATURE:HONEY BADGER MAN:STOMACH -!CREATURE:HONEY BADGER MAN:GIZZARD -"CREATURE:HONEY BADGER MAN:PANCREAS - CREATURE:HONEY BADGER MAN:SPLEEN - CREATURE:HONEY BADGER MAN:KIDNEY -#CREATURE:HONEY BADGER, GIANT:MUSCLE - CREATURE:HONEY BADGER, GIANT:EYE -"CREATURE:HONEY BADGER, GIANT:BRAIN -!CREATURE:HONEY BADGER, GIANT:LUNG -"CREATURE:HONEY BADGER, GIANT:HEART -"CREATURE:HONEY BADGER, GIANT:LIVER - CREATURE:HONEY BADGER, GIANT:GUT -$CREATURE:HONEY BADGER, GIANT:STOMACH -$CREATURE:HONEY BADGER, GIANT:GIZZARD -%CREATURE:HONEY BADGER, GIANT:PANCREAS -#CREATURE:HONEY BADGER, GIANT:SPLEEN -#CREATURE:HONEY BADGER, GIANT:KIDNEY -CREATURE:GIANT TORTOISE:MUSCLE -CREATURE:GIANT TORTOISE:EYE -CREATURE:GIANT TORTOISE:BRAIN -CREATURE:GIANT TORTOISE:LUNG -CREATURE:GIANT TORTOISE:HEART -CREATURE:GIANT TORTOISE:LIVER -CREATURE:GIANT TORTOISE:GUT -CREATURE:GIANT TORTOISE:STOMACH -CREATURE:GIANT TORTOISE:GIZZARD - CREATURE:GIANT TORTOISE:PANCREAS -CREATURE:GIANT TORTOISE:SPLEEN -CREATURE:GIANT TORTOISE:KIDNEY -"CREATURE:GIANT TORTOISE MAN:MUSCLE -CREATURE:GIANT TORTOISE MAN:EYE -!CREATURE:GIANT TORTOISE MAN:BRAIN - CREATURE:GIANT TORTOISE MAN:LUNG -!CREATURE:GIANT TORTOISE MAN:HEART -!CREATURE:GIANT TORTOISE MAN:LIVER -CREATURE:GIANT TORTOISE MAN:GUT -#CREATURE:GIANT TORTOISE MAN:STOMACH -#CREATURE:GIANT TORTOISE MAN:GIZZARD -$CREATURE:GIANT TORTOISE MAN:PANCREAS -"CREATURE:GIANT TORTOISE MAN:SPLEEN -"CREATURE:GIANT TORTOISE MAN:KIDNEY -!CREATURE:GIGANTIC TORTOISE:MUSCLE -CREATURE:GIGANTIC TORTOISE:EYE - CREATURE:GIGANTIC TORTOISE:BRAIN -CREATURE:GIGANTIC TORTOISE:LUNG - CREATURE:GIGANTIC TORTOISE:HEART - CREATURE:GIGANTIC TORTOISE:LIVER -CREATURE:GIGANTIC TORTOISE:GUT -"CREATURE:GIGANTIC TORTOISE:STOMACH -"CREATURE:GIGANTIC TORTOISE:GIZZARD -#CREATURE:GIGANTIC TORTOISE:PANCREAS -!CREATURE:GIGANTIC TORTOISE:SPLEEN -!CREATURE:GIGANTIC TORTOISE:KIDNEY -CREATURE:ARMADILLO:MUSCLE -CREATURE:ARMADILLO:EYE -CREATURE:ARMADILLO:BRAIN -CREATURE:ARMADILLO:LUNG -CREATURE:ARMADILLO:HEART -CREATURE:ARMADILLO:LIVER -CREATURE:ARMADILLO:GUT -CREATURE:ARMADILLO:STOMACH -CREATURE:ARMADILLO:GIZZARD -CREATURE:ARMADILLO:PANCREAS -CREATURE:ARMADILLO:SPLEEN -CREATURE:ARMADILLO:KIDNEY -CREATURE:ARMADILLO MAN:MUSCLE -CREATURE:ARMADILLO MAN:EYE -CREATURE:ARMADILLO MAN:BRAIN -CREATURE:ARMADILLO MAN:LUNG -CREATURE:ARMADILLO MAN:HEART -CREATURE:ARMADILLO MAN:LIVER -CREATURE:ARMADILLO MAN:GUT -CREATURE:ARMADILLO MAN:STOMACH -CREATURE:ARMADILLO MAN:GIZZARD -CREATURE:ARMADILLO MAN:PANCREAS -CREATURE:ARMADILLO MAN:SPLEEN -CREATURE:ARMADILLO MAN:KIDNEY - CREATURE:ARMADILLO, GIANT:MUSCLE -CREATURE:ARMADILLO, GIANT:EYE -CREATURE:ARMADILLO, GIANT:BRAIN -CREATURE:ARMADILLO, GIANT:LUNG -CREATURE:ARMADILLO, GIANT:HEART -CREATURE:ARMADILLO, GIANT:LIVER -CREATURE:ARMADILLO, GIANT:GUT -!CREATURE:ARMADILLO, GIANT:STOMACH -!CREATURE:ARMADILLO, GIANT:GIZZARD -"CREATURE:ARMADILLO, GIANT:PANCREAS - CREATURE:ARMADILLO, GIANT:SPLEEN - CREATURE:ARMADILLO, GIANT:KIDNEY -CREATURE:MUSKOX:MUSCLE -CREATURE:MUSKOX:EYE -CREATURE:MUSKOX:BRAIN -CREATURE:MUSKOX:LUNG -CREATURE:MUSKOX:HEART -CREATURE:MUSKOX:LIVER -CREATURE:MUSKOX:GUT -CREATURE:MUSKOX:STOMACH -CREATURE:MUSKOX:GIZZARD -CREATURE:MUSKOX:PANCREAS -CREATURE:MUSKOX:SPLEEN -CREATURE:MUSKOX:KIDNEY -CREATURE:MUSKOX_MAN:MUSCLE -CREATURE:MUSKOX_MAN:EYE -CREATURE:MUSKOX_MAN:BRAIN -CREATURE:MUSKOX_MAN:LUNG -CREATURE:MUSKOX_MAN:HEART -CREATURE:MUSKOX_MAN:LIVER -CREATURE:MUSKOX_MAN:GUT -CREATURE:MUSKOX_MAN:STOMACH -CREATURE:MUSKOX_MAN:GIZZARD -CREATURE:MUSKOX_MAN:PANCREAS -CREATURE:MUSKOX_MAN:SPLEEN -CREATURE:MUSKOX_MAN:KIDNEY -CREATURE:GIANT_MUSKOX:MUSCLE -CREATURE:GIANT_MUSKOX:EYE -CREATURE:GIANT_MUSKOX:BRAIN -CREATURE:GIANT_MUSKOX:LUNG -CREATURE:GIANT_MUSKOX:HEART -CREATURE:GIANT_MUSKOX:LIVER -CREATURE:GIANT_MUSKOX:GUT -CREATURE:GIANT_MUSKOX:STOMACH -CREATURE:GIANT_MUSKOX:GIZZARD -CREATURE:GIANT_MUSKOX:PANCREAS -CREATURE:GIANT_MUSKOX:SPLEEN -CREATURE:GIANT_MUSKOX:KIDNEY -CREATURE:ELK:MUSCLE -CREATURE:ELK:EYE -CREATURE:ELK:BRAIN -CREATURE:ELK:LUNG -CREATURE:ELK:HEART -CREATURE:ELK:LIVER -CREATURE:ELK:GUT -CREATURE:ELK:STOMACH -CREATURE:ELK:GIZZARD -CREATURE:ELK:PANCREAS -CREATURE:ELK:SPLEEN -CREATURE:ELK:KIDNEY -CREATURE:ELK_MAN:MUSCLE -CREATURE:ELK_MAN:EYE -CREATURE:ELK_MAN:BRAIN -CREATURE:ELK_MAN:LUNG -CREATURE:ELK_MAN:HEART -CREATURE:ELK_MAN:LIVER -CREATURE:ELK_MAN:GUT -CREATURE:ELK_MAN:STOMACH -CREATURE:ELK_MAN:GIZZARD -CREATURE:ELK_MAN:PANCREAS -CREATURE:ELK_MAN:SPLEEN -CREATURE:ELK_MAN:KIDNEY -CREATURE:GIANT_ELK:MUSCLE -CREATURE:GIANT_ELK:EYE -CREATURE:GIANT_ELK:BRAIN -CREATURE:GIANT_ELK:LUNG -CREATURE:GIANT_ELK:HEART -CREATURE:GIANT_ELK:LIVER -CREATURE:GIANT_ELK:GUT -CREATURE:GIANT_ELK:STOMACH -CREATURE:GIANT_ELK:GIZZARD -CREATURE:GIANT_ELK:PANCREAS -CREATURE:GIANT_ELK:SPLEEN -CREATURE:GIANT_ELK:KIDNEY -CREATURE:BEAR_POLAR:MUSCLE -CREATURE:BEAR_POLAR:EYE -CREATURE:BEAR_POLAR:BRAIN -CREATURE:BEAR_POLAR:LUNG -CREATURE:BEAR_POLAR:HEART -CREATURE:BEAR_POLAR:LIVER -CREATURE:BEAR_POLAR:GUT -CREATURE:BEAR_POLAR:STOMACH -CREATURE:BEAR_POLAR:GIZZARD -CREATURE:BEAR_POLAR:PANCREAS -CREATURE:BEAR_POLAR:SPLEEN -CREATURE:BEAR_POLAR:KIDNEY -CREATURE:BEAR_POLAR_MAN:MUSCLE -CREATURE:BEAR_POLAR_MAN:EYE -CREATURE:BEAR_POLAR_MAN:BRAIN -CREATURE:BEAR_POLAR_MAN:LUNG -CREATURE:BEAR_POLAR_MAN:HEART -CREATURE:BEAR_POLAR_MAN:LIVER -CREATURE:BEAR_POLAR_MAN:GUT -CREATURE:BEAR_POLAR_MAN:STOMACH -CREATURE:BEAR_POLAR_MAN:GIZZARD - CREATURE:BEAR_POLAR_MAN:PANCREAS -CREATURE:BEAR_POLAR_MAN:SPLEEN -CREATURE:BEAR_POLAR_MAN:KIDNEY - CREATURE:GIANT_BEAR_POLAR:MUSCLE -CREATURE:GIANT_BEAR_POLAR:EYE -CREATURE:GIANT_BEAR_POLAR:BRAIN -CREATURE:GIANT_BEAR_POLAR:LUNG -CREATURE:GIANT_BEAR_POLAR:HEART -CREATURE:GIANT_BEAR_POLAR:LIVER -CREATURE:GIANT_BEAR_POLAR:GUT -!CREATURE:GIANT_BEAR_POLAR:STOMACH -!CREATURE:GIANT_BEAR_POLAR:GIZZARD -"CREATURE:GIANT_BEAR_POLAR:PANCREAS - CREATURE:GIANT_BEAR_POLAR:SPLEEN - CREATURE:GIANT_BEAR_POLAR:KIDNEY -CREATURE:WOLVERINE:MUSCLE -CREATURE:WOLVERINE:EYE -CREATURE:WOLVERINE:BRAIN -CREATURE:WOLVERINE:LUNG -CREATURE:WOLVERINE:HEART -CREATURE:WOLVERINE:LIVER -CREATURE:WOLVERINE:GUT -CREATURE:WOLVERINE:STOMACH -CREATURE:WOLVERINE:GIZZARD -CREATURE:WOLVERINE:PANCREAS -CREATURE:WOLVERINE:SPLEEN -CREATURE:WOLVERINE:KIDNEY -CREATURE:WOLVERINE_MAN:MUSCLE -CREATURE:WOLVERINE_MAN:EYE -CREATURE:WOLVERINE_MAN:BRAIN -CREATURE:WOLVERINE_MAN:LUNG -CREATURE:WOLVERINE_MAN:HEART -CREATURE:WOLVERINE_MAN:LIVER -CREATURE:WOLVERINE_MAN:GUT -CREATURE:WOLVERINE_MAN:STOMACH -CREATURE:WOLVERINE_MAN:GIZZARD -CREATURE:WOLVERINE_MAN:PANCREAS -CREATURE:WOLVERINE_MAN:SPLEEN -CREATURE:WOLVERINE_MAN:KIDNEY -CREATURE:GIANT_WOLVERINE:MUSCLE -CREATURE:GIANT_WOLVERINE:EYE -CREATURE:GIANT_WOLVERINE:BRAIN -CREATURE:GIANT_WOLVERINE:LUNG -CREATURE:GIANT_WOLVERINE:HEART -CREATURE:GIANT_WOLVERINE:LIVER -CREATURE:GIANT_WOLVERINE:GUT - CREATURE:GIANT_WOLVERINE:STOMACH - CREATURE:GIANT_WOLVERINE:GIZZARD -!CREATURE:GIANT_WOLVERINE:PANCREAS -CREATURE:GIANT_WOLVERINE:SPLEEN -CREATURE:GIANT_WOLVERINE:KIDNEY -CREATURE:CHINCHILLA:MUSCLE -CREATURE:CHINCHILLA:EYE -CREATURE:CHINCHILLA:BRAIN -CREATURE:CHINCHILLA:LUNG -CREATURE:CHINCHILLA:HEART -CREATURE:CHINCHILLA:LIVER -CREATURE:CHINCHILLA:GUT -CREATURE:CHINCHILLA:STOMACH -CREATURE:CHINCHILLA:GIZZARD -CREATURE:CHINCHILLA:PANCREAS -CREATURE:CHINCHILLA:SPLEEN -CREATURE:CHINCHILLA:KIDNEY -CREATURE:CHINCHILLA_MAN:MUSCLE -CREATURE:CHINCHILLA_MAN:EYE -CREATURE:CHINCHILLA_MAN:BRAIN -CREATURE:CHINCHILLA_MAN:LUNG -CREATURE:CHINCHILLA_MAN:HEART -CREATURE:CHINCHILLA_MAN:LIVER -CREATURE:CHINCHILLA_MAN:GUT -CREATURE:CHINCHILLA_MAN:STOMACH -CREATURE:CHINCHILLA_MAN:GIZZARD - CREATURE:CHINCHILLA_MAN:PANCREAS -CREATURE:CHINCHILLA_MAN:SPLEEN -CREATURE:CHINCHILLA_MAN:KIDNEY - CREATURE:GIANT_CHINCHILLA:MUSCLE -CREATURE:GIANT_CHINCHILLA:EYE -CREATURE:GIANT_CHINCHILLA:BRAIN -CREATURE:GIANT_CHINCHILLA:LUNG -CREATURE:GIANT_CHINCHILLA:HEART -CREATURE:GIANT_CHINCHILLA:LIVER -CREATURE:GIANT_CHINCHILLA:GUT -!CREATURE:GIANT_CHINCHILLA:STOMACH -!CREATURE:GIANT_CHINCHILLA:GIZZARD -"CREATURE:GIANT_CHINCHILLA:PANCREAS - CREATURE:GIANT_CHINCHILLA:SPLEEN - CREATURE:GIANT_CHINCHILLA:KIDNEY -CREATURE:FLOATING_GUTS:MUSCLE -CREATURE:FLOATING_GUTS:EYE -CREATURE:FLOATING_GUTS:BRAIN -CREATURE:FLOATING_GUTS:LUNG -CREATURE:FLOATING_GUTS:HEART -CREATURE:FLOATING_GUTS:LIVER -CREATURE:FLOATING_GUTS:GUT -CREATURE:FLOATING_GUTS:STOMACH -CREATURE:FLOATING_GUTS:GIZZARD -CREATURE:FLOATING_GUTS:PANCREAS -CREATURE:FLOATING_GUTS:SPLEEN -CREATURE:FLOATING_GUTS:KIDNEY -CREATURE:DRUNIAN:MUSCLE -CREATURE:DRUNIAN:EYE -CREATURE:DRUNIAN:BRAIN -CREATURE:DRUNIAN:LUNG -CREATURE:DRUNIAN:HEART -CREATURE:DRUNIAN:LIVER -CREATURE:DRUNIAN:GUT -CREATURE:DRUNIAN:STOMACH -CREATURE:DRUNIAN:GIZZARD -CREATURE:DRUNIAN:PANCREAS -CREATURE:DRUNIAN:SPLEEN -CREATURE:DRUNIAN:KIDNEY -CREATURE:CREEPING_EYE:MUSCLE -CREATURE:CREEPING_EYE:EYE -CREATURE:CREEPING_EYE:BRAIN -CREATURE:CREEPING_EYE:LUNG -CREATURE:CREEPING_EYE:HEART -CREATURE:CREEPING_EYE:LIVER -CREATURE:CREEPING_EYE:GUT -CREATURE:CREEPING_EYE:STOMACH -CREATURE:CREEPING_EYE:GIZZARD -CREATURE:CREEPING_EYE:PANCREAS -CREATURE:CREEPING_EYE:SPLEEN -CREATURE:CREEPING_EYE:KIDNEY -&CREATURE:VORACIOUS_CAVE_CRAWLER:MUSCLE -#CREATURE:VORACIOUS_CAVE_CRAWLER:EYE -%CREATURE:VORACIOUS_CAVE_CRAWLER:BRAIN -$CREATURE:VORACIOUS_CAVE_CRAWLER:LUNG -%CREATURE:VORACIOUS_CAVE_CRAWLER:HEART -%CREATURE:VORACIOUS_CAVE_CRAWLER:LIVER -#CREATURE:VORACIOUS_CAVE_CRAWLER:GUT -'CREATURE:VORACIOUS_CAVE_CRAWLER:STOMACH -'CREATURE:VORACIOUS_CAVE_CRAWLER:GIZZARD -(CREATURE:VORACIOUS_CAVE_CRAWLER:PANCREAS -&CREATURE:VORACIOUS_CAVE_CRAWLER:SPLEEN -&CREATURE:VORACIOUS_CAVE_CRAWLER:KIDNEY -CREATURE:BLIND_CAVE_OGRE:MUSCLE -CREATURE:BLIND_CAVE_OGRE:EYE -CREATURE:BLIND_CAVE_OGRE:BRAIN -CREATURE:BLIND_CAVE_OGRE:LUNG -CREATURE:BLIND_CAVE_OGRE:HEART -CREATURE:BLIND_CAVE_OGRE:LIVER -CREATURE:BLIND_CAVE_OGRE:GUT - CREATURE:BLIND_CAVE_OGRE:STOMACH - CREATURE:BLIND_CAVE_OGRE:GIZZARD -!CREATURE:BLIND_CAVE_OGRE:PANCREAS -CREATURE:BLIND_CAVE_OGRE:SPLEEN -CREATURE:BLIND_CAVE_OGRE:KIDNEY -CREATURE:CAP_HOPPER:MUSCLE -CREATURE:CAP_HOPPER:EYE -CREATURE:CAP_HOPPER:BRAIN -CREATURE:CAP_HOPPER:LUNG -CREATURE:CAP_HOPPER:HEART -CREATURE:CAP_HOPPER:LIVER -CREATURE:CAP_HOPPER:GUT -CREATURE:CAP_HOPPER:STOMACH -CREATURE:CAP_HOPPER:GIZZARD -CREATURE:CAP_HOPPER:PANCREAS -CREATURE:CAP_HOPPER:SPLEEN -CREATURE:CAP_HOPPER:KIDNEY -CREATURE:MAGMA_CRAB:MUSCLE -CREATURE:MAGMA_CRAB:EYE -CREATURE:MAGMA_CRAB:BRAIN -CREATURE:MAGMA_CRAB:LUNG -CREATURE:MAGMA_CRAB:HEART -CREATURE:MAGMA_CRAB:LIVER -CREATURE:MAGMA_CRAB:GUT -CREATURE:MAGMA_CRAB:STOMACH -CREATURE:MAGMA_CRAB:GIZZARD -CREATURE:MAGMA_CRAB:PANCREAS -CREATURE:MAGMA_CRAB:SPLEEN -CREATURE:MAGMA_CRAB:KIDNEY -CREATURE:CRUNDLE:MUSCLE -CREATURE:CRUNDLE:EYE -CREATURE:CRUNDLE:BRAIN -CREATURE:CRUNDLE:LUNG -CREATURE:CRUNDLE:HEART -CREATURE:CRUNDLE:LIVER -CREATURE:CRUNDLE:GUT -CREATURE:CRUNDLE:STOMACH -CREATURE:CRUNDLE:GIZZARD -CREATURE:CRUNDLE:PANCREAS -CREATURE:CRUNDLE:SPLEEN -CREATURE:CRUNDLE:KIDNEY -CREATURE:HUNGRY_HEAD:MUSCLE -CREATURE:HUNGRY_HEAD:EYE -CREATURE:HUNGRY_HEAD:BRAIN -CREATURE:HUNGRY_HEAD:LUNG -CREATURE:HUNGRY_HEAD:HEART -CREATURE:HUNGRY_HEAD:LIVER -CREATURE:HUNGRY_HEAD:GUT -CREATURE:HUNGRY_HEAD:STOMACH -CREATURE:HUNGRY_HEAD:GIZZARD -CREATURE:HUNGRY_HEAD:PANCREAS -CREATURE:HUNGRY_HEAD:SPLEEN -CREATURE:HUNGRY_HEAD:KIDNEY -CREATURE:FLESH_BALL:MUSCLE -CREATURE:ELK_BIRD:MUSCLE -CREATURE:ELK_BIRD:EYE -CREATURE:ELK_BIRD:BRAIN -CREATURE:ELK_BIRD:LUNG -CREATURE:ELK_BIRD:HEART -CREATURE:ELK_BIRD:LIVER -CREATURE:ELK_BIRD:GUT -CREATURE:ELK_BIRD:STOMACH -CREATURE:ELK_BIRD:GIZZARD -CREATURE:ELK_BIRD:PANCREAS -CREATURE:ELK_BIRD:SPLEEN -CREATURE:ELK_BIRD:KIDNEY -CREATURE:HELMET_SNAKE:MUSCLE -CREATURE:HELMET_SNAKE:EYE -CREATURE:HELMET_SNAKE:BRAIN -CREATURE:HELMET_SNAKE:LUNG -CREATURE:HELMET_SNAKE:HEART -CREATURE:HELMET_SNAKE:LIVER -CREATURE:HELMET_SNAKE:GUT -CREATURE:HELMET_SNAKE:STOMACH -CREATURE:HELMET_SNAKE:GIZZARD -CREATURE:HELMET_SNAKE:PANCREAS -CREATURE:HELMET_SNAKE:SPLEEN -CREATURE:HELMET_SNAKE:KIDNEY -CREATURE:GREEN_DEVOURER:MUSCLE -CREATURE:GREEN_DEVOURER:EYE -CREATURE:GREEN_DEVOURER:BRAIN -CREATURE:GREEN_DEVOURER:LUNG -CREATURE:GREEN_DEVOURER:HEART -CREATURE:GREEN_DEVOURER:LIVER -CREATURE:GREEN_DEVOURER:GUT -CREATURE:GREEN_DEVOURER:STOMACH -CREATURE:GREEN_DEVOURER:GIZZARD - CREATURE:GREEN_DEVOURER:PANCREAS -CREATURE:GREEN_DEVOURER:SPLEEN -CREATURE:GREEN_DEVOURER:KIDNEY -CREATURE:RUTHERER:MUSCLE -CREATURE:RUTHERER:EYE -CREATURE:RUTHERER:BRAIN -CREATURE:RUTHERER:LUNG -CREATURE:RUTHERER:HEART -CREATURE:RUTHERER:LIVER -CREATURE:RUTHERER:GUT -CREATURE:RUTHERER:STOMACH -CREATURE:RUTHERER:GIZZARD -CREATURE:RUTHERER:PANCREAS -CREATURE:RUTHERER:SPLEEN -CREATURE:RUTHERER:KIDNEY -CREATURE:CREEPY_CRAWLER:MUSCLE -CREATURE:CREEPY_CRAWLER:EYE -CREATURE:CREEPY_CRAWLER:BRAIN -CREATURE:CREEPY_CRAWLER:LUNG -CREATURE:CREEPY_CRAWLER:HEART -CREATURE:CREEPY_CRAWLER:LIVER -CREATURE:CREEPY_CRAWLER:GUT -CREATURE:CREEPY_CRAWLER:STOMACH -CREATURE:CREEPY_CRAWLER:GIZZARD - CREATURE:CREEPY_CRAWLER:PANCREAS -CREATURE:CREEPY_CRAWLER:SPLEEN -CREATURE:CREEPY_CRAWLER:KIDNEY -CREATURE:DRALTHA:MUSCLE -CREATURE:DRALTHA:EYE -CREATURE:DRALTHA:BRAIN -CREATURE:DRALTHA:LUNG -CREATURE:DRALTHA:HEART -CREATURE:DRALTHA:LIVER -CREATURE:DRALTHA:GUT -CREATURE:DRALTHA:STOMACH -CREATURE:DRALTHA:GIZZARD -CREATURE:DRALTHA:PANCREAS -CREATURE:DRALTHA:SPLEEN -CREATURE:DRALTHA:KIDNEY -CREATURE:GIANT_EARTHWORM:MUSCLE -CREATURE:GIANT_EARTHWORM:EYE -CREATURE:GIANT_EARTHWORM:BRAIN -CREATURE:GIANT_EARTHWORM:LUNG -CREATURE:GIANT_EARTHWORM:HEART -CREATURE:GIANT_EARTHWORM:LIVER -CREATURE:GIANT_EARTHWORM:GUT - CREATURE:GIANT_EARTHWORM:STOMACH - CREATURE:GIANT_EARTHWORM:GIZZARD -!CREATURE:GIANT_EARTHWORM:PANCREAS -CREATURE:GIANT_EARTHWORM:SPLEEN -CREATURE:GIANT_EARTHWORM:KIDNEY -CREATURE:BUGBAT:MUSCLE -CREATURE:BUGBAT:EYE -CREATURE:BUGBAT:BRAIN -CREATURE:BUGBAT:LUNG -CREATURE:BUGBAT:HEART -CREATURE:BUGBAT:LIVER -CREATURE:BUGBAT:GUT -CREATURE:BUGBAT:STOMACH -CREATURE:BUGBAT:GIZZARD -CREATURE:BUGBAT:PANCREAS -CREATURE:BUGBAT:SPLEEN -CREATURE:BUGBAT:KIDNEY -CREATURE:MANERA:MUSCLE -CREATURE:MANERA:EYE -CREATURE:MANERA:BRAIN -CREATURE:MANERA:LUNG -CREATURE:MANERA:HEART -CREATURE:MANERA:LIVER -CREATURE:MANERA:GUT -CREATURE:MANERA:STOMACH -CREATURE:MANERA:GIZZARD -CREATURE:MANERA:PANCREAS -CREATURE:MANERA:SPLEEN -CREATURE:MANERA:KIDNEY -CREATURE:MOLEMARIAN:MUSCLE -CREATURE:MOLEMARIAN:EYE -CREATURE:MOLEMARIAN:BRAIN -CREATURE:MOLEMARIAN:LUNG -CREATURE:MOLEMARIAN:HEART -CREATURE:MOLEMARIAN:LIVER -CREATURE:MOLEMARIAN:GUT -CREATURE:MOLEMARIAN:STOMACH -CREATURE:MOLEMARIAN:GIZZARD -CREATURE:MOLEMARIAN:PANCREAS -CREATURE:MOLEMARIAN:SPLEEN -CREATURE:MOLEMARIAN:KIDNEY -CREATURE:JABBERER:MUSCLE -CREATURE:JABBERER:EYE -CREATURE:JABBERER:BRAIN -CREATURE:JABBERER:LUNG -CREATURE:JABBERER:HEART -CREATURE:JABBERER:LIVER -CREATURE:JABBERER:GUT -CREATURE:JABBERER:STOMACH -CREATURE:JABBERER:GIZZARD -CREATURE:JABBERER:PANCREAS -CREATURE:JABBERER:SPLEEN -CREATURE:JABBERER:KIDNEY -CREATURE:POND_GRABBER:MUSCLE -CREATURE:POND_GRABBER:EYE -CREATURE:POND_GRABBER:BRAIN -CREATURE:POND_GRABBER:LUNG -CREATURE:POND_GRABBER:HEART -CREATURE:POND_GRABBER:LIVER -CREATURE:POND_GRABBER:GUT -CREATURE:POND_GRABBER:STOMACH -CREATURE:POND_GRABBER:GIZZARD -CREATURE:POND_GRABBER:PANCREAS -CREATURE:POND_GRABBER:SPLEEN -CREATURE:POND_GRABBER:KIDNEY -CREATURE:BLIND_CAVE_BEAR:MUSCLE -CREATURE:BLIND_CAVE_BEAR:EYE -CREATURE:BLIND_CAVE_BEAR:BRAIN -CREATURE:BLIND_CAVE_BEAR:LUNG -CREATURE:BLIND_CAVE_BEAR:HEART -CREATURE:BLIND_CAVE_BEAR:LIVER -CREATURE:BLIND_CAVE_BEAR:GUT - CREATURE:BLIND_CAVE_BEAR:STOMACH - CREATURE:BLIND_CAVE_BEAR:GIZZARD -!CREATURE:BLIND_CAVE_BEAR:PANCREAS -CREATURE:BLIND_CAVE_BEAR:SPLEEN -CREATURE:BLIND_CAVE_BEAR:KIDNEY -CREATURE:CAVE_DRAGON:MUSCLE -CREATURE:CAVE_DRAGON:EYE -CREATURE:CAVE_DRAGON:BRAIN -CREATURE:CAVE_DRAGON:LUNG -CREATURE:CAVE_DRAGON:HEART -CREATURE:CAVE_DRAGON:LIVER -CREATURE:CAVE_DRAGON:GUT -CREATURE:CAVE_DRAGON:STOMACH -CREATURE:CAVE_DRAGON:GIZZARD -CREATURE:CAVE_DRAGON:PANCREAS -CREATURE:CAVE_DRAGON:SPLEEN -CREATURE:CAVE_DRAGON:KIDNEY -CREATURE:REACHER:MUSCLE -CREATURE:REACHER:EYE -CREATURE:REACHER:BRAIN -CREATURE:REACHER:LUNG -CREATURE:REACHER:HEART -CREATURE:REACHER:LIVER -CREATURE:REACHER:GUT -CREATURE:REACHER:STOMACH -CREATURE:REACHER:GIZZARD -CREATURE:REACHER:PANCREAS -CREATURE:REACHER:SPLEEN -CREATURE:REACHER:KIDNEY -CREATURE:GORLAK:MUSCLE -CREATURE:GORLAK:EYE -CREATURE:GORLAK:BRAIN -CREATURE:GORLAK:LUNG -CREATURE:GORLAK:HEART -CREATURE:GORLAK:LIVER -CREATURE:GORLAK:GUT -CREATURE:GORLAK:STOMACH -CREATURE:GORLAK:GIZZARD -CREATURE:GORLAK:PANCREAS -CREATURE:GORLAK:SPLEEN -CREATURE:GORLAK:KIDNEY -CREATURE:OCTOPUS:MUSCLE -CREATURE:OCTOPUS:EYE -CREATURE:OCTOPUS:BRAIN -CREATURE:OCTOPUS:LUNG -CREATURE:OCTOPUS:HEART -CREATURE:OCTOPUS:LIVER -CREATURE:OCTOPUS:GUT -CREATURE:OCTOPUS:STOMACH -CREATURE:OCTOPUS:GIZZARD -CREATURE:OCTOPUS:PANCREAS -CREATURE:OCTOPUS:SPLEEN -CREATURE:OCTOPUS:KIDNEY -CREATURE:OCTOPUS_MAN:MUSCLE -CREATURE:OCTOPUS_MAN:EYE -CREATURE:OCTOPUS_MAN:BRAIN -CREATURE:OCTOPUS_MAN:LUNG -CREATURE:OCTOPUS_MAN:HEART -CREATURE:OCTOPUS_MAN:LIVER -CREATURE:OCTOPUS_MAN:GUT -CREATURE:OCTOPUS_MAN:STOMACH -CREATURE:OCTOPUS_MAN:GIZZARD -CREATURE:OCTOPUS_MAN:PANCREAS -CREATURE:OCTOPUS_MAN:SPLEEN -CREATURE:OCTOPUS_MAN:KIDNEY -CREATURE:GIANT_OCTOPUS:MUSCLE -CREATURE:GIANT_OCTOPUS:EYE -CREATURE:GIANT_OCTOPUS:BRAIN -CREATURE:GIANT_OCTOPUS:LUNG -CREATURE:GIANT_OCTOPUS:HEART -CREATURE:GIANT_OCTOPUS:LIVER -CREATURE:GIANT_OCTOPUS:GUT -CREATURE:GIANT_OCTOPUS:STOMACH -CREATURE:GIANT_OCTOPUS:GIZZARD -CREATURE:GIANT_OCTOPUS:PANCREAS -CREATURE:GIANT_OCTOPUS:SPLEEN -CREATURE:GIANT_OCTOPUS:KIDNEY -CREATURE:CRAB:MUSCLE -CREATURE:CRAB:EYE -CREATURE:CRAB:BRAIN -CREATURE:CRAB:LUNG -CREATURE:CRAB:HEART -CREATURE:CRAB:LIVER -CREATURE:CRAB:GUT -CREATURE:CRAB:STOMACH -CREATURE:CRAB:GIZZARD -CREATURE:CRAB:PANCREAS -CREATURE:CRAB:SPLEEN -CREATURE:CRAB:KIDNEY -CREATURE:CRAB_MAN:MUSCLE -CREATURE:CRAB_MAN:EYE -CREATURE:CRAB_MAN:BRAIN -CREATURE:CRAB_MAN:LUNG -CREATURE:CRAB_MAN:HEART -CREATURE:CRAB_MAN:LIVER -CREATURE:CRAB_MAN:GUT -CREATURE:CRAB_MAN:STOMACH -CREATURE:CRAB_MAN:GIZZARD -CREATURE:CRAB_MAN:PANCREAS -CREATURE:CRAB_MAN:SPLEEN -CREATURE:CRAB_MAN:KIDNEY -CREATURE:GIANT_CRAB:MUSCLE -CREATURE:GIANT_CRAB:EYE -CREATURE:GIANT_CRAB:BRAIN -CREATURE:GIANT_CRAB:LUNG -CREATURE:GIANT_CRAB:HEART -CREATURE:GIANT_CRAB:LIVER -CREATURE:GIANT_CRAB:GUT -CREATURE:GIANT_CRAB:STOMACH -CREATURE:GIANT_CRAB:GIZZARD -CREATURE:GIANT_CRAB:PANCREAS -CREATURE:GIANT_CRAB:SPLEEN -CREATURE:GIANT_CRAB:KIDNEY -CREATURE:LEOPARD_SEAL:MUSCLE -CREATURE:LEOPARD_SEAL:EYE -CREATURE:LEOPARD_SEAL:BRAIN -CREATURE:LEOPARD_SEAL:LUNG -CREATURE:LEOPARD_SEAL:HEART -CREATURE:LEOPARD_SEAL:LIVER -CREATURE:LEOPARD_SEAL:GUT -CREATURE:LEOPARD_SEAL:STOMACH -CREATURE:LEOPARD_SEAL:GIZZARD -CREATURE:LEOPARD_SEAL:PANCREAS -CREATURE:LEOPARD_SEAL:SPLEEN -CREATURE:LEOPARD_SEAL:KIDNEY - CREATURE:LEOPARD_SEAL_MAN:MUSCLE -CREATURE:LEOPARD_SEAL_MAN:EYE -CREATURE:LEOPARD_SEAL_MAN:BRAIN -CREATURE:LEOPARD_SEAL_MAN:LUNG -CREATURE:LEOPARD_SEAL_MAN:HEART -CREATURE:LEOPARD_SEAL_MAN:LIVER -CREATURE:LEOPARD_SEAL_MAN:GUT -!CREATURE:LEOPARD_SEAL_MAN:STOMACH -!CREATURE:LEOPARD_SEAL_MAN:GIZZARD -"CREATURE:LEOPARD_SEAL_MAN:PANCREAS - CREATURE:LEOPARD_SEAL_MAN:SPLEEN - CREATURE:LEOPARD_SEAL_MAN:KIDNEY -"CREATURE:GIANT_LEOPARD_SEAL:MUSCLE -CREATURE:GIANT_LEOPARD_SEAL:EYE -!CREATURE:GIANT_LEOPARD_SEAL:BRAIN - CREATURE:GIANT_LEOPARD_SEAL:LUNG -!CREATURE:GIANT_LEOPARD_SEAL:HEART -!CREATURE:GIANT_LEOPARD_SEAL:LIVER -CREATURE:GIANT_LEOPARD_SEAL:GUT -#CREATURE:GIANT_LEOPARD_SEAL:STOMACH -#CREATURE:GIANT_LEOPARD_SEAL:GIZZARD -$CREATURE:GIANT_LEOPARD_SEAL:PANCREAS -"CREATURE:GIANT_LEOPARD_SEAL:SPLEEN -"CREATURE:GIANT_LEOPARD_SEAL:KIDNEY -CREATURE:CUTTLEFISH:MUSCLE -CREATURE:CUTTLEFISH:EYE -CREATURE:CUTTLEFISH:BRAIN -CREATURE:CUTTLEFISH:LUNG -CREATURE:CUTTLEFISH:HEART -CREATURE:CUTTLEFISH:LIVER -CREATURE:CUTTLEFISH:GUT -CREATURE:CUTTLEFISH:STOMACH -CREATURE:CUTTLEFISH:GIZZARD -CREATURE:CUTTLEFISH:PANCREAS -CREATURE:CUTTLEFISH:SPLEEN -CREATURE:CUTTLEFISH:KIDNEY -CREATURE:CUTTLEFISH_MAN:MUSCLE -CREATURE:CUTTLEFISH_MAN:EYE -CREATURE:CUTTLEFISH_MAN:BRAIN -CREATURE:CUTTLEFISH_MAN:LUNG -CREATURE:CUTTLEFISH_MAN:HEART -CREATURE:CUTTLEFISH_MAN:LIVER -CREATURE:CUTTLEFISH_MAN:GUT -CREATURE:CUTTLEFISH_MAN:STOMACH -CREATURE:CUTTLEFISH_MAN:GIZZARD - CREATURE:CUTTLEFISH_MAN:PANCREAS -CREATURE:CUTTLEFISH_MAN:SPLEEN -CREATURE:CUTTLEFISH_MAN:KIDNEY - CREATURE:GIANT_CUTTLEFISH:MUSCLE -CREATURE:GIANT_CUTTLEFISH:EYE -CREATURE:GIANT_CUTTLEFISH:BRAIN -CREATURE:GIANT_CUTTLEFISH:LUNG -CREATURE:GIANT_CUTTLEFISH:HEART -CREATURE:GIANT_CUTTLEFISH:LIVER -CREATURE:GIANT_CUTTLEFISH:GUT -!CREATURE:GIANT_CUTTLEFISH:STOMACH -!CREATURE:GIANT_CUTTLEFISH:GIZZARD -"CREATURE:GIANT_CUTTLEFISH:PANCREAS - CREATURE:GIANT_CUTTLEFISH:SPLEEN - CREATURE:GIANT_CUTTLEFISH:KIDNEY -CREATURE:ORCA:MUSCLE -CREATURE:ORCA:EYE -CREATURE:ORCA:BRAIN -CREATURE:ORCA:LUNG -CREATURE:ORCA:HEART -CREATURE:ORCA:LIVER -CREATURE:ORCA:GUT -CREATURE:ORCA:STOMACH -CREATURE:ORCA:GIZZARD -CREATURE:ORCA:PANCREAS -CREATURE:ORCA:SPLEEN -CREATURE:ORCA:KIDNEY -CREATURE:ORCA_MAN:MUSCLE -CREATURE:ORCA_MAN:EYE -CREATURE:ORCA_MAN:BRAIN -CREATURE:ORCA_MAN:LUNG -CREATURE:ORCA_MAN:HEART -CREATURE:ORCA_MAN:LIVER -CREATURE:ORCA_MAN:GUT -CREATURE:ORCA_MAN:STOMACH -CREATURE:ORCA_MAN:GIZZARD -CREATURE:ORCA_MAN:PANCREAS -CREATURE:ORCA_MAN:SPLEEN -CREATURE:ORCA_MAN:KIDNEY -CREATURE:GIANT_ORCA:MUSCLE -CREATURE:GIANT_ORCA:EYE -CREATURE:GIANT_ORCA:BRAIN -CREATURE:GIANT_ORCA:LUNG -CREATURE:GIANT_ORCA:HEART -CREATURE:GIANT_ORCA:LIVER -CREATURE:GIANT_ORCA:GUT -CREATURE:GIANT_ORCA:STOMACH -CREATURE:GIANT_ORCA:GIZZARD -CREATURE:GIANT_ORCA:PANCREAS -CREATURE:GIANT_ORCA:SPLEEN -CREATURE:GIANT_ORCA:KIDNEY -CREATURE:HORSESHOE_CRAB:MUSCLE -CREATURE:HORSESHOE_CRAB:EYE -CREATURE:HORSESHOE_CRAB:BRAIN -CREATURE:HORSESHOE_CRAB:LUNG -CREATURE:HORSESHOE_CRAB:HEART -CREATURE:HORSESHOE_CRAB:LIVER -CREATURE:HORSESHOE_CRAB:GUT -CREATURE:HORSESHOE_CRAB:STOMACH -CREATURE:HORSESHOE_CRAB:GIZZARD - CREATURE:HORSESHOE_CRAB:PANCREAS -CREATURE:HORSESHOE_CRAB:SPLEEN -CREATURE:HORSESHOE_CRAB:KIDNEY -"CREATURE:HORSESHOE_CRAB_MAN:MUSCLE -CREATURE:HORSESHOE_CRAB_MAN:EYE -!CREATURE:HORSESHOE_CRAB_MAN:BRAIN - CREATURE:HORSESHOE_CRAB_MAN:LUNG -!CREATURE:HORSESHOE_CRAB_MAN:HEART -!CREATURE:HORSESHOE_CRAB_MAN:LIVER -CREATURE:HORSESHOE_CRAB_MAN:GUT -#CREATURE:HORSESHOE_CRAB_MAN:STOMACH -#CREATURE:HORSESHOE_CRAB_MAN:GIZZARD -$CREATURE:HORSESHOE_CRAB_MAN:PANCREAS -"CREATURE:HORSESHOE_CRAB_MAN:SPLEEN -"CREATURE:HORSESHOE_CRAB_MAN:KIDNEY -$CREATURE:GIANT_HORSESHOE_CRAB:MUSCLE -!CREATURE:GIANT_HORSESHOE_CRAB:EYE -#CREATURE:GIANT_HORSESHOE_CRAB:BRAIN -"CREATURE:GIANT_HORSESHOE_CRAB:LUNG -#CREATURE:GIANT_HORSESHOE_CRAB:HEART -#CREATURE:GIANT_HORSESHOE_CRAB:LIVER -!CREATURE:GIANT_HORSESHOE_CRAB:GUT -%CREATURE:GIANT_HORSESHOE_CRAB:STOMACH -%CREATURE:GIANT_HORSESHOE_CRAB:GIZZARD -&CREATURE:GIANT_HORSESHOE_CRAB:PANCREAS -$CREATURE:GIANT_HORSESHOE_CRAB:SPLEEN -$CREATURE:GIANT_HORSESHOE_CRAB:KIDNEY -CREATURE:SPERM_WHALE:MUSCLE -CREATURE:SPERM_WHALE:EYE -CREATURE:SPERM_WHALE:BRAIN -CREATURE:SPERM_WHALE:LUNG -CREATURE:SPERM_WHALE:HEART -CREATURE:SPERM_WHALE:LIVER -CREATURE:SPERM_WHALE:GUT -CREATURE:SPERM_WHALE:STOMACH -CREATURE:SPERM_WHALE:GIZZARD -CREATURE:SPERM_WHALE:PANCREAS -CREATURE:SPERM_WHALE:SPLEEN -CREATURE:SPERM_WHALE:KIDNEY -CREATURE:SPERM_WHALE_MAN:MUSCLE -CREATURE:SPERM_WHALE_MAN:EYE -CREATURE:SPERM_WHALE_MAN:BRAIN -CREATURE:SPERM_WHALE_MAN:LUNG -CREATURE:SPERM_WHALE_MAN:HEART -CREATURE:SPERM_WHALE_MAN:LIVER -CREATURE:SPERM_WHALE_MAN:GUT - CREATURE:SPERM_WHALE_MAN:STOMACH - CREATURE:SPERM_WHALE_MAN:GIZZARD -!CREATURE:SPERM_WHALE_MAN:PANCREAS -CREATURE:SPERM_WHALE_MAN:SPLEEN -CREATURE:SPERM_WHALE_MAN:KIDNEY -!CREATURE:GIANT_SPERM_WHALE:MUSCLE -CREATURE:GIANT_SPERM_WHALE:EYE - CREATURE:GIANT_SPERM_WHALE:BRAIN -CREATURE:GIANT_SPERM_WHALE:LUNG - CREATURE:GIANT_SPERM_WHALE:HEART - CREATURE:GIANT_SPERM_WHALE:LIVER -CREATURE:GIANT_SPERM_WHALE:GUT -"CREATURE:GIANT_SPERM_WHALE:STOMACH -"CREATURE:GIANT_SPERM_WHALE:GIZZARD -#CREATURE:GIANT_SPERM_WHALE:PANCREAS -!CREATURE:GIANT_SPERM_WHALE:SPLEEN -!CREATURE:GIANT_SPERM_WHALE:KIDNEY -CREATURE:ELEPHANT_SEAL:MUSCLE -CREATURE:ELEPHANT_SEAL:EYE -CREATURE:ELEPHANT_SEAL:BRAIN -CREATURE:ELEPHANT_SEAL:LUNG -CREATURE:ELEPHANT_SEAL:HEART -CREATURE:ELEPHANT_SEAL:LIVER -CREATURE:ELEPHANT_SEAL:GUT -CREATURE:ELEPHANT_SEAL:STOMACH -CREATURE:ELEPHANT_SEAL:GIZZARD -CREATURE:ELEPHANT_SEAL:PANCREAS -CREATURE:ELEPHANT_SEAL:SPLEEN -CREATURE:ELEPHANT_SEAL:KIDNEY -!CREATURE:ELEPHANT_SEAL_MAN:MUSCLE -CREATURE:ELEPHANT_SEAL_MAN:EYE - CREATURE:ELEPHANT_SEAL_MAN:BRAIN -CREATURE:ELEPHANT_SEAL_MAN:LUNG - CREATURE:ELEPHANT_SEAL_MAN:HEART - CREATURE:ELEPHANT_SEAL_MAN:LIVER -CREATURE:ELEPHANT_SEAL_MAN:GUT -"CREATURE:ELEPHANT_SEAL_MAN:STOMACH -"CREATURE:ELEPHANT_SEAL_MAN:GIZZARD -#CREATURE:ELEPHANT_SEAL_MAN:PANCREAS -!CREATURE:ELEPHANT_SEAL_MAN:SPLEEN -!CREATURE:ELEPHANT_SEAL_MAN:KIDNEY -#CREATURE:GIANT_ELEPHANT_SEAL:MUSCLE - CREATURE:GIANT_ELEPHANT_SEAL:EYE -"CREATURE:GIANT_ELEPHANT_SEAL:BRAIN -!CREATURE:GIANT_ELEPHANT_SEAL:LUNG -"CREATURE:GIANT_ELEPHANT_SEAL:HEART -"CREATURE:GIANT_ELEPHANT_SEAL:LIVER - CREATURE:GIANT_ELEPHANT_SEAL:GUT -$CREATURE:GIANT_ELEPHANT_SEAL:STOMACH -$CREATURE:GIANT_ELEPHANT_SEAL:GIZZARD -%CREATURE:GIANT_ELEPHANT_SEAL:PANCREAS -#CREATURE:GIANT_ELEPHANT_SEAL:SPLEEN -#CREATURE:GIANT_ELEPHANT_SEAL:KIDNEY -CREATURE:HARP_SEAL:MUSCLE -CREATURE:HARP_SEAL:EYE -CREATURE:HARP_SEAL:BRAIN -CREATURE:HARP_SEAL:LUNG -CREATURE:HARP_SEAL:HEART -CREATURE:HARP_SEAL:LIVER -CREATURE:HARP_SEAL:GUT -CREATURE:HARP_SEAL:STOMACH -CREATURE:HARP_SEAL:GIZZARD -CREATURE:HARP_SEAL:PANCREAS -CREATURE:HARP_SEAL:SPLEEN -CREATURE:HARP_SEAL:KIDNEY -CREATURE:HARP_SEAL_MAN:MUSCLE -CREATURE:HARP_SEAL_MAN:EYE -CREATURE:HARP_SEAL_MAN:BRAIN -CREATURE:HARP_SEAL_MAN:LUNG -CREATURE:HARP_SEAL_MAN:HEART -CREATURE:HARP_SEAL_MAN:LIVER -CREATURE:HARP_SEAL_MAN:GUT -CREATURE:HARP_SEAL_MAN:STOMACH -CREATURE:HARP_SEAL_MAN:GIZZARD -CREATURE:HARP_SEAL_MAN:PANCREAS -CREATURE:HARP_SEAL_MAN:SPLEEN -CREATURE:HARP_SEAL_MAN:KIDNEY -CREATURE:GIANT_HARP_SEAL:MUSCLE -CREATURE:GIANT_HARP_SEAL:EYE -CREATURE:GIANT_HARP_SEAL:BRAIN -CREATURE:GIANT_HARP_SEAL:LUNG -CREATURE:GIANT_HARP_SEAL:HEART -CREATURE:GIANT_HARP_SEAL:LIVER -CREATURE:GIANT_HARP_SEAL:GUT - CREATURE:GIANT_HARP_SEAL:STOMACH - CREATURE:GIANT_HARP_SEAL:GIZZARD -!CREATURE:GIANT_HARP_SEAL:PANCREAS -CREATURE:GIANT_HARP_SEAL:SPLEEN -CREATURE:GIANT_HARP_SEAL:KIDNEY -CREATURE:NAUTILUS:MUSCLE -CREATURE:NAUTILUS:EYE -CREATURE:NAUTILUS:BRAIN -CREATURE:NAUTILUS:LUNG -CREATURE:NAUTILUS:HEART -CREATURE:NAUTILUS:LIVER -CREATURE:NAUTILUS:GUT -CREATURE:NAUTILUS:STOMACH -CREATURE:NAUTILUS:GIZZARD -CREATURE:NAUTILUS:PANCREAS -CREATURE:NAUTILUS:SPLEEN -CREATURE:NAUTILUS:KIDNEY -CREATURE:NAUTILUS_MAN:MUSCLE -CREATURE:NAUTILUS_MAN:EYE -CREATURE:NAUTILUS_MAN:BRAIN -CREATURE:NAUTILUS_MAN:LUNG -CREATURE:NAUTILUS_MAN:HEART -CREATURE:NAUTILUS_MAN:LIVER -CREATURE:NAUTILUS_MAN:GUT -CREATURE:NAUTILUS_MAN:STOMACH -CREATURE:NAUTILUS_MAN:GIZZARD -CREATURE:NAUTILUS_MAN:PANCREAS -CREATURE:NAUTILUS_MAN:SPLEEN -CREATURE:NAUTILUS_MAN:KIDNEY -CREATURE:GIANT_NAUTILUS:MUSCLE -CREATURE:GIANT_NAUTILUS:EYE -CREATURE:GIANT_NAUTILUS:BRAIN -CREATURE:GIANT_NAUTILUS:LUNG -CREATURE:GIANT_NAUTILUS:HEART -CREATURE:GIANT_NAUTILUS:LIVER -CREATURE:GIANT_NAUTILUS:GUT -CREATURE:GIANT_NAUTILUS:STOMACH -CREATURE:GIANT_NAUTILUS:GIZZARD - CREATURE:GIANT_NAUTILUS:PANCREAS -CREATURE:GIANT_NAUTILUS:SPLEEN -CREATURE:GIANT_NAUTILUS:KIDNEY -CREATURE:FOXSQUIRREL:MUSCLE -CREATURE:FOXSQUIRREL:EYE -CREATURE:FOXSQUIRREL:BRAIN -CREATURE:FOXSQUIRREL:LUNG -CREATURE:FOXSQUIRREL:HEART -CREATURE:FOXSQUIRREL:LIVER -CREATURE:FOXSQUIRREL:GUT -CREATURE:FOXSQUIRREL:STOMACH -CREATURE:FOXSQUIRREL:GIZZARD -CREATURE:FOXSQUIRREL:PANCREAS -CREATURE:FOXSQUIRREL:SPLEEN -CREATURE:FOXSQUIRREL:KIDNEY -CREATURE:MOGHOPPER:MUSCLE -CREATURE:MOGHOPPER:EYE -CREATURE:MOGHOPPER:BRAIN -CREATURE:MOGHOPPER:LUNG -CREATURE:MOGHOPPER:HEART -CREATURE:MOGHOPPER:LIVER -CREATURE:MOGHOPPER:GUT -CREATURE:MOGHOPPER:STOMACH -CREATURE:MOGHOPPER:GIZZARD -CREATURE:MOGHOPPER:PANCREAS -CREATURE:MOGHOPPER:SPLEEN -CREATURE:MOGHOPPER:KIDNEY -CREATURE:RAT_DEMON:MUSCLE -CREATURE:RAT_DEMON:EYE -CREATURE:RAT_DEMON:BRAIN -CREATURE:RAT_DEMON:LUNG -CREATURE:RAT_DEMON:HEART -CREATURE:RAT_DEMON:LIVER -CREATURE:RAT_DEMON:GUT -CREATURE:RAT_DEMON:STOMACH -CREATURE:RAT_DEMON:GIZZARD -CREATURE:RAT_DEMON:PANCREAS -CREATURE:RAT_DEMON:SPLEEN -CREATURE:RAT_DEMON:KIDNEY -CREATURE:WAMBLER_FLUFFY:EYE -'CREATURE:LIZARD_RHINO_TWO_LEGGED:MUSCLE -$CREATURE:LIZARD_RHINO_TWO_LEGGED:EYE -&CREATURE:LIZARD_RHINO_TWO_LEGGED:BRAIN -%CREATURE:LIZARD_RHINO_TWO_LEGGED:LUNG -&CREATURE:LIZARD_RHINO_TWO_LEGGED:HEART -&CREATURE:LIZARD_RHINO_TWO_LEGGED:LIVER -$CREATURE:LIZARD_RHINO_TWO_LEGGED:GUT -(CREATURE:LIZARD_RHINO_TWO_LEGGED:STOMACH -(CREATURE:LIZARD_RHINO_TWO_LEGGED:GIZZARD -)CREATURE:LIZARD_RHINO_TWO_LEGGED:PANCREAS -'CREATURE:LIZARD_RHINO_TWO_LEGGED:SPLEEN -'CREATURE:LIZARD_RHINO_TWO_LEGGED:KIDNEY -CREATURE:WORM_KNUCKLE:MUSCLE -CREATURE:WORM_KNUCKLE:EYE -CREATURE:WORM_KNUCKLE:BRAIN -CREATURE:WORM_KNUCKLE:LUNG -CREATURE:WORM_KNUCKLE:HEART -CREATURE:WORM_KNUCKLE:LIVER -CREATURE:WORM_KNUCKLE:GUT -CREATURE:WORM_KNUCKLE:STOMACH -CREATURE:WORM_KNUCKLE:GIZZARD -CREATURE:WORM_KNUCKLE:PANCREAS -CREATURE:WORM_KNUCKLE:SPLEEN -CREATURE:WORM_KNUCKLE:KIDNEY -CREATURE:SPIDER_PHANTOM:MUSCLE -CREATURE:SPIDER_PHANTOM:EYE -CREATURE:SPIDER_PHANTOM:BRAIN -CREATURE:SPIDER_PHANTOM:LUNG -CREATURE:SPIDER_PHANTOM:HEART -CREATURE:SPIDER_PHANTOM:LIVER -CREATURE:SPIDER_PHANTOM:GUT -CREATURE:SPIDER_PHANTOM:STOMACH -CREATURE:SPIDER_PHANTOM:GIZZARD - CREATURE:SPIDER_PHANTOM:PANCREAS -CREATURE:SPIDER_PHANTOM:SPLEEN -CREATURE:SPIDER_PHANTOM:KIDNEY -CREATURE:FLY_ACORN:MUSCLE -CREATURE:FLY_ACORN:EYE -CREATURE:FLY_ACORN:BRAIN -CREATURE:FLY_ACORN:LUNG -CREATURE:FLY_ACORN:HEART -CREATURE:FLY_ACORN:LIVER -CREATURE:FLY_ACORN:GUT -CREATURE:FLY_ACORN:STOMACH -CREATURE:FLY_ACORN:GIZZARD -CREATURE:FLY_ACORN:PANCREAS -CREATURE:FLY_ACORN:SPLEEN -CREATURE:FLY_ACORN:KIDNEY -CREATURE:GNAT_BLOOD:MUSCLE -CREATURE:GNAT_BLOOD:EYE -CREATURE:GNAT_BLOOD:BRAIN -CREATURE:GNAT_BLOOD:LUNG -CREATURE:GNAT_BLOOD:HEART -CREATURE:GNAT_BLOOD:LIVER -CREATURE:GNAT_BLOOD:GUT -CREATURE:GNAT_BLOOD:STOMACH -CREATURE:GNAT_BLOOD:GIZZARD -CREATURE:GNAT_BLOOD:PANCREAS -CREATURE:GNAT_BLOOD:SPLEEN -CREATURE:GNAT_BLOOD:KIDNEY -CREATURE:LIZARD:MUSCLE -CREATURE:LIZARD:EYE -CREATURE:LIZARD:BRAIN -CREATURE:LIZARD:LUNG -CREATURE:LIZARD:HEART -CREATURE:LIZARD:LIVER -CREATURE:LIZARD:GUT -CREATURE:LIZARD:STOMACH -CREATURE:LIZARD:GIZZARD -CREATURE:LIZARD:PANCREAS -CREATURE:LIZARD:SPLEEN -CREATURE:LIZARD:KIDNEY -CREATURE:LIZARD_MAN:MUSCLE -CREATURE:LIZARD_MAN:EYE -CREATURE:LIZARD_MAN:BRAIN -CREATURE:LIZARD_MAN:LUNG -CREATURE:LIZARD_MAN:HEART -CREATURE:LIZARD_MAN:LIVER -CREATURE:LIZARD_MAN:GUT -CREATURE:LIZARD_MAN:STOMACH -CREATURE:LIZARD_MAN:GIZZARD -CREATURE:LIZARD_MAN:PANCREAS -CREATURE:LIZARD_MAN:SPLEEN -CREATURE:LIZARD_MAN:KIDNEY -CREATURE:GIANT_LIZARD:MUSCLE -CREATURE:GIANT_LIZARD:EYE -CREATURE:GIANT_LIZARD:BRAIN -CREATURE:GIANT_LIZARD:LUNG -CREATURE:GIANT_LIZARD:HEART -CREATURE:GIANT_LIZARD:LIVER -CREATURE:GIANT_LIZARD:GUT -CREATURE:GIANT_LIZARD:STOMACH -CREATURE:GIANT_LIZARD:GIZZARD -CREATURE:GIANT_LIZARD:PANCREAS -CREATURE:GIANT_LIZARD:SPLEEN -CREATURE:GIANT_LIZARD:KIDNEY -CREATURE:SKINK:MUSCLE -CREATURE:SKINK:EYE -CREATURE:SKINK:BRAIN -CREATURE:SKINK:LUNG -CREATURE:SKINK:HEART -CREATURE:SKINK:LIVER -CREATURE:SKINK:GUT -CREATURE:SKINK:STOMACH -CREATURE:SKINK:GIZZARD -CREATURE:SKINK:PANCREAS -CREATURE:SKINK:SPLEEN -CREATURE:SKINK:KIDNEY -CREATURE:SKINK_MAN:MUSCLE -CREATURE:SKINK_MAN:EYE -CREATURE:SKINK_MAN:BRAIN -CREATURE:SKINK_MAN:LUNG -CREATURE:SKINK_MAN:HEART -CREATURE:SKINK_MAN:LIVER -CREATURE:SKINK_MAN:GUT -CREATURE:SKINK_MAN:STOMACH -CREATURE:SKINK_MAN:GIZZARD -CREATURE:SKINK_MAN:PANCREAS -CREATURE:SKINK_MAN:SPLEEN -CREATURE:SKINK_MAN:KIDNEY -CREATURE:GIANT_SKINK:MUSCLE -CREATURE:GIANT_SKINK:EYE -CREATURE:GIANT_SKINK:BRAIN -CREATURE:GIANT_SKINK:LUNG -CREATURE:GIANT_SKINK:HEART -CREATURE:GIANT_SKINK:LIVER -CREATURE:GIANT_SKINK:GUT -CREATURE:GIANT_SKINK:STOMACH -CREATURE:GIANT_SKINK:GIZZARD -CREATURE:GIANT_SKINK:PANCREAS -CREATURE:GIANT_SKINK:SPLEEN -CREATURE:GIANT_SKINK:KIDNEY -CREATURE:CHAMELEON:MUSCLE -CREATURE:CHAMELEON:EYE -CREATURE:CHAMELEON:BRAIN -CREATURE:CHAMELEON:LUNG -CREATURE:CHAMELEON:HEART -CREATURE:CHAMELEON:LIVER -CREATURE:CHAMELEON:GUT -CREATURE:CHAMELEON:STOMACH -CREATURE:CHAMELEON:GIZZARD -CREATURE:CHAMELEON:PANCREAS -CREATURE:CHAMELEON:SPLEEN -CREATURE:CHAMELEON:KIDNEY -CREATURE:CHAMELEON_MAN:MUSCLE -CREATURE:CHAMELEON_MAN:EYE -CREATURE:CHAMELEON_MAN:BRAIN -CREATURE:CHAMELEON_MAN:LUNG -CREATURE:CHAMELEON_MAN:HEART -CREATURE:CHAMELEON_MAN:LIVER -CREATURE:CHAMELEON_MAN:GUT -CREATURE:CHAMELEON_MAN:STOMACH -CREATURE:CHAMELEON_MAN:GIZZARD -CREATURE:CHAMELEON_MAN:PANCREAS -CREATURE:CHAMELEON_MAN:SPLEEN -CREATURE:CHAMELEON_MAN:KIDNEY -CREATURE:GIANT_CHAMELEON:MUSCLE -CREATURE:GIANT_CHAMELEON:EYE -CREATURE:GIANT_CHAMELEON:BRAIN -CREATURE:GIANT_CHAMELEON:LUNG -CREATURE:GIANT_CHAMELEON:HEART -CREATURE:GIANT_CHAMELEON:LIVER -CREATURE:GIANT_CHAMELEON:GUT - CREATURE:GIANT_CHAMELEON:STOMACH - CREATURE:GIANT_CHAMELEON:GIZZARD -!CREATURE:GIANT_CHAMELEON:PANCREAS -CREATURE:GIANT_CHAMELEON:SPLEEN -CREATURE:GIANT_CHAMELEON:KIDNEY -CREATURE:ANOLE:MUSCLE -CREATURE:ANOLE:EYE -CREATURE:ANOLE:BRAIN -CREATURE:ANOLE:LUNG -CREATURE:ANOLE:HEART -CREATURE:ANOLE:LIVER -CREATURE:ANOLE:GUT -CREATURE:ANOLE:STOMACH -CREATURE:ANOLE:GIZZARD -CREATURE:ANOLE:PANCREAS -CREATURE:ANOLE:SPLEEN -CREATURE:ANOLE:KIDNEY -CREATURE:ANOLE_MAN:MUSCLE -CREATURE:ANOLE_MAN:EYE -CREATURE:ANOLE_MAN:BRAIN -CREATURE:ANOLE_MAN:LUNG -CREATURE:ANOLE_MAN:HEART -CREATURE:ANOLE_MAN:LIVER -CREATURE:ANOLE_MAN:GUT -CREATURE:ANOLE_MAN:STOMACH -CREATURE:ANOLE_MAN:GIZZARD -CREATURE:ANOLE_MAN:PANCREAS -CREATURE:ANOLE_MAN:SPLEEN -CREATURE:ANOLE_MAN:KIDNEY -CREATURE:GIANT_ANOLE:MUSCLE -CREATURE:GIANT_ANOLE:EYE -CREATURE:GIANT_ANOLE:BRAIN -CREATURE:GIANT_ANOLE:LUNG -CREATURE:GIANT_ANOLE:HEART -CREATURE:GIANT_ANOLE:LIVER -CREATURE:GIANT_ANOLE:GUT -CREATURE:GIANT_ANOLE:STOMACH -CREATURE:GIANT_ANOLE:GIZZARD -CREATURE:GIANT_ANOLE:PANCREAS -CREATURE:GIANT_ANOLE:SPLEEN -CREATURE:GIANT_ANOLE:KIDNEY -CREATURE:IGUANA:MUSCLE -CREATURE:IGUANA:EYE -CREATURE:IGUANA:BRAIN -CREATURE:IGUANA:LUNG -CREATURE:IGUANA:HEART -CREATURE:IGUANA:LIVER -CREATURE:IGUANA:GUT -CREATURE:IGUANA:STOMACH -CREATURE:IGUANA:GIZZARD -CREATURE:IGUANA:PANCREAS -CREATURE:IGUANA:SPLEEN -CREATURE:IGUANA:KIDNEY -CREATURE:IGUANA_MAN:MUSCLE -CREATURE:IGUANA_MAN:EYE -CREATURE:IGUANA_MAN:BRAIN -CREATURE:IGUANA_MAN:LUNG -CREATURE:IGUANA_MAN:HEART -CREATURE:IGUANA_MAN:LIVER -CREATURE:IGUANA_MAN:GUT -CREATURE:IGUANA_MAN:STOMACH -CREATURE:IGUANA_MAN:GIZZARD -CREATURE:IGUANA_MAN:PANCREAS -CREATURE:IGUANA_MAN:SPLEEN -CREATURE:IGUANA_MAN:KIDNEY -CREATURE:GIANT_IGUANA:MUSCLE -CREATURE:GIANT_IGUANA:EYE -CREATURE:GIANT_IGUANA:BRAIN -CREATURE:GIANT_IGUANA:LUNG -CREATURE:GIANT_IGUANA:HEART -CREATURE:GIANT_IGUANA:LIVER -CREATURE:GIANT_IGUANA:GUT -CREATURE:GIANT_IGUANA:STOMACH -CREATURE:GIANT_IGUANA:GIZZARD -CREATURE:GIANT_IGUANA:PANCREAS -CREATURE:GIANT_IGUANA:SPLEEN -CREATURE:GIANT_IGUANA:KIDNEY -CREATURE:RIVER OTTER:MUSCLE -CREATURE:RIVER OTTER:EYE -CREATURE:RIVER OTTER:BRAIN -CREATURE:RIVER OTTER:LUNG -CREATURE:RIVER OTTER:HEART -CREATURE:RIVER OTTER:LIVER -CREATURE:RIVER OTTER:GUT -CREATURE:RIVER OTTER:STOMACH -CREATURE:RIVER OTTER:GIZZARD -CREATURE:RIVER OTTER:PANCREAS -CREATURE:RIVER OTTER:SPLEEN -CREATURE:RIVER OTTER:KIDNEY -CREATURE:SEA OTTER:MUSCLE -CREATURE:SEA OTTER:EYE -CREATURE:SEA OTTER:BRAIN -CREATURE:SEA OTTER:LUNG -CREATURE:SEA OTTER:HEART -CREATURE:SEA OTTER:LIVER -CREATURE:SEA OTTER:GUT -CREATURE:SEA OTTER:STOMACH -CREATURE:SEA OTTER:GIZZARD -CREATURE:SEA OTTER:PANCREAS -CREATURE:SEA OTTER:SPLEEN -CREATURE:SEA OTTER:KIDNEY -CREATURE:OTTER_MAN:MUSCLE -CREATURE:OTTER_MAN:EYE -CREATURE:OTTER_MAN:BRAIN -CREATURE:OTTER_MAN:LUNG -CREATURE:OTTER_MAN:HEART -CREATURE:OTTER_MAN:LIVER -CREATURE:OTTER_MAN:GUT -CREATURE:OTTER_MAN:STOMACH -CREATURE:OTTER_MAN:GIZZARD -CREATURE:OTTER_MAN:PANCREAS -CREATURE:OTTER_MAN:SPLEEN -CREATURE:OTTER_MAN:KIDNEY -CREATURE:GIANT_OTTER:MUSCLE -CREATURE:GIANT_OTTER:EYE -CREATURE:GIANT_OTTER:BRAIN -CREATURE:GIANT_OTTER:LUNG -CREATURE:GIANT_OTTER:HEART -CREATURE:GIANT_OTTER:LIVER -CREATURE:GIANT_OTTER:GUT -CREATURE:GIANT_OTTER:STOMACH -CREATURE:GIANT_OTTER:GIZZARD -CREATURE:GIANT_OTTER:PANCREAS -CREATURE:GIANT_OTTER:SPLEEN -CREATURE:GIANT_OTTER:KIDNEY -CREATURE:SNAPPING TURTLE:MUSCLE -CREATURE:SNAPPING TURTLE:EYE -CREATURE:SNAPPING TURTLE:BRAIN -CREATURE:SNAPPING TURTLE:LUNG -CREATURE:SNAPPING TURTLE:HEART -CREATURE:SNAPPING TURTLE:LIVER -CREATURE:SNAPPING TURTLE:GUT - CREATURE:SNAPPING TURTLE:STOMACH - CREATURE:SNAPPING TURTLE:GIZZARD -!CREATURE:SNAPPING TURTLE:PANCREAS -CREATURE:SNAPPING TURTLE:SPLEEN -CREATURE:SNAPPING TURTLE:KIDNEY -)CREATURE:ALLIGATOR SNAPPING TURTLE:MUSCLE -&CREATURE:ALLIGATOR SNAPPING TURTLE:EYE -(CREATURE:ALLIGATOR SNAPPING TURTLE:BRAIN -'CREATURE:ALLIGATOR SNAPPING TURTLE:LUNG -(CREATURE:ALLIGATOR SNAPPING TURTLE:HEART -(CREATURE:ALLIGATOR SNAPPING TURTLE:LIVER -&CREATURE:ALLIGATOR SNAPPING TURTLE:GUT -*CREATURE:ALLIGATOR SNAPPING TURTLE:STOMACH -*CREATURE:ALLIGATOR SNAPPING TURTLE:GIZZARD -+CREATURE:ALLIGATOR SNAPPING TURTLE:PANCREAS -)CREATURE:ALLIGATOR SNAPPING TURTLE:SPLEEN -)CREATURE:ALLIGATOR SNAPPING TURTLE:KIDNEY -#CREATURE:SNAPPING_TURTLE_MAN:MUSCLE - CREATURE:SNAPPING_TURTLE_MAN:EYE -"CREATURE:SNAPPING_TURTLE_MAN:BRAIN -!CREATURE:SNAPPING_TURTLE_MAN:LUNG -"CREATURE:SNAPPING_TURTLE_MAN:HEART -"CREATURE:SNAPPING_TURTLE_MAN:LIVER - CREATURE:SNAPPING_TURTLE_MAN:GUT -$CREATURE:SNAPPING_TURTLE_MAN:STOMACH -$CREATURE:SNAPPING_TURTLE_MAN:GIZZARD -%CREATURE:SNAPPING_TURTLE_MAN:PANCREAS -#CREATURE:SNAPPING_TURTLE_MAN:SPLEEN -#CREATURE:SNAPPING_TURTLE_MAN:KIDNEY -%CREATURE:GIANT_SNAPPING_TURTLE:MUSCLE -"CREATURE:GIANT_SNAPPING_TURTLE:EYE -$CREATURE:GIANT_SNAPPING_TURTLE:BRAIN -#CREATURE:GIANT_SNAPPING_TURTLE:LUNG -$CREATURE:GIANT_SNAPPING_TURTLE:HEART -$CREATURE:GIANT_SNAPPING_TURTLE:LIVER -"CREATURE:GIANT_SNAPPING_TURTLE:GUT -&CREATURE:GIANT_SNAPPING_TURTLE:STOMACH -&CREATURE:GIANT_SNAPPING_TURTLE:GIZZARD -'CREATURE:GIANT_SNAPPING_TURTLE:PANCREAS -%CREATURE:GIANT_SNAPPING_TURTLE:SPLEEN -%CREATURE:GIANT_SNAPPING_TURTLE:KIDNEY -CREATURE:BEAVER:MUSCLE -CREATURE:BEAVER:EYE -CREATURE:BEAVER:BRAIN -CREATURE:BEAVER:LUNG -CREATURE:BEAVER:HEART -CREATURE:BEAVER:LIVER -CREATURE:BEAVER:GUT -CREATURE:BEAVER:STOMACH -CREATURE:BEAVER:GIZZARD -CREATURE:BEAVER:PANCREAS -CREATURE:BEAVER:SPLEEN -CREATURE:BEAVER:KIDNEY -CREATURE:BEAVER_MAN:MUSCLE -CREATURE:BEAVER_MAN:EYE -CREATURE:BEAVER_MAN:BRAIN -CREATURE:BEAVER_MAN:LUNG -CREATURE:BEAVER_MAN:HEART -CREATURE:BEAVER_MAN:LIVER -CREATURE:BEAVER_MAN:GUT -CREATURE:BEAVER_MAN:STOMACH -CREATURE:BEAVER_MAN:GIZZARD -CREATURE:BEAVER_MAN:PANCREAS -CREATURE:BEAVER_MAN:SPLEEN -CREATURE:BEAVER_MAN:KIDNEY -CREATURE:GIANT_BEAVER:MUSCLE -CREATURE:GIANT_BEAVER:EYE -CREATURE:GIANT_BEAVER:BRAIN -CREATURE:GIANT_BEAVER:LUNG -CREATURE:GIANT_BEAVER:HEART -CREATURE:GIANT_BEAVER:LIVER -CREATURE:GIANT_BEAVER:GUT -CREATURE:GIANT_BEAVER:STOMACH -CREATURE:GIANT_BEAVER:GIZZARD -CREATURE:GIANT_BEAVER:PANCREAS -CREATURE:GIANT_BEAVER:SPLEEN -CREATURE:GIANT_BEAVER:KIDNEY -CREATURE:LEECH:MUSCLE -CREATURE:LEECH:EYE -CREATURE:LEECH:BRAIN -CREATURE:LEECH:LUNG -CREATURE:LEECH:HEART -CREATURE:LEECH:LIVER -CREATURE:LEECH:GUT -CREATURE:LEECH:STOMACH -CREATURE:LEECH:GIZZARD -CREATURE:LEECH:PANCREAS -CREATURE:LEECH:SPLEEN -CREATURE:LEECH:KIDNEY -CREATURE:LEECH_MAN:MUSCLE -CREATURE:LEECH_MAN:EYE -CREATURE:LEECH_MAN:BRAIN -CREATURE:LEECH_MAN:LUNG -CREATURE:LEECH_MAN:HEART -CREATURE:LEECH_MAN:LIVER -CREATURE:LEECH_MAN:GUT -CREATURE:LEECH_MAN:STOMACH -CREATURE:LEECH_MAN:GIZZARD -CREATURE:LEECH_MAN:PANCREAS -CREATURE:LEECH_MAN:SPLEEN -CREATURE:LEECH_MAN:KIDNEY -CREATURE:GIANT_LEECH:MUSCLE -CREATURE:GIANT_LEECH:EYE -CREATURE:GIANT_LEECH:BRAIN -CREATURE:GIANT_LEECH:LUNG -CREATURE:GIANT_LEECH:HEART -CREATURE:GIANT_LEECH:LIVER -CREATURE:GIANT_LEECH:GUT -CREATURE:GIANT_LEECH:STOMACH -CREATURE:GIANT_LEECH:GIZZARD -CREATURE:GIANT_LEECH:PANCREAS -CREATURE:GIANT_LEECH:SPLEEN -CREATURE:GIANT_LEECH:KIDNEY -CREATURE:AXOLOTL:MUSCLE -CREATURE:AXOLOTL:EYE -CREATURE:AXOLOTL:BRAIN -CREATURE:AXOLOTL:LUNG -CREATURE:AXOLOTL:HEART -CREATURE:AXOLOTL:LIVER -CREATURE:AXOLOTL:GUT -CREATURE:AXOLOTL:STOMACH -CREATURE:AXOLOTL:GIZZARD -CREATURE:AXOLOTL:PANCREAS -CREATURE:AXOLOTL:SPLEEN -CREATURE:AXOLOTL:KIDNEY -CREATURE:AXOLOTL_MAN:MUSCLE -CREATURE:AXOLOTL_MAN:EYE -CREATURE:AXOLOTL_MAN:BRAIN -CREATURE:AXOLOTL_MAN:LUNG -CREATURE:AXOLOTL_MAN:HEART -CREATURE:AXOLOTL_MAN:LIVER -CREATURE:AXOLOTL_MAN:GUT -CREATURE:AXOLOTL_MAN:STOMACH -CREATURE:AXOLOTL_MAN:GIZZARD -CREATURE:AXOLOTL_MAN:PANCREAS -CREATURE:AXOLOTL_MAN:SPLEEN -CREATURE:AXOLOTL_MAN:KIDNEY -CREATURE:GIANT_AXOLOTL:MUSCLE -CREATURE:GIANT_AXOLOTL:EYE -CREATURE:GIANT_AXOLOTL:BRAIN -CREATURE:GIANT_AXOLOTL:LUNG -CREATURE:GIANT_AXOLOTL:HEART -CREATURE:GIANT_AXOLOTL:LIVER -CREATURE:GIANT_AXOLOTL:GUT -CREATURE:GIANT_AXOLOTL:STOMACH -CREATURE:GIANT_AXOLOTL:GIZZARD -CREATURE:GIANT_AXOLOTL:PANCREAS -CREATURE:GIANT_AXOLOTL:SPLEEN -CREATURE:GIANT_AXOLOTL:KIDNEY -CREATURE:MINK:MUSCLE -CREATURE:MINK:EYE -CREATURE:MINK:BRAIN -CREATURE:MINK:LUNG -CREATURE:MINK:HEART -CREATURE:MINK:LIVER -CREATURE:MINK:GUT -CREATURE:MINK:STOMACH -CREATURE:MINK:GIZZARD -CREATURE:MINK:PANCREAS -CREATURE:MINK:SPLEEN -CREATURE:MINK:KIDNEY -CREATURE:MINK_MAN:MUSCLE -CREATURE:MINK_MAN:EYE -CREATURE:MINK_MAN:BRAIN -CREATURE:MINK_MAN:LUNG -CREATURE:MINK_MAN:HEART -CREATURE:MINK_MAN:LIVER -CREATURE:MINK_MAN:GUT -CREATURE:MINK_MAN:STOMACH -CREATURE:MINK_MAN:GIZZARD -CREATURE:MINK_MAN:PANCREAS -CREATURE:MINK_MAN:SPLEEN -CREATURE:MINK_MAN:KIDNEY -CREATURE:GIANT_MINK:MUSCLE -CREATURE:GIANT_MINK:EYE -CREATURE:GIANT_MINK:BRAIN -CREATURE:GIANT_MINK:LUNG -CREATURE:GIANT_MINK:HEART -CREATURE:GIANT_MINK:LIVER -CREATURE:GIANT_MINK:GUT -CREATURE:GIANT_MINK:STOMACH -CREATURE:GIANT_MINK:GIZZARD -CREATURE:GIANT_MINK:PANCREAS -CREATURE:GIANT_MINK:SPLEEN -CREATURE:GIANT_MINK:KIDNEY -CREATURE:POND_TURTLE:MUSCLE -CREATURE:POND_TURTLE:EYE -CREATURE:POND_TURTLE:BRAIN -CREATURE:POND_TURTLE:LUNG -CREATURE:POND_TURTLE:HEART -CREATURE:POND_TURTLE:LIVER -CREATURE:POND_TURTLE:GUT -CREATURE:POND_TURTLE:STOMACH -CREATURE:POND_TURTLE:GIZZARD -CREATURE:POND_TURTLE:PANCREAS -CREATURE:POND_TURTLE:SPLEEN -CREATURE:POND_TURTLE:KIDNEY -CREATURE:POND_TURTLE_MAN:MUSCLE -CREATURE:POND_TURTLE_MAN:EYE -CREATURE:POND_TURTLE_MAN:BRAIN -CREATURE:POND_TURTLE_MAN:LUNG -CREATURE:POND_TURTLE_MAN:HEART -CREATURE:POND_TURTLE_MAN:LIVER -CREATURE:POND_TURTLE_MAN:GUT - CREATURE:POND_TURTLE_MAN:STOMACH - CREATURE:POND_TURTLE_MAN:GIZZARD -!CREATURE:POND_TURTLE_MAN:PANCREAS -CREATURE:POND_TURTLE_MAN:SPLEEN -CREATURE:POND_TURTLE_MAN:KIDNEY -!CREATURE:GIANT_POND_TURTLE:MUSCLE -CREATURE:GIANT_POND_TURTLE:EYE - CREATURE:GIANT_POND_TURTLE:BRAIN -CREATURE:GIANT_POND_TURTLE:LUNG - CREATURE:GIANT_POND_TURTLE:HEART - CREATURE:GIANT_POND_TURTLE:LIVER -CREATURE:GIANT_POND_TURTLE:GUT -"CREATURE:GIANT_POND_TURTLE:STOMACH -"CREATURE:GIANT_POND_TURTLE:GIZZARD -#CREATURE:GIANT_POND_TURTLE:PANCREAS -!CREATURE:GIANT_POND_TURTLE:SPLEEN -!CREATURE:GIANT_POND_TURTLE:KIDNEY -CREATURE:RAT:MUSCLE -CREATURE:RAT:EYE -CREATURE:RAT:BRAIN -CREATURE:RAT:LUNG -CREATURE:RAT:HEART -CREATURE:RAT:LIVER -CREATURE:RAT:GUT -CREATURE:RAT:STOMACH -CREATURE:RAT:GIZZARD -CREATURE:RAT:PANCREAS -CREATURE:RAT:SPLEEN -CREATURE:RAT:KIDNEY -CREATURE:RAT_MAN:MUSCLE -CREATURE:RAT_MAN:EYE -CREATURE:RAT_MAN:BRAIN -CREATURE:RAT_MAN:LUNG -CREATURE:RAT_MAN:HEART -CREATURE:RAT_MAN:LIVER -CREATURE:RAT_MAN:GUT -CREATURE:RAT_MAN:STOMACH -CREATURE:RAT_MAN:GIZZARD -CREATURE:RAT_MAN:PANCREAS -CREATURE:RAT_MAN:SPLEEN -CREATURE:RAT_MAN:KIDNEY -CREATURE:SQUIRREL_GRAY:MUSCLE -CREATURE:SQUIRREL_GRAY:EYE -CREATURE:SQUIRREL_GRAY:BRAIN -CREATURE:SQUIRREL_GRAY:LUNG -CREATURE:SQUIRREL_GRAY:HEART -CREATURE:SQUIRREL_GRAY:LIVER -CREATURE:SQUIRREL_GRAY:GUT -CREATURE:SQUIRREL_GRAY:STOMACH -CREATURE:SQUIRREL_GRAY:GIZZARD -CREATURE:SQUIRREL_GRAY:PANCREAS -CREATURE:SQUIRREL_GRAY:SPLEEN -CREATURE:SQUIRREL_GRAY:KIDNEY -!CREATURE:SQUIRREL_GRAY_MAN:MUSCLE -CREATURE:SQUIRREL_GRAY_MAN:EYE - CREATURE:SQUIRREL_GRAY_MAN:BRAIN -CREATURE:SQUIRREL_GRAY_MAN:LUNG - CREATURE:SQUIRREL_GRAY_MAN:HEART - CREATURE:SQUIRREL_GRAY_MAN:LIVER -CREATURE:SQUIRREL_GRAY_MAN:GUT -"CREATURE:SQUIRREL_GRAY_MAN:STOMACH -"CREATURE:SQUIRREL_GRAY_MAN:GIZZARD -#CREATURE:SQUIRREL_GRAY_MAN:PANCREAS -!CREATURE:SQUIRREL_GRAY_MAN:SPLEEN -!CREATURE:SQUIRREL_GRAY_MAN:KIDNEY -#CREATURE:GIANT_SQUIRREL_GRAY:MUSCLE - CREATURE:GIANT_SQUIRREL_GRAY:EYE -"CREATURE:GIANT_SQUIRREL_GRAY:BRAIN -!CREATURE:GIANT_SQUIRREL_GRAY:LUNG -"CREATURE:GIANT_SQUIRREL_GRAY:HEART -"CREATURE:GIANT_SQUIRREL_GRAY:LIVER - CREATURE:GIANT_SQUIRREL_GRAY:GUT -$CREATURE:GIANT_SQUIRREL_GRAY:STOMACH -$CREATURE:GIANT_SQUIRREL_GRAY:GIZZARD -%CREATURE:GIANT_SQUIRREL_GRAY:PANCREAS -#CREATURE:GIANT_SQUIRREL_GRAY:SPLEEN -#CREATURE:GIANT_SQUIRREL_GRAY:KIDNEY -CREATURE:SQUIRREL_RED:MUSCLE -CREATURE:SQUIRREL_RED:EYE -CREATURE:SQUIRREL_RED:BRAIN -CREATURE:SQUIRREL_RED:LUNG -CREATURE:SQUIRREL_RED:HEART -CREATURE:SQUIRREL_RED:LIVER -CREATURE:SQUIRREL_RED:GUT -CREATURE:SQUIRREL_RED:STOMACH -CREATURE:SQUIRREL_RED:GIZZARD -CREATURE:SQUIRREL_RED:PANCREAS -CREATURE:SQUIRREL_RED:SPLEEN -CREATURE:SQUIRREL_RED:KIDNEY - CREATURE:SQUIRREL_RED_MAN:MUSCLE -CREATURE:SQUIRREL_RED_MAN:EYE -CREATURE:SQUIRREL_RED_MAN:BRAIN -CREATURE:SQUIRREL_RED_MAN:LUNG -CREATURE:SQUIRREL_RED_MAN:HEART -CREATURE:SQUIRREL_RED_MAN:LIVER -CREATURE:SQUIRREL_RED_MAN:GUT -!CREATURE:SQUIRREL_RED_MAN:STOMACH -!CREATURE:SQUIRREL_RED_MAN:GIZZARD -"CREATURE:SQUIRREL_RED_MAN:PANCREAS - CREATURE:SQUIRREL_RED_MAN:SPLEEN - CREATURE:SQUIRREL_RED_MAN:KIDNEY -"CREATURE:GIANT_SQUIRREL_RED:MUSCLE -CREATURE:GIANT_SQUIRREL_RED:EYE -!CREATURE:GIANT_SQUIRREL_RED:BRAIN - CREATURE:GIANT_SQUIRREL_RED:LUNG -!CREATURE:GIANT_SQUIRREL_RED:HEART -!CREATURE:GIANT_SQUIRREL_RED:LIVER -CREATURE:GIANT_SQUIRREL_RED:GUT -#CREATURE:GIANT_SQUIRREL_RED:STOMACH -#CREATURE:GIANT_SQUIRREL_RED:GIZZARD -$CREATURE:GIANT_SQUIRREL_RED:PANCREAS -"CREATURE:GIANT_SQUIRREL_RED:SPLEEN -"CREATURE:GIANT_SQUIRREL_RED:KIDNEY -CREATURE:CHIPMUNK:MUSCLE -CREATURE:CHIPMUNK:EYE -CREATURE:CHIPMUNK:BRAIN -CREATURE:CHIPMUNK:LUNG -CREATURE:CHIPMUNK:HEART -CREATURE:CHIPMUNK:LIVER -CREATURE:CHIPMUNK:GUT -CREATURE:CHIPMUNK:STOMACH -CREATURE:CHIPMUNK:GIZZARD -CREATURE:CHIPMUNK:PANCREAS -CREATURE:CHIPMUNK:SPLEEN -CREATURE:CHIPMUNK:KIDNEY -CREATURE:CHIPMUNK_MAN:MUSCLE -CREATURE:CHIPMUNK_MAN:EYE -CREATURE:CHIPMUNK_MAN:BRAIN -CREATURE:CHIPMUNK_MAN:LUNG -CREATURE:CHIPMUNK_MAN:HEART -CREATURE:CHIPMUNK_MAN:LIVER -CREATURE:CHIPMUNK_MAN:GUT -CREATURE:CHIPMUNK_MAN:STOMACH -CREATURE:CHIPMUNK_MAN:GIZZARD -CREATURE:CHIPMUNK_MAN:PANCREAS -CREATURE:CHIPMUNK_MAN:SPLEEN -CREATURE:CHIPMUNK_MAN:KIDNEY -CREATURE:GIANT_CHIPMUNK:MUSCLE -CREATURE:GIANT_CHIPMUNK:EYE -CREATURE:GIANT_CHIPMUNK:BRAIN -CREATURE:GIANT_CHIPMUNK:LUNG -CREATURE:GIANT_CHIPMUNK:HEART -CREATURE:GIANT_CHIPMUNK:LIVER -CREATURE:GIANT_CHIPMUNK:GUT -CREATURE:GIANT_CHIPMUNK:STOMACH -CREATURE:GIANT_CHIPMUNK:GIZZARD - CREATURE:GIANT_CHIPMUNK:PANCREAS -CREATURE:GIANT_CHIPMUNK:SPLEEN -CREATURE:GIANT_CHIPMUNK:KIDNEY -CREATURE:HAMSTER:MUSCLE -CREATURE:HAMSTER:EYE -CREATURE:HAMSTER:BRAIN -CREATURE:HAMSTER:LUNG -CREATURE:HAMSTER:HEART -CREATURE:HAMSTER:LIVER -CREATURE:HAMSTER:GUT -CREATURE:HAMSTER:STOMACH -CREATURE:HAMSTER:GIZZARD -CREATURE:HAMSTER:PANCREAS -CREATURE:HAMSTER:SPLEEN -CREATURE:HAMSTER:KIDNEY -CREATURE:HAMSTER_MAN:MUSCLE -CREATURE:HAMSTER_MAN:EYE -CREATURE:HAMSTER_MAN:BRAIN -CREATURE:HAMSTER_MAN:LUNG -CREATURE:HAMSTER_MAN:HEART -CREATURE:HAMSTER_MAN:LIVER -CREATURE:HAMSTER_MAN:GUT -CREATURE:HAMSTER_MAN:STOMACH -CREATURE:HAMSTER_MAN:GIZZARD -CREATURE:HAMSTER_MAN:PANCREAS -CREATURE:HAMSTER_MAN:SPLEEN -CREATURE:HAMSTER_MAN:KIDNEY -CREATURE:GIANT_HAMSTER:MUSCLE -CREATURE:GIANT_HAMSTER:EYE -CREATURE:GIANT_HAMSTER:BRAIN -CREATURE:GIANT_HAMSTER:LUNG -CREATURE:GIANT_HAMSTER:HEART -CREATURE:GIANT_HAMSTER:LIVER -CREATURE:GIANT_HAMSTER:GUT -CREATURE:GIANT_HAMSTER:STOMACH -CREATURE:GIANT_HAMSTER:GIZZARD -CREATURE:GIANT_HAMSTER:PANCREAS -CREATURE:GIANT_HAMSTER:SPLEEN -CREATURE:GIANT_HAMSTER:KIDNEY -CREATURE:HEDGEHOG:MUSCLE -CREATURE:HEDGEHOG:EYE -CREATURE:HEDGEHOG:BRAIN -CREATURE:HEDGEHOG:LUNG -CREATURE:HEDGEHOG:HEART -CREATURE:HEDGEHOG:LIVER -CREATURE:HEDGEHOG:GUT -CREATURE:HEDGEHOG:STOMACH -CREATURE:HEDGEHOG:GIZZARD -CREATURE:HEDGEHOG:PANCREAS -CREATURE:HEDGEHOG:SPLEEN -CREATURE:HEDGEHOG:KIDNEY -CREATURE:HEDGEHOG_MAN:MUSCLE -CREATURE:HEDGEHOG_MAN:EYE -CREATURE:HEDGEHOG_MAN:BRAIN -CREATURE:HEDGEHOG_MAN:LUNG -CREATURE:HEDGEHOG_MAN:HEART -CREATURE:HEDGEHOG_MAN:LIVER -CREATURE:HEDGEHOG_MAN:GUT -CREATURE:HEDGEHOG_MAN:STOMACH -CREATURE:HEDGEHOG_MAN:GIZZARD -CREATURE:HEDGEHOG_MAN:PANCREAS -CREATURE:HEDGEHOG_MAN:SPLEEN -CREATURE:HEDGEHOG_MAN:KIDNEY -CREATURE:GIANT_HEDGEHOG:MUSCLE -CREATURE:GIANT_HEDGEHOG:EYE -CREATURE:GIANT_HEDGEHOG:BRAIN -CREATURE:GIANT_HEDGEHOG:LUNG -CREATURE:GIANT_HEDGEHOG:HEART -CREATURE:GIANT_HEDGEHOG:LIVER -CREATURE:GIANT_HEDGEHOG:GUT -CREATURE:GIANT_HEDGEHOG:STOMACH -CREATURE:GIANT_HEDGEHOG:GIZZARD - CREATURE:GIANT_HEDGEHOG:PANCREAS -CREATURE:GIANT_HEDGEHOG:SPLEEN -CREATURE:GIANT_HEDGEHOG:KIDNEY -CREATURE:SQUIRREL_FLYING:MUSCLE -CREATURE:SQUIRREL_FLYING:EYE -CREATURE:SQUIRREL_FLYING:BRAIN -CREATURE:SQUIRREL_FLYING:LUNG -CREATURE:SQUIRREL_FLYING:HEART -CREATURE:SQUIRREL_FLYING:LIVER -CREATURE:SQUIRREL_FLYING:GUT - CREATURE:SQUIRREL_FLYING:STOMACH - CREATURE:SQUIRREL_FLYING:GIZZARD -!CREATURE:SQUIRREL_FLYING:PANCREAS -CREATURE:SQUIRREL_FLYING:SPLEEN -CREATURE:SQUIRREL_FLYING:KIDNEY -#CREATURE:FLYING_SQUIRREL_MAN:MUSCLE - CREATURE:FLYING_SQUIRREL_MAN:EYE -"CREATURE:FLYING_SQUIRREL_MAN:BRAIN -!CREATURE:FLYING_SQUIRREL_MAN:LUNG -"CREATURE:FLYING_SQUIRREL_MAN:HEART -"CREATURE:FLYING_SQUIRREL_MAN:LIVER - CREATURE:FLYING_SQUIRREL_MAN:GUT -$CREATURE:FLYING_SQUIRREL_MAN:STOMACH -$CREATURE:FLYING_SQUIRREL_MAN:GIZZARD -%CREATURE:FLYING_SQUIRREL_MAN:PANCREAS -#CREATURE:FLYING_SQUIRREL_MAN:SPLEEN -#CREATURE:FLYING_SQUIRREL_MAN:KIDNEY -%CREATURE:GIANT_FLYING_SQUIRREL:MUSCLE -"CREATURE:GIANT_FLYING_SQUIRREL:EYE -$CREATURE:GIANT_FLYING_SQUIRREL:BRAIN -#CREATURE:GIANT_FLYING_SQUIRREL:LUNG -$CREATURE:GIANT_FLYING_SQUIRREL:HEART -$CREATURE:GIANT_FLYING_SQUIRREL:LIVER -"CREATURE:GIANT_FLYING_SQUIRREL:GUT -&CREATURE:GIANT_FLYING_SQUIRREL:STOMACH -&CREATURE:GIANT_FLYING_SQUIRREL:GIZZARD -'CREATURE:GIANT_FLYING_SQUIRREL:PANCREAS -%CREATURE:GIANT_FLYING_SQUIRREL:SPLEEN -%CREATURE:GIANT_FLYING_SQUIRREL:KIDNEY -CREATURE:MUSSEL:MUSCLE -CREATURE:OYSTER:MUSCLE -CREATURE:FISH_SALMON:MUSCLE -CREATURE:FISH_SALMON:EYE -CREATURE:FISH_SALMON:BRAIN -CREATURE:FISH_SALMON:LUNG -CREATURE:FISH_SALMON:HEART -CREATURE:FISH_SALMON:LIVER -CREATURE:FISH_SALMON:GUT -CREATURE:FISH_SALMON:STOMACH -CREATURE:FISH_SALMON:GIZZARD -CREATURE:FISH_SALMON:PANCREAS -CREATURE:FISH_SALMON:SPLEEN -CREATURE:FISH_SALMON:KIDNEY -CREATURE:FISH_CLOWNFISH:MUSCLE -CREATURE:FISH_CLOWNFISH:EYE -CREATURE:FISH_CLOWNFISH:BRAIN -CREATURE:FISH_CLOWNFISH:LUNG -CREATURE:FISH_CLOWNFISH:HEART -CREATURE:FISH_CLOWNFISH:LIVER -CREATURE:FISH_CLOWNFISH:GUT -CREATURE:FISH_CLOWNFISH:STOMACH -CREATURE:FISH_CLOWNFISH:GIZZARD - CREATURE:FISH_CLOWNFISH:PANCREAS -CREATURE:FISH_CLOWNFISH:SPLEEN -CREATURE:FISH_CLOWNFISH:KIDNEY -CREATURE:FISH_HAGFISH:MUSCLE -CREATURE:FISH_HAGFISH:EYE -CREATURE:FISH_HAGFISH:BRAIN -CREATURE:FISH_HAGFISH:LUNG -CREATURE:FISH_HAGFISH:HEART -CREATURE:FISH_HAGFISH:LIVER -CREATURE:FISH_HAGFISH:GUT -CREATURE:FISH_HAGFISH:STOMACH -CREATURE:FISH_HAGFISH:GIZZARD -CREATURE:FISH_HAGFISH:PANCREAS -CREATURE:FISH_HAGFISH:SPLEEN -CREATURE:FISH_HAGFISH:KIDNEY -"CREATURE:FISH_LAMPREY_BROOK:MUSCLE -CREATURE:FISH_LAMPREY_BROOK:EYE -!CREATURE:FISH_LAMPREY_BROOK:BRAIN - CREATURE:FISH_LAMPREY_BROOK:LUNG -!CREATURE:FISH_LAMPREY_BROOK:HEART -!CREATURE:FISH_LAMPREY_BROOK:LIVER -CREATURE:FISH_LAMPREY_BROOK:GUT -#CREATURE:FISH_LAMPREY_BROOK:STOMACH -#CREATURE:FISH_LAMPREY_BROOK:GIZZARD -$CREATURE:FISH_LAMPREY_BROOK:PANCREAS -"CREATURE:FISH_LAMPREY_BROOK:SPLEEN -"CREATURE:FISH_LAMPREY_BROOK:KIDNEY -CREATURE:FISH_RAY_BAT:MUSCLE -CREATURE:FISH_RAY_BAT:EYE -CREATURE:FISH_RAY_BAT:BRAIN -CREATURE:FISH_RAY_BAT:LUNG -CREATURE:FISH_RAY_BAT:HEART -CREATURE:FISH_RAY_BAT:LIVER -CREATURE:FISH_RAY_BAT:GUT -CREATURE:FISH_RAY_BAT:STOMACH -CREATURE:FISH_RAY_BAT:GIZZARD -CREATURE:FISH_RAY_BAT:PANCREAS -CREATURE:FISH_RAY_BAT:SPLEEN -CREATURE:FISH_RAY_BAT:KIDNEY -"CREATURE:FISH_RAY_THORNBACK:MUSCLE -CREATURE:FISH_RAY_THORNBACK:EYE -!CREATURE:FISH_RAY_THORNBACK:BRAIN - CREATURE:FISH_RAY_THORNBACK:LUNG -!CREATURE:FISH_RAY_THORNBACK:HEART -!CREATURE:FISH_RAY_THORNBACK:LIVER -CREATURE:FISH_RAY_THORNBACK:GUT -#CREATURE:FISH_RAY_THORNBACK:STOMACH -#CREATURE:FISH_RAY_THORNBACK:GIZZARD -$CREATURE:FISH_RAY_THORNBACK:PANCREAS -"CREATURE:FISH_RAY_THORNBACK:SPLEEN -"CREATURE:FISH_RAY_THORNBACK:KIDNEY -$CREATURE:FISH_RATFISH_SPOTTED:MUSCLE -!CREATURE:FISH_RATFISH_SPOTTED:EYE -#CREATURE:FISH_RATFISH_SPOTTED:BRAIN -"CREATURE:FISH_RATFISH_SPOTTED:LUNG -#CREATURE:FISH_RATFISH_SPOTTED:HEART -#CREATURE:FISH_RATFISH_SPOTTED:LIVER -!CREATURE:FISH_RATFISH_SPOTTED:GUT -%CREATURE:FISH_RATFISH_SPOTTED:STOMACH -%CREATURE:FISH_RATFISH_SPOTTED:GIZZARD -&CREATURE:FISH_RATFISH_SPOTTED:PANCREAS -$CREATURE:FISH_RATFISH_SPOTTED:SPLEEN -$CREATURE:FISH_RATFISH_SPOTTED:KIDNEY -CREATURE:FISH_HERRING:MUSCLE -CREATURE:FISH_HERRING:EYE -CREATURE:FISH_HERRING:BRAIN -CREATURE:FISH_HERRING:LUNG -CREATURE:FISH_HERRING:HEART -CREATURE:FISH_HERRING:LIVER -CREATURE:FISH_HERRING:GUT -CREATURE:FISH_HERRING:STOMACH -CREATURE:FISH_HERRING:GIZZARD -CREATURE:FISH_HERRING:PANCREAS -CREATURE:FISH_HERRING:SPLEEN -CREATURE:FISH_HERRING:KIDNEY -CREATURE:FISH_SHAD:MUSCLE -CREATURE:FISH_SHAD:EYE -CREATURE:FISH_SHAD:BRAIN -CREATURE:FISH_SHAD:LUNG -CREATURE:FISH_SHAD:HEART -CREATURE:FISH_SHAD:LIVER -CREATURE:FISH_SHAD:GUT -CREATURE:FISH_SHAD:STOMACH -CREATURE:FISH_SHAD:GIZZARD -CREATURE:FISH_SHAD:PANCREAS -CREATURE:FISH_SHAD:SPLEEN -CREATURE:FISH_SHAD:KIDNEY -CREATURE:FISH_ANCHOVY:MUSCLE -CREATURE:FISH_ANCHOVY:EYE -CREATURE:FISH_ANCHOVY:BRAIN -CREATURE:FISH_ANCHOVY:LUNG -CREATURE:FISH_ANCHOVY:HEART -CREATURE:FISH_ANCHOVY:LIVER -CREATURE:FISH_ANCHOVY:GUT -CREATURE:FISH_ANCHOVY:STOMACH -CREATURE:FISH_ANCHOVY:GIZZARD -CREATURE:FISH_ANCHOVY:PANCREAS -CREATURE:FISH_ANCHOVY:SPLEEN -CREATURE:FISH_ANCHOVY:KIDNEY -$CREATURE:FISH_TROUT_STEELHEAD:MUSCLE -!CREATURE:FISH_TROUT_STEELHEAD:EYE -#CREATURE:FISH_TROUT_STEELHEAD:BRAIN -"CREATURE:FISH_TROUT_STEELHEAD:LUNG -#CREATURE:FISH_TROUT_STEELHEAD:HEART -#CREATURE:FISH_TROUT_STEELHEAD:LIVER -!CREATURE:FISH_TROUT_STEELHEAD:GUT -%CREATURE:FISH_TROUT_STEELHEAD:STOMACH -%CREATURE:FISH_TROUT_STEELHEAD:GIZZARD -&CREATURE:FISH_TROUT_STEELHEAD:PANCREAS -$CREATURE:FISH_TROUT_STEELHEAD:SPLEEN -$CREATURE:FISH_TROUT_STEELHEAD:KIDNEY -CREATURE:FISH_HAKE:MUSCLE -CREATURE:FISH_HAKE:EYE -CREATURE:FISH_HAKE:BRAIN -CREATURE:FISH_HAKE:LUNG -CREATURE:FISH_HAKE:HEART -CREATURE:FISH_HAKE:LIVER -CREATURE:FISH_HAKE:GUT -CREATURE:FISH_HAKE:STOMACH -CREATURE:FISH_HAKE:GIZZARD -CREATURE:FISH_HAKE:PANCREAS -CREATURE:FISH_HAKE:SPLEEN -CREATURE:FISH_HAKE:KIDNEY -CREATURE:FISH_SEAHORSE:MUSCLE -CREATURE:FISH_SEAHORSE:EYE -CREATURE:FISH_SEAHORSE:BRAIN -CREATURE:FISH_SEAHORSE:LUNG -CREATURE:FISH_SEAHORSE:HEART -CREATURE:FISH_SEAHORSE:LIVER -CREATURE:FISH_SEAHORSE:GUT -CREATURE:FISH_SEAHORSE:STOMACH -CREATURE:FISH_SEAHORSE:GIZZARD -CREATURE:FISH_SEAHORSE:PANCREAS -CREATURE:FISH_SEAHORSE:SPLEEN -CREATURE:FISH_SEAHORSE:KIDNEY -CREATURE:FISH_GLASSEYE:MUSCLE -CREATURE:FISH_GLASSEYE:EYE -CREATURE:FISH_GLASSEYE:BRAIN -CREATURE:FISH_GLASSEYE:LUNG -CREATURE:FISH_GLASSEYE:HEART -CREATURE:FISH_GLASSEYE:LIVER -CREATURE:FISH_GLASSEYE:GUT -CREATURE:FISH_GLASSEYE:STOMACH -CREATURE:FISH_GLASSEYE:GIZZARD -CREATURE:FISH_GLASSEYE:PANCREAS -CREATURE:FISH_GLASSEYE:SPLEEN -CREATURE:FISH_GLASSEYE:KIDNEY -)CREATURE:FISH_PUFFER_WHITE_SPOTTED:MUSCLE -&CREATURE:FISH_PUFFER_WHITE_SPOTTED:EYE -(CREATURE:FISH_PUFFER_WHITE_SPOTTED:BRAIN -'CREATURE:FISH_PUFFER_WHITE_SPOTTED:LUNG -(CREATURE:FISH_PUFFER_WHITE_SPOTTED:HEART -(CREATURE:FISH_PUFFER_WHITE_SPOTTED:LIVER -&CREATURE:FISH_PUFFER_WHITE_SPOTTED:GUT -*CREATURE:FISH_PUFFER_WHITE_SPOTTED:STOMACH -*CREATURE:FISH_PUFFER_WHITE_SPOTTED:GIZZARD -+CREATURE:FISH_PUFFER_WHITE_SPOTTED:PANCREAS -)CREATURE:FISH_PUFFER_WHITE_SPOTTED:SPLEEN -)CREATURE:FISH_PUFFER_WHITE_SPOTTED:KIDNEY -CREATURE:FISH_SOLE:MUSCLE -CREATURE:FISH_SOLE:EYE -CREATURE:FISH_SOLE:BRAIN -CREATURE:FISH_SOLE:LUNG -CREATURE:FISH_SOLE:HEART -CREATURE:FISH_SOLE:LIVER -CREATURE:FISH_SOLE:GUT -CREATURE:FISH_SOLE:STOMACH -CREATURE:FISH_SOLE:GIZZARD -CREATURE:FISH_SOLE:PANCREAS -CREATURE:FISH_SOLE:SPLEEN -CREATURE:FISH_SOLE:KIDNEY -CREATURE:FISH_FLOUNDER:MUSCLE -CREATURE:FISH_FLOUNDER:EYE -CREATURE:FISH_FLOUNDER:BRAIN -CREATURE:FISH_FLOUNDER:LUNG -CREATURE:FISH_FLOUNDER:HEART -CREATURE:FISH_FLOUNDER:LIVER -CREATURE:FISH_FLOUNDER:GUT -CREATURE:FISH_FLOUNDER:STOMACH -CREATURE:FISH_FLOUNDER:GIZZARD -CREATURE:FISH_FLOUNDER:PANCREAS -CREATURE:FISH_FLOUNDER:SPLEEN -CREATURE:FISH_FLOUNDER:KIDNEY -CREATURE:FISH_MACKEREL:MUSCLE -CREATURE:FISH_MACKEREL:EYE -CREATURE:FISH_MACKEREL:BRAIN -CREATURE:FISH_MACKEREL:LUNG -CREATURE:FISH_MACKEREL:HEART -CREATURE:FISH_MACKEREL:LIVER -CREATURE:FISH_MACKEREL:GUT -CREATURE:FISH_MACKEREL:STOMACH -CREATURE:FISH_MACKEREL:GIZZARD -CREATURE:FISH_MACKEREL:PANCREAS -CREATURE:FISH_MACKEREL:SPLEEN -CREATURE:FISH_MACKEREL:KIDNEY -$CREATURE:JELLYFISH_SEA_NETTLE:MUSCLE -!CREATURE:JELLYFISH_SEA_NETTLE:EYE -#CREATURE:JELLYFISH_SEA_NETTLE:BRAIN -"CREATURE:JELLYFISH_SEA_NETTLE:LUNG -#CREATURE:JELLYFISH_SEA_NETTLE:HEART -#CREATURE:JELLYFISH_SEA_NETTLE:LIVER -!CREATURE:JELLYFISH_SEA_NETTLE:GUT -%CREATURE:JELLYFISH_SEA_NETTLE:STOMACH -%CREATURE:JELLYFISH_SEA_NETTLE:GIZZARD -&CREATURE:JELLYFISH_SEA_NETTLE:PANCREAS -$CREATURE:JELLYFISH_SEA_NETTLE:SPLEEN -$CREATURE:JELLYFISH_SEA_NETTLE:KIDNEY -CREATURE:SQUID:MUSCLE -CREATURE:SQUID:EYE -CREATURE:SQUID:BRAIN -CREATURE:SQUID:LUNG -CREATURE:SQUID:HEART -CREATURE:SQUID:LIVER -CREATURE:SQUID:GUT -CREATURE:SQUID:STOMACH -CREATURE:SQUID:GIZZARD -CREATURE:SQUID:PANCREAS -CREATURE:SQUID:SPLEEN -CREATURE:SQUID:KIDNEY -CREATURE:SQUID MAN:MUSCLE -CREATURE:SQUID MAN:EYE -CREATURE:SQUID MAN:BRAIN -CREATURE:SQUID MAN:LUNG -CREATURE:SQUID MAN:HEART -CREATURE:SQUID MAN:LIVER -CREATURE:SQUID MAN:GUT -CREATURE:SQUID MAN:STOMACH -CREATURE:SQUID MAN:GIZZARD -CREATURE:SQUID MAN:PANCREAS -CREATURE:SQUID MAN:SPLEEN -CREATURE:SQUID MAN:KIDNEY -CREATURE:GIGANTIC SQUID:MUSCLE -CREATURE:GIGANTIC SQUID:EYE -CREATURE:GIGANTIC SQUID:BRAIN -CREATURE:GIGANTIC SQUID:LUNG -CREATURE:GIGANTIC SQUID:HEART -CREATURE:GIGANTIC SQUID:LIVER -CREATURE:GIGANTIC SQUID:GUT -CREATURE:GIGANTIC SQUID:STOMACH -CREATURE:GIGANTIC SQUID:GIZZARD - CREATURE:GIGANTIC SQUID:PANCREAS -CREATURE:GIGANTIC SQUID:SPLEEN -CREATURE:GIGANTIC SQUID:KIDNEY -CREATURE:FISH_LUNGFISH:MUSCLE -CREATURE:FISH_LUNGFISH:EYE -CREATURE:FISH_LUNGFISH:BRAIN -CREATURE:FISH_LUNGFISH:LUNG -CREATURE:FISH_LUNGFISH:HEART -CREATURE:FISH_LUNGFISH:LIVER -CREATURE:FISH_LUNGFISH:GUT -CREATURE:FISH_LUNGFISH:STOMACH -CREATURE:FISH_LUNGFISH:GIZZARD -CREATURE:FISH_LUNGFISH:PANCREAS -CREATURE:FISH_LUNGFISH:SPLEEN -CREATURE:FISH_LUNGFISH:KIDNEY - CREATURE:FISH_LOACH_CLOWN:MUSCLE -CREATURE:FISH_LOACH_CLOWN:EYE -CREATURE:FISH_LOACH_CLOWN:BRAIN -CREATURE:FISH_LOACH_CLOWN:LUNG -CREATURE:FISH_LOACH_CLOWN:HEART -CREATURE:FISH_LOACH_CLOWN:LIVER -CREATURE:FISH_LOACH_CLOWN:GUT -!CREATURE:FISH_LOACH_CLOWN:STOMACH -!CREATURE:FISH_LOACH_CLOWN:GIZZARD -"CREATURE:FISH_LOACH_CLOWN:PANCREAS - CREATURE:FISH_LOACH_CLOWN:SPLEEN - CREATURE:FISH_LOACH_CLOWN:KIDNEY -#CREATURE:FISH_BULLHEAD_BROWN:MUSCLE - CREATURE:FISH_BULLHEAD_BROWN:EYE -"CREATURE:FISH_BULLHEAD_BROWN:BRAIN -!CREATURE:FISH_BULLHEAD_BROWN:LUNG -"CREATURE:FISH_BULLHEAD_BROWN:HEART -"CREATURE:FISH_BULLHEAD_BROWN:LIVER - CREATURE:FISH_BULLHEAD_BROWN:GUT -$CREATURE:FISH_BULLHEAD_BROWN:STOMACH -$CREATURE:FISH_BULLHEAD_BROWN:GIZZARD -%CREATURE:FISH_BULLHEAD_BROWN:PANCREAS -#CREATURE:FISH_BULLHEAD_BROWN:SPLEEN -#CREATURE:FISH_BULLHEAD_BROWN:KIDNEY -$CREATURE:FISH_BULLHEAD_YELLOW:MUSCLE -!CREATURE:FISH_BULLHEAD_YELLOW:EYE -#CREATURE:FISH_BULLHEAD_YELLOW:BRAIN -"CREATURE:FISH_BULLHEAD_YELLOW:LUNG -#CREATURE:FISH_BULLHEAD_YELLOW:HEART -#CREATURE:FISH_BULLHEAD_YELLOW:LIVER -!CREATURE:FISH_BULLHEAD_YELLOW:GUT -%CREATURE:FISH_BULLHEAD_YELLOW:STOMACH -%CREATURE:FISH_BULLHEAD_YELLOW:GIZZARD -&CREATURE:FISH_BULLHEAD_YELLOW:PANCREAS -$CREATURE:FISH_BULLHEAD_YELLOW:SPLEEN -$CREATURE:FISH_BULLHEAD_YELLOW:KIDNEY -#CREATURE:FISH_BULLHEAD_BLACK:MUSCLE - CREATURE:FISH_BULLHEAD_BLACK:EYE -"CREATURE:FISH_BULLHEAD_BLACK:BRAIN -!CREATURE:FISH_BULLHEAD_BLACK:LUNG -"CREATURE:FISH_BULLHEAD_BLACK:HEART -"CREATURE:FISH_BULLHEAD_BLACK:LIVER - CREATURE:FISH_BULLHEAD_BLACK:GUT -$CREATURE:FISH_BULLHEAD_BLACK:STOMACH -$CREATURE:FISH_BULLHEAD_BLACK:GIZZARD -%CREATURE:FISH_BULLHEAD_BLACK:PANCREAS -#CREATURE:FISH_BULLHEAD_BLACK:SPLEEN -#CREATURE:FISH_BULLHEAD_BLACK:KIDNEY -%CREATURE:FISH_KNIFEFISH_BANDED:MUSCLE -"CREATURE:FISH_KNIFEFISH_BANDED:EYE -$CREATURE:FISH_KNIFEFISH_BANDED:BRAIN -#CREATURE:FISH_KNIFEFISH_BANDED:LUNG -$CREATURE:FISH_KNIFEFISH_BANDED:HEART -$CREATURE:FISH_KNIFEFISH_BANDED:LIVER -"CREATURE:FISH_KNIFEFISH_BANDED:GUT -&CREATURE:FISH_KNIFEFISH_BANDED:STOMACH -&CREATURE:FISH_KNIFEFISH_BANDED:GIZZARD -'CREATURE:FISH_KNIFEFISH_BANDED:PANCREAS -%CREATURE:FISH_KNIFEFISH_BANDED:SPLEEN -%CREATURE:FISH_KNIFEFISH_BANDED:KIDNEY -CREATURE:FISH_CHAR:MUSCLE -CREATURE:FISH_CHAR:EYE -CREATURE:FISH_CHAR:BRAIN -CREATURE:FISH_CHAR:LUNG -CREATURE:FISH_CHAR:HEART -CREATURE:FISH_CHAR:LIVER -CREATURE:FISH_CHAR:GUT -CREATURE:FISH_CHAR:STOMACH -CREATURE:FISH_CHAR:GIZZARD -CREATURE:FISH_CHAR:PANCREAS -CREATURE:FISH_CHAR:SPLEEN -CREATURE:FISH_CHAR:KIDNEY -"CREATURE:FISH_TROUT_RAINBOW:MUSCLE -CREATURE:FISH_TROUT_RAINBOW:EYE -!CREATURE:FISH_TROUT_RAINBOW:BRAIN - CREATURE:FISH_TROUT_RAINBOW:LUNG -!CREATURE:FISH_TROUT_RAINBOW:HEART -!CREATURE:FISH_TROUT_RAINBOW:LIVER -CREATURE:FISH_TROUT_RAINBOW:GUT -#CREATURE:FISH_TROUT_RAINBOW:STOMACH -#CREATURE:FISH_TROUT_RAINBOW:GIZZARD -$CREATURE:FISH_TROUT_RAINBOW:PANCREAS -"CREATURE:FISH_TROUT_RAINBOW:SPLEEN -"CREATURE:FISH_TROUT_RAINBOW:KIDNEY -"CREATURE:FISH_MOLLY_SAILFIN:MUSCLE -CREATURE:FISH_MOLLY_SAILFIN:EYE -!CREATURE:FISH_MOLLY_SAILFIN:BRAIN - CREATURE:FISH_MOLLY_SAILFIN:LUNG -!CREATURE:FISH_MOLLY_SAILFIN:HEART -!CREATURE:FISH_MOLLY_SAILFIN:LIVER -CREATURE:FISH_MOLLY_SAILFIN:GUT -#CREATURE:FISH_MOLLY_SAILFIN:STOMACH -#CREATURE:FISH_MOLLY_SAILFIN:GIZZARD -$CREATURE:FISH_MOLLY_SAILFIN:PANCREAS -"CREATURE:FISH_MOLLY_SAILFIN:SPLEEN -"CREATURE:FISH_MOLLY_SAILFIN:KIDNEY -CREATURE:FISH_GUPPY:MUSCLE -CREATURE:FISH_GUPPY:EYE -CREATURE:FISH_GUPPY:BRAIN -CREATURE:FISH_GUPPY:LUNG -CREATURE:FISH_GUPPY:HEART -CREATURE:FISH_GUPPY:LIVER -CREATURE:FISH_GUPPY:GUT -CREATURE:FISH_GUPPY:STOMACH -CREATURE:FISH_GUPPY:GIZZARD -CREATURE:FISH_GUPPY:PANCREAS -CREATURE:FISH_GUPPY:SPLEEN -CREATURE:FISH_GUPPY:KIDNEY -CREATURE:FISH_PERCH:MUSCLE -CREATURE:FISH_PERCH:EYE -CREATURE:FISH_PERCH:BRAIN -CREATURE:FISH_PERCH:LUNG -CREATURE:FISH_PERCH:HEART -CREATURE:FISH_PERCH:LIVER -CREATURE:FISH_PERCH:GUT -CREATURE:FISH_PERCH:STOMACH -CREATURE:FISH_PERCH:GIZZARD -CREATURE:FISH_PERCH:PANCREAS -CREATURE:FISH_PERCH:SPLEEN -CREATURE:FISH_PERCH:KIDNEY -CREATURE:DWARF:MUSCLE -CREATURE:DWARF:EYE -CREATURE:DWARF:BRAIN -CREATURE:DWARF:LUNG -CREATURE:DWARF:HEART -CREATURE:DWARF:LIVER -CREATURE:DWARF:GUT -CREATURE:DWARF:STOMACH -CREATURE:DWARF:GIZZARD -CREATURE:DWARF:PANCREAS -CREATURE:DWARF:SPLEEN -CREATURE:DWARF:KIDNEY -CREATURE:HUMAN:MUSCLE -CREATURE:HUMAN:EYE -CREATURE:HUMAN:BRAIN -CREATURE:HUMAN:LUNG -CREATURE:HUMAN:HEART -CREATURE:HUMAN:LIVER -CREATURE:HUMAN:GUT -CREATURE:HUMAN:STOMACH -CREATURE:HUMAN:GIZZARD -CREATURE:HUMAN:PANCREAS -CREATURE:HUMAN:SPLEEN -CREATURE:HUMAN:KIDNEY -CREATURE:ELF:MUSCLE -CREATURE:ELF:EYE -CREATURE:ELF:BRAIN -CREATURE:ELF:LUNG -CREATURE:ELF:HEART -CREATURE:ELF:LIVER -CREATURE:ELF:GUT -CREATURE:ELF:STOMACH -CREATURE:ELF:GIZZARD -CREATURE:ELF:PANCREAS -CREATURE:ELF:SPLEEN -CREATURE:ELF:KIDNEY -CREATURE:GOBLIN:MUSCLE -CREATURE:GOBLIN:EYE -CREATURE:GOBLIN:BRAIN -CREATURE:GOBLIN:LUNG -CREATURE:GOBLIN:HEART -CREATURE:GOBLIN:LIVER -CREATURE:GOBLIN:GUT -CREATURE:GOBLIN:STOMACH -CREATURE:GOBLIN:GIZZARD -CREATURE:GOBLIN:PANCREAS -CREATURE:GOBLIN:SPLEEN -CREATURE:GOBLIN:KIDNEY -CREATURE:KOBOLD:MUSCLE -CREATURE:KOBOLD:EYE -CREATURE:KOBOLD:BRAIN -CREATURE:KOBOLD:LUNG -CREATURE:KOBOLD:HEART -CREATURE:KOBOLD:LIVER -CREATURE:KOBOLD:GUT -CREATURE:KOBOLD:STOMACH -CREATURE:KOBOLD:GIZZARD -CREATURE:KOBOLD:PANCREAS -CREATURE:KOBOLD:SPLEEN -CREATURE:KOBOLD:KIDNEY -CREATURE:GREMLIN:MUSCLE -CREATURE:GREMLIN:EYE -CREATURE:GREMLIN:BRAIN -CREATURE:GREMLIN:LUNG -CREATURE:GREMLIN:HEART -CREATURE:GREMLIN:LIVER -CREATURE:GREMLIN:GUT -CREATURE:GREMLIN:STOMACH -CREATURE:GREMLIN:GIZZARD -CREATURE:GREMLIN:PANCREAS -CREATURE:GREMLIN:SPLEEN -CREATURE:GREMLIN:KIDNEY -CREATURE:TROLL:MUSCLE -CREATURE:TROLL:EYE -CREATURE:TROLL:BRAIN -CREATURE:TROLL:LUNG -CREATURE:TROLL:HEART -CREATURE:TROLL:LIVER -CREATURE:TROLL:GUT -CREATURE:TROLL:STOMACH -CREATURE:TROLL:GIZZARD -CREATURE:TROLL:PANCREAS -CREATURE:TROLL:SPLEEN -CREATURE:TROLL:KIDNEY -CREATURE:OGRE:MUSCLE -CREATURE:OGRE:EYE -CREATURE:OGRE:BRAIN -CREATURE:OGRE:LUNG -CREATURE:OGRE:HEART -CREATURE:OGRE:LIVER -CREATURE:OGRE:GUT -CREATURE:OGRE:STOMACH -CREATURE:OGRE:GIZZARD -CREATURE:OGRE:PANCREAS -CREATURE:OGRE:SPLEEN -CREATURE:OGRE:KIDNEY -CREATURE:UNICORN:MUSCLE -CREATURE:UNICORN:EYE -CREATURE:UNICORN:BRAIN -CREATURE:UNICORN:LUNG -CREATURE:UNICORN:HEART -CREATURE:UNICORN:LIVER -CREATURE:UNICORN:GUT -CREATURE:UNICORN:STOMACH -CREATURE:UNICORN:GIZZARD -CREATURE:UNICORN:PANCREAS -CREATURE:UNICORN:SPLEEN -CREATURE:UNICORN:KIDNEY -CREATURE:DRAGON:MUSCLE -CREATURE:DRAGON:EYE -CREATURE:DRAGON:BRAIN -CREATURE:DRAGON:LUNG -CREATURE:DRAGON:HEART -CREATURE:DRAGON:LIVER -CREATURE:DRAGON:GUT -CREATURE:DRAGON:STOMACH -CREATURE:DRAGON:GIZZARD -CREATURE:DRAGON:PANCREAS -CREATURE:DRAGON:SPLEEN -CREATURE:DRAGON:KIDNEY -CREATURE:SATYR:MUSCLE -CREATURE:SATYR:EYE -CREATURE:SATYR:BRAIN -CREATURE:SATYR:LUNG -CREATURE:SATYR:HEART -CREATURE:SATYR:LIVER -CREATURE:SATYR:GUT -CREATURE:SATYR:STOMACH -CREATURE:SATYR:GIZZARD -CREATURE:SATYR:PANCREAS -CREATURE:SATYR:SPLEEN -CREATURE:SATYR:KIDNEY -CREATURE:GIANT:MUSCLE -CREATURE:GIANT:EYE -CREATURE:GIANT:BRAIN -CREATURE:GIANT:LUNG -CREATURE:GIANT:HEART -CREATURE:GIANT:LIVER -CREATURE:GIANT:GUT -CREATURE:GIANT:STOMACH -CREATURE:GIANT:GIZZARD -CREATURE:GIANT:PANCREAS -CREATURE:GIANT:SPLEEN -CREATURE:GIANT:KIDNEY -CREATURE:CYCLOPS:MUSCLE -CREATURE:CYCLOPS:EYE -CREATURE:CYCLOPS:BRAIN -CREATURE:CYCLOPS:LUNG -CREATURE:CYCLOPS:HEART -CREATURE:CYCLOPS:LIVER -CREATURE:CYCLOPS:GUT -CREATURE:CYCLOPS:STOMACH -CREATURE:CYCLOPS:GIZZARD -CREATURE:CYCLOPS:PANCREAS -CREATURE:CYCLOPS:SPLEEN -CREATURE:CYCLOPS:KIDNEY -CREATURE:ETTIN:MUSCLE -CREATURE:ETTIN:EYE -CREATURE:ETTIN:BRAIN -CREATURE:ETTIN:LUNG -CREATURE:ETTIN:HEART -CREATURE:ETTIN:LIVER -CREATURE:ETTIN:GUT -CREATURE:ETTIN:STOMACH -CREATURE:ETTIN:GIZZARD -CREATURE:ETTIN:PANCREAS -CREATURE:ETTIN:SPLEEN -CREATURE:ETTIN:KIDNEY -CREATURE:MINOTAUR:MUSCLE -CREATURE:MINOTAUR:EYE -CREATURE:MINOTAUR:BRAIN -CREATURE:MINOTAUR:LUNG -CREATURE:MINOTAUR:HEART -CREATURE:MINOTAUR:LIVER -CREATURE:MINOTAUR:GUT -CREATURE:MINOTAUR:STOMACH -CREATURE:MINOTAUR:GIZZARD -CREATURE:MINOTAUR:PANCREAS -CREATURE:MINOTAUR:SPLEEN -CREATURE:MINOTAUR:KIDNEY -CREATURE:YETI:MUSCLE -CREATURE:YETI:EYE -CREATURE:YETI:BRAIN -CREATURE:YETI:LUNG -CREATURE:YETI:HEART -CREATURE:YETI:LIVER -CREATURE:YETI:GUT -CREATURE:YETI:STOMACH -CREATURE:YETI:GIZZARD -CREATURE:YETI:PANCREAS -CREATURE:YETI:SPLEEN -CREATURE:YETI:KIDNEY -CREATURE:SASQUATCH:MUSCLE -CREATURE:SASQUATCH:EYE -CREATURE:SASQUATCH:BRAIN -CREATURE:SASQUATCH:LUNG -CREATURE:SASQUATCH:HEART -CREATURE:SASQUATCH:LIVER -CREATURE:SASQUATCH:GUT -CREATURE:SASQUATCH:STOMACH -CREATURE:SASQUATCH:GIZZARD -CREATURE:SASQUATCH:PANCREAS -CREATURE:SASQUATCH:SPLEEN -CREATURE:SASQUATCH:KIDNEY -CREATURE:BLIZZARD_MAN:MUSCLE -CREATURE:BLIZZARD_MAN:EYE -CREATURE:BLIZZARD_MAN:BRAIN -CREATURE:BLIZZARD_MAN:LUNG -CREATURE:BLIZZARD_MAN:HEART -CREATURE:BLIZZARD_MAN:LIVER -CREATURE:BLIZZARD_MAN:GUT -CREATURE:BLIZZARD_MAN:STOMACH -CREATURE:BLIZZARD_MAN:GIZZARD -CREATURE:BLIZZARD_MAN:PANCREAS -CREATURE:BLIZZARD_MAN:SPLEEN -CREATURE:BLIZZARD_MAN:KIDNEY -CREATURE:WOLF_ICE:MUSCLE -CREATURE:WOLF_ICE:EYE -CREATURE:WOLF_ICE:BRAIN -CREATURE:WOLF_ICE:LUNG -CREATURE:WOLF_ICE:HEART -CREATURE:WOLF_ICE:LIVER -CREATURE:WOLF_ICE:GUT -CREATURE:WOLF_ICE:STOMACH -CREATURE:WOLF_ICE:GIZZARD -CREATURE:WOLF_ICE:PANCREAS -CREATURE:WOLF_ICE:SPLEEN -CREATURE:WOLF_ICE:KIDNEY -CREATURE:FAIRY:MUSCLE -CREATURE:FAIRY:EYE -CREATURE:FAIRY:BRAIN -CREATURE:FAIRY:LUNG -CREATURE:FAIRY:HEART -CREATURE:FAIRY:LIVER -CREATURE:FAIRY:GUT -CREATURE:FAIRY:STOMACH -CREATURE:FAIRY:GIZZARD -CREATURE:FAIRY:PANCREAS -CREATURE:FAIRY:SPLEEN -CREATURE:FAIRY:KIDNEY -CREATURE:PIXIE:MUSCLE -CREATURE:PIXIE:EYE -CREATURE:PIXIE:BRAIN -CREATURE:PIXIE:LUNG -CREATURE:PIXIE:HEART -CREATURE:PIXIE:LIVER -CREATURE:PIXIE:GUT -CREATURE:PIXIE:STOMACH -CREATURE:PIXIE:GIZZARD -CREATURE:PIXIE:PANCREAS -CREATURE:PIXIE:SPLEEN -CREATURE:PIXIE:KIDNEY -CREATURE:BEAK_DOG:MUSCLE -CREATURE:BEAK_DOG:EYE -CREATURE:BEAK_DOG:BRAIN -CREATURE:BEAK_DOG:LUNG -CREATURE:BEAK_DOG:HEART -CREATURE:BEAK_DOG:LIVER -CREATURE:BEAK_DOG:GUT -CREATURE:BEAK_DOG:STOMACH -CREATURE:BEAK_DOG:GIZZARD -CREATURE:BEAK_DOG:PANCREAS -CREATURE:BEAK_DOG:SPLEEN -CREATURE:BEAK_DOG:KIDNEY -CREATURE:GRIMELING:MUSCLE -CREATURE:GRIMELING:EYE -CREATURE:GRIMELING:BRAIN -CREATURE:GRIMELING:LUNG -CREATURE:GRIMELING:HEART -CREATURE:GRIMELING:LIVER -CREATURE:GRIMELING:GUT -CREATURE:GRIMELING:STOMACH -CREATURE:GRIMELING:GIZZARD -CREATURE:GRIMELING:PANCREAS -CREATURE:GRIMELING:SPLEEN -CREATURE:GRIMELING:KIDNEY -CREATURE:BLENDEC_FOUL:MUSCLE -CREATURE:BLENDEC_FOUL:EYE -CREATURE:BLENDEC_FOUL:BRAIN -CREATURE:BLENDEC_FOUL:LUNG -CREATURE:BLENDEC_FOUL:HEART -CREATURE:BLENDEC_FOUL:LIVER -CREATURE:BLENDEC_FOUL:GUT -CREATURE:BLENDEC_FOUL:STOMACH -CREATURE:BLENDEC_FOUL:GIZZARD -CREATURE:BLENDEC_FOUL:PANCREAS -CREATURE:BLENDEC_FOUL:SPLEEN -CREATURE:BLENDEC_FOUL:KIDNEY -CREATURE:STRANGLER:MUSCLE -CREATURE:STRANGLER:EYE -CREATURE:STRANGLER:BRAIN -CREATURE:STRANGLER:LUNG -CREATURE:STRANGLER:HEART -CREATURE:STRANGLER:LIVER -CREATURE:STRANGLER:GUT -CREATURE:STRANGLER:STOMACH -CREATURE:STRANGLER:GIZZARD -CREATURE:STRANGLER:PANCREAS -CREATURE:STRANGLER:SPLEEN -CREATURE:STRANGLER:KIDNEY -CREATURE:NIGHTWING:MUSCLE -CREATURE:NIGHTWING:EYE -CREATURE:NIGHTWING:BRAIN -CREATURE:NIGHTWING:LUNG -CREATURE:NIGHTWING:HEART -CREATURE:NIGHTWING:LIVER -CREATURE:NIGHTWING:GUT -CREATURE:NIGHTWING:STOMACH -CREATURE:NIGHTWING:GIZZARD -CREATURE:NIGHTWING:PANCREAS -CREATURE:NIGHTWING:SPLEEN -CREATURE:NIGHTWING:KIDNEY -CREATURE:HARPY:MUSCLE -CREATURE:HARPY:EYE -CREATURE:HARPY:BRAIN -CREATURE:HARPY:LUNG -CREATURE:HARPY:HEART -CREATURE:HARPY:LIVER -CREATURE:HARPY:GUT -CREATURE:HARPY:STOMACH -CREATURE:HARPY:GIZZARD -CREATURE:HARPY:PANCREAS -CREATURE:HARPY:SPLEEN -CREATURE:HARPY:KIDNEY -CREATURE:HYDRA:MUSCLE -CREATURE:HYDRA:EYE -CREATURE:HYDRA:BRAIN -CREATURE:HYDRA:LUNG -CREATURE:HYDRA:HEART -CREATURE:HYDRA:LIVER -CREATURE:HYDRA:GUT -CREATURE:HYDRA:STOMACH -CREATURE:HYDRA:GIZZARD -CREATURE:HYDRA:PANCREAS -CREATURE:HYDRA:SPLEEN -CREATURE:HYDRA:KIDNEY -CREATURE:MERPERSON:MUSCLE -CREATURE:MERPERSON:EYE -CREATURE:MERPERSON:BRAIN -CREATURE:MERPERSON:LUNG -CREATURE:MERPERSON:HEART -CREATURE:MERPERSON:LIVER -CREATURE:MERPERSON:GUT -CREATURE:MERPERSON:STOMACH -CREATURE:MERPERSON:GIZZARD -CREATURE:MERPERSON:PANCREAS -CREATURE:MERPERSON:SPLEEN -CREATURE:MERPERSON:KIDNEY -CREATURE:SEA_SERPENT:MUSCLE -CREATURE:SEA_SERPENT:EYE -CREATURE:SEA_SERPENT:BRAIN -CREATURE:SEA_SERPENT:LUNG -CREATURE:SEA_SERPENT:HEART -CREATURE:SEA_SERPENT:LIVER -CREATURE:SEA_SERPENT:GUT -CREATURE:SEA_SERPENT:STOMACH -CREATURE:SEA_SERPENT:GIZZARD -CREATURE:SEA_SERPENT:PANCREAS -CREATURE:SEA_SERPENT:SPLEEN -CREATURE:SEA_SERPENT:KIDNEY -CREATURE:SEA_MONSTER:MUSCLE -CREATURE:SEA_MONSTER:EYE -CREATURE:SEA_MONSTER:BRAIN -CREATURE:SEA_MONSTER:LUNG -CREATURE:SEA_MONSTER:HEART -CREATURE:SEA_MONSTER:LIVER -CREATURE:SEA_MONSTER:GUT -CREATURE:SEA_MONSTER:STOMACH -CREATURE:SEA_MONSTER:GIZZARD -CREATURE:SEA_MONSTER:PANCREAS -CREATURE:SEA_MONSTER:SPLEEN -CREATURE:SEA_MONSTER:KIDNEY -CREATURE:BIRD_ROC:MUSCLE -CREATURE:BIRD_ROC:EYE -CREATURE:BIRD_ROC:BRAIN -CREATURE:BIRD_ROC:LUNG -CREATURE:BIRD_ROC:HEART -CREATURE:BIRD_ROC:LIVER -CREATURE:BIRD_ROC:GUT -CREATURE:BIRD_ROC:STOMACH -CREATURE:BIRD_ROC:GIZZARD -CREATURE:BIRD_ROC:PANCREAS -CREATURE:BIRD_ROC:SPLEEN -CREATURE:BIRD_ROC:KIDNEY -CREATURE:CROCODILE_CAVE:MUSCLE -CREATURE:CROCODILE_CAVE:EYE -CREATURE:CROCODILE_CAVE:BRAIN -CREATURE:CROCODILE_CAVE:LUNG -CREATURE:CROCODILE_CAVE:HEART -CREATURE:CROCODILE_CAVE:LIVER -CREATURE:CROCODILE_CAVE:GUT -CREATURE:CROCODILE_CAVE:STOMACH -CREATURE:CROCODILE_CAVE:GIZZARD - CREATURE:CROCODILE_CAVE:PANCREAS -CREATURE:CROCODILE_CAVE:SPLEEN -CREATURE:CROCODILE_CAVE:KIDNEY -CREATURE:TOAD_GIANT_CAVE:MUSCLE -CREATURE:TOAD_GIANT_CAVE:EYE -CREATURE:TOAD_GIANT_CAVE:BRAIN -CREATURE:TOAD_GIANT_CAVE:LUNG -CREATURE:TOAD_GIANT_CAVE:HEART -CREATURE:TOAD_GIANT_CAVE:LIVER -CREATURE:TOAD_GIANT_CAVE:GUT - CREATURE:TOAD_GIANT_CAVE:STOMACH - CREATURE:TOAD_GIANT_CAVE:GIZZARD -!CREATURE:TOAD_GIANT_CAVE:PANCREAS -CREATURE:TOAD_GIANT_CAVE:SPLEEN -CREATURE:TOAD_GIANT_CAVE:KIDNEY -CREATURE:OLM_GIANT:MUSCLE -CREATURE:OLM_GIANT:EYE -CREATURE:OLM_GIANT:BRAIN -CREATURE:OLM_GIANT:LUNG -CREATURE:OLM_GIANT:HEART -CREATURE:OLM_GIANT:LIVER -CREATURE:OLM_GIANT:GUT -CREATURE:OLM_GIANT:STOMACH -CREATURE:OLM_GIANT:GIZZARD -CREATURE:OLM_GIANT:PANCREAS -CREATURE:OLM_GIANT:SPLEEN -CREATURE:OLM_GIANT:KIDNEY -CREATURE:BAT_GIANT:MUSCLE -CREATURE:BAT_GIANT:EYE -CREATURE:BAT_GIANT:BRAIN -CREATURE:BAT_GIANT:LUNG -CREATURE:BAT_GIANT:HEART -CREATURE:BAT_GIANT:LIVER -CREATURE:BAT_GIANT:GUT -CREATURE:BAT_GIANT:STOMACH -CREATURE:BAT_GIANT:GIZZARD -CREATURE:BAT_GIANT:PANCREAS -CREATURE:BAT_GIANT:SPLEEN -CREATURE:BAT_GIANT:KIDNEY -CREATURE:RAT_GIANT:MUSCLE -CREATURE:RAT_GIANT:EYE -CREATURE:RAT_GIANT:BRAIN -CREATURE:RAT_GIANT:LUNG -CREATURE:RAT_GIANT:HEART -CREATURE:RAT_GIANT:LIVER -CREATURE:RAT_GIANT:GUT -CREATURE:RAT_GIANT:STOMACH -CREATURE:RAT_GIANT:GIZZARD -CREATURE:RAT_GIANT:PANCREAS -CREATURE:RAT_GIANT:SPLEEN -CREATURE:RAT_GIANT:KIDNEY -CREATURE:RAT_LARGE:MUSCLE -CREATURE:RAT_LARGE:EYE -CREATURE:RAT_LARGE:BRAIN -CREATURE:RAT_LARGE:LUNG -CREATURE:RAT_LARGE:HEART -CREATURE:RAT_LARGE:LIVER -CREATURE:RAT_LARGE:GUT -CREATURE:RAT_LARGE:STOMACH -CREATURE:RAT_LARGE:GIZZARD -CREATURE:RAT_LARGE:PANCREAS -CREATURE:RAT_LARGE:SPLEEN -CREATURE:RAT_LARGE:KIDNEY -CREATURE:MOLE_DOG_NAKED:MUSCLE -CREATURE:MOLE_DOG_NAKED:EYE -CREATURE:MOLE_DOG_NAKED:BRAIN -CREATURE:MOLE_DOG_NAKED:LUNG -CREATURE:MOLE_DOG_NAKED:HEART -CREATURE:MOLE_DOG_NAKED:LIVER -CREATURE:MOLE_DOG_NAKED:GUT -CREATURE:MOLE_DOG_NAKED:STOMACH -CREATURE:MOLE_DOG_NAKED:GIZZARD - CREATURE:MOLE_DOG_NAKED:PANCREAS -CREATURE:MOLE_DOG_NAKED:SPLEEN -CREATURE:MOLE_DOG_NAKED:KIDNEY -CREATURE:TROGLODYTE:MUSCLE -CREATURE:TROGLODYTE:EYE -CREATURE:TROGLODYTE:BRAIN -CREATURE:TROGLODYTE:LUNG -CREATURE:TROGLODYTE:HEART -CREATURE:TROGLODYTE:LIVER -CREATURE:TROGLODYTE:GUT -CREATURE:TROGLODYTE:STOMACH -CREATURE:TROGLODYTE:GIZZARD -CREATURE:TROGLODYTE:PANCREAS -CREATURE:TROGLODYTE:SPLEEN -CREATURE:TROGLODYTE:KIDNEY -CREATURE:MOLE_GIANT:MUSCLE -CREATURE:MOLE_GIANT:EYE -CREATURE:MOLE_GIANT:BRAIN -CREATURE:MOLE_GIANT:LUNG -CREATURE:MOLE_GIANT:HEART -CREATURE:MOLE_GIANT:LIVER -CREATURE:MOLE_GIANT:GUT -CREATURE:MOLE_GIANT:STOMACH -CREATURE:MOLE_GIANT:GIZZARD -CREATURE:MOLE_GIANT:PANCREAS -CREATURE:MOLE_GIANT:SPLEEN -CREATURE:MOLE_GIANT:KIDNEY -CREATURE:IMP_FIRE:MUSCLE -CREATURE:IMP_FIRE:EYE -CREATURE:IMP_FIRE:BRAIN -CREATURE:IMP_FIRE:LUNG -CREATURE:IMP_FIRE:HEART -CREATURE:IMP_FIRE:LIVER -CREATURE:IMP_FIRE:GUT -CREATURE:IMP_FIRE:STOMACH -CREATURE:IMP_FIRE:GIZZARD -CREATURE:IMP_FIRE:PANCREAS -CREATURE:IMP_FIRE:SPLEEN -CREATURE:IMP_FIRE:KIDNEY -!CREATURE:SPIDER_CAVE_GIANT:MUSCLE -CREATURE:SPIDER_CAVE_GIANT:EYE - CREATURE:SPIDER_CAVE_GIANT:BRAIN -CREATURE:SPIDER_CAVE_GIANT:LUNG - CREATURE:SPIDER_CAVE_GIANT:HEART - CREATURE:SPIDER_CAVE_GIANT:LIVER -CREATURE:SPIDER_CAVE_GIANT:GUT -"CREATURE:SPIDER_CAVE_GIANT:STOMACH -"CREATURE:SPIDER_CAVE_GIANT:GIZZARD -#CREATURE:SPIDER_CAVE_GIANT:PANCREAS -!CREATURE:SPIDER_CAVE_GIANT:SPLEEN -!CREATURE:SPIDER_CAVE_GIANT:KIDNEY -CREATURE:SPIDER_CAVE:MUSCLE -CREATURE:SPIDER_CAVE:EYE -CREATURE:SPIDER_CAVE:BRAIN -CREATURE:SPIDER_CAVE:LUNG -CREATURE:SPIDER_CAVE:HEART -CREATURE:SPIDER_CAVE:LIVER -CREATURE:SPIDER_CAVE:GUT -CREATURE:SPIDER_CAVE:STOMACH -CREATURE:SPIDER_CAVE:GIZZARD -CREATURE:SPIDER_CAVE:PANCREAS -CREATURE:SPIDER_CAVE:SPLEEN -CREATURE:SPIDER_CAVE:KIDNEY -CREATURE:FISH_CAVE:MUSCLE -CREATURE:FISH_CAVE:BRAIN -CREATURE:FISH_CAVE:LUNG -CREATURE:FISH_CAVE:HEART -CREATURE:FISH_CAVE:LIVER -CREATURE:FISH_CAVE:GUT -CREATURE:FISH_CAVE:STOMACH -CREATURE:FISH_CAVE:GIZZARD -CREATURE:FISH_CAVE:PANCREAS -CREATURE:FISH_CAVE:SPLEEN -CREATURE:FISH_CAVE:KIDNEY -CREATURE:CAVE_FISH_MAN:MUSCLE -CREATURE:CAVE_FISH_MAN:BRAIN -CREATURE:CAVE_FISH_MAN:LUNG -CREATURE:CAVE_FISH_MAN:HEART -CREATURE:CAVE_FISH_MAN:LIVER -CREATURE:CAVE_FISH_MAN:GUT -CREATURE:CAVE_FISH_MAN:STOMACH -CREATURE:CAVE_FISH_MAN:GIZZARD -CREATURE:CAVE_FISH_MAN:PANCREAS -CREATURE:CAVE_FISH_MAN:SPLEEN -CREATURE:CAVE_FISH_MAN:KIDNEY -CREATURE:LOBSTER_CAVE:MUSCLE -CREATURE:LOBSTER_CAVE:EYE -CREATURE:LOBSTER_CAVE:BRAIN -CREATURE:LOBSTER_CAVE:LUNG -CREATURE:LOBSTER_CAVE:HEART -CREATURE:LOBSTER_CAVE:LIVER -CREATURE:LOBSTER_CAVE:GUT -CREATURE:LOBSTER_CAVE:STOMACH -CREATURE:LOBSTER_CAVE:GIZZARD -CREATURE:LOBSTER_CAVE:PANCREAS -CREATURE:LOBSTER_CAVE:SPLEEN -CREATURE:LOBSTER_CAVE:KIDNEY -CREATURE:OLM:MUSCLE -CREATURE:OLM:EYE -CREATURE:OLM:BRAIN -CREATURE:OLM:LUNG -CREATURE:OLM:HEART -CREATURE:OLM:LIVER -CREATURE:OLM:GUT -CREATURE:OLM:STOMACH -CREATURE:OLM:GIZZARD -CREATURE:OLM:PANCREAS -CREATURE:OLM:SPLEEN -CREATURE:OLM:KIDNEY -CREATURE:OLM_MAN:MUSCLE -CREATURE:OLM_MAN:EYE -CREATURE:OLM_MAN:BRAIN -CREATURE:OLM_MAN:LUNG -CREATURE:OLM_MAN:HEART -CREATURE:OLM_MAN:LIVER -CREATURE:OLM_MAN:GUT -CREATURE:OLM_MAN:STOMACH -CREATURE:OLM_MAN:GIZZARD -CREATURE:OLM_MAN:PANCREAS -CREATURE:OLM_MAN:SPLEEN -CREATURE:OLM_MAN:KIDNEY -CREATURE:BAT:MUSCLE -CREATURE:BAT:EYE -CREATURE:BAT:BRAIN -CREATURE:BAT:LUNG -CREATURE:BAT:HEART -CREATURE:BAT:LIVER -CREATURE:BAT:GUT -CREATURE:BAT:STOMACH -CREATURE:BAT:GIZZARD -CREATURE:BAT:PANCREAS -CREATURE:BAT:SPLEEN -CREATURE:BAT:KIDNEY -CREATURE:BAT_MAN:MUSCLE -CREATURE:BAT_MAN:EYE -CREATURE:BAT_MAN:BRAIN -CREATURE:BAT_MAN:LUNG -CREATURE:BAT_MAN:HEART -CREATURE:BAT_MAN:LIVER -CREATURE:BAT_MAN:GUT -CREATURE:BAT_MAN:STOMACH -CREATURE:BAT_MAN:GIZZARD -CREATURE:BAT_MAN:PANCREAS -CREATURE:BAT_MAN:SPLEEN -CREATURE:BAT_MAN:KIDNEY -CREATURE:MAGGOT_PURRING:MUSCLE -CREATURE:MAGGOT_PURRING:EYE -CREATURE:MAGGOT_PURRING:BRAIN -CREATURE:MAGGOT_PURRING:LUNG -CREATURE:MAGGOT_PURRING:HEART -CREATURE:MAGGOT_PURRING:LIVER -CREATURE:MAGGOT_PURRING:GUT -CREATURE:MAGGOT_PURRING:STOMACH -CREATURE:MAGGOT_PURRING:GIZZARD - CREATURE:MAGGOT_PURRING:PANCREAS -CREATURE:MAGGOT_PURRING:SPLEEN -CREATURE:MAGGOT_PURRING:KIDNEY -!CREATURE:BIRD_SWALLOW_CAVE:MUSCLE -CREATURE:BIRD_SWALLOW_CAVE:EYE - CREATURE:BIRD_SWALLOW_CAVE:BRAIN -CREATURE:BIRD_SWALLOW_CAVE:LUNG - CREATURE:BIRD_SWALLOW_CAVE:HEART - CREATURE:BIRD_SWALLOW_CAVE:LIVER -CREATURE:BIRD_SWALLOW_CAVE:GUT -"CREATURE:BIRD_SWALLOW_CAVE:STOMACH -"CREATURE:BIRD_SWALLOW_CAVE:GIZZARD -#CREATURE:BIRD_SWALLOW_CAVE:PANCREAS -!CREATURE:BIRD_SWALLOW_CAVE:SPLEEN -!CREATURE:BIRD_SWALLOW_CAVE:KIDNEY - CREATURE:CAVE_SWALLOW_MAN:MUSCLE -CREATURE:CAVE_SWALLOW_MAN:EYE -CREATURE:CAVE_SWALLOW_MAN:BRAIN -CREATURE:CAVE_SWALLOW_MAN:LUNG -CREATURE:CAVE_SWALLOW_MAN:HEART -CREATURE:CAVE_SWALLOW_MAN:LIVER -CREATURE:CAVE_SWALLOW_MAN:GUT -!CREATURE:CAVE_SWALLOW_MAN:STOMACH -!CREATURE:CAVE_SWALLOW_MAN:GIZZARD -"CREATURE:CAVE_SWALLOW_MAN:PANCREAS - CREATURE:CAVE_SWALLOW_MAN:SPLEEN - CREATURE:CAVE_SWALLOW_MAN:KIDNEY -'CREATURE:BIRD_SWALLOW_CAVE_GIANT:MUSCLE -$CREATURE:BIRD_SWALLOW_CAVE_GIANT:EYE -&CREATURE:BIRD_SWALLOW_CAVE_GIANT:BRAIN -%CREATURE:BIRD_SWALLOW_CAVE_GIANT:LUNG -&CREATURE:BIRD_SWALLOW_CAVE_GIANT:HEART -&CREATURE:BIRD_SWALLOW_CAVE_GIANT:LIVER -$CREATURE:BIRD_SWALLOW_CAVE_GIANT:GUT -(CREATURE:BIRD_SWALLOW_CAVE_GIANT:STOMACH -(CREATURE:BIRD_SWALLOW_CAVE_GIANT:GIZZARD -)CREATURE:BIRD_SWALLOW_CAVE_GIANT:PANCREAS -'CREATURE:BIRD_SWALLOW_CAVE_GIANT:SPLEEN -'CREATURE:BIRD_SWALLOW_CAVE_GIANT:KIDNEY -CREATURE:AMPHIBIAN_MAN:MUSCLE -CREATURE:AMPHIBIAN_MAN:EYE -CREATURE:AMPHIBIAN_MAN:BRAIN -CREATURE:AMPHIBIAN_MAN:LUNG -CREATURE:AMPHIBIAN_MAN:HEART -CREATURE:AMPHIBIAN_MAN:LIVER -CREATURE:AMPHIBIAN_MAN:GUT -CREATURE:AMPHIBIAN_MAN:STOMACH -CREATURE:AMPHIBIAN_MAN:GIZZARD -CREATURE:AMPHIBIAN_MAN:PANCREAS -CREATURE:AMPHIBIAN_MAN:SPLEEN -CREATURE:AMPHIBIAN_MAN:KIDNEY -CREATURE:REPTILE_MAN:MUSCLE -CREATURE:REPTILE_MAN:EYE -CREATURE:REPTILE_MAN:BRAIN -CREATURE:REPTILE_MAN:LUNG -CREATURE:REPTILE_MAN:HEART -CREATURE:REPTILE_MAN:LIVER -CREATURE:REPTILE_MAN:GUT -CREATURE:REPTILE_MAN:STOMACH -CREATURE:REPTILE_MAN:GIZZARD -CREATURE:REPTILE_MAN:PANCREAS -CREATURE:REPTILE_MAN:SPLEEN -CREATURE:REPTILE_MAN:KIDNEY -CREATURE:SERPENT_MAN:MUSCLE -CREATURE:SERPENT_MAN:EYE -CREATURE:SERPENT_MAN:BRAIN -CREATURE:SERPENT_MAN:LUNG -CREATURE:SERPENT_MAN:HEART -CREATURE:SERPENT_MAN:LIVER -CREATURE:SERPENT_MAN:GUT -CREATURE:SERPENT_MAN:STOMACH -CREATURE:SERPENT_MAN:GIZZARD -CREATURE:SERPENT_MAN:PANCREAS -CREATURE:SERPENT_MAN:SPLEEN -CREATURE:SERPENT_MAN:KIDNEY -CREATURE:ANT_MAN:MUSCLE -CREATURE:ANT_MAN:EYE -CREATURE:ANT_MAN:BRAIN -CREATURE:ANT_MAN:LUNG -CREATURE:ANT_MAN:HEART -CREATURE:ANT_MAN:LIVER -CREATURE:ANT_MAN:GUT -CREATURE:ANT_MAN:STOMACH -CREATURE:ANT_MAN:GIZZARD -CREATURE:ANT_MAN:PANCREAS -CREATURE:ANT_MAN:SPLEEN -CREATURE:ANT_MAN:KIDNEY -CREATURE:RODENT MAN:MUSCLE -CREATURE:RODENT MAN:EYE -CREATURE:RODENT MAN:BRAIN -CREATURE:RODENT MAN:LUNG -CREATURE:RODENT MAN:HEART -CREATURE:RODENT MAN:LIVER -CREATURE:RODENT MAN:GUT -CREATURE:RODENT MAN:STOMACH -CREATURE:RODENT MAN:GIZZARD -CREATURE:RODENT MAN:PANCREAS -CREATURE:RODENT MAN:SPLEEN -CREATURE:RODENT MAN:KIDNEY -CREATURE:WILD_BOAR:MUSCLE -CREATURE:WILD_BOAR:EYE -CREATURE:WILD_BOAR:BRAIN -CREATURE:WILD_BOAR:LUNG -CREATURE:WILD_BOAR:HEART -CREATURE:WILD_BOAR:LIVER -CREATURE:WILD_BOAR:GUT -CREATURE:WILD_BOAR:STOMACH -CREATURE:WILD_BOAR:GIZZARD -CREATURE:WILD_BOAR:PANCREAS -CREATURE:WILD_BOAR:SPLEEN -CREATURE:WILD_BOAR:KIDNEY -CREATURE:WILD_BOAR_MAN:MUSCLE -CREATURE:WILD_BOAR_MAN:EYE -CREATURE:WILD_BOAR_MAN:BRAIN -CREATURE:WILD_BOAR_MAN:LUNG -CREATURE:WILD_BOAR_MAN:HEART -CREATURE:WILD_BOAR_MAN:LIVER -CREATURE:WILD_BOAR_MAN:GUT -CREATURE:WILD_BOAR_MAN:STOMACH -CREATURE:WILD_BOAR_MAN:GIZZARD -CREATURE:WILD_BOAR_MAN:PANCREAS -CREATURE:WILD_BOAR_MAN:SPLEEN -CREATURE:WILD_BOAR_MAN:KIDNEY -CREATURE:GIANT_WILD_BOAR:MUSCLE -CREATURE:GIANT_WILD_BOAR:EYE -CREATURE:GIANT_WILD_BOAR:BRAIN -CREATURE:GIANT_WILD_BOAR:LUNG -CREATURE:GIANT_WILD_BOAR:HEART -CREATURE:GIANT_WILD_BOAR:LIVER -CREATURE:GIANT_WILD_BOAR:GUT - CREATURE:GIANT_WILD_BOAR:STOMACH - CREATURE:GIANT_WILD_BOAR:GIZZARD -!CREATURE:GIANT_WILD_BOAR:PANCREAS -CREATURE:GIANT_WILD_BOAR:SPLEEN -CREATURE:GIANT_WILD_BOAR:KIDNEY -CREATURE:COYOTE:MUSCLE -CREATURE:COYOTE:EYE -CREATURE:COYOTE:BRAIN -CREATURE:COYOTE:LUNG -CREATURE:COYOTE:HEART -CREATURE:COYOTE:LIVER -CREATURE:COYOTE:GUT -CREATURE:COYOTE:STOMACH -CREATURE:COYOTE:GIZZARD -CREATURE:COYOTE:PANCREAS -CREATURE:COYOTE:SPLEEN -CREATURE:COYOTE:KIDNEY -CREATURE:COYOTE_MAN:MUSCLE -CREATURE:COYOTE_MAN:EYE -CREATURE:COYOTE_MAN:BRAIN -CREATURE:COYOTE_MAN:LUNG -CREATURE:COYOTE_MAN:HEART -CREATURE:COYOTE_MAN:LIVER -CREATURE:COYOTE_MAN:GUT -CREATURE:COYOTE_MAN:STOMACH -CREATURE:COYOTE_MAN:GIZZARD -CREATURE:COYOTE_MAN:PANCREAS -CREATURE:COYOTE_MAN:SPLEEN -CREATURE:COYOTE_MAN:KIDNEY -CREATURE:GIANT_COYOTE:MUSCLE -CREATURE:GIANT_COYOTE:EYE -CREATURE:GIANT_COYOTE:BRAIN -CREATURE:GIANT_COYOTE:LUNG -CREATURE:GIANT_COYOTE:HEART -CREATURE:GIANT_COYOTE:LIVER -CREATURE:GIANT_COYOTE:GUT -CREATURE:GIANT_COYOTE:STOMACH -CREATURE:GIANT_COYOTE:GIZZARD -CREATURE:GIANT_COYOTE:PANCREAS -CREATURE:GIANT_COYOTE:SPLEEN -CREATURE:GIANT_COYOTE:KIDNEY -CREATURE:KANGAROO:MUSCLE -CREATURE:KANGAROO:EYE -CREATURE:KANGAROO:BRAIN -CREATURE:KANGAROO:LUNG -CREATURE:KANGAROO:HEART -CREATURE:KANGAROO:LIVER -CREATURE:KANGAROO:GUT -CREATURE:KANGAROO:STOMACH -CREATURE:KANGAROO:GIZZARD -CREATURE:KANGAROO:PANCREAS -CREATURE:KANGAROO:SPLEEN -CREATURE:KANGAROO:KIDNEY -CREATURE:KANGAROO_MAN:MUSCLE -CREATURE:KANGAROO_MAN:EYE -CREATURE:KANGAROO_MAN:BRAIN -CREATURE:KANGAROO_MAN:LUNG -CREATURE:KANGAROO_MAN:HEART -CREATURE:KANGAROO_MAN:LIVER -CREATURE:KANGAROO_MAN:GUT -CREATURE:KANGAROO_MAN:STOMACH -CREATURE:KANGAROO_MAN:GIZZARD -CREATURE:KANGAROO_MAN:PANCREAS -CREATURE:KANGAROO_MAN:SPLEEN -CREATURE:KANGAROO_MAN:KIDNEY -CREATURE:GIANT_KANGAROO:MUSCLE -CREATURE:GIANT_KANGAROO:EYE -CREATURE:GIANT_KANGAROO:BRAIN -CREATURE:GIANT_KANGAROO:LUNG -CREATURE:GIANT_KANGAROO:HEART -CREATURE:GIANT_KANGAROO:LIVER -CREATURE:GIANT_KANGAROO:GUT -CREATURE:GIANT_KANGAROO:STOMACH -CREATURE:GIANT_KANGAROO:GIZZARD - CREATURE:GIANT_KANGAROO:PANCREAS -CREATURE:GIANT_KANGAROO:SPLEEN -CREATURE:GIANT_KANGAROO:KIDNEY -CREATURE:KOALA:MUSCLE -CREATURE:KOALA:EYE -CREATURE:KOALA:BRAIN -CREATURE:KOALA:LUNG -CREATURE:KOALA:HEART -CREATURE:KOALA:LIVER -CREATURE:KOALA:GUT -CREATURE:KOALA:STOMACH -CREATURE:KOALA:GIZZARD -CREATURE:KOALA:PANCREAS -CREATURE:KOALA:SPLEEN -CREATURE:KOALA:KIDNEY -CREATURE:KOALA_MAN:MUSCLE -CREATURE:KOALA_MAN:EYE -CREATURE:KOALA_MAN:BRAIN -CREATURE:KOALA_MAN:LUNG -CREATURE:KOALA_MAN:HEART -CREATURE:KOALA_MAN:LIVER -CREATURE:KOALA_MAN:GUT -CREATURE:KOALA_MAN:STOMACH -CREATURE:KOALA_MAN:GIZZARD -CREATURE:KOALA_MAN:PANCREAS -CREATURE:KOALA_MAN:SPLEEN -CREATURE:KOALA_MAN:KIDNEY -CREATURE:GIANT_KOALA:MUSCLE -CREATURE:GIANT_KOALA:EYE -CREATURE:GIANT_KOALA:BRAIN -CREATURE:GIANT_KOALA:LUNG -CREATURE:GIANT_KOALA:HEART -CREATURE:GIANT_KOALA:LIVER -CREATURE:GIANT_KOALA:GUT -CREATURE:GIANT_KOALA:STOMACH -CREATURE:GIANT_KOALA:GIZZARD -CREATURE:GIANT_KOALA:PANCREAS -CREATURE:GIANT_KOALA:SPLEEN -CREATURE:GIANT_KOALA:KIDNEY -CREATURE:ADDER:MUSCLE -CREATURE:ADDER:EYE -CREATURE:ADDER:BRAIN -CREATURE:ADDER:LUNG -CREATURE:ADDER:HEART -CREATURE:ADDER:LIVER -CREATURE:ADDER:GUT -CREATURE:ADDER:STOMACH -CREATURE:ADDER:GIZZARD -CREATURE:ADDER:PANCREAS -CREATURE:ADDER:SPLEEN -CREATURE:ADDER:KIDNEY -CREATURE:ADDER_MAN:MUSCLE -CREATURE:ADDER_MAN:EYE -CREATURE:ADDER_MAN:BRAIN -CREATURE:ADDER_MAN:LUNG -CREATURE:ADDER_MAN:HEART -CREATURE:ADDER_MAN:LIVER -CREATURE:ADDER_MAN:GUT -CREATURE:ADDER_MAN:STOMACH -CREATURE:ADDER_MAN:GIZZARD -CREATURE:ADDER_MAN:PANCREAS -CREATURE:ADDER_MAN:SPLEEN -CREATURE:ADDER_MAN:KIDNEY -CREATURE:GIANT_ADDER:MUSCLE -CREATURE:GIANT_ADDER:EYE -CREATURE:GIANT_ADDER:BRAIN -CREATURE:GIANT_ADDER:LUNG -CREATURE:GIANT_ADDER:HEART -CREATURE:GIANT_ADDER:LIVER -CREATURE:GIANT_ADDER:GUT -CREATURE:GIANT_ADDER:STOMACH -CREATURE:GIANT_ADDER:GIZZARD -CREATURE:GIANT_ADDER:PANCREAS -CREATURE:GIANT_ADDER:SPLEEN -CREATURE:GIANT_ADDER:KIDNEY -CREATURE:ECHIDNA:MUSCLE -CREATURE:ECHIDNA:EYE -CREATURE:ECHIDNA:BRAIN -CREATURE:ECHIDNA:LUNG -CREATURE:ECHIDNA:HEART -CREATURE:ECHIDNA:LIVER -CREATURE:ECHIDNA:GUT -CREATURE:ECHIDNA:STOMACH -CREATURE:ECHIDNA:GIZZARD -CREATURE:ECHIDNA:PANCREAS -CREATURE:ECHIDNA:SPLEEN -CREATURE:ECHIDNA:KIDNEY -CREATURE:ECHIDNA_MAN:MUSCLE -CREATURE:ECHIDNA_MAN:EYE -CREATURE:ECHIDNA_MAN:BRAIN -CREATURE:ECHIDNA_MAN:LUNG -CREATURE:ECHIDNA_MAN:HEART -CREATURE:ECHIDNA_MAN:LIVER -CREATURE:ECHIDNA_MAN:GUT -CREATURE:ECHIDNA_MAN:STOMACH -CREATURE:ECHIDNA_MAN:GIZZARD -CREATURE:ECHIDNA_MAN:PANCREAS -CREATURE:ECHIDNA_MAN:SPLEEN -CREATURE:ECHIDNA_MAN:KIDNEY -CREATURE:GIANT_ECHIDNA:MUSCLE -CREATURE:GIANT_ECHIDNA:EYE -CREATURE:GIANT_ECHIDNA:BRAIN -CREATURE:GIANT_ECHIDNA:LUNG -CREATURE:GIANT_ECHIDNA:HEART -CREATURE:GIANT_ECHIDNA:LIVER -CREATURE:GIANT_ECHIDNA:GUT -CREATURE:GIANT_ECHIDNA:STOMACH -CREATURE:GIANT_ECHIDNA:GIZZARD -CREATURE:GIANT_ECHIDNA:PANCREAS -CREATURE:GIANT_ECHIDNA:SPLEEN -CREATURE:GIANT_ECHIDNA:KIDNEY -CREATURE:PORCUPINE:MUSCLE -CREATURE:PORCUPINE:EYE -CREATURE:PORCUPINE:BRAIN -CREATURE:PORCUPINE:LUNG -CREATURE:PORCUPINE:HEART -CREATURE:PORCUPINE:LIVER -CREATURE:PORCUPINE:GUT -CREATURE:PORCUPINE:STOMACH -CREATURE:PORCUPINE:GIZZARD -CREATURE:PORCUPINE:PANCREAS -CREATURE:PORCUPINE:SPLEEN -CREATURE:PORCUPINE:KIDNEY -CREATURE:PORCUPINE_MAN:MUSCLE -CREATURE:PORCUPINE_MAN:EYE -CREATURE:PORCUPINE_MAN:BRAIN -CREATURE:PORCUPINE_MAN:LUNG -CREATURE:PORCUPINE_MAN:HEART -CREATURE:PORCUPINE_MAN:LIVER -CREATURE:PORCUPINE_MAN:GUT -CREATURE:PORCUPINE_MAN:STOMACH -CREATURE:PORCUPINE_MAN:GIZZARD -CREATURE:PORCUPINE_MAN:PANCREAS -CREATURE:PORCUPINE_MAN:SPLEEN -CREATURE:PORCUPINE_MAN:KIDNEY -CREATURE:GIANT_PORCUPINE:MUSCLE -CREATURE:GIANT_PORCUPINE:EYE -CREATURE:GIANT_PORCUPINE:BRAIN -CREATURE:GIANT_PORCUPINE:LUNG -CREATURE:GIANT_PORCUPINE:HEART -CREATURE:GIANT_PORCUPINE:LIVER -CREATURE:GIANT_PORCUPINE:GUT - CREATURE:GIANT_PORCUPINE:STOMACH - CREATURE:GIANT_PORCUPINE:GIZZARD -!CREATURE:GIANT_PORCUPINE:PANCREAS -CREATURE:GIANT_PORCUPINE:SPLEEN -CREATURE:GIANT_PORCUPINE:KIDNEY -CREATURE:KINGSNAKE:MUSCLE -CREATURE:KINGSNAKE:EYE -CREATURE:KINGSNAKE:BRAIN -CREATURE:KINGSNAKE:LUNG -CREATURE:KINGSNAKE:HEART -CREATURE:KINGSNAKE:LIVER -CREATURE:KINGSNAKE:GUT -CREATURE:KINGSNAKE:STOMACH -CREATURE:KINGSNAKE:GIZZARD -CREATURE:KINGSNAKE:PANCREAS -CREATURE:KINGSNAKE:SPLEEN -CREATURE:KINGSNAKE:KIDNEY -CREATURE:KINGSNAKE_MAN:MUSCLE -CREATURE:KINGSNAKE_MAN:EYE -CREATURE:KINGSNAKE_MAN:BRAIN -CREATURE:KINGSNAKE_MAN:LUNG -CREATURE:KINGSNAKE_MAN:HEART -CREATURE:KINGSNAKE_MAN:LIVER -CREATURE:KINGSNAKE_MAN:GUT -CREATURE:KINGSNAKE_MAN:STOMACH -CREATURE:KINGSNAKE_MAN:GIZZARD -CREATURE:KINGSNAKE_MAN:PANCREAS -CREATURE:KINGSNAKE_MAN:SPLEEN -CREATURE:KINGSNAKE_MAN:KIDNEY -CREATURE:GIANT_KINGSNAKE:MUSCLE -CREATURE:GIANT_KINGSNAKE:EYE -CREATURE:GIANT_KINGSNAKE:BRAIN -CREATURE:GIANT_KINGSNAKE:LUNG -CREATURE:GIANT_KINGSNAKE:HEART -CREATURE:GIANT_KINGSNAKE:LIVER -CREATURE:GIANT_KINGSNAKE:GUT - CREATURE:GIANT_KINGSNAKE:STOMACH - CREATURE:GIANT_KINGSNAKE:GIZZARD -!CREATURE:GIANT_KINGSNAKE:PANCREAS -CREATURE:GIANT_KINGSNAKE:SPLEEN -CREATURE:GIANT_KINGSNAKE:KIDNEY -CREATURE:GRAY_LANGUR:MUSCLE -CREATURE:GRAY_LANGUR:EYE -CREATURE:GRAY_LANGUR:BRAIN -CREATURE:GRAY_LANGUR:LUNG -CREATURE:GRAY_LANGUR:HEART -CREATURE:GRAY_LANGUR:LIVER -CREATURE:GRAY_LANGUR:GUT -CREATURE:GRAY_LANGUR:STOMACH -CREATURE:GRAY_LANGUR:GIZZARD -CREATURE:GRAY_LANGUR:PANCREAS -CREATURE:GRAY_LANGUR:SPLEEN -CREATURE:GRAY_LANGUR:KIDNEY -CREATURE:GRAY_LANGUR_MAN:MUSCLE -CREATURE:GRAY_LANGUR_MAN:EYE -CREATURE:GRAY_LANGUR_MAN:BRAIN -CREATURE:GRAY_LANGUR_MAN:LUNG -CREATURE:GRAY_LANGUR_MAN:HEART -CREATURE:GRAY_LANGUR_MAN:LIVER -CREATURE:GRAY_LANGUR_MAN:GUT - CREATURE:GRAY_LANGUR_MAN:STOMACH - CREATURE:GRAY_LANGUR_MAN:GIZZARD -!CREATURE:GRAY_LANGUR_MAN:PANCREAS -CREATURE:GRAY_LANGUR_MAN:SPLEEN -CREATURE:GRAY_LANGUR_MAN:KIDNEY -!CREATURE:GIANT_GRAY_LANGUR:MUSCLE -CREATURE:GIANT_GRAY_LANGUR:EYE - CREATURE:GIANT_GRAY_LANGUR:BRAIN -CREATURE:GIANT_GRAY_LANGUR:LUNG - CREATURE:GIANT_GRAY_LANGUR:HEART - CREATURE:GIANT_GRAY_LANGUR:LIVER -CREATURE:GIANT_GRAY_LANGUR:GUT -"CREATURE:GIANT_GRAY_LANGUR:STOMACH -"CREATURE:GIANT_GRAY_LANGUR:GIZZARD -#CREATURE:GIANT_GRAY_LANGUR:PANCREAS -!CREATURE:GIANT_GRAY_LANGUR:SPLEEN -!CREATURE:GIANT_GRAY_LANGUR:KIDNEY -CREATURE:BOBCAT:MUSCLE -CREATURE:BOBCAT:EYE -CREATURE:BOBCAT:BRAIN -CREATURE:BOBCAT:LUNG -CREATURE:BOBCAT:HEART -CREATURE:BOBCAT:LIVER -CREATURE:BOBCAT:GUT -CREATURE:BOBCAT:STOMACH -CREATURE:BOBCAT:GIZZARD -CREATURE:BOBCAT:PANCREAS -CREATURE:BOBCAT:SPLEEN -CREATURE:BOBCAT:KIDNEY -CREATURE:BOBCAT_MAN:MUSCLE -CREATURE:BOBCAT_MAN:EYE -CREATURE:BOBCAT_MAN:BRAIN -CREATURE:BOBCAT_MAN:LUNG -CREATURE:BOBCAT_MAN:HEART -CREATURE:BOBCAT_MAN:LIVER -CREATURE:BOBCAT_MAN:GUT -CREATURE:BOBCAT_MAN:STOMACH -CREATURE:BOBCAT_MAN:GIZZARD -CREATURE:BOBCAT_MAN:PANCREAS -CREATURE:BOBCAT_MAN:SPLEEN -CREATURE:BOBCAT_MAN:KIDNEY -CREATURE:GIANT_BOBCAT:MUSCLE -CREATURE:GIANT_BOBCAT:EYE -CREATURE:GIANT_BOBCAT:BRAIN -CREATURE:GIANT_BOBCAT:LUNG -CREATURE:GIANT_BOBCAT:HEART -CREATURE:GIANT_BOBCAT:LIVER -CREATURE:GIANT_BOBCAT:GUT -CREATURE:GIANT_BOBCAT:STOMACH -CREATURE:GIANT_BOBCAT:GIZZARD -CREATURE:GIANT_BOBCAT:PANCREAS -CREATURE:GIANT_BOBCAT:SPLEEN -CREATURE:GIANT_BOBCAT:KIDNEY -CREATURE:SKUNK:MUSCLE -CREATURE:SKUNK:EYE -CREATURE:SKUNK:BRAIN -CREATURE:SKUNK:LUNG -CREATURE:SKUNK:HEART -CREATURE:SKUNK:LIVER -CREATURE:SKUNK:GUT -CREATURE:SKUNK:STOMACH -CREATURE:SKUNK:GIZZARD -CREATURE:SKUNK:PANCREAS -CREATURE:SKUNK:SPLEEN -CREATURE:SKUNK:KIDNEY -CREATURE:SKUNK_MAN:MUSCLE -CREATURE:SKUNK_MAN:EYE -CREATURE:SKUNK_MAN:BRAIN -CREATURE:SKUNK_MAN:LUNG -CREATURE:SKUNK_MAN:HEART -CREATURE:SKUNK_MAN:LIVER -CREATURE:SKUNK_MAN:GUT -CREATURE:SKUNK_MAN:STOMACH -CREATURE:SKUNK_MAN:GIZZARD -CREATURE:SKUNK_MAN:PANCREAS -CREATURE:SKUNK_MAN:SPLEEN -CREATURE:SKUNK_MAN:KIDNEY -CREATURE:GIANT_SKUNK:MUSCLE -CREATURE:GIANT_SKUNK:EYE -CREATURE:GIANT_SKUNK:BRAIN -CREATURE:GIANT_SKUNK:LUNG -CREATURE:GIANT_SKUNK:HEART -CREATURE:GIANT_SKUNK:LIVER -CREATURE:GIANT_SKUNK:GUT -CREATURE:GIANT_SKUNK:STOMACH -CREATURE:GIANT_SKUNK:GIZZARD -CREATURE:GIANT_SKUNK:PANCREAS -CREATURE:GIANT_SKUNK:SPLEEN -CREATURE:GIANT_SKUNK:KIDNEY -CREATURE:GREEN_TREE_FROG:MUSCLE -CREATURE:GREEN_TREE_FROG:EYE -CREATURE:GREEN_TREE_FROG:BRAIN -CREATURE:GREEN_TREE_FROG:LUNG -CREATURE:GREEN_TREE_FROG:HEART -CREATURE:GREEN_TREE_FROG:LIVER -CREATURE:GREEN_TREE_FROG:GUT - CREATURE:GREEN_TREE_FROG:STOMACH - CREATURE:GREEN_TREE_FROG:GIZZARD -!CREATURE:GREEN_TREE_FROG:PANCREAS -CREATURE:GREEN_TREE_FROG:SPLEEN -CREATURE:GREEN_TREE_FROG:KIDNEY -#CREATURE:GREEN_TREE_FROG_MAN:MUSCLE - CREATURE:GREEN_TREE_FROG_MAN:EYE -"CREATURE:GREEN_TREE_FROG_MAN:BRAIN -!CREATURE:GREEN_TREE_FROG_MAN:LUNG -"CREATURE:GREEN_TREE_FROG_MAN:HEART -"CREATURE:GREEN_TREE_FROG_MAN:LIVER - CREATURE:GREEN_TREE_FROG_MAN:GUT -$CREATURE:GREEN_TREE_FROG_MAN:STOMACH -$CREATURE:GREEN_TREE_FROG_MAN:GIZZARD -%CREATURE:GREEN_TREE_FROG_MAN:PANCREAS -#CREATURE:GREEN_TREE_FROG_MAN:SPLEEN -#CREATURE:GREEN_TREE_FROG_MAN:KIDNEY -%CREATURE:GIANT_GREEN_TREE_FROG:MUSCLE -"CREATURE:GIANT_GREEN_TREE_FROG:EYE -$CREATURE:GIANT_GREEN_TREE_FROG:BRAIN -#CREATURE:GIANT_GREEN_TREE_FROG:LUNG -$CREATURE:GIANT_GREEN_TREE_FROG:HEART -$CREATURE:GIANT_GREEN_TREE_FROG:LIVER -"CREATURE:GIANT_GREEN_TREE_FROG:GUT -&CREATURE:GIANT_GREEN_TREE_FROG:STOMACH -&CREATURE:GIANT_GREEN_TREE_FROG:GIZZARD -'CREATURE:GIANT_GREEN_TREE_FROG:PANCREAS -%CREATURE:GIANT_GREEN_TREE_FROG:SPLEEN -%CREATURE:GIANT_GREEN_TREE_FROG:KIDNEY -CREATURE:HARE:MUSCLE -CREATURE:HARE:EYE -CREATURE:HARE:BRAIN -CREATURE:HARE:LUNG -CREATURE:HARE:HEART -CREATURE:HARE:LIVER -CREATURE:HARE:GUT -CREATURE:HARE:STOMACH -CREATURE:HARE:GIZZARD -CREATURE:HARE:PANCREAS -CREATURE:HARE:SPLEEN -CREATURE:HARE:KIDNEY -CREATURE:HARE_MAN:MUSCLE -CREATURE:HARE_MAN:EYE -CREATURE:HARE_MAN:BRAIN -CREATURE:HARE_MAN:LUNG -CREATURE:HARE_MAN:HEART -CREATURE:HARE_MAN:LIVER -CREATURE:HARE_MAN:GUT -CREATURE:HARE_MAN:STOMACH -CREATURE:HARE_MAN:GIZZARD -CREATURE:HARE_MAN:PANCREAS -CREATURE:HARE_MAN:SPLEEN -CREATURE:HARE_MAN:KIDNEY -CREATURE:GIANT_HARE:MUSCLE -CREATURE:GIANT_HARE:EYE -CREATURE:GIANT_HARE:BRAIN -CREATURE:GIANT_HARE:LUNG -CREATURE:GIANT_HARE:HEART -CREATURE:GIANT_HARE:LIVER -CREATURE:GIANT_HARE:GUT -CREATURE:GIANT_HARE:STOMACH -CREATURE:GIANT_HARE:GIZZARD -CREATURE:GIANT_HARE:PANCREAS -CREATURE:GIANT_HARE:SPLEEN -CREATURE:GIANT_HARE:KIDNEY -CREATURE:RATTLESNAKE:MUSCLE -CREATURE:RATTLESNAKE:EYE -CREATURE:RATTLESNAKE:BRAIN -CREATURE:RATTLESNAKE:LUNG -CREATURE:RATTLESNAKE:HEART -CREATURE:RATTLESNAKE:LIVER -CREATURE:RATTLESNAKE:GUT -CREATURE:RATTLESNAKE:STOMACH -CREATURE:RATTLESNAKE:GIZZARD -CREATURE:RATTLESNAKE:PANCREAS -CREATURE:RATTLESNAKE:SPLEEN -CREATURE:RATTLESNAKE:KIDNEY -CREATURE:RATTLESNAKE_MAN:MUSCLE -CREATURE:RATTLESNAKE_MAN:EYE -CREATURE:RATTLESNAKE_MAN:BRAIN -CREATURE:RATTLESNAKE_MAN:LUNG -CREATURE:RATTLESNAKE_MAN:HEART -CREATURE:RATTLESNAKE_MAN:LIVER -CREATURE:RATTLESNAKE_MAN:GUT - CREATURE:RATTLESNAKE_MAN:STOMACH - CREATURE:RATTLESNAKE_MAN:GIZZARD -!CREATURE:RATTLESNAKE_MAN:PANCREAS -CREATURE:RATTLESNAKE_MAN:SPLEEN -CREATURE:RATTLESNAKE_MAN:KIDNEY -!CREATURE:GIANT_RATTLESNAKE:MUSCLE -CREATURE:GIANT_RATTLESNAKE:EYE - CREATURE:GIANT_RATTLESNAKE:BRAIN -CREATURE:GIANT_RATTLESNAKE:LUNG - CREATURE:GIANT_RATTLESNAKE:HEART - CREATURE:GIANT_RATTLESNAKE:LIVER -CREATURE:GIANT_RATTLESNAKE:GUT -"CREATURE:GIANT_RATTLESNAKE:STOMACH -"CREATURE:GIANT_RATTLESNAKE:GIZZARD -#CREATURE:GIANT_RATTLESNAKE:PANCREAS -!CREATURE:GIANT_RATTLESNAKE:SPLEEN -!CREATURE:GIANT_RATTLESNAKE:KIDNEY -CREATURE:WEASEL:MUSCLE -CREATURE:WEASEL:EYE -CREATURE:WEASEL:BRAIN -CREATURE:WEASEL:LUNG -CREATURE:WEASEL:HEART -CREATURE:WEASEL:LIVER -CREATURE:WEASEL:GUT -CREATURE:WEASEL:STOMACH -CREATURE:WEASEL:GIZZARD -CREATURE:WEASEL:PANCREAS -CREATURE:WEASEL:SPLEEN -CREATURE:WEASEL:KIDNEY -CREATURE:WEASEL_MAN:MUSCLE -CREATURE:WEASEL_MAN:EYE -CREATURE:WEASEL_MAN:BRAIN -CREATURE:WEASEL_MAN:LUNG -CREATURE:WEASEL_MAN:HEART -CREATURE:WEASEL_MAN:LIVER -CREATURE:WEASEL_MAN:GUT -CREATURE:WEASEL_MAN:STOMACH -CREATURE:WEASEL_MAN:GIZZARD -CREATURE:WEASEL_MAN:PANCREAS -CREATURE:WEASEL_MAN:SPLEEN -CREATURE:WEASEL_MAN:KIDNEY -CREATURE:GIANT_WEASEL:MUSCLE -CREATURE:GIANT_WEASEL:EYE -CREATURE:GIANT_WEASEL:BRAIN -CREATURE:GIANT_WEASEL:LUNG -CREATURE:GIANT_WEASEL:HEART -CREATURE:GIANT_WEASEL:LIVER -CREATURE:GIANT_WEASEL:GUT -CREATURE:GIANT_WEASEL:STOMACH -CREATURE:GIANT_WEASEL:GIZZARD -CREATURE:GIANT_WEASEL:PANCREAS -CREATURE:GIANT_WEASEL:SPLEEN -CREATURE:GIANT_WEASEL:KIDNEY - CREATURE:COPPERHEAD_SNAKE:MUSCLE -CREATURE:COPPERHEAD_SNAKE:EYE -CREATURE:COPPERHEAD_SNAKE:BRAIN -CREATURE:COPPERHEAD_SNAKE:LUNG -CREATURE:COPPERHEAD_SNAKE:HEART -CREATURE:COPPERHEAD_SNAKE:LIVER -CREATURE:COPPERHEAD_SNAKE:GUT -!CREATURE:COPPERHEAD_SNAKE:STOMACH -!CREATURE:COPPERHEAD_SNAKE:GIZZARD -"CREATURE:COPPERHEAD_SNAKE:PANCREAS - CREATURE:COPPERHEAD_SNAKE:SPLEEN - CREATURE:COPPERHEAD_SNAKE:KIDNEY -$CREATURE:COPPERHEAD_SNAKE_MAN:MUSCLE -!CREATURE:COPPERHEAD_SNAKE_MAN:EYE -#CREATURE:COPPERHEAD_SNAKE_MAN:BRAIN -"CREATURE:COPPERHEAD_SNAKE_MAN:LUNG -#CREATURE:COPPERHEAD_SNAKE_MAN:HEART -#CREATURE:COPPERHEAD_SNAKE_MAN:LIVER -!CREATURE:COPPERHEAD_SNAKE_MAN:GUT -%CREATURE:COPPERHEAD_SNAKE_MAN:STOMACH -%CREATURE:COPPERHEAD_SNAKE_MAN:GIZZARD -&CREATURE:COPPERHEAD_SNAKE_MAN:PANCREAS -$CREATURE:COPPERHEAD_SNAKE_MAN:SPLEEN -$CREATURE:COPPERHEAD_SNAKE_MAN:KIDNEY -&CREATURE:GIANT_COPPERHEAD_SNAKE:MUSCLE -#CREATURE:GIANT_COPPERHEAD_SNAKE:EYE -%CREATURE:GIANT_COPPERHEAD_SNAKE:BRAIN -$CREATURE:GIANT_COPPERHEAD_SNAKE:LUNG -%CREATURE:GIANT_COPPERHEAD_SNAKE:HEART -%CREATURE:GIANT_COPPERHEAD_SNAKE:LIVER -#CREATURE:GIANT_COPPERHEAD_SNAKE:GUT -'CREATURE:GIANT_COPPERHEAD_SNAKE:STOMACH -'CREATURE:GIANT_COPPERHEAD_SNAKE:GIZZARD -(CREATURE:GIANT_COPPERHEAD_SNAKE:PANCREAS -&CREATURE:GIANT_COPPERHEAD_SNAKE:SPLEEN -&CREATURE:GIANT_COPPERHEAD_SNAKE:KIDNEY -CREATURE:IBEX:MUSCLE -CREATURE:IBEX:EYE -CREATURE:IBEX:BRAIN -CREATURE:IBEX:LUNG -CREATURE:IBEX:HEART -CREATURE:IBEX:LIVER -CREATURE:IBEX:GUT -CREATURE:IBEX:STOMACH -CREATURE:IBEX:GIZZARD -CREATURE:IBEX:PANCREAS -CREATURE:IBEX:SPLEEN -CREATURE:IBEX:KIDNEY -CREATURE:IBEX_MAN:MUSCLE -CREATURE:IBEX_MAN:EYE -CREATURE:IBEX_MAN:BRAIN -CREATURE:IBEX_MAN:LUNG -CREATURE:IBEX_MAN:HEART -CREATURE:IBEX_MAN:LIVER -CREATURE:IBEX_MAN:GUT -CREATURE:IBEX_MAN:STOMACH -CREATURE:IBEX_MAN:GIZZARD -CREATURE:IBEX_MAN:PANCREAS -CREATURE:IBEX_MAN:SPLEEN -CREATURE:IBEX_MAN:KIDNEY -CREATURE:GIANT_IBEX:MUSCLE -CREATURE:GIANT_IBEX:EYE -CREATURE:GIANT_IBEX:BRAIN -CREATURE:GIANT_IBEX:LUNG -CREATURE:GIANT_IBEX:HEART -CREATURE:GIANT_IBEX:LIVER -CREATURE:GIANT_IBEX:GUT -CREATURE:GIANT_IBEX:STOMACH -CREATURE:GIANT_IBEX:GIZZARD -CREATURE:GIANT_IBEX:PANCREAS -CREATURE:GIANT_IBEX:SPLEEN -CREATURE:GIANT_IBEX:KIDNEY -CREATURE:WOMBAT:MUSCLE -CREATURE:WOMBAT:EYE -CREATURE:WOMBAT:BRAIN -CREATURE:WOMBAT:LUNG -CREATURE:WOMBAT:HEART -CREATURE:WOMBAT:LIVER -CREATURE:WOMBAT:GUT -CREATURE:WOMBAT:STOMACH -CREATURE:WOMBAT:GIZZARD -CREATURE:WOMBAT:PANCREAS -CREATURE:WOMBAT:SPLEEN -CREATURE:WOMBAT:KIDNEY -CREATURE:WOMBAT_MAN:MUSCLE -CREATURE:WOMBAT_MAN:EYE -CREATURE:WOMBAT_MAN:BRAIN -CREATURE:WOMBAT_MAN:LUNG -CREATURE:WOMBAT_MAN:HEART -CREATURE:WOMBAT_MAN:LIVER -CREATURE:WOMBAT_MAN:GUT -CREATURE:WOMBAT_MAN:STOMACH -CREATURE:WOMBAT_MAN:GIZZARD -CREATURE:WOMBAT_MAN:PANCREAS -CREATURE:WOMBAT_MAN:SPLEEN -CREATURE:WOMBAT_MAN:KIDNEY -CREATURE:GIANT_WOMBAT:MUSCLE -CREATURE:GIANT_WOMBAT:EYE -CREATURE:GIANT_WOMBAT:BRAIN -CREATURE:GIANT_WOMBAT:LUNG -CREATURE:GIANT_WOMBAT:HEART -CREATURE:GIANT_WOMBAT:LIVER -CREATURE:GIANT_WOMBAT:GUT -CREATURE:GIANT_WOMBAT:STOMACH -CREATURE:GIANT_WOMBAT:GIZZARD -CREATURE:GIANT_WOMBAT:PANCREAS -CREATURE:GIANT_WOMBAT:SPLEEN -CREATURE:GIANT_WOMBAT:KIDNEY -CREATURE:DINGO:MUSCLE -CREATURE:DINGO:EYE -CREATURE:DINGO:BRAIN -CREATURE:DINGO:LUNG -CREATURE:DINGO:HEART -CREATURE:DINGO:LIVER -CREATURE:DINGO:GUT -CREATURE:DINGO:STOMACH -CREATURE:DINGO:GIZZARD -CREATURE:DINGO:PANCREAS -CREATURE:DINGO:SPLEEN -CREATURE:DINGO:KIDNEY -CREATURE:DINGO_MAN:MUSCLE -CREATURE:DINGO_MAN:EYE -CREATURE:DINGO_MAN:BRAIN -CREATURE:DINGO_MAN:LUNG -CREATURE:DINGO_MAN:HEART -CREATURE:DINGO_MAN:LIVER -CREATURE:DINGO_MAN:GUT -CREATURE:DINGO_MAN:STOMACH -CREATURE:DINGO_MAN:GIZZARD -CREATURE:DINGO_MAN:PANCREAS -CREATURE:DINGO_MAN:SPLEEN -CREATURE:DINGO_MAN:KIDNEY -CREATURE:GIANT_DINGO:MUSCLE -CREATURE:GIANT_DINGO:EYE -CREATURE:GIANT_DINGO:BRAIN -CREATURE:GIANT_DINGO:LUNG -CREATURE:GIANT_DINGO:HEART -CREATURE:GIANT_DINGO:LIVER -CREATURE:GIANT_DINGO:GUT -CREATURE:GIANT_DINGO:STOMACH -CREATURE:GIANT_DINGO:GIZZARD -CREATURE:GIANT_DINGO:PANCREAS -CREATURE:GIANT_DINGO:SPLEEN -CREATURE:GIANT_DINGO:KIDNEY -CREATURE:COATI:MUSCLE -CREATURE:COATI:EYE -CREATURE:COATI:BRAIN -CREATURE:COATI:LUNG -CREATURE:COATI:HEART -CREATURE:COATI:LIVER -CREATURE:COATI:GUT -CREATURE:COATI:STOMACH -CREATURE:COATI:GIZZARD -CREATURE:COATI:PANCREAS -CREATURE:COATI:SPLEEN -CREATURE:COATI:KIDNEY -CREATURE:COATI_MAN:MUSCLE -CREATURE:COATI_MAN:EYE -CREATURE:COATI_MAN:BRAIN -CREATURE:COATI_MAN:LUNG -CREATURE:COATI_MAN:HEART -CREATURE:COATI_MAN:LIVER -CREATURE:COATI_MAN:GUT -CREATURE:COATI_MAN:STOMACH -CREATURE:COATI_MAN:GIZZARD -CREATURE:COATI_MAN:PANCREAS -CREATURE:COATI_MAN:SPLEEN -CREATURE:COATI_MAN:KIDNEY -CREATURE:GIANT_COATI:MUSCLE -CREATURE:GIANT_COATI:EYE -CREATURE:GIANT_COATI:BRAIN -CREATURE:GIANT_COATI:LUNG -CREATURE:GIANT_COATI:HEART -CREATURE:GIANT_COATI:LIVER -CREATURE:GIANT_COATI:GUT -CREATURE:GIANT_COATI:STOMACH -CREATURE:GIANT_COATI:GIZZARD -CREATURE:GIANT_COATI:PANCREAS -CREATURE:GIANT_COATI:SPLEEN -CREATURE:GIANT_COATI:KIDNEY -CREATURE:OPOSSUM:MUSCLE -CREATURE:OPOSSUM:EYE -CREATURE:OPOSSUM:BRAIN -CREATURE:OPOSSUM:LUNG -CREATURE:OPOSSUM:HEART -CREATURE:OPOSSUM:LIVER -CREATURE:OPOSSUM:GUT -CREATURE:OPOSSUM:STOMACH -CREATURE:OPOSSUM:GIZZARD -CREATURE:OPOSSUM:PANCREAS -CREATURE:OPOSSUM:SPLEEN -CREATURE:OPOSSUM:KIDNEY -CREATURE:OPOSSUM_MAN:MUSCLE -CREATURE:OPOSSUM_MAN:EYE -CREATURE:OPOSSUM_MAN:BRAIN -CREATURE:OPOSSUM_MAN:LUNG -CREATURE:OPOSSUM_MAN:HEART -CREATURE:OPOSSUM_MAN:LIVER -CREATURE:OPOSSUM_MAN:GUT -CREATURE:OPOSSUM_MAN:STOMACH -CREATURE:OPOSSUM_MAN:GIZZARD -CREATURE:OPOSSUM_MAN:PANCREAS -CREATURE:OPOSSUM_MAN:SPLEEN -CREATURE:OPOSSUM_MAN:KIDNEY -CREATURE:GIANT_OPOSSUM:MUSCLE -CREATURE:GIANT_OPOSSUM:EYE -CREATURE:GIANT_OPOSSUM:BRAIN -CREATURE:GIANT_OPOSSUM:LUNG -CREATURE:GIANT_OPOSSUM:HEART -CREATURE:GIANT_OPOSSUM:LIVER -CREATURE:GIANT_OPOSSUM:GUT -CREATURE:GIANT_OPOSSUM:STOMACH -CREATURE:GIANT_OPOSSUM:GIZZARD -CREATURE:GIANT_OPOSSUM:PANCREAS -CREATURE:GIANT_OPOSSUM:SPLEEN -CREATURE:GIANT_OPOSSUM:KIDNEY -CREATURE:MONGOOSE:MUSCLE -CREATURE:MONGOOSE:EYE -CREATURE:MONGOOSE:BRAIN -CREATURE:MONGOOSE:LUNG -CREATURE:MONGOOSE:HEART -CREATURE:MONGOOSE:LIVER -CREATURE:MONGOOSE:GUT -CREATURE:MONGOOSE:STOMACH -CREATURE:MONGOOSE:GIZZARD -CREATURE:MONGOOSE:PANCREAS -CREATURE:MONGOOSE:SPLEEN -CREATURE:MONGOOSE:KIDNEY -CREATURE:MONGOOSE_MAN:MUSCLE -CREATURE:MONGOOSE_MAN:EYE -CREATURE:MONGOOSE_MAN:BRAIN -CREATURE:MONGOOSE_MAN:LUNG -CREATURE:MONGOOSE_MAN:HEART -CREATURE:MONGOOSE_MAN:LIVER -CREATURE:MONGOOSE_MAN:GUT -CREATURE:MONGOOSE_MAN:STOMACH -CREATURE:MONGOOSE_MAN:GIZZARD -CREATURE:MONGOOSE_MAN:PANCREAS -CREATURE:MONGOOSE_MAN:SPLEEN -CREATURE:MONGOOSE_MAN:KIDNEY -CREATURE:GIANT_MONGOOSE:MUSCLE -CREATURE:GIANT_MONGOOSE:EYE -CREATURE:GIANT_MONGOOSE:BRAIN -CREATURE:GIANT_MONGOOSE:LUNG -CREATURE:GIANT_MONGOOSE:HEART -CREATURE:GIANT_MONGOOSE:LIVER -CREATURE:GIANT_MONGOOSE:GUT -CREATURE:GIANT_MONGOOSE:STOMACH -CREATURE:GIANT_MONGOOSE:GIZZARD - CREATURE:GIANT_MONGOOSE:PANCREAS -CREATURE:GIANT_MONGOOSE:SPLEEN -CREATURE:GIANT_MONGOOSE:KIDNEY -CREATURE:HYENA:MUSCLE -CREATURE:HYENA:EYE -CREATURE:HYENA:BRAIN -CREATURE:HYENA:LUNG -CREATURE:HYENA:HEART -CREATURE:HYENA:LIVER -CREATURE:HYENA:GUT -CREATURE:HYENA:STOMACH -CREATURE:HYENA:GIZZARD -CREATURE:HYENA:PANCREAS -CREATURE:HYENA:SPLEEN -CREATURE:HYENA:KIDNEY -CREATURE:HYENA_MAN:MUSCLE -CREATURE:HYENA_MAN:EYE -CREATURE:HYENA_MAN:BRAIN -CREATURE:HYENA_MAN:LUNG -CREATURE:HYENA_MAN:HEART -CREATURE:HYENA_MAN:LIVER -CREATURE:HYENA_MAN:GUT -CREATURE:HYENA_MAN:STOMACH -CREATURE:HYENA_MAN:GIZZARD -CREATURE:HYENA_MAN:PANCREAS -CREATURE:HYENA_MAN:SPLEEN -CREATURE:HYENA_MAN:KIDNEY -CREATURE:GIANT_HYENA:MUSCLE -CREATURE:GIANT_HYENA:EYE -CREATURE:GIANT_HYENA:BRAIN -CREATURE:GIANT_HYENA:LUNG -CREATURE:GIANT_HYENA:HEART -CREATURE:GIANT_HYENA:LIVER -CREATURE:GIANT_HYENA:GUT -CREATURE:GIANT_HYENA:STOMACH -CREATURE:GIANT_HYENA:GIZZARD -CREATURE:GIANT_HYENA:PANCREAS -CREATURE:GIANT_HYENA:SPLEEN -CREATURE:GIANT_HYENA:KIDNEY -CREATURE:ANACONDA:MUSCLE -CREATURE:ANACONDA:EYE -CREATURE:ANACONDA:BRAIN -CREATURE:ANACONDA:LUNG -CREATURE:ANACONDA:HEART -CREATURE:ANACONDA:LIVER -CREATURE:ANACONDA:GUT -CREATURE:ANACONDA:STOMACH -CREATURE:ANACONDA:GIZZARD -CREATURE:ANACONDA:PANCREAS -CREATURE:ANACONDA:SPLEEN -CREATURE:ANACONDA:KIDNEY -CREATURE:ANACONDA_MAN:MUSCLE -CREATURE:ANACONDA_MAN:EYE -CREATURE:ANACONDA_MAN:BRAIN -CREATURE:ANACONDA_MAN:LUNG -CREATURE:ANACONDA_MAN:HEART -CREATURE:ANACONDA_MAN:LIVER -CREATURE:ANACONDA_MAN:GUT -CREATURE:ANACONDA_MAN:STOMACH -CREATURE:ANACONDA_MAN:GIZZARD -CREATURE:ANACONDA_MAN:PANCREAS -CREATURE:ANACONDA_MAN:SPLEEN -CREATURE:ANACONDA_MAN:KIDNEY -CREATURE:GIANT_ANACONDA:MUSCLE -CREATURE:GIANT_ANACONDA:EYE -CREATURE:GIANT_ANACONDA:BRAIN -CREATURE:GIANT_ANACONDA:LUNG -CREATURE:GIANT_ANACONDA:HEART -CREATURE:GIANT_ANACONDA:LIVER -CREATURE:GIANT_ANACONDA:GUT -CREATURE:GIANT_ANACONDA:STOMACH -CREATURE:GIANT_ANACONDA:GIZZARD - CREATURE:GIANT_ANACONDA:PANCREAS -CREATURE:GIANT_ANACONDA:SPLEEN -CREATURE:GIANT_ANACONDA:KIDNEY -CREATURE:MONITOR_LIZARD:MUSCLE -CREATURE:MONITOR_LIZARD:EYE -CREATURE:MONITOR_LIZARD:BRAIN -CREATURE:MONITOR_LIZARD:LUNG -CREATURE:MONITOR_LIZARD:HEART -CREATURE:MONITOR_LIZARD:LIVER -CREATURE:MONITOR_LIZARD:GUT -CREATURE:MONITOR_LIZARD:STOMACH -CREATURE:MONITOR_LIZARD:GIZZARD - CREATURE:MONITOR_LIZARD:PANCREAS -CREATURE:MONITOR_LIZARD:SPLEEN -CREATURE:MONITOR_LIZARD:KIDNEY -"CREATURE:MONITOR_LIZARD_MAN:MUSCLE -CREATURE:MONITOR_LIZARD_MAN:EYE -!CREATURE:MONITOR_LIZARD_MAN:BRAIN - CREATURE:MONITOR_LIZARD_MAN:LUNG -!CREATURE:MONITOR_LIZARD_MAN:HEART -!CREATURE:MONITOR_LIZARD_MAN:LIVER -CREATURE:MONITOR_LIZARD_MAN:GUT -#CREATURE:MONITOR_LIZARD_MAN:STOMACH -#CREATURE:MONITOR_LIZARD_MAN:GIZZARD -$CREATURE:MONITOR_LIZARD_MAN:PANCREAS -"CREATURE:MONITOR_LIZARD_MAN:SPLEEN -"CREATURE:MONITOR_LIZARD_MAN:KIDNEY -$CREATURE:GIANT_MONITOR_LIZARD:MUSCLE -!CREATURE:GIANT_MONITOR_LIZARD:EYE -#CREATURE:GIANT_MONITOR_LIZARD:BRAIN -"CREATURE:GIANT_MONITOR_LIZARD:LUNG -#CREATURE:GIANT_MONITOR_LIZARD:HEART -#CREATURE:GIANT_MONITOR_LIZARD:LIVER -!CREATURE:GIANT_MONITOR_LIZARD:GUT -%CREATURE:GIANT_MONITOR_LIZARD:STOMACH -%CREATURE:GIANT_MONITOR_LIZARD:GIZZARD -&CREATURE:GIANT_MONITOR_LIZARD:PANCREAS -$CREATURE:GIANT_MONITOR_LIZARD:SPLEEN -$CREATURE:GIANT_MONITOR_LIZARD:KIDNEY -CREATURE:KING_COBRA:MUSCLE -CREATURE:KING_COBRA:EYE -CREATURE:KING_COBRA:BRAIN -CREATURE:KING_COBRA:LUNG -CREATURE:KING_COBRA:HEART -CREATURE:KING_COBRA:LIVER -CREATURE:KING_COBRA:GUT -CREATURE:KING_COBRA:STOMACH -CREATURE:KING_COBRA:GIZZARD -CREATURE:KING_COBRA:PANCREAS -CREATURE:KING_COBRA:SPLEEN -CREATURE:KING_COBRA:KIDNEY -CREATURE:KING_COBRA_MAN:MUSCLE -CREATURE:KING_COBRA_MAN:EYE -CREATURE:KING_COBRA_MAN:BRAIN -CREATURE:KING_COBRA_MAN:LUNG -CREATURE:KING_COBRA_MAN:HEART -CREATURE:KING_COBRA_MAN:LIVER -CREATURE:KING_COBRA_MAN:GUT -CREATURE:KING_COBRA_MAN:STOMACH -CREATURE:KING_COBRA_MAN:GIZZARD - CREATURE:KING_COBRA_MAN:PANCREAS -CREATURE:KING_COBRA_MAN:SPLEEN -CREATURE:KING_COBRA_MAN:KIDNEY - CREATURE:GIANT_KING_COBRA:MUSCLE -CREATURE:GIANT_KING_COBRA:EYE -CREATURE:GIANT_KING_COBRA:BRAIN -CREATURE:GIANT_KING_COBRA:LUNG -CREATURE:GIANT_KING_COBRA:HEART -CREATURE:GIANT_KING_COBRA:LIVER -CREATURE:GIANT_KING_COBRA:GUT -!CREATURE:GIANT_KING_COBRA:STOMACH -!CREATURE:GIANT_KING_COBRA:GIZZARD -"CREATURE:GIANT_KING_COBRA:PANCREAS - CREATURE:GIANT_KING_COBRA:SPLEEN - CREATURE:GIANT_KING_COBRA:KIDNEY -CREATURE:OCELOT:MUSCLE -CREATURE:OCELOT:EYE -CREATURE:OCELOT:BRAIN -CREATURE:OCELOT:LUNG -CREATURE:OCELOT:HEART -CREATURE:OCELOT:LIVER -CREATURE:OCELOT:GUT -CREATURE:OCELOT:STOMACH -CREATURE:OCELOT:GIZZARD -CREATURE:OCELOT:PANCREAS -CREATURE:OCELOT:SPLEEN -CREATURE:OCELOT:KIDNEY -CREATURE:OCELOT_MAN:MUSCLE -CREATURE:OCELOT_MAN:EYE -CREATURE:OCELOT_MAN:BRAIN -CREATURE:OCELOT_MAN:LUNG -CREATURE:OCELOT_MAN:HEART -CREATURE:OCELOT_MAN:LIVER -CREATURE:OCELOT_MAN:GUT -CREATURE:OCELOT_MAN:STOMACH -CREATURE:OCELOT_MAN:GIZZARD -CREATURE:OCELOT_MAN:PANCREAS -CREATURE:OCELOT_MAN:SPLEEN -CREATURE:OCELOT_MAN:KIDNEY -CREATURE:GIANT_OCELOT:MUSCLE -CREATURE:GIANT_OCELOT:EYE -CREATURE:GIANT_OCELOT:BRAIN -CREATURE:GIANT_OCELOT:LUNG -CREATURE:GIANT_OCELOT:HEART -CREATURE:GIANT_OCELOT:LIVER -CREATURE:GIANT_OCELOT:GUT -CREATURE:GIANT_OCELOT:STOMACH -CREATURE:GIANT_OCELOT:GIZZARD -CREATURE:GIANT_OCELOT:PANCREAS -CREATURE:GIANT_OCELOT:SPLEEN -CREATURE:GIANT_OCELOT:KIDNEY -CREATURE:JACKAL:MUSCLE -CREATURE:JACKAL:EYE -CREATURE:JACKAL:BRAIN -CREATURE:JACKAL:LUNG -CREATURE:JACKAL:HEART -CREATURE:JACKAL:LIVER -CREATURE:JACKAL:GUT -CREATURE:JACKAL:STOMACH -CREATURE:JACKAL:GIZZARD -CREATURE:JACKAL:PANCREAS -CREATURE:JACKAL:SPLEEN -CREATURE:JACKAL:KIDNEY -CREATURE:JACKAL_MAN:MUSCLE -CREATURE:JACKAL_MAN:EYE -CREATURE:JACKAL_MAN:BRAIN -CREATURE:JACKAL_MAN:LUNG -CREATURE:JACKAL_MAN:HEART -CREATURE:JACKAL_MAN:LIVER -CREATURE:JACKAL_MAN:GUT -CREATURE:JACKAL_MAN:STOMACH -CREATURE:JACKAL_MAN:GIZZARD -CREATURE:JACKAL_MAN:PANCREAS -CREATURE:JACKAL_MAN:SPLEEN -CREATURE:JACKAL_MAN:KIDNEY -CREATURE:GIANT_JACKAL:MUSCLE -CREATURE:GIANT_JACKAL:EYE -CREATURE:GIANT_JACKAL:BRAIN -CREATURE:GIANT_JACKAL:LUNG -CREATURE:GIANT_JACKAL:HEART -CREATURE:GIANT_JACKAL:LIVER -CREATURE:GIANT_JACKAL:GUT -CREATURE:GIANT_JACKAL:STOMACH -CREATURE:GIANT_JACKAL:GIZZARD -CREATURE:GIANT_JACKAL:PANCREAS -CREATURE:GIANT_JACKAL:SPLEEN -CREATURE:GIANT_JACKAL:KIDNEY -CREATURE:CAPUCHIN:MUSCLE -CREATURE:CAPUCHIN:EYE -CREATURE:CAPUCHIN:BRAIN -CREATURE:CAPUCHIN:LUNG -CREATURE:CAPUCHIN:HEART -CREATURE:CAPUCHIN:LIVER -CREATURE:CAPUCHIN:GUT -CREATURE:CAPUCHIN:STOMACH -CREATURE:CAPUCHIN:GIZZARD -CREATURE:CAPUCHIN:PANCREAS -CREATURE:CAPUCHIN:SPLEEN -CREATURE:CAPUCHIN:KIDNEY -CREATURE:CAPUCHIN_MAN:MUSCLE -CREATURE:CAPUCHIN_MAN:EYE -CREATURE:CAPUCHIN_MAN:BRAIN -CREATURE:CAPUCHIN_MAN:LUNG -CREATURE:CAPUCHIN_MAN:HEART -CREATURE:CAPUCHIN_MAN:LIVER -CREATURE:CAPUCHIN_MAN:GUT -CREATURE:CAPUCHIN_MAN:STOMACH -CREATURE:CAPUCHIN_MAN:GIZZARD -CREATURE:CAPUCHIN_MAN:PANCREAS -CREATURE:CAPUCHIN_MAN:SPLEEN -CREATURE:CAPUCHIN_MAN:KIDNEY -CREATURE:GIANT_CAPUCHIN:MUSCLE -CREATURE:GIANT_CAPUCHIN:EYE -CREATURE:GIANT_CAPUCHIN:BRAIN -CREATURE:GIANT_CAPUCHIN:LUNG -CREATURE:GIANT_CAPUCHIN:HEART -CREATURE:GIANT_CAPUCHIN:LIVER -CREATURE:GIANT_CAPUCHIN:GUT -CREATURE:GIANT_CAPUCHIN:STOMACH -CREATURE:GIANT_CAPUCHIN:GIZZARD - CREATURE:GIANT_CAPUCHIN:PANCREAS -CREATURE:GIANT_CAPUCHIN:SPLEEN -CREATURE:GIANT_CAPUCHIN:KIDNEY -CREATURE:SLOTH:MUSCLE -CREATURE:SLOTH:EYE -CREATURE:SLOTH:BRAIN -CREATURE:SLOTH:LUNG -CREATURE:SLOTH:HEART -CREATURE:SLOTH:LIVER -CREATURE:SLOTH:GUT -CREATURE:SLOTH:STOMACH -CREATURE:SLOTH:GIZZARD -CREATURE:SLOTH:PANCREAS -CREATURE:SLOTH:SPLEEN -CREATURE:SLOTH:KIDNEY -CREATURE:SLOTH_MAN:MUSCLE -CREATURE:SLOTH_MAN:EYE -CREATURE:SLOTH_MAN:BRAIN -CREATURE:SLOTH_MAN:LUNG -CREATURE:SLOTH_MAN:HEART -CREATURE:SLOTH_MAN:LIVER -CREATURE:SLOTH_MAN:GUT -CREATURE:SLOTH_MAN:STOMACH -CREATURE:SLOTH_MAN:GIZZARD -CREATURE:SLOTH_MAN:PANCREAS -CREATURE:SLOTH_MAN:SPLEEN -CREATURE:SLOTH_MAN:KIDNEY -CREATURE:GIANT_SLOTH:MUSCLE -CREATURE:GIANT_SLOTH:EYE -CREATURE:GIANT_SLOTH:BRAIN -CREATURE:GIANT_SLOTH:LUNG -CREATURE:GIANT_SLOTH:HEART -CREATURE:GIANT_SLOTH:LIVER -CREATURE:GIANT_SLOTH:GUT -CREATURE:GIANT_SLOTH:STOMACH -CREATURE:GIANT_SLOTH:GIZZARD -CREATURE:GIANT_SLOTH:PANCREAS -CREATURE:GIANT_SLOTH:SPLEEN -CREATURE:GIANT_SLOTH:KIDNEY -CREATURE:SPIDER_MONKEY:MUSCLE -CREATURE:SPIDER_MONKEY:EYE -CREATURE:SPIDER_MONKEY:BRAIN -CREATURE:SPIDER_MONKEY:LUNG -CREATURE:SPIDER_MONKEY:HEART -CREATURE:SPIDER_MONKEY:LIVER -CREATURE:SPIDER_MONKEY:GUT -CREATURE:SPIDER_MONKEY:STOMACH -CREATURE:SPIDER_MONKEY:GIZZARD -CREATURE:SPIDER_MONKEY:PANCREAS -CREATURE:SPIDER_MONKEY:SPLEEN -CREATURE:SPIDER_MONKEY:KIDNEY -!CREATURE:SPIDER_MONKEY_MAN:MUSCLE -CREATURE:SPIDER_MONKEY_MAN:EYE - CREATURE:SPIDER_MONKEY_MAN:BRAIN -CREATURE:SPIDER_MONKEY_MAN:LUNG - CREATURE:SPIDER_MONKEY_MAN:HEART - CREATURE:SPIDER_MONKEY_MAN:LIVER -CREATURE:SPIDER_MONKEY_MAN:GUT -"CREATURE:SPIDER_MONKEY_MAN:STOMACH -"CREATURE:SPIDER_MONKEY_MAN:GIZZARD -#CREATURE:SPIDER_MONKEY_MAN:PANCREAS -!CREATURE:SPIDER_MONKEY_MAN:SPLEEN -!CREATURE:SPIDER_MONKEY_MAN:KIDNEY -#CREATURE:GIANT_SPIDER_MONKEY:MUSCLE - CREATURE:GIANT_SPIDER_MONKEY:EYE -"CREATURE:GIANT_SPIDER_MONKEY:BRAIN -!CREATURE:GIANT_SPIDER_MONKEY:LUNG -"CREATURE:GIANT_SPIDER_MONKEY:HEART -"CREATURE:GIANT_SPIDER_MONKEY:LIVER - CREATURE:GIANT_SPIDER_MONKEY:GUT -$CREATURE:GIANT_SPIDER_MONKEY:STOMACH -$CREATURE:GIANT_SPIDER_MONKEY:GIZZARD -%CREATURE:GIANT_SPIDER_MONKEY:PANCREAS -#CREATURE:GIANT_SPIDER_MONKEY:SPLEEN -#CREATURE:GIANT_SPIDER_MONKEY:KIDNEY -CREATURE:PANGOLIN:MUSCLE -CREATURE:PANGOLIN:EYE -CREATURE:PANGOLIN:BRAIN -CREATURE:PANGOLIN:LUNG -CREATURE:PANGOLIN:HEART -CREATURE:PANGOLIN:LIVER -CREATURE:PANGOLIN:GUT -CREATURE:PANGOLIN:STOMACH -CREATURE:PANGOLIN:GIZZARD -CREATURE:PANGOLIN:PANCREAS -CREATURE:PANGOLIN:SPLEEN -CREATURE:PANGOLIN:KIDNEY -CREATURE:PANGOLIN_MAN:MUSCLE -CREATURE:PANGOLIN_MAN:EYE -CREATURE:PANGOLIN_MAN:BRAIN -CREATURE:PANGOLIN_MAN:LUNG -CREATURE:PANGOLIN_MAN:HEART -CREATURE:PANGOLIN_MAN:LIVER -CREATURE:PANGOLIN_MAN:GUT -CREATURE:PANGOLIN_MAN:STOMACH -CREATURE:PANGOLIN_MAN:GIZZARD -CREATURE:PANGOLIN_MAN:PANCREAS -CREATURE:PANGOLIN_MAN:SPLEEN -CREATURE:PANGOLIN_MAN:KIDNEY -CREATURE:GIANT_PANGOLIN:MUSCLE -CREATURE:GIANT_PANGOLIN:EYE -CREATURE:GIANT_PANGOLIN:BRAIN -CREATURE:GIANT_PANGOLIN:LUNG -CREATURE:GIANT_PANGOLIN:HEART -CREATURE:GIANT_PANGOLIN:LIVER -CREATURE:GIANT_PANGOLIN:GUT -CREATURE:GIANT_PANGOLIN:STOMACH -CREATURE:GIANT_PANGOLIN:GIZZARD - CREATURE:GIANT_PANGOLIN:PANCREAS -CREATURE:GIANT_PANGOLIN:SPLEEN -CREATURE:GIANT_PANGOLIN:KIDNEY -CREATURE:BLACK_MAMBA:MUSCLE -CREATURE:BLACK_MAMBA:EYE -CREATURE:BLACK_MAMBA:BRAIN -CREATURE:BLACK_MAMBA:LUNG -CREATURE:BLACK_MAMBA:HEART -CREATURE:BLACK_MAMBA:LIVER -CREATURE:BLACK_MAMBA:GUT -CREATURE:BLACK_MAMBA:STOMACH -CREATURE:BLACK_MAMBA:GIZZARD -CREATURE:BLACK_MAMBA:PANCREAS -CREATURE:BLACK_MAMBA:SPLEEN -CREATURE:BLACK_MAMBA:KIDNEY -CREATURE:BLACK_MAMBA_MAN:MUSCLE -CREATURE:BLACK_MAMBA_MAN:EYE -CREATURE:BLACK_MAMBA_MAN:BRAIN -CREATURE:BLACK_MAMBA_MAN:LUNG -CREATURE:BLACK_MAMBA_MAN:HEART -CREATURE:BLACK_MAMBA_MAN:LIVER -CREATURE:BLACK_MAMBA_MAN:GUT - CREATURE:BLACK_MAMBA_MAN:STOMACH - CREATURE:BLACK_MAMBA_MAN:GIZZARD -!CREATURE:BLACK_MAMBA_MAN:PANCREAS -CREATURE:BLACK_MAMBA_MAN:SPLEEN -CREATURE:BLACK_MAMBA_MAN:KIDNEY -!CREATURE:GIANT_BLACK_MAMBA:MUSCLE -CREATURE:GIANT_BLACK_MAMBA:EYE - CREATURE:GIANT_BLACK_MAMBA:BRAIN -CREATURE:GIANT_BLACK_MAMBA:LUNG - CREATURE:GIANT_BLACK_MAMBA:HEART - CREATURE:GIANT_BLACK_MAMBA:LIVER -CREATURE:GIANT_BLACK_MAMBA:GUT -"CREATURE:GIANT_BLACK_MAMBA:STOMACH -"CREATURE:GIANT_BLACK_MAMBA:GIZZARD -#CREATURE:GIANT_BLACK_MAMBA:PANCREAS -!CREATURE:GIANT_BLACK_MAMBA:SPLEEN -!CREATURE:GIANT_BLACK_MAMBA:KIDNEY -CREATURE:BEAR_SLOTH:MUSCLE -CREATURE:BEAR_SLOTH:EYE -CREATURE:BEAR_SLOTH:BRAIN -CREATURE:BEAR_SLOTH:LUNG -CREATURE:BEAR_SLOTH:HEART -CREATURE:BEAR_SLOTH:LIVER -CREATURE:BEAR_SLOTH:GUT -CREATURE:BEAR_SLOTH:STOMACH -CREATURE:BEAR_SLOTH:GIZZARD -CREATURE:BEAR_SLOTH:PANCREAS -CREATURE:BEAR_SLOTH:SPLEEN -CREATURE:BEAR_SLOTH:KIDNEY -CREATURE:SLOTH_BEAR_MAN:MUSCLE -CREATURE:SLOTH_BEAR_MAN:EYE -CREATURE:SLOTH_BEAR_MAN:BRAIN -CREATURE:SLOTH_BEAR_MAN:LUNG -CREATURE:SLOTH_BEAR_MAN:HEART -CREATURE:SLOTH_BEAR_MAN:LIVER -CREATURE:SLOTH_BEAR_MAN:GUT -CREATURE:SLOTH_BEAR_MAN:STOMACH -CREATURE:SLOTH_BEAR_MAN:GIZZARD - CREATURE:SLOTH_BEAR_MAN:PANCREAS -CREATURE:SLOTH_BEAR_MAN:SPLEEN -CREATURE:SLOTH_BEAR_MAN:KIDNEY - CREATURE:GIANT_SLOTH_BEAR:MUSCLE -CREATURE:GIANT_SLOTH_BEAR:EYE -CREATURE:GIANT_SLOTH_BEAR:BRAIN -CREATURE:GIANT_SLOTH_BEAR:LUNG -CREATURE:GIANT_SLOTH_BEAR:HEART -CREATURE:GIANT_SLOTH_BEAR:LIVER -CREATURE:GIANT_SLOTH_BEAR:GUT -!CREATURE:GIANT_SLOTH_BEAR:STOMACH -!CREATURE:GIANT_SLOTH_BEAR:GIZZARD -"CREATURE:GIANT_SLOTH_BEAR:PANCREAS - CREATURE:GIANT_SLOTH_BEAR:SPLEEN - CREATURE:GIANT_SLOTH_BEAR:KIDNEY -CREATURE:AYE-AYE:MUSCLE -CREATURE:AYE-AYE:EYE -CREATURE:AYE-AYE:BRAIN -CREATURE:AYE-AYE:LUNG -CREATURE:AYE-AYE:HEART -CREATURE:AYE-AYE:LIVER -CREATURE:AYE-AYE:GUT -CREATURE:AYE-AYE:STOMACH -CREATURE:AYE-AYE:GIZZARD -CREATURE:AYE-AYE:PANCREAS -CREATURE:AYE-AYE:SPLEEN -CREATURE:AYE-AYE:KIDNEY -CREATURE:AYE-AYE_MAN:MUSCLE -CREATURE:AYE-AYE_MAN:EYE -CREATURE:AYE-AYE_MAN:BRAIN -CREATURE:AYE-AYE_MAN:LUNG -CREATURE:AYE-AYE_MAN:HEART -CREATURE:AYE-AYE_MAN:LIVER -CREATURE:AYE-AYE_MAN:GUT -CREATURE:AYE-AYE_MAN:STOMACH -CREATURE:AYE-AYE_MAN:GIZZARD -CREATURE:AYE-AYE_MAN:PANCREAS -CREATURE:AYE-AYE_MAN:SPLEEN -CREATURE:AYE-AYE_MAN:KIDNEY -CREATURE:GIANT_AYE-AYE:MUSCLE -CREATURE:GIANT_AYE-AYE:EYE -CREATURE:GIANT_AYE-AYE:BRAIN -CREATURE:GIANT_AYE-AYE:LUNG -CREATURE:GIANT_AYE-AYE:HEART -CREATURE:GIANT_AYE-AYE:LIVER -CREATURE:GIANT_AYE-AYE:GUT -CREATURE:GIANT_AYE-AYE:STOMACH -CREATURE:GIANT_AYE-AYE:GIZZARD -CREATURE:GIANT_AYE-AYE:PANCREAS -CREATURE:GIANT_AYE-AYE:SPLEEN -CREATURE:GIANT_AYE-AYE:KIDNEY -CREATURE:BUSHMASTER:MUSCLE -CREATURE:BUSHMASTER:EYE -CREATURE:BUSHMASTER:BRAIN -CREATURE:BUSHMASTER:LUNG -CREATURE:BUSHMASTER:HEART -CREATURE:BUSHMASTER:LIVER -CREATURE:BUSHMASTER:GUT -CREATURE:BUSHMASTER:STOMACH -CREATURE:BUSHMASTER:GIZZARD -CREATURE:BUSHMASTER:PANCREAS -CREATURE:BUSHMASTER:SPLEEN -CREATURE:BUSHMASTER:KIDNEY -CREATURE:BUSHMASTER_MAN:MUSCLE -CREATURE:BUSHMASTER_MAN:EYE -CREATURE:BUSHMASTER_MAN:BRAIN -CREATURE:BUSHMASTER_MAN:LUNG -CREATURE:BUSHMASTER_MAN:HEART -CREATURE:BUSHMASTER_MAN:LIVER -CREATURE:BUSHMASTER_MAN:GUT -CREATURE:BUSHMASTER_MAN:STOMACH -CREATURE:BUSHMASTER_MAN:GIZZARD - CREATURE:BUSHMASTER_MAN:PANCREAS -CREATURE:BUSHMASTER_MAN:SPLEEN -CREATURE:BUSHMASTER_MAN:KIDNEY - CREATURE:GIANT_BUSHMASTER:MUSCLE -CREATURE:GIANT_BUSHMASTER:EYE -CREATURE:GIANT_BUSHMASTER:BRAIN -CREATURE:GIANT_BUSHMASTER:LUNG -CREATURE:GIANT_BUSHMASTER:HEART -CREATURE:GIANT_BUSHMASTER:LIVER -CREATURE:GIANT_BUSHMASTER:GUT -!CREATURE:GIANT_BUSHMASTER:STOMACH -!CREATURE:GIANT_BUSHMASTER:GIZZARD -"CREATURE:GIANT_BUSHMASTER:PANCREAS - CREATURE:GIANT_BUSHMASTER:SPLEEN - CREATURE:GIANT_BUSHMASTER:KIDNEY -CREATURE:PYTHON:MUSCLE -CREATURE:PYTHON:EYE -CREATURE:PYTHON:BRAIN -CREATURE:PYTHON:LUNG -CREATURE:PYTHON:HEART -CREATURE:PYTHON:LIVER -CREATURE:PYTHON:GUT -CREATURE:PYTHON:STOMACH -CREATURE:PYTHON:GIZZARD -CREATURE:PYTHON:PANCREAS -CREATURE:PYTHON:SPLEEN -CREATURE:PYTHON:KIDNEY -CREATURE:PYTHON_MAN:MUSCLE -CREATURE:PYTHON_MAN:EYE -CREATURE:PYTHON_MAN:BRAIN -CREATURE:PYTHON_MAN:LUNG -CREATURE:PYTHON_MAN:HEART -CREATURE:PYTHON_MAN:LIVER -CREATURE:PYTHON_MAN:GUT -CREATURE:PYTHON_MAN:STOMACH -CREATURE:PYTHON_MAN:GIZZARD -CREATURE:PYTHON_MAN:PANCREAS -CREATURE:PYTHON_MAN:SPLEEN -CREATURE:PYTHON_MAN:KIDNEY -CREATURE:GIANT_PYTHON:MUSCLE -CREATURE:GIANT_PYTHON:EYE -CREATURE:GIANT_PYTHON:BRAIN -CREATURE:GIANT_PYTHON:LUNG -CREATURE:GIANT_PYTHON:HEART -CREATURE:GIANT_PYTHON:LIVER -CREATURE:GIANT_PYTHON:GUT -CREATURE:GIANT_PYTHON:STOMACH -CREATURE:GIANT_PYTHON:GIZZARD -CREATURE:GIANT_PYTHON:PANCREAS -CREATURE:GIANT_PYTHON:SPLEEN -CREATURE:GIANT_PYTHON:KIDNEY -CREATURE:TAPIR:MUSCLE -CREATURE:TAPIR:EYE -CREATURE:TAPIR:BRAIN -CREATURE:TAPIR:LUNG -CREATURE:TAPIR:HEART -CREATURE:TAPIR:LIVER -CREATURE:TAPIR:GUT -CREATURE:TAPIR:STOMACH -CREATURE:TAPIR:GIZZARD -CREATURE:TAPIR:PANCREAS -CREATURE:TAPIR:SPLEEN -CREATURE:TAPIR:KIDNEY -CREATURE:TAPIR_MAN:MUSCLE -CREATURE:TAPIR_MAN:EYE -CREATURE:TAPIR_MAN:BRAIN -CREATURE:TAPIR_MAN:LUNG -CREATURE:TAPIR_MAN:HEART -CREATURE:TAPIR_MAN:LIVER -CREATURE:TAPIR_MAN:GUT -CREATURE:TAPIR_MAN:STOMACH -CREATURE:TAPIR_MAN:GIZZARD -CREATURE:TAPIR_MAN:PANCREAS -CREATURE:TAPIR_MAN:SPLEEN -CREATURE:TAPIR_MAN:KIDNEY -CREATURE:GIANT_TAPIR:MUSCLE -CREATURE:GIANT_TAPIR:EYE -CREATURE:GIANT_TAPIR:BRAIN -CREATURE:GIANT_TAPIR:LUNG -CREATURE:GIANT_TAPIR:HEART -CREATURE:GIANT_TAPIR:LIVER -CREATURE:GIANT_TAPIR:GUT -CREATURE:GIANT_TAPIR:STOMACH -CREATURE:GIANT_TAPIR:GIZZARD -CREATURE:GIANT_TAPIR:PANCREAS -CREATURE:GIANT_TAPIR:SPLEEN -CREATURE:GIANT_TAPIR:KIDNEY -CREATURE:IMPALA:MUSCLE -CREATURE:IMPALA:EYE -CREATURE:IMPALA:BRAIN -CREATURE:IMPALA:LUNG -CREATURE:IMPALA:HEART -CREATURE:IMPALA:LIVER -CREATURE:IMPALA:GUT -CREATURE:IMPALA:STOMACH -CREATURE:IMPALA:GIZZARD -CREATURE:IMPALA:PANCREAS -CREATURE:IMPALA:SPLEEN -CREATURE:IMPALA:KIDNEY -CREATURE:IMPALA_MAN:MUSCLE -CREATURE:IMPALA_MAN:EYE -CREATURE:IMPALA_MAN:BRAIN -CREATURE:IMPALA_MAN:LUNG -CREATURE:IMPALA_MAN:HEART -CREATURE:IMPALA_MAN:LIVER -CREATURE:IMPALA_MAN:GUT -CREATURE:IMPALA_MAN:STOMACH -CREATURE:IMPALA_MAN:GIZZARD -CREATURE:IMPALA_MAN:PANCREAS -CREATURE:IMPALA_MAN:SPLEEN -CREATURE:IMPALA_MAN:KIDNEY -CREATURE:GIANT_IMPALA:MUSCLE -CREATURE:GIANT_IMPALA:EYE -CREATURE:GIANT_IMPALA:BRAIN -CREATURE:GIANT_IMPALA:LUNG -CREATURE:GIANT_IMPALA:HEART -CREATURE:GIANT_IMPALA:LIVER -CREATURE:GIANT_IMPALA:GUT -CREATURE:GIANT_IMPALA:STOMACH -CREATURE:GIANT_IMPALA:GIZZARD -CREATURE:GIANT_IMPALA:PANCREAS -CREATURE:GIANT_IMPALA:SPLEEN -CREATURE:GIANT_IMPALA:KIDNEY -CREATURE:AARDVARK:MUSCLE -CREATURE:AARDVARK:EYE -CREATURE:AARDVARK:BRAIN -CREATURE:AARDVARK:LUNG -CREATURE:AARDVARK:HEART -CREATURE:AARDVARK:LIVER -CREATURE:AARDVARK:GUT -CREATURE:AARDVARK:STOMACH -CREATURE:AARDVARK:GIZZARD -CREATURE:AARDVARK:PANCREAS -CREATURE:AARDVARK:SPLEEN -CREATURE:AARDVARK:KIDNEY -CREATURE:AARDVARK_MAN:MUSCLE -CREATURE:AARDVARK_MAN:EYE -CREATURE:AARDVARK_MAN:BRAIN -CREATURE:AARDVARK_MAN:LUNG -CREATURE:AARDVARK_MAN:HEART -CREATURE:AARDVARK_MAN:LIVER -CREATURE:AARDVARK_MAN:GUT -CREATURE:AARDVARK_MAN:STOMACH -CREATURE:AARDVARK_MAN:GIZZARD -CREATURE:AARDVARK_MAN:PANCREAS -CREATURE:AARDVARK_MAN:SPLEEN -CREATURE:AARDVARK_MAN:KIDNEY -CREATURE:GIANT_AARDVARK:MUSCLE -CREATURE:GIANT_AARDVARK:EYE -CREATURE:GIANT_AARDVARK:BRAIN -CREATURE:GIANT_AARDVARK:LUNG -CREATURE:GIANT_AARDVARK:HEART -CREATURE:GIANT_AARDVARK:LIVER -CREATURE:GIANT_AARDVARK:GUT -CREATURE:GIANT_AARDVARK:STOMACH -CREATURE:GIANT_AARDVARK:GIZZARD - CREATURE:GIANT_AARDVARK:PANCREAS -CREATURE:GIANT_AARDVARK:SPLEEN -CREATURE:GIANT_AARDVARK:KIDNEY -CREATURE:LION_TAMARIN:MUSCLE -CREATURE:LION_TAMARIN:EYE -CREATURE:LION_TAMARIN:BRAIN -CREATURE:LION_TAMARIN:LUNG -CREATURE:LION_TAMARIN:HEART -CREATURE:LION_TAMARIN:LIVER -CREATURE:LION_TAMARIN:GUT -CREATURE:LION_TAMARIN:STOMACH -CREATURE:LION_TAMARIN:GIZZARD -CREATURE:LION_TAMARIN:PANCREAS -CREATURE:LION_TAMARIN:SPLEEN -CREATURE:LION_TAMARIN:KIDNEY - CREATURE:LION_TAMARIN_MAN:MUSCLE -CREATURE:LION_TAMARIN_MAN:EYE -CREATURE:LION_TAMARIN_MAN:BRAIN -CREATURE:LION_TAMARIN_MAN:LUNG -CREATURE:LION_TAMARIN_MAN:HEART -CREATURE:LION_TAMARIN_MAN:LIVER -CREATURE:LION_TAMARIN_MAN:GUT -!CREATURE:LION_TAMARIN_MAN:STOMACH -!CREATURE:LION_TAMARIN_MAN:GIZZARD -"CREATURE:LION_TAMARIN_MAN:PANCREAS - CREATURE:LION_TAMARIN_MAN:SPLEEN - CREATURE:LION_TAMARIN_MAN:KIDNEY -"CREATURE:GIANT_LION_TAMARIN:MUSCLE -CREATURE:GIANT_LION_TAMARIN:EYE -!CREATURE:GIANT_LION_TAMARIN:BRAIN - CREATURE:GIANT_LION_TAMARIN:LUNG -!CREATURE:GIANT_LION_TAMARIN:HEART -!CREATURE:GIANT_LION_TAMARIN:LIVER -CREATURE:GIANT_LION_TAMARIN:GUT -#CREATURE:GIANT_LION_TAMARIN:STOMACH -#CREATURE:GIANT_LION_TAMARIN:GIZZARD -$CREATURE:GIANT_LION_TAMARIN:PANCREAS -"CREATURE:GIANT_LION_TAMARIN:SPLEEN -"CREATURE:GIANT_LION_TAMARIN:KIDNEY -CREATURE:STOAT:MUSCLE -CREATURE:STOAT:EYE -CREATURE:STOAT:BRAIN -CREATURE:STOAT:LUNG -CREATURE:STOAT:HEART -CREATURE:STOAT:LIVER -CREATURE:STOAT:GUT -CREATURE:STOAT:STOMACH -CREATURE:STOAT:GIZZARD -CREATURE:STOAT:PANCREAS -CREATURE:STOAT:SPLEEN -CREATURE:STOAT:KIDNEY -CREATURE:STOAT_MAN:MUSCLE -CREATURE:STOAT_MAN:EYE -CREATURE:STOAT_MAN:BRAIN -CREATURE:STOAT_MAN:LUNG -CREATURE:STOAT_MAN:HEART -CREATURE:STOAT_MAN:LIVER -CREATURE:STOAT_MAN:GUT -CREATURE:STOAT_MAN:STOMACH -CREATURE:STOAT_MAN:GIZZARD -CREATURE:STOAT_MAN:PANCREAS -CREATURE:STOAT_MAN:SPLEEN -CREATURE:STOAT_MAN:KIDNEY -CREATURE:GIANT_STOAT:MUSCLE -CREATURE:GIANT_STOAT:EYE -CREATURE:GIANT_STOAT:BRAIN -CREATURE:GIANT_STOAT:LUNG -CREATURE:GIANT_STOAT:HEART -CREATURE:GIANT_STOAT:LIVER -CREATURE:GIANT_STOAT:GUT -CREATURE:GIANT_STOAT:STOMACH -CREATURE:GIANT_STOAT:GIZZARD -CREATURE:GIANT_STOAT:PANCREAS -CREATURE:GIANT_STOAT:SPLEEN -CREATURE:GIANT_STOAT:KIDNEY -CREATURE:LYNX:MUSCLE -CREATURE:LYNX:EYE -CREATURE:LYNX:BRAIN -CREATURE:LYNX:LUNG -CREATURE:LYNX:HEART -CREATURE:LYNX:LIVER -CREATURE:LYNX:GUT -CREATURE:LYNX:STOMACH -CREATURE:LYNX:GIZZARD -CREATURE:LYNX:PANCREAS -CREATURE:LYNX:SPLEEN -CREATURE:LYNX:KIDNEY -CREATURE:LYNX_MAN:MUSCLE -CREATURE:LYNX_MAN:EYE -CREATURE:LYNX_MAN:BRAIN -CREATURE:LYNX_MAN:LUNG -CREATURE:LYNX_MAN:HEART -CREATURE:LYNX_MAN:LIVER -CREATURE:LYNX_MAN:GUT -CREATURE:LYNX_MAN:STOMACH -CREATURE:LYNX_MAN:GIZZARD -CREATURE:LYNX_MAN:PANCREAS -CREATURE:LYNX_MAN:SPLEEN -CREATURE:LYNX_MAN:KIDNEY -CREATURE:GIANT_LYNX:MUSCLE -CREATURE:GIANT_LYNX:EYE -CREATURE:GIANT_LYNX:BRAIN -CREATURE:GIANT_LYNX:LUNG -CREATURE:GIANT_LYNX:HEART -CREATURE:GIANT_LYNX:LIVER -CREATURE:GIANT_LYNX:GUT -CREATURE:GIANT_LYNX:STOMACH -CREATURE:GIANT_LYNX:GIZZARD -CREATURE:GIANT_LYNX:PANCREAS -CREATURE:GIANT_LYNX:SPLEEN -CREATURE:GIANT_LYNX:KIDNEY -CREATURE:GNOLL:MUSCLE -CREATURE:GNOLL:EYE -CREATURE:GNOLL:BRAIN -CREATURE:GNOLL:LUNG -CREATURE:GNOLL:HEART -CREATURE:GNOLL:LIVER -CREATURE:GNOLL:GUT -CREATURE:GNOLL:STOMACH -CREATURE:GNOLL:GIZZARD -CREATURE:GNOLL:PANCREAS -CREATURE:GNOLL:SPLEEN -CREATURE:GNOLL:KIDNEY -CREATURE:NAGA:MUSCLE -CREATURE:NAGA:EYE -CREATURE:NAGA:BRAIN -CREATURE:NAGA:LUNG -CREATURE:NAGA:HEART -CREATURE:NAGA:LIVER -CREATURE:NAGA:GUT -CREATURE:NAGA:STOMACH -CREATURE:NAGA:GIZZARD -CREATURE:NAGA:PANCREAS -CREATURE:NAGA:SPLEEN -CREATURE:NAGA:KIDNEY -!CREATURE:FORGOTTEN_BEAST_2:MUSCLE -CREATURE:FORGOTTEN_BEAST_2:EYE - CREATURE:FORGOTTEN_BEAST_2:BRAIN -CREATURE:FORGOTTEN_BEAST_2:LUNG - CREATURE:FORGOTTEN_BEAST_2:HEART - CREATURE:FORGOTTEN_BEAST_2:LIVER -CREATURE:FORGOTTEN_BEAST_2:GUT -"CREATURE:FORGOTTEN_BEAST_2:STOMACH -"CREATURE:FORGOTTEN_BEAST_2:GIZZARD -#CREATURE:FORGOTTEN_BEAST_2:PANCREAS -!CREATURE:FORGOTTEN_BEAST_2:SPLEEN -!CREATURE:FORGOTTEN_BEAST_2:KIDNEY -!CREATURE:FORGOTTEN_BEAST_4:MUSCLE -CREATURE:FORGOTTEN_BEAST_4:EYE - CREATURE:FORGOTTEN_BEAST_4:BRAIN -CREATURE:FORGOTTEN_BEAST_4:LUNG - CREATURE:FORGOTTEN_BEAST_4:HEART - CREATURE:FORGOTTEN_BEAST_4:LIVER -CREATURE:FORGOTTEN_BEAST_4:GUT -"CREATURE:FORGOTTEN_BEAST_4:STOMACH -"CREATURE:FORGOTTEN_BEAST_4:GIZZARD -#CREATURE:FORGOTTEN_BEAST_4:PANCREAS -!CREATURE:FORGOTTEN_BEAST_4:SPLEEN -!CREATURE:FORGOTTEN_BEAST_4:KIDNEY -!CREATURE:FORGOTTEN_BEAST_5:MUSCLE -CREATURE:FORGOTTEN_BEAST_5:EYE - CREATURE:FORGOTTEN_BEAST_5:BRAIN -CREATURE:FORGOTTEN_BEAST_5:LUNG - CREATURE:FORGOTTEN_BEAST_5:HEART - CREATURE:FORGOTTEN_BEAST_5:LIVER -CREATURE:FORGOTTEN_BEAST_5:GUT -"CREATURE:FORGOTTEN_BEAST_5:STOMACH -"CREATURE:FORGOTTEN_BEAST_5:GIZZARD -#CREATURE:FORGOTTEN_BEAST_5:PANCREAS -!CREATURE:FORGOTTEN_BEAST_5:SPLEEN -!CREATURE:FORGOTTEN_BEAST_5:KIDNEY -!CREATURE:FORGOTTEN_BEAST_6:MUSCLE -CREATURE:FORGOTTEN_BEAST_6:EYE - CREATURE:FORGOTTEN_BEAST_6:BRAIN -CREATURE:FORGOTTEN_BEAST_6:LUNG - CREATURE:FORGOTTEN_BEAST_6:HEART - CREATURE:FORGOTTEN_BEAST_6:LIVER -CREATURE:FORGOTTEN_BEAST_6:GUT -"CREATURE:FORGOTTEN_BEAST_6:STOMACH -"CREATURE:FORGOTTEN_BEAST_6:GIZZARD -#CREATURE:FORGOTTEN_BEAST_6:PANCREAS -!CREATURE:FORGOTTEN_BEAST_6:SPLEEN -!CREATURE:FORGOTTEN_BEAST_6:KIDNEY -!CREATURE:FORGOTTEN_BEAST_7:MUSCLE -CREATURE:FORGOTTEN_BEAST_7:EYE - CREATURE:FORGOTTEN_BEAST_7:BRAIN -CREATURE:FORGOTTEN_BEAST_7:LUNG - CREATURE:FORGOTTEN_BEAST_7:HEART - CREATURE:FORGOTTEN_BEAST_7:LIVER -CREATURE:FORGOTTEN_BEAST_7:GUT -"CREATURE:FORGOTTEN_BEAST_7:STOMACH -"CREATURE:FORGOTTEN_BEAST_7:GIZZARD -#CREATURE:FORGOTTEN_BEAST_7:PANCREAS -!CREATURE:FORGOTTEN_BEAST_7:SPLEEN -!CREATURE:FORGOTTEN_BEAST_7:KIDNEY -"CREATURE:FORGOTTEN_BEAST_10:MUSCLE -CREATURE:FORGOTTEN_BEAST_10:EYE -!CREATURE:FORGOTTEN_BEAST_10:BRAIN - CREATURE:FORGOTTEN_BEAST_10:LUNG -!CREATURE:FORGOTTEN_BEAST_10:HEART -!CREATURE:FORGOTTEN_BEAST_10:LIVER -CREATURE:FORGOTTEN_BEAST_10:GUT -#CREATURE:FORGOTTEN_BEAST_10:STOMACH -#CREATURE:FORGOTTEN_BEAST_10:GIZZARD -$CREATURE:FORGOTTEN_BEAST_10:PANCREAS -"CREATURE:FORGOTTEN_BEAST_10:SPLEEN -"CREATURE:FORGOTTEN_BEAST_10:KIDNEY -"CREATURE:FORGOTTEN_BEAST_12:MUSCLE -CREATURE:FORGOTTEN_BEAST_12:EYE -!CREATURE:FORGOTTEN_BEAST_12:BRAIN - CREATURE:FORGOTTEN_BEAST_12:LUNG -!CREATURE:FORGOTTEN_BEAST_12:HEART -!CREATURE:FORGOTTEN_BEAST_12:LIVER -CREATURE:FORGOTTEN_BEAST_12:GUT -#CREATURE:FORGOTTEN_BEAST_12:STOMACH -#CREATURE:FORGOTTEN_BEAST_12:GIZZARD -$CREATURE:FORGOTTEN_BEAST_12:PANCREAS -"CREATURE:FORGOTTEN_BEAST_12:SPLEEN -"CREATURE:FORGOTTEN_BEAST_12:KIDNEY -"CREATURE:FORGOTTEN_BEAST_13:MUSCLE -CREATURE:FORGOTTEN_BEAST_13:EYE -!CREATURE:FORGOTTEN_BEAST_13:BRAIN - CREATURE:FORGOTTEN_BEAST_13:LUNG -!CREATURE:FORGOTTEN_BEAST_13:HEART -!CREATURE:FORGOTTEN_BEAST_13:LIVER -CREATURE:FORGOTTEN_BEAST_13:GUT -#CREATURE:FORGOTTEN_BEAST_13:STOMACH -#CREATURE:FORGOTTEN_BEAST_13:GIZZARD -$CREATURE:FORGOTTEN_BEAST_13:PANCREAS -"CREATURE:FORGOTTEN_BEAST_13:SPLEEN -"CREATURE:FORGOTTEN_BEAST_13:KIDNEY -"CREATURE:FORGOTTEN_BEAST_16:MUSCLE -CREATURE:FORGOTTEN_BEAST_16:EYE -!CREATURE:FORGOTTEN_BEAST_16:BRAIN - CREATURE:FORGOTTEN_BEAST_16:LUNG -!CREATURE:FORGOTTEN_BEAST_16:HEART -!CREATURE:FORGOTTEN_BEAST_16:LIVER -CREATURE:FORGOTTEN_BEAST_16:GUT -#CREATURE:FORGOTTEN_BEAST_16:STOMACH -#CREATURE:FORGOTTEN_BEAST_16:GIZZARD -$CREATURE:FORGOTTEN_BEAST_16:PANCREAS -"CREATURE:FORGOTTEN_BEAST_16:SPLEEN -"CREATURE:FORGOTTEN_BEAST_16:KIDNEY -"CREATURE:FORGOTTEN_BEAST_17:MUSCLE -CREATURE:FORGOTTEN_BEAST_17:EYE -!CREATURE:FORGOTTEN_BEAST_17:BRAIN - CREATURE:FORGOTTEN_BEAST_17:LUNG -!CREATURE:FORGOTTEN_BEAST_17:HEART -!CREATURE:FORGOTTEN_BEAST_17:LIVER -CREATURE:FORGOTTEN_BEAST_17:GUT -#CREATURE:FORGOTTEN_BEAST_17:STOMACH -#CREATURE:FORGOTTEN_BEAST_17:GIZZARD -$CREATURE:FORGOTTEN_BEAST_17:PANCREAS -"CREATURE:FORGOTTEN_BEAST_17:SPLEEN -"CREATURE:FORGOTTEN_BEAST_17:KIDNEY -"CREATURE:FORGOTTEN_BEAST_18:MUSCLE -CREATURE:FORGOTTEN_BEAST_18:EYE -!CREATURE:FORGOTTEN_BEAST_18:BRAIN - CREATURE:FORGOTTEN_BEAST_18:LUNG -!CREATURE:FORGOTTEN_BEAST_18:HEART -!CREATURE:FORGOTTEN_BEAST_18:LIVER -CREATURE:FORGOTTEN_BEAST_18:GUT -#CREATURE:FORGOTTEN_BEAST_18:STOMACH -#CREATURE:FORGOTTEN_BEAST_18:GIZZARD -$CREATURE:FORGOTTEN_BEAST_18:PANCREAS -"CREATURE:FORGOTTEN_BEAST_18:SPLEEN -"CREATURE:FORGOTTEN_BEAST_18:KIDNEY -"CREATURE:FORGOTTEN_BEAST_19:MUSCLE -CREATURE:FORGOTTEN_BEAST_19:EYE -!CREATURE:FORGOTTEN_BEAST_19:BRAIN - CREATURE:FORGOTTEN_BEAST_19:LUNG -!CREATURE:FORGOTTEN_BEAST_19:HEART -!CREATURE:FORGOTTEN_BEAST_19:LIVER -CREATURE:FORGOTTEN_BEAST_19:GUT -#CREATURE:FORGOTTEN_BEAST_19:STOMACH -#CREATURE:FORGOTTEN_BEAST_19:GIZZARD -$CREATURE:FORGOTTEN_BEAST_19:PANCREAS -"CREATURE:FORGOTTEN_BEAST_19:SPLEEN -"CREATURE:FORGOTTEN_BEAST_19:KIDNEY -"CREATURE:FORGOTTEN_BEAST_20:MUSCLE -CREATURE:FORGOTTEN_BEAST_20:EYE -!CREATURE:FORGOTTEN_BEAST_20:BRAIN - CREATURE:FORGOTTEN_BEAST_20:LUNG -!CREATURE:FORGOTTEN_BEAST_20:HEART -!CREATURE:FORGOTTEN_BEAST_20:LIVER -CREATURE:FORGOTTEN_BEAST_20:GUT -#CREATURE:FORGOTTEN_BEAST_20:STOMACH -#CREATURE:FORGOTTEN_BEAST_20:GIZZARD -$CREATURE:FORGOTTEN_BEAST_20:PANCREAS -"CREATURE:FORGOTTEN_BEAST_20:SPLEEN -"CREATURE:FORGOTTEN_BEAST_20:KIDNEY -"CREATURE:FORGOTTEN_BEAST_22:MUSCLE -CREATURE:FORGOTTEN_BEAST_22:EYE -!CREATURE:FORGOTTEN_BEAST_22:BRAIN - CREATURE:FORGOTTEN_BEAST_22:LUNG -!CREATURE:FORGOTTEN_BEAST_22:HEART -!CREATURE:FORGOTTEN_BEAST_22:LIVER -CREATURE:FORGOTTEN_BEAST_22:GUT -#CREATURE:FORGOTTEN_BEAST_22:STOMACH -#CREATURE:FORGOTTEN_BEAST_22:GIZZARD -$CREATURE:FORGOTTEN_BEAST_22:PANCREAS -"CREATURE:FORGOTTEN_BEAST_22:SPLEEN -"CREATURE:FORGOTTEN_BEAST_22:KIDNEY -"CREATURE:FORGOTTEN_BEAST_23:MUSCLE -CREATURE:FORGOTTEN_BEAST_23:EYE -!CREATURE:FORGOTTEN_BEAST_23:BRAIN - CREATURE:FORGOTTEN_BEAST_23:LUNG -!CREATURE:FORGOTTEN_BEAST_23:HEART -!CREATURE:FORGOTTEN_BEAST_23:LIVER -CREATURE:FORGOTTEN_BEAST_23:GUT -#CREATURE:FORGOTTEN_BEAST_23:STOMACH -#CREATURE:FORGOTTEN_BEAST_23:GIZZARD -$CREATURE:FORGOTTEN_BEAST_23:PANCREAS -"CREATURE:FORGOTTEN_BEAST_23:SPLEEN -"CREATURE:FORGOTTEN_BEAST_23:KIDNEY -"CREATURE:FORGOTTEN_BEAST_24:MUSCLE -CREATURE:FORGOTTEN_BEAST_24:EYE -!CREATURE:FORGOTTEN_BEAST_24:BRAIN - CREATURE:FORGOTTEN_BEAST_24:LUNG -!CREATURE:FORGOTTEN_BEAST_24:HEART -!CREATURE:FORGOTTEN_BEAST_24:LIVER -CREATURE:FORGOTTEN_BEAST_24:GUT -#CREATURE:FORGOTTEN_BEAST_24:STOMACH -#CREATURE:FORGOTTEN_BEAST_24:GIZZARD -$CREATURE:FORGOTTEN_BEAST_24:PANCREAS -"CREATURE:FORGOTTEN_BEAST_24:SPLEEN -"CREATURE:FORGOTTEN_BEAST_24:KIDNEY -"CREATURE:FORGOTTEN_BEAST_25:MUSCLE -CREATURE:FORGOTTEN_BEAST_25:EYE -!CREATURE:FORGOTTEN_BEAST_25:BRAIN - CREATURE:FORGOTTEN_BEAST_25:LUNG -!CREATURE:FORGOTTEN_BEAST_25:HEART -!CREATURE:FORGOTTEN_BEAST_25:LIVER -CREATURE:FORGOTTEN_BEAST_25:GUT -#CREATURE:FORGOTTEN_BEAST_25:STOMACH -#CREATURE:FORGOTTEN_BEAST_25:GIZZARD -$CREATURE:FORGOTTEN_BEAST_25:PANCREAS -"CREATURE:FORGOTTEN_BEAST_25:SPLEEN -"CREATURE:FORGOTTEN_BEAST_25:KIDNEY -"CREATURE:FORGOTTEN_BEAST_26:MUSCLE -CREATURE:FORGOTTEN_BEAST_26:EYE -!CREATURE:FORGOTTEN_BEAST_26:BRAIN - CREATURE:FORGOTTEN_BEAST_26:LUNG -!CREATURE:FORGOTTEN_BEAST_26:HEART -!CREATURE:FORGOTTEN_BEAST_26:LIVER -CREATURE:FORGOTTEN_BEAST_26:GUT -#CREATURE:FORGOTTEN_BEAST_26:STOMACH -#CREATURE:FORGOTTEN_BEAST_26:GIZZARD -$CREATURE:FORGOTTEN_BEAST_26:PANCREAS -"CREATURE:FORGOTTEN_BEAST_26:SPLEEN -"CREATURE:FORGOTTEN_BEAST_26:KIDNEY -"CREATURE:FORGOTTEN_BEAST_27:MUSCLE -CREATURE:FORGOTTEN_BEAST_27:EYE -!CREATURE:FORGOTTEN_BEAST_27:BRAIN - CREATURE:FORGOTTEN_BEAST_27:LUNG -!CREATURE:FORGOTTEN_BEAST_27:HEART -!CREATURE:FORGOTTEN_BEAST_27:LIVER -CREATURE:FORGOTTEN_BEAST_27:GUT -#CREATURE:FORGOTTEN_BEAST_27:STOMACH -#CREATURE:FORGOTTEN_BEAST_27:GIZZARD -$CREATURE:FORGOTTEN_BEAST_27:PANCREAS -"CREATURE:FORGOTTEN_BEAST_27:SPLEEN -"CREATURE:FORGOTTEN_BEAST_27:KIDNEY -"CREATURE:FORGOTTEN_BEAST_28:MUSCLE -CREATURE:FORGOTTEN_BEAST_28:EYE -!CREATURE:FORGOTTEN_BEAST_28:BRAIN - CREATURE:FORGOTTEN_BEAST_28:LUNG -!CREATURE:FORGOTTEN_BEAST_28:HEART -!CREATURE:FORGOTTEN_BEAST_28:LIVER -CREATURE:FORGOTTEN_BEAST_28:GUT -#CREATURE:FORGOTTEN_BEAST_28:STOMACH -#CREATURE:FORGOTTEN_BEAST_28:GIZZARD -$CREATURE:FORGOTTEN_BEAST_28:PANCREAS -"CREATURE:FORGOTTEN_BEAST_28:SPLEEN -"CREATURE:FORGOTTEN_BEAST_28:KIDNEY -"CREATURE:FORGOTTEN_BEAST_29:MUSCLE -CREATURE:FORGOTTEN_BEAST_29:EYE -!CREATURE:FORGOTTEN_BEAST_29:BRAIN - CREATURE:FORGOTTEN_BEAST_29:LUNG -!CREATURE:FORGOTTEN_BEAST_29:HEART -!CREATURE:FORGOTTEN_BEAST_29:LIVER -CREATURE:FORGOTTEN_BEAST_29:GUT -#CREATURE:FORGOTTEN_BEAST_29:STOMACH -#CREATURE:FORGOTTEN_BEAST_29:GIZZARD -$CREATURE:FORGOTTEN_BEAST_29:PANCREAS -"CREATURE:FORGOTTEN_BEAST_29:SPLEEN -"CREATURE:FORGOTTEN_BEAST_29:KIDNEY -"CREATURE:FORGOTTEN_BEAST_32:MUSCLE -CREATURE:FORGOTTEN_BEAST_32:EYE -!CREATURE:FORGOTTEN_BEAST_32:BRAIN - CREATURE:FORGOTTEN_BEAST_32:LUNG -!CREATURE:FORGOTTEN_BEAST_32:HEART -!CREATURE:FORGOTTEN_BEAST_32:LIVER -CREATURE:FORGOTTEN_BEAST_32:GUT -#CREATURE:FORGOTTEN_BEAST_32:STOMACH -#CREATURE:FORGOTTEN_BEAST_32:GIZZARD -$CREATURE:FORGOTTEN_BEAST_32:PANCREAS -"CREATURE:FORGOTTEN_BEAST_32:SPLEEN -"CREATURE:FORGOTTEN_BEAST_32:KIDNEY -"CREATURE:FORGOTTEN_BEAST_33:MUSCLE -CREATURE:FORGOTTEN_BEAST_33:EYE -!CREATURE:FORGOTTEN_BEAST_33:BRAIN - CREATURE:FORGOTTEN_BEAST_33:LUNG -!CREATURE:FORGOTTEN_BEAST_33:HEART -!CREATURE:FORGOTTEN_BEAST_33:LIVER -CREATURE:FORGOTTEN_BEAST_33:GUT -#CREATURE:FORGOTTEN_BEAST_33:STOMACH -#CREATURE:FORGOTTEN_BEAST_33:GIZZARD -$CREATURE:FORGOTTEN_BEAST_33:PANCREAS -"CREATURE:FORGOTTEN_BEAST_33:SPLEEN -"CREATURE:FORGOTTEN_BEAST_33:KIDNEY -"CREATURE:FORGOTTEN_BEAST_34:MUSCLE -CREATURE:FORGOTTEN_BEAST_34:EYE -!CREATURE:FORGOTTEN_BEAST_34:BRAIN - CREATURE:FORGOTTEN_BEAST_34:LUNG -!CREATURE:FORGOTTEN_BEAST_34:HEART -!CREATURE:FORGOTTEN_BEAST_34:LIVER -CREATURE:FORGOTTEN_BEAST_34:GUT -#CREATURE:FORGOTTEN_BEAST_34:STOMACH -#CREATURE:FORGOTTEN_BEAST_34:GIZZARD -$CREATURE:FORGOTTEN_BEAST_34:PANCREAS -"CREATURE:FORGOTTEN_BEAST_34:SPLEEN -"CREATURE:FORGOTTEN_BEAST_34:KIDNEY -"CREATURE:FORGOTTEN_BEAST_35:MUSCLE -CREATURE:FORGOTTEN_BEAST_35:EYE -!CREATURE:FORGOTTEN_BEAST_35:BRAIN - CREATURE:FORGOTTEN_BEAST_35:LUNG -!CREATURE:FORGOTTEN_BEAST_35:HEART -!CREATURE:FORGOTTEN_BEAST_35:LIVER -CREATURE:FORGOTTEN_BEAST_35:GUT -#CREATURE:FORGOTTEN_BEAST_35:STOMACH -#CREATURE:FORGOTTEN_BEAST_35:GIZZARD -$CREATURE:FORGOTTEN_BEAST_35:PANCREAS -"CREATURE:FORGOTTEN_BEAST_35:SPLEEN -"CREATURE:FORGOTTEN_BEAST_35:KIDNEY -"CREATURE:FORGOTTEN_BEAST_36:MUSCLE -CREATURE:FORGOTTEN_BEAST_36:EYE -!CREATURE:FORGOTTEN_BEAST_36:BRAIN - CREATURE:FORGOTTEN_BEAST_36:LUNG -!CREATURE:FORGOTTEN_BEAST_36:HEART -!CREATURE:FORGOTTEN_BEAST_36:LIVER -CREATURE:FORGOTTEN_BEAST_36:GUT -#CREATURE:FORGOTTEN_BEAST_36:STOMACH -#CREATURE:FORGOTTEN_BEAST_36:GIZZARD -$CREATURE:FORGOTTEN_BEAST_36:PANCREAS -"CREATURE:FORGOTTEN_BEAST_36:SPLEEN -"CREATURE:FORGOTTEN_BEAST_36:KIDNEY -"CREATURE:FORGOTTEN_BEAST_39:MUSCLE -CREATURE:FORGOTTEN_BEAST_39:EYE -!CREATURE:FORGOTTEN_BEAST_39:BRAIN - CREATURE:FORGOTTEN_BEAST_39:LUNG -!CREATURE:FORGOTTEN_BEAST_39:HEART -!CREATURE:FORGOTTEN_BEAST_39:LIVER -CREATURE:FORGOTTEN_BEAST_39:GUT -#CREATURE:FORGOTTEN_BEAST_39:STOMACH -#CREATURE:FORGOTTEN_BEAST_39:GIZZARD -$CREATURE:FORGOTTEN_BEAST_39:PANCREAS -"CREATURE:FORGOTTEN_BEAST_39:SPLEEN -"CREATURE:FORGOTTEN_BEAST_39:KIDNEY -"CREATURE:FORGOTTEN_BEAST_41:MUSCLE -CREATURE:FORGOTTEN_BEAST_41:EYE -!CREATURE:FORGOTTEN_BEAST_41:BRAIN - CREATURE:FORGOTTEN_BEAST_41:LUNG -!CREATURE:FORGOTTEN_BEAST_41:HEART -!CREATURE:FORGOTTEN_BEAST_41:LIVER -CREATURE:FORGOTTEN_BEAST_41:GUT -#CREATURE:FORGOTTEN_BEAST_41:STOMACH -#CREATURE:FORGOTTEN_BEAST_41:GIZZARD -$CREATURE:FORGOTTEN_BEAST_41:PANCREAS -"CREATURE:FORGOTTEN_BEAST_41:SPLEEN -"CREATURE:FORGOTTEN_BEAST_41:KIDNEY -"CREATURE:FORGOTTEN_BEAST_42:MUSCLE -CREATURE:FORGOTTEN_BEAST_42:EYE -!CREATURE:FORGOTTEN_BEAST_42:BRAIN - CREATURE:FORGOTTEN_BEAST_42:LUNG -!CREATURE:FORGOTTEN_BEAST_42:HEART -!CREATURE:FORGOTTEN_BEAST_42:LIVER -CREATURE:FORGOTTEN_BEAST_42:GUT -#CREATURE:FORGOTTEN_BEAST_42:STOMACH -#CREATURE:FORGOTTEN_BEAST_42:GIZZARD -$CREATURE:FORGOTTEN_BEAST_42:PANCREAS -"CREATURE:FORGOTTEN_BEAST_42:SPLEEN -"CREATURE:FORGOTTEN_BEAST_42:KIDNEY -"CREATURE:FORGOTTEN_BEAST_43:MUSCLE -CREATURE:FORGOTTEN_BEAST_43:EYE -!CREATURE:FORGOTTEN_BEAST_43:BRAIN - CREATURE:FORGOTTEN_BEAST_43:LUNG -!CREATURE:FORGOTTEN_BEAST_43:HEART -!CREATURE:FORGOTTEN_BEAST_43:LIVER -CREATURE:FORGOTTEN_BEAST_43:GUT -#CREATURE:FORGOTTEN_BEAST_43:STOMACH -#CREATURE:FORGOTTEN_BEAST_43:GIZZARD -$CREATURE:FORGOTTEN_BEAST_43:PANCREAS -"CREATURE:FORGOTTEN_BEAST_43:SPLEEN -"CREATURE:FORGOTTEN_BEAST_43:KIDNEY -"CREATURE:FORGOTTEN_BEAST_44:MUSCLE -CREATURE:FORGOTTEN_BEAST_44:EYE -!CREATURE:FORGOTTEN_BEAST_44:BRAIN - CREATURE:FORGOTTEN_BEAST_44:LUNG -!CREATURE:FORGOTTEN_BEAST_44:HEART -!CREATURE:FORGOTTEN_BEAST_44:LIVER -CREATURE:FORGOTTEN_BEAST_44:GUT -#CREATURE:FORGOTTEN_BEAST_44:STOMACH -#CREATURE:FORGOTTEN_BEAST_44:GIZZARD -$CREATURE:FORGOTTEN_BEAST_44:PANCREAS -"CREATURE:FORGOTTEN_BEAST_44:SPLEEN -"CREATURE:FORGOTTEN_BEAST_44:KIDNEY -"CREATURE:FORGOTTEN_BEAST_45:MUSCLE -CREATURE:FORGOTTEN_BEAST_45:EYE -!CREATURE:FORGOTTEN_BEAST_45:BRAIN - CREATURE:FORGOTTEN_BEAST_45:LUNG -!CREATURE:FORGOTTEN_BEAST_45:HEART -!CREATURE:FORGOTTEN_BEAST_45:LIVER -CREATURE:FORGOTTEN_BEAST_45:GUT -#CREATURE:FORGOTTEN_BEAST_45:STOMACH -#CREATURE:FORGOTTEN_BEAST_45:GIZZARD -$CREATURE:FORGOTTEN_BEAST_45:PANCREAS -"CREATURE:FORGOTTEN_BEAST_45:SPLEEN -"CREATURE:FORGOTTEN_BEAST_45:KIDNEY -"CREATURE:FORGOTTEN_BEAST_47:MUSCLE -CREATURE:FORGOTTEN_BEAST_47:EYE -!CREATURE:FORGOTTEN_BEAST_47:BRAIN - CREATURE:FORGOTTEN_BEAST_47:LUNG -!CREATURE:FORGOTTEN_BEAST_47:HEART -!CREATURE:FORGOTTEN_BEAST_47:LIVER -CREATURE:FORGOTTEN_BEAST_47:GUT -#CREATURE:FORGOTTEN_BEAST_47:STOMACH -#CREATURE:FORGOTTEN_BEAST_47:GIZZARD -$CREATURE:FORGOTTEN_BEAST_47:PANCREAS -"CREATURE:FORGOTTEN_BEAST_47:SPLEEN -"CREATURE:FORGOTTEN_BEAST_47:KIDNEY -"CREATURE:FORGOTTEN_BEAST_50:MUSCLE -CREATURE:FORGOTTEN_BEAST_50:EYE -!CREATURE:FORGOTTEN_BEAST_50:BRAIN - CREATURE:FORGOTTEN_BEAST_50:LUNG -!CREATURE:FORGOTTEN_BEAST_50:HEART -!CREATURE:FORGOTTEN_BEAST_50:LIVER -CREATURE:FORGOTTEN_BEAST_50:GUT -#CREATURE:FORGOTTEN_BEAST_50:STOMACH -#CREATURE:FORGOTTEN_BEAST_50:GIZZARD -$CREATURE:FORGOTTEN_BEAST_50:PANCREAS -"CREATURE:FORGOTTEN_BEAST_50:SPLEEN -"CREATURE:FORGOTTEN_BEAST_50:KIDNEY -"CREATURE:FORGOTTEN_BEAST_52:MUSCLE -CREATURE:FORGOTTEN_BEAST_52:EYE -!CREATURE:FORGOTTEN_BEAST_52:BRAIN - CREATURE:FORGOTTEN_BEAST_52:LUNG -!CREATURE:FORGOTTEN_BEAST_52:HEART -!CREATURE:FORGOTTEN_BEAST_52:LIVER -CREATURE:FORGOTTEN_BEAST_52:GUT -#CREATURE:FORGOTTEN_BEAST_52:STOMACH -#CREATURE:FORGOTTEN_BEAST_52:GIZZARD -$CREATURE:FORGOTTEN_BEAST_52:PANCREAS -"CREATURE:FORGOTTEN_BEAST_52:SPLEEN -"CREATURE:FORGOTTEN_BEAST_52:KIDNEY -"CREATURE:FORGOTTEN_BEAST_53:MUSCLE -CREATURE:FORGOTTEN_BEAST_53:EYE -!CREATURE:FORGOTTEN_BEAST_53:BRAIN - CREATURE:FORGOTTEN_BEAST_53:LUNG -!CREATURE:FORGOTTEN_BEAST_53:HEART -!CREATURE:FORGOTTEN_BEAST_53:LIVER -CREATURE:FORGOTTEN_BEAST_53:GUT -#CREATURE:FORGOTTEN_BEAST_53:STOMACH -#CREATURE:FORGOTTEN_BEAST_53:GIZZARD -$CREATURE:FORGOTTEN_BEAST_53:PANCREAS -"CREATURE:FORGOTTEN_BEAST_53:SPLEEN -"CREATURE:FORGOTTEN_BEAST_53:KIDNEY -"CREATURE:FORGOTTEN_BEAST_55:MUSCLE -CREATURE:FORGOTTEN_BEAST_55:EYE -!CREATURE:FORGOTTEN_BEAST_55:BRAIN - CREATURE:FORGOTTEN_BEAST_55:LUNG -!CREATURE:FORGOTTEN_BEAST_55:HEART -!CREATURE:FORGOTTEN_BEAST_55:LIVER -CREATURE:FORGOTTEN_BEAST_55:GUT -#CREATURE:FORGOTTEN_BEAST_55:STOMACH -#CREATURE:FORGOTTEN_BEAST_55:GIZZARD -$CREATURE:FORGOTTEN_BEAST_55:PANCREAS -"CREATURE:FORGOTTEN_BEAST_55:SPLEEN -"CREATURE:FORGOTTEN_BEAST_55:KIDNEY -"CREATURE:FORGOTTEN_BEAST_56:MUSCLE -CREATURE:FORGOTTEN_BEAST_56:EYE -!CREATURE:FORGOTTEN_BEAST_56:BRAIN - CREATURE:FORGOTTEN_BEAST_56:LUNG -!CREATURE:FORGOTTEN_BEAST_56:HEART -!CREATURE:FORGOTTEN_BEAST_56:LIVER -CREATURE:FORGOTTEN_BEAST_56:GUT -#CREATURE:FORGOTTEN_BEAST_56:STOMACH -#CREATURE:FORGOTTEN_BEAST_56:GIZZARD -$CREATURE:FORGOTTEN_BEAST_56:PANCREAS -"CREATURE:FORGOTTEN_BEAST_56:SPLEEN -"CREATURE:FORGOTTEN_BEAST_56:KIDNEY -"CREATURE:FORGOTTEN_BEAST_58:MUSCLE -CREATURE:FORGOTTEN_BEAST_58:EYE -!CREATURE:FORGOTTEN_BEAST_58:BRAIN - CREATURE:FORGOTTEN_BEAST_58:LUNG -!CREATURE:FORGOTTEN_BEAST_58:HEART -!CREATURE:FORGOTTEN_BEAST_58:LIVER -CREATURE:FORGOTTEN_BEAST_58:GUT -#CREATURE:FORGOTTEN_BEAST_58:STOMACH -#CREATURE:FORGOTTEN_BEAST_58:GIZZARD -$CREATURE:FORGOTTEN_BEAST_58:PANCREAS -"CREATURE:FORGOTTEN_BEAST_58:SPLEEN -"CREATURE:FORGOTTEN_BEAST_58:KIDNEY -"CREATURE:FORGOTTEN_BEAST_59:MUSCLE -CREATURE:FORGOTTEN_BEAST_59:EYE -!CREATURE:FORGOTTEN_BEAST_59:BRAIN - CREATURE:FORGOTTEN_BEAST_59:LUNG -!CREATURE:FORGOTTEN_BEAST_59:HEART -!CREATURE:FORGOTTEN_BEAST_59:LIVER -CREATURE:FORGOTTEN_BEAST_59:GUT -#CREATURE:FORGOTTEN_BEAST_59:STOMACH -#CREATURE:FORGOTTEN_BEAST_59:GIZZARD -$CREATURE:FORGOTTEN_BEAST_59:PANCREAS -"CREATURE:FORGOTTEN_BEAST_59:SPLEEN -"CREATURE:FORGOTTEN_BEAST_59:KIDNEY -"CREATURE:FORGOTTEN_BEAST_60:MUSCLE -CREATURE:FORGOTTEN_BEAST_60:EYE -!CREATURE:FORGOTTEN_BEAST_60:BRAIN - CREATURE:FORGOTTEN_BEAST_60:LUNG -!CREATURE:FORGOTTEN_BEAST_60:HEART -!CREATURE:FORGOTTEN_BEAST_60:LIVER -CREATURE:FORGOTTEN_BEAST_60:GUT -#CREATURE:FORGOTTEN_BEAST_60:STOMACH -#CREATURE:FORGOTTEN_BEAST_60:GIZZARD -$CREATURE:FORGOTTEN_BEAST_60:PANCREAS -"CREATURE:FORGOTTEN_BEAST_60:SPLEEN -"CREATURE:FORGOTTEN_BEAST_60:KIDNEY -"CREATURE:FORGOTTEN_BEAST_61:MUSCLE -CREATURE:FORGOTTEN_BEAST_61:EYE -!CREATURE:FORGOTTEN_BEAST_61:BRAIN - CREATURE:FORGOTTEN_BEAST_61:LUNG -!CREATURE:FORGOTTEN_BEAST_61:HEART -!CREATURE:FORGOTTEN_BEAST_61:LIVER -CREATURE:FORGOTTEN_BEAST_61:GUT -#CREATURE:FORGOTTEN_BEAST_61:STOMACH -#CREATURE:FORGOTTEN_BEAST_61:GIZZARD -$CREATURE:FORGOTTEN_BEAST_61:PANCREAS -"CREATURE:FORGOTTEN_BEAST_61:SPLEEN -"CREATURE:FORGOTTEN_BEAST_61:KIDNEY -"CREATURE:FORGOTTEN_BEAST_64:MUSCLE -CREATURE:FORGOTTEN_BEAST_64:EYE -!CREATURE:FORGOTTEN_BEAST_64:BRAIN - CREATURE:FORGOTTEN_BEAST_64:LUNG -!CREATURE:FORGOTTEN_BEAST_64:HEART -!CREATURE:FORGOTTEN_BEAST_64:LIVER -CREATURE:FORGOTTEN_BEAST_64:GUT -#CREATURE:FORGOTTEN_BEAST_64:STOMACH -#CREATURE:FORGOTTEN_BEAST_64:GIZZARD -$CREATURE:FORGOTTEN_BEAST_64:PANCREAS -"CREATURE:FORGOTTEN_BEAST_64:SPLEEN -"CREATURE:FORGOTTEN_BEAST_64:KIDNEY -"CREATURE:FORGOTTEN_BEAST_66:MUSCLE -CREATURE:FORGOTTEN_BEAST_66:EYE -!CREATURE:FORGOTTEN_BEAST_66:BRAIN - CREATURE:FORGOTTEN_BEAST_66:LUNG -!CREATURE:FORGOTTEN_BEAST_66:HEART -!CREATURE:FORGOTTEN_BEAST_66:LIVER -CREATURE:FORGOTTEN_BEAST_66:GUT -#CREATURE:FORGOTTEN_BEAST_66:STOMACH -#CREATURE:FORGOTTEN_BEAST_66:GIZZARD -$CREATURE:FORGOTTEN_BEAST_66:PANCREAS -"CREATURE:FORGOTTEN_BEAST_66:SPLEEN -"CREATURE:FORGOTTEN_BEAST_66:KIDNEY -"CREATURE:FORGOTTEN_BEAST_67:MUSCLE -CREATURE:FORGOTTEN_BEAST_67:EYE -!CREATURE:FORGOTTEN_BEAST_67:BRAIN - CREATURE:FORGOTTEN_BEAST_67:LUNG -!CREATURE:FORGOTTEN_BEAST_67:HEART -!CREATURE:FORGOTTEN_BEAST_67:LIVER -CREATURE:FORGOTTEN_BEAST_67:GUT -#CREATURE:FORGOTTEN_BEAST_67:STOMACH -#CREATURE:FORGOTTEN_BEAST_67:GIZZARD -$CREATURE:FORGOTTEN_BEAST_67:PANCREAS -"CREATURE:FORGOTTEN_BEAST_67:SPLEEN -"CREATURE:FORGOTTEN_BEAST_67:KIDNEY -"CREATURE:FORGOTTEN_BEAST_69:MUSCLE -CREATURE:FORGOTTEN_BEAST_69:EYE -!CREATURE:FORGOTTEN_BEAST_69:BRAIN - CREATURE:FORGOTTEN_BEAST_69:LUNG -!CREATURE:FORGOTTEN_BEAST_69:HEART -!CREATURE:FORGOTTEN_BEAST_69:LIVER -CREATURE:FORGOTTEN_BEAST_69:GUT -#CREATURE:FORGOTTEN_BEAST_69:STOMACH -#CREATURE:FORGOTTEN_BEAST_69:GIZZARD -$CREATURE:FORGOTTEN_BEAST_69:PANCREAS -"CREATURE:FORGOTTEN_BEAST_69:SPLEEN -"CREATURE:FORGOTTEN_BEAST_69:KIDNEY -"CREATURE:FORGOTTEN_BEAST_71:MUSCLE -CREATURE:FORGOTTEN_BEAST_71:EYE -!CREATURE:FORGOTTEN_BEAST_71:BRAIN - CREATURE:FORGOTTEN_BEAST_71:LUNG -!CREATURE:FORGOTTEN_BEAST_71:HEART -!CREATURE:FORGOTTEN_BEAST_71:LIVER -CREATURE:FORGOTTEN_BEAST_71:GUT -#CREATURE:FORGOTTEN_BEAST_71:STOMACH -#CREATURE:FORGOTTEN_BEAST_71:GIZZARD -$CREATURE:FORGOTTEN_BEAST_71:PANCREAS -"CREATURE:FORGOTTEN_BEAST_71:SPLEEN -"CREATURE:FORGOTTEN_BEAST_71:KIDNEY -"CREATURE:FORGOTTEN_BEAST_72:MUSCLE -CREATURE:FORGOTTEN_BEAST_72:EYE -!CREATURE:FORGOTTEN_BEAST_72:BRAIN - CREATURE:FORGOTTEN_BEAST_72:LUNG -!CREATURE:FORGOTTEN_BEAST_72:HEART -!CREATURE:FORGOTTEN_BEAST_72:LIVER -CREATURE:FORGOTTEN_BEAST_72:GUT -#CREATURE:FORGOTTEN_BEAST_72:STOMACH -#CREATURE:FORGOTTEN_BEAST_72:GIZZARD -$CREATURE:FORGOTTEN_BEAST_72:PANCREAS -"CREATURE:FORGOTTEN_BEAST_72:SPLEEN -"CREATURE:FORGOTTEN_BEAST_72:KIDNEY -"CREATURE:FORGOTTEN_BEAST_73:MUSCLE -CREATURE:FORGOTTEN_BEAST_73:EYE -!CREATURE:FORGOTTEN_BEAST_73:BRAIN - CREATURE:FORGOTTEN_BEAST_73:LUNG -!CREATURE:FORGOTTEN_BEAST_73:HEART -!CREATURE:FORGOTTEN_BEAST_73:LIVER -CREATURE:FORGOTTEN_BEAST_73:GUT -#CREATURE:FORGOTTEN_BEAST_73:STOMACH -#CREATURE:FORGOTTEN_BEAST_73:GIZZARD -$CREATURE:FORGOTTEN_BEAST_73:PANCREAS -"CREATURE:FORGOTTEN_BEAST_73:SPLEEN -"CREATURE:FORGOTTEN_BEAST_73:KIDNEY -"CREATURE:FORGOTTEN_BEAST_74:MUSCLE -CREATURE:FORGOTTEN_BEAST_74:EYE -!CREATURE:FORGOTTEN_BEAST_74:BRAIN - CREATURE:FORGOTTEN_BEAST_74:LUNG -!CREATURE:FORGOTTEN_BEAST_74:HEART -!CREATURE:FORGOTTEN_BEAST_74:LIVER -CREATURE:FORGOTTEN_BEAST_74:GUT -#CREATURE:FORGOTTEN_BEAST_74:STOMACH -#CREATURE:FORGOTTEN_BEAST_74:GIZZARD -$CREATURE:FORGOTTEN_BEAST_74:PANCREAS -"CREATURE:FORGOTTEN_BEAST_74:SPLEEN -"CREATURE:FORGOTTEN_BEAST_74:KIDNEY -"CREATURE:FORGOTTEN_BEAST_75:MUSCLE -CREATURE:FORGOTTEN_BEAST_75:EYE -!CREATURE:FORGOTTEN_BEAST_75:BRAIN - CREATURE:FORGOTTEN_BEAST_75:LUNG -!CREATURE:FORGOTTEN_BEAST_75:HEART -!CREATURE:FORGOTTEN_BEAST_75:LIVER -CREATURE:FORGOTTEN_BEAST_75:GUT -#CREATURE:FORGOTTEN_BEAST_75:STOMACH -#CREATURE:FORGOTTEN_BEAST_75:GIZZARD -$CREATURE:FORGOTTEN_BEAST_75:PANCREAS -"CREATURE:FORGOTTEN_BEAST_75:SPLEEN -"CREATURE:FORGOTTEN_BEAST_75:KIDNEY -"CREATURE:FORGOTTEN_BEAST_78:MUSCLE -CREATURE:FORGOTTEN_BEAST_78:EYE -!CREATURE:FORGOTTEN_BEAST_78:BRAIN - CREATURE:FORGOTTEN_BEAST_78:LUNG -!CREATURE:FORGOTTEN_BEAST_78:HEART -!CREATURE:FORGOTTEN_BEAST_78:LIVER -CREATURE:FORGOTTEN_BEAST_78:GUT -#CREATURE:FORGOTTEN_BEAST_78:STOMACH -#CREATURE:FORGOTTEN_BEAST_78:GIZZARD -$CREATURE:FORGOTTEN_BEAST_78:PANCREAS -"CREATURE:FORGOTTEN_BEAST_78:SPLEEN -"CREATURE:FORGOTTEN_BEAST_78:KIDNEY -"CREATURE:FORGOTTEN_BEAST_80:MUSCLE -CREATURE:FORGOTTEN_BEAST_80:EYE -!CREATURE:FORGOTTEN_BEAST_80:BRAIN - CREATURE:FORGOTTEN_BEAST_80:LUNG -!CREATURE:FORGOTTEN_BEAST_80:HEART -!CREATURE:FORGOTTEN_BEAST_80:LIVER -CREATURE:FORGOTTEN_BEAST_80:GUT -#CREATURE:FORGOTTEN_BEAST_80:STOMACH -#CREATURE:FORGOTTEN_BEAST_80:GIZZARD -$CREATURE:FORGOTTEN_BEAST_80:PANCREAS -"CREATURE:FORGOTTEN_BEAST_80:SPLEEN -"CREATURE:FORGOTTEN_BEAST_80:KIDNEY -"CREATURE:FORGOTTEN_BEAST_81:MUSCLE -CREATURE:FORGOTTEN_BEAST_81:EYE -!CREATURE:FORGOTTEN_BEAST_81:BRAIN - CREATURE:FORGOTTEN_BEAST_81:LUNG -!CREATURE:FORGOTTEN_BEAST_81:HEART -!CREATURE:FORGOTTEN_BEAST_81:LIVER -CREATURE:FORGOTTEN_BEAST_81:GUT -#CREATURE:FORGOTTEN_BEAST_81:STOMACH -#CREATURE:FORGOTTEN_BEAST_81:GIZZARD -$CREATURE:FORGOTTEN_BEAST_81:PANCREAS -"CREATURE:FORGOTTEN_BEAST_81:SPLEEN -"CREATURE:FORGOTTEN_BEAST_81:KIDNEY -"CREATURE:FORGOTTEN_BEAST_82:MUSCLE -CREATURE:FORGOTTEN_BEAST_82:EYE -!CREATURE:FORGOTTEN_BEAST_82:BRAIN - CREATURE:FORGOTTEN_BEAST_82:LUNG -!CREATURE:FORGOTTEN_BEAST_82:HEART -!CREATURE:FORGOTTEN_BEAST_82:LIVER -CREATURE:FORGOTTEN_BEAST_82:GUT -#CREATURE:FORGOTTEN_BEAST_82:STOMACH -#CREATURE:FORGOTTEN_BEAST_82:GIZZARD -$CREATURE:FORGOTTEN_BEAST_82:PANCREAS -"CREATURE:FORGOTTEN_BEAST_82:SPLEEN -"CREATURE:FORGOTTEN_BEAST_82:KIDNEY -"CREATURE:FORGOTTEN_BEAST_83:MUSCLE -CREATURE:FORGOTTEN_BEAST_83:EYE -!CREATURE:FORGOTTEN_BEAST_83:BRAIN - CREATURE:FORGOTTEN_BEAST_83:LUNG -!CREATURE:FORGOTTEN_BEAST_83:HEART -!CREATURE:FORGOTTEN_BEAST_83:LIVER -CREATURE:FORGOTTEN_BEAST_83:GUT -#CREATURE:FORGOTTEN_BEAST_83:STOMACH -#CREATURE:FORGOTTEN_BEAST_83:GIZZARD -$CREATURE:FORGOTTEN_BEAST_83:PANCREAS -"CREATURE:FORGOTTEN_BEAST_83:SPLEEN -"CREATURE:FORGOTTEN_BEAST_83:KIDNEY -"CREATURE:FORGOTTEN_BEAST_84:MUSCLE -CREATURE:FORGOTTEN_BEAST_84:EYE -!CREATURE:FORGOTTEN_BEAST_84:BRAIN - CREATURE:FORGOTTEN_BEAST_84:LUNG -!CREATURE:FORGOTTEN_BEAST_84:HEART -!CREATURE:FORGOTTEN_BEAST_84:LIVER -CREATURE:FORGOTTEN_BEAST_84:GUT -#CREATURE:FORGOTTEN_BEAST_84:STOMACH -#CREATURE:FORGOTTEN_BEAST_84:GIZZARD -$CREATURE:FORGOTTEN_BEAST_84:PANCREAS -"CREATURE:FORGOTTEN_BEAST_84:SPLEEN -"CREATURE:FORGOTTEN_BEAST_84:KIDNEY -"CREATURE:FORGOTTEN_BEAST_86:MUSCLE -CREATURE:FORGOTTEN_BEAST_86:EYE -!CREATURE:FORGOTTEN_BEAST_86:BRAIN - CREATURE:FORGOTTEN_BEAST_86:LUNG -!CREATURE:FORGOTTEN_BEAST_86:HEART -!CREATURE:FORGOTTEN_BEAST_86:LIVER -CREATURE:FORGOTTEN_BEAST_86:GUT -#CREATURE:FORGOTTEN_BEAST_86:STOMACH -#CREATURE:FORGOTTEN_BEAST_86:GIZZARD -$CREATURE:FORGOTTEN_BEAST_86:PANCREAS -"CREATURE:FORGOTTEN_BEAST_86:SPLEEN -"CREATURE:FORGOTTEN_BEAST_86:KIDNEY -"CREATURE:FORGOTTEN_BEAST_87:MUSCLE -CREATURE:FORGOTTEN_BEAST_87:EYE -!CREATURE:FORGOTTEN_BEAST_87:BRAIN - CREATURE:FORGOTTEN_BEAST_87:LUNG -!CREATURE:FORGOTTEN_BEAST_87:HEART -!CREATURE:FORGOTTEN_BEAST_87:LIVER -CREATURE:FORGOTTEN_BEAST_87:GUT -#CREATURE:FORGOTTEN_BEAST_87:STOMACH -#CREATURE:FORGOTTEN_BEAST_87:GIZZARD -$CREATURE:FORGOTTEN_BEAST_87:PANCREAS -"CREATURE:FORGOTTEN_BEAST_87:SPLEEN -"CREATURE:FORGOTTEN_BEAST_87:KIDNEY -"CREATURE:FORGOTTEN_BEAST_89:MUSCLE -CREATURE:FORGOTTEN_BEAST_89:EYE -!CREATURE:FORGOTTEN_BEAST_89:BRAIN - CREATURE:FORGOTTEN_BEAST_89:LUNG -!CREATURE:FORGOTTEN_BEAST_89:HEART -!CREATURE:FORGOTTEN_BEAST_89:LIVER -CREATURE:FORGOTTEN_BEAST_89:GUT -#CREATURE:FORGOTTEN_BEAST_89:STOMACH -#CREATURE:FORGOTTEN_BEAST_89:GIZZARD -$CREATURE:FORGOTTEN_BEAST_89:PANCREAS -"CREATURE:FORGOTTEN_BEAST_89:SPLEEN -"CREATURE:FORGOTTEN_BEAST_89:KIDNEY -"CREATURE:FORGOTTEN_BEAST_90:MUSCLE -CREATURE:FORGOTTEN_BEAST_90:EYE -!CREATURE:FORGOTTEN_BEAST_90:BRAIN - CREATURE:FORGOTTEN_BEAST_90:LUNG -!CREATURE:FORGOTTEN_BEAST_90:HEART -!CREATURE:FORGOTTEN_BEAST_90:LIVER -CREATURE:FORGOTTEN_BEAST_90:GUT -#CREATURE:FORGOTTEN_BEAST_90:STOMACH -#CREATURE:FORGOTTEN_BEAST_90:GIZZARD -$CREATURE:FORGOTTEN_BEAST_90:PANCREAS -"CREATURE:FORGOTTEN_BEAST_90:SPLEEN -"CREATURE:FORGOTTEN_BEAST_90:KIDNEY -"CREATURE:FORGOTTEN_BEAST_92:MUSCLE -CREATURE:FORGOTTEN_BEAST_92:EYE -!CREATURE:FORGOTTEN_BEAST_92:BRAIN - CREATURE:FORGOTTEN_BEAST_92:LUNG -!CREATURE:FORGOTTEN_BEAST_92:HEART -!CREATURE:FORGOTTEN_BEAST_92:LIVER -CREATURE:FORGOTTEN_BEAST_92:GUT -#CREATURE:FORGOTTEN_BEAST_92:STOMACH -#CREATURE:FORGOTTEN_BEAST_92:GIZZARD -$CREATURE:FORGOTTEN_BEAST_92:PANCREAS -"CREATURE:FORGOTTEN_BEAST_92:SPLEEN -"CREATURE:FORGOTTEN_BEAST_92:KIDNEY -"CREATURE:FORGOTTEN_BEAST_94:MUSCLE -CREATURE:FORGOTTEN_BEAST_94:EYE -!CREATURE:FORGOTTEN_BEAST_94:BRAIN - CREATURE:FORGOTTEN_BEAST_94:LUNG -!CREATURE:FORGOTTEN_BEAST_94:HEART -!CREATURE:FORGOTTEN_BEAST_94:LIVER -CREATURE:FORGOTTEN_BEAST_94:GUT -#CREATURE:FORGOTTEN_BEAST_94:STOMACH -#CREATURE:FORGOTTEN_BEAST_94:GIZZARD -$CREATURE:FORGOTTEN_BEAST_94:PANCREAS -"CREATURE:FORGOTTEN_BEAST_94:SPLEEN -"CREATURE:FORGOTTEN_BEAST_94:KIDNEY -"CREATURE:FORGOTTEN_BEAST_95:MUSCLE -CREATURE:FORGOTTEN_BEAST_95:EYE -!CREATURE:FORGOTTEN_BEAST_95:BRAIN - CREATURE:FORGOTTEN_BEAST_95:LUNG -!CREATURE:FORGOTTEN_BEAST_95:HEART -!CREATURE:FORGOTTEN_BEAST_95:LIVER -CREATURE:FORGOTTEN_BEAST_95:GUT -#CREATURE:FORGOTTEN_BEAST_95:STOMACH -#CREATURE:FORGOTTEN_BEAST_95:GIZZARD -$CREATURE:FORGOTTEN_BEAST_95:PANCREAS -"CREATURE:FORGOTTEN_BEAST_95:SPLEEN -"CREATURE:FORGOTTEN_BEAST_95:KIDNEY -"CREATURE:FORGOTTEN_BEAST_96:MUSCLE -CREATURE:FORGOTTEN_BEAST_96:EYE -!CREATURE:FORGOTTEN_BEAST_96:BRAIN - CREATURE:FORGOTTEN_BEAST_96:LUNG -!CREATURE:FORGOTTEN_BEAST_96:HEART -!CREATURE:FORGOTTEN_BEAST_96:LIVER -CREATURE:FORGOTTEN_BEAST_96:GUT -#CREATURE:FORGOTTEN_BEAST_96:STOMACH -#CREATURE:FORGOTTEN_BEAST_96:GIZZARD -$CREATURE:FORGOTTEN_BEAST_96:PANCREAS -"CREATURE:FORGOTTEN_BEAST_96:SPLEEN -"CREATURE:FORGOTTEN_BEAST_96:KIDNEY -"CREATURE:FORGOTTEN_BEAST_97:MUSCLE -CREATURE:FORGOTTEN_BEAST_97:EYE -!CREATURE:FORGOTTEN_BEAST_97:BRAIN - CREATURE:FORGOTTEN_BEAST_97:LUNG -!CREATURE:FORGOTTEN_BEAST_97:HEART -!CREATURE:FORGOTTEN_BEAST_97:LIVER -CREATURE:FORGOTTEN_BEAST_97:GUT -#CREATURE:FORGOTTEN_BEAST_97:STOMACH -#CREATURE:FORGOTTEN_BEAST_97:GIZZARD -$CREATURE:FORGOTTEN_BEAST_97:PANCREAS -"CREATURE:FORGOTTEN_BEAST_97:SPLEEN -"CREATURE:FORGOTTEN_BEAST_97:KIDNEY -"CREATURE:FORGOTTEN_BEAST_98:MUSCLE -CREATURE:FORGOTTEN_BEAST_98:EYE -!CREATURE:FORGOTTEN_BEAST_98:BRAIN - CREATURE:FORGOTTEN_BEAST_98:LUNG -!CREATURE:FORGOTTEN_BEAST_98:HEART -!CREATURE:FORGOTTEN_BEAST_98:LIVER -CREATURE:FORGOTTEN_BEAST_98:GUT -#CREATURE:FORGOTTEN_BEAST_98:STOMACH -#CREATURE:FORGOTTEN_BEAST_98:GIZZARD -$CREATURE:FORGOTTEN_BEAST_98:PANCREAS -"CREATURE:FORGOTTEN_BEAST_98:SPLEEN -"CREATURE:FORGOTTEN_BEAST_98:KIDNEY -#CREATURE:FORGOTTEN_BEAST_100:MUSCLE - CREATURE:FORGOTTEN_BEAST_100:EYE -"CREATURE:FORGOTTEN_BEAST_100:BRAIN -!CREATURE:FORGOTTEN_BEAST_100:LUNG -"CREATURE:FORGOTTEN_BEAST_100:HEART -"CREATURE:FORGOTTEN_BEAST_100:LIVER - CREATURE:FORGOTTEN_BEAST_100:GUT -$CREATURE:FORGOTTEN_BEAST_100:STOMACH -$CREATURE:FORGOTTEN_BEAST_100:GIZZARD -%CREATURE:FORGOTTEN_BEAST_100:PANCREAS -#CREATURE:FORGOTTEN_BEAST_100:SPLEEN -#CREATURE:FORGOTTEN_BEAST_100:KIDNEY -#CREATURE:FORGOTTEN_BEAST_105:MUSCLE - CREATURE:FORGOTTEN_BEAST_105:EYE -"CREATURE:FORGOTTEN_BEAST_105:BRAIN -!CREATURE:FORGOTTEN_BEAST_105:LUNG -"CREATURE:FORGOTTEN_BEAST_105:HEART -"CREATURE:FORGOTTEN_BEAST_105:LIVER - CREATURE:FORGOTTEN_BEAST_105:GUT -$CREATURE:FORGOTTEN_BEAST_105:STOMACH -$CREATURE:FORGOTTEN_BEAST_105:GIZZARD -%CREATURE:FORGOTTEN_BEAST_105:PANCREAS -#CREATURE:FORGOTTEN_BEAST_105:SPLEEN -#CREATURE:FORGOTTEN_BEAST_105:KIDNEY -#CREATURE:FORGOTTEN_BEAST_106:MUSCLE - CREATURE:FORGOTTEN_BEAST_106:EYE -"CREATURE:FORGOTTEN_BEAST_106:BRAIN -!CREATURE:FORGOTTEN_BEAST_106:LUNG -"CREATURE:FORGOTTEN_BEAST_106:HEART -"CREATURE:FORGOTTEN_BEAST_106:LIVER - CREATURE:FORGOTTEN_BEAST_106:GUT -$CREATURE:FORGOTTEN_BEAST_106:STOMACH -$CREATURE:FORGOTTEN_BEAST_106:GIZZARD -%CREATURE:FORGOTTEN_BEAST_106:PANCREAS -#CREATURE:FORGOTTEN_BEAST_106:SPLEEN -#CREATURE:FORGOTTEN_BEAST_106:KIDNEY -#CREATURE:FORGOTTEN_BEAST_107:MUSCLE - CREATURE:FORGOTTEN_BEAST_107:EYE -"CREATURE:FORGOTTEN_BEAST_107:BRAIN -!CREATURE:FORGOTTEN_BEAST_107:LUNG -"CREATURE:FORGOTTEN_BEAST_107:HEART -"CREATURE:FORGOTTEN_BEAST_107:LIVER - CREATURE:FORGOTTEN_BEAST_107:GUT -$CREATURE:FORGOTTEN_BEAST_107:STOMACH -$CREATURE:FORGOTTEN_BEAST_107:GIZZARD -%CREATURE:FORGOTTEN_BEAST_107:PANCREAS -#CREATURE:FORGOTTEN_BEAST_107:SPLEEN -#CREATURE:FORGOTTEN_BEAST_107:KIDNEY -#CREATURE:FORGOTTEN_BEAST_108:MUSCLE - CREATURE:FORGOTTEN_BEAST_108:EYE -"CREATURE:FORGOTTEN_BEAST_108:BRAIN -!CREATURE:FORGOTTEN_BEAST_108:LUNG -"CREATURE:FORGOTTEN_BEAST_108:HEART -"CREATURE:FORGOTTEN_BEAST_108:LIVER - CREATURE:FORGOTTEN_BEAST_108:GUT -$CREATURE:FORGOTTEN_BEAST_108:STOMACH -$CREATURE:FORGOTTEN_BEAST_108:GIZZARD -%CREATURE:FORGOTTEN_BEAST_108:PANCREAS -#CREATURE:FORGOTTEN_BEAST_108:SPLEEN -#CREATURE:FORGOTTEN_BEAST_108:KIDNEY -#CREATURE:FORGOTTEN_BEAST_109:MUSCLE - CREATURE:FORGOTTEN_BEAST_109:EYE -"CREATURE:FORGOTTEN_BEAST_109:BRAIN -!CREATURE:FORGOTTEN_BEAST_109:LUNG -"CREATURE:FORGOTTEN_BEAST_109:HEART -"CREATURE:FORGOTTEN_BEAST_109:LIVER - CREATURE:FORGOTTEN_BEAST_109:GUT -$CREATURE:FORGOTTEN_BEAST_109:STOMACH -$CREATURE:FORGOTTEN_BEAST_109:GIZZARD -%CREATURE:FORGOTTEN_BEAST_109:PANCREAS -#CREATURE:FORGOTTEN_BEAST_109:SPLEEN -#CREATURE:FORGOTTEN_BEAST_109:KIDNEY -#CREATURE:FORGOTTEN_BEAST_111:MUSCLE - CREATURE:FORGOTTEN_BEAST_111:EYE -"CREATURE:FORGOTTEN_BEAST_111:BRAIN -!CREATURE:FORGOTTEN_BEAST_111:LUNG -"CREATURE:FORGOTTEN_BEAST_111:HEART -"CREATURE:FORGOTTEN_BEAST_111:LIVER - CREATURE:FORGOTTEN_BEAST_111:GUT -$CREATURE:FORGOTTEN_BEAST_111:STOMACH -$CREATURE:FORGOTTEN_BEAST_111:GIZZARD -%CREATURE:FORGOTTEN_BEAST_111:PANCREAS -#CREATURE:FORGOTTEN_BEAST_111:SPLEEN -#CREATURE:FORGOTTEN_BEAST_111:KIDNEY -#CREATURE:FORGOTTEN_BEAST_112:MUSCLE - CREATURE:FORGOTTEN_BEAST_112:EYE -"CREATURE:FORGOTTEN_BEAST_112:BRAIN -!CREATURE:FORGOTTEN_BEAST_112:LUNG -"CREATURE:FORGOTTEN_BEAST_112:HEART -"CREATURE:FORGOTTEN_BEAST_112:LIVER - CREATURE:FORGOTTEN_BEAST_112:GUT -$CREATURE:FORGOTTEN_BEAST_112:STOMACH -$CREATURE:FORGOTTEN_BEAST_112:GIZZARD -%CREATURE:FORGOTTEN_BEAST_112:PANCREAS -#CREATURE:FORGOTTEN_BEAST_112:SPLEEN -#CREATURE:FORGOTTEN_BEAST_112:KIDNEY -#CREATURE:FORGOTTEN_BEAST_113:MUSCLE - CREATURE:FORGOTTEN_BEAST_113:EYE -"CREATURE:FORGOTTEN_BEAST_113:BRAIN -!CREATURE:FORGOTTEN_BEAST_113:LUNG -"CREATURE:FORGOTTEN_BEAST_113:HEART -"CREATURE:FORGOTTEN_BEAST_113:LIVER - CREATURE:FORGOTTEN_BEAST_113:GUT -$CREATURE:FORGOTTEN_BEAST_113:STOMACH -$CREATURE:FORGOTTEN_BEAST_113:GIZZARD -%CREATURE:FORGOTTEN_BEAST_113:PANCREAS -#CREATURE:FORGOTTEN_BEAST_113:SPLEEN -#CREATURE:FORGOTTEN_BEAST_113:KIDNEY -#CREATURE:FORGOTTEN_BEAST_114:MUSCLE - CREATURE:FORGOTTEN_BEAST_114:EYE -"CREATURE:FORGOTTEN_BEAST_114:BRAIN -!CREATURE:FORGOTTEN_BEAST_114:LUNG -"CREATURE:FORGOTTEN_BEAST_114:HEART -"CREATURE:FORGOTTEN_BEAST_114:LIVER - CREATURE:FORGOTTEN_BEAST_114:GUT -$CREATURE:FORGOTTEN_BEAST_114:STOMACH -$CREATURE:FORGOTTEN_BEAST_114:GIZZARD -%CREATURE:FORGOTTEN_BEAST_114:PANCREAS -#CREATURE:FORGOTTEN_BEAST_114:SPLEEN -#CREATURE:FORGOTTEN_BEAST_114:KIDNEY -#CREATURE:FORGOTTEN_BEAST_115:MUSCLE - CREATURE:FORGOTTEN_BEAST_115:EYE -"CREATURE:FORGOTTEN_BEAST_115:BRAIN -!CREATURE:FORGOTTEN_BEAST_115:LUNG -"CREATURE:FORGOTTEN_BEAST_115:HEART -"CREATURE:FORGOTTEN_BEAST_115:LIVER - CREATURE:FORGOTTEN_BEAST_115:GUT -$CREATURE:FORGOTTEN_BEAST_115:STOMACH -$CREATURE:FORGOTTEN_BEAST_115:GIZZARD -%CREATURE:FORGOTTEN_BEAST_115:PANCREAS -#CREATURE:FORGOTTEN_BEAST_115:SPLEEN -#CREATURE:FORGOTTEN_BEAST_115:KIDNEY -#CREATURE:FORGOTTEN_BEAST_116:MUSCLE - CREATURE:FORGOTTEN_BEAST_116:EYE -"CREATURE:FORGOTTEN_BEAST_116:BRAIN -!CREATURE:FORGOTTEN_BEAST_116:LUNG -"CREATURE:FORGOTTEN_BEAST_116:HEART -"CREATURE:FORGOTTEN_BEAST_116:LIVER - CREATURE:FORGOTTEN_BEAST_116:GUT -$CREATURE:FORGOTTEN_BEAST_116:STOMACH -$CREATURE:FORGOTTEN_BEAST_116:GIZZARD -%CREATURE:FORGOTTEN_BEAST_116:PANCREAS -#CREATURE:FORGOTTEN_BEAST_116:SPLEEN -#CREATURE:FORGOTTEN_BEAST_116:KIDNEY -#CREATURE:FORGOTTEN_BEAST_117:MUSCLE - CREATURE:FORGOTTEN_BEAST_117:EYE -"CREATURE:FORGOTTEN_BEAST_117:BRAIN -!CREATURE:FORGOTTEN_BEAST_117:LUNG -"CREATURE:FORGOTTEN_BEAST_117:HEART -"CREATURE:FORGOTTEN_BEAST_117:LIVER - CREATURE:FORGOTTEN_BEAST_117:GUT -$CREATURE:FORGOTTEN_BEAST_117:STOMACH -$CREATURE:FORGOTTEN_BEAST_117:GIZZARD -%CREATURE:FORGOTTEN_BEAST_117:PANCREAS -#CREATURE:FORGOTTEN_BEAST_117:SPLEEN -#CREATURE:FORGOTTEN_BEAST_117:KIDNEY -#CREATURE:FORGOTTEN_BEAST_118:MUSCLE - CREATURE:FORGOTTEN_BEAST_118:EYE -"CREATURE:FORGOTTEN_BEAST_118:BRAIN -!CREATURE:FORGOTTEN_BEAST_118:LUNG -"CREATURE:FORGOTTEN_BEAST_118:HEART -"CREATURE:FORGOTTEN_BEAST_118:LIVER - CREATURE:FORGOTTEN_BEAST_118:GUT -$CREATURE:FORGOTTEN_BEAST_118:STOMACH -$CREATURE:FORGOTTEN_BEAST_118:GIZZARD -%CREATURE:FORGOTTEN_BEAST_118:PANCREAS -#CREATURE:FORGOTTEN_BEAST_118:SPLEEN -#CREATURE:FORGOTTEN_BEAST_118:KIDNEY -#CREATURE:FORGOTTEN_BEAST_119:MUSCLE - CREATURE:FORGOTTEN_BEAST_119:EYE -"CREATURE:FORGOTTEN_BEAST_119:BRAIN -!CREATURE:FORGOTTEN_BEAST_119:LUNG -"CREATURE:FORGOTTEN_BEAST_119:HEART -"CREATURE:FORGOTTEN_BEAST_119:LIVER - CREATURE:FORGOTTEN_BEAST_119:GUT -$CREATURE:FORGOTTEN_BEAST_119:STOMACH -$CREATURE:FORGOTTEN_BEAST_119:GIZZARD -%CREATURE:FORGOTTEN_BEAST_119:PANCREAS -#CREATURE:FORGOTTEN_BEAST_119:SPLEEN -#CREATURE:FORGOTTEN_BEAST_119:KIDNEY -#CREATURE:FORGOTTEN_BEAST_120:MUSCLE - CREATURE:FORGOTTEN_BEAST_120:EYE -"CREATURE:FORGOTTEN_BEAST_120:BRAIN -!CREATURE:FORGOTTEN_BEAST_120:LUNG -"CREATURE:FORGOTTEN_BEAST_120:HEART -"CREATURE:FORGOTTEN_BEAST_120:LIVER - CREATURE:FORGOTTEN_BEAST_120:GUT -$CREATURE:FORGOTTEN_BEAST_120:STOMACH -$CREATURE:FORGOTTEN_BEAST_120:GIZZARD -%CREATURE:FORGOTTEN_BEAST_120:PANCREAS -#CREATURE:FORGOTTEN_BEAST_120:SPLEEN -#CREATURE:FORGOTTEN_BEAST_120:KIDNEY -#CREATURE:FORGOTTEN_BEAST_122:MUSCLE - CREATURE:FORGOTTEN_BEAST_122:EYE -"CREATURE:FORGOTTEN_BEAST_122:BRAIN -!CREATURE:FORGOTTEN_BEAST_122:LUNG -"CREATURE:FORGOTTEN_BEAST_122:HEART -"CREATURE:FORGOTTEN_BEAST_122:LIVER - CREATURE:FORGOTTEN_BEAST_122:GUT -$CREATURE:FORGOTTEN_BEAST_122:STOMACH -$CREATURE:FORGOTTEN_BEAST_122:GIZZARD -%CREATURE:FORGOTTEN_BEAST_122:PANCREAS -#CREATURE:FORGOTTEN_BEAST_122:SPLEEN -#CREATURE:FORGOTTEN_BEAST_122:KIDNEY -#CREATURE:FORGOTTEN_BEAST_123:MUSCLE - CREATURE:FORGOTTEN_BEAST_123:EYE -"CREATURE:FORGOTTEN_BEAST_123:BRAIN -!CREATURE:FORGOTTEN_BEAST_123:LUNG -"CREATURE:FORGOTTEN_BEAST_123:HEART -"CREATURE:FORGOTTEN_BEAST_123:LIVER - CREATURE:FORGOTTEN_BEAST_123:GUT -$CREATURE:FORGOTTEN_BEAST_123:STOMACH -$CREATURE:FORGOTTEN_BEAST_123:GIZZARD -%CREATURE:FORGOTTEN_BEAST_123:PANCREAS -#CREATURE:FORGOTTEN_BEAST_123:SPLEEN -#CREATURE:FORGOTTEN_BEAST_123:KIDNEY -#CREATURE:FORGOTTEN_BEAST_124:MUSCLE - CREATURE:FORGOTTEN_BEAST_124:EYE -"CREATURE:FORGOTTEN_BEAST_124:BRAIN -!CREATURE:FORGOTTEN_BEAST_124:LUNG -"CREATURE:FORGOTTEN_BEAST_124:HEART -"CREATURE:FORGOTTEN_BEAST_124:LIVER - CREATURE:FORGOTTEN_BEAST_124:GUT -$CREATURE:FORGOTTEN_BEAST_124:STOMACH -$CREATURE:FORGOTTEN_BEAST_124:GIZZARD -%CREATURE:FORGOTTEN_BEAST_124:PANCREAS -#CREATURE:FORGOTTEN_BEAST_124:SPLEEN -#CREATURE:FORGOTTEN_BEAST_124:KIDNEY -#CREATURE:FORGOTTEN_BEAST_125:MUSCLE - CREATURE:FORGOTTEN_BEAST_125:EYE -"CREATURE:FORGOTTEN_BEAST_125:BRAIN -!CREATURE:FORGOTTEN_BEAST_125:LUNG -"CREATURE:FORGOTTEN_BEAST_125:HEART -"CREATURE:FORGOTTEN_BEAST_125:LIVER - CREATURE:FORGOTTEN_BEAST_125:GUT -$CREATURE:FORGOTTEN_BEAST_125:STOMACH -$CREATURE:FORGOTTEN_BEAST_125:GIZZARD -%CREATURE:FORGOTTEN_BEAST_125:PANCREAS -#CREATURE:FORGOTTEN_BEAST_125:SPLEEN -#CREATURE:FORGOTTEN_BEAST_125:KIDNEY -#CREATURE:FORGOTTEN_BEAST_127:MUSCLE - CREATURE:FORGOTTEN_BEAST_127:EYE -"CREATURE:FORGOTTEN_BEAST_127:BRAIN -!CREATURE:FORGOTTEN_BEAST_127:LUNG -"CREATURE:FORGOTTEN_BEAST_127:HEART -"CREATURE:FORGOTTEN_BEAST_127:LIVER - CREATURE:FORGOTTEN_BEAST_127:GUT -$CREATURE:FORGOTTEN_BEAST_127:STOMACH -$CREATURE:FORGOTTEN_BEAST_127:GIZZARD -%CREATURE:FORGOTTEN_BEAST_127:PANCREAS -#CREATURE:FORGOTTEN_BEAST_127:SPLEEN -#CREATURE:FORGOTTEN_BEAST_127:KIDNEY -#CREATURE:FORGOTTEN_BEAST_128:MUSCLE - CREATURE:FORGOTTEN_BEAST_128:EYE -"CREATURE:FORGOTTEN_BEAST_128:BRAIN -!CREATURE:FORGOTTEN_BEAST_128:LUNG -"CREATURE:FORGOTTEN_BEAST_128:HEART -"CREATURE:FORGOTTEN_BEAST_128:LIVER - CREATURE:FORGOTTEN_BEAST_128:GUT -$CREATURE:FORGOTTEN_BEAST_128:STOMACH -$CREATURE:FORGOTTEN_BEAST_128:GIZZARD -%CREATURE:FORGOTTEN_BEAST_128:PANCREAS -#CREATURE:FORGOTTEN_BEAST_128:SPLEEN -#CREATURE:FORGOTTEN_BEAST_128:KIDNEY -#CREATURE:FORGOTTEN_BEAST_130:MUSCLE - CREATURE:FORGOTTEN_BEAST_130:EYE -"CREATURE:FORGOTTEN_BEAST_130:BRAIN -!CREATURE:FORGOTTEN_BEAST_130:LUNG -"CREATURE:FORGOTTEN_BEAST_130:HEART -"CREATURE:FORGOTTEN_BEAST_130:LIVER - CREATURE:FORGOTTEN_BEAST_130:GUT -$CREATURE:FORGOTTEN_BEAST_130:STOMACH -$CREATURE:FORGOTTEN_BEAST_130:GIZZARD -%CREATURE:FORGOTTEN_BEAST_130:PANCREAS -#CREATURE:FORGOTTEN_BEAST_130:SPLEEN -#CREATURE:FORGOTTEN_BEAST_130:KIDNEY -#CREATURE:FORGOTTEN_BEAST_131:MUSCLE - CREATURE:FORGOTTEN_BEAST_131:EYE -"CREATURE:FORGOTTEN_BEAST_131:BRAIN -!CREATURE:FORGOTTEN_BEAST_131:LUNG -"CREATURE:FORGOTTEN_BEAST_131:HEART -"CREATURE:FORGOTTEN_BEAST_131:LIVER - CREATURE:FORGOTTEN_BEAST_131:GUT -$CREATURE:FORGOTTEN_BEAST_131:STOMACH -$CREATURE:FORGOTTEN_BEAST_131:GIZZARD -%CREATURE:FORGOTTEN_BEAST_131:PANCREAS -#CREATURE:FORGOTTEN_BEAST_131:SPLEEN -#CREATURE:FORGOTTEN_BEAST_131:KIDNEY -#CREATURE:FORGOTTEN_BEAST_132:MUSCLE - CREATURE:FORGOTTEN_BEAST_132:EYE -"CREATURE:FORGOTTEN_BEAST_132:BRAIN -!CREATURE:FORGOTTEN_BEAST_132:LUNG -"CREATURE:FORGOTTEN_BEAST_132:HEART -"CREATURE:FORGOTTEN_BEAST_132:LIVER - CREATURE:FORGOTTEN_BEAST_132:GUT -$CREATURE:FORGOTTEN_BEAST_132:STOMACH -$CREATURE:FORGOTTEN_BEAST_132:GIZZARD -%CREATURE:FORGOTTEN_BEAST_132:PANCREAS -#CREATURE:FORGOTTEN_BEAST_132:SPLEEN -#CREATURE:FORGOTTEN_BEAST_132:KIDNEY -#CREATURE:FORGOTTEN_BEAST_133:MUSCLE - CREATURE:FORGOTTEN_BEAST_133:EYE -"CREATURE:FORGOTTEN_BEAST_133:BRAIN -!CREATURE:FORGOTTEN_BEAST_133:LUNG -"CREATURE:FORGOTTEN_BEAST_133:HEART -"CREATURE:FORGOTTEN_BEAST_133:LIVER - CREATURE:FORGOTTEN_BEAST_133:GUT -$CREATURE:FORGOTTEN_BEAST_133:STOMACH -$CREATURE:FORGOTTEN_BEAST_133:GIZZARD -%CREATURE:FORGOTTEN_BEAST_133:PANCREAS -#CREATURE:FORGOTTEN_BEAST_133:SPLEEN -#CREATURE:FORGOTTEN_BEAST_133:KIDNEY -#CREATURE:FORGOTTEN_BEAST_134:MUSCLE - CREATURE:FORGOTTEN_BEAST_134:EYE -"CREATURE:FORGOTTEN_BEAST_134:BRAIN -!CREATURE:FORGOTTEN_BEAST_134:LUNG -"CREATURE:FORGOTTEN_BEAST_134:HEART -"CREATURE:FORGOTTEN_BEAST_134:LIVER - CREATURE:FORGOTTEN_BEAST_134:GUT -$CREATURE:FORGOTTEN_BEAST_134:STOMACH -$CREATURE:FORGOTTEN_BEAST_134:GIZZARD -%CREATURE:FORGOTTEN_BEAST_134:PANCREAS -#CREATURE:FORGOTTEN_BEAST_134:SPLEEN -#CREATURE:FORGOTTEN_BEAST_134:KIDNEY -#CREATURE:FORGOTTEN_BEAST_135:MUSCLE - CREATURE:FORGOTTEN_BEAST_135:EYE -"CREATURE:FORGOTTEN_BEAST_135:BRAIN -!CREATURE:FORGOTTEN_BEAST_135:LUNG -"CREATURE:FORGOTTEN_BEAST_135:HEART -"CREATURE:FORGOTTEN_BEAST_135:LIVER - CREATURE:FORGOTTEN_BEAST_135:GUT -$CREATURE:FORGOTTEN_BEAST_135:STOMACH -$CREATURE:FORGOTTEN_BEAST_135:GIZZARD -%CREATURE:FORGOTTEN_BEAST_135:PANCREAS -#CREATURE:FORGOTTEN_BEAST_135:SPLEEN -#CREATURE:FORGOTTEN_BEAST_135:KIDNEY -#CREATURE:FORGOTTEN_BEAST_137:MUSCLE - CREATURE:FORGOTTEN_BEAST_137:EYE -"CREATURE:FORGOTTEN_BEAST_137:BRAIN -!CREATURE:FORGOTTEN_BEAST_137:LUNG -"CREATURE:FORGOTTEN_BEAST_137:HEART -"CREATURE:FORGOTTEN_BEAST_137:LIVER - CREATURE:FORGOTTEN_BEAST_137:GUT -$CREATURE:FORGOTTEN_BEAST_137:STOMACH -$CREATURE:FORGOTTEN_BEAST_137:GIZZARD -%CREATURE:FORGOTTEN_BEAST_137:PANCREAS -#CREATURE:FORGOTTEN_BEAST_137:SPLEEN -#CREATURE:FORGOTTEN_BEAST_137:KIDNEY -#CREATURE:FORGOTTEN_BEAST_138:MUSCLE - CREATURE:FORGOTTEN_BEAST_138:EYE -"CREATURE:FORGOTTEN_BEAST_138:BRAIN -!CREATURE:FORGOTTEN_BEAST_138:LUNG -"CREATURE:FORGOTTEN_BEAST_138:HEART -"CREATURE:FORGOTTEN_BEAST_138:LIVER - CREATURE:FORGOTTEN_BEAST_138:GUT -$CREATURE:FORGOTTEN_BEAST_138:STOMACH -$CREATURE:FORGOTTEN_BEAST_138:GIZZARD -%CREATURE:FORGOTTEN_BEAST_138:PANCREAS -#CREATURE:FORGOTTEN_BEAST_138:SPLEEN -#CREATURE:FORGOTTEN_BEAST_138:KIDNEY -#CREATURE:FORGOTTEN_BEAST_139:MUSCLE - CREATURE:FORGOTTEN_BEAST_139:EYE -"CREATURE:FORGOTTEN_BEAST_139:BRAIN -!CREATURE:FORGOTTEN_BEAST_139:LUNG -"CREATURE:FORGOTTEN_BEAST_139:HEART -"CREATURE:FORGOTTEN_BEAST_139:LIVER - CREATURE:FORGOTTEN_BEAST_139:GUT -$CREATURE:FORGOTTEN_BEAST_139:STOMACH -$CREATURE:FORGOTTEN_BEAST_139:GIZZARD -%CREATURE:FORGOTTEN_BEAST_139:PANCREAS -#CREATURE:FORGOTTEN_BEAST_139:SPLEEN -#CREATURE:FORGOTTEN_BEAST_139:KIDNEY -#CREATURE:FORGOTTEN_BEAST_141:MUSCLE - CREATURE:FORGOTTEN_BEAST_141:EYE -"CREATURE:FORGOTTEN_BEAST_141:BRAIN -!CREATURE:FORGOTTEN_BEAST_141:LUNG -"CREATURE:FORGOTTEN_BEAST_141:HEART -"CREATURE:FORGOTTEN_BEAST_141:LIVER - CREATURE:FORGOTTEN_BEAST_141:GUT -$CREATURE:FORGOTTEN_BEAST_141:STOMACH -$CREATURE:FORGOTTEN_BEAST_141:GIZZARD -%CREATURE:FORGOTTEN_BEAST_141:PANCREAS -#CREATURE:FORGOTTEN_BEAST_141:SPLEEN -#CREATURE:FORGOTTEN_BEAST_141:KIDNEY -#CREATURE:FORGOTTEN_BEAST_142:MUSCLE - CREATURE:FORGOTTEN_BEAST_142:EYE -"CREATURE:FORGOTTEN_BEAST_142:BRAIN -!CREATURE:FORGOTTEN_BEAST_142:LUNG -"CREATURE:FORGOTTEN_BEAST_142:HEART -"CREATURE:FORGOTTEN_BEAST_142:LIVER - CREATURE:FORGOTTEN_BEAST_142:GUT -$CREATURE:FORGOTTEN_BEAST_142:STOMACH -$CREATURE:FORGOTTEN_BEAST_142:GIZZARD -%CREATURE:FORGOTTEN_BEAST_142:PANCREAS -#CREATURE:FORGOTTEN_BEAST_142:SPLEEN -#CREATURE:FORGOTTEN_BEAST_142:KIDNEY -#CREATURE:FORGOTTEN_BEAST_144:MUSCLE - CREATURE:FORGOTTEN_BEAST_144:EYE -"CREATURE:FORGOTTEN_BEAST_144:BRAIN -!CREATURE:FORGOTTEN_BEAST_144:LUNG -"CREATURE:FORGOTTEN_BEAST_144:HEART -"CREATURE:FORGOTTEN_BEAST_144:LIVER - CREATURE:FORGOTTEN_BEAST_144:GUT -$CREATURE:FORGOTTEN_BEAST_144:STOMACH -$CREATURE:FORGOTTEN_BEAST_144:GIZZARD -%CREATURE:FORGOTTEN_BEAST_144:PANCREAS -#CREATURE:FORGOTTEN_BEAST_144:SPLEEN -#CREATURE:FORGOTTEN_BEAST_144:KIDNEY -#CREATURE:FORGOTTEN_BEAST_146:MUSCLE - CREATURE:FORGOTTEN_BEAST_146:EYE -"CREATURE:FORGOTTEN_BEAST_146:BRAIN -!CREATURE:FORGOTTEN_BEAST_146:LUNG -"CREATURE:FORGOTTEN_BEAST_146:HEART -"CREATURE:FORGOTTEN_BEAST_146:LIVER - CREATURE:FORGOTTEN_BEAST_146:GUT -$CREATURE:FORGOTTEN_BEAST_146:STOMACH -$CREATURE:FORGOTTEN_BEAST_146:GIZZARD -%CREATURE:FORGOTTEN_BEAST_146:PANCREAS -#CREATURE:FORGOTTEN_BEAST_146:SPLEEN -#CREATURE:FORGOTTEN_BEAST_146:KIDNEY -#CREATURE:FORGOTTEN_BEAST_148:MUSCLE - CREATURE:FORGOTTEN_BEAST_148:EYE -"CREATURE:FORGOTTEN_BEAST_148:BRAIN -!CREATURE:FORGOTTEN_BEAST_148:LUNG -"CREATURE:FORGOTTEN_BEAST_148:HEART -"CREATURE:FORGOTTEN_BEAST_148:LIVER - CREATURE:FORGOTTEN_BEAST_148:GUT -$CREATURE:FORGOTTEN_BEAST_148:STOMACH -$CREATURE:FORGOTTEN_BEAST_148:GIZZARD -%CREATURE:FORGOTTEN_BEAST_148:PANCREAS -#CREATURE:FORGOTTEN_BEAST_148:SPLEEN -#CREATURE:FORGOTTEN_BEAST_148:KIDNEY -#CREATURE:FORGOTTEN_BEAST_149:MUSCLE - CREATURE:FORGOTTEN_BEAST_149:EYE -"CREATURE:FORGOTTEN_BEAST_149:BRAIN -!CREATURE:FORGOTTEN_BEAST_149:LUNG -"CREATURE:FORGOTTEN_BEAST_149:HEART -"CREATURE:FORGOTTEN_BEAST_149:LIVER - CREATURE:FORGOTTEN_BEAST_149:GUT -$CREATURE:FORGOTTEN_BEAST_149:STOMACH -$CREATURE:FORGOTTEN_BEAST_149:GIZZARD -%CREATURE:FORGOTTEN_BEAST_149:PANCREAS -#CREATURE:FORGOTTEN_BEAST_149:SPLEEN -#CREATURE:FORGOTTEN_BEAST_149:KIDNEY -#CREATURE:FORGOTTEN_BEAST_150:MUSCLE - CREATURE:FORGOTTEN_BEAST_150:EYE -"CREATURE:FORGOTTEN_BEAST_150:BRAIN -!CREATURE:FORGOTTEN_BEAST_150:LUNG -"CREATURE:FORGOTTEN_BEAST_150:HEART -"CREATURE:FORGOTTEN_BEAST_150:LIVER - CREATURE:FORGOTTEN_BEAST_150:GUT -$CREATURE:FORGOTTEN_BEAST_150:STOMACH -$CREATURE:FORGOTTEN_BEAST_150:GIZZARD -%CREATURE:FORGOTTEN_BEAST_150:PANCREAS -#CREATURE:FORGOTTEN_BEAST_150:SPLEEN -#CREATURE:FORGOTTEN_BEAST_150:KIDNEY -#CREATURE:FORGOTTEN_BEAST_152:MUSCLE - CREATURE:FORGOTTEN_BEAST_152:EYE -"CREATURE:FORGOTTEN_BEAST_152:BRAIN -!CREATURE:FORGOTTEN_BEAST_152:LUNG -"CREATURE:FORGOTTEN_BEAST_152:HEART -"CREATURE:FORGOTTEN_BEAST_152:LIVER - CREATURE:FORGOTTEN_BEAST_152:GUT -$CREATURE:FORGOTTEN_BEAST_152:STOMACH -$CREATURE:FORGOTTEN_BEAST_152:GIZZARD -%CREATURE:FORGOTTEN_BEAST_152:PANCREAS -#CREATURE:FORGOTTEN_BEAST_152:SPLEEN -#CREATURE:FORGOTTEN_BEAST_152:KIDNEY -#CREATURE:FORGOTTEN_BEAST_154:MUSCLE - CREATURE:FORGOTTEN_BEAST_154:EYE -"CREATURE:FORGOTTEN_BEAST_154:BRAIN -!CREATURE:FORGOTTEN_BEAST_154:LUNG -"CREATURE:FORGOTTEN_BEAST_154:HEART -"CREATURE:FORGOTTEN_BEAST_154:LIVER - CREATURE:FORGOTTEN_BEAST_154:GUT -$CREATURE:FORGOTTEN_BEAST_154:STOMACH -$CREATURE:FORGOTTEN_BEAST_154:GIZZARD -%CREATURE:FORGOTTEN_BEAST_154:PANCREAS -#CREATURE:FORGOTTEN_BEAST_154:SPLEEN -#CREATURE:FORGOTTEN_BEAST_154:KIDNEY -#CREATURE:FORGOTTEN_BEAST_156:MUSCLE - CREATURE:FORGOTTEN_BEAST_156:EYE -"CREATURE:FORGOTTEN_BEAST_156:BRAIN -!CREATURE:FORGOTTEN_BEAST_156:LUNG -"CREATURE:FORGOTTEN_BEAST_156:HEART -"CREATURE:FORGOTTEN_BEAST_156:LIVER - CREATURE:FORGOTTEN_BEAST_156:GUT -$CREATURE:FORGOTTEN_BEAST_156:STOMACH -$CREATURE:FORGOTTEN_BEAST_156:GIZZARD -%CREATURE:FORGOTTEN_BEAST_156:PANCREAS -#CREATURE:FORGOTTEN_BEAST_156:SPLEEN -#CREATURE:FORGOTTEN_BEAST_156:KIDNEY -#CREATURE:FORGOTTEN_BEAST_157:MUSCLE - CREATURE:FORGOTTEN_BEAST_157:EYE -"CREATURE:FORGOTTEN_BEAST_157:BRAIN -!CREATURE:FORGOTTEN_BEAST_157:LUNG -"CREATURE:FORGOTTEN_BEAST_157:HEART -"CREATURE:FORGOTTEN_BEAST_157:LIVER - CREATURE:FORGOTTEN_BEAST_157:GUT -$CREATURE:FORGOTTEN_BEAST_157:STOMACH -$CREATURE:FORGOTTEN_BEAST_157:GIZZARD -%CREATURE:FORGOTTEN_BEAST_157:PANCREAS -#CREATURE:FORGOTTEN_BEAST_157:SPLEEN -#CREATURE:FORGOTTEN_BEAST_157:KIDNEY -#CREATURE:FORGOTTEN_BEAST_158:MUSCLE - CREATURE:FORGOTTEN_BEAST_158:EYE -"CREATURE:FORGOTTEN_BEAST_158:BRAIN -!CREATURE:FORGOTTEN_BEAST_158:LUNG -"CREATURE:FORGOTTEN_BEAST_158:HEART -"CREATURE:FORGOTTEN_BEAST_158:LIVER - CREATURE:FORGOTTEN_BEAST_158:GUT -$CREATURE:FORGOTTEN_BEAST_158:STOMACH -$CREATURE:FORGOTTEN_BEAST_158:GIZZARD -%CREATURE:FORGOTTEN_BEAST_158:PANCREAS -#CREATURE:FORGOTTEN_BEAST_158:SPLEEN -#CREATURE:FORGOTTEN_BEAST_158:KIDNEY -#CREATURE:FORGOTTEN_BEAST_159:MUSCLE - CREATURE:FORGOTTEN_BEAST_159:EYE -"CREATURE:FORGOTTEN_BEAST_159:BRAIN -!CREATURE:FORGOTTEN_BEAST_159:LUNG -"CREATURE:FORGOTTEN_BEAST_159:HEART -"CREATURE:FORGOTTEN_BEAST_159:LIVER - CREATURE:FORGOTTEN_BEAST_159:GUT -$CREATURE:FORGOTTEN_BEAST_159:STOMACH -$CREATURE:FORGOTTEN_BEAST_159:GIZZARD -%CREATURE:FORGOTTEN_BEAST_159:PANCREAS -#CREATURE:FORGOTTEN_BEAST_159:SPLEEN -#CREATURE:FORGOTTEN_BEAST_159:KIDNEY -#CREATURE:FORGOTTEN_BEAST_161:MUSCLE - CREATURE:FORGOTTEN_BEAST_161:EYE -"CREATURE:FORGOTTEN_BEAST_161:BRAIN -!CREATURE:FORGOTTEN_BEAST_161:LUNG -"CREATURE:FORGOTTEN_BEAST_161:HEART -"CREATURE:FORGOTTEN_BEAST_161:LIVER - CREATURE:FORGOTTEN_BEAST_161:GUT -$CREATURE:FORGOTTEN_BEAST_161:STOMACH -$CREATURE:FORGOTTEN_BEAST_161:GIZZARD -%CREATURE:FORGOTTEN_BEAST_161:PANCREAS -#CREATURE:FORGOTTEN_BEAST_161:SPLEEN -#CREATURE:FORGOTTEN_BEAST_161:KIDNEY -#CREATURE:FORGOTTEN_BEAST_162:MUSCLE - CREATURE:FORGOTTEN_BEAST_162:EYE -"CREATURE:FORGOTTEN_BEAST_162:BRAIN -!CREATURE:FORGOTTEN_BEAST_162:LUNG -"CREATURE:FORGOTTEN_BEAST_162:HEART -"CREATURE:FORGOTTEN_BEAST_162:LIVER - CREATURE:FORGOTTEN_BEAST_162:GUT -$CREATURE:FORGOTTEN_BEAST_162:STOMACH -$CREATURE:FORGOTTEN_BEAST_162:GIZZARD -%CREATURE:FORGOTTEN_BEAST_162:PANCREAS -#CREATURE:FORGOTTEN_BEAST_162:SPLEEN -#CREATURE:FORGOTTEN_BEAST_162:KIDNEY -#CREATURE:FORGOTTEN_BEAST_163:MUSCLE - CREATURE:FORGOTTEN_BEAST_163:EYE -"CREATURE:FORGOTTEN_BEAST_163:BRAIN -!CREATURE:FORGOTTEN_BEAST_163:LUNG -"CREATURE:FORGOTTEN_BEAST_163:HEART -"CREATURE:FORGOTTEN_BEAST_163:LIVER - CREATURE:FORGOTTEN_BEAST_163:GUT -$CREATURE:FORGOTTEN_BEAST_163:STOMACH -$CREATURE:FORGOTTEN_BEAST_163:GIZZARD -%CREATURE:FORGOTTEN_BEAST_163:PANCREAS -#CREATURE:FORGOTTEN_BEAST_163:SPLEEN -#CREATURE:FORGOTTEN_BEAST_163:KIDNEY -#CREATURE:FORGOTTEN_BEAST_165:MUSCLE - CREATURE:FORGOTTEN_BEAST_165:EYE -"CREATURE:FORGOTTEN_BEAST_165:BRAIN -!CREATURE:FORGOTTEN_BEAST_165:LUNG -"CREATURE:FORGOTTEN_BEAST_165:HEART -"CREATURE:FORGOTTEN_BEAST_165:LIVER - CREATURE:FORGOTTEN_BEAST_165:GUT -$CREATURE:FORGOTTEN_BEAST_165:STOMACH -$CREATURE:FORGOTTEN_BEAST_165:GIZZARD -%CREATURE:FORGOTTEN_BEAST_165:PANCREAS -#CREATURE:FORGOTTEN_BEAST_165:SPLEEN -#CREATURE:FORGOTTEN_BEAST_165:KIDNEY -#CREATURE:FORGOTTEN_BEAST_167:MUSCLE - CREATURE:FORGOTTEN_BEAST_167:EYE -"CREATURE:FORGOTTEN_BEAST_167:BRAIN -!CREATURE:FORGOTTEN_BEAST_167:LUNG -"CREATURE:FORGOTTEN_BEAST_167:HEART -"CREATURE:FORGOTTEN_BEAST_167:LIVER - CREATURE:FORGOTTEN_BEAST_167:GUT -$CREATURE:FORGOTTEN_BEAST_167:STOMACH -$CREATURE:FORGOTTEN_BEAST_167:GIZZARD -%CREATURE:FORGOTTEN_BEAST_167:PANCREAS -#CREATURE:FORGOTTEN_BEAST_167:SPLEEN -#CREATURE:FORGOTTEN_BEAST_167:KIDNEY -#CREATURE:FORGOTTEN_BEAST_168:MUSCLE - CREATURE:FORGOTTEN_BEAST_168:EYE -"CREATURE:FORGOTTEN_BEAST_168:BRAIN -!CREATURE:FORGOTTEN_BEAST_168:LUNG -"CREATURE:FORGOTTEN_BEAST_168:HEART -"CREATURE:FORGOTTEN_BEAST_168:LIVER - CREATURE:FORGOTTEN_BEAST_168:GUT -$CREATURE:FORGOTTEN_BEAST_168:STOMACH -$CREATURE:FORGOTTEN_BEAST_168:GIZZARD -%CREATURE:FORGOTTEN_BEAST_168:PANCREAS -#CREATURE:FORGOTTEN_BEAST_168:SPLEEN -#CREATURE:FORGOTTEN_BEAST_168:KIDNEY -#CREATURE:FORGOTTEN_BEAST_169:MUSCLE - CREATURE:FORGOTTEN_BEAST_169:EYE -"CREATURE:FORGOTTEN_BEAST_169:BRAIN -!CREATURE:FORGOTTEN_BEAST_169:LUNG -"CREATURE:FORGOTTEN_BEAST_169:HEART -"CREATURE:FORGOTTEN_BEAST_169:LIVER - CREATURE:FORGOTTEN_BEAST_169:GUT -$CREATURE:FORGOTTEN_BEAST_169:STOMACH -$CREATURE:FORGOTTEN_BEAST_169:GIZZARD -%CREATURE:FORGOTTEN_BEAST_169:PANCREAS -#CREATURE:FORGOTTEN_BEAST_169:SPLEEN -#CREATURE:FORGOTTEN_BEAST_169:KIDNEY -#CREATURE:FORGOTTEN_BEAST_170:MUSCLE - CREATURE:FORGOTTEN_BEAST_170:EYE -"CREATURE:FORGOTTEN_BEAST_170:BRAIN -!CREATURE:FORGOTTEN_BEAST_170:LUNG -"CREATURE:FORGOTTEN_BEAST_170:HEART -"CREATURE:FORGOTTEN_BEAST_170:LIVER - CREATURE:FORGOTTEN_BEAST_170:GUT -$CREATURE:FORGOTTEN_BEAST_170:STOMACH -$CREATURE:FORGOTTEN_BEAST_170:GIZZARD -%CREATURE:FORGOTTEN_BEAST_170:PANCREAS -#CREATURE:FORGOTTEN_BEAST_170:SPLEEN -#CREATURE:FORGOTTEN_BEAST_170:KIDNEY -#CREATURE:FORGOTTEN_BEAST_171:MUSCLE - CREATURE:FORGOTTEN_BEAST_171:EYE -"CREATURE:FORGOTTEN_BEAST_171:BRAIN -!CREATURE:FORGOTTEN_BEAST_171:LUNG -"CREATURE:FORGOTTEN_BEAST_171:HEART -"CREATURE:FORGOTTEN_BEAST_171:LIVER - CREATURE:FORGOTTEN_BEAST_171:GUT -$CREATURE:FORGOTTEN_BEAST_171:STOMACH -$CREATURE:FORGOTTEN_BEAST_171:GIZZARD -%CREATURE:FORGOTTEN_BEAST_171:PANCREAS -#CREATURE:FORGOTTEN_BEAST_171:SPLEEN -#CREATURE:FORGOTTEN_BEAST_171:KIDNEY -#CREATURE:FORGOTTEN_BEAST_172:MUSCLE - CREATURE:FORGOTTEN_BEAST_172:EYE -"CREATURE:FORGOTTEN_BEAST_172:BRAIN -!CREATURE:FORGOTTEN_BEAST_172:LUNG -"CREATURE:FORGOTTEN_BEAST_172:HEART -"CREATURE:FORGOTTEN_BEAST_172:LIVER - CREATURE:FORGOTTEN_BEAST_172:GUT -$CREATURE:FORGOTTEN_BEAST_172:STOMACH -$CREATURE:FORGOTTEN_BEAST_172:GIZZARD -%CREATURE:FORGOTTEN_BEAST_172:PANCREAS -#CREATURE:FORGOTTEN_BEAST_172:SPLEEN -#CREATURE:FORGOTTEN_BEAST_172:KIDNEY -#CREATURE:FORGOTTEN_BEAST_173:MUSCLE - CREATURE:FORGOTTEN_BEAST_173:EYE -"CREATURE:FORGOTTEN_BEAST_173:BRAIN -!CREATURE:FORGOTTEN_BEAST_173:LUNG -"CREATURE:FORGOTTEN_BEAST_173:HEART -"CREATURE:FORGOTTEN_BEAST_173:LIVER - CREATURE:FORGOTTEN_BEAST_173:GUT -$CREATURE:FORGOTTEN_BEAST_173:STOMACH -$CREATURE:FORGOTTEN_BEAST_173:GIZZARD -%CREATURE:FORGOTTEN_BEAST_173:PANCREAS -#CREATURE:FORGOTTEN_BEAST_173:SPLEEN -#CREATURE:FORGOTTEN_BEAST_173:KIDNEY -#CREATURE:FORGOTTEN_BEAST_176:MUSCLE - CREATURE:FORGOTTEN_BEAST_176:EYE -"CREATURE:FORGOTTEN_BEAST_176:BRAIN -!CREATURE:FORGOTTEN_BEAST_176:LUNG -"CREATURE:FORGOTTEN_BEAST_176:HEART -"CREATURE:FORGOTTEN_BEAST_176:LIVER - CREATURE:FORGOTTEN_BEAST_176:GUT -$CREATURE:FORGOTTEN_BEAST_176:STOMACH -$CREATURE:FORGOTTEN_BEAST_176:GIZZARD -%CREATURE:FORGOTTEN_BEAST_176:PANCREAS -#CREATURE:FORGOTTEN_BEAST_176:SPLEEN -#CREATURE:FORGOTTEN_BEAST_176:KIDNEY -#CREATURE:FORGOTTEN_BEAST_177:MUSCLE - CREATURE:FORGOTTEN_BEAST_177:EYE -"CREATURE:FORGOTTEN_BEAST_177:BRAIN -!CREATURE:FORGOTTEN_BEAST_177:LUNG -"CREATURE:FORGOTTEN_BEAST_177:HEART -"CREATURE:FORGOTTEN_BEAST_177:LIVER - CREATURE:FORGOTTEN_BEAST_177:GUT -$CREATURE:FORGOTTEN_BEAST_177:STOMACH -$CREATURE:FORGOTTEN_BEAST_177:GIZZARD -%CREATURE:FORGOTTEN_BEAST_177:PANCREAS -#CREATURE:FORGOTTEN_BEAST_177:SPLEEN -#CREATURE:FORGOTTEN_BEAST_177:KIDNEY -#CREATURE:FORGOTTEN_BEAST_178:MUSCLE - CREATURE:FORGOTTEN_BEAST_178:EYE -"CREATURE:FORGOTTEN_BEAST_178:BRAIN -!CREATURE:FORGOTTEN_BEAST_178:LUNG -"CREATURE:FORGOTTEN_BEAST_178:HEART -"CREATURE:FORGOTTEN_BEAST_178:LIVER - CREATURE:FORGOTTEN_BEAST_178:GUT -$CREATURE:FORGOTTEN_BEAST_178:STOMACH -$CREATURE:FORGOTTEN_BEAST_178:GIZZARD -%CREATURE:FORGOTTEN_BEAST_178:PANCREAS -#CREATURE:FORGOTTEN_BEAST_178:SPLEEN -#CREATURE:FORGOTTEN_BEAST_178:KIDNEY -#CREATURE:FORGOTTEN_BEAST_179:MUSCLE - CREATURE:FORGOTTEN_BEAST_179:EYE -"CREATURE:FORGOTTEN_BEAST_179:BRAIN -!CREATURE:FORGOTTEN_BEAST_179:LUNG -"CREATURE:FORGOTTEN_BEAST_179:HEART -"CREATURE:FORGOTTEN_BEAST_179:LIVER - CREATURE:FORGOTTEN_BEAST_179:GUT -$CREATURE:FORGOTTEN_BEAST_179:STOMACH -$CREATURE:FORGOTTEN_BEAST_179:GIZZARD -%CREATURE:FORGOTTEN_BEAST_179:PANCREAS -#CREATURE:FORGOTTEN_BEAST_179:SPLEEN -#CREATURE:FORGOTTEN_BEAST_179:KIDNEY -#CREATURE:FORGOTTEN_BEAST_180:MUSCLE - CREATURE:FORGOTTEN_BEAST_180:EYE -"CREATURE:FORGOTTEN_BEAST_180:BRAIN -!CREATURE:FORGOTTEN_BEAST_180:LUNG -"CREATURE:FORGOTTEN_BEAST_180:HEART -"CREATURE:FORGOTTEN_BEAST_180:LIVER - CREATURE:FORGOTTEN_BEAST_180:GUT -$CREATURE:FORGOTTEN_BEAST_180:STOMACH -$CREATURE:FORGOTTEN_BEAST_180:GIZZARD -%CREATURE:FORGOTTEN_BEAST_180:PANCREAS -#CREATURE:FORGOTTEN_BEAST_180:SPLEEN -#CREATURE:FORGOTTEN_BEAST_180:KIDNEY -#CREATURE:FORGOTTEN_BEAST_181:MUSCLE - CREATURE:FORGOTTEN_BEAST_181:EYE -"CREATURE:FORGOTTEN_BEAST_181:BRAIN -!CREATURE:FORGOTTEN_BEAST_181:LUNG -"CREATURE:FORGOTTEN_BEAST_181:HEART -"CREATURE:FORGOTTEN_BEAST_181:LIVER - CREATURE:FORGOTTEN_BEAST_181:GUT -$CREATURE:FORGOTTEN_BEAST_181:STOMACH -$CREATURE:FORGOTTEN_BEAST_181:GIZZARD -%CREATURE:FORGOTTEN_BEAST_181:PANCREAS -#CREATURE:FORGOTTEN_BEAST_181:SPLEEN -#CREATURE:FORGOTTEN_BEAST_181:KIDNEY -#CREATURE:FORGOTTEN_BEAST_182:MUSCLE - CREATURE:FORGOTTEN_BEAST_182:EYE -"CREATURE:FORGOTTEN_BEAST_182:BRAIN -!CREATURE:FORGOTTEN_BEAST_182:LUNG -"CREATURE:FORGOTTEN_BEAST_182:HEART -"CREATURE:FORGOTTEN_BEAST_182:LIVER - CREATURE:FORGOTTEN_BEAST_182:GUT -$CREATURE:FORGOTTEN_BEAST_182:STOMACH -$CREATURE:FORGOTTEN_BEAST_182:GIZZARD -%CREATURE:FORGOTTEN_BEAST_182:PANCREAS -#CREATURE:FORGOTTEN_BEAST_182:SPLEEN -#CREATURE:FORGOTTEN_BEAST_182:KIDNEY -#CREATURE:FORGOTTEN_BEAST_183:MUSCLE - CREATURE:FORGOTTEN_BEAST_183:EYE -"CREATURE:FORGOTTEN_BEAST_183:BRAIN -!CREATURE:FORGOTTEN_BEAST_183:LUNG -"CREATURE:FORGOTTEN_BEAST_183:HEART -"CREATURE:FORGOTTEN_BEAST_183:LIVER - CREATURE:FORGOTTEN_BEAST_183:GUT -$CREATURE:FORGOTTEN_BEAST_183:STOMACH -$CREATURE:FORGOTTEN_BEAST_183:GIZZARD -%CREATURE:FORGOTTEN_BEAST_183:PANCREAS -#CREATURE:FORGOTTEN_BEAST_183:SPLEEN -#CREATURE:FORGOTTEN_BEAST_183:KIDNEY -#CREATURE:FORGOTTEN_BEAST_184:MUSCLE - CREATURE:FORGOTTEN_BEAST_184:EYE -"CREATURE:FORGOTTEN_BEAST_184:BRAIN -!CREATURE:FORGOTTEN_BEAST_184:LUNG -"CREATURE:FORGOTTEN_BEAST_184:HEART -"CREATURE:FORGOTTEN_BEAST_184:LIVER - CREATURE:FORGOTTEN_BEAST_184:GUT -$CREATURE:FORGOTTEN_BEAST_184:STOMACH -$CREATURE:FORGOTTEN_BEAST_184:GIZZARD -%CREATURE:FORGOTTEN_BEAST_184:PANCREAS -#CREATURE:FORGOTTEN_BEAST_184:SPLEEN -#CREATURE:FORGOTTEN_BEAST_184:KIDNEY -#CREATURE:FORGOTTEN_BEAST_185:MUSCLE - CREATURE:FORGOTTEN_BEAST_185:EYE -"CREATURE:FORGOTTEN_BEAST_185:BRAIN -!CREATURE:FORGOTTEN_BEAST_185:LUNG -"CREATURE:FORGOTTEN_BEAST_185:HEART -"CREATURE:FORGOTTEN_BEAST_185:LIVER - CREATURE:FORGOTTEN_BEAST_185:GUT -$CREATURE:FORGOTTEN_BEAST_185:STOMACH -$CREATURE:FORGOTTEN_BEAST_185:GIZZARD -%CREATURE:FORGOTTEN_BEAST_185:PANCREAS -#CREATURE:FORGOTTEN_BEAST_185:SPLEEN -#CREATURE:FORGOTTEN_BEAST_185:KIDNEY -#CREATURE:FORGOTTEN_BEAST_186:MUSCLE - CREATURE:FORGOTTEN_BEAST_186:EYE -"CREATURE:FORGOTTEN_BEAST_186:BRAIN -!CREATURE:FORGOTTEN_BEAST_186:LUNG -"CREATURE:FORGOTTEN_BEAST_186:HEART -"CREATURE:FORGOTTEN_BEAST_186:LIVER - CREATURE:FORGOTTEN_BEAST_186:GUT -$CREATURE:FORGOTTEN_BEAST_186:STOMACH -$CREATURE:FORGOTTEN_BEAST_186:GIZZARD -%CREATURE:FORGOTTEN_BEAST_186:PANCREAS -#CREATURE:FORGOTTEN_BEAST_186:SPLEEN -#CREATURE:FORGOTTEN_BEAST_186:KIDNEY -#CREATURE:FORGOTTEN_BEAST_188:MUSCLE - CREATURE:FORGOTTEN_BEAST_188:EYE -"CREATURE:FORGOTTEN_BEAST_188:BRAIN -!CREATURE:FORGOTTEN_BEAST_188:LUNG -"CREATURE:FORGOTTEN_BEAST_188:HEART -"CREATURE:FORGOTTEN_BEAST_188:LIVER - CREATURE:FORGOTTEN_BEAST_188:GUT -$CREATURE:FORGOTTEN_BEAST_188:STOMACH -$CREATURE:FORGOTTEN_BEAST_188:GIZZARD -%CREATURE:FORGOTTEN_BEAST_188:PANCREAS -#CREATURE:FORGOTTEN_BEAST_188:SPLEEN -#CREATURE:FORGOTTEN_BEAST_188:KIDNEY -#CREATURE:FORGOTTEN_BEAST_189:MUSCLE - CREATURE:FORGOTTEN_BEAST_189:EYE -"CREATURE:FORGOTTEN_BEAST_189:BRAIN -!CREATURE:FORGOTTEN_BEAST_189:LUNG -"CREATURE:FORGOTTEN_BEAST_189:HEART -"CREATURE:FORGOTTEN_BEAST_189:LIVER - CREATURE:FORGOTTEN_BEAST_189:GUT -$CREATURE:FORGOTTEN_BEAST_189:STOMACH -$CREATURE:FORGOTTEN_BEAST_189:GIZZARD -%CREATURE:FORGOTTEN_BEAST_189:PANCREAS -#CREATURE:FORGOTTEN_BEAST_189:SPLEEN -#CREATURE:FORGOTTEN_BEAST_189:KIDNEY -#CREATURE:FORGOTTEN_BEAST_190:MUSCLE - CREATURE:FORGOTTEN_BEAST_190:EYE -"CREATURE:FORGOTTEN_BEAST_190:BRAIN -!CREATURE:FORGOTTEN_BEAST_190:LUNG -"CREATURE:FORGOTTEN_BEAST_190:HEART -"CREATURE:FORGOTTEN_BEAST_190:LIVER - CREATURE:FORGOTTEN_BEAST_190:GUT -$CREATURE:FORGOTTEN_BEAST_190:STOMACH -$CREATURE:FORGOTTEN_BEAST_190:GIZZARD -%CREATURE:FORGOTTEN_BEAST_190:PANCREAS -#CREATURE:FORGOTTEN_BEAST_190:SPLEEN -#CREATURE:FORGOTTEN_BEAST_190:KIDNEY -#CREATURE:FORGOTTEN_BEAST_191:MUSCLE - CREATURE:FORGOTTEN_BEAST_191:EYE -"CREATURE:FORGOTTEN_BEAST_191:BRAIN -!CREATURE:FORGOTTEN_BEAST_191:LUNG -"CREATURE:FORGOTTEN_BEAST_191:HEART -"CREATURE:FORGOTTEN_BEAST_191:LIVER - CREATURE:FORGOTTEN_BEAST_191:GUT -$CREATURE:FORGOTTEN_BEAST_191:STOMACH -$CREATURE:FORGOTTEN_BEAST_191:GIZZARD -%CREATURE:FORGOTTEN_BEAST_191:PANCREAS -#CREATURE:FORGOTTEN_BEAST_191:SPLEEN -#CREATURE:FORGOTTEN_BEAST_191:KIDNEY -#CREATURE:FORGOTTEN_BEAST_193:MUSCLE - CREATURE:FORGOTTEN_BEAST_193:EYE -"CREATURE:FORGOTTEN_BEAST_193:BRAIN -!CREATURE:FORGOTTEN_BEAST_193:LUNG -"CREATURE:FORGOTTEN_BEAST_193:HEART -"CREATURE:FORGOTTEN_BEAST_193:LIVER - CREATURE:FORGOTTEN_BEAST_193:GUT -$CREATURE:FORGOTTEN_BEAST_193:STOMACH -$CREATURE:FORGOTTEN_BEAST_193:GIZZARD -%CREATURE:FORGOTTEN_BEAST_193:PANCREAS -#CREATURE:FORGOTTEN_BEAST_193:SPLEEN -#CREATURE:FORGOTTEN_BEAST_193:KIDNEY -#CREATURE:FORGOTTEN_BEAST_194:MUSCLE - CREATURE:FORGOTTEN_BEAST_194:EYE -"CREATURE:FORGOTTEN_BEAST_194:BRAIN -!CREATURE:FORGOTTEN_BEAST_194:LUNG -"CREATURE:FORGOTTEN_BEAST_194:HEART -"CREATURE:FORGOTTEN_BEAST_194:LIVER - CREATURE:FORGOTTEN_BEAST_194:GUT -$CREATURE:FORGOTTEN_BEAST_194:STOMACH -$CREATURE:FORGOTTEN_BEAST_194:GIZZARD -%CREATURE:FORGOTTEN_BEAST_194:PANCREAS -#CREATURE:FORGOTTEN_BEAST_194:SPLEEN -#CREATURE:FORGOTTEN_BEAST_194:KIDNEY -#CREATURE:FORGOTTEN_BEAST_195:MUSCLE - CREATURE:FORGOTTEN_BEAST_195:EYE -"CREATURE:FORGOTTEN_BEAST_195:BRAIN -!CREATURE:FORGOTTEN_BEAST_195:LUNG -"CREATURE:FORGOTTEN_BEAST_195:HEART -"CREATURE:FORGOTTEN_BEAST_195:LIVER - CREATURE:FORGOTTEN_BEAST_195:GUT -$CREATURE:FORGOTTEN_BEAST_195:STOMACH -$CREATURE:FORGOTTEN_BEAST_195:GIZZARD -%CREATURE:FORGOTTEN_BEAST_195:PANCREAS -#CREATURE:FORGOTTEN_BEAST_195:SPLEEN -#CREATURE:FORGOTTEN_BEAST_195:KIDNEY -#CREATURE:FORGOTTEN_BEAST_196:MUSCLE - CREATURE:FORGOTTEN_BEAST_196:EYE -"CREATURE:FORGOTTEN_BEAST_196:BRAIN -!CREATURE:FORGOTTEN_BEAST_196:LUNG -"CREATURE:FORGOTTEN_BEAST_196:HEART -"CREATURE:FORGOTTEN_BEAST_196:LIVER - CREATURE:FORGOTTEN_BEAST_196:GUT -$CREATURE:FORGOTTEN_BEAST_196:STOMACH -$CREATURE:FORGOTTEN_BEAST_196:GIZZARD -%CREATURE:FORGOTTEN_BEAST_196:PANCREAS -#CREATURE:FORGOTTEN_BEAST_196:SPLEEN -#CREATURE:FORGOTTEN_BEAST_196:KIDNEY -#CREATURE:FORGOTTEN_BEAST_197:MUSCLE - CREATURE:FORGOTTEN_BEAST_197:EYE -"CREATURE:FORGOTTEN_BEAST_197:BRAIN -!CREATURE:FORGOTTEN_BEAST_197:LUNG -"CREATURE:FORGOTTEN_BEAST_197:HEART -"CREATURE:FORGOTTEN_BEAST_197:LIVER - CREATURE:FORGOTTEN_BEAST_197:GUT -$CREATURE:FORGOTTEN_BEAST_197:STOMACH -$CREATURE:FORGOTTEN_BEAST_197:GIZZARD -%CREATURE:FORGOTTEN_BEAST_197:PANCREAS -#CREATURE:FORGOTTEN_BEAST_197:SPLEEN -#CREATURE:FORGOTTEN_BEAST_197:KIDNEY -#CREATURE:FORGOTTEN_BEAST_199:MUSCLE - CREATURE:FORGOTTEN_BEAST_199:EYE -"CREATURE:FORGOTTEN_BEAST_199:BRAIN -!CREATURE:FORGOTTEN_BEAST_199:LUNG -"CREATURE:FORGOTTEN_BEAST_199:HEART -"CREATURE:FORGOTTEN_BEAST_199:LIVER - CREATURE:FORGOTTEN_BEAST_199:GUT -$CREATURE:FORGOTTEN_BEAST_199:STOMACH -$CREATURE:FORGOTTEN_BEAST_199:GIZZARD -%CREATURE:FORGOTTEN_BEAST_199:PANCREAS -#CREATURE:FORGOTTEN_BEAST_199:SPLEEN -#CREATURE:FORGOTTEN_BEAST_199:KIDNEY -#CREATURE:FORGOTTEN_BEAST_200:MUSCLE - CREATURE:FORGOTTEN_BEAST_200:EYE -"CREATURE:FORGOTTEN_BEAST_200:BRAIN -!CREATURE:FORGOTTEN_BEAST_200:LUNG -"CREATURE:FORGOTTEN_BEAST_200:HEART -"CREATURE:FORGOTTEN_BEAST_200:LIVER - CREATURE:FORGOTTEN_BEAST_200:GUT -$CREATURE:FORGOTTEN_BEAST_200:STOMACH -$CREATURE:FORGOTTEN_BEAST_200:GIZZARD -%CREATURE:FORGOTTEN_BEAST_200:PANCREAS -#CREATURE:FORGOTTEN_BEAST_200:SPLEEN -#CREATURE:FORGOTTEN_BEAST_200:KIDNEY -#CREATURE:FORGOTTEN_BEAST_201:MUSCLE - CREATURE:FORGOTTEN_BEAST_201:EYE -"CREATURE:FORGOTTEN_BEAST_201:BRAIN -!CREATURE:FORGOTTEN_BEAST_201:LUNG -"CREATURE:FORGOTTEN_BEAST_201:HEART -"CREATURE:FORGOTTEN_BEAST_201:LIVER - CREATURE:FORGOTTEN_BEAST_201:GUT -$CREATURE:FORGOTTEN_BEAST_201:STOMACH -$CREATURE:FORGOTTEN_BEAST_201:GIZZARD -%CREATURE:FORGOTTEN_BEAST_201:PANCREAS -#CREATURE:FORGOTTEN_BEAST_201:SPLEEN -#CREATURE:FORGOTTEN_BEAST_201:KIDNEY -#CREATURE:FORGOTTEN_BEAST_204:MUSCLE - CREATURE:FORGOTTEN_BEAST_204:EYE -"CREATURE:FORGOTTEN_BEAST_204:BRAIN -!CREATURE:FORGOTTEN_BEAST_204:LUNG -"CREATURE:FORGOTTEN_BEAST_204:HEART -"CREATURE:FORGOTTEN_BEAST_204:LIVER - CREATURE:FORGOTTEN_BEAST_204:GUT -$CREATURE:FORGOTTEN_BEAST_204:STOMACH -$CREATURE:FORGOTTEN_BEAST_204:GIZZARD -%CREATURE:FORGOTTEN_BEAST_204:PANCREAS -#CREATURE:FORGOTTEN_BEAST_204:SPLEEN -#CREATURE:FORGOTTEN_BEAST_204:KIDNEY -#CREATURE:FORGOTTEN_BEAST_205:MUSCLE - CREATURE:FORGOTTEN_BEAST_205:EYE -"CREATURE:FORGOTTEN_BEAST_205:BRAIN -!CREATURE:FORGOTTEN_BEAST_205:LUNG -"CREATURE:FORGOTTEN_BEAST_205:HEART -"CREATURE:FORGOTTEN_BEAST_205:LIVER - CREATURE:FORGOTTEN_BEAST_205:GUT -$CREATURE:FORGOTTEN_BEAST_205:STOMACH -$CREATURE:FORGOTTEN_BEAST_205:GIZZARD -%CREATURE:FORGOTTEN_BEAST_205:PANCREAS -#CREATURE:FORGOTTEN_BEAST_205:SPLEEN -#CREATURE:FORGOTTEN_BEAST_205:KIDNEY -#CREATURE:FORGOTTEN_BEAST_206:MUSCLE - CREATURE:FORGOTTEN_BEAST_206:EYE -"CREATURE:FORGOTTEN_BEAST_206:BRAIN -!CREATURE:FORGOTTEN_BEAST_206:LUNG -"CREATURE:FORGOTTEN_BEAST_206:HEART -"CREATURE:FORGOTTEN_BEAST_206:LIVER - CREATURE:FORGOTTEN_BEAST_206:GUT -$CREATURE:FORGOTTEN_BEAST_206:STOMACH -$CREATURE:FORGOTTEN_BEAST_206:GIZZARD -%CREATURE:FORGOTTEN_BEAST_206:PANCREAS -#CREATURE:FORGOTTEN_BEAST_206:SPLEEN -#CREATURE:FORGOTTEN_BEAST_206:KIDNEY -#CREATURE:FORGOTTEN_BEAST_207:MUSCLE - CREATURE:FORGOTTEN_BEAST_207:EYE -"CREATURE:FORGOTTEN_BEAST_207:BRAIN -!CREATURE:FORGOTTEN_BEAST_207:LUNG -"CREATURE:FORGOTTEN_BEAST_207:HEART -"CREATURE:FORGOTTEN_BEAST_207:LIVER - CREATURE:FORGOTTEN_BEAST_207:GUT -$CREATURE:FORGOTTEN_BEAST_207:STOMACH -$CREATURE:FORGOTTEN_BEAST_207:GIZZARD -%CREATURE:FORGOTTEN_BEAST_207:PANCREAS -#CREATURE:FORGOTTEN_BEAST_207:SPLEEN -#CREATURE:FORGOTTEN_BEAST_207:KIDNEY -#CREATURE:FORGOTTEN_BEAST_208:MUSCLE - CREATURE:FORGOTTEN_BEAST_208:EYE -"CREATURE:FORGOTTEN_BEAST_208:BRAIN -!CREATURE:FORGOTTEN_BEAST_208:LUNG -"CREATURE:FORGOTTEN_BEAST_208:HEART -"CREATURE:FORGOTTEN_BEAST_208:LIVER - CREATURE:FORGOTTEN_BEAST_208:GUT -$CREATURE:FORGOTTEN_BEAST_208:STOMACH -$CREATURE:FORGOTTEN_BEAST_208:GIZZARD -%CREATURE:FORGOTTEN_BEAST_208:PANCREAS -#CREATURE:FORGOTTEN_BEAST_208:SPLEEN -#CREATURE:FORGOTTEN_BEAST_208:KIDNEY -#CREATURE:FORGOTTEN_BEAST_209:MUSCLE - CREATURE:FORGOTTEN_BEAST_209:EYE -"CREATURE:FORGOTTEN_BEAST_209:BRAIN -!CREATURE:FORGOTTEN_BEAST_209:LUNG -"CREATURE:FORGOTTEN_BEAST_209:HEART -"CREATURE:FORGOTTEN_BEAST_209:LIVER - CREATURE:FORGOTTEN_BEAST_209:GUT -$CREATURE:FORGOTTEN_BEAST_209:STOMACH -$CREATURE:FORGOTTEN_BEAST_209:GIZZARD -%CREATURE:FORGOTTEN_BEAST_209:PANCREAS -#CREATURE:FORGOTTEN_BEAST_209:SPLEEN -#CREATURE:FORGOTTEN_BEAST_209:KIDNEY -#CREATURE:FORGOTTEN_BEAST_210:MUSCLE - CREATURE:FORGOTTEN_BEAST_210:EYE -"CREATURE:FORGOTTEN_BEAST_210:BRAIN -!CREATURE:FORGOTTEN_BEAST_210:LUNG -"CREATURE:FORGOTTEN_BEAST_210:HEART -"CREATURE:FORGOTTEN_BEAST_210:LIVER - CREATURE:FORGOTTEN_BEAST_210:GUT -$CREATURE:FORGOTTEN_BEAST_210:STOMACH -$CREATURE:FORGOTTEN_BEAST_210:GIZZARD -%CREATURE:FORGOTTEN_BEAST_210:PANCREAS -#CREATURE:FORGOTTEN_BEAST_210:SPLEEN -#CREATURE:FORGOTTEN_BEAST_210:KIDNEY -#CREATURE:FORGOTTEN_BEAST_211:MUSCLE - CREATURE:FORGOTTEN_BEAST_211:EYE -"CREATURE:FORGOTTEN_BEAST_211:BRAIN -!CREATURE:FORGOTTEN_BEAST_211:LUNG -"CREATURE:FORGOTTEN_BEAST_211:HEART -"CREATURE:FORGOTTEN_BEAST_211:LIVER - CREATURE:FORGOTTEN_BEAST_211:GUT -$CREATURE:FORGOTTEN_BEAST_211:STOMACH -$CREATURE:FORGOTTEN_BEAST_211:GIZZARD -%CREATURE:FORGOTTEN_BEAST_211:PANCREAS -#CREATURE:FORGOTTEN_BEAST_211:SPLEEN -#CREATURE:FORGOTTEN_BEAST_211:KIDNEY -#CREATURE:FORGOTTEN_BEAST_212:MUSCLE - CREATURE:FORGOTTEN_BEAST_212:EYE -"CREATURE:FORGOTTEN_BEAST_212:BRAIN -!CREATURE:FORGOTTEN_BEAST_212:LUNG -"CREATURE:FORGOTTEN_BEAST_212:HEART -"CREATURE:FORGOTTEN_BEAST_212:LIVER - CREATURE:FORGOTTEN_BEAST_212:GUT -$CREATURE:FORGOTTEN_BEAST_212:STOMACH -$CREATURE:FORGOTTEN_BEAST_212:GIZZARD -%CREATURE:FORGOTTEN_BEAST_212:PANCREAS -#CREATURE:FORGOTTEN_BEAST_212:SPLEEN -#CREATURE:FORGOTTEN_BEAST_212:KIDNEY -#CREATURE:FORGOTTEN_BEAST_213:MUSCLE - CREATURE:FORGOTTEN_BEAST_213:EYE -"CREATURE:FORGOTTEN_BEAST_213:BRAIN -!CREATURE:FORGOTTEN_BEAST_213:LUNG -"CREATURE:FORGOTTEN_BEAST_213:HEART -"CREATURE:FORGOTTEN_BEAST_213:LIVER - CREATURE:FORGOTTEN_BEAST_213:GUT -$CREATURE:FORGOTTEN_BEAST_213:STOMACH -$CREATURE:FORGOTTEN_BEAST_213:GIZZARD -%CREATURE:FORGOTTEN_BEAST_213:PANCREAS -#CREATURE:FORGOTTEN_BEAST_213:SPLEEN -#CREATURE:FORGOTTEN_BEAST_213:KIDNEY -#CREATURE:FORGOTTEN_BEAST_214:MUSCLE - CREATURE:FORGOTTEN_BEAST_214:EYE -"CREATURE:FORGOTTEN_BEAST_214:BRAIN -!CREATURE:FORGOTTEN_BEAST_214:LUNG -"CREATURE:FORGOTTEN_BEAST_214:HEART -"CREATURE:FORGOTTEN_BEAST_214:LIVER - CREATURE:FORGOTTEN_BEAST_214:GUT -$CREATURE:FORGOTTEN_BEAST_214:STOMACH -$CREATURE:FORGOTTEN_BEAST_214:GIZZARD -%CREATURE:FORGOTTEN_BEAST_214:PANCREAS -#CREATURE:FORGOTTEN_BEAST_214:SPLEEN -#CREATURE:FORGOTTEN_BEAST_214:KIDNEY -#CREATURE:FORGOTTEN_BEAST_215:MUSCLE - CREATURE:FORGOTTEN_BEAST_215:EYE -"CREATURE:FORGOTTEN_BEAST_215:BRAIN -!CREATURE:FORGOTTEN_BEAST_215:LUNG -"CREATURE:FORGOTTEN_BEAST_215:HEART -"CREATURE:FORGOTTEN_BEAST_215:LIVER - CREATURE:FORGOTTEN_BEAST_215:GUT -$CREATURE:FORGOTTEN_BEAST_215:STOMACH -$CREATURE:FORGOTTEN_BEAST_215:GIZZARD -%CREATURE:FORGOTTEN_BEAST_215:PANCREAS -#CREATURE:FORGOTTEN_BEAST_215:SPLEEN -#CREATURE:FORGOTTEN_BEAST_215:KIDNEY -#CREATURE:FORGOTTEN_BEAST_216:MUSCLE - CREATURE:FORGOTTEN_BEAST_216:EYE -"CREATURE:FORGOTTEN_BEAST_216:BRAIN -!CREATURE:FORGOTTEN_BEAST_216:LUNG -"CREATURE:FORGOTTEN_BEAST_216:HEART -"CREATURE:FORGOTTEN_BEAST_216:LIVER - CREATURE:FORGOTTEN_BEAST_216:GUT -$CREATURE:FORGOTTEN_BEAST_216:STOMACH -$CREATURE:FORGOTTEN_BEAST_216:GIZZARD -%CREATURE:FORGOTTEN_BEAST_216:PANCREAS -#CREATURE:FORGOTTEN_BEAST_216:SPLEEN -#CREATURE:FORGOTTEN_BEAST_216:KIDNEY -#CREATURE:FORGOTTEN_BEAST_217:MUSCLE - CREATURE:FORGOTTEN_BEAST_217:EYE -"CREATURE:FORGOTTEN_BEAST_217:BRAIN -!CREATURE:FORGOTTEN_BEAST_217:LUNG -"CREATURE:FORGOTTEN_BEAST_217:HEART -"CREATURE:FORGOTTEN_BEAST_217:LIVER - CREATURE:FORGOTTEN_BEAST_217:GUT -$CREATURE:FORGOTTEN_BEAST_217:STOMACH -$CREATURE:FORGOTTEN_BEAST_217:GIZZARD -%CREATURE:FORGOTTEN_BEAST_217:PANCREAS -#CREATURE:FORGOTTEN_BEAST_217:SPLEEN -#CREATURE:FORGOTTEN_BEAST_217:KIDNEY -#CREATURE:FORGOTTEN_BEAST_218:MUSCLE - CREATURE:FORGOTTEN_BEAST_218:EYE -"CREATURE:FORGOTTEN_BEAST_218:BRAIN -!CREATURE:FORGOTTEN_BEAST_218:LUNG -"CREATURE:FORGOTTEN_BEAST_218:HEART -"CREATURE:FORGOTTEN_BEAST_218:LIVER - CREATURE:FORGOTTEN_BEAST_218:GUT -$CREATURE:FORGOTTEN_BEAST_218:STOMACH -$CREATURE:FORGOTTEN_BEAST_218:GIZZARD -%CREATURE:FORGOTTEN_BEAST_218:PANCREAS -#CREATURE:FORGOTTEN_BEAST_218:SPLEEN -#CREATURE:FORGOTTEN_BEAST_218:KIDNEY -#CREATURE:FORGOTTEN_BEAST_220:MUSCLE - CREATURE:FORGOTTEN_BEAST_220:EYE -"CREATURE:FORGOTTEN_BEAST_220:BRAIN -!CREATURE:FORGOTTEN_BEAST_220:LUNG -"CREATURE:FORGOTTEN_BEAST_220:HEART -"CREATURE:FORGOTTEN_BEAST_220:LIVER - CREATURE:FORGOTTEN_BEAST_220:GUT -$CREATURE:FORGOTTEN_BEAST_220:STOMACH -$CREATURE:FORGOTTEN_BEAST_220:GIZZARD -%CREATURE:FORGOTTEN_BEAST_220:PANCREAS -#CREATURE:FORGOTTEN_BEAST_220:SPLEEN -#CREATURE:FORGOTTEN_BEAST_220:KIDNEY -#CREATURE:FORGOTTEN_BEAST_221:MUSCLE - CREATURE:FORGOTTEN_BEAST_221:EYE -"CREATURE:FORGOTTEN_BEAST_221:BRAIN -!CREATURE:FORGOTTEN_BEAST_221:LUNG -"CREATURE:FORGOTTEN_BEAST_221:HEART -"CREATURE:FORGOTTEN_BEAST_221:LIVER - CREATURE:FORGOTTEN_BEAST_221:GUT -$CREATURE:FORGOTTEN_BEAST_221:STOMACH -$CREATURE:FORGOTTEN_BEAST_221:GIZZARD -%CREATURE:FORGOTTEN_BEAST_221:PANCREAS -#CREATURE:FORGOTTEN_BEAST_221:SPLEEN -#CREATURE:FORGOTTEN_BEAST_221:KIDNEY -#CREATURE:FORGOTTEN_BEAST_224:MUSCLE - CREATURE:FORGOTTEN_BEAST_224:EYE -"CREATURE:FORGOTTEN_BEAST_224:BRAIN -!CREATURE:FORGOTTEN_BEAST_224:LUNG -"CREATURE:FORGOTTEN_BEAST_224:HEART -"CREATURE:FORGOTTEN_BEAST_224:LIVER - CREATURE:FORGOTTEN_BEAST_224:GUT -$CREATURE:FORGOTTEN_BEAST_224:STOMACH -$CREATURE:FORGOTTEN_BEAST_224:GIZZARD -%CREATURE:FORGOTTEN_BEAST_224:PANCREAS -#CREATURE:FORGOTTEN_BEAST_224:SPLEEN -#CREATURE:FORGOTTEN_BEAST_224:KIDNEY -#CREATURE:FORGOTTEN_BEAST_225:MUSCLE - CREATURE:FORGOTTEN_BEAST_225:EYE -"CREATURE:FORGOTTEN_BEAST_225:BRAIN -!CREATURE:FORGOTTEN_BEAST_225:LUNG -"CREATURE:FORGOTTEN_BEAST_225:HEART -"CREATURE:FORGOTTEN_BEAST_225:LIVER - CREATURE:FORGOTTEN_BEAST_225:GUT -$CREATURE:FORGOTTEN_BEAST_225:STOMACH -$CREATURE:FORGOTTEN_BEAST_225:GIZZARD -%CREATURE:FORGOTTEN_BEAST_225:PANCREAS -#CREATURE:FORGOTTEN_BEAST_225:SPLEEN -#CREATURE:FORGOTTEN_BEAST_225:KIDNEY -#CREATURE:FORGOTTEN_BEAST_228:MUSCLE - CREATURE:FORGOTTEN_BEAST_228:EYE -"CREATURE:FORGOTTEN_BEAST_228:BRAIN -!CREATURE:FORGOTTEN_BEAST_228:LUNG -"CREATURE:FORGOTTEN_BEAST_228:HEART -"CREATURE:FORGOTTEN_BEAST_228:LIVER - CREATURE:FORGOTTEN_BEAST_228:GUT -$CREATURE:FORGOTTEN_BEAST_228:STOMACH -$CREATURE:FORGOTTEN_BEAST_228:GIZZARD -%CREATURE:FORGOTTEN_BEAST_228:PANCREAS -#CREATURE:FORGOTTEN_BEAST_228:SPLEEN -#CREATURE:FORGOTTEN_BEAST_228:KIDNEY -#CREATURE:FORGOTTEN_BEAST_231:MUSCLE - CREATURE:FORGOTTEN_BEAST_231:EYE -"CREATURE:FORGOTTEN_BEAST_231:BRAIN -!CREATURE:FORGOTTEN_BEAST_231:LUNG -"CREATURE:FORGOTTEN_BEAST_231:HEART -"CREATURE:FORGOTTEN_BEAST_231:LIVER - CREATURE:FORGOTTEN_BEAST_231:GUT -$CREATURE:FORGOTTEN_BEAST_231:STOMACH -$CREATURE:FORGOTTEN_BEAST_231:GIZZARD -%CREATURE:FORGOTTEN_BEAST_231:PANCREAS -#CREATURE:FORGOTTEN_BEAST_231:SPLEEN -#CREATURE:FORGOTTEN_BEAST_231:KIDNEY -#CREATURE:FORGOTTEN_BEAST_232:MUSCLE - CREATURE:FORGOTTEN_BEAST_232:EYE -"CREATURE:FORGOTTEN_BEAST_232:BRAIN -!CREATURE:FORGOTTEN_BEAST_232:LUNG -"CREATURE:FORGOTTEN_BEAST_232:HEART -"CREATURE:FORGOTTEN_BEAST_232:LIVER - CREATURE:FORGOTTEN_BEAST_232:GUT -$CREATURE:FORGOTTEN_BEAST_232:STOMACH -$CREATURE:FORGOTTEN_BEAST_232:GIZZARD -%CREATURE:FORGOTTEN_BEAST_232:PANCREAS -#CREATURE:FORGOTTEN_BEAST_232:SPLEEN -#CREATURE:FORGOTTEN_BEAST_232:KIDNEY -#CREATURE:FORGOTTEN_BEAST_235:MUSCLE - CREATURE:FORGOTTEN_BEAST_235:EYE -"CREATURE:FORGOTTEN_BEAST_235:BRAIN -!CREATURE:FORGOTTEN_BEAST_235:LUNG -"CREATURE:FORGOTTEN_BEAST_235:HEART -"CREATURE:FORGOTTEN_BEAST_235:LIVER - CREATURE:FORGOTTEN_BEAST_235:GUT -$CREATURE:FORGOTTEN_BEAST_235:STOMACH -$CREATURE:FORGOTTEN_BEAST_235:GIZZARD -%CREATURE:FORGOTTEN_BEAST_235:PANCREAS -#CREATURE:FORGOTTEN_BEAST_235:SPLEEN -#CREATURE:FORGOTTEN_BEAST_235:KIDNEY -#CREATURE:FORGOTTEN_BEAST_236:MUSCLE - CREATURE:FORGOTTEN_BEAST_236:EYE -"CREATURE:FORGOTTEN_BEAST_236:BRAIN -!CREATURE:FORGOTTEN_BEAST_236:LUNG -"CREATURE:FORGOTTEN_BEAST_236:HEART -"CREATURE:FORGOTTEN_BEAST_236:LIVER - CREATURE:FORGOTTEN_BEAST_236:GUT -$CREATURE:FORGOTTEN_BEAST_236:STOMACH -$CREATURE:FORGOTTEN_BEAST_236:GIZZARD -%CREATURE:FORGOTTEN_BEAST_236:PANCREAS -#CREATURE:FORGOTTEN_BEAST_236:SPLEEN -#CREATURE:FORGOTTEN_BEAST_236:KIDNEY -#CREATURE:FORGOTTEN_BEAST_237:MUSCLE - CREATURE:FORGOTTEN_BEAST_237:EYE -"CREATURE:FORGOTTEN_BEAST_237:BRAIN -!CREATURE:FORGOTTEN_BEAST_237:LUNG -"CREATURE:FORGOTTEN_BEAST_237:HEART -"CREATURE:FORGOTTEN_BEAST_237:LIVER - CREATURE:FORGOTTEN_BEAST_237:GUT -$CREATURE:FORGOTTEN_BEAST_237:STOMACH -$CREATURE:FORGOTTEN_BEAST_237:GIZZARD -%CREATURE:FORGOTTEN_BEAST_237:PANCREAS -#CREATURE:FORGOTTEN_BEAST_237:SPLEEN -#CREATURE:FORGOTTEN_BEAST_237:KIDNEY -#CREATURE:FORGOTTEN_BEAST_239:MUSCLE - CREATURE:FORGOTTEN_BEAST_239:EYE -"CREATURE:FORGOTTEN_BEAST_239:BRAIN -!CREATURE:FORGOTTEN_BEAST_239:LUNG -"CREATURE:FORGOTTEN_BEAST_239:HEART -"CREATURE:FORGOTTEN_BEAST_239:LIVER - CREATURE:FORGOTTEN_BEAST_239:GUT -$CREATURE:FORGOTTEN_BEAST_239:STOMACH -$CREATURE:FORGOTTEN_BEAST_239:GIZZARD -%CREATURE:FORGOTTEN_BEAST_239:PANCREAS -#CREATURE:FORGOTTEN_BEAST_239:SPLEEN -#CREATURE:FORGOTTEN_BEAST_239:KIDNEY -#CREATURE:FORGOTTEN_BEAST_240:MUSCLE - CREATURE:FORGOTTEN_BEAST_240:EYE -"CREATURE:FORGOTTEN_BEAST_240:BRAIN -!CREATURE:FORGOTTEN_BEAST_240:LUNG -"CREATURE:FORGOTTEN_BEAST_240:HEART -"CREATURE:FORGOTTEN_BEAST_240:LIVER - CREATURE:FORGOTTEN_BEAST_240:GUT -$CREATURE:FORGOTTEN_BEAST_240:STOMACH -$CREATURE:FORGOTTEN_BEAST_240:GIZZARD -%CREATURE:FORGOTTEN_BEAST_240:PANCREAS -#CREATURE:FORGOTTEN_BEAST_240:SPLEEN -#CREATURE:FORGOTTEN_BEAST_240:KIDNEY -#CREATURE:FORGOTTEN_BEAST_242:MUSCLE - CREATURE:FORGOTTEN_BEAST_242:EYE -"CREATURE:FORGOTTEN_BEAST_242:BRAIN -!CREATURE:FORGOTTEN_BEAST_242:LUNG -"CREATURE:FORGOTTEN_BEAST_242:HEART -"CREATURE:FORGOTTEN_BEAST_242:LIVER - CREATURE:FORGOTTEN_BEAST_242:GUT -$CREATURE:FORGOTTEN_BEAST_242:STOMACH -$CREATURE:FORGOTTEN_BEAST_242:GIZZARD -%CREATURE:FORGOTTEN_BEAST_242:PANCREAS -#CREATURE:FORGOTTEN_BEAST_242:SPLEEN -#CREATURE:FORGOTTEN_BEAST_242:KIDNEY -#CREATURE:FORGOTTEN_BEAST_243:MUSCLE - CREATURE:FORGOTTEN_BEAST_243:EYE -"CREATURE:FORGOTTEN_BEAST_243:BRAIN -!CREATURE:FORGOTTEN_BEAST_243:LUNG -"CREATURE:FORGOTTEN_BEAST_243:HEART -"CREATURE:FORGOTTEN_BEAST_243:LIVER - CREATURE:FORGOTTEN_BEAST_243:GUT -$CREATURE:FORGOTTEN_BEAST_243:STOMACH -$CREATURE:FORGOTTEN_BEAST_243:GIZZARD -%CREATURE:FORGOTTEN_BEAST_243:PANCREAS -#CREATURE:FORGOTTEN_BEAST_243:SPLEEN -#CREATURE:FORGOTTEN_BEAST_243:KIDNEY -#CREATURE:FORGOTTEN_BEAST_244:MUSCLE - CREATURE:FORGOTTEN_BEAST_244:EYE -"CREATURE:FORGOTTEN_BEAST_244:BRAIN -!CREATURE:FORGOTTEN_BEAST_244:LUNG -"CREATURE:FORGOTTEN_BEAST_244:HEART -"CREATURE:FORGOTTEN_BEAST_244:LIVER - CREATURE:FORGOTTEN_BEAST_244:GUT -$CREATURE:FORGOTTEN_BEAST_244:STOMACH -$CREATURE:FORGOTTEN_BEAST_244:GIZZARD -%CREATURE:FORGOTTEN_BEAST_244:PANCREAS -#CREATURE:FORGOTTEN_BEAST_244:SPLEEN -#CREATURE:FORGOTTEN_BEAST_244:KIDNEY -#CREATURE:FORGOTTEN_BEAST_245:MUSCLE - CREATURE:FORGOTTEN_BEAST_245:EYE -"CREATURE:FORGOTTEN_BEAST_245:BRAIN -!CREATURE:FORGOTTEN_BEAST_245:LUNG -"CREATURE:FORGOTTEN_BEAST_245:HEART -"CREATURE:FORGOTTEN_BEAST_245:LIVER - CREATURE:FORGOTTEN_BEAST_245:GUT -$CREATURE:FORGOTTEN_BEAST_245:STOMACH -$CREATURE:FORGOTTEN_BEAST_245:GIZZARD -%CREATURE:FORGOTTEN_BEAST_245:PANCREAS -#CREATURE:FORGOTTEN_BEAST_245:SPLEEN -#CREATURE:FORGOTTEN_BEAST_245:KIDNEY -#CREATURE:FORGOTTEN_BEAST_246:MUSCLE - CREATURE:FORGOTTEN_BEAST_246:EYE -"CREATURE:FORGOTTEN_BEAST_246:BRAIN -!CREATURE:FORGOTTEN_BEAST_246:LUNG -"CREATURE:FORGOTTEN_BEAST_246:HEART -"CREATURE:FORGOTTEN_BEAST_246:LIVER - CREATURE:FORGOTTEN_BEAST_246:GUT -$CREATURE:FORGOTTEN_BEAST_246:STOMACH -$CREATURE:FORGOTTEN_BEAST_246:GIZZARD -%CREATURE:FORGOTTEN_BEAST_246:PANCREAS -#CREATURE:FORGOTTEN_BEAST_246:SPLEEN -#CREATURE:FORGOTTEN_BEAST_246:KIDNEY -#CREATURE:FORGOTTEN_BEAST_247:MUSCLE - CREATURE:FORGOTTEN_BEAST_247:EYE -"CREATURE:FORGOTTEN_BEAST_247:BRAIN -!CREATURE:FORGOTTEN_BEAST_247:LUNG -"CREATURE:FORGOTTEN_BEAST_247:HEART -"CREATURE:FORGOTTEN_BEAST_247:LIVER - CREATURE:FORGOTTEN_BEAST_247:GUT -$CREATURE:FORGOTTEN_BEAST_247:STOMACH -$CREATURE:FORGOTTEN_BEAST_247:GIZZARD -%CREATURE:FORGOTTEN_BEAST_247:PANCREAS -#CREATURE:FORGOTTEN_BEAST_247:SPLEEN -#CREATURE:FORGOTTEN_BEAST_247:KIDNEY -#CREATURE:FORGOTTEN_BEAST_248:MUSCLE - CREATURE:FORGOTTEN_BEAST_248:EYE -"CREATURE:FORGOTTEN_BEAST_248:BRAIN -!CREATURE:FORGOTTEN_BEAST_248:LUNG -"CREATURE:FORGOTTEN_BEAST_248:HEART -"CREATURE:FORGOTTEN_BEAST_248:LIVER - CREATURE:FORGOTTEN_BEAST_248:GUT -$CREATURE:FORGOTTEN_BEAST_248:STOMACH -$CREATURE:FORGOTTEN_BEAST_248:GIZZARD -%CREATURE:FORGOTTEN_BEAST_248:PANCREAS -#CREATURE:FORGOTTEN_BEAST_248:SPLEEN -#CREATURE:FORGOTTEN_BEAST_248:KIDNEY -#CREATURE:FORGOTTEN_BEAST_249:MUSCLE - CREATURE:FORGOTTEN_BEAST_249:EYE -"CREATURE:FORGOTTEN_BEAST_249:BRAIN -!CREATURE:FORGOTTEN_BEAST_249:LUNG -"CREATURE:FORGOTTEN_BEAST_249:HEART -"CREATURE:FORGOTTEN_BEAST_249:LIVER - CREATURE:FORGOTTEN_BEAST_249:GUT -$CREATURE:FORGOTTEN_BEAST_249:STOMACH -$CREATURE:FORGOTTEN_BEAST_249:GIZZARD -%CREATURE:FORGOTTEN_BEAST_249:PANCREAS -#CREATURE:FORGOTTEN_BEAST_249:SPLEEN -#CREATURE:FORGOTTEN_BEAST_249:KIDNEY -#CREATURE:FORGOTTEN_BEAST_253:MUSCLE - CREATURE:FORGOTTEN_BEAST_253:EYE -"CREATURE:FORGOTTEN_BEAST_253:BRAIN -!CREATURE:FORGOTTEN_BEAST_253:LUNG -"CREATURE:FORGOTTEN_BEAST_253:HEART -"CREATURE:FORGOTTEN_BEAST_253:LIVER - CREATURE:FORGOTTEN_BEAST_253:GUT -$CREATURE:FORGOTTEN_BEAST_253:STOMACH -$CREATURE:FORGOTTEN_BEAST_253:GIZZARD -%CREATURE:FORGOTTEN_BEAST_253:PANCREAS -#CREATURE:FORGOTTEN_BEAST_253:SPLEEN -#CREATURE:FORGOTTEN_BEAST_253:KIDNEY -#CREATURE:FORGOTTEN_BEAST_255:MUSCLE - CREATURE:FORGOTTEN_BEAST_255:EYE -"CREATURE:FORGOTTEN_BEAST_255:BRAIN -!CREATURE:FORGOTTEN_BEAST_255:LUNG -"CREATURE:FORGOTTEN_BEAST_255:HEART -"CREATURE:FORGOTTEN_BEAST_255:LIVER - CREATURE:FORGOTTEN_BEAST_255:GUT -$CREATURE:FORGOTTEN_BEAST_255:STOMACH -$CREATURE:FORGOTTEN_BEAST_255:GIZZARD -%CREATURE:FORGOTTEN_BEAST_255:PANCREAS -#CREATURE:FORGOTTEN_BEAST_255:SPLEEN -#CREATURE:FORGOTTEN_BEAST_255:KIDNEY -#CREATURE:FORGOTTEN_BEAST_256:MUSCLE - CREATURE:FORGOTTEN_BEAST_256:EYE -"CREATURE:FORGOTTEN_BEAST_256:BRAIN -!CREATURE:FORGOTTEN_BEAST_256:LUNG -"CREATURE:FORGOTTEN_BEAST_256:HEART -"CREATURE:FORGOTTEN_BEAST_256:LIVER - CREATURE:FORGOTTEN_BEAST_256:GUT -$CREATURE:FORGOTTEN_BEAST_256:STOMACH -$CREATURE:FORGOTTEN_BEAST_256:GIZZARD -%CREATURE:FORGOTTEN_BEAST_256:PANCREAS -#CREATURE:FORGOTTEN_BEAST_256:SPLEEN -#CREATURE:FORGOTTEN_BEAST_256:KIDNEY -#CREATURE:FORGOTTEN_BEAST_259:MUSCLE - CREATURE:FORGOTTEN_BEAST_259:EYE -"CREATURE:FORGOTTEN_BEAST_259:BRAIN -!CREATURE:FORGOTTEN_BEAST_259:LUNG -"CREATURE:FORGOTTEN_BEAST_259:HEART -"CREATURE:FORGOTTEN_BEAST_259:LIVER - CREATURE:FORGOTTEN_BEAST_259:GUT -$CREATURE:FORGOTTEN_BEAST_259:STOMACH -$CREATURE:FORGOTTEN_BEAST_259:GIZZARD -%CREATURE:FORGOTTEN_BEAST_259:PANCREAS -#CREATURE:FORGOTTEN_BEAST_259:SPLEEN -#CREATURE:FORGOTTEN_BEAST_259:KIDNEY -#CREATURE:FORGOTTEN_BEAST_260:MUSCLE - CREATURE:FORGOTTEN_BEAST_260:EYE -"CREATURE:FORGOTTEN_BEAST_260:BRAIN -!CREATURE:FORGOTTEN_BEAST_260:LUNG -"CREATURE:FORGOTTEN_BEAST_260:HEART -"CREATURE:FORGOTTEN_BEAST_260:LIVER - CREATURE:FORGOTTEN_BEAST_260:GUT -$CREATURE:FORGOTTEN_BEAST_260:STOMACH -$CREATURE:FORGOTTEN_BEAST_260:GIZZARD -%CREATURE:FORGOTTEN_BEAST_260:PANCREAS -#CREATURE:FORGOTTEN_BEAST_260:SPLEEN -#CREATURE:FORGOTTEN_BEAST_260:KIDNEY -#CREATURE:FORGOTTEN_BEAST_263:MUSCLE - CREATURE:FORGOTTEN_BEAST_263:EYE -"CREATURE:FORGOTTEN_BEAST_263:BRAIN -!CREATURE:FORGOTTEN_BEAST_263:LUNG -"CREATURE:FORGOTTEN_BEAST_263:HEART -"CREATURE:FORGOTTEN_BEAST_263:LIVER - CREATURE:FORGOTTEN_BEAST_263:GUT -$CREATURE:FORGOTTEN_BEAST_263:STOMACH -$CREATURE:FORGOTTEN_BEAST_263:GIZZARD -%CREATURE:FORGOTTEN_BEAST_263:PANCREAS -#CREATURE:FORGOTTEN_BEAST_263:SPLEEN -#CREATURE:FORGOTTEN_BEAST_263:KIDNEY -#CREATURE:FORGOTTEN_BEAST_264:MUSCLE - CREATURE:FORGOTTEN_BEAST_264:EYE -"CREATURE:FORGOTTEN_BEAST_264:BRAIN -!CREATURE:FORGOTTEN_BEAST_264:LUNG -"CREATURE:FORGOTTEN_BEAST_264:HEART -"CREATURE:FORGOTTEN_BEAST_264:LIVER - CREATURE:FORGOTTEN_BEAST_264:GUT -$CREATURE:FORGOTTEN_BEAST_264:STOMACH -$CREATURE:FORGOTTEN_BEAST_264:GIZZARD -%CREATURE:FORGOTTEN_BEAST_264:PANCREAS -#CREATURE:FORGOTTEN_BEAST_264:SPLEEN -#CREATURE:FORGOTTEN_BEAST_264:KIDNEY -#CREATURE:FORGOTTEN_BEAST_265:MUSCLE - CREATURE:FORGOTTEN_BEAST_265:EYE -"CREATURE:FORGOTTEN_BEAST_265:BRAIN -!CREATURE:FORGOTTEN_BEAST_265:LUNG -"CREATURE:FORGOTTEN_BEAST_265:HEART -"CREATURE:FORGOTTEN_BEAST_265:LIVER - CREATURE:FORGOTTEN_BEAST_265:GUT -$CREATURE:FORGOTTEN_BEAST_265:STOMACH -$CREATURE:FORGOTTEN_BEAST_265:GIZZARD -%CREATURE:FORGOTTEN_BEAST_265:PANCREAS -#CREATURE:FORGOTTEN_BEAST_265:SPLEEN -#CREATURE:FORGOTTEN_BEAST_265:KIDNEY -#CREATURE:FORGOTTEN_BEAST_266:MUSCLE - CREATURE:FORGOTTEN_BEAST_266:EYE -"CREATURE:FORGOTTEN_BEAST_266:BRAIN -!CREATURE:FORGOTTEN_BEAST_266:LUNG -"CREATURE:FORGOTTEN_BEAST_266:HEART -"CREATURE:FORGOTTEN_BEAST_266:LIVER - CREATURE:FORGOTTEN_BEAST_266:GUT -$CREATURE:FORGOTTEN_BEAST_266:STOMACH -$CREATURE:FORGOTTEN_BEAST_266:GIZZARD -%CREATURE:FORGOTTEN_BEAST_266:PANCREAS -#CREATURE:FORGOTTEN_BEAST_266:SPLEEN -#CREATURE:FORGOTTEN_BEAST_266:KIDNEY -#CREATURE:FORGOTTEN_BEAST_268:MUSCLE - CREATURE:FORGOTTEN_BEAST_268:EYE -"CREATURE:FORGOTTEN_BEAST_268:BRAIN -!CREATURE:FORGOTTEN_BEAST_268:LUNG -"CREATURE:FORGOTTEN_BEAST_268:HEART -"CREATURE:FORGOTTEN_BEAST_268:LIVER - CREATURE:FORGOTTEN_BEAST_268:GUT -$CREATURE:FORGOTTEN_BEAST_268:STOMACH -$CREATURE:FORGOTTEN_BEAST_268:GIZZARD -%CREATURE:FORGOTTEN_BEAST_268:PANCREAS -#CREATURE:FORGOTTEN_BEAST_268:SPLEEN -#CREATURE:FORGOTTEN_BEAST_268:KIDNEY -#CREATURE:FORGOTTEN_BEAST_269:MUSCLE - CREATURE:FORGOTTEN_BEAST_269:EYE -"CREATURE:FORGOTTEN_BEAST_269:BRAIN -!CREATURE:FORGOTTEN_BEAST_269:LUNG -"CREATURE:FORGOTTEN_BEAST_269:HEART -"CREATURE:FORGOTTEN_BEAST_269:LIVER - CREATURE:FORGOTTEN_BEAST_269:GUT -$CREATURE:FORGOTTEN_BEAST_269:STOMACH -$CREATURE:FORGOTTEN_BEAST_269:GIZZARD -%CREATURE:FORGOTTEN_BEAST_269:PANCREAS -#CREATURE:FORGOTTEN_BEAST_269:SPLEEN -#CREATURE:FORGOTTEN_BEAST_269:KIDNEY -#CREATURE:FORGOTTEN_BEAST_270:MUSCLE - CREATURE:FORGOTTEN_BEAST_270:EYE -"CREATURE:FORGOTTEN_BEAST_270:BRAIN -!CREATURE:FORGOTTEN_BEAST_270:LUNG -"CREATURE:FORGOTTEN_BEAST_270:HEART -"CREATURE:FORGOTTEN_BEAST_270:LIVER - CREATURE:FORGOTTEN_BEAST_270:GUT -$CREATURE:FORGOTTEN_BEAST_270:STOMACH -$CREATURE:FORGOTTEN_BEAST_270:GIZZARD -%CREATURE:FORGOTTEN_BEAST_270:PANCREAS -#CREATURE:FORGOTTEN_BEAST_270:SPLEEN -#CREATURE:FORGOTTEN_BEAST_270:KIDNEY -#CREATURE:FORGOTTEN_BEAST_271:MUSCLE - CREATURE:FORGOTTEN_BEAST_271:EYE -"CREATURE:FORGOTTEN_BEAST_271:BRAIN -!CREATURE:FORGOTTEN_BEAST_271:LUNG -"CREATURE:FORGOTTEN_BEAST_271:HEART -"CREATURE:FORGOTTEN_BEAST_271:LIVER - CREATURE:FORGOTTEN_BEAST_271:GUT -$CREATURE:FORGOTTEN_BEAST_271:STOMACH -$CREATURE:FORGOTTEN_BEAST_271:GIZZARD -%CREATURE:FORGOTTEN_BEAST_271:PANCREAS -#CREATURE:FORGOTTEN_BEAST_271:SPLEEN -#CREATURE:FORGOTTEN_BEAST_271:KIDNEY -#CREATURE:FORGOTTEN_BEAST_272:MUSCLE - CREATURE:FORGOTTEN_BEAST_272:EYE -"CREATURE:FORGOTTEN_BEAST_272:BRAIN -!CREATURE:FORGOTTEN_BEAST_272:LUNG -"CREATURE:FORGOTTEN_BEAST_272:HEART -"CREATURE:FORGOTTEN_BEAST_272:LIVER - CREATURE:FORGOTTEN_BEAST_272:GUT -$CREATURE:FORGOTTEN_BEAST_272:STOMACH -$CREATURE:FORGOTTEN_BEAST_272:GIZZARD -%CREATURE:FORGOTTEN_BEAST_272:PANCREAS -#CREATURE:FORGOTTEN_BEAST_272:SPLEEN -#CREATURE:FORGOTTEN_BEAST_272:KIDNEY -#CREATURE:FORGOTTEN_BEAST_273:MUSCLE - CREATURE:FORGOTTEN_BEAST_273:EYE -"CREATURE:FORGOTTEN_BEAST_273:BRAIN -!CREATURE:FORGOTTEN_BEAST_273:LUNG -"CREATURE:FORGOTTEN_BEAST_273:HEART -"CREATURE:FORGOTTEN_BEAST_273:LIVER - CREATURE:FORGOTTEN_BEAST_273:GUT -$CREATURE:FORGOTTEN_BEAST_273:STOMACH -$CREATURE:FORGOTTEN_BEAST_273:GIZZARD -%CREATURE:FORGOTTEN_BEAST_273:PANCREAS -#CREATURE:FORGOTTEN_BEAST_273:SPLEEN -#CREATURE:FORGOTTEN_BEAST_273:KIDNEY -#CREATURE:FORGOTTEN_BEAST_274:MUSCLE - CREATURE:FORGOTTEN_BEAST_274:EYE -"CREATURE:FORGOTTEN_BEAST_274:BRAIN -!CREATURE:FORGOTTEN_BEAST_274:LUNG -"CREATURE:FORGOTTEN_BEAST_274:HEART -"CREATURE:FORGOTTEN_BEAST_274:LIVER - CREATURE:FORGOTTEN_BEAST_274:GUT -$CREATURE:FORGOTTEN_BEAST_274:STOMACH -$CREATURE:FORGOTTEN_BEAST_274:GIZZARD -%CREATURE:FORGOTTEN_BEAST_274:PANCREAS -#CREATURE:FORGOTTEN_BEAST_274:SPLEEN -#CREATURE:FORGOTTEN_BEAST_274:KIDNEY -#CREATURE:FORGOTTEN_BEAST_275:MUSCLE - CREATURE:FORGOTTEN_BEAST_275:EYE -"CREATURE:FORGOTTEN_BEAST_275:BRAIN -!CREATURE:FORGOTTEN_BEAST_275:LUNG -"CREATURE:FORGOTTEN_BEAST_275:HEART -"CREATURE:FORGOTTEN_BEAST_275:LIVER - CREATURE:FORGOTTEN_BEAST_275:GUT -$CREATURE:FORGOTTEN_BEAST_275:STOMACH -$CREATURE:FORGOTTEN_BEAST_275:GIZZARD -%CREATURE:FORGOTTEN_BEAST_275:PANCREAS -#CREATURE:FORGOTTEN_BEAST_275:SPLEEN -#CREATURE:FORGOTTEN_BEAST_275:KIDNEY -#CREATURE:FORGOTTEN_BEAST_276:MUSCLE - CREATURE:FORGOTTEN_BEAST_276:EYE -"CREATURE:FORGOTTEN_BEAST_276:BRAIN -!CREATURE:FORGOTTEN_BEAST_276:LUNG -"CREATURE:FORGOTTEN_BEAST_276:HEART -"CREATURE:FORGOTTEN_BEAST_276:LIVER - CREATURE:FORGOTTEN_BEAST_276:GUT -$CREATURE:FORGOTTEN_BEAST_276:STOMACH -$CREATURE:FORGOTTEN_BEAST_276:GIZZARD -%CREATURE:FORGOTTEN_BEAST_276:PANCREAS -#CREATURE:FORGOTTEN_BEAST_276:SPLEEN -#CREATURE:FORGOTTEN_BEAST_276:KIDNEY -#CREATURE:FORGOTTEN_BEAST_277:MUSCLE - CREATURE:FORGOTTEN_BEAST_277:EYE -"CREATURE:FORGOTTEN_BEAST_277:BRAIN -!CREATURE:FORGOTTEN_BEAST_277:LUNG -"CREATURE:FORGOTTEN_BEAST_277:HEART -"CREATURE:FORGOTTEN_BEAST_277:LIVER - CREATURE:FORGOTTEN_BEAST_277:GUT -$CREATURE:FORGOTTEN_BEAST_277:STOMACH -$CREATURE:FORGOTTEN_BEAST_277:GIZZARD -%CREATURE:FORGOTTEN_BEAST_277:PANCREAS -#CREATURE:FORGOTTEN_BEAST_277:SPLEEN -#CREATURE:FORGOTTEN_BEAST_277:KIDNEY -#CREATURE:FORGOTTEN_BEAST_279:MUSCLE - CREATURE:FORGOTTEN_BEAST_279:EYE -"CREATURE:FORGOTTEN_BEAST_279:BRAIN -!CREATURE:FORGOTTEN_BEAST_279:LUNG -"CREATURE:FORGOTTEN_BEAST_279:HEART -"CREATURE:FORGOTTEN_BEAST_279:LIVER - CREATURE:FORGOTTEN_BEAST_279:GUT -$CREATURE:FORGOTTEN_BEAST_279:STOMACH -$CREATURE:FORGOTTEN_BEAST_279:GIZZARD -%CREATURE:FORGOTTEN_BEAST_279:PANCREAS -#CREATURE:FORGOTTEN_BEAST_279:SPLEEN -#CREATURE:FORGOTTEN_BEAST_279:KIDNEY -#CREATURE:FORGOTTEN_BEAST_280:MUSCLE - CREATURE:FORGOTTEN_BEAST_280:EYE -"CREATURE:FORGOTTEN_BEAST_280:BRAIN -!CREATURE:FORGOTTEN_BEAST_280:LUNG -"CREATURE:FORGOTTEN_BEAST_280:HEART -"CREATURE:FORGOTTEN_BEAST_280:LIVER - CREATURE:FORGOTTEN_BEAST_280:GUT -$CREATURE:FORGOTTEN_BEAST_280:STOMACH -$CREATURE:FORGOTTEN_BEAST_280:GIZZARD -%CREATURE:FORGOTTEN_BEAST_280:PANCREAS -#CREATURE:FORGOTTEN_BEAST_280:SPLEEN -#CREATURE:FORGOTTEN_BEAST_280:KIDNEY -#CREATURE:FORGOTTEN_BEAST_281:MUSCLE - CREATURE:FORGOTTEN_BEAST_281:EYE -"CREATURE:FORGOTTEN_BEAST_281:BRAIN -!CREATURE:FORGOTTEN_BEAST_281:LUNG -"CREATURE:FORGOTTEN_BEAST_281:HEART -"CREATURE:FORGOTTEN_BEAST_281:LIVER - CREATURE:FORGOTTEN_BEAST_281:GUT -$CREATURE:FORGOTTEN_BEAST_281:STOMACH -$CREATURE:FORGOTTEN_BEAST_281:GIZZARD -%CREATURE:FORGOTTEN_BEAST_281:PANCREAS -#CREATURE:FORGOTTEN_BEAST_281:SPLEEN -#CREATURE:FORGOTTEN_BEAST_281:KIDNEY -#CREATURE:FORGOTTEN_BEAST_282:MUSCLE - CREATURE:FORGOTTEN_BEAST_282:EYE -"CREATURE:FORGOTTEN_BEAST_282:BRAIN -!CREATURE:FORGOTTEN_BEAST_282:LUNG -"CREATURE:FORGOTTEN_BEAST_282:HEART -"CREATURE:FORGOTTEN_BEAST_282:LIVER - CREATURE:FORGOTTEN_BEAST_282:GUT -$CREATURE:FORGOTTEN_BEAST_282:STOMACH -$CREATURE:FORGOTTEN_BEAST_282:GIZZARD -%CREATURE:FORGOTTEN_BEAST_282:PANCREAS -#CREATURE:FORGOTTEN_BEAST_282:SPLEEN -#CREATURE:FORGOTTEN_BEAST_282:KIDNEY -#CREATURE:FORGOTTEN_BEAST_283:MUSCLE - CREATURE:FORGOTTEN_BEAST_283:EYE -"CREATURE:FORGOTTEN_BEAST_283:BRAIN -!CREATURE:FORGOTTEN_BEAST_283:LUNG -"CREATURE:FORGOTTEN_BEAST_283:HEART -"CREATURE:FORGOTTEN_BEAST_283:LIVER - CREATURE:FORGOTTEN_BEAST_283:GUT -$CREATURE:FORGOTTEN_BEAST_283:STOMACH -$CREATURE:FORGOTTEN_BEAST_283:GIZZARD -%CREATURE:FORGOTTEN_BEAST_283:PANCREAS -#CREATURE:FORGOTTEN_BEAST_283:SPLEEN -#CREATURE:FORGOTTEN_BEAST_283:KIDNEY -#CREATURE:FORGOTTEN_BEAST_285:MUSCLE - CREATURE:FORGOTTEN_BEAST_285:EYE -"CREATURE:FORGOTTEN_BEAST_285:BRAIN -!CREATURE:FORGOTTEN_BEAST_285:LUNG -"CREATURE:FORGOTTEN_BEAST_285:HEART -"CREATURE:FORGOTTEN_BEAST_285:LIVER - CREATURE:FORGOTTEN_BEAST_285:GUT -$CREATURE:FORGOTTEN_BEAST_285:STOMACH -$CREATURE:FORGOTTEN_BEAST_285:GIZZARD -%CREATURE:FORGOTTEN_BEAST_285:PANCREAS -#CREATURE:FORGOTTEN_BEAST_285:SPLEEN -#CREATURE:FORGOTTEN_BEAST_285:KIDNEY -#CREATURE:FORGOTTEN_BEAST_286:MUSCLE - CREATURE:FORGOTTEN_BEAST_286:EYE -"CREATURE:FORGOTTEN_BEAST_286:BRAIN -!CREATURE:FORGOTTEN_BEAST_286:LUNG -"CREATURE:FORGOTTEN_BEAST_286:HEART -"CREATURE:FORGOTTEN_BEAST_286:LIVER - CREATURE:FORGOTTEN_BEAST_286:GUT -$CREATURE:FORGOTTEN_BEAST_286:STOMACH -$CREATURE:FORGOTTEN_BEAST_286:GIZZARD -%CREATURE:FORGOTTEN_BEAST_286:PANCREAS -#CREATURE:FORGOTTEN_BEAST_286:SPLEEN -#CREATURE:FORGOTTEN_BEAST_286:KIDNEY -#CREATURE:FORGOTTEN_BEAST_287:MUSCLE - CREATURE:FORGOTTEN_BEAST_287:EYE -"CREATURE:FORGOTTEN_BEAST_287:BRAIN -!CREATURE:FORGOTTEN_BEAST_287:LUNG -"CREATURE:FORGOTTEN_BEAST_287:HEART -"CREATURE:FORGOTTEN_BEAST_287:LIVER - CREATURE:FORGOTTEN_BEAST_287:GUT -$CREATURE:FORGOTTEN_BEAST_287:STOMACH -$CREATURE:FORGOTTEN_BEAST_287:GIZZARD -%CREATURE:FORGOTTEN_BEAST_287:PANCREAS -#CREATURE:FORGOTTEN_BEAST_287:SPLEEN -#CREATURE:FORGOTTEN_BEAST_287:KIDNEY -#CREATURE:FORGOTTEN_BEAST_288:MUSCLE - CREATURE:FORGOTTEN_BEAST_288:EYE -"CREATURE:FORGOTTEN_BEAST_288:BRAIN -!CREATURE:FORGOTTEN_BEAST_288:LUNG -"CREATURE:FORGOTTEN_BEAST_288:HEART -"CREATURE:FORGOTTEN_BEAST_288:LIVER - CREATURE:FORGOTTEN_BEAST_288:GUT -$CREATURE:FORGOTTEN_BEAST_288:STOMACH -$CREATURE:FORGOTTEN_BEAST_288:GIZZARD -%CREATURE:FORGOTTEN_BEAST_288:PANCREAS -#CREATURE:FORGOTTEN_BEAST_288:SPLEEN -#CREATURE:FORGOTTEN_BEAST_288:KIDNEY -#CREATURE:FORGOTTEN_BEAST_289:MUSCLE - CREATURE:FORGOTTEN_BEAST_289:EYE -"CREATURE:FORGOTTEN_BEAST_289:BRAIN -!CREATURE:FORGOTTEN_BEAST_289:LUNG -"CREATURE:FORGOTTEN_BEAST_289:HEART -"CREATURE:FORGOTTEN_BEAST_289:LIVER - CREATURE:FORGOTTEN_BEAST_289:GUT -$CREATURE:FORGOTTEN_BEAST_289:STOMACH -$CREATURE:FORGOTTEN_BEAST_289:GIZZARD -%CREATURE:FORGOTTEN_BEAST_289:PANCREAS -#CREATURE:FORGOTTEN_BEAST_289:SPLEEN -#CREATURE:FORGOTTEN_BEAST_289:KIDNEY -#CREATURE:FORGOTTEN_BEAST_290:MUSCLE - CREATURE:FORGOTTEN_BEAST_290:EYE -"CREATURE:FORGOTTEN_BEAST_290:BRAIN -!CREATURE:FORGOTTEN_BEAST_290:LUNG -"CREATURE:FORGOTTEN_BEAST_290:HEART -"CREATURE:FORGOTTEN_BEAST_290:LIVER - CREATURE:FORGOTTEN_BEAST_290:GUT -$CREATURE:FORGOTTEN_BEAST_290:STOMACH -$CREATURE:FORGOTTEN_BEAST_290:GIZZARD -%CREATURE:FORGOTTEN_BEAST_290:PANCREAS -#CREATURE:FORGOTTEN_BEAST_290:SPLEEN -#CREATURE:FORGOTTEN_BEAST_290:KIDNEY -#CREATURE:FORGOTTEN_BEAST_293:MUSCLE - CREATURE:FORGOTTEN_BEAST_293:EYE -"CREATURE:FORGOTTEN_BEAST_293:BRAIN -!CREATURE:FORGOTTEN_BEAST_293:LUNG -"CREATURE:FORGOTTEN_BEAST_293:HEART -"CREATURE:FORGOTTEN_BEAST_293:LIVER - CREATURE:FORGOTTEN_BEAST_293:GUT -$CREATURE:FORGOTTEN_BEAST_293:STOMACH -$CREATURE:FORGOTTEN_BEAST_293:GIZZARD -%CREATURE:FORGOTTEN_BEAST_293:PANCREAS -#CREATURE:FORGOTTEN_BEAST_293:SPLEEN -#CREATURE:FORGOTTEN_BEAST_293:KIDNEY -#CREATURE:FORGOTTEN_BEAST_295:MUSCLE - CREATURE:FORGOTTEN_BEAST_295:EYE -"CREATURE:FORGOTTEN_BEAST_295:BRAIN -!CREATURE:FORGOTTEN_BEAST_295:LUNG -"CREATURE:FORGOTTEN_BEAST_295:HEART -"CREATURE:FORGOTTEN_BEAST_295:LIVER - CREATURE:FORGOTTEN_BEAST_295:GUT -$CREATURE:FORGOTTEN_BEAST_295:STOMACH -$CREATURE:FORGOTTEN_BEAST_295:GIZZARD -%CREATURE:FORGOTTEN_BEAST_295:PANCREAS -#CREATURE:FORGOTTEN_BEAST_295:SPLEEN -#CREATURE:FORGOTTEN_BEAST_295:KIDNEY -#CREATURE:FORGOTTEN_BEAST_297:MUSCLE - CREATURE:FORGOTTEN_BEAST_297:EYE -"CREATURE:FORGOTTEN_BEAST_297:BRAIN -!CREATURE:FORGOTTEN_BEAST_297:LUNG -"CREATURE:FORGOTTEN_BEAST_297:HEART -"CREATURE:FORGOTTEN_BEAST_297:LIVER - CREATURE:FORGOTTEN_BEAST_297:GUT -$CREATURE:FORGOTTEN_BEAST_297:STOMACH -$CREATURE:FORGOTTEN_BEAST_297:GIZZARD -%CREATURE:FORGOTTEN_BEAST_297:PANCREAS -#CREATURE:FORGOTTEN_BEAST_297:SPLEEN -#CREATURE:FORGOTTEN_BEAST_297:KIDNEY -#CREATURE:FORGOTTEN_BEAST_300:MUSCLE - CREATURE:FORGOTTEN_BEAST_300:EYE -"CREATURE:FORGOTTEN_BEAST_300:BRAIN -!CREATURE:FORGOTTEN_BEAST_300:LUNG -"CREATURE:FORGOTTEN_BEAST_300:HEART -"CREATURE:FORGOTTEN_BEAST_300:LIVER - CREATURE:FORGOTTEN_BEAST_300:GUT -$CREATURE:FORGOTTEN_BEAST_300:STOMACH -$CREATURE:FORGOTTEN_BEAST_300:GIZZARD -%CREATURE:FORGOTTEN_BEAST_300:PANCREAS -#CREATURE:FORGOTTEN_BEAST_300:SPLEEN -#CREATURE:FORGOTTEN_BEAST_300:KIDNEY -#CREATURE:FORGOTTEN_BEAST_302:MUSCLE - CREATURE:FORGOTTEN_BEAST_302:EYE -"CREATURE:FORGOTTEN_BEAST_302:BRAIN -!CREATURE:FORGOTTEN_BEAST_302:LUNG -"CREATURE:FORGOTTEN_BEAST_302:HEART -"CREATURE:FORGOTTEN_BEAST_302:LIVER - CREATURE:FORGOTTEN_BEAST_302:GUT -$CREATURE:FORGOTTEN_BEAST_302:STOMACH -$CREATURE:FORGOTTEN_BEAST_302:GIZZARD -%CREATURE:FORGOTTEN_BEAST_302:PANCREAS -#CREATURE:FORGOTTEN_BEAST_302:SPLEEN -#CREATURE:FORGOTTEN_BEAST_302:KIDNEY -#CREATURE:FORGOTTEN_BEAST_306:MUSCLE - CREATURE:FORGOTTEN_BEAST_306:EYE -"CREATURE:FORGOTTEN_BEAST_306:BRAIN -!CREATURE:FORGOTTEN_BEAST_306:LUNG -"CREATURE:FORGOTTEN_BEAST_306:HEART -"CREATURE:FORGOTTEN_BEAST_306:LIVER - CREATURE:FORGOTTEN_BEAST_306:GUT -$CREATURE:FORGOTTEN_BEAST_306:STOMACH -$CREATURE:FORGOTTEN_BEAST_306:GIZZARD -%CREATURE:FORGOTTEN_BEAST_306:PANCREAS -#CREATURE:FORGOTTEN_BEAST_306:SPLEEN -#CREATURE:FORGOTTEN_BEAST_306:KIDNEY -#CREATURE:FORGOTTEN_BEAST_307:MUSCLE - CREATURE:FORGOTTEN_BEAST_307:EYE -"CREATURE:FORGOTTEN_BEAST_307:BRAIN -!CREATURE:FORGOTTEN_BEAST_307:LUNG -"CREATURE:FORGOTTEN_BEAST_307:HEART -"CREATURE:FORGOTTEN_BEAST_307:LIVER - CREATURE:FORGOTTEN_BEAST_307:GUT -$CREATURE:FORGOTTEN_BEAST_307:STOMACH -$CREATURE:FORGOTTEN_BEAST_307:GIZZARD -%CREATURE:FORGOTTEN_BEAST_307:PANCREAS -#CREATURE:FORGOTTEN_BEAST_307:SPLEEN -#CREATURE:FORGOTTEN_BEAST_307:KIDNEY -#CREATURE:FORGOTTEN_BEAST_310:MUSCLE - CREATURE:FORGOTTEN_BEAST_310:EYE -"CREATURE:FORGOTTEN_BEAST_310:BRAIN -!CREATURE:FORGOTTEN_BEAST_310:LUNG -"CREATURE:FORGOTTEN_BEAST_310:HEART -"CREATURE:FORGOTTEN_BEAST_310:LIVER - CREATURE:FORGOTTEN_BEAST_310:GUT -$CREATURE:FORGOTTEN_BEAST_310:STOMACH -$CREATURE:FORGOTTEN_BEAST_310:GIZZARD -%CREATURE:FORGOTTEN_BEAST_310:PANCREAS -#CREATURE:FORGOTTEN_BEAST_310:SPLEEN -#CREATURE:FORGOTTEN_BEAST_310:KIDNEY -#CREATURE:FORGOTTEN_BEAST_311:MUSCLE - CREATURE:FORGOTTEN_BEAST_311:EYE -"CREATURE:FORGOTTEN_BEAST_311:BRAIN -!CREATURE:FORGOTTEN_BEAST_311:LUNG -"CREATURE:FORGOTTEN_BEAST_311:HEART -"CREATURE:FORGOTTEN_BEAST_311:LIVER - CREATURE:FORGOTTEN_BEAST_311:GUT -$CREATURE:FORGOTTEN_BEAST_311:STOMACH -$CREATURE:FORGOTTEN_BEAST_311:GIZZARD -%CREATURE:FORGOTTEN_BEAST_311:PANCREAS -#CREATURE:FORGOTTEN_BEAST_311:SPLEEN -#CREATURE:FORGOTTEN_BEAST_311:KIDNEY -#CREATURE:FORGOTTEN_BEAST_312:MUSCLE - CREATURE:FORGOTTEN_BEAST_312:EYE -"CREATURE:FORGOTTEN_BEAST_312:BRAIN -!CREATURE:FORGOTTEN_BEAST_312:LUNG -"CREATURE:FORGOTTEN_BEAST_312:HEART -"CREATURE:FORGOTTEN_BEAST_312:LIVER - CREATURE:FORGOTTEN_BEAST_312:GUT -$CREATURE:FORGOTTEN_BEAST_312:STOMACH -$CREATURE:FORGOTTEN_BEAST_312:GIZZARD -%CREATURE:FORGOTTEN_BEAST_312:PANCREAS -#CREATURE:FORGOTTEN_BEAST_312:SPLEEN -#CREATURE:FORGOTTEN_BEAST_312:KIDNEY -#CREATURE:FORGOTTEN_BEAST_314:MUSCLE - CREATURE:FORGOTTEN_BEAST_314:EYE -"CREATURE:FORGOTTEN_BEAST_314:BRAIN -!CREATURE:FORGOTTEN_BEAST_314:LUNG -"CREATURE:FORGOTTEN_BEAST_314:HEART -"CREATURE:FORGOTTEN_BEAST_314:LIVER - CREATURE:FORGOTTEN_BEAST_314:GUT -$CREATURE:FORGOTTEN_BEAST_314:STOMACH -$CREATURE:FORGOTTEN_BEAST_314:GIZZARD -%CREATURE:FORGOTTEN_BEAST_314:PANCREAS -#CREATURE:FORGOTTEN_BEAST_314:SPLEEN -#CREATURE:FORGOTTEN_BEAST_314:KIDNEY -#CREATURE:FORGOTTEN_BEAST_316:MUSCLE - CREATURE:FORGOTTEN_BEAST_316:EYE -"CREATURE:FORGOTTEN_BEAST_316:BRAIN -!CREATURE:FORGOTTEN_BEAST_316:LUNG -"CREATURE:FORGOTTEN_BEAST_316:HEART -"CREATURE:FORGOTTEN_BEAST_316:LIVER - CREATURE:FORGOTTEN_BEAST_316:GUT -$CREATURE:FORGOTTEN_BEAST_316:STOMACH -$CREATURE:FORGOTTEN_BEAST_316:GIZZARD -%CREATURE:FORGOTTEN_BEAST_316:PANCREAS -#CREATURE:FORGOTTEN_BEAST_316:SPLEEN -#CREATURE:FORGOTTEN_BEAST_316:KIDNEY -#CREATURE:FORGOTTEN_BEAST_317:MUSCLE - CREATURE:FORGOTTEN_BEAST_317:EYE -"CREATURE:FORGOTTEN_BEAST_317:BRAIN -!CREATURE:FORGOTTEN_BEAST_317:LUNG -"CREATURE:FORGOTTEN_BEAST_317:HEART -"CREATURE:FORGOTTEN_BEAST_317:LIVER - CREATURE:FORGOTTEN_BEAST_317:GUT -$CREATURE:FORGOTTEN_BEAST_317:STOMACH -$CREATURE:FORGOTTEN_BEAST_317:GIZZARD -%CREATURE:FORGOTTEN_BEAST_317:PANCREAS -#CREATURE:FORGOTTEN_BEAST_317:SPLEEN -#CREATURE:FORGOTTEN_BEAST_317:KIDNEY -#CREATURE:FORGOTTEN_BEAST_318:MUSCLE - CREATURE:FORGOTTEN_BEAST_318:EYE -"CREATURE:FORGOTTEN_BEAST_318:BRAIN -!CREATURE:FORGOTTEN_BEAST_318:LUNG -"CREATURE:FORGOTTEN_BEAST_318:HEART -"CREATURE:FORGOTTEN_BEAST_318:LIVER - CREATURE:FORGOTTEN_BEAST_318:GUT -$CREATURE:FORGOTTEN_BEAST_318:STOMACH -$CREATURE:FORGOTTEN_BEAST_318:GIZZARD -%CREATURE:FORGOTTEN_BEAST_318:PANCREAS -#CREATURE:FORGOTTEN_BEAST_318:SPLEEN -#CREATURE:FORGOTTEN_BEAST_318:KIDNEY -#CREATURE:FORGOTTEN_BEAST_320:MUSCLE - CREATURE:FORGOTTEN_BEAST_320:EYE -"CREATURE:FORGOTTEN_BEAST_320:BRAIN -!CREATURE:FORGOTTEN_BEAST_320:LUNG -"CREATURE:FORGOTTEN_BEAST_320:HEART -"CREATURE:FORGOTTEN_BEAST_320:LIVER - CREATURE:FORGOTTEN_BEAST_320:GUT -$CREATURE:FORGOTTEN_BEAST_320:STOMACH -$CREATURE:FORGOTTEN_BEAST_320:GIZZARD -%CREATURE:FORGOTTEN_BEAST_320:PANCREAS -#CREATURE:FORGOTTEN_BEAST_320:SPLEEN -#CREATURE:FORGOTTEN_BEAST_320:KIDNEY -#CREATURE:FORGOTTEN_BEAST_321:MUSCLE - CREATURE:FORGOTTEN_BEAST_321:EYE -"CREATURE:FORGOTTEN_BEAST_321:BRAIN -!CREATURE:FORGOTTEN_BEAST_321:LUNG -"CREATURE:FORGOTTEN_BEAST_321:HEART -"CREATURE:FORGOTTEN_BEAST_321:LIVER - CREATURE:FORGOTTEN_BEAST_321:GUT -$CREATURE:FORGOTTEN_BEAST_321:STOMACH -$CREATURE:FORGOTTEN_BEAST_321:GIZZARD -%CREATURE:FORGOTTEN_BEAST_321:PANCREAS -#CREATURE:FORGOTTEN_BEAST_321:SPLEEN -#CREATURE:FORGOTTEN_BEAST_321:KIDNEY -#CREATURE:FORGOTTEN_BEAST_322:MUSCLE - CREATURE:FORGOTTEN_BEAST_322:EYE -"CREATURE:FORGOTTEN_BEAST_322:BRAIN -!CREATURE:FORGOTTEN_BEAST_322:LUNG -"CREATURE:FORGOTTEN_BEAST_322:HEART -"CREATURE:FORGOTTEN_BEAST_322:LIVER - CREATURE:FORGOTTEN_BEAST_322:GUT -$CREATURE:FORGOTTEN_BEAST_322:STOMACH -$CREATURE:FORGOTTEN_BEAST_322:GIZZARD -%CREATURE:FORGOTTEN_BEAST_322:PANCREAS -#CREATURE:FORGOTTEN_BEAST_322:SPLEEN -#CREATURE:FORGOTTEN_BEAST_322:KIDNEY -#CREATURE:FORGOTTEN_BEAST_323:MUSCLE - CREATURE:FORGOTTEN_BEAST_323:EYE -"CREATURE:FORGOTTEN_BEAST_323:BRAIN -!CREATURE:FORGOTTEN_BEAST_323:LUNG -"CREATURE:FORGOTTEN_BEAST_323:HEART -"CREATURE:FORGOTTEN_BEAST_323:LIVER - CREATURE:FORGOTTEN_BEAST_323:GUT -$CREATURE:FORGOTTEN_BEAST_323:STOMACH -$CREATURE:FORGOTTEN_BEAST_323:GIZZARD -%CREATURE:FORGOTTEN_BEAST_323:PANCREAS -#CREATURE:FORGOTTEN_BEAST_323:SPLEEN -#CREATURE:FORGOTTEN_BEAST_323:KIDNEY -#CREATURE:FORGOTTEN_BEAST_324:MUSCLE - CREATURE:FORGOTTEN_BEAST_324:EYE -"CREATURE:FORGOTTEN_BEAST_324:BRAIN -!CREATURE:FORGOTTEN_BEAST_324:LUNG -"CREATURE:FORGOTTEN_BEAST_324:HEART -"CREATURE:FORGOTTEN_BEAST_324:LIVER - CREATURE:FORGOTTEN_BEAST_324:GUT -$CREATURE:FORGOTTEN_BEAST_324:STOMACH -$CREATURE:FORGOTTEN_BEAST_324:GIZZARD -%CREATURE:FORGOTTEN_BEAST_324:PANCREAS -#CREATURE:FORGOTTEN_BEAST_324:SPLEEN -#CREATURE:FORGOTTEN_BEAST_324:KIDNEY -#CREATURE:FORGOTTEN_BEAST_325:MUSCLE - CREATURE:FORGOTTEN_BEAST_325:EYE -"CREATURE:FORGOTTEN_BEAST_325:BRAIN -!CREATURE:FORGOTTEN_BEAST_325:LUNG -"CREATURE:FORGOTTEN_BEAST_325:HEART -"CREATURE:FORGOTTEN_BEAST_325:LIVER - CREATURE:FORGOTTEN_BEAST_325:GUT -$CREATURE:FORGOTTEN_BEAST_325:STOMACH -$CREATURE:FORGOTTEN_BEAST_325:GIZZARD -%CREATURE:FORGOTTEN_BEAST_325:PANCREAS -#CREATURE:FORGOTTEN_BEAST_325:SPLEEN -#CREATURE:FORGOTTEN_BEAST_325:KIDNEY -#CREATURE:FORGOTTEN_BEAST_326:MUSCLE - CREATURE:FORGOTTEN_BEAST_326:EYE -"CREATURE:FORGOTTEN_BEAST_326:BRAIN -!CREATURE:FORGOTTEN_BEAST_326:LUNG -"CREATURE:FORGOTTEN_BEAST_326:HEART -"CREATURE:FORGOTTEN_BEAST_326:LIVER - CREATURE:FORGOTTEN_BEAST_326:GUT -$CREATURE:FORGOTTEN_BEAST_326:STOMACH -$CREATURE:FORGOTTEN_BEAST_326:GIZZARD -%CREATURE:FORGOTTEN_BEAST_326:PANCREAS -#CREATURE:FORGOTTEN_BEAST_326:SPLEEN -#CREATURE:FORGOTTEN_BEAST_326:KIDNEY -#CREATURE:FORGOTTEN_BEAST_327:MUSCLE - CREATURE:FORGOTTEN_BEAST_327:EYE -"CREATURE:FORGOTTEN_BEAST_327:BRAIN -!CREATURE:FORGOTTEN_BEAST_327:LUNG -"CREATURE:FORGOTTEN_BEAST_327:HEART -"CREATURE:FORGOTTEN_BEAST_327:LIVER - CREATURE:FORGOTTEN_BEAST_327:GUT -$CREATURE:FORGOTTEN_BEAST_327:STOMACH -$CREATURE:FORGOTTEN_BEAST_327:GIZZARD -%CREATURE:FORGOTTEN_BEAST_327:PANCREAS -#CREATURE:FORGOTTEN_BEAST_327:SPLEEN -#CREATURE:FORGOTTEN_BEAST_327:KIDNEY -#CREATURE:FORGOTTEN_BEAST_328:MUSCLE - CREATURE:FORGOTTEN_BEAST_328:EYE -"CREATURE:FORGOTTEN_BEAST_328:BRAIN -!CREATURE:FORGOTTEN_BEAST_328:LUNG -"CREATURE:FORGOTTEN_BEAST_328:HEART -"CREATURE:FORGOTTEN_BEAST_328:LIVER - CREATURE:FORGOTTEN_BEAST_328:GUT -$CREATURE:FORGOTTEN_BEAST_328:STOMACH -$CREATURE:FORGOTTEN_BEAST_328:GIZZARD -%CREATURE:FORGOTTEN_BEAST_328:PANCREAS -#CREATURE:FORGOTTEN_BEAST_328:SPLEEN -#CREATURE:FORGOTTEN_BEAST_328:KIDNEY -#CREATURE:FORGOTTEN_BEAST_329:MUSCLE - CREATURE:FORGOTTEN_BEAST_329:EYE -"CREATURE:FORGOTTEN_BEAST_329:BRAIN -!CREATURE:FORGOTTEN_BEAST_329:LUNG -"CREATURE:FORGOTTEN_BEAST_329:HEART -"CREATURE:FORGOTTEN_BEAST_329:LIVER - CREATURE:FORGOTTEN_BEAST_329:GUT -$CREATURE:FORGOTTEN_BEAST_329:STOMACH -$CREATURE:FORGOTTEN_BEAST_329:GIZZARD -%CREATURE:FORGOTTEN_BEAST_329:PANCREAS -#CREATURE:FORGOTTEN_BEAST_329:SPLEEN -#CREATURE:FORGOTTEN_BEAST_329:KIDNEY -#CREATURE:FORGOTTEN_BEAST_330:MUSCLE - CREATURE:FORGOTTEN_BEAST_330:EYE -"CREATURE:FORGOTTEN_BEAST_330:BRAIN -!CREATURE:FORGOTTEN_BEAST_330:LUNG -"CREATURE:FORGOTTEN_BEAST_330:HEART -"CREATURE:FORGOTTEN_BEAST_330:LIVER - CREATURE:FORGOTTEN_BEAST_330:GUT -$CREATURE:FORGOTTEN_BEAST_330:STOMACH -$CREATURE:FORGOTTEN_BEAST_330:GIZZARD -%CREATURE:FORGOTTEN_BEAST_330:PANCREAS -#CREATURE:FORGOTTEN_BEAST_330:SPLEEN -#CREATURE:FORGOTTEN_BEAST_330:KIDNEY -#CREATURE:FORGOTTEN_BEAST_332:MUSCLE - CREATURE:FORGOTTEN_BEAST_332:EYE -"CREATURE:FORGOTTEN_BEAST_332:BRAIN -!CREATURE:FORGOTTEN_BEAST_332:LUNG -"CREATURE:FORGOTTEN_BEAST_332:HEART -"CREATURE:FORGOTTEN_BEAST_332:LIVER - CREATURE:FORGOTTEN_BEAST_332:GUT -$CREATURE:FORGOTTEN_BEAST_332:STOMACH -$CREATURE:FORGOTTEN_BEAST_332:GIZZARD -%CREATURE:FORGOTTEN_BEAST_332:PANCREAS -#CREATURE:FORGOTTEN_BEAST_332:SPLEEN -#CREATURE:FORGOTTEN_BEAST_332:KIDNEY -#CREATURE:FORGOTTEN_BEAST_333:MUSCLE - CREATURE:FORGOTTEN_BEAST_333:EYE -"CREATURE:FORGOTTEN_BEAST_333:BRAIN -!CREATURE:FORGOTTEN_BEAST_333:LUNG -"CREATURE:FORGOTTEN_BEAST_333:HEART -"CREATURE:FORGOTTEN_BEAST_333:LIVER - CREATURE:FORGOTTEN_BEAST_333:GUT -$CREATURE:FORGOTTEN_BEAST_333:STOMACH -$CREATURE:FORGOTTEN_BEAST_333:GIZZARD -%CREATURE:FORGOTTEN_BEAST_333:PANCREAS -#CREATURE:FORGOTTEN_BEAST_333:SPLEEN -#CREATURE:FORGOTTEN_BEAST_333:KIDNEY -#CREATURE:FORGOTTEN_BEAST_335:MUSCLE - CREATURE:FORGOTTEN_BEAST_335:EYE -"CREATURE:FORGOTTEN_BEAST_335:BRAIN -!CREATURE:FORGOTTEN_BEAST_335:LUNG -"CREATURE:FORGOTTEN_BEAST_335:HEART -"CREATURE:FORGOTTEN_BEAST_335:LIVER - CREATURE:FORGOTTEN_BEAST_335:GUT -$CREATURE:FORGOTTEN_BEAST_335:STOMACH -$CREATURE:FORGOTTEN_BEAST_335:GIZZARD -%CREATURE:FORGOTTEN_BEAST_335:PANCREAS -#CREATURE:FORGOTTEN_BEAST_335:SPLEEN -#CREATURE:FORGOTTEN_BEAST_335:KIDNEY -#CREATURE:FORGOTTEN_BEAST_336:MUSCLE - CREATURE:FORGOTTEN_BEAST_336:EYE -"CREATURE:FORGOTTEN_BEAST_336:BRAIN -!CREATURE:FORGOTTEN_BEAST_336:LUNG -"CREATURE:FORGOTTEN_BEAST_336:HEART -"CREATURE:FORGOTTEN_BEAST_336:LIVER - CREATURE:FORGOTTEN_BEAST_336:GUT -$CREATURE:FORGOTTEN_BEAST_336:STOMACH -$CREATURE:FORGOTTEN_BEAST_336:GIZZARD -%CREATURE:FORGOTTEN_BEAST_336:PANCREAS -#CREATURE:FORGOTTEN_BEAST_336:SPLEEN -#CREATURE:FORGOTTEN_BEAST_336:KIDNEY -#CREATURE:FORGOTTEN_BEAST_337:MUSCLE - CREATURE:FORGOTTEN_BEAST_337:EYE -"CREATURE:FORGOTTEN_BEAST_337:BRAIN -!CREATURE:FORGOTTEN_BEAST_337:LUNG -"CREATURE:FORGOTTEN_BEAST_337:HEART -"CREATURE:FORGOTTEN_BEAST_337:LIVER - CREATURE:FORGOTTEN_BEAST_337:GUT -$CREATURE:FORGOTTEN_BEAST_337:STOMACH -$CREATURE:FORGOTTEN_BEAST_337:GIZZARD -%CREATURE:FORGOTTEN_BEAST_337:PANCREAS -#CREATURE:FORGOTTEN_BEAST_337:SPLEEN -#CREATURE:FORGOTTEN_BEAST_337:KIDNEY -#CREATURE:FORGOTTEN_BEAST_338:MUSCLE - CREATURE:FORGOTTEN_BEAST_338:EYE -"CREATURE:FORGOTTEN_BEAST_338:BRAIN -!CREATURE:FORGOTTEN_BEAST_338:LUNG -"CREATURE:FORGOTTEN_BEAST_338:HEART -"CREATURE:FORGOTTEN_BEAST_338:LIVER - CREATURE:FORGOTTEN_BEAST_338:GUT -$CREATURE:FORGOTTEN_BEAST_338:STOMACH -$CREATURE:FORGOTTEN_BEAST_338:GIZZARD -%CREATURE:FORGOTTEN_BEAST_338:PANCREAS -#CREATURE:FORGOTTEN_BEAST_338:SPLEEN -#CREATURE:FORGOTTEN_BEAST_338:KIDNEY -#CREATURE:FORGOTTEN_BEAST_339:MUSCLE - CREATURE:FORGOTTEN_BEAST_339:EYE -"CREATURE:FORGOTTEN_BEAST_339:BRAIN -!CREATURE:FORGOTTEN_BEAST_339:LUNG -"CREATURE:FORGOTTEN_BEAST_339:HEART -"CREATURE:FORGOTTEN_BEAST_339:LIVER - CREATURE:FORGOTTEN_BEAST_339:GUT -$CREATURE:FORGOTTEN_BEAST_339:STOMACH -$CREATURE:FORGOTTEN_BEAST_339:GIZZARD -%CREATURE:FORGOTTEN_BEAST_339:PANCREAS -#CREATURE:FORGOTTEN_BEAST_339:SPLEEN -#CREATURE:FORGOTTEN_BEAST_339:KIDNEY -#CREATURE:FORGOTTEN_BEAST_341:MUSCLE - CREATURE:FORGOTTEN_BEAST_341:EYE -"CREATURE:FORGOTTEN_BEAST_341:BRAIN -!CREATURE:FORGOTTEN_BEAST_341:LUNG -"CREATURE:FORGOTTEN_BEAST_341:HEART -"CREATURE:FORGOTTEN_BEAST_341:LIVER - CREATURE:FORGOTTEN_BEAST_341:GUT -$CREATURE:FORGOTTEN_BEAST_341:STOMACH -$CREATURE:FORGOTTEN_BEAST_341:GIZZARD -%CREATURE:FORGOTTEN_BEAST_341:PANCREAS -#CREATURE:FORGOTTEN_BEAST_341:SPLEEN -#CREATURE:FORGOTTEN_BEAST_341:KIDNEY -#CREATURE:FORGOTTEN_BEAST_342:MUSCLE - CREATURE:FORGOTTEN_BEAST_342:EYE -"CREATURE:FORGOTTEN_BEAST_342:BRAIN -!CREATURE:FORGOTTEN_BEAST_342:LUNG -"CREATURE:FORGOTTEN_BEAST_342:HEART -"CREATURE:FORGOTTEN_BEAST_342:LIVER - CREATURE:FORGOTTEN_BEAST_342:GUT -$CREATURE:FORGOTTEN_BEAST_342:STOMACH -$CREATURE:FORGOTTEN_BEAST_342:GIZZARD -%CREATURE:FORGOTTEN_BEAST_342:PANCREAS -#CREATURE:FORGOTTEN_BEAST_342:SPLEEN -#CREATURE:FORGOTTEN_BEAST_342:KIDNEY -#CREATURE:FORGOTTEN_BEAST_343:MUSCLE - CREATURE:FORGOTTEN_BEAST_343:EYE -"CREATURE:FORGOTTEN_BEAST_343:BRAIN -!CREATURE:FORGOTTEN_BEAST_343:LUNG -"CREATURE:FORGOTTEN_BEAST_343:HEART -"CREATURE:FORGOTTEN_BEAST_343:LIVER - CREATURE:FORGOTTEN_BEAST_343:GUT -$CREATURE:FORGOTTEN_BEAST_343:STOMACH -$CREATURE:FORGOTTEN_BEAST_343:GIZZARD -%CREATURE:FORGOTTEN_BEAST_343:PANCREAS -#CREATURE:FORGOTTEN_BEAST_343:SPLEEN -#CREATURE:FORGOTTEN_BEAST_343:KIDNEY -#CREATURE:FORGOTTEN_BEAST_344:MUSCLE - CREATURE:FORGOTTEN_BEAST_344:EYE -"CREATURE:FORGOTTEN_BEAST_344:BRAIN -!CREATURE:FORGOTTEN_BEAST_344:LUNG -"CREATURE:FORGOTTEN_BEAST_344:HEART -"CREATURE:FORGOTTEN_BEAST_344:LIVER - CREATURE:FORGOTTEN_BEAST_344:GUT -$CREATURE:FORGOTTEN_BEAST_344:STOMACH -$CREATURE:FORGOTTEN_BEAST_344:GIZZARD -%CREATURE:FORGOTTEN_BEAST_344:PANCREAS -#CREATURE:FORGOTTEN_BEAST_344:SPLEEN -#CREATURE:FORGOTTEN_BEAST_344:KIDNEY -#CREATURE:FORGOTTEN_BEAST_347:MUSCLE - CREATURE:FORGOTTEN_BEAST_347:EYE -"CREATURE:FORGOTTEN_BEAST_347:BRAIN -!CREATURE:FORGOTTEN_BEAST_347:LUNG -"CREATURE:FORGOTTEN_BEAST_347:HEART -"CREATURE:FORGOTTEN_BEAST_347:LIVER - CREATURE:FORGOTTEN_BEAST_347:GUT -$CREATURE:FORGOTTEN_BEAST_347:STOMACH -$CREATURE:FORGOTTEN_BEAST_347:GIZZARD -%CREATURE:FORGOTTEN_BEAST_347:PANCREAS -#CREATURE:FORGOTTEN_BEAST_347:SPLEEN -#CREATURE:FORGOTTEN_BEAST_347:KIDNEY -#CREATURE:FORGOTTEN_BEAST_348:MUSCLE - CREATURE:FORGOTTEN_BEAST_348:EYE -"CREATURE:FORGOTTEN_BEAST_348:BRAIN -!CREATURE:FORGOTTEN_BEAST_348:LUNG -"CREATURE:FORGOTTEN_BEAST_348:HEART -"CREATURE:FORGOTTEN_BEAST_348:LIVER - CREATURE:FORGOTTEN_BEAST_348:GUT -$CREATURE:FORGOTTEN_BEAST_348:STOMACH -$CREATURE:FORGOTTEN_BEAST_348:GIZZARD -%CREATURE:FORGOTTEN_BEAST_348:PANCREAS -#CREATURE:FORGOTTEN_BEAST_348:SPLEEN -#CREATURE:FORGOTTEN_BEAST_348:KIDNEY -#CREATURE:FORGOTTEN_BEAST_349:MUSCLE - CREATURE:FORGOTTEN_BEAST_349:EYE -"CREATURE:FORGOTTEN_BEAST_349:BRAIN -!CREATURE:FORGOTTEN_BEAST_349:LUNG -"CREATURE:FORGOTTEN_BEAST_349:HEART -"CREATURE:FORGOTTEN_BEAST_349:LIVER - CREATURE:FORGOTTEN_BEAST_349:GUT -$CREATURE:FORGOTTEN_BEAST_349:STOMACH -$CREATURE:FORGOTTEN_BEAST_349:GIZZARD -%CREATURE:FORGOTTEN_BEAST_349:PANCREAS -#CREATURE:FORGOTTEN_BEAST_349:SPLEEN -#CREATURE:FORGOTTEN_BEAST_349:KIDNEY -#CREATURE:FORGOTTEN_BEAST_351:MUSCLE - CREATURE:FORGOTTEN_BEAST_351:EYE -"CREATURE:FORGOTTEN_BEAST_351:BRAIN -!CREATURE:FORGOTTEN_BEAST_351:LUNG -"CREATURE:FORGOTTEN_BEAST_351:HEART -"CREATURE:FORGOTTEN_BEAST_351:LIVER - CREATURE:FORGOTTEN_BEAST_351:GUT -$CREATURE:FORGOTTEN_BEAST_351:STOMACH -$CREATURE:FORGOTTEN_BEAST_351:GIZZARD -%CREATURE:FORGOTTEN_BEAST_351:PANCREAS -#CREATURE:FORGOTTEN_BEAST_351:SPLEEN -#CREATURE:FORGOTTEN_BEAST_351:KIDNEY -#CREATURE:FORGOTTEN_BEAST_352:MUSCLE - CREATURE:FORGOTTEN_BEAST_352:EYE -"CREATURE:FORGOTTEN_BEAST_352:BRAIN -!CREATURE:FORGOTTEN_BEAST_352:LUNG -"CREATURE:FORGOTTEN_BEAST_352:HEART -"CREATURE:FORGOTTEN_BEAST_352:LIVER - CREATURE:FORGOTTEN_BEAST_352:GUT -$CREATURE:FORGOTTEN_BEAST_352:STOMACH -$CREATURE:FORGOTTEN_BEAST_352:GIZZARD -%CREATURE:FORGOTTEN_BEAST_352:PANCREAS -#CREATURE:FORGOTTEN_BEAST_352:SPLEEN -#CREATURE:FORGOTTEN_BEAST_352:KIDNEY -#CREATURE:FORGOTTEN_BEAST_353:MUSCLE - CREATURE:FORGOTTEN_BEAST_353:EYE -"CREATURE:FORGOTTEN_BEAST_353:BRAIN -!CREATURE:FORGOTTEN_BEAST_353:LUNG -"CREATURE:FORGOTTEN_BEAST_353:HEART -"CREATURE:FORGOTTEN_BEAST_353:LIVER - CREATURE:FORGOTTEN_BEAST_353:GUT -$CREATURE:FORGOTTEN_BEAST_353:STOMACH -$CREATURE:FORGOTTEN_BEAST_353:GIZZARD -%CREATURE:FORGOTTEN_BEAST_353:PANCREAS -#CREATURE:FORGOTTEN_BEAST_353:SPLEEN -#CREATURE:FORGOTTEN_BEAST_353:KIDNEY -#CREATURE:FORGOTTEN_BEAST_354:MUSCLE - CREATURE:FORGOTTEN_BEAST_354:EYE -"CREATURE:FORGOTTEN_BEAST_354:BRAIN -!CREATURE:FORGOTTEN_BEAST_354:LUNG -"CREATURE:FORGOTTEN_BEAST_354:HEART -"CREATURE:FORGOTTEN_BEAST_354:LIVER - CREATURE:FORGOTTEN_BEAST_354:GUT -$CREATURE:FORGOTTEN_BEAST_354:STOMACH -$CREATURE:FORGOTTEN_BEAST_354:GIZZARD -%CREATURE:FORGOTTEN_BEAST_354:PANCREAS -#CREATURE:FORGOTTEN_BEAST_354:SPLEEN -#CREATURE:FORGOTTEN_BEAST_354:KIDNEY -#CREATURE:FORGOTTEN_BEAST_355:MUSCLE - CREATURE:FORGOTTEN_BEAST_355:EYE -"CREATURE:FORGOTTEN_BEAST_355:BRAIN -!CREATURE:FORGOTTEN_BEAST_355:LUNG -"CREATURE:FORGOTTEN_BEAST_355:HEART -"CREATURE:FORGOTTEN_BEAST_355:LIVER - CREATURE:FORGOTTEN_BEAST_355:GUT -$CREATURE:FORGOTTEN_BEAST_355:STOMACH -$CREATURE:FORGOTTEN_BEAST_355:GIZZARD -%CREATURE:FORGOTTEN_BEAST_355:PANCREAS -#CREATURE:FORGOTTEN_BEAST_355:SPLEEN -#CREATURE:FORGOTTEN_BEAST_355:KIDNEY -#CREATURE:FORGOTTEN_BEAST_356:MUSCLE - CREATURE:FORGOTTEN_BEAST_356:EYE -"CREATURE:FORGOTTEN_BEAST_356:BRAIN -!CREATURE:FORGOTTEN_BEAST_356:LUNG -"CREATURE:FORGOTTEN_BEAST_356:HEART -"CREATURE:FORGOTTEN_BEAST_356:LIVER - CREATURE:FORGOTTEN_BEAST_356:GUT -$CREATURE:FORGOTTEN_BEAST_356:STOMACH -$CREATURE:FORGOTTEN_BEAST_356:GIZZARD -%CREATURE:FORGOTTEN_BEAST_356:PANCREAS -#CREATURE:FORGOTTEN_BEAST_356:SPLEEN -#CREATURE:FORGOTTEN_BEAST_356:KIDNEY -#CREATURE:FORGOTTEN_BEAST_357:MUSCLE - CREATURE:FORGOTTEN_BEAST_357:EYE -"CREATURE:FORGOTTEN_BEAST_357:BRAIN -!CREATURE:FORGOTTEN_BEAST_357:LUNG -"CREATURE:FORGOTTEN_BEAST_357:HEART -"CREATURE:FORGOTTEN_BEAST_357:LIVER - CREATURE:FORGOTTEN_BEAST_357:GUT -$CREATURE:FORGOTTEN_BEAST_357:STOMACH -$CREATURE:FORGOTTEN_BEAST_357:GIZZARD -%CREATURE:FORGOTTEN_BEAST_357:PANCREAS -#CREATURE:FORGOTTEN_BEAST_357:SPLEEN -#CREATURE:FORGOTTEN_BEAST_357:KIDNEY -#CREATURE:FORGOTTEN_BEAST_358:MUSCLE - CREATURE:FORGOTTEN_BEAST_358:EYE -"CREATURE:FORGOTTEN_BEAST_358:BRAIN -!CREATURE:FORGOTTEN_BEAST_358:LUNG -"CREATURE:FORGOTTEN_BEAST_358:HEART -"CREATURE:FORGOTTEN_BEAST_358:LIVER - CREATURE:FORGOTTEN_BEAST_358:GUT -$CREATURE:FORGOTTEN_BEAST_358:STOMACH -$CREATURE:FORGOTTEN_BEAST_358:GIZZARD -%CREATURE:FORGOTTEN_BEAST_358:PANCREAS -#CREATURE:FORGOTTEN_BEAST_358:SPLEEN -#CREATURE:FORGOTTEN_BEAST_358:KIDNEY -#CREATURE:FORGOTTEN_BEAST_359:MUSCLE - CREATURE:FORGOTTEN_BEAST_359:EYE -"CREATURE:FORGOTTEN_BEAST_359:BRAIN -!CREATURE:FORGOTTEN_BEAST_359:LUNG -"CREATURE:FORGOTTEN_BEAST_359:HEART -"CREATURE:FORGOTTEN_BEAST_359:LIVER - CREATURE:FORGOTTEN_BEAST_359:GUT -$CREATURE:FORGOTTEN_BEAST_359:STOMACH -$CREATURE:FORGOTTEN_BEAST_359:GIZZARD -%CREATURE:FORGOTTEN_BEAST_359:PANCREAS -#CREATURE:FORGOTTEN_BEAST_359:SPLEEN -#CREATURE:FORGOTTEN_BEAST_359:KIDNEY -#CREATURE:FORGOTTEN_BEAST_361:MUSCLE - CREATURE:FORGOTTEN_BEAST_361:EYE -"CREATURE:FORGOTTEN_BEAST_361:BRAIN -!CREATURE:FORGOTTEN_BEAST_361:LUNG -"CREATURE:FORGOTTEN_BEAST_361:HEART -"CREATURE:FORGOTTEN_BEAST_361:LIVER - CREATURE:FORGOTTEN_BEAST_361:GUT -$CREATURE:FORGOTTEN_BEAST_361:STOMACH -$CREATURE:FORGOTTEN_BEAST_361:GIZZARD -%CREATURE:FORGOTTEN_BEAST_361:PANCREAS -#CREATURE:FORGOTTEN_BEAST_361:SPLEEN -#CREATURE:FORGOTTEN_BEAST_361:KIDNEY -#CREATURE:FORGOTTEN_BEAST_363:MUSCLE - CREATURE:FORGOTTEN_BEAST_363:EYE -"CREATURE:FORGOTTEN_BEAST_363:BRAIN -!CREATURE:FORGOTTEN_BEAST_363:LUNG -"CREATURE:FORGOTTEN_BEAST_363:HEART -"CREATURE:FORGOTTEN_BEAST_363:LIVER - CREATURE:FORGOTTEN_BEAST_363:GUT -$CREATURE:FORGOTTEN_BEAST_363:STOMACH -$CREATURE:FORGOTTEN_BEAST_363:GIZZARD -%CREATURE:FORGOTTEN_BEAST_363:PANCREAS -#CREATURE:FORGOTTEN_BEAST_363:SPLEEN -#CREATURE:FORGOTTEN_BEAST_363:KIDNEY -#CREATURE:FORGOTTEN_BEAST_364:MUSCLE - CREATURE:FORGOTTEN_BEAST_364:EYE -"CREATURE:FORGOTTEN_BEAST_364:BRAIN -!CREATURE:FORGOTTEN_BEAST_364:LUNG -"CREATURE:FORGOTTEN_BEAST_364:HEART -"CREATURE:FORGOTTEN_BEAST_364:LIVER - CREATURE:FORGOTTEN_BEAST_364:GUT -$CREATURE:FORGOTTEN_BEAST_364:STOMACH -$CREATURE:FORGOTTEN_BEAST_364:GIZZARD -%CREATURE:FORGOTTEN_BEAST_364:PANCREAS -#CREATURE:FORGOTTEN_BEAST_364:SPLEEN -#CREATURE:FORGOTTEN_BEAST_364:KIDNEY -#CREATURE:FORGOTTEN_BEAST_365:MUSCLE - CREATURE:FORGOTTEN_BEAST_365:EYE -"CREATURE:FORGOTTEN_BEAST_365:BRAIN -!CREATURE:FORGOTTEN_BEAST_365:LUNG -"CREATURE:FORGOTTEN_BEAST_365:HEART -"CREATURE:FORGOTTEN_BEAST_365:LIVER - CREATURE:FORGOTTEN_BEAST_365:GUT -$CREATURE:FORGOTTEN_BEAST_365:STOMACH -$CREATURE:FORGOTTEN_BEAST_365:GIZZARD -%CREATURE:FORGOTTEN_BEAST_365:PANCREAS -#CREATURE:FORGOTTEN_BEAST_365:SPLEEN -#CREATURE:FORGOTTEN_BEAST_365:KIDNEY -#CREATURE:FORGOTTEN_BEAST_366:MUSCLE - CREATURE:FORGOTTEN_BEAST_366:EYE -"CREATURE:FORGOTTEN_BEAST_366:BRAIN -!CREATURE:FORGOTTEN_BEAST_366:LUNG -"CREATURE:FORGOTTEN_BEAST_366:HEART -"CREATURE:FORGOTTEN_BEAST_366:LIVER - CREATURE:FORGOTTEN_BEAST_366:GUT -$CREATURE:FORGOTTEN_BEAST_366:STOMACH -$CREATURE:FORGOTTEN_BEAST_366:GIZZARD -%CREATURE:FORGOTTEN_BEAST_366:PANCREAS -#CREATURE:FORGOTTEN_BEAST_366:SPLEEN -#CREATURE:FORGOTTEN_BEAST_366:KIDNEY -#CREATURE:FORGOTTEN_BEAST_367:MUSCLE - CREATURE:FORGOTTEN_BEAST_367:EYE -"CREATURE:FORGOTTEN_BEAST_367:BRAIN -!CREATURE:FORGOTTEN_BEAST_367:LUNG -"CREATURE:FORGOTTEN_BEAST_367:HEART -"CREATURE:FORGOTTEN_BEAST_367:LIVER - CREATURE:FORGOTTEN_BEAST_367:GUT -$CREATURE:FORGOTTEN_BEAST_367:STOMACH -$CREATURE:FORGOTTEN_BEAST_367:GIZZARD -%CREATURE:FORGOTTEN_BEAST_367:PANCREAS -#CREATURE:FORGOTTEN_BEAST_367:SPLEEN -#CREATURE:FORGOTTEN_BEAST_367:KIDNEY -#CREATURE:FORGOTTEN_BEAST_368:MUSCLE - CREATURE:FORGOTTEN_BEAST_368:EYE -"CREATURE:FORGOTTEN_BEAST_368:BRAIN -!CREATURE:FORGOTTEN_BEAST_368:LUNG -"CREATURE:FORGOTTEN_BEAST_368:HEART -"CREATURE:FORGOTTEN_BEAST_368:LIVER - CREATURE:FORGOTTEN_BEAST_368:GUT -$CREATURE:FORGOTTEN_BEAST_368:STOMACH -$CREATURE:FORGOTTEN_BEAST_368:GIZZARD -%CREATURE:FORGOTTEN_BEAST_368:PANCREAS -#CREATURE:FORGOTTEN_BEAST_368:SPLEEN -#CREATURE:FORGOTTEN_BEAST_368:KIDNEY -#CREATURE:FORGOTTEN_BEAST_370:MUSCLE - CREATURE:FORGOTTEN_BEAST_370:EYE -"CREATURE:FORGOTTEN_BEAST_370:BRAIN -!CREATURE:FORGOTTEN_BEAST_370:LUNG -"CREATURE:FORGOTTEN_BEAST_370:HEART -"CREATURE:FORGOTTEN_BEAST_370:LIVER - CREATURE:FORGOTTEN_BEAST_370:GUT -$CREATURE:FORGOTTEN_BEAST_370:STOMACH -$CREATURE:FORGOTTEN_BEAST_370:GIZZARD -%CREATURE:FORGOTTEN_BEAST_370:PANCREAS -#CREATURE:FORGOTTEN_BEAST_370:SPLEEN -#CREATURE:FORGOTTEN_BEAST_370:KIDNEY -#CREATURE:FORGOTTEN_BEAST_372:MUSCLE - CREATURE:FORGOTTEN_BEAST_372:EYE -"CREATURE:FORGOTTEN_BEAST_372:BRAIN -!CREATURE:FORGOTTEN_BEAST_372:LUNG -"CREATURE:FORGOTTEN_BEAST_372:HEART -"CREATURE:FORGOTTEN_BEAST_372:LIVER - CREATURE:FORGOTTEN_BEAST_372:GUT -$CREATURE:FORGOTTEN_BEAST_372:STOMACH -$CREATURE:FORGOTTEN_BEAST_372:GIZZARD -%CREATURE:FORGOTTEN_BEAST_372:PANCREAS -#CREATURE:FORGOTTEN_BEAST_372:SPLEEN -#CREATURE:FORGOTTEN_BEAST_372:KIDNEY -#CREATURE:FORGOTTEN_BEAST_374:MUSCLE - CREATURE:FORGOTTEN_BEAST_374:EYE -"CREATURE:FORGOTTEN_BEAST_374:BRAIN -!CREATURE:FORGOTTEN_BEAST_374:LUNG -"CREATURE:FORGOTTEN_BEAST_374:HEART -"CREATURE:FORGOTTEN_BEAST_374:LIVER - CREATURE:FORGOTTEN_BEAST_374:GUT -$CREATURE:FORGOTTEN_BEAST_374:STOMACH -$CREATURE:FORGOTTEN_BEAST_374:GIZZARD -%CREATURE:FORGOTTEN_BEAST_374:PANCREAS -#CREATURE:FORGOTTEN_BEAST_374:SPLEEN -#CREATURE:FORGOTTEN_BEAST_374:KIDNEY -#CREATURE:FORGOTTEN_BEAST_375:MUSCLE - CREATURE:FORGOTTEN_BEAST_375:EYE -"CREATURE:FORGOTTEN_BEAST_375:BRAIN -!CREATURE:FORGOTTEN_BEAST_375:LUNG -"CREATURE:FORGOTTEN_BEAST_375:HEART -"CREATURE:FORGOTTEN_BEAST_375:LIVER - CREATURE:FORGOTTEN_BEAST_375:GUT -$CREATURE:FORGOTTEN_BEAST_375:STOMACH -$CREATURE:FORGOTTEN_BEAST_375:GIZZARD -%CREATURE:FORGOTTEN_BEAST_375:PANCREAS -#CREATURE:FORGOTTEN_BEAST_375:SPLEEN -#CREATURE:FORGOTTEN_BEAST_375:KIDNEY -#CREATURE:FORGOTTEN_BEAST_377:MUSCLE - CREATURE:FORGOTTEN_BEAST_377:EYE -"CREATURE:FORGOTTEN_BEAST_377:BRAIN -!CREATURE:FORGOTTEN_BEAST_377:LUNG -"CREATURE:FORGOTTEN_BEAST_377:HEART -"CREATURE:FORGOTTEN_BEAST_377:LIVER - CREATURE:FORGOTTEN_BEAST_377:GUT -$CREATURE:FORGOTTEN_BEAST_377:STOMACH -$CREATURE:FORGOTTEN_BEAST_377:GIZZARD -%CREATURE:FORGOTTEN_BEAST_377:PANCREAS -#CREATURE:FORGOTTEN_BEAST_377:SPLEEN -#CREATURE:FORGOTTEN_BEAST_377:KIDNEY -#CREATURE:FORGOTTEN_BEAST_378:MUSCLE - CREATURE:FORGOTTEN_BEAST_378:EYE -"CREATURE:FORGOTTEN_BEAST_378:BRAIN -!CREATURE:FORGOTTEN_BEAST_378:LUNG -"CREATURE:FORGOTTEN_BEAST_378:HEART -"CREATURE:FORGOTTEN_BEAST_378:LIVER - CREATURE:FORGOTTEN_BEAST_378:GUT -$CREATURE:FORGOTTEN_BEAST_378:STOMACH -$CREATURE:FORGOTTEN_BEAST_378:GIZZARD -%CREATURE:FORGOTTEN_BEAST_378:PANCREAS -#CREATURE:FORGOTTEN_BEAST_378:SPLEEN -#CREATURE:FORGOTTEN_BEAST_378:KIDNEY -#CREATURE:FORGOTTEN_BEAST_379:MUSCLE - CREATURE:FORGOTTEN_BEAST_379:EYE -"CREATURE:FORGOTTEN_BEAST_379:BRAIN -!CREATURE:FORGOTTEN_BEAST_379:LUNG -"CREATURE:FORGOTTEN_BEAST_379:HEART -"CREATURE:FORGOTTEN_BEAST_379:LIVER - CREATURE:FORGOTTEN_BEAST_379:GUT -$CREATURE:FORGOTTEN_BEAST_379:STOMACH -$CREATURE:FORGOTTEN_BEAST_379:GIZZARD -%CREATURE:FORGOTTEN_BEAST_379:PANCREAS -#CREATURE:FORGOTTEN_BEAST_379:SPLEEN -#CREATURE:FORGOTTEN_BEAST_379:KIDNEY -#CREATURE:FORGOTTEN_BEAST_380:MUSCLE - CREATURE:FORGOTTEN_BEAST_380:EYE -"CREATURE:FORGOTTEN_BEAST_380:BRAIN -!CREATURE:FORGOTTEN_BEAST_380:LUNG -"CREATURE:FORGOTTEN_BEAST_380:HEART -"CREATURE:FORGOTTEN_BEAST_380:LIVER - CREATURE:FORGOTTEN_BEAST_380:GUT -$CREATURE:FORGOTTEN_BEAST_380:STOMACH -$CREATURE:FORGOTTEN_BEAST_380:GIZZARD -%CREATURE:FORGOTTEN_BEAST_380:PANCREAS -#CREATURE:FORGOTTEN_BEAST_380:SPLEEN -#CREATURE:FORGOTTEN_BEAST_380:KIDNEY -#CREATURE:FORGOTTEN_BEAST_381:MUSCLE - CREATURE:FORGOTTEN_BEAST_381:EYE -"CREATURE:FORGOTTEN_BEAST_381:BRAIN -!CREATURE:FORGOTTEN_BEAST_381:LUNG -"CREATURE:FORGOTTEN_BEAST_381:HEART -"CREATURE:FORGOTTEN_BEAST_381:LIVER - CREATURE:FORGOTTEN_BEAST_381:GUT -$CREATURE:FORGOTTEN_BEAST_381:STOMACH -$CREATURE:FORGOTTEN_BEAST_381:GIZZARD -%CREATURE:FORGOTTEN_BEAST_381:PANCREAS -#CREATURE:FORGOTTEN_BEAST_381:SPLEEN -#CREATURE:FORGOTTEN_BEAST_381:KIDNEY -#CREATURE:FORGOTTEN_BEAST_382:MUSCLE - CREATURE:FORGOTTEN_BEAST_382:EYE -"CREATURE:FORGOTTEN_BEAST_382:BRAIN -!CREATURE:FORGOTTEN_BEAST_382:LUNG -"CREATURE:FORGOTTEN_BEAST_382:HEART -"CREATURE:FORGOTTEN_BEAST_382:LIVER - CREATURE:FORGOTTEN_BEAST_382:GUT -$CREATURE:FORGOTTEN_BEAST_382:STOMACH -$CREATURE:FORGOTTEN_BEAST_382:GIZZARD -%CREATURE:FORGOTTEN_BEAST_382:PANCREAS -#CREATURE:FORGOTTEN_BEAST_382:SPLEEN -#CREATURE:FORGOTTEN_BEAST_382:KIDNEY -#CREATURE:FORGOTTEN_BEAST_383:MUSCLE - CREATURE:FORGOTTEN_BEAST_383:EYE -"CREATURE:FORGOTTEN_BEAST_383:BRAIN -!CREATURE:FORGOTTEN_BEAST_383:LUNG -"CREATURE:FORGOTTEN_BEAST_383:HEART -"CREATURE:FORGOTTEN_BEAST_383:LIVER - CREATURE:FORGOTTEN_BEAST_383:GUT -$CREATURE:FORGOTTEN_BEAST_383:STOMACH -$CREATURE:FORGOTTEN_BEAST_383:GIZZARD -%CREATURE:FORGOTTEN_BEAST_383:PANCREAS -#CREATURE:FORGOTTEN_BEAST_383:SPLEEN -#CREATURE:FORGOTTEN_BEAST_383:KIDNEY -#CREATURE:FORGOTTEN_BEAST_384:MUSCLE - CREATURE:FORGOTTEN_BEAST_384:EYE -"CREATURE:FORGOTTEN_BEAST_384:BRAIN -!CREATURE:FORGOTTEN_BEAST_384:LUNG -"CREATURE:FORGOTTEN_BEAST_384:HEART -"CREATURE:FORGOTTEN_BEAST_384:LIVER - CREATURE:FORGOTTEN_BEAST_384:GUT -$CREATURE:FORGOTTEN_BEAST_384:STOMACH -$CREATURE:FORGOTTEN_BEAST_384:GIZZARD -%CREATURE:FORGOTTEN_BEAST_384:PANCREAS -#CREATURE:FORGOTTEN_BEAST_384:SPLEEN -#CREATURE:FORGOTTEN_BEAST_384:KIDNEY -#CREATURE:FORGOTTEN_BEAST_385:MUSCLE - CREATURE:FORGOTTEN_BEAST_385:EYE -"CREATURE:FORGOTTEN_BEAST_385:BRAIN -!CREATURE:FORGOTTEN_BEAST_385:LUNG -"CREATURE:FORGOTTEN_BEAST_385:HEART -"CREATURE:FORGOTTEN_BEAST_385:LIVER - CREATURE:FORGOTTEN_BEAST_385:GUT -$CREATURE:FORGOTTEN_BEAST_385:STOMACH -$CREATURE:FORGOTTEN_BEAST_385:GIZZARD -%CREATURE:FORGOTTEN_BEAST_385:PANCREAS -#CREATURE:FORGOTTEN_BEAST_385:SPLEEN -#CREATURE:FORGOTTEN_BEAST_385:KIDNEY -#CREATURE:FORGOTTEN_BEAST_386:MUSCLE - CREATURE:FORGOTTEN_BEAST_386:EYE -"CREATURE:FORGOTTEN_BEAST_386:BRAIN -!CREATURE:FORGOTTEN_BEAST_386:LUNG -"CREATURE:FORGOTTEN_BEAST_386:HEART -"CREATURE:FORGOTTEN_BEAST_386:LIVER - CREATURE:FORGOTTEN_BEAST_386:GUT -$CREATURE:FORGOTTEN_BEAST_386:STOMACH -$CREATURE:FORGOTTEN_BEAST_386:GIZZARD -%CREATURE:FORGOTTEN_BEAST_386:PANCREAS -#CREATURE:FORGOTTEN_BEAST_386:SPLEEN -#CREATURE:FORGOTTEN_BEAST_386:KIDNEY -#CREATURE:FORGOTTEN_BEAST_387:MUSCLE - CREATURE:FORGOTTEN_BEAST_387:EYE -"CREATURE:FORGOTTEN_BEAST_387:BRAIN -!CREATURE:FORGOTTEN_BEAST_387:LUNG -"CREATURE:FORGOTTEN_BEAST_387:HEART -"CREATURE:FORGOTTEN_BEAST_387:LIVER - CREATURE:FORGOTTEN_BEAST_387:GUT -$CREATURE:FORGOTTEN_BEAST_387:STOMACH -$CREATURE:FORGOTTEN_BEAST_387:GIZZARD -%CREATURE:FORGOTTEN_BEAST_387:PANCREAS -#CREATURE:FORGOTTEN_BEAST_387:SPLEEN -#CREATURE:FORGOTTEN_BEAST_387:KIDNEY -#CREATURE:FORGOTTEN_BEAST_388:MUSCLE - CREATURE:FORGOTTEN_BEAST_388:EYE -"CREATURE:FORGOTTEN_BEAST_388:BRAIN -!CREATURE:FORGOTTEN_BEAST_388:LUNG -"CREATURE:FORGOTTEN_BEAST_388:HEART -"CREATURE:FORGOTTEN_BEAST_388:LIVER - CREATURE:FORGOTTEN_BEAST_388:GUT -$CREATURE:FORGOTTEN_BEAST_388:STOMACH -$CREATURE:FORGOTTEN_BEAST_388:GIZZARD -%CREATURE:FORGOTTEN_BEAST_388:PANCREAS -#CREATURE:FORGOTTEN_BEAST_388:SPLEEN -#CREATURE:FORGOTTEN_BEAST_388:KIDNEY -#CREATURE:FORGOTTEN_BEAST_389:MUSCLE - CREATURE:FORGOTTEN_BEAST_389:EYE -"CREATURE:FORGOTTEN_BEAST_389:BRAIN -!CREATURE:FORGOTTEN_BEAST_389:LUNG -"CREATURE:FORGOTTEN_BEAST_389:HEART -"CREATURE:FORGOTTEN_BEAST_389:LIVER - CREATURE:FORGOTTEN_BEAST_389:GUT -$CREATURE:FORGOTTEN_BEAST_389:STOMACH -$CREATURE:FORGOTTEN_BEAST_389:GIZZARD -%CREATURE:FORGOTTEN_BEAST_389:PANCREAS -#CREATURE:FORGOTTEN_BEAST_389:SPLEEN -#CREATURE:FORGOTTEN_BEAST_389:KIDNEY -#CREATURE:FORGOTTEN_BEAST_390:MUSCLE - CREATURE:FORGOTTEN_BEAST_390:EYE -"CREATURE:FORGOTTEN_BEAST_390:BRAIN -!CREATURE:FORGOTTEN_BEAST_390:LUNG -"CREATURE:FORGOTTEN_BEAST_390:HEART -"CREATURE:FORGOTTEN_BEAST_390:LIVER - CREATURE:FORGOTTEN_BEAST_390:GUT -$CREATURE:FORGOTTEN_BEAST_390:STOMACH -$CREATURE:FORGOTTEN_BEAST_390:GIZZARD -%CREATURE:FORGOTTEN_BEAST_390:PANCREAS -#CREATURE:FORGOTTEN_BEAST_390:SPLEEN -#CREATURE:FORGOTTEN_BEAST_390:KIDNEY -#CREATURE:FORGOTTEN_BEAST_391:MUSCLE - CREATURE:FORGOTTEN_BEAST_391:EYE -"CREATURE:FORGOTTEN_BEAST_391:BRAIN -!CREATURE:FORGOTTEN_BEAST_391:LUNG -"CREATURE:FORGOTTEN_BEAST_391:HEART -"CREATURE:FORGOTTEN_BEAST_391:LIVER - CREATURE:FORGOTTEN_BEAST_391:GUT -$CREATURE:FORGOTTEN_BEAST_391:STOMACH -$CREATURE:FORGOTTEN_BEAST_391:GIZZARD -%CREATURE:FORGOTTEN_BEAST_391:PANCREAS -#CREATURE:FORGOTTEN_BEAST_391:SPLEEN -#CREATURE:FORGOTTEN_BEAST_391:KIDNEY -#CREATURE:FORGOTTEN_BEAST_392:MUSCLE - CREATURE:FORGOTTEN_BEAST_392:EYE -"CREATURE:FORGOTTEN_BEAST_392:BRAIN -!CREATURE:FORGOTTEN_BEAST_392:LUNG -"CREATURE:FORGOTTEN_BEAST_392:HEART -"CREATURE:FORGOTTEN_BEAST_392:LIVER - CREATURE:FORGOTTEN_BEAST_392:GUT -$CREATURE:FORGOTTEN_BEAST_392:STOMACH -$CREATURE:FORGOTTEN_BEAST_392:GIZZARD -%CREATURE:FORGOTTEN_BEAST_392:PANCREAS -#CREATURE:FORGOTTEN_BEAST_392:SPLEEN -#CREATURE:FORGOTTEN_BEAST_392:KIDNEY -#CREATURE:FORGOTTEN_BEAST_393:MUSCLE - CREATURE:FORGOTTEN_BEAST_393:EYE -"CREATURE:FORGOTTEN_BEAST_393:BRAIN -!CREATURE:FORGOTTEN_BEAST_393:LUNG -"CREATURE:FORGOTTEN_BEAST_393:HEART -"CREATURE:FORGOTTEN_BEAST_393:LIVER - CREATURE:FORGOTTEN_BEAST_393:GUT -$CREATURE:FORGOTTEN_BEAST_393:STOMACH -$CREATURE:FORGOTTEN_BEAST_393:GIZZARD -%CREATURE:FORGOTTEN_BEAST_393:PANCREAS -#CREATURE:FORGOTTEN_BEAST_393:SPLEEN -#CREATURE:FORGOTTEN_BEAST_393:KIDNEY -#CREATURE:FORGOTTEN_BEAST_396:MUSCLE - CREATURE:FORGOTTEN_BEAST_396:EYE -"CREATURE:FORGOTTEN_BEAST_396:BRAIN -!CREATURE:FORGOTTEN_BEAST_396:LUNG -"CREATURE:FORGOTTEN_BEAST_396:HEART -"CREATURE:FORGOTTEN_BEAST_396:LIVER - CREATURE:FORGOTTEN_BEAST_396:GUT -$CREATURE:FORGOTTEN_BEAST_396:STOMACH -$CREATURE:FORGOTTEN_BEAST_396:GIZZARD -%CREATURE:FORGOTTEN_BEAST_396:PANCREAS -#CREATURE:FORGOTTEN_BEAST_396:SPLEEN -#CREATURE:FORGOTTEN_BEAST_396:KIDNEY -#CREATURE:FORGOTTEN_BEAST_397:MUSCLE - CREATURE:FORGOTTEN_BEAST_397:EYE -"CREATURE:FORGOTTEN_BEAST_397:BRAIN -!CREATURE:FORGOTTEN_BEAST_397:LUNG -"CREATURE:FORGOTTEN_BEAST_397:HEART -"CREATURE:FORGOTTEN_BEAST_397:LIVER - CREATURE:FORGOTTEN_BEAST_397:GUT -$CREATURE:FORGOTTEN_BEAST_397:STOMACH -$CREATURE:FORGOTTEN_BEAST_397:GIZZARD -%CREATURE:FORGOTTEN_BEAST_397:PANCREAS -#CREATURE:FORGOTTEN_BEAST_397:SPLEEN -#CREATURE:FORGOTTEN_BEAST_397:KIDNEY -#CREATURE:FORGOTTEN_BEAST_398:MUSCLE - CREATURE:FORGOTTEN_BEAST_398:EYE -"CREATURE:FORGOTTEN_BEAST_398:BRAIN -!CREATURE:FORGOTTEN_BEAST_398:LUNG -"CREATURE:FORGOTTEN_BEAST_398:HEART -"CREATURE:FORGOTTEN_BEAST_398:LIVER - CREATURE:FORGOTTEN_BEAST_398:GUT -$CREATURE:FORGOTTEN_BEAST_398:STOMACH -$CREATURE:FORGOTTEN_BEAST_398:GIZZARD -%CREATURE:FORGOTTEN_BEAST_398:PANCREAS -#CREATURE:FORGOTTEN_BEAST_398:SPLEEN -#CREATURE:FORGOTTEN_BEAST_398:KIDNEY -#CREATURE:FORGOTTEN_BEAST_400:MUSCLE - CREATURE:FORGOTTEN_BEAST_400:EYE -"CREATURE:FORGOTTEN_BEAST_400:BRAIN -!CREATURE:FORGOTTEN_BEAST_400:LUNG -"CREATURE:FORGOTTEN_BEAST_400:HEART -"CREATURE:FORGOTTEN_BEAST_400:LIVER - CREATURE:FORGOTTEN_BEAST_400:GUT -$CREATURE:FORGOTTEN_BEAST_400:STOMACH -$CREATURE:FORGOTTEN_BEAST_400:GIZZARD -%CREATURE:FORGOTTEN_BEAST_400:PANCREAS -#CREATURE:FORGOTTEN_BEAST_400:SPLEEN -#CREATURE:FORGOTTEN_BEAST_400:KIDNEY -#CREATURE:FORGOTTEN_BEAST_403:MUSCLE - CREATURE:FORGOTTEN_BEAST_403:EYE -"CREATURE:FORGOTTEN_BEAST_403:BRAIN -!CREATURE:FORGOTTEN_BEAST_403:LUNG -"CREATURE:FORGOTTEN_BEAST_403:HEART -"CREATURE:FORGOTTEN_BEAST_403:LIVER - CREATURE:FORGOTTEN_BEAST_403:GUT -$CREATURE:FORGOTTEN_BEAST_403:STOMACH -$CREATURE:FORGOTTEN_BEAST_403:GIZZARD -%CREATURE:FORGOTTEN_BEAST_403:PANCREAS -#CREATURE:FORGOTTEN_BEAST_403:SPLEEN -#CREATURE:FORGOTTEN_BEAST_403:KIDNEY -#CREATURE:FORGOTTEN_BEAST_404:MUSCLE - CREATURE:FORGOTTEN_BEAST_404:EYE -"CREATURE:FORGOTTEN_BEAST_404:BRAIN -!CREATURE:FORGOTTEN_BEAST_404:LUNG -"CREATURE:FORGOTTEN_BEAST_404:HEART -"CREATURE:FORGOTTEN_BEAST_404:LIVER - CREATURE:FORGOTTEN_BEAST_404:GUT -$CREATURE:FORGOTTEN_BEAST_404:STOMACH -$CREATURE:FORGOTTEN_BEAST_404:GIZZARD -%CREATURE:FORGOTTEN_BEAST_404:PANCREAS -#CREATURE:FORGOTTEN_BEAST_404:SPLEEN -#CREATURE:FORGOTTEN_BEAST_404:KIDNEY -#CREATURE:FORGOTTEN_BEAST_405:MUSCLE - CREATURE:FORGOTTEN_BEAST_405:EYE -"CREATURE:FORGOTTEN_BEAST_405:BRAIN -!CREATURE:FORGOTTEN_BEAST_405:LUNG -"CREATURE:FORGOTTEN_BEAST_405:HEART -"CREATURE:FORGOTTEN_BEAST_405:LIVER - CREATURE:FORGOTTEN_BEAST_405:GUT -$CREATURE:FORGOTTEN_BEAST_405:STOMACH -$CREATURE:FORGOTTEN_BEAST_405:GIZZARD -%CREATURE:FORGOTTEN_BEAST_405:PANCREAS -#CREATURE:FORGOTTEN_BEAST_405:SPLEEN -#CREATURE:FORGOTTEN_BEAST_405:KIDNEY -#CREATURE:FORGOTTEN_BEAST_406:MUSCLE - CREATURE:FORGOTTEN_BEAST_406:EYE -"CREATURE:FORGOTTEN_BEAST_406:BRAIN -!CREATURE:FORGOTTEN_BEAST_406:LUNG -"CREATURE:FORGOTTEN_BEAST_406:HEART -"CREATURE:FORGOTTEN_BEAST_406:LIVER - CREATURE:FORGOTTEN_BEAST_406:GUT -$CREATURE:FORGOTTEN_BEAST_406:STOMACH -$CREATURE:FORGOTTEN_BEAST_406:GIZZARD -%CREATURE:FORGOTTEN_BEAST_406:PANCREAS -#CREATURE:FORGOTTEN_BEAST_406:SPLEEN -#CREATURE:FORGOTTEN_BEAST_406:KIDNEY -#CREATURE:FORGOTTEN_BEAST_407:MUSCLE - CREATURE:FORGOTTEN_BEAST_407:EYE -"CREATURE:FORGOTTEN_BEAST_407:BRAIN -!CREATURE:FORGOTTEN_BEAST_407:LUNG -"CREATURE:FORGOTTEN_BEAST_407:HEART -"CREATURE:FORGOTTEN_BEAST_407:LIVER - CREATURE:FORGOTTEN_BEAST_407:GUT -$CREATURE:FORGOTTEN_BEAST_407:STOMACH -$CREATURE:FORGOTTEN_BEAST_407:GIZZARD -%CREATURE:FORGOTTEN_BEAST_407:PANCREAS -#CREATURE:FORGOTTEN_BEAST_407:SPLEEN -#CREATURE:FORGOTTEN_BEAST_407:KIDNEY -#CREATURE:FORGOTTEN_BEAST_408:MUSCLE - CREATURE:FORGOTTEN_BEAST_408:EYE -"CREATURE:FORGOTTEN_BEAST_408:BRAIN -!CREATURE:FORGOTTEN_BEAST_408:LUNG -"CREATURE:FORGOTTEN_BEAST_408:HEART -"CREATURE:FORGOTTEN_BEAST_408:LIVER - CREATURE:FORGOTTEN_BEAST_408:GUT -$CREATURE:FORGOTTEN_BEAST_408:STOMACH -$CREATURE:FORGOTTEN_BEAST_408:GIZZARD -%CREATURE:FORGOTTEN_BEAST_408:PANCREAS -#CREATURE:FORGOTTEN_BEAST_408:SPLEEN -#CREATURE:FORGOTTEN_BEAST_408:KIDNEY -#CREATURE:FORGOTTEN_BEAST_409:MUSCLE - CREATURE:FORGOTTEN_BEAST_409:EYE -"CREATURE:FORGOTTEN_BEAST_409:BRAIN -!CREATURE:FORGOTTEN_BEAST_409:LUNG -"CREATURE:FORGOTTEN_BEAST_409:HEART -"CREATURE:FORGOTTEN_BEAST_409:LIVER - CREATURE:FORGOTTEN_BEAST_409:GUT -$CREATURE:FORGOTTEN_BEAST_409:STOMACH -$CREATURE:FORGOTTEN_BEAST_409:GIZZARD -%CREATURE:FORGOTTEN_BEAST_409:PANCREAS -#CREATURE:FORGOTTEN_BEAST_409:SPLEEN -#CREATURE:FORGOTTEN_BEAST_409:KIDNEY -#CREATURE:FORGOTTEN_BEAST_410:MUSCLE - CREATURE:FORGOTTEN_BEAST_410:EYE -"CREATURE:FORGOTTEN_BEAST_410:BRAIN -!CREATURE:FORGOTTEN_BEAST_410:LUNG -"CREATURE:FORGOTTEN_BEAST_410:HEART -"CREATURE:FORGOTTEN_BEAST_410:LIVER - CREATURE:FORGOTTEN_BEAST_410:GUT -$CREATURE:FORGOTTEN_BEAST_410:STOMACH -$CREATURE:FORGOTTEN_BEAST_410:GIZZARD -%CREATURE:FORGOTTEN_BEAST_410:PANCREAS -#CREATURE:FORGOTTEN_BEAST_410:SPLEEN -#CREATURE:FORGOTTEN_BEAST_410:KIDNEY -#CREATURE:FORGOTTEN_BEAST_411:MUSCLE - CREATURE:FORGOTTEN_BEAST_411:EYE -"CREATURE:FORGOTTEN_BEAST_411:BRAIN -!CREATURE:FORGOTTEN_BEAST_411:LUNG -"CREATURE:FORGOTTEN_BEAST_411:HEART -"CREATURE:FORGOTTEN_BEAST_411:LIVER - CREATURE:FORGOTTEN_BEAST_411:GUT -$CREATURE:FORGOTTEN_BEAST_411:STOMACH -$CREATURE:FORGOTTEN_BEAST_411:GIZZARD -%CREATURE:FORGOTTEN_BEAST_411:PANCREAS -#CREATURE:FORGOTTEN_BEAST_411:SPLEEN -#CREATURE:FORGOTTEN_BEAST_411:KIDNEY -#CREATURE:FORGOTTEN_BEAST_412:MUSCLE - CREATURE:FORGOTTEN_BEAST_412:EYE -"CREATURE:FORGOTTEN_BEAST_412:BRAIN -!CREATURE:FORGOTTEN_BEAST_412:LUNG -"CREATURE:FORGOTTEN_BEAST_412:HEART -"CREATURE:FORGOTTEN_BEAST_412:LIVER - CREATURE:FORGOTTEN_BEAST_412:GUT -$CREATURE:FORGOTTEN_BEAST_412:STOMACH -$CREATURE:FORGOTTEN_BEAST_412:GIZZARD -%CREATURE:FORGOTTEN_BEAST_412:PANCREAS -#CREATURE:FORGOTTEN_BEAST_412:SPLEEN -#CREATURE:FORGOTTEN_BEAST_412:KIDNEY -#CREATURE:FORGOTTEN_BEAST_413:MUSCLE - CREATURE:FORGOTTEN_BEAST_413:EYE -"CREATURE:FORGOTTEN_BEAST_413:BRAIN -!CREATURE:FORGOTTEN_BEAST_413:LUNG -"CREATURE:FORGOTTEN_BEAST_413:HEART -"CREATURE:FORGOTTEN_BEAST_413:LIVER - CREATURE:FORGOTTEN_BEAST_413:GUT -$CREATURE:FORGOTTEN_BEAST_413:STOMACH -$CREATURE:FORGOTTEN_BEAST_413:GIZZARD -%CREATURE:FORGOTTEN_BEAST_413:PANCREAS -#CREATURE:FORGOTTEN_BEAST_413:SPLEEN -#CREATURE:FORGOTTEN_BEAST_413:KIDNEY -#CREATURE:FORGOTTEN_BEAST_414:MUSCLE - CREATURE:FORGOTTEN_BEAST_414:EYE -"CREATURE:FORGOTTEN_BEAST_414:BRAIN -!CREATURE:FORGOTTEN_BEAST_414:LUNG -"CREATURE:FORGOTTEN_BEAST_414:HEART -"CREATURE:FORGOTTEN_BEAST_414:LIVER - CREATURE:FORGOTTEN_BEAST_414:GUT -$CREATURE:FORGOTTEN_BEAST_414:STOMACH -$CREATURE:FORGOTTEN_BEAST_414:GIZZARD -%CREATURE:FORGOTTEN_BEAST_414:PANCREAS -#CREATURE:FORGOTTEN_BEAST_414:SPLEEN -#CREATURE:FORGOTTEN_BEAST_414:KIDNEY -#CREATURE:FORGOTTEN_BEAST_416:MUSCLE - CREATURE:FORGOTTEN_BEAST_416:EYE -"CREATURE:FORGOTTEN_BEAST_416:BRAIN -!CREATURE:FORGOTTEN_BEAST_416:LUNG -"CREATURE:FORGOTTEN_BEAST_416:HEART -"CREATURE:FORGOTTEN_BEAST_416:LIVER - CREATURE:FORGOTTEN_BEAST_416:GUT -$CREATURE:FORGOTTEN_BEAST_416:STOMACH -$CREATURE:FORGOTTEN_BEAST_416:GIZZARD -%CREATURE:FORGOTTEN_BEAST_416:PANCREAS -#CREATURE:FORGOTTEN_BEAST_416:SPLEEN -#CREATURE:FORGOTTEN_BEAST_416:KIDNEY -#CREATURE:FORGOTTEN_BEAST_417:MUSCLE - CREATURE:FORGOTTEN_BEAST_417:EYE -"CREATURE:FORGOTTEN_BEAST_417:BRAIN -!CREATURE:FORGOTTEN_BEAST_417:LUNG -"CREATURE:FORGOTTEN_BEAST_417:HEART -"CREATURE:FORGOTTEN_BEAST_417:LIVER - CREATURE:FORGOTTEN_BEAST_417:GUT -$CREATURE:FORGOTTEN_BEAST_417:STOMACH -$CREATURE:FORGOTTEN_BEAST_417:GIZZARD -%CREATURE:FORGOTTEN_BEAST_417:PANCREAS -#CREATURE:FORGOTTEN_BEAST_417:SPLEEN -#CREATURE:FORGOTTEN_BEAST_417:KIDNEY -#CREATURE:FORGOTTEN_BEAST_418:MUSCLE - CREATURE:FORGOTTEN_BEAST_418:EYE -"CREATURE:FORGOTTEN_BEAST_418:BRAIN -!CREATURE:FORGOTTEN_BEAST_418:LUNG -"CREATURE:FORGOTTEN_BEAST_418:HEART -"CREATURE:FORGOTTEN_BEAST_418:LIVER - CREATURE:FORGOTTEN_BEAST_418:GUT -$CREATURE:FORGOTTEN_BEAST_418:STOMACH -$CREATURE:FORGOTTEN_BEAST_418:GIZZARD -%CREATURE:FORGOTTEN_BEAST_418:PANCREAS -#CREATURE:FORGOTTEN_BEAST_418:SPLEEN -#CREATURE:FORGOTTEN_BEAST_418:KIDNEY -#CREATURE:FORGOTTEN_BEAST_420:MUSCLE - CREATURE:FORGOTTEN_BEAST_420:EYE -"CREATURE:FORGOTTEN_BEAST_420:BRAIN -!CREATURE:FORGOTTEN_BEAST_420:LUNG -"CREATURE:FORGOTTEN_BEAST_420:HEART -"CREATURE:FORGOTTEN_BEAST_420:LIVER - CREATURE:FORGOTTEN_BEAST_420:GUT -$CREATURE:FORGOTTEN_BEAST_420:STOMACH -$CREATURE:FORGOTTEN_BEAST_420:GIZZARD -%CREATURE:FORGOTTEN_BEAST_420:PANCREAS -#CREATURE:FORGOTTEN_BEAST_420:SPLEEN -#CREATURE:FORGOTTEN_BEAST_420:KIDNEY -#CREATURE:FORGOTTEN_BEAST_421:MUSCLE - CREATURE:FORGOTTEN_BEAST_421:EYE -"CREATURE:FORGOTTEN_BEAST_421:BRAIN -!CREATURE:FORGOTTEN_BEAST_421:LUNG -"CREATURE:FORGOTTEN_BEAST_421:HEART -"CREATURE:FORGOTTEN_BEAST_421:LIVER - CREATURE:FORGOTTEN_BEAST_421:GUT -$CREATURE:FORGOTTEN_BEAST_421:STOMACH -$CREATURE:FORGOTTEN_BEAST_421:GIZZARD -%CREATURE:FORGOTTEN_BEAST_421:PANCREAS -#CREATURE:FORGOTTEN_BEAST_421:SPLEEN -#CREATURE:FORGOTTEN_BEAST_421:KIDNEY -#CREATURE:FORGOTTEN_BEAST_422:MUSCLE - CREATURE:FORGOTTEN_BEAST_422:EYE -"CREATURE:FORGOTTEN_BEAST_422:BRAIN -!CREATURE:FORGOTTEN_BEAST_422:LUNG -"CREATURE:FORGOTTEN_BEAST_422:HEART -"CREATURE:FORGOTTEN_BEAST_422:LIVER - CREATURE:FORGOTTEN_BEAST_422:GUT -$CREATURE:FORGOTTEN_BEAST_422:STOMACH -$CREATURE:FORGOTTEN_BEAST_422:GIZZARD -%CREATURE:FORGOTTEN_BEAST_422:PANCREAS -#CREATURE:FORGOTTEN_BEAST_422:SPLEEN -#CREATURE:FORGOTTEN_BEAST_422:KIDNEY -#CREATURE:FORGOTTEN_BEAST_423:MUSCLE - CREATURE:FORGOTTEN_BEAST_423:EYE -"CREATURE:FORGOTTEN_BEAST_423:BRAIN -!CREATURE:FORGOTTEN_BEAST_423:LUNG -"CREATURE:FORGOTTEN_BEAST_423:HEART -"CREATURE:FORGOTTEN_BEAST_423:LIVER - CREATURE:FORGOTTEN_BEAST_423:GUT -$CREATURE:FORGOTTEN_BEAST_423:STOMACH -$CREATURE:FORGOTTEN_BEAST_423:GIZZARD -%CREATURE:FORGOTTEN_BEAST_423:PANCREAS -#CREATURE:FORGOTTEN_BEAST_423:SPLEEN -#CREATURE:FORGOTTEN_BEAST_423:KIDNEY -#CREATURE:FORGOTTEN_BEAST_424:MUSCLE - CREATURE:FORGOTTEN_BEAST_424:EYE -"CREATURE:FORGOTTEN_BEAST_424:BRAIN -!CREATURE:FORGOTTEN_BEAST_424:LUNG -"CREATURE:FORGOTTEN_BEAST_424:HEART -"CREATURE:FORGOTTEN_BEAST_424:LIVER - CREATURE:FORGOTTEN_BEAST_424:GUT -$CREATURE:FORGOTTEN_BEAST_424:STOMACH -$CREATURE:FORGOTTEN_BEAST_424:GIZZARD -%CREATURE:FORGOTTEN_BEAST_424:PANCREAS -#CREATURE:FORGOTTEN_BEAST_424:SPLEEN -#CREATURE:FORGOTTEN_BEAST_424:KIDNEY -#CREATURE:FORGOTTEN_BEAST_427:MUSCLE - CREATURE:FORGOTTEN_BEAST_427:EYE -"CREATURE:FORGOTTEN_BEAST_427:BRAIN -!CREATURE:FORGOTTEN_BEAST_427:LUNG -"CREATURE:FORGOTTEN_BEAST_427:HEART -"CREATURE:FORGOTTEN_BEAST_427:LIVER - CREATURE:FORGOTTEN_BEAST_427:GUT -$CREATURE:FORGOTTEN_BEAST_427:STOMACH -$CREATURE:FORGOTTEN_BEAST_427:GIZZARD -%CREATURE:FORGOTTEN_BEAST_427:PANCREAS -#CREATURE:FORGOTTEN_BEAST_427:SPLEEN -#CREATURE:FORGOTTEN_BEAST_427:KIDNEY -#CREATURE:FORGOTTEN_BEAST_429:MUSCLE - CREATURE:FORGOTTEN_BEAST_429:EYE -"CREATURE:FORGOTTEN_BEAST_429:BRAIN -!CREATURE:FORGOTTEN_BEAST_429:LUNG -"CREATURE:FORGOTTEN_BEAST_429:HEART -"CREATURE:FORGOTTEN_BEAST_429:LIVER - CREATURE:FORGOTTEN_BEAST_429:GUT -$CREATURE:FORGOTTEN_BEAST_429:STOMACH -$CREATURE:FORGOTTEN_BEAST_429:GIZZARD -%CREATURE:FORGOTTEN_BEAST_429:PANCREAS -#CREATURE:FORGOTTEN_BEAST_429:SPLEEN -#CREATURE:FORGOTTEN_BEAST_429:KIDNEY -#CREATURE:FORGOTTEN_BEAST_430:MUSCLE - CREATURE:FORGOTTEN_BEAST_430:EYE -"CREATURE:FORGOTTEN_BEAST_430:BRAIN -!CREATURE:FORGOTTEN_BEAST_430:LUNG -"CREATURE:FORGOTTEN_BEAST_430:HEART -"CREATURE:FORGOTTEN_BEAST_430:LIVER - CREATURE:FORGOTTEN_BEAST_430:GUT -$CREATURE:FORGOTTEN_BEAST_430:STOMACH -$CREATURE:FORGOTTEN_BEAST_430:GIZZARD -%CREATURE:FORGOTTEN_BEAST_430:PANCREAS -#CREATURE:FORGOTTEN_BEAST_430:SPLEEN -#CREATURE:FORGOTTEN_BEAST_430:KIDNEY -#CREATURE:FORGOTTEN_BEAST_432:MUSCLE - CREATURE:FORGOTTEN_BEAST_432:EYE -"CREATURE:FORGOTTEN_BEAST_432:BRAIN -!CREATURE:FORGOTTEN_BEAST_432:LUNG -"CREATURE:FORGOTTEN_BEAST_432:HEART -"CREATURE:FORGOTTEN_BEAST_432:LIVER - CREATURE:FORGOTTEN_BEAST_432:GUT -$CREATURE:FORGOTTEN_BEAST_432:STOMACH -$CREATURE:FORGOTTEN_BEAST_432:GIZZARD -%CREATURE:FORGOTTEN_BEAST_432:PANCREAS -#CREATURE:FORGOTTEN_BEAST_432:SPLEEN -#CREATURE:FORGOTTEN_BEAST_432:KIDNEY -#CREATURE:FORGOTTEN_BEAST_433:MUSCLE - CREATURE:FORGOTTEN_BEAST_433:EYE -"CREATURE:FORGOTTEN_BEAST_433:BRAIN -!CREATURE:FORGOTTEN_BEAST_433:LUNG -"CREATURE:FORGOTTEN_BEAST_433:HEART -"CREATURE:FORGOTTEN_BEAST_433:LIVER - CREATURE:FORGOTTEN_BEAST_433:GUT -$CREATURE:FORGOTTEN_BEAST_433:STOMACH -$CREATURE:FORGOTTEN_BEAST_433:GIZZARD -%CREATURE:FORGOTTEN_BEAST_433:PANCREAS -#CREATURE:FORGOTTEN_BEAST_433:SPLEEN -#CREATURE:FORGOTTEN_BEAST_433:KIDNEY -#CREATURE:FORGOTTEN_BEAST_434:MUSCLE - CREATURE:FORGOTTEN_BEAST_434:EYE -"CREATURE:FORGOTTEN_BEAST_434:BRAIN -!CREATURE:FORGOTTEN_BEAST_434:LUNG -"CREATURE:FORGOTTEN_BEAST_434:HEART -"CREATURE:FORGOTTEN_BEAST_434:LIVER - CREATURE:FORGOTTEN_BEAST_434:GUT -$CREATURE:FORGOTTEN_BEAST_434:STOMACH -$CREATURE:FORGOTTEN_BEAST_434:GIZZARD -%CREATURE:FORGOTTEN_BEAST_434:PANCREAS -#CREATURE:FORGOTTEN_BEAST_434:SPLEEN -#CREATURE:FORGOTTEN_BEAST_434:KIDNEY -#CREATURE:FORGOTTEN_BEAST_435:MUSCLE - CREATURE:FORGOTTEN_BEAST_435:EYE -"CREATURE:FORGOTTEN_BEAST_435:BRAIN -!CREATURE:FORGOTTEN_BEAST_435:LUNG -"CREATURE:FORGOTTEN_BEAST_435:HEART -"CREATURE:FORGOTTEN_BEAST_435:LIVER - CREATURE:FORGOTTEN_BEAST_435:GUT -$CREATURE:FORGOTTEN_BEAST_435:STOMACH -$CREATURE:FORGOTTEN_BEAST_435:GIZZARD -%CREATURE:FORGOTTEN_BEAST_435:PANCREAS -#CREATURE:FORGOTTEN_BEAST_435:SPLEEN -#CREATURE:FORGOTTEN_BEAST_435:KIDNEY -#CREATURE:FORGOTTEN_BEAST_436:MUSCLE - CREATURE:FORGOTTEN_BEAST_436:EYE -"CREATURE:FORGOTTEN_BEAST_436:BRAIN -!CREATURE:FORGOTTEN_BEAST_436:LUNG -"CREATURE:FORGOTTEN_BEAST_436:HEART -"CREATURE:FORGOTTEN_BEAST_436:LIVER - CREATURE:FORGOTTEN_BEAST_436:GUT -$CREATURE:FORGOTTEN_BEAST_436:STOMACH -$CREATURE:FORGOTTEN_BEAST_436:GIZZARD -%CREATURE:FORGOTTEN_BEAST_436:PANCREAS -#CREATURE:FORGOTTEN_BEAST_436:SPLEEN -#CREATURE:FORGOTTEN_BEAST_436:KIDNEY -#CREATURE:FORGOTTEN_BEAST_437:MUSCLE - CREATURE:FORGOTTEN_BEAST_437:EYE -"CREATURE:FORGOTTEN_BEAST_437:BRAIN -!CREATURE:FORGOTTEN_BEAST_437:LUNG -"CREATURE:FORGOTTEN_BEAST_437:HEART -"CREATURE:FORGOTTEN_BEAST_437:LIVER - CREATURE:FORGOTTEN_BEAST_437:GUT -$CREATURE:FORGOTTEN_BEAST_437:STOMACH -$CREATURE:FORGOTTEN_BEAST_437:GIZZARD -%CREATURE:FORGOTTEN_BEAST_437:PANCREAS -#CREATURE:FORGOTTEN_BEAST_437:SPLEEN -#CREATURE:FORGOTTEN_BEAST_437:KIDNEY -#CREATURE:FORGOTTEN_BEAST_438:MUSCLE - CREATURE:FORGOTTEN_BEAST_438:EYE -"CREATURE:FORGOTTEN_BEAST_438:BRAIN -!CREATURE:FORGOTTEN_BEAST_438:LUNG -"CREATURE:FORGOTTEN_BEAST_438:HEART -"CREATURE:FORGOTTEN_BEAST_438:LIVER - CREATURE:FORGOTTEN_BEAST_438:GUT -$CREATURE:FORGOTTEN_BEAST_438:STOMACH -$CREATURE:FORGOTTEN_BEAST_438:GIZZARD -%CREATURE:FORGOTTEN_BEAST_438:PANCREAS -#CREATURE:FORGOTTEN_BEAST_438:SPLEEN -#CREATURE:FORGOTTEN_BEAST_438:KIDNEY -#CREATURE:FORGOTTEN_BEAST_440:MUSCLE - CREATURE:FORGOTTEN_BEAST_440:EYE -"CREATURE:FORGOTTEN_BEAST_440:BRAIN -!CREATURE:FORGOTTEN_BEAST_440:LUNG -"CREATURE:FORGOTTEN_BEAST_440:HEART -"CREATURE:FORGOTTEN_BEAST_440:LIVER - CREATURE:FORGOTTEN_BEAST_440:GUT -$CREATURE:FORGOTTEN_BEAST_440:STOMACH -$CREATURE:FORGOTTEN_BEAST_440:GIZZARD -%CREATURE:FORGOTTEN_BEAST_440:PANCREAS -#CREATURE:FORGOTTEN_BEAST_440:SPLEEN -#CREATURE:FORGOTTEN_BEAST_440:KIDNEY -#CREATURE:FORGOTTEN_BEAST_441:MUSCLE - CREATURE:FORGOTTEN_BEAST_441:EYE -"CREATURE:FORGOTTEN_BEAST_441:BRAIN -!CREATURE:FORGOTTEN_BEAST_441:LUNG -"CREATURE:FORGOTTEN_BEAST_441:HEART -"CREATURE:FORGOTTEN_BEAST_441:LIVER - CREATURE:FORGOTTEN_BEAST_441:GUT -$CREATURE:FORGOTTEN_BEAST_441:STOMACH -$CREATURE:FORGOTTEN_BEAST_441:GIZZARD -%CREATURE:FORGOTTEN_BEAST_441:PANCREAS -#CREATURE:FORGOTTEN_BEAST_441:SPLEEN -#CREATURE:FORGOTTEN_BEAST_441:KIDNEY -#CREATURE:FORGOTTEN_BEAST_442:MUSCLE - CREATURE:FORGOTTEN_BEAST_442:EYE -"CREATURE:FORGOTTEN_BEAST_442:BRAIN -!CREATURE:FORGOTTEN_BEAST_442:LUNG -"CREATURE:FORGOTTEN_BEAST_442:HEART -"CREATURE:FORGOTTEN_BEAST_442:LIVER - CREATURE:FORGOTTEN_BEAST_442:GUT -$CREATURE:FORGOTTEN_BEAST_442:STOMACH -$CREATURE:FORGOTTEN_BEAST_442:GIZZARD -%CREATURE:FORGOTTEN_BEAST_442:PANCREAS -#CREATURE:FORGOTTEN_BEAST_442:SPLEEN -#CREATURE:FORGOTTEN_BEAST_442:KIDNEY -#CREATURE:FORGOTTEN_BEAST_444:MUSCLE - CREATURE:FORGOTTEN_BEAST_444:EYE -"CREATURE:FORGOTTEN_BEAST_444:BRAIN -!CREATURE:FORGOTTEN_BEAST_444:LUNG -"CREATURE:FORGOTTEN_BEAST_444:HEART -"CREATURE:FORGOTTEN_BEAST_444:LIVER - CREATURE:FORGOTTEN_BEAST_444:GUT -$CREATURE:FORGOTTEN_BEAST_444:STOMACH -$CREATURE:FORGOTTEN_BEAST_444:GIZZARD -%CREATURE:FORGOTTEN_BEAST_444:PANCREAS -#CREATURE:FORGOTTEN_BEAST_444:SPLEEN -#CREATURE:FORGOTTEN_BEAST_444:KIDNEY -#CREATURE:FORGOTTEN_BEAST_446:MUSCLE - CREATURE:FORGOTTEN_BEAST_446:EYE -"CREATURE:FORGOTTEN_BEAST_446:BRAIN -!CREATURE:FORGOTTEN_BEAST_446:LUNG -"CREATURE:FORGOTTEN_BEAST_446:HEART -"CREATURE:FORGOTTEN_BEAST_446:LIVER - CREATURE:FORGOTTEN_BEAST_446:GUT -$CREATURE:FORGOTTEN_BEAST_446:STOMACH -$CREATURE:FORGOTTEN_BEAST_446:GIZZARD -%CREATURE:FORGOTTEN_BEAST_446:PANCREAS -#CREATURE:FORGOTTEN_BEAST_446:SPLEEN -#CREATURE:FORGOTTEN_BEAST_446:KIDNEY -#CREATURE:FORGOTTEN_BEAST_447:MUSCLE - CREATURE:FORGOTTEN_BEAST_447:EYE -"CREATURE:FORGOTTEN_BEAST_447:BRAIN -!CREATURE:FORGOTTEN_BEAST_447:LUNG -"CREATURE:FORGOTTEN_BEAST_447:HEART -"CREATURE:FORGOTTEN_BEAST_447:LIVER - CREATURE:FORGOTTEN_BEAST_447:GUT -$CREATURE:FORGOTTEN_BEAST_447:STOMACH -$CREATURE:FORGOTTEN_BEAST_447:GIZZARD -%CREATURE:FORGOTTEN_BEAST_447:PANCREAS -#CREATURE:FORGOTTEN_BEAST_447:SPLEEN -#CREATURE:FORGOTTEN_BEAST_447:KIDNEY -#CREATURE:FORGOTTEN_BEAST_448:MUSCLE - CREATURE:FORGOTTEN_BEAST_448:EYE -"CREATURE:FORGOTTEN_BEAST_448:BRAIN -!CREATURE:FORGOTTEN_BEAST_448:LUNG -"CREATURE:FORGOTTEN_BEAST_448:HEART -"CREATURE:FORGOTTEN_BEAST_448:LIVER - CREATURE:FORGOTTEN_BEAST_448:GUT -$CREATURE:FORGOTTEN_BEAST_448:STOMACH -$CREATURE:FORGOTTEN_BEAST_448:GIZZARD -%CREATURE:FORGOTTEN_BEAST_448:PANCREAS -#CREATURE:FORGOTTEN_BEAST_448:SPLEEN -#CREATURE:FORGOTTEN_BEAST_448:KIDNEY -#CREATURE:FORGOTTEN_BEAST_449:MUSCLE - CREATURE:FORGOTTEN_BEAST_449:EYE -"CREATURE:FORGOTTEN_BEAST_449:BRAIN -!CREATURE:FORGOTTEN_BEAST_449:LUNG -"CREATURE:FORGOTTEN_BEAST_449:HEART -"CREATURE:FORGOTTEN_BEAST_449:LIVER - CREATURE:FORGOTTEN_BEAST_449:GUT -$CREATURE:FORGOTTEN_BEAST_449:STOMACH -$CREATURE:FORGOTTEN_BEAST_449:GIZZARD -%CREATURE:FORGOTTEN_BEAST_449:PANCREAS -#CREATURE:FORGOTTEN_BEAST_449:SPLEEN -#CREATURE:FORGOTTEN_BEAST_449:KIDNEY -#CREATURE:FORGOTTEN_BEAST_450:MUSCLE - CREATURE:FORGOTTEN_BEAST_450:EYE -"CREATURE:FORGOTTEN_BEAST_450:BRAIN -!CREATURE:FORGOTTEN_BEAST_450:LUNG -"CREATURE:FORGOTTEN_BEAST_450:HEART -"CREATURE:FORGOTTEN_BEAST_450:LIVER - CREATURE:FORGOTTEN_BEAST_450:GUT -$CREATURE:FORGOTTEN_BEAST_450:STOMACH -$CREATURE:FORGOTTEN_BEAST_450:GIZZARD -%CREATURE:FORGOTTEN_BEAST_450:PANCREAS -#CREATURE:FORGOTTEN_BEAST_450:SPLEEN -#CREATURE:FORGOTTEN_BEAST_450:KIDNEY -#CREATURE:FORGOTTEN_BEAST_451:MUSCLE - CREATURE:FORGOTTEN_BEAST_451:EYE -"CREATURE:FORGOTTEN_BEAST_451:BRAIN -!CREATURE:FORGOTTEN_BEAST_451:LUNG -"CREATURE:FORGOTTEN_BEAST_451:HEART -"CREATURE:FORGOTTEN_BEAST_451:LIVER - CREATURE:FORGOTTEN_BEAST_451:GUT -$CREATURE:FORGOTTEN_BEAST_451:STOMACH -$CREATURE:FORGOTTEN_BEAST_451:GIZZARD -%CREATURE:FORGOTTEN_BEAST_451:PANCREAS -#CREATURE:FORGOTTEN_BEAST_451:SPLEEN -#CREATURE:FORGOTTEN_BEAST_451:KIDNEY -#CREATURE:FORGOTTEN_BEAST_453:MUSCLE - CREATURE:FORGOTTEN_BEAST_453:EYE -"CREATURE:FORGOTTEN_BEAST_453:BRAIN -!CREATURE:FORGOTTEN_BEAST_453:LUNG -"CREATURE:FORGOTTEN_BEAST_453:HEART -"CREATURE:FORGOTTEN_BEAST_453:LIVER - CREATURE:FORGOTTEN_BEAST_453:GUT -$CREATURE:FORGOTTEN_BEAST_453:STOMACH -$CREATURE:FORGOTTEN_BEAST_453:GIZZARD -%CREATURE:FORGOTTEN_BEAST_453:PANCREAS -#CREATURE:FORGOTTEN_BEAST_453:SPLEEN -#CREATURE:FORGOTTEN_BEAST_453:KIDNEY -#CREATURE:FORGOTTEN_BEAST_454:MUSCLE - CREATURE:FORGOTTEN_BEAST_454:EYE -"CREATURE:FORGOTTEN_BEAST_454:BRAIN -!CREATURE:FORGOTTEN_BEAST_454:LUNG -"CREATURE:FORGOTTEN_BEAST_454:HEART -"CREATURE:FORGOTTEN_BEAST_454:LIVER - CREATURE:FORGOTTEN_BEAST_454:GUT -$CREATURE:FORGOTTEN_BEAST_454:STOMACH -$CREATURE:FORGOTTEN_BEAST_454:GIZZARD -%CREATURE:FORGOTTEN_BEAST_454:PANCREAS -#CREATURE:FORGOTTEN_BEAST_454:SPLEEN -#CREATURE:FORGOTTEN_BEAST_454:KIDNEY -#CREATURE:FORGOTTEN_BEAST_455:MUSCLE - CREATURE:FORGOTTEN_BEAST_455:EYE -"CREATURE:FORGOTTEN_BEAST_455:BRAIN -!CREATURE:FORGOTTEN_BEAST_455:LUNG -"CREATURE:FORGOTTEN_BEAST_455:HEART -"CREATURE:FORGOTTEN_BEAST_455:LIVER - CREATURE:FORGOTTEN_BEAST_455:GUT -$CREATURE:FORGOTTEN_BEAST_455:STOMACH -$CREATURE:FORGOTTEN_BEAST_455:GIZZARD -%CREATURE:FORGOTTEN_BEAST_455:PANCREAS -#CREATURE:FORGOTTEN_BEAST_455:SPLEEN -#CREATURE:FORGOTTEN_BEAST_455:KIDNEY -#CREATURE:FORGOTTEN_BEAST_457:MUSCLE - CREATURE:FORGOTTEN_BEAST_457:EYE -"CREATURE:FORGOTTEN_BEAST_457:BRAIN -!CREATURE:FORGOTTEN_BEAST_457:LUNG -"CREATURE:FORGOTTEN_BEAST_457:HEART -"CREATURE:FORGOTTEN_BEAST_457:LIVER - CREATURE:FORGOTTEN_BEAST_457:GUT -$CREATURE:FORGOTTEN_BEAST_457:STOMACH -$CREATURE:FORGOTTEN_BEAST_457:GIZZARD -%CREATURE:FORGOTTEN_BEAST_457:PANCREAS -#CREATURE:FORGOTTEN_BEAST_457:SPLEEN -#CREATURE:FORGOTTEN_BEAST_457:KIDNEY -#CREATURE:FORGOTTEN_BEAST_459:MUSCLE - CREATURE:FORGOTTEN_BEAST_459:EYE -"CREATURE:FORGOTTEN_BEAST_459:BRAIN -!CREATURE:FORGOTTEN_BEAST_459:LUNG -"CREATURE:FORGOTTEN_BEAST_459:HEART -"CREATURE:FORGOTTEN_BEAST_459:LIVER - CREATURE:FORGOTTEN_BEAST_459:GUT -$CREATURE:FORGOTTEN_BEAST_459:STOMACH -$CREATURE:FORGOTTEN_BEAST_459:GIZZARD -%CREATURE:FORGOTTEN_BEAST_459:PANCREAS -#CREATURE:FORGOTTEN_BEAST_459:SPLEEN -#CREATURE:FORGOTTEN_BEAST_459:KIDNEY -#CREATURE:FORGOTTEN_BEAST_461:MUSCLE - CREATURE:FORGOTTEN_BEAST_461:EYE -"CREATURE:FORGOTTEN_BEAST_461:BRAIN -!CREATURE:FORGOTTEN_BEAST_461:LUNG -"CREATURE:FORGOTTEN_BEAST_461:HEART -"CREATURE:FORGOTTEN_BEAST_461:LIVER - CREATURE:FORGOTTEN_BEAST_461:GUT -$CREATURE:FORGOTTEN_BEAST_461:STOMACH -$CREATURE:FORGOTTEN_BEAST_461:GIZZARD -%CREATURE:FORGOTTEN_BEAST_461:PANCREAS -#CREATURE:FORGOTTEN_BEAST_461:SPLEEN -#CREATURE:FORGOTTEN_BEAST_461:KIDNEY -#CREATURE:FORGOTTEN_BEAST_462:MUSCLE - CREATURE:FORGOTTEN_BEAST_462:EYE -"CREATURE:FORGOTTEN_BEAST_462:BRAIN -!CREATURE:FORGOTTEN_BEAST_462:LUNG -"CREATURE:FORGOTTEN_BEAST_462:HEART -"CREATURE:FORGOTTEN_BEAST_462:LIVER - CREATURE:FORGOTTEN_BEAST_462:GUT -$CREATURE:FORGOTTEN_BEAST_462:STOMACH -$CREATURE:FORGOTTEN_BEAST_462:GIZZARD -%CREATURE:FORGOTTEN_BEAST_462:PANCREAS -#CREATURE:FORGOTTEN_BEAST_462:SPLEEN -#CREATURE:FORGOTTEN_BEAST_462:KIDNEY -#CREATURE:FORGOTTEN_BEAST_463:MUSCLE - CREATURE:FORGOTTEN_BEAST_463:EYE -"CREATURE:FORGOTTEN_BEAST_463:BRAIN -!CREATURE:FORGOTTEN_BEAST_463:LUNG -"CREATURE:FORGOTTEN_BEAST_463:HEART -"CREATURE:FORGOTTEN_BEAST_463:LIVER - CREATURE:FORGOTTEN_BEAST_463:GUT -$CREATURE:FORGOTTEN_BEAST_463:STOMACH -$CREATURE:FORGOTTEN_BEAST_463:GIZZARD -%CREATURE:FORGOTTEN_BEAST_463:PANCREAS -#CREATURE:FORGOTTEN_BEAST_463:SPLEEN -#CREATURE:FORGOTTEN_BEAST_463:KIDNEY -#CREATURE:FORGOTTEN_BEAST_465:MUSCLE - CREATURE:FORGOTTEN_BEAST_465:EYE -"CREATURE:FORGOTTEN_BEAST_465:BRAIN -!CREATURE:FORGOTTEN_BEAST_465:LUNG -"CREATURE:FORGOTTEN_BEAST_465:HEART -"CREATURE:FORGOTTEN_BEAST_465:LIVER - CREATURE:FORGOTTEN_BEAST_465:GUT -$CREATURE:FORGOTTEN_BEAST_465:STOMACH -$CREATURE:FORGOTTEN_BEAST_465:GIZZARD -%CREATURE:FORGOTTEN_BEAST_465:PANCREAS -#CREATURE:FORGOTTEN_BEAST_465:SPLEEN -#CREATURE:FORGOTTEN_BEAST_465:KIDNEY -#CREATURE:FORGOTTEN_BEAST_466:MUSCLE - CREATURE:FORGOTTEN_BEAST_466:EYE -"CREATURE:FORGOTTEN_BEAST_466:BRAIN -!CREATURE:FORGOTTEN_BEAST_466:LUNG -"CREATURE:FORGOTTEN_BEAST_466:HEART -"CREATURE:FORGOTTEN_BEAST_466:LIVER - CREATURE:FORGOTTEN_BEAST_466:GUT -$CREATURE:FORGOTTEN_BEAST_466:STOMACH -$CREATURE:FORGOTTEN_BEAST_466:GIZZARD -%CREATURE:FORGOTTEN_BEAST_466:PANCREAS -#CREATURE:FORGOTTEN_BEAST_466:SPLEEN -#CREATURE:FORGOTTEN_BEAST_466:KIDNEY -#CREATURE:FORGOTTEN_BEAST_468:MUSCLE - CREATURE:FORGOTTEN_BEAST_468:EYE -"CREATURE:FORGOTTEN_BEAST_468:BRAIN -!CREATURE:FORGOTTEN_BEAST_468:LUNG -"CREATURE:FORGOTTEN_BEAST_468:HEART -"CREATURE:FORGOTTEN_BEAST_468:LIVER - CREATURE:FORGOTTEN_BEAST_468:GUT -$CREATURE:FORGOTTEN_BEAST_468:STOMACH -$CREATURE:FORGOTTEN_BEAST_468:GIZZARD -%CREATURE:FORGOTTEN_BEAST_468:PANCREAS -#CREATURE:FORGOTTEN_BEAST_468:SPLEEN -#CREATURE:FORGOTTEN_BEAST_468:KIDNEY -#CREATURE:FORGOTTEN_BEAST_469:MUSCLE - CREATURE:FORGOTTEN_BEAST_469:EYE -"CREATURE:FORGOTTEN_BEAST_469:BRAIN -!CREATURE:FORGOTTEN_BEAST_469:LUNG -"CREATURE:FORGOTTEN_BEAST_469:HEART -"CREATURE:FORGOTTEN_BEAST_469:LIVER - CREATURE:FORGOTTEN_BEAST_469:GUT -$CREATURE:FORGOTTEN_BEAST_469:STOMACH -$CREATURE:FORGOTTEN_BEAST_469:GIZZARD -%CREATURE:FORGOTTEN_BEAST_469:PANCREAS -#CREATURE:FORGOTTEN_BEAST_469:SPLEEN -#CREATURE:FORGOTTEN_BEAST_469:KIDNEY -#CREATURE:FORGOTTEN_BEAST_470:MUSCLE - CREATURE:FORGOTTEN_BEAST_470:EYE -"CREATURE:FORGOTTEN_BEAST_470:BRAIN -!CREATURE:FORGOTTEN_BEAST_470:LUNG -"CREATURE:FORGOTTEN_BEAST_470:HEART -"CREATURE:FORGOTTEN_BEAST_470:LIVER - CREATURE:FORGOTTEN_BEAST_470:GUT -$CREATURE:FORGOTTEN_BEAST_470:STOMACH -$CREATURE:FORGOTTEN_BEAST_470:GIZZARD -%CREATURE:FORGOTTEN_BEAST_470:PANCREAS -#CREATURE:FORGOTTEN_BEAST_470:SPLEEN -#CREATURE:FORGOTTEN_BEAST_470:KIDNEY -#CREATURE:FORGOTTEN_BEAST_471:MUSCLE - CREATURE:FORGOTTEN_BEAST_471:EYE -"CREATURE:FORGOTTEN_BEAST_471:BRAIN -!CREATURE:FORGOTTEN_BEAST_471:LUNG -"CREATURE:FORGOTTEN_BEAST_471:HEART -"CREATURE:FORGOTTEN_BEAST_471:LIVER - CREATURE:FORGOTTEN_BEAST_471:GUT -$CREATURE:FORGOTTEN_BEAST_471:STOMACH -$CREATURE:FORGOTTEN_BEAST_471:GIZZARD -%CREATURE:FORGOTTEN_BEAST_471:PANCREAS -#CREATURE:FORGOTTEN_BEAST_471:SPLEEN -#CREATURE:FORGOTTEN_BEAST_471:KIDNEY -#CREATURE:FORGOTTEN_BEAST_472:MUSCLE - CREATURE:FORGOTTEN_BEAST_472:EYE -"CREATURE:FORGOTTEN_BEAST_472:BRAIN -!CREATURE:FORGOTTEN_BEAST_472:LUNG -"CREATURE:FORGOTTEN_BEAST_472:HEART -"CREATURE:FORGOTTEN_BEAST_472:LIVER - CREATURE:FORGOTTEN_BEAST_472:GUT -$CREATURE:FORGOTTEN_BEAST_472:STOMACH -$CREATURE:FORGOTTEN_BEAST_472:GIZZARD -%CREATURE:FORGOTTEN_BEAST_472:PANCREAS -#CREATURE:FORGOTTEN_BEAST_472:SPLEEN -#CREATURE:FORGOTTEN_BEAST_472:KIDNEY -#CREATURE:FORGOTTEN_BEAST_474:MUSCLE - CREATURE:FORGOTTEN_BEAST_474:EYE -"CREATURE:FORGOTTEN_BEAST_474:BRAIN -!CREATURE:FORGOTTEN_BEAST_474:LUNG -"CREATURE:FORGOTTEN_BEAST_474:HEART -"CREATURE:FORGOTTEN_BEAST_474:LIVER - CREATURE:FORGOTTEN_BEAST_474:GUT -$CREATURE:FORGOTTEN_BEAST_474:STOMACH -$CREATURE:FORGOTTEN_BEAST_474:GIZZARD -%CREATURE:FORGOTTEN_BEAST_474:PANCREAS -#CREATURE:FORGOTTEN_BEAST_474:SPLEEN -#CREATURE:FORGOTTEN_BEAST_474:KIDNEY -#CREATURE:FORGOTTEN_BEAST_475:MUSCLE - CREATURE:FORGOTTEN_BEAST_475:EYE -"CREATURE:FORGOTTEN_BEAST_475:BRAIN -!CREATURE:FORGOTTEN_BEAST_475:LUNG -"CREATURE:FORGOTTEN_BEAST_475:HEART -"CREATURE:FORGOTTEN_BEAST_475:LIVER - CREATURE:FORGOTTEN_BEAST_475:GUT -$CREATURE:FORGOTTEN_BEAST_475:STOMACH -$CREATURE:FORGOTTEN_BEAST_475:GIZZARD -%CREATURE:FORGOTTEN_BEAST_475:PANCREAS -#CREATURE:FORGOTTEN_BEAST_475:SPLEEN -#CREATURE:FORGOTTEN_BEAST_475:KIDNEY -#CREATURE:FORGOTTEN_BEAST_476:MUSCLE - CREATURE:FORGOTTEN_BEAST_476:EYE -"CREATURE:FORGOTTEN_BEAST_476:BRAIN -!CREATURE:FORGOTTEN_BEAST_476:LUNG -"CREATURE:FORGOTTEN_BEAST_476:HEART -"CREATURE:FORGOTTEN_BEAST_476:LIVER - CREATURE:FORGOTTEN_BEAST_476:GUT -$CREATURE:FORGOTTEN_BEAST_476:STOMACH -$CREATURE:FORGOTTEN_BEAST_476:GIZZARD -%CREATURE:FORGOTTEN_BEAST_476:PANCREAS -#CREATURE:FORGOTTEN_BEAST_476:SPLEEN -#CREATURE:FORGOTTEN_BEAST_476:KIDNEY -#CREATURE:FORGOTTEN_BEAST_478:MUSCLE - CREATURE:FORGOTTEN_BEAST_478:EYE -"CREATURE:FORGOTTEN_BEAST_478:BRAIN -!CREATURE:FORGOTTEN_BEAST_478:LUNG -"CREATURE:FORGOTTEN_BEAST_478:HEART -"CREATURE:FORGOTTEN_BEAST_478:LIVER - CREATURE:FORGOTTEN_BEAST_478:GUT -$CREATURE:FORGOTTEN_BEAST_478:STOMACH -$CREATURE:FORGOTTEN_BEAST_478:GIZZARD -%CREATURE:FORGOTTEN_BEAST_478:PANCREAS -#CREATURE:FORGOTTEN_BEAST_478:SPLEEN -#CREATURE:FORGOTTEN_BEAST_478:KIDNEY -#CREATURE:FORGOTTEN_BEAST_479:MUSCLE - CREATURE:FORGOTTEN_BEAST_479:EYE -"CREATURE:FORGOTTEN_BEAST_479:BRAIN -!CREATURE:FORGOTTEN_BEAST_479:LUNG -"CREATURE:FORGOTTEN_BEAST_479:HEART -"CREATURE:FORGOTTEN_BEAST_479:LIVER - CREATURE:FORGOTTEN_BEAST_479:GUT -$CREATURE:FORGOTTEN_BEAST_479:STOMACH -$CREATURE:FORGOTTEN_BEAST_479:GIZZARD -%CREATURE:FORGOTTEN_BEAST_479:PANCREAS -#CREATURE:FORGOTTEN_BEAST_479:SPLEEN -#CREATURE:FORGOTTEN_BEAST_479:KIDNEY -#CREATURE:FORGOTTEN_BEAST_480:MUSCLE - CREATURE:FORGOTTEN_BEAST_480:EYE -"CREATURE:FORGOTTEN_BEAST_480:BRAIN -!CREATURE:FORGOTTEN_BEAST_480:LUNG -"CREATURE:FORGOTTEN_BEAST_480:HEART -"CREATURE:FORGOTTEN_BEAST_480:LIVER - CREATURE:FORGOTTEN_BEAST_480:GUT -$CREATURE:FORGOTTEN_BEAST_480:STOMACH -$CREATURE:FORGOTTEN_BEAST_480:GIZZARD -%CREATURE:FORGOTTEN_BEAST_480:PANCREAS -#CREATURE:FORGOTTEN_BEAST_480:SPLEEN -#CREATURE:FORGOTTEN_BEAST_480:KIDNEY -#CREATURE:FORGOTTEN_BEAST_481:MUSCLE - CREATURE:FORGOTTEN_BEAST_481:EYE -"CREATURE:FORGOTTEN_BEAST_481:BRAIN -!CREATURE:FORGOTTEN_BEAST_481:LUNG -"CREATURE:FORGOTTEN_BEAST_481:HEART -"CREATURE:FORGOTTEN_BEAST_481:LIVER - CREATURE:FORGOTTEN_BEAST_481:GUT -$CREATURE:FORGOTTEN_BEAST_481:STOMACH -$CREATURE:FORGOTTEN_BEAST_481:GIZZARD -%CREATURE:FORGOTTEN_BEAST_481:PANCREAS -#CREATURE:FORGOTTEN_BEAST_481:SPLEEN -#CREATURE:FORGOTTEN_BEAST_481:KIDNEY -#CREATURE:FORGOTTEN_BEAST_483:MUSCLE - CREATURE:FORGOTTEN_BEAST_483:EYE -"CREATURE:FORGOTTEN_BEAST_483:BRAIN -!CREATURE:FORGOTTEN_BEAST_483:LUNG -"CREATURE:FORGOTTEN_BEAST_483:HEART -"CREATURE:FORGOTTEN_BEAST_483:LIVER - CREATURE:FORGOTTEN_BEAST_483:GUT -$CREATURE:FORGOTTEN_BEAST_483:STOMACH -$CREATURE:FORGOTTEN_BEAST_483:GIZZARD -%CREATURE:FORGOTTEN_BEAST_483:PANCREAS -#CREATURE:FORGOTTEN_BEAST_483:SPLEEN -#CREATURE:FORGOTTEN_BEAST_483:KIDNEY -#CREATURE:FORGOTTEN_BEAST_486:MUSCLE - CREATURE:FORGOTTEN_BEAST_486:EYE -"CREATURE:FORGOTTEN_BEAST_486:BRAIN -!CREATURE:FORGOTTEN_BEAST_486:LUNG -"CREATURE:FORGOTTEN_BEAST_486:HEART -"CREATURE:FORGOTTEN_BEAST_486:LIVER - CREATURE:FORGOTTEN_BEAST_486:GUT -$CREATURE:FORGOTTEN_BEAST_486:STOMACH -$CREATURE:FORGOTTEN_BEAST_486:GIZZARD -%CREATURE:FORGOTTEN_BEAST_486:PANCREAS -#CREATURE:FORGOTTEN_BEAST_486:SPLEEN -#CREATURE:FORGOTTEN_BEAST_486:KIDNEY -#CREATURE:FORGOTTEN_BEAST_487:MUSCLE - CREATURE:FORGOTTEN_BEAST_487:EYE -"CREATURE:FORGOTTEN_BEAST_487:BRAIN -!CREATURE:FORGOTTEN_BEAST_487:LUNG -"CREATURE:FORGOTTEN_BEAST_487:HEART -"CREATURE:FORGOTTEN_BEAST_487:LIVER - CREATURE:FORGOTTEN_BEAST_487:GUT -$CREATURE:FORGOTTEN_BEAST_487:STOMACH -$CREATURE:FORGOTTEN_BEAST_487:GIZZARD -%CREATURE:FORGOTTEN_BEAST_487:PANCREAS -#CREATURE:FORGOTTEN_BEAST_487:SPLEEN -#CREATURE:FORGOTTEN_BEAST_487:KIDNEY -#CREATURE:FORGOTTEN_BEAST_489:MUSCLE - CREATURE:FORGOTTEN_BEAST_489:EYE -"CREATURE:FORGOTTEN_BEAST_489:BRAIN -!CREATURE:FORGOTTEN_BEAST_489:LUNG -"CREATURE:FORGOTTEN_BEAST_489:HEART -"CREATURE:FORGOTTEN_BEAST_489:LIVER - CREATURE:FORGOTTEN_BEAST_489:GUT -$CREATURE:FORGOTTEN_BEAST_489:STOMACH -$CREATURE:FORGOTTEN_BEAST_489:GIZZARD -%CREATURE:FORGOTTEN_BEAST_489:PANCREAS -#CREATURE:FORGOTTEN_BEAST_489:SPLEEN -#CREATURE:FORGOTTEN_BEAST_489:KIDNEY -#CREATURE:FORGOTTEN_BEAST_492:MUSCLE - CREATURE:FORGOTTEN_BEAST_492:EYE -"CREATURE:FORGOTTEN_BEAST_492:BRAIN -!CREATURE:FORGOTTEN_BEAST_492:LUNG -"CREATURE:FORGOTTEN_BEAST_492:HEART -"CREATURE:FORGOTTEN_BEAST_492:LIVER - CREATURE:FORGOTTEN_BEAST_492:GUT -$CREATURE:FORGOTTEN_BEAST_492:STOMACH -$CREATURE:FORGOTTEN_BEAST_492:GIZZARD -%CREATURE:FORGOTTEN_BEAST_492:PANCREAS -#CREATURE:FORGOTTEN_BEAST_492:SPLEEN -#CREATURE:FORGOTTEN_BEAST_492:KIDNEY -#CREATURE:FORGOTTEN_BEAST_494:MUSCLE - CREATURE:FORGOTTEN_BEAST_494:EYE -"CREATURE:FORGOTTEN_BEAST_494:BRAIN -!CREATURE:FORGOTTEN_BEAST_494:LUNG -"CREATURE:FORGOTTEN_BEAST_494:HEART -"CREATURE:FORGOTTEN_BEAST_494:LIVER - CREATURE:FORGOTTEN_BEAST_494:GUT -$CREATURE:FORGOTTEN_BEAST_494:STOMACH -$CREATURE:FORGOTTEN_BEAST_494:GIZZARD -%CREATURE:FORGOTTEN_BEAST_494:PANCREAS -#CREATURE:FORGOTTEN_BEAST_494:SPLEEN -#CREATURE:FORGOTTEN_BEAST_494:KIDNEY -#CREATURE:FORGOTTEN_BEAST_495:MUSCLE - CREATURE:FORGOTTEN_BEAST_495:EYE -"CREATURE:FORGOTTEN_BEAST_495:BRAIN -!CREATURE:FORGOTTEN_BEAST_495:LUNG -"CREATURE:FORGOTTEN_BEAST_495:HEART -"CREATURE:FORGOTTEN_BEAST_495:LIVER - CREATURE:FORGOTTEN_BEAST_495:GUT -$CREATURE:FORGOTTEN_BEAST_495:STOMACH -$CREATURE:FORGOTTEN_BEAST_495:GIZZARD -%CREATURE:FORGOTTEN_BEAST_495:PANCREAS -#CREATURE:FORGOTTEN_BEAST_495:SPLEEN -#CREATURE:FORGOTTEN_BEAST_495:KIDNEY -#CREATURE:FORGOTTEN_BEAST_496:MUSCLE - CREATURE:FORGOTTEN_BEAST_496:EYE -"CREATURE:FORGOTTEN_BEAST_496:BRAIN -!CREATURE:FORGOTTEN_BEAST_496:LUNG -"CREATURE:FORGOTTEN_BEAST_496:HEART -"CREATURE:FORGOTTEN_BEAST_496:LIVER - CREATURE:FORGOTTEN_BEAST_496:GUT -$CREATURE:FORGOTTEN_BEAST_496:STOMACH -$CREATURE:FORGOTTEN_BEAST_496:GIZZARD -%CREATURE:FORGOTTEN_BEAST_496:PANCREAS -#CREATURE:FORGOTTEN_BEAST_496:SPLEEN -#CREATURE:FORGOTTEN_BEAST_496:KIDNEY -#CREATURE:FORGOTTEN_BEAST_497:MUSCLE - CREATURE:FORGOTTEN_BEAST_497:EYE -"CREATURE:FORGOTTEN_BEAST_497:BRAIN -!CREATURE:FORGOTTEN_BEAST_497:LUNG -"CREATURE:FORGOTTEN_BEAST_497:HEART -"CREATURE:FORGOTTEN_BEAST_497:LIVER - CREATURE:FORGOTTEN_BEAST_497:GUT -$CREATURE:FORGOTTEN_BEAST_497:STOMACH -$CREATURE:FORGOTTEN_BEAST_497:GIZZARD -%CREATURE:FORGOTTEN_BEAST_497:PANCREAS -#CREATURE:FORGOTTEN_BEAST_497:SPLEEN -#CREATURE:FORGOTTEN_BEAST_497:KIDNEY -#CREATURE:FORGOTTEN_BEAST_498:MUSCLE - CREATURE:FORGOTTEN_BEAST_498:EYE -"CREATURE:FORGOTTEN_BEAST_498:BRAIN -!CREATURE:FORGOTTEN_BEAST_498:LUNG -"CREATURE:FORGOTTEN_BEAST_498:HEART -"CREATURE:FORGOTTEN_BEAST_498:LIVER - CREATURE:FORGOTTEN_BEAST_498:GUT -$CREATURE:FORGOTTEN_BEAST_498:STOMACH -$CREATURE:FORGOTTEN_BEAST_498:GIZZARD -%CREATURE:FORGOTTEN_BEAST_498:PANCREAS -#CREATURE:FORGOTTEN_BEAST_498:SPLEEN -#CREATURE:FORGOTTEN_BEAST_498:KIDNEY -#CREATURE:FORGOTTEN_BEAST_499:MUSCLE - CREATURE:FORGOTTEN_BEAST_499:EYE -"CREATURE:FORGOTTEN_BEAST_499:BRAIN -!CREATURE:FORGOTTEN_BEAST_499:LUNG -"CREATURE:FORGOTTEN_BEAST_499:HEART -"CREATURE:FORGOTTEN_BEAST_499:LIVER - CREATURE:FORGOTTEN_BEAST_499:GUT -$CREATURE:FORGOTTEN_BEAST_499:STOMACH -$CREATURE:FORGOTTEN_BEAST_499:GIZZARD -%CREATURE:FORGOTTEN_BEAST_499:PANCREAS -#CREATURE:FORGOTTEN_BEAST_499:SPLEEN -#CREATURE:FORGOTTEN_BEAST_499:KIDNEY -#CREATURE:FORGOTTEN_BEAST_501:MUSCLE - CREATURE:FORGOTTEN_BEAST_501:EYE -"CREATURE:FORGOTTEN_BEAST_501:BRAIN -!CREATURE:FORGOTTEN_BEAST_501:LUNG -"CREATURE:FORGOTTEN_BEAST_501:HEART -"CREATURE:FORGOTTEN_BEAST_501:LIVER - CREATURE:FORGOTTEN_BEAST_501:GUT -$CREATURE:FORGOTTEN_BEAST_501:STOMACH -$CREATURE:FORGOTTEN_BEAST_501:GIZZARD -%CREATURE:FORGOTTEN_BEAST_501:PANCREAS -#CREATURE:FORGOTTEN_BEAST_501:SPLEEN -#CREATURE:FORGOTTEN_BEAST_501:KIDNEY -#CREATURE:FORGOTTEN_BEAST_503:MUSCLE - CREATURE:FORGOTTEN_BEAST_503:EYE -"CREATURE:FORGOTTEN_BEAST_503:BRAIN -!CREATURE:FORGOTTEN_BEAST_503:LUNG -"CREATURE:FORGOTTEN_BEAST_503:HEART -"CREATURE:FORGOTTEN_BEAST_503:LIVER - CREATURE:FORGOTTEN_BEAST_503:GUT -$CREATURE:FORGOTTEN_BEAST_503:STOMACH -$CREATURE:FORGOTTEN_BEAST_503:GIZZARD -%CREATURE:FORGOTTEN_BEAST_503:PANCREAS -#CREATURE:FORGOTTEN_BEAST_503:SPLEEN -#CREATURE:FORGOTTEN_BEAST_503:KIDNEY -#CREATURE:FORGOTTEN_BEAST_504:MUSCLE - CREATURE:FORGOTTEN_BEAST_504:EYE -"CREATURE:FORGOTTEN_BEAST_504:BRAIN -!CREATURE:FORGOTTEN_BEAST_504:LUNG -"CREATURE:FORGOTTEN_BEAST_504:HEART -"CREATURE:FORGOTTEN_BEAST_504:LIVER - CREATURE:FORGOTTEN_BEAST_504:GUT -$CREATURE:FORGOTTEN_BEAST_504:STOMACH -$CREATURE:FORGOTTEN_BEAST_504:GIZZARD -%CREATURE:FORGOTTEN_BEAST_504:PANCREAS -#CREATURE:FORGOTTEN_BEAST_504:SPLEEN -#CREATURE:FORGOTTEN_BEAST_504:KIDNEY -#CREATURE:FORGOTTEN_BEAST_505:MUSCLE - CREATURE:FORGOTTEN_BEAST_505:EYE -"CREATURE:FORGOTTEN_BEAST_505:BRAIN -!CREATURE:FORGOTTEN_BEAST_505:LUNG -"CREATURE:FORGOTTEN_BEAST_505:HEART -"CREATURE:FORGOTTEN_BEAST_505:LIVER - CREATURE:FORGOTTEN_BEAST_505:GUT -$CREATURE:FORGOTTEN_BEAST_505:STOMACH -$CREATURE:FORGOTTEN_BEAST_505:GIZZARD -%CREATURE:FORGOTTEN_BEAST_505:PANCREAS -#CREATURE:FORGOTTEN_BEAST_505:SPLEEN -#CREATURE:FORGOTTEN_BEAST_505:KIDNEY -#CREATURE:FORGOTTEN_BEAST_507:MUSCLE - CREATURE:FORGOTTEN_BEAST_507:EYE -"CREATURE:FORGOTTEN_BEAST_507:BRAIN -!CREATURE:FORGOTTEN_BEAST_507:LUNG -"CREATURE:FORGOTTEN_BEAST_507:HEART -"CREATURE:FORGOTTEN_BEAST_507:LIVER - CREATURE:FORGOTTEN_BEAST_507:GUT -$CREATURE:FORGOTTEN_BEAST_507:STOMACH -$CREATURE:FORGOTTEN_BEAST_507:GIZZARD -%CREATURE:FORGOTTEN_BEAST_507:PANCREAS -#CREATURE:FORGOTTEN_BEAST_507:SPLEEN -#CREATURE:FORGOTTEN_BEAST_507:KIDNEY -#CREATURE:FORGOTTEN_BEAST_508:MUSCLE - CREATURE:FORGOTTEN_BEAST_508:EYE -"CREATURE:FORGOTTEN_BEAST_508:BRAIN -!CREATURE:FORGOTTEN_BEAST_508:LUNG -"CREATURE:FORGOTTEN_BEAST_508:HEART -"CREATURE:FORGOTTEN_BEAST_508:LIVER - CREATURE:FORGOTTEN_BEAST_508:GUT -$CREATURE:FORGOTTEN_BEAST_508:STOMACH -$CREATURE:FORGOTTEN_BEAST_508:GIZZARD -%CREATURE:FORGOTTEN_BEAST_508:PANCREAS -#CREATURE:FORGOTTEN_BEAST_508:SPLEEN -#CREATURE:FORGOTTEN_BEAST_508:KIDNEY -#CREATURE:FORGOTTEN_BEAST_510:MUSCLE - CREATURE:FORGOTTEN_BEAST_510:EYE -"CREATURE:FORGOTTEN_BEAST_510:BRAIN -!CREATURE:FORGOTTEN_BEAST_510:LUNG -"CREATURE:FORGOTTEN_BEAST_510:HEART -"CREATURE:FORGOTTEN_BEAST_510:LIVER - CREATURE:FORGOTTEN_BEAST_510:GUT -$CREATURE:FORGOTTEN_BEAST_510:STOMACH -$CREATURE:FORGOTTEN_BEAST_510:GIZZARD -%CREATURE:FORGOTTEN_BEAST_510:PANCREAS -#CREATURE:FORGOTTEN_BEAST_510:SPLEEN -#CREATURE:FORGOTTEN_BEAST_510:KIDNEY -#CREATURE:FORGOTTEN_BEAST_512:MUSCLE - CREATURE:FORGOTTEN_BEAST_512:EYE -"CREATURE:FORGOTTEN_BEAST_512:BRAIN -!CREATURE:FORGOTTEN_BEAST_512:LUNG -"CREATURE:FORGOTTEN_BEAST_512:HEART -"CREATURE:FORGOTTEN_BEAST_512:LIVER - CREATURE:FORGOTTEN_BEAST_512:GUT -$CREATURE:FORGOTTEN_BEAST_512:STOMACH -$CREATURE:FORGOTTEN_BEAST_512:GIZZARD -%CREATURE:FORGOTTEN_BEAST_512:PANCREAS -#CREATURE:FORGOTTEN_BEAST_512:SPLEEN -#CREATURE:FORGOTTEN_BEAST_512:KIDNEY -#CREATURE:FORGOTTEN_BEAST_513:MUSCLE - CREATURE:FORGOTTEN_BEAST_513:EYE -"CREATURE:FORGOTTEN_BEAST_513:BRAIN -!CREATURE:FORGOTTEN_BEAST_513:LUNG -"CREATURE:FORGOTTEN_BEAST_513:HEART -"CREATURE:FORGOTTEN_BEAST_513:LIVER - CREATURE:FORGOTTEN_BEAST_513:GUT -$CREATURE:FORGOTTEN_BEAST_513:STOMACH -$CREATURE:FORGOTTEN_BEAST_513:GIZZARD -%CREATURE:FORGOTTEN_BEAST_513:PANCREAS -#CREATURE:FORGOTTEN_BEAST_513:SPLEEN -#CREATURE:FORGOTTEN_BEAST_513:KIDNEY -#CREATURE:FORGOTTEN_BEAST_514:MUSCLE - CREATURE:FORGOTTEN_BEAST_514:EYE -"CREATURE:FORGOTTEN_BEAST_514:BRAIN -!CREATURE:FORGOTTEN_BEAST_514:LUNG -"CREATURE:FORGOTTEN_BEAST_514:HEART -"CREATURE:FORGOTTEN_BEAST_514:LIVER - CREATURE:FORGOTTEN_BEAST_514:GUT -$CREATURE:FORGOTTEN_BEAST_514:STOMACH -$CREATURE:FORGOTTEN_BEAST_514:GIZZARD -%CREATURE:FORGOTTEN_BEAST_514:PANCREAS -#CREATURE:FORGOTTEN_BEAST_514:SPLEEN -#CREATURE:FORGOTTEN_BEAST_514:KIDNEY -#CREATURE:FORGOTTEN_BEAST_515:MUSCLE - CREATURE:FORGOTTEN_BEAST_515:EYE -"CREATURE:FORGOTTEN_BEAST_515:BRAIN -!CREATURE:FORGOTTEN_BEAST_515:LUNG -"CREATURE:FORGOTTEN_BEAST_515:HEART -"CREATURE:FORGOTTEN_BEAST_515:LIVER - CREATURE:FORGOTTEN_BEAST_515:GUT -$CREATURE:FORGOTTEN_BEAST_515:STOMACH -$CREATURE:FORGOTTEN_BEAST_515:GIZZARD -%CREATURE:FORGOTTEN_BEAST_515:PANCREAS -#CREATURE:FORGOTTEN_BEAST_515:SPLEEN -#CREATURE:FORGOTTEN_BEAST_515:KIDNEY -#CREATURE:FORGOTTEN_BEAST_516:MUSCLE - CREATURE:FORGOTTEN_BEAST_516:EYE -"CREATURE:FORGOTTEN_BEAST_516:BRAIN -!CREATURE:FORGOTTEN_BEAST_516:LUNG -"CREATURE:FORGOTTEN_BEAST_516:HEART -"CREATURE:FORGOTTEN_BEAST_516:LIVER - CREATURE:FORGOTTEN_BEAST_516:GUT -$CREATURE:FORGOTTEN_BEAST_516:STOMACH -$CREATURE:FORGOTTEN_BEAST_516:GIZZARD -%CREATURE:FORGOTTEN_BEAST_516:PANCREAS -#CREATURE:FORGOTTEN_BEAST_516:SPLEEN -#CREATURE:FORGOTTEN_BEAST_516:KIDNEY -#CREATURE:FORGOTTEN_BEAST_517:MUSCLE - CREATURE:FORGOTTEN_BEAST_517:EYE -"CREATURE:FORGOTTEN_BEAST_517:BRAIN -!CREATURE:FORGOTTEN_BEAST_517:LUNG -"CREATURE:FORGOTTEN_BEAST_517:HEART -"CREATURE:FORGOTTEN_BEAST_517:LIVER - CREATURE:FORGOTTEN_BEAST_517:GUT -$CREATURE:FORGOTTEN_BEAST_517:STOMACH -$CREATURE:FORGOTTEN_BEAST_517:GIZZARD -%CREATURE:FORGOTTEN_BEAST_517:PANCREAS -#CREATURE:FORGOTTEN_BEAST_517:SPLEEN -#CREATURE:FORGOTTEN_BEAST_517:KIDNEY -#CREATURE:FORGOTTEN_BEAST_518:MUSCLE - CREATURE:FORGOTTEN_BEAST_518:EYE -"CREATURE:FORGOTTEN_BEAST_518:BRAIN -!CREATURE:FORGOTTEN_BEAST_518:LUNG -"CREATURE:FORGOTTEN_BEAST_518:HEART -"CREATURE:FORGOTTEN_BEAST_518:LIVER - CREATURE:FORGOTTEN_BEAST_518:GUT -$CREATURE:FORGOTTEN_BEAST_518:STOMACH -$CREATURE:FORGOTTEN_BEAST_518:GIZZARD -%CREATURE:FORGOTTEN_BEAST_518:PANCREAS -#CREATURE:FORGOTTEN_BEAST_518:SPLEEN -#CREATURE:FORGOTTEN_BEAST_518:KIDNEY -#CREATURE:FORGOTTEN_BEAST_519:MUSCLE - CREATURE:FORGOTTEN_BEAST_519:EYE -"CREATURE:FORGOTTEN_BEAST_519:BRAIN -!CREATURE:FORGOTTEN_BEAST_519:LUNG -"CREATURE:FORGOTTEN_BEAST_519:HEART -"CREATURE:FORGOTTEN_BEAST_519:LIVER - CREATURE:FORGOTTEN_BEAST_519:GUT -$CREATURE:FORGOTTEN_BEAST_519:STOMACH -$CREATURE:FORGOTTEN_BEAST_519:GIZZARD -%CREATURE:FORGOTTEN_BEAST_519:PANCREAS -#CREATURE:FORGOTTEN_BEAST_519:SPLEEN -#CREATURE:FORGOTTEN_BEAST_519:KIDNEY -#CREATURE:FORGOTTEN_BEAST_520:MUSCLE - CREATURE:FORGOTTEN_BEAST_520:EYE -"CREATURE:FORGOTTEN_BEAST_520:BRAIN -!CREATURE:FORGOTTEN_BEAST_520:LUNG -"CREATURE:FORGOTTEN_BEAST_520:HEART -"CREATURE:FORGOTTEN_BEAST_520:LIVER - CREATURE:FORGOTTEN_BEAST_520:GUT -$CREATURE:FORGOTTEN_BEAST_520:STOMACH -$CREATURE:FORGOTTEN_BEAST_520:GIZZARD -%CREATURE:FORGOTTEN_BEAST_520:PANCREAS -#CREATURE:FORGOTTEN_BEAST_520:SPLEEN -#CREATURE:FORGOTTEN_BEAST_520:KIDNEY -#CREATURE:FORGOTTEN_BEAST_521:MUSCLE - CREATURE:FORGOTTEN_BEAST_521:EYE -"CREATURE:FORGOTTEN_BEAST_521:BRAIN -!CREATURE:FORGOTTEN_BEAST_521:LUNG -"CREATURE:FORGOTTEN_BEAST_521:HEART -"CREATURE:FORGOTTEN_BEAST_521:LIVER - CREATURE:FORGOTTEN_BEAST_521:GUT -$CREATURE:FORGOTTEN_BEAST_521:STOMACH -$CREATURE:FORGOTTEN_BEAST_521:GIZZARD -%CREATURE:FORGOTTEN_BEAST_521:PANCREAS -#CREATURE:FORGOTTEN_BEAST_521:SPLEEN -#CREATURE:FORGOTTEN_BEAST_521:KIDNEY -#CREATURE:FORGOTTEN_BEAST_522:MUSCLE - CREATURE:FORGOTTEN_BEAST_522:EYE -"CREATURE:FORGOTTEN_BEAST_522:BRAIN -!CREATURE:FORGOTTEN_BEAST_522:LUNG -"CREATURE:FORGOTTEN_BEAST_522:HEART -"CREATURE:FORGOTTEN_BEAST_522:LIVER - CREATURE:FORGOTTEN_BEAST_522:GUT -$CREATURE:FORGOTTEN_BEAST_522:STOMACH -$CREATURE:FORGOTTEN_BEAST_522:GIZZARD -%CREATURE:FORGOTTEN_BEAST_522:PANCREAS -#CREATURE:FORGOTTEN_BEAST_522:SPLEEN -#CREATURE:FORGOTTEN_BEAST_522:KIDNEY -#CREATURE:FORGOTTEN_BEAST_523:MUSCLE - CREATURE:FORGOTTEN_BEAST_523:EYE -"CREATURE:FORGOTTEN_BEAST_523:BRAIN -!CREATURE:FORGOTTEN_BEAST_523:LUNG -"CREATURE:FORGOTTEN_BEAST_523:HEART -"CREATURE:FORGOTTEN_BEAST_523:LIVER - CREATURE:FORGOTTEN_BEAST_523:GUT -$CREATURE:FORGOTTEN_BEAST_523:STOMACH -$CREATURE:FORGOTTEN_BEAST_523:GIZZARD -%CREATURE:FORGOTTEN_BEAST_523:PANCREAS -#CREATURE:FORGOTTEN_BEAST_523:SPLEEN -#CREATURE:FORGOTTEN_BEAST_523:KIDNEY -#CREATURE:FORGOTTEN_BEAST_525:MUSCLE - CREATURE:FORGOTTEN_BEAST_525:EYE -"CREATURE:FORGOTTEN_BEAST_525:BRAIN -!CREATURE:FORGOTTEN_BEAST_525:LUNG -"CREATURE:FORGOTTEN_BEAST_525:HEART -"CREATURE:FORGOTTEN_BEAST_525:LIVER - CREATURE:FORGOTTEN_BEAST_525:GUT -$CREATURE:FORGOTTEN_BEAST_525:STOMACH -$CREATURE:FORGOTTEN_BEAST_525:GIZZARD -%CREATURE:FORGOTTEN_BEAST_525:PANCREAS -#CREATURE:FORGOTTEN_BEAST_525:SPLEEN -#CREATURE:FORGOTTEN_BEAST_525:KIDNEY -#CREATURE:FORGOTTEN_BEAST_526:MUSCLE - CREATURE:FORGOTTEN_BEAST_526:EYE -"CREATURE:FORGOTTEN_BEAST_526:BRAIN -!CREATURE:FORGOTTEN_BEAST_526:LUNG -"CREATURE:FORGOTTEN_BEAST_526:HEART -"CREATURE:FORGOTTEN_BEAST_526:LIVER - CREATURE:FORGOTTEN_BEAST_526:GUT -$CREATURE:FORGOTTEN_BEAST_526:STOMACH -$CREATURE:FORGOTTEN_BEAST_526:GIZZARD -%CREATURE:FORGOTTEN_BEAST_526:PANCREAS -#CREATURE:FORGOTTEN_BEAST_526:SPLEEN -#CREATURE:FORGOTTEN_BEAST_526:KIDNEY -#CREATURE:FORGOTTEN_BEAST_527:MUSCLE - CREATURE:FORGOTTEN_BEAST_527:EYE -"CREATURE:FORGOTTEN_BEAST_527:BRAIN -!CREATURE:FORGOTTEN_BEAST_527:LUNG -"CREATURE:FORGOTTEN_BEAST_527:HEART -"CREATURE:FORGOTTEN_BEAST_527:LIVER - CREATURE:FORGOTTEN_BEAST_527:GUT -$CREATURE:FORGOTTEN_BEAST_527:STOMACH -$CREATURE:FORGOTTEN_BEAST_527:GIZZARD -%CREATURE:FORGOTTEN_BEAST_527:PANCREAS -#CREATURE:FORGOTTEN_BEAST_527:SPLEEN -#CREATURE:FORGOTTEN_BEAST_527:KIDNEY -#CREATURE:FORGOTTEN_BEAST_528:MUSCLE - CREATURE:FORGOTTEN_BEAST_528:EYE -"CREATURE:FORGOTTEN_BEAST_528:BRAIN -!CREATURE:FORGOTTEN_BEAST_528:LUNG -"CREATURE:FORGOTTEN_BEAST_528:HEART -"CREATURE:FORGOTTEN_BEAST_528:LIVER - CREATURE:FORGOTTEN_BEAST_528:GUT -$CREATURE:FORGOTTEN_BEAST_528:STOMACH -$CREATURE:FORGOTTEN_BEAST_528:GIZZARD -%CREATURE:FORGOTTEN_BEAST_528:PANCREAS -#CREATURE:FORGOTTEN_BEAST_528:SPLEEN -#CREATURE:FORGOTTEN_BEAST_528:KIDNEY -#CREATURE:FORGOTTEN_BEAST_529:MUSCLE - CREATURE:FORGOTTEN_BEAST_529:EYE -"CREATURE:FORGOTTEN_BEAST_529:BRAIN -!CREATURE:FORGOTTEN_BEAST_529:LUNG -"CREATURE:FORGOTTEN_BEAST_529:HEART -"CREATURE:FORGOTTEN_BEAST_529:LIVER - CREATURE:FORGOTTEN_BEAST_529:GUT -$CREATURE:FORGOTTEN_BEAST_529:STOMACH -$CREATURE:FORGOTTEN_BEAST_529:GIZZARD -%CREATURE:FORGOTTEN_BEAST_529:PANCREAS -#CREATURE:FORGOTTEN_BEAST_529:SPLEEN -#CREATURE:FORGOTTEN_BEAST_529:KIDNEY -#CREATURE:FORGOTTEN_BEAST_530:MUSCLE - CREATURE:FORGOTTEN_BEAST_530:EYE -"CREATURE:FORGOTTEN_BEAST_530:BRAIN -!CREATURE:FORGOTTEN_BEAST_530:LUNG -"CREATURE:FORGOTTEN_BEAST_530:HEART -"CREATURE:FORGOTTEN_BEAST_530:LIVER - CREATURE:FORGOTTEN_BEAST_530:GUT -$CREATURE:FORGOTTEN_BEAST_530:STOMACH -$CREATURE:FORGOTTEN_BEAST_530:GIZZARD -%CREATURE:FORGOTTEN_BEAST_530:PANCREAS -#CREATURE:FORGOTTEN_BEAST_530:SPLEEN -#CREATURE:FORGOTTEN_BEAST_530:KIDNEY -#CREATURE:FORGOTTEN_BEAST_531:MUSCLE - CREATURE:FORGOTTEN_BEAST_531:EYE -"CREATURE:FORGOTTEN_BEAST_531:BRAIN -!CREATURE:FORGOTTEN_BEAST_531:LUNG -"CREATURE:FORGOTTEN_BEAST_531:HEART -"CREATURE:FORGOTTEN_BEAST_531:LIVER - CREATURE:FORGOTTEN_BEAST_531:GUT -$CREATURE:FORGOTTEN_BEAST_531:STOMACH -$CREATURE:FORGOTTEN_BEAST_531:GIZZARD -%CREATURE:FORGOTTEN_BEAST_531:PANCREAS -#CREATURE:FORGOTTEN_BEAST_531:SPLEEN -#CREATURE:FORGOTTEN_BEAST_531:KIDNEY -#CREATURE:FORGOTTEN_BEAST_532:MUSCLE - CREATURE:FORGOTTEN_BEAST_532:EYE -"CREATURE:FORGOTTEN_BEAST_532:BRAIN -!CREATURE:FORGOTTEN_BEAST_532:LUNG -"CREATURE:FORGOTTEN_BEAST_532:HEART -"CREATURE:FORGOTTEN_BEAST_532:LIVER - CREATURE:FORGOTTEN_BEAST_532:GUT -$CREATURE:FORGOTTEN_BEAST_532:STOMACH -$CREATURE:FORGOTTEN_BEAST_532:GIZZARD -%CREATURE:FORGOTTEN_BEAST_532:PANCREAS -#CREATURE:FORGOTTEN_BEAST_532:SPLEEN -#CREATURE:FORGOTTEN_BEAST_532:KIDNEY -#CREATURE:FORGOTTEN_BEAST_533:MUSCLE - CREATURE:FORGOTTEN_BEAST_533:EYE -"CREATURE:FORGOTTEN_BEAST_533:BRAIN -!CREATURE:FORGOTTEN_BEAST_533:LUNG -"CREATURE:FORGOTTEN_BEAST_533:HEART -"CREATURE:FORGOTTEN_BEAST_533:LIVER - CREATURE:FORGOTTEN_BEAST_533:GUT -$CREATURE:FORGOTTEN_BEAST_533:STOMACH -$CREATURE:FORGOTTEN_BEAST_533:GIZZARD -%CREATURE:FORGOTTEN_BEAST_533:PANCREAS -#CREATURE:FORGOTTEN_BEAST_533:SPLEEN -#CREATURE:FORGOTTEN_BEAST_533:KIDNEY -#CREATURE:FORGOTTEN_BEAST_534:MUSCLE - CREATURE:FORGOTTEN_BEAST_534:EYE -"CREATURE:FORGOTTEN_BEAST_534:BRAIN -!CREATURE:FORGOTTEN_BEAST_534:LUNG -"CREATURE:FORGOTTEN_BEAST_534:HEART -"CREATURE:FORGOTTEN_BEAST_534:LIVER - CREATURE:FORGOTTEN_BEAST_534:GUT -$CREATURE:FORGOTTEN_BEAST_534:STOMACH -$CREATURE:FORGOTTEN_BEAST_534:GIZZARD -%CREATURE:FORGOTTEN_BEAST_534:PANCREAS -#CREATURE:FORGOTTEN_BEAST_534:SPLEEN -#CREATURE:FORGOTTEN_BEAST_534:KIDNEY -#CREATURE:FORGOTTEN_BEAST_535:MUSCLE - CREATURE:FORGOTTEN_BEAST_535:EYE -"CREATURE:FORGOTTEN_BEAST_535:BRAIN -!CREATURE:FORGOTTEN_BEAST_535:LUNG -"CREATURE:FORGOTTEN_BEAST_535:HEART -"CREATURE:FORGOTTEN_BEAST_535:LIVER - CREATURE:FORGOTTEN_BEAST_535:GUT -$CREATURE:FORGOTTEN_BEAST_535:STOMACH -$CREATURE:FORGOTTEN_BEAST_535:GIZZARD -%CREATURE:FORGOTTEN_BEAST_535:PANCREAS -#CREATURE:FORGOTTEN_BEAST_535:SPLEEN -#CREATURE:FORGOTTEN_BEAST_535:KIDNEY -#CREATURE:FORGOTTEN_BEAST_536:MUSCLE - CREATURE:FORGOTTEN_BEAST_536:EYE -"CREATURE:FORGOTTEN_BEAST_536:BRAIN -!CREATURE:FORGOTTEN_BEAST_536:LUNG -"CREATURE:FORGOTTEN_BEAST_536:HEART -"CREATURE:FORGOTTEN_BEAST_536:LIVER - CREATURE:FORGOTTEN_BEAST_536:GUT -$CREATURE:FORGOTTEN_BEAST_536:STOMACH -$CREATURE:FORGOTTEN_BEAST_536:GIZZARD -%CREATURE:FORGOTTEN_BEAST_536:PANCREAS -#CREATURE:FORGOTTEN_BEAST_536:SPLEEN -#CREATURE:FORGOTTEN_BEAST_536:KIDNEY -#CREATURE:FORGOTTEN_BEAST_539:MUSCLE - CREATURE:FORGOTTEN_BEAST_539:EYE -"CREATURE:FORGOTTEN_BEAST_539:BRAIN -!CREATURE:FORGOTTEN_BEAST_539:LUNG -"CREATURE:FORGOTTEN_BEAST_539:HEART -"CREATURE:FORGOTTEN_BEAST_539:LIVER - CREATURE:FORGOTTEN_BEAST_539:GUT -$CREATURE:FORGOTTEN_BEAST_539:STOMACH -$CREATURE:FORGOTTEN_BEAST_539:GIZZARD -%CREATURE:FORGOTTEN_BEAST_539:PANCREAS -#CREATURE:FORGOTTEN_BEAST_539:SPLEEN -#CREATURE:FORGOTTEN_BEAST_539:KIDNEY -#CREATURE:FORGOTTEN_BEAST_540:MUSCLE - CREATURE:FORGOTTEN_BEAST_540:EYE -"CREATURE:FORGOTTEN_BEAST_540:BRAIN -!CREATURE:FORGOTTEN_BEAST_540:LUNG -"CREATURE:FORGOTTEN_BEAST_540:HEART -"CREATURE:FORGOTTEN_BEAST_540:LIVER - CREATURE:FORGOTTEN_BEAST_540:GUT -$CREATURE:FORGOTTEN_BEAST_540:STOMACH -$CREATURE:FORGOTTEN_BEAST_540:GIZZARD -%CREATURE:FORGOTTEN_BEAST_540:PANCREAS -#CREATURE:FORGOTTEN_BEAST_540:SPLEEN -#CREATURE:FORGOTTEN_BEAST_540:KIDNEY -#CREATURE:FORGOTTEN_BEAST_541:MUSCLE - CREATURE:FORGOTTEN_BEAST_541:EYE -"CREATURE:FORGOTTEN_BEAST_541:BRAIN -!CREATURE:FORGOTTEN_BEAST_541:LUNG -"CREATURE:FORGOTTEN_BEAST_541:HEART -"CREATURE:FORGOTTEN_BEAST_541:LIVER - CREATURE:FORGOTTEN_BEAST_541:GUT -$CREATURE:FORGOTTEN_BEAST_541:STOMACH -$CREATURE:FORGOTTEN_BEAST_541:GIZZARD -%CREATURE:FORGOTTEN_BEAST_541:PANCREAS -#CREATURE:FORGOTTEN_BEAST_541:SPLEEN -#CREATURE:FORGOTTEN_BEAST_541:KIDNEY -#CREATURE:FORGOTTEN_BEAST_543:MUSCLE - CREATURE:FORGOTTEN_BEAST_543:EYE -"CREATURE:FORGOTTEN_BEAST_543:BRAIN -!CREATURE:FORGOTTEN_BEAST_543:LUNG -"CREATURE:FORGOTTEN_BEAST_543:HEART -"CREATURE:FORGOTTEN_BEAST_543:LIVER - CREATURE:FORGOTTEN_BEAST_543:GUT -$CREATURE:FORGOTTEN_BEAST_543:STOMACH -$CREATURE:FORGOTTEN_BEAST_543:GIZZARD -%CREATURE:FORGOTTEN_BEAST_543:PANCREAS -#CREATURE:FORGOTTEN_BEAST_543:SPLEEN -#CREATURE:FORGOTTEN_BEAST_543:KIDNEY -#CREATURE:FORGOTTEN_BEAST_545:MUSCLE - CREATURE:FORGOTTEN_BEAST_545:EYE -"CREATURE:FORGOTTEN_BEAST_545:BRAIN -!CREATURE:FORGOTTEN_BEAST_545:LUNG -"CREATURE:FORGOTTEN_BEAST_545:HEART -"CREATURE:FORGOTTEN_BEAST_545:LIVER - CREATURE:FORGOTTEN_BEAST_545:GUT -$CREATURE:FORGOTTEN_BEAST_545:STOMACH -$CREATURE:FORGOTTEN_BEAST_545:GIZZARD -%CREATURE:FORGOTTEN_BEAST_545:PANCREAS -#CREATURE:FORGOTTEN_BEAST_545:SPLEEN -#CREATURE:FORGOTTEN_BEAST_545:KIDNEY -#CREATURE:FORGOTTEN_BEAST_546:MUSCLE - CREATURE:FORGOTTEN_BEAST_546:EYE -"CREATURE:FORGOTTEN_BEAST_546:BRAIN -!CREATURE:FORGOTTEN_BEAST_546:LUNG -"CREATURE:FORGOTTEN_BEAST_546:HEART -"CREATURE:FORGOTTEN_BEAST_546:LIVER - CREATURE:FORGOTTEN_BEAST_546:GUT -$CREATURE:FORGOTTEN_BEAST_546:STOMACH -$CREATURE:FORGOTTEN_BEAST_546:GIZZARD -%CREATURE:FORGOTTEN_BEAST_546:PANCREAS -#CREATURE:FORGOTTEN_BEAST_546:SPLEEN -#CREATURE:FORGOTTEN_BEAST_546:KIDNEY -#CREATURE:FORGOTTEN_BEAST_547:MUSCLE - CREATURE:FORGOTTEN_BEAST_547:EYE -"CREATURE:FORGOTTEN_BEAST_547:BRAIN -!CREATURE:FORGOTTEN_BEAST_547:LUNG -"CREATURE:FORGOTTEN_BEAST_547:HEART -"CREATURE:FORGOTTEN_BEAST_547:LIVER - CREATURE:FORGOTTEN_BEAST_547:GUT -$CREATURE:FORGOTTEN_BEAST_547:STOMACH -$CREATURE:FORGOTTEN_BEAST_547:GIZZARD -%CREATURE:FORGOTTEN_BEAST_547:PANCREAS -#CREATURE:FORGOTTEN_BEAST_547:SPLEEN -#CREATURE:FORGOTTEN_BEAST_547:KIDNEY -#CREATURE:FORGOTTEN_BEAST_548:MUSCLE - CREATURE:FORGOTTEN_BEAST_548:EYE -"CREATURE:FORGOTTEN_BEAST_548:BRAIN -!CREATURE:FORGOTTEN_BEAST_548:LUNG -"CREATURE:FORGOTTEN_BEAST_548:HEART -"CREATURE:FORGOTTEN_BEAST_548:LIVER - CREATURE:FORGOTTEN_BEAST_548:GUT -$CREATURE:FORGOTTEN_BEAST_548:STOMACH -$CREATURE:FORGOTTEN_BEAST_548:GIZZARD -%CREATURE:FORGOTTEN_BEAST_548:PANCREAS -#CREATURE:FORGOTTEN_BEAST_548:SPLEEN -#CREATURE:FORGOTTEN_BEAST_548:KIDNEY -#CREATURE:FORGOTTEN_BEAST_549:MUSCLE - CREATURE:FORGOTTEN_BEAST_549:EYE -"CREATURE:FORGOTTEN_BEAST_549:BRAIN -!CREATURE:FORGOTTEN_BEAST_549:LUNG -"CREATURE:FORGOTTEN_BEAST_549:HEART -"CREATURE:FORGOTTEN_BEAST_549:LIVER - CREATURE:FORGOTTEN_BEAST_549:GUT -$CREATURE:FORGOTTEN_BEAST_549:STOMACH -$CREATURE:FORGOTTEN_BEAST_549:GIZZARD -%CREATURE:FORGOTTEN_BEAST_549:PANCREAS -#CREATURE:FORGOTTEN_BEAST_549:SPLEEN -#CREATURE:FORGOTTEN_BEAST_549:KIDNEY -#CREATURE:FORGOTTEN_BEAST_550:MUSCLE - CREATURE:FORGOTTEN_BEAST_550:EYE -"CREATURE:FORGOTTEN_BEAST_550:BRAIN -!CREATURE:FORGOTTEN_BEAST_550:LUNG -"CREATURE:FORGOTTEN_BEAST_550:HEART -"CREATURE:FORGOTTEN_BEAST_550:LIVER - CREATURE:FORGOTTEN_BEAST_550:GUT -$CREATURE:FORGOTTEN_BEAST_550:STOMACH -$CREATURE:FORGOTTEN_BEAST_550:GIZZARD -%CREATURE:FORGOTTEN_BEAST_550:PANCREAS -#CREATURE:FORGOTTEN_BEAST_550:SPLEEN -#CREATURE:FORGOTTEN_BEAST_550:KIDNEY -#CREATURE:FORGOTTEN_BEAST_551:MUSCLE - CREATURE:FORGOTTEN_BEAST_551:EYE -"CREATURE:FORGOTTEN_BEAST_551:BRAIN -!CREATURE:FORGOTTEN_BEAST_551:LUNG -"CREATURE:FORGOTTEN_BEAST_551:HEART -"CREATURE:FORGOTTEN_BEAST_551:LIVER - CREATURE:FORGOTTEN_BEAST_551:GUT -$CREATURE:FORGOTTEN_BEAST_551:STOMACH -$CREATURE:FORGOTTEN_BEAST_551:GIZZARD -%CREATURE:FORGOTTEN_BEAST_551:PANCREAS -#CREATURE:FORGOTTEN_BEAST_551:SPLEEN -#CREATURE:FORGOTTEN_BEAST_551:KIDNEY -#CREATURE:FORGOTTEN_BEAST_554:MUSCLE - CREATURE:FORGOTTEN_BEAST_554:EYE -"CREATURE:FORGOTTEN_BEAST_554:BRAIN -!CREATURE:FORGOTTEN_BEAST_554:LUNG -"CREATURE:FORGOTTEN_BEAST_554:HEART -"CREATURE:FORGOTTEN_BEAST_554:LIVER - CREATURE:FORGOTTEN_BEAST_554:GUT -$CREATURE:FORGOTTEN_BEAST_554:STOMACH -$CREATURE:FORGOTTEN_BEAST_554:GIZZARD -%CREATURE:FORGOTTEN_BEAST_554:PANCREAS -#CREATURE:FORGOTTEN_BEAST_554:SPLEEN -#CREATURE:FORGOTTEN_BEAST_554:KIDNEY -#CREATURE:FORGOTTEN_BEAST_555:MUSCLE - CREATURE:FORGOTTEN_BEAST_555:EYE -"CREATURE:FORGOTTEN_BEAST_555:BRAIN -!CREATURE:FORGOTTEN_BEAST_555:LUNG -"CREATURE:FORGOTTEN_BEAST_555:HEART -"CREATURE:FORGOTTEN_BEAST_555:LIVER - CREATURE:FORGOTTEN_BEAST_555:GUT -$CREATURE:FORGOTTEN_BEAST_555:STOMACH -$CREATURE:FORGOTTEN_BEAST_555:GIZZARD -%CREATURE:FORGOTTEN_BEAST_555:PANCREAS -#CREATURE:FORGOTTEN_BEAST_555:SPLEEN -#CREATURE:FORGOTTEN_BEAST_555:KIDNEY -#CREATURE:FORGOTTEN_BEAST_556:MUSCLE - CREATURE:FORGOTTEN_BEAST_556:EYE -"CREATURE:FORGOTTEN_BEAST_556:BRAIN -!CREATURE:FORGOTTEN_BEAST_556:LUNG -"CREATURE:FORGOTTEN_BEAST_556:HEART -"CREATURE:FORGOTTEN_BEAST_556:LIVER - CREATURE:FORGOTTEN_BEAST_556:GUT -$CREATURE:FORGOTTEN_BEAST_556:STOMACH -$CREATURE:FORGOTTEN_BEAST_556:GIZZARD -%CREATURE:FORGOTTEN_BEAST_556:PANCREAS -#CREATURE:FORGOTTEN_BEAST_556:SPLEEN -#CREATURE:FORGOTTEN_BEAST_556:KIDNEY -#CREATURE:FORGOTTEN_BEAST_557:MUSCLE - CREATURE:FORGOTTEN_BEAST_557:EYE -"CREATURE:FORGOTTEN_BEAST_557:BRAIN -!CREATURE:FORGOTTEN_BEAST_557:LUNG -"CREATURE:FORGOTTEN_BEAST_557:HEART -"CREATURE:FORGOTTEN_BEAST_557:LIVER - CREATURE:FORGOTTEN_BEAST_557:GUT -$CREATURE:FORGOTTEN_BEAST_557:STOMACH -$CREATURE:FORGOTTEN_BEAST_557:GIZZARD -%CREATURE:FORGOTTEN_BEAST_557:PANCREAS -#CREATURE:FORGOTTEN_BEAST_557:SPLEEN -#CREATURE:FORGOTTEN_BEAST_557:KIDNEY -#CREATURE:FORGOTTEN_BEAST_561:MUSCLE - CREATURE:FORGOTTEN_BEAST_561:EYE -"CREATURE:FORGOTTEN_BEAST_561:BRAIN -!CREATURE:FORGOTTEN_BEAST_561:LUNG -"CREATURE:FORGOTTEN_BEAST_561:HEART -"CREATURE:FORGOTTEN_BEAST_561:LIVER - CREATURE:FORGOTTEN_BEAST_561:GUT -$CREATURE:FORGOTTEN_BEAST_561:STOMACH -$CREATURE:FORGOTTEN_BEAST_561:GIZZARD -%CREATURE:FORGOTTEN_BEAST_561:PANCREAS -#CREATURE:FORGOTTEN_BEAST_561:SPLEEN -#CREATURE:FORGOTTEN_BEAST_561:KIDNEY -#CREATURE:FORGOTTEN_BEAST_562:MUSCLE - CREATURE:FORGOTTEN_BEAST_562:EYE -"CREATURE:FORGOTTEN_BEAST_562:BRAIN -!CREATURE:FORGOTTEN_BEAST_562:LUNG -"CREATURE:FORGOTTEN_BEAST_562:HEART -"CREATURE:FORGOTTEN_BEAST_562:LIVER - CREATURE:FORGOTTEN_BEAST_562:GUT -$CREATURE:FORGOTTEN_BEAST_562:STOMACH -$CREATURE:FORGOTTEN_BEAST_562:GIZZARD -%CREATURE:FORGOTTEN_BEAST_562:PANCREAS -#CREATURE:FORGOTTEN_BEAST_562:SPLEEN -#CREATURE:FORGOTTEN_BEAST_562:KIDNEY -#CREATURE:FORGOTTEN_BEAST_564:MUSCLE - CREATURE:FORGOTTEN_BEAST_564:EYE -"CREATURE:FORGOTTEN_BEAST_564:BRAIN -!CREATURE:FORGOTTEN_BEAST_564:LUNG -"CREATURE:FORGOTTEN_BEAST_564:HEART -"CREATURE:FORGOTTEN_BEAST_564:LIVER - CREATURE:FORGOTTEN_BEAST_564:GUT -$CREATURE:FORGOTTEN_BEAST_564:STOMACH -$CREATURE:FORGOTTEN_BEAST_564:GIZZARD -%CREATURE:FORGOTTEN_BEAST_564:PANCREAS -#CREATURE:FORGOTTEN_BEAST_564:SPLEEN -#CREATURE:FORGOTTEN_BEAST_564:KIDNEY -#CREATURE:FORGOTTEN_BEAST_569:MUSCLE - CREATURE:FORGOTTEN_BEAST_569:EYE -"CREATURE:FORGOTTEN_BEAST_569:BRAIN -!CREATURE:FORGOTTEN_BEAST_569:LUNG -"CREATURE:FORGOTTEN_BEAST_569:HEART -"CREATURE:FORGOTTEN_BEAST_569:LIVER - CREATURE:FORGOTTEN_BEAST_569:GUT -$CREATURE:FORGOTTEN_BEAST_569:STOMACH -$CREATURE:FORGOTTEN_BEAST_569:GIZZARD -%CREATURE:FORGOTTEN_BEAST_569:PANCREAS -#CREATURE:FORGOTTEN_BEAST_569:SPLEEN -#CREATURE:FORGOTTEN_BEAST_569:KIDNEY -#CREATURE:FORGOTTEN_BEAST_570:MUSCLE - CREATURE:FORGOTTEN_BEAST_570:EYE -"CREATURE:FORGOTTEN_BEAST_570:BRAIN -!CREATURE:FORGOTTEN_BEAST_570:LUNG -"CREATURE:FORGOTTEN_BEAST_570:HEART -"CREATURE:FORGOTTEN_BEAST_570:LIVER - CREATURE:FORGOTTEN_BEAST_570:GUT -$CREATURE:FORGOTTEN_BEAST_570:STOMACH -$CREATURE:FORGOTTEN_BEAST_570:GIZZARD -%CREATURE:FORGOTTEN_BEAST_570:PANCREAS -#CREATURE:FORGOTTEN_BEAST_570:SPLEEN -#CREATURE:FORGOTTEN_BEAST_570:KIDNEY -#CREATURE:FORGOTTEN_BEAST_571:MUSCLE - CREATURE:FORGOTTEN_BEAST_571:EYE -"CREATURE:FORGOTTEN_BEAST_571:BRAIN -!CREATURE:FORGOTTEN_BEAST_571:LUNG -"CREATURE:FORGOTTEN_BEAST_571:HEART -"CREATURE:FORGOTTEN_BEAST_571:LIVER - CREATURE:FORGOTTEN_BEAST_571:GUT -$CREATURE:FORGOTTEN_BEAST_571:STOMACH -$CREATURE:FORGOTTEN_BEAST_571:GIZZARD -%CREATURE:FORGOTTEN_BEAST_571:PANCREAS -#CREATURE:FORGOTTEN_BEAST_571:SPLEEN -#CREATURE:FORGOTTEN_BEAST_571:KIDNEY -#CREATURE:FORGOTTEN_BEAST_572:MUSCLE - CREATURE:FORGOTTEN_BEAST_572:EYE -"CREATURE:FORGOTTEN_BEAST_572:BRAIN -!CREATURE:FORGOTTEN_BEAST_572:LUNG -"CREATURE:FORGOTTEN_BEAST_572:HEART -"CREATURE:FORGOTTEN_BEAST_572:LIVER - CREATURE:FORGOTTEN_BEAST_572:GUT -$CREATURE:FORGOTTEN_BEAST_572:STOMACH -$CREATURE:FORGOTTEN_BEAST_572:GIZZARD -%CREATURE:FORGOTTEN_BEAST_572:PANCREAS -#CREATURE:FORGOTTEN_BEAST_572:SPLEEN -#CREATURE:FORGOTTEN_BEAST_572:KIDNEY -#CREATURE:FORGOTTEN_BEAST_574:MUSCLE - CREATURE:FORGOTTEN_BEAST_574:EYE -"CREATURE:FORGOTTEN_BEAST_574:BRAIN -!CREATURE:FORGOTTEN_BEAST_574:LUNG -"CREATURE:FORGOTTEN_BEAST_574:HEART -"CREATURE:FORGOTTEN_BEAST_574:LIVER - CREATURE:FORGOTTEN_BEAST_574:GUT -$CREATURE:FORGOTTEN_BEAST_574:STOMACH -$CREATURE:FORGOTTEN_BEAST_574:GIZZARD -%CREATURE:FORGOTTEN_BEAST_574:PANCREAS -#CREATURE:FORGOTTEN_BEAST_574:SPLEEN -#CREATURE:FORGOTTEN_BEAST_574:KIDNEY -#CREATURE:FORGOTTEN_BEAST_576:MUSCLE - CREATURE:FORGOTTEN_BEAST_576:EYE -"CREATURE:FORGOTTEN_BEAST_576:BRAIN -!CREATURE:FORGOTTEN_BEAST_576:LUNG -"CREATURE:FORGOTTEN_BEAST_576:HEART -"CREATURE:FORGOTTEN_BEAST_576:LIVER - CREATURE:FORGOTTEN_BEAST_576:GUT -$CREATURE:FORGOTTEN_BEAST_576:STOMACH -$CREATURE:FORGOTTEN_BEAST_576:GIZZARD -%CREATURE:FORGOTTEN_BEAST_576:PANCREAS -#CREATURE:FORGOTTEN_BEAST_576:SPLEEN -#CREATURE:FORGOTTEN_BEAST_576:KIDNEY -#CREATURE:FORGOTTEN_BEAST_578:MUSCLE - CREATURE:FORGOTTEN_BEAST_578:EYE -"CREATURE:FORGOTTEN_BEAST_578:BRAIN -!CREATURE:FORGOTTEN_BEAST_578:LUNG -"CREATURE:FORGOTTEN_BEAST_578:HEART -"CREATURE:FORGOTTEN_BEAST_578:LIVER - CREATURE:FORGOTTEN_BEAST_578:GUT -$CREATURE:FORGOTTEN_BEAST_578:STOMACH -$CREATURE:FORGOTTEN_BEAST_578:GIZZARD -%CREATURE:FORGOTTEN_BEAST_578:PANCREAS -#CREATURE:FORGOTTEN_BEAST_578:SPLEEN -#CREATURE:FORGOTTEN_BEAST_578:KIDNEY -#CREATURE:FORGOTTEN_BEAST_579:MUSCLE - CREATURE:FORGOTTEN_BEAST_579:EYE -"CREATURE:FORGOTTEN_BEAST_579:BRAIN -!CREATURE:FORGOTTEN_BEAST_579:LUNG -"CREATURE:FORGOTTEN_BEAST_579:HEART -"CREATURE:FORGOTTEN_BEAST_579:LIVER - CREATURE:FORGOTTEN_BEAST_579:GUT -$CREATURE:FORGOTTEN_BEAST_579:STOMACH -$CREATURE:FORGOTTEN_BEAST_579:GIZZARD -%CREATURE:FORGOTTEN_BEAST_579:PANCREAS -#CREATURE:FORGOTTEN_BEAST_579:SPLEEN -#CREATURE:FORGOTTEN_BEAST_579:KIDNEY -#CREATURE:FORGOTTEN_BEAST_580:MUSCLE - CREATURE:FORGOTTEN_BEAST_580:EYE -"CREATURE:FORGOTTEN_BEAST_580:BRAIN -!CREATURE:FORGOTTEN_BEAST_580:LUNG -"CREATURE:FORGOTTEN_BEAST_580:HEART -"CREATURE:FORGOTTEN_BEAST_580:LIVER - CREATURE:FORGOTTEN_BEAST_580:GUT -$CREATURE:FORGOTTEN_BEAST_580:STOMACH -$CREATURE:FORGOTTEN_BEAST_580:GIZZARD -%CREATURE:FORGOTTEN_BEAST_580:PANCREAS -#CREATURE:FORGOTTEN_BEAST_580:SPLEEN -#CREATURE:FORGOTTEN_BEAST_580:KIDNEY -#CREATURE:FORGOTTEN_BEAST_581:MUSCLE - CREATURE:FORGOTTEN_BEAST_581:EYE -"CREATURE:FORGOTTEN_BEAST_581:BRAIN -!CREATURE:FORGOTTEN_BEAST_581:LUNG -"CREATURE:FORGOTTEN_BEAST_581:HEART -"CREATURE:FORGOTTEN_BEAST_581:LIVER - CREATURE:FORGOTTEN_BEAST_581:GUT -$CREATURE:FORGOTTEN_BEAST_581:STOMACH -$CREATURE:FORGOTTEN_BEAST_581:GIZZARD -%CREATURE:FORGOTTEN_BEAST_581:PANCREAS -#CREATURE:FORGOTTEN_BEAST_581:SPLEEN -#CREATURE:FORGOTTEN_BEAST_581:KIDNEY -#CREATURE:FORGOTTEN_BEAST_583:MUSCLE - CREATURE:FORGOTTEN_BEAST_583:EYE -"CREATURE:FORGOTTEN_BEAST_583:BRAIN -!CREATURE:FORGOTTEN_BEAST_583:LUNG -"CREATURE:FORGOTTEN_BEAST_583:HEART -"CREATURE:FORGOTTEN_BEAST_583:LIVER - CREATURE:FORGOTTEN_BEAST_583:GUT -$CREATURE:FORGOTTEN_BEAST_583:STOMACH -$CREATURE:FORGOTTEN_BEAST_583:GIZZARD -%CREATURE:FORGOTTEN_BEAST_583:PANCREAS -#CREATURE:FORGOTTEN_BEAST_583:SPLEEN -#CREATURE:FORGOTTEN_BEAST_583:KIDNEY -#CREATURE:FORGOTTEN_BEAST_584:MUSCLE - CREATURE:FORGOTTEN_BEAST_584:EYE -"CREATURE:FORGOTTEN_BEAST_584:BRAIN -!CREATURE:FORGOTTEN_BEAST_584:LUNG -"CREATURE:FORGOTTEN_BEAST_584:HEART -"CREATURE:FORGOTTEN_BEAST_584:LIVER - CREATURE:FORGOTTEN_BEAST_584:GUT -$CREATURE:FORGOTTEN_BEAST_584:STOMACH -$CREATURE:FORGOTTEN_BEAST_584:GIZZARD -%CREATURE:FORGOTTEN_BEAST_584:PANCREAS -#CREATURE:FORGOTTEN_BEAST_584:SPLEEN -#CREATURE:FORGOTTEN_BEAST_584:KIDNEY -#CREATURE:FORGOTTEN_BEAST_586:MUSCLE - CREATURE:FORGOTTEN_BEAST_586:EYE -"CREATURE:FORGOTTEN_BEAST_586:BRAIN -!CREATURE:FORGOTTEN_BEAST_586:LUNG -"CREATURE:FORGOTTEN_BEAST_586:HEART -"CREATURE:FORGOTTEN_BEAST_586:LIVER - CREATURE:FORGOTTEN_BEAST_586:GUT -$CREATURE:FORGOTTEN_BEAST_586:STOMACH -$CREATURE:FORGOTTEN_BEAST_586:GIZZARD -%CREATURE:FORGOTTEN_BEAST_586:PANCREAS -#CREATURE:FORGOTTEN_BEAST_586:SPLEEN -#CREATURE:FORGOTTEN_BEAST_586:KIDNEY -#CREATURE:FORGOTTEN_BEAST_588:MUSCLE - CREATURE:FORGOTTEN_BEAST_588:EYE -"CREATURE:FORGOTTEN_BEAST_588:BRAIN -!CREATURE:FORGOTTEN_BEAST_588:LUNG -"CREATURE:FORGOTTEN_BEAST_588:HEART -"CREATURE:FORGOTTEN_BEAST_588:LIVER - CREATURE:FORGOTTEN_BEAST_588:GUT -$CREATURE:FORGOTTEN_BEAST_588:STOMACH -$CREATURE:FORGOTTEN_BEAST_588:GIZZARD -%CREATURE:FORGOTTEN_BEAST_588:PANCREAS -#CREATURE:FORGOTTEN_BEAST_588:SPLEEN -#CREATURE:FORGOTTEN_BEAST_588:KIDNEY -#CREATURE:FORGOTTEN_BEAST_589:MUSCLE - CREATURE:FORGOTTEN_BEAST_589:EYE -"CREATURE:FORGOTTEN_BEAST_589:BRAIN -!CREATURE:FORGOTTEN_BEAST_589:LUNG -"CREATURE:FORGOTTEN_BEAST_589:HEART -"CREATURE:FORGOTTEN_BEAST_589:LIVER - CREATURE:FORGOTTEN_BEAST_589:GUT -$CREATURE:FORGOTTEN_BEAST_589:STOMACH -$CREATURE:FORGOTTEN_BEAST_589:GIZZARD -%CREATURE:FORGOTTEN_BEAST_589:PANCREAS -#CREATURE:FORGOTTEN_BEAST_589:SPLEEN -#CREATURE:FORGOTTEN_BEAST_589:KIDNEY -#CREATURE:FORGOTTEN_BEAST_590:MUSCLE - CREATURE:FORGOTTEN_BEAST_590:EYE -"CREATURE:FORGOTTEN_BEAST_590:BRAIN -!CREATURE:FORGOTTEN_BEAST_590:LUNG -"CREATURE:FORGOTTEN_BEAST_590:HEART -"CREATURE:FORGOTTEN_BEAST_590:LIVER - CREATURE:FORGOTTEN_BEAST_590:GUT -$CREATURE:FORGOTTEN_BEAST_590:STOMACH -$CREATURE:FORGOTTEN_BEAST_590:GIZZARD -%CREATURE:FORGOTTEN_BEAST_590:PANCREAS -#CREATURE:FORGOTTEN_BEAST_590:SPLEEN -#CREATURE:FORGOTTEN_BEAST_590:KIDNEY -#CREATURE:FORGOTTEN_BEAST_592:MUSCLE - CREATURE:FORGOTTEN_BEAST_592:EYE -"CREATURE:FORGOTTEN_BEAST_592:BRAIN -!CREATURE:FORGOTTEN_BEAST_592:LUNG -"CREATURE:FORGOTTEN_BEAST_592:HEART -"CREATURE:FORGOTTEN_BEAST_592:LIVER - CREATURE:FORGOTTEN_BEAST_592:GUT -$CREATURE:FORGOTTEN_BEAST_592:STOMACH -$CREATURE:FORGOTTEN_BEAST_592:GIZZARD -%CREATURE:FORGOTTEN_BEAST_592:PANCREAS -#CREATURE:FORGOTTEN_BEAST_592:SPLEEN -#CREATURE:FORGOTTEN_BEAST_592:KIDNEY -#CREATURE:FORGOTTEN_BEAST_593:MUSCLE - CREATURE:FORGOTTEN_BEAST_593:EYE -"CREATURE:FORGOTTEN_BEAST_593:BRAIN -!CREATURE:FORGOTTEN_BEAST_593:LUNG -"CREATURE:FORGOTTEN_BEAST_593:HEART -"CREATURE:FORGOTTEN_BEAST_593:LIVER - CREATURE:FORGOTTEN_BEAST_593:GUT -$CREATURE:FORGOTTEN_BEAST_593:STOMACH -$CREATURE:FORGOTTEN_BEAST_593:GIZZARD -%CREATURE:FORGOTTEN_BEAST_593:PANCREAS -#CREATURE:FORGOTTEN_BEAST_593:SPLEEN -#CREATURE:FORGOTTEN_BEAST_593:KIDNEY -#CREATURE:FORGOTTEN_BEAST_594:MUSCLE - CREATURE:FORGOTTEN_BEAST_594:EYE -"CREATURE:FORGOTTEN_BEAST_594:BRAIN -!CREATURE:FORGOTTEN_BEAST_594:LUNG -"CREATURE:FORGOTTEN_BEAST_594:HEART -"CREATURE:FORGOTTEN_BEAST_594:LIVER - CREATURE:FORGOTTEN_BEAST_594:GUT -$CREATURE:FORGOTTEN_BEAST_594:STOMACH -$CREATURE:FORGOTTEN_BEAST_594:GIZZARD -%CREATURE:FORGOTTEN_BEAST_594:PANCREAS -#CREATURE:FORGOTTEN_BEAST_594:SPLEEN -#CREATURE:FORGOTTEN_BEAST_594:KIDNEY -#CREATURE:FORGOTTEN_BEAST_595:MUSCLE - CREATURE:FORGOTTEN_BEAST_595:EYE -"CREATURE:FORGOTTEN_BEAST_595:BRAIN -!CREATURE:FORGOTTEN_BEAST_595:LUNG -"CREATURE:FORGOTTEN_BEAST_595:HEART -"CREATURE:FORGOTTEN_BEAST_595:LIVER - CREATURE:FORGOTTEN_BEAST_595:GUT -$CREATURE:FORGOTTEN_BEAST_595:STOMACH -$CREATURE:FORGOTTEN_BEAST_595:GIZZARD -%CREATURE:FORGOTTEN_BEAST_595:PANCREAS -#CREATURE:FORGOTTEN_BEAST_595:SPLEEN -#CREATURE:FORGOTTEN_BEAST_595:KIDNEY -#CREATURE:FORGOTTEN_BEAST_596:MUSCLE - CREATURE:FORGOTTEN_BEAST_596:EYE -"CREATURE:FORGOTTEN_BEAST_596:BRAIN -!CREATURE:FORGOTTEN_BEAST_596:LUNG -"CREATURE:FORGOTTEN_BEAST_596:HEART -"CREATURE:FORGOTTEN_BEAST_596:LIVER - CREATURE:FORGOTTEN_BEAST_596:GUT -$CREATURE:FORGOTTEN_BEAST_596:STOMACH -$CREATURE:FORGOTTEN_BEAST_596:GIZZARD -%CREATURE:FORGOTTEN_BEAST_596:PANCREAS -#CREATURE:FORGOTTEN_BEAST_596:SPLEEN -#CREATURE:FORGOTTEN_BEAST_596:KIDNEY -#CREATURE:FORGOTTEN_BEAST_597:MUSCLE - CREATURE:FORGOTTEN_BEAST_597:EYE -"CREATURE:FORGOTTEN_BEAST_597:BRAIN -!CREATURE:FORGOTTEN_BEAST_597:LUNG -"CREATURE:FORGOTTEN_BEAST_597:HEART -"CREATURE:FORGOTTEN_BEAST_597:LIVER - CREATURE:FORGOTTEN_BEAST_597:GUT -$CREATURE:FORGOTTEN_BEAST_597:STOMACH -$CREATURE:FORGOTTEN_BEAST_597:GIZZARD -%CREATURE:FORGOTTEN_BEAST_597:PANCREAS -#CREATURE:FORGOTTEN_BEAST_597:SPLEEN -#CREATURE:FORGOTTEN_BEAST_597:KIDNEY -#CREATURE:FORGOTTEN_BEAST_599:MUSCLE - CREATURE:FORGOTTEN_BEAST_599:EYE -"CREATURE:FORGOTTEN_BEAST_599:BRAIN -!CREATURE:FORGOTTEN_BEAST_599:LUNG -"CREATURE:FORGOTTEN_BEAST_599:HEART -"CREATURE:FORGOTTEN_BEAST_599:LIVER - CREATURE:FORGOTTEN_BEAST_599:GUT -$CREATURE:FORGOTTEN_BEAST_599:STOMACH -$CREATURE:FORGOTTEN_BEAST_599:GIZZARD -%CREATURE:FORGOTTEN_BEAST_599:PANCREAS -#CREATURE:FORGOTTEN_BEAST_599:SPLEEN -#CREATURE:FORGOTTEN_BEAST_599:KIDNEY -#CREATURE:FORGOTTEN_BEAST_600:MUSCLE - CREATURE:FORGOTTEN_BEAST_600:EYE -"CREATURE:FORGOTTEN_BEAST_600:BRAIN -!CREATURE:FORGOTTEN_BEAST_600:LUNG -"CREATURE:FORGOTTEN_BEAST_600:HEART -"CREATURE:FORGOTTEN_BEAST_600:LIVER - CREATURE:FORGOTTEN_BEAST_600:GUT -$CREATURE:FORGOTTEN_BEAST_600:STOMACH -$CREATURE:FORGOTTEN_BEAST_600:GIZZARD -%CREATURE:FORGOTTEN_BEAST_600:PANCREAS -#CREATURE:FORGOTTEN_BEAST_600:SPLEEN -#CREATURE:FORGOTTEN_BEAST_600:KIDNEY -#CREATURE:FORGOTTEN_BEAST_601:MUSCLE - CREATURE:FORGOTTEN_BEAST_601:EYE -"CREATURE:FORGOTTEN_BEAST_601:BRAIN -!CREATURE:FORGOTTEN_BEAST_601:LUNG -"CREATURE:FORGOTTEN_BEAST_601:HEART -"CREATURE:FORGOTTEN_BEAST_601:LIVER - CREATURE:FORGOTTEN_BEAST_601:GUT -$CREATURE:FORGOTTEN_BEAST_601:STOMACH -$CREATURE:FORGOTTEN_BEAST_601:GIZZARD -%CREATURE:FORGOTTEN_BEAST_601:PANCREAS -#CREATURE:FORGOTTEN_BEAST_601:SPLEEN -#CREATURE:FORGOTTEN_BEAST_601:KIDNEY -#CREATURE:FORGOTTEN_BEAST_603:MUSCLE - CREATURE:FORGOTTEN_BEAST_603:EYE -"CREATURE:FORGOTTEN_BEAST_603:BRAIN -!CREATURE:FORGOTTEN_BEAST_603:LUNG -"CREATURE:FORGOTTEN_BEAST_603:HEART -"CREATURE:FORGOTTEN_BEAST_603:LIVER - CREATURE:FORGOTTEN_BEAST_603:GUT -$CREATURE:FORGOTTEN_BEAST_603:STOMACH -$CREATURE:FORGOTTEN_BEAST_603:GIZZARD -%CREATURE:FORGOTTEN_BEAST_603:PANCREAS -#CREATURE:FORGOTTEN_BEAST_603:SPLEEN -#CREATURE:FORGOTTEN_BEAST_603:KIDNEY -#CREATURE:FORGOTTEN_BEAST_605:MUSCLE - CREATURE:FORGOTTEN_BEAST_605:EYE -"CREATURE:FORGOTTEN_BEAST_605:BRAIN -!CREATURE:FORGOTTEN_BEAST_605:LUNG -"CREATURE:FORGOTTEN_BEAST_605:HEART -"CREATURE:FORGOTTEN_BEAST_605:LIVER - CREATURE:FORGOTTEN_BEAST_605:GUT -$CREATURE:FORGOTTEN_BEAST_605:STOMACH -$CREATURE:FORGOTTEN_BEAST_605:GIZZARD -%CREATURE:FORGOTTEN_BEAST_605:PANCREAS -#CREATURE:FORGOTTEN_BEAST_605:SPLEEN -#CREATURE:FORGOTTEN_BEAST_605:KIDNEY -#CREATURE:FORGOTTEN_BEAST_607:MUSCLE - CREATURE:FORGOTTEN_BEAST_607:EYE -"CREATURE:FORGOTTEN_BEAST_607:BRAIN -!CREATURE:FORGOTTEN_BEAST_607:LUNG -"CREATURE:FORGOTTEN_BEAST_607:HEART -"CREATURE:FORGOTTEN_BEAST_607:LIVER - CREATURE:FORGOTTEN_BEAST_607:GUT -$CREATURE:FORGOTTEN_BEAST_607:STOMACH -$CREATURE:FORGOTTEN_BEAST_607:GIZZARD -%CREATURE:FORGOTTEN_BEAST_607:PANCREAS -#CREATURE:FORGOTTEN_BEAST_607:SPLEEN -#CREATURE:FORGOTTEN_BEAST_607:KIDNEY -#CREATURE:FORGOTTEN_BEAST_608:MUSCLE - CREATURE:FORGOTTEN_BEAST_608:EYE -"CREATURE:FORGOTTEN_BEAST_608:BRAIN -!CREATURE:FORGOTTEN_BEAST_608:LUNG -"CREATURE:FORGOTTEN_BEAST_608:HEART -"CREATURE:FORGOTTEN_BEAST_608:LIVER - CREATURE:FORGOTTEN_BEAST_608:GUT -$CREATURE:FORGOTTEN_BEAST_608:STOMACH -$CREATURE:FORGOTTEN_BEAST_608:GIZZARD -%CREATURE:FORGOTTEN_BEAST_608:PANCREAS -#CREATURE:FORGOTTEN_BEAST_608:SPLEEN -#CREATURE:FORGOTTEN_BEAST_608:KIDNEY -#CREATURE:FORGOTTEN_BEAST_609:MUSCLE - CREATURE:FORGOTTEN_BEAST_609:EYE -"CREATURE:FORGOTTEN_BEAST_609:BRAIN -!CREATURE:FORGOTTEN_BEAST_609:LUNG -"CREATURE:FORGOTTEN_BEAST_609:HEART -"CREATURE:FORGOTTEN_BEAST_609:LIVER - CREATURE:FORGOTTEN_BEAST_609:GUT -$CREATURE:FORGOTTEN_BEAST_609:STOMACH -$CREATURE:FORGOTTEN_BEAST_609:GIZZARD -%CREATURE:FORGOTTEN_BEAST_609:PANCREAS -#CREATURE:FORGOTTEN_BEAST_609:SPLEEN -#CREATURE:FORGOTTEN_BEAST_609:KIDNEY -#CREATURE:FORGOTTEN_BEAST_610:MUSCLE - CREATURE:FORGOTTEN_BEAST_610:EYE -"CREATURE:FORGOTTEN_BEAST_610:BRAIN -!CREATURE:FORGOTTEN_BEAST_610:LUNG -"CREATURE:FORGOTTEN_BEAST_610:HEART -"CREATURE:FORGOTTEN_BEAST_610:LIVER - CREATURE:FORGOTTEN_BEAST_610:GUT -$CREATURE:FORGOTTEN_BEAST_610:STOMACH -$CREATURE:FORGOTTEN_BEAST_610:GIZZARD -%CREATURE:FORGOTTEN_BEAST_610:PANCREAS -#CREATURE:FORGOTTEN_BEAST_610:SPLEEN -#CREATURE:FORGOTTEN_BEAST_610:KIDNEY -#CREATURE:FORGOTTEN_BEAST_611:MUSCLE - CREATURE:FORGOTTEN_BEAST_611:EYE -"CREATURE:FORGOTTEN_BEAST_611:BRAIN -!CREATURE:FORGOTTEN_BEAST_611:LUNG -"CREATURE:FORGOTTEN_BEAST_611:HEART -"CREATURE:FORGOTTEN_BEAST_611:LIVER - CREATURE:FORGOTTEN_BEAST_611:GUT -$CREATURE:FORGOTTEN_BEAST_611:STOMACH -$CREATURE:FORGOTTEN_BEAST_611:GIZZARD -%CREATURE:FORGOTTEN_BEAST_611:PANCREAS -#CREATURE:FORGOTTEN_BEAST_611:SPLEEN -#CREATURE:FORGOTTEN_BEAST_611:KIDNEY -#CREATURE:FORGOTTEN_BEAST_612:MUSCLE - CREATURE:FORGOTTEN_BEAST_612:EYE -"CREATURE:FORGOTTEN_BEAST_612:BRAIN -!CREATURE:FORGOTTEN_BEAST_612:LUNG -"CREATURE:FORGOTTEN_BEAST_612:HEART -"CREATURE:FORGOTTEN_BEAST_612:LIVER - CREATURE:FORGOTTEN_BEAST_612:GUT -$CREATURE:FORGOTTEN_BEAST_612:STOMACH -$CREATURE:FORGOTTEN_BEAST_612:GIZZARD -%CREATURE:FORGOTTEN_BEAST_612:PANCREAS -#CREATURE:FORGOTTEN_BEAST_612:SPLEEN -#CREATURE:FORGOTTEN_BEAST_612:KIDNEY -#CREATURE:FORGOTTEN_BEAST_613:MUSCLE - CREATURE:FORGOTTEN_BEAST_613:EYE -"CREATURE:FORGOTTEN_BEAST_613:BRAIN -!CREATURE:FORGOTTEN_BEAST_613:LUNG -"CREATURE:FORGOTTEN_BEAST_613:HEART -"CREATURE:FORGOTTEN_BEAST_613:LIVER - CREATURE:FORGOTTEN_BEAST_613:GUT -$CREATURE:FORGOTTEN_BEAST_613:STOMACH -$CREATURE:FORGOTTEN_BEAST_613:GIZZARD -%CREATURE:FORGOTTEN_BEAST_613:PANCREAS -#CREATURE:FORGOTTEN_BEAST_613:SPLEEN -#CREATURE:FORGOTTEN_BEAST_613:KIDNEY -#CREATURE:FORGOTTEN_BEAST_614:MUSCLE - CREATURE:FORGOTTEN_BEAST_614:EYE -"CREATURE:FORGOTTEN_BEAST_614:BRAIN -!CREATURE:FORGOTTEN_BEAST_614:LUNG -"CREATURE:FORGOTTEN_BEAST_614:HEART -"CREATURE:FORGOTTEN_BEAST_614:LIVER - CREATURE:FORGOTTEN_BEAST_614:GUT -$CREATURE:FORGOTTEN_BEAST_614:STOMACH -$CREATURE:FORGOTTEN_BEAST_614:GIZZARD -%CREATURE:FORGOTTEN_BEAST_614:PANCREAS -#CREATURE:FORGOTTEN_BEAST_614:SPLEEN -#CREATURE:FORGOTTEN_BEAST_614:KIDNEY -#CREATURE:FORGOTTEN_BEAST_616:MUSCLE - CREATURE:FORGOTTEN_BEAST_616:EYE -"CREATURE:FORGOTTEN_BEAST_616:BRAIN -!CREATURE:FORGOTTEN_BEAST_616:LUNG -"CREATURE:FORGOTTEN_BEAST_616:HEART -"CREATURE:FORGOTTEN_BEAST_616:LIVER - CREATURE:FORGOTTEN_BEAST_616:GUT -$CREATURE:FORGOTTEN_BEAST_616:STOMACH -$CREATURE:FORGOTTEN_BEAST_616:GIZZARD -%CREATURE:FORGOTTEN_BEAST_616:PANCREAS -#CREATURE:FORGOTTEN_BEAST_616:SPLEEN -#CREATURE:FORGOTTEN_BEAST_616:KIDNEY -#CREATURE:FORGOTTEN_BEAST_619:MUSCLE - CREATURE:FORGOTTEN_BEAST_619:EYE -"CREATURE:FORGOTTEN_BEAST_619:BRAIN -!CREATURE:FORGOTTEN_BEAST_619:LUNG -"CREATURE:FORGOTTEN_BEAST_619:HEART -"CREATURE:FORGOTTEN_BEAST_619:LIVER - CREATURE:FORGOTTEN_BEAST_619:GUT -$CREATURE:FORGOTTEN_BEAST_619:STOMACH -$CREATURE:FORGOTTEN_BEAST_619:GIZZARD -%CREATURE:FORGOTTEN_BEAST_619:PANCREAS -#CREATURE:FORGOTTEN_BEAST_619:SPLEEN -#CREATURE:FORGOTTEN_BEAST_619:KIDNEY -#CREATURE:FORGOTTEN_BEAST_620:MUSCLE - CREATURE:FORGOTTEN_BEAST_620:EYE -"CREATURE:FORGOTTEN_BEAST_620:BRAIN -!CREATURE:FORGOTTEN_BEAST_620:LUNG -"CREATURE:FORGOTTEN_BEAST_620:HEART -"CREATURE:FORGOTTEN_BEAST_620:LIVER - CREATURE:FORGOTTEN_BEAST_620:GUT -$CREATURE:FORGOTTEN_BEAST_620:STOMACH -$CREATURE:FORGOTTEN_BEAST_620:GIZZARD -%CREATURE:FORGOTTEN_BEAST_620:PANCREAS -#CREATURE:FORGOTTEN_BEAST_620:SPLEEN -#CREATURE:FORGOTTEN_BEAST_620:KIDNEY -#CREATURE:FORGOTTEN_BEAST_621:MUSCLE - CREATURE:FORGOTTEN_BEAST_621:EYE -"CREATURE:FORGOTTEN_BEAST_621:BRAIN -!CREATURE:FORGOTTEN_BEAST_621:LUNG -"CREATURE:FORGOTTEN_BEAST_621:HEART -"CREATURE:FORGOTTEN_BEAST_621:LIVER - CREATURE:FORGOTTEN_BEAST_621:GUT -$CREATURE:FORGOTTEN_BEAST_621:STOMACH -$CREATURE:FORGOTTEN_BEAST_621:GIZZARD -%CREATURE:FORGOTTEN_BEAST_621:PANCREAS -#CREATURE:FORGOTTEN_BEAST_621:SPLEEN -#CREATURE:FORGOTTEN_BEAST_621:KIDNEY -#CREATURE:FORGOTTEN_BEAST_623:MUSCLE - CREATURE:FORGOTTEN_BEAST_623:EYE -"CREATURE:FORGOTTEN_BEAST_623:BRAIN -!CREATURE:FORGOTTEN_BEAST_623:LUNG -"CREATURE:FORGOTTEN_BEAST_623:HEART -"CREATURE:FORGOTTEN_BEAST_623:LIVER - CREATURE:FORGOTTEN_BEAST_623:GUT -$CREATURE:FORGOTTEN_BEAST_623:STOMACH -$CREATURE:FORGOTTEN_BEAST_623:GIZZARD -%CREATURE:FORGOTTEN_BEAST_623:PANCREAS -#CREATURE:FORGOTTEN_BEAST_623:SPLEEN -#CREATURE:FORGOTTEN_BEAST_623:KIDNEY -#CREATURE:FORGOTTEN_BEAST_624:MUSCLE - CREATURE:FORGOTTEN_BEAST_624:EYE -"CREATURE:FORGOTTEN_BEAST_624:BRAIN -!CREATURE:FORGOTTEN_BEAST_624:LUNG -"CREATURE:FORGOTTEN_BEAST_624:HEART -"CREATURE:FORGOTTEN_BEAST_624:LIVER - CREATURE:FORGOTTEN_BEAST_624:GUT -$CREATURE:FORGOTTEN_BEAST_624:STOMACH -$CREATURE:FORGOTTEN_BEAST_624:GIZZARD -%CREATURE:FORGOTTEN_BEAST_624:PANCREAS -#CREATURE:FORGOTTEN_BEAST_624:SPLEEN -#CREATURE:FORGOTTEN_BEAST_624:KIDNEY -#CREATURE:FORGOTTEN_BEAST_625:MUSCLE - CREATURE:FORGOTTEN_BEAST_625:EYE -"CREATURE:FORGOTTEN_BEAST_625:BRAIN -!CREATURE:FORGOTTEN_BEAST_625:LUNG -"CREATURE:FORGOTTEN_BEAST_625:HEART -"CREATURE:FORGOTTEN_BEAST_625:LIVER - CREATURE:FORGOTTEN_BEAST_625:GUT -$CREATURE:FORGOTTEN_BEAST_625:STOMACH -$CREATURE:FORGOTTEN_BEAST_625:GIZZARD -%CREATURE:FORGOTTEN_BEAST_625:PANCREAS -#CREATURE:FORGOTTEN_BEAST_625:SPLEEN -#CREATURE:FORGOTTEN_BEAST_625:KIDNEY -#CREATURE:FORGOTTEN_BEAST_626:MUSCLE - CREATURE:FORGOTTEN_BEAST_626:EYE -"CREATURE:FORGOTTEN_BEAST_626:BRAIN -!CREATURE:FORGOTTEN_BEAST_626:LUNG -"CREATURE:FORGOTTEN_BEAST_626:HEART -"CREATURE:FORGOTTEN_BEAST_626:LIVER - CREATURE:FORGOTTEN_BEAST_626:GUT -$CREATURE:FORGOTTEN_BEAST_626:STOMACH -$CREATURE:FORGOTTEN_BEAST_626:GIZZARD -%CREATURE:FORGOTTEN_BEAST_626:PANCREAS -#CREATURE:FORGOTTEN_BEAST_626:SPLEEN -#CREATURE:FORGOTTEN_BEAST_626:KIDNEY -#CREATURE:FORGOTTEN_BEAST_627:MUSCLE - CREATURE:FORGOTTEN_BEAST_627:EYE -"CREATURE:FORGOTTEN_BEAST_627:BRAIN -!CREATURE:FORGOTTEN_BEAST_627:LUNG -"CREATURE:FORGOTTEN_BEAST_627:HEART -"CREATURE:FORGOTTEN_BEAST_627:LIVER - CREATURE:FORGOTTEN_BEAST_627:GUT -$CREATURE:FORGOTTEN_BEAST_627:STOMACH -$CREATURE:FORGOTTEN_BEAST_627:GIZZARD -%CREATURE:FORGOTTEN_BEAST_627:PANCREAS -#CREATURE:FORGOTTEN_BEAST_627:SPLEEN -#CREATURE:FORGOTTEN_BEAST_627:KIDNEY -#CREATURE:FORGOTTEN_BEAST_628:MUSCLE - CREATURE:FORGOTTEN_BEAST_628:EYE -"CREATURE:FORGOTTEN_BEAST_628:BRAIN -!CREATURE:FORGOTTEN_BEAST_628:LUNG -"CREATURE:FORGOTTEN_BEAST_628:HEART -"CREATURE:FORGOTTEN_BEAST_628:LIVER - CREATURE:FORGOTTEN_BEAST_628:GUT -$CREATURE:FORGOTTEN_BEAST_628:STOMACH -$CREATURE:FORGOTTEN_BEAST_628:GIZZARD -%CREATURE:FORGOTTEN_BEAST_628:PANCREAS -#CREATURE:FORGOTTEN_BEAST_628:SPLEEN -#CREATURE:FORGOTTEN_BEAST_628:KIDNEY -#CREATURE:FORGOTTEN_BEAST_629:MUSCLE - CREATURE:FORGOTTEN_BEAST_629:EYE -"CREATURE:FORGOTTEN_BEAST_629:BRAIN -!CREATURE:FORGOTTEN_BEAST_629:LUNG -"CREATURE:FORGOTTEN_BEAST_629:HEART -"CREATURE:FORGOTTEN_BEAST_629:LIVER - CREATURE:FORGOTTEN_BEAST_629:GUT -$CREATURE:FORGOTTEN_BEAST_629:STOMACH -$CREATURE:FORGOTTEN_BEAST_629:GIZZARD -%CREATURE:FORGOTTEN_BEAST_629:PANCREAS -#CREATURE:FORGOTTEN_BEAST_629:SPLEEN -#CREATURE:FORGOTTEN_BEAST_629:KIDNEY -#CREATURE:FORGOTTEN_BEAST_630:MUSCLE - CREATURE:FORGOTTEN_BEAST_630:EYE -"CREATURE:FORGOTTEN_BEAST_630:BRAIN -!CREATURE:FORGOTTEN_BEAST_630:LUNG -"CREATURE:FORGOTTEN_BEAST_630:HEART -"CREATURE:FORGOTTEN_BEAST_630:LIVER - CREATURE:FORGOTTEN_BEAST_630:GUT -$CREATURE:FORGOTTEN_BEAST_630:STOMACH -$CREATURE:FORGOTTEN_BEAST_630:GIZZARD -%CREATURE:FORGOTTEN_BEAST_630:PANCREAS -#CREATURE:FORGOTTEN_BEAST_630:SPLEEN -#CREATURE:FORGOTTEN_BEAST_630:KIDNEY -#CREATURE:FORGOTTEN_BEAST_631:MUSCLE - CREATURE:FORGOTTEN_BEAST_631:EYE -"CREATURE:FORGOTTEN_BEAST_631:BRAIN -!CREATURE:FORGOTTEN_BEAST_631:LUNG -"CREATURE:FORGOTTEN_BEAST_631:HEART -"CREATURE:FORGOTTEN_BEAST_631:LIVER - CREATURE:FORGOTTEN_BEAST_631:GUT -$CREATURE:FORGOTTEN_BEAST_631:STOMACH -$CREATURE:FORGOTTEN_BEAST_631:GIZZARD -%CREATURE:FORGOTTEN_BEAST_631:PANCREAS -#CREATURE:FORGOTTEN_BEAST_631:SPLEEN -#CREATURE:FORGOTTEN_BEAST_631:KIDNEY -#CREATURE:FORGOTTEN_BEAST_632:MUSCLE - CREATURE:FORGOTTEN_BEAST_632:EYE -"CREATURE:FORGOTTEN_BEAST_632:BRAIN -!CREATURE:FORGOTTEN_BEAST_632:LUNG -"CREATURE:FORGOTTEN_BEAST_632:HEART -"CREATURE:FORGOTTEN_BEAST_632:LIVER - CREATURE:FORGOTTEN_BEAST_632:GUT -$CREATURE:FORGOTTEN_BEAST_632:STOMACH -$CREATURE:FORGOTTEN_BEAST_632:GIZZARD -%CREATURE:FORGOTTEN_BEAST_632:PANCREAS -#CREATURE:FORGOTTEN_BEAST_632:SPLEEN -#CREATURE:FORGOTTEN_BEAST_632:KIDNEY -#CREATURE:FORGOTTEN_BEAST_633:MUSCLE - CREATURE:FORGOTTEN_BEAST_633:EYE -"CREATURE:FORGOTTEN_BEAST_633:BRAIN -!CREATURE:FORGOTTEN_BEAST_633:LUNG -"CREATURE:FORGOTTEN_BEAST_633:HEART -"CREATURE:FORGOTTEN_BEAST_633:LIVER - CREATURE:FORGOTTEN_BEAST_633:GUT -$CREATURE:FORGOTTEN_BEAST_633:STOMACH -$CREATURE:FORGOTTEN_BEAST_633:GIZZARD -%CREATURE:FORGOTTEN_BEAST_633:PANCREAS -#CREATURE:FORGOTTEN_BEAST_633:SPLEEN -#CREATURE:FORGOTTEN_BEAST_633:KIDNEY -#CREATURE:FORGOTTEN_BEAST_634:MUSCLE - CREATURE:FORGOTTEN_BEAST_634:EYE -"CREATURE:FORGOTTEN_BEAST_634:BRAIN -!CREATURE:FORGOTTEN_BEAST_634:LUNG -"CREATURE:FORGOTTEN_BEAST_634:HEART -"CREATURE:FORGOTTEN_BEAST_634:LIVER - CREATURE:FORGOTTEN_BEAST_634:GUT -$CREATURE:FORGOTTEN_BEAST_634:STOMACH -$CREATURE:FORGOTTEN_BEAST_634:GIZZARD -%CREATURE:FORGOTTEN_BEAST_634:PANCREAS -#CREATURE:FORGOTTEN_BEAST_634:SPLEEN -#CREATURE:FORGOTTEN_BEAST_634:KIDNEY -#CREATURE:FORGOTTEN_BEAST_635:MUSCLE - CREATURE:FORGOTTEN_BEAST_635:EYE -"CREATURE:FORGOTTEN_BEAST_635:BRAIN -!CREATURE:FORGOTTEN_BEAST_635:LUNG -"CREATURE:FORGOTTEN_BEAST_635:HEART -"CREATURE:FORGOTTEN_BEAST_635:LIVER - CREATURE:FORGOTTEN_BEAST_635:GUT -$CREATURE:FORGOTTEN_BEAST_635:STOMACH -$CREATURE:FORGOTTEN_BEAST_635:GIZZARD -%CREATURE:FORGOTTEN_BEAST_635:PANCREAS -#CREATURE:FORGOTTEN_BEAST_635:SPLEEN -#CREATURE:FORGOTTEN_BEAST_635:KIDNEY -#CREATURE:FORGOTTEN_BEAST_636:MUSCLE - CREATURE:FORGOTTEN_BEAST_636:EYE -"CREATURE:FORGOTTEN_BEAST_636:BRAIN -!CREATURE:FORGOTTEN_BEAST_636:LUNG -"CREATURE:FORGOTTEN_BEAST_636:HEART -"CREATURE:FORGOTTEN_BEAST_636:LIVER - CREATURE:FORGOTTEN_BEAST_636:GUT -$CREATURE:FORGOTTEN_BEAST_636:STOMACH -$CREATURE:FORGOTTEN_BEAST_636:GIZZARD -%CREATURE:FORGOTTEN_BEAST_636:PANCREAS -#CREATURE:FORGOTTEN_BEAST_636:SPLEEN -#CREATURE:FORGOTTEN_BEAST_636:KIDNEY -#CREATURE:FORGOTTEN_BEAST_637:MUSCLE - CREATURE:FORGOTTEN_BEAST_637:EYE -"CREATURE:FORGOTTEN_BEAST_637:BRAIN -!CREATURE:FORGOTTEN_BEAST_637:LUNG -"CREATURE:FORGOTTEN_BEAST_637:HEART -"CREATURE:FORGOTTEN_BEAST_637:LIVER - CREATURE:FORGOTTEN_BEAST_637:GUT -$CREATURE:FORGOTTEN_BEAST_637:STOMACH -$CREATURE:FORGOTTEN_BEAST_637:GIZZARD -%CREATURE:FORGOTTEN_BEAST_637:PANCREAS -#CREATURE:FORGOTTEN_BEAST_637:SPLEEN -#CREATURE:FORGOTTEN_BEAST_637:KIDNEY -#CREATURE:FORGOTTEN_BEAST_639:MUSCLE - CREATURE:FORGOTTEN_BEAST_639:EYE -"CREATURE:FORGOTTEN_BEAST_639:BRAIN -!CREATURE:FORGOTTEN_BEAST_639:LUNG -"CREATURE:FORGOTTEN_BEAST_639:HEART -"CREATURE:FORGOTTEN_BEAST_639:LIVER - CREATURE:FORGOTTEN_BEAST_639:GUT -$CREATURE:FORGOTTEN_BEAST_639:STOMACH -$CREATURE:FORGOTTEN_BEAST_639:GIZZARD -%CREATURE:FORGOTTEN_BEAST_639:PANCREAS -#CREATURE:FORGOTTEN_BEAST_639:SPLEEN -#CREATURE:FORGOTTEN_BEAST_639:KIDNEY -#CREATURE:FORGOTTEN_BEAST_640:MUSCLE - CREATURE:FORGOTTEN_BEAST_640:EYE -"CREATURE:FORGOTTEN_BEAST_640:BRAIN -!CREATURE:FORGOTTEN_BEAST_640:LUNG -"CREATURE:FORGOTTEN_BEAST_640:HEART -"CREATURE:FORGOTTEN_BEAST_640:LIVER - CREATURE:FORGOTTEN_BEAST_640:GUT -$CREATURE:FORGOTTEN_BEAST_640:STOMACH -$CREATURE:FORGOTTEN_BEAST_640:GIZZARD -%CREATURE:FORGOTTEN_BEAST_640:PANCREAS -#CREATURE:FORGOTTEN_BEAST_640:SPLEEN -#CREATURE:FORGOTTEN_BEAST_640:KIDNEY -#CREATURE:FORGOTTEN_BEAST_643:MUSCLE - CREATURE:FORGOTTEN_BEAST_643:EYE -"CREATURE:FORGOTTEN_BEAST_643:BRAIN -!CREATURE:FORGOTTEN_BEAST_643:LUNG -"CREATURE:FORGOTTEN_BEAST_643:HEART -"CREATURE:FORGOTTEN_BEAST_643:LIVER - CREATURE:FORGOTTEN_BEAST_643:GUT -$CREATURE:FORGOTTEN_BEAST_643:STOMACH -$CREATURE:FORGOTTEN_BEAST_643:GIZZARD -%CREATURE:FORGOTTEN_BEAST_643:PANCREAS -#CREATURE:FORGOTTEN_BEAST_643:SPLEEN -#CREATURE:FORGOTTEN_BEAST_643:KIDNEY -#CREATURE:FORGOTTEN_BEAST_644:MUSCLE - CREATURE:FORGOTTEN_BEAST_644:EYE -"CREATURE:FORGOTTEN_BEAST_644:BRAIN -!CREATURE:FORGOTTEN_BEAST_644:LUNG -"CREATURE:FORGOTTEN_BEAST_644:HEART -"CREATURE:FORGOTTEN_BEAST_644:LIVER - CREATURE:FORGOTTEN_BEAST_644:GUT -$CREATURE:FORGOTTEN_BEAST_644:STOMACH -$CREATURE:FORGOTTEN_BEAST_644:GIZZARD -%CREATURE:FORGOTTEN_BEAST_644:PANCREAS -#CREATURE:FORGOTTEN_BEAST_644:SPLEEN -#CREATURE:FORGOTTEN_BEAST_644:KIDNEY -#CREATURE:FORGOTTEN_BEAST_645:MUSCLE - CREATURE:FORGOTTEN_BEAST_645:EYE -"CREATURE:FORGOTTEN_BEAST_645:BRAIN -!CREATURE:FORGOTTEN_BEAST_645:LUNG -"CREATURE:FORGOTTEN_BEAST_645:HEART -"CREATURE:FORGOTTEN_BEAST_645:LIVER - CREATURE:FORGOTTEN_BEAST_645:GUT -$CREATURE:FORGOTTEN_BEAST_645:STOMACH -$CREATURE:FORGOTTEN_BEAST_645:GIZZARD -%CREATURE:FORGOTTEN_BEAST_645:PANCREAS -#CREATURE:FORGOTTEN_BEAST_645:SPLEEN -#CREATURE:FORGOTTEN_BEAST_645:KIDNEY -#CREATURE:FORGOTTEN_BEAST_646:MUSCLE - CREATURE:FORGOTTEN_BEAST_646:EYE -"CREATURE:FORGOTTEN_BEAST_646:BRAIN -!CREATURE:FORGOTTEN_BEAST_646:LUNG -"CREATURE:FORGOTTEN_BEAST_646:HEART -"CREATURE:FORGOTTEN_BEAST_646:LIVER - CREATURE:FORGOTTEN_BEAST_646:GUT -$CREATURE:FORGOTTEN_BEAST_646:STOMACH -$CREATURE:FORGOTTEN_BEAST_646:GIZZARD -%CREATURE:FORGOTTEN_BEAST_646:PANCREAS -#CREATURE:FORGOTTEN_BEAST_646:SPLEEN -#CREATURE:FORGOTTEN_BEAST_646:KIDNEY -#CREATURE:FORGOTTEN_BEAST_647:MUSCLE - CREATURE:FORGOTTEN_BEAST_647:EYE -"CREATURE:FORGOTTEN_BEAST_647:BRAIN -!CREATURE:FORGOTTEN_BEAST_647:LUNG -"CREATURE:FORGOTTEN_BEAST_647:HEART -"CREATURE:FORGOTTEN_BEAST_647:LIVER - CREATURE:FORGOTTEN_BEAST_647:GUT -$CREATURE:FORGOTTEN_BEAST_647:STOMACH -$CREATURE:FORGOTTEN_BEAST_647:GIZZARD -%CREATURE:FORGOTTEN_BEAST_647:PANCREAS -#CREATURE:FORGOTTEN_BEAST_647:SPLEEN -#CREATURE:FORGOTTEN_BEAST_647:KIDNEY -#CREATURE:FORGOTTEN_BEAST_649:MUSCLE - CREATURE:FORGOTTEN_BEAST_649:EYE -"CREATURE:FORGOTTEN_BEAST_649:BRAIN -!CREATURE:FORGOTTEN_BEAST_649:LUNG -"CREATURE:FORGOTTEN_BEAST_649:HEART -"CREATURE:FORGOTTEN_BEAST_649:LIVER - CREATURE:FORGOTTEN_BEAST_649:GUT -$CREATURE:FORGOTTEN_BEAST_649:STOMACH -$CREATURE:FORGOTTEN_BEAST_649:GIZZARD -%CREATURE:FORGOTTEN_BEAST_649:PANCREAS -#CREATURE:FORGOTTEN_BEAST_649:SPLEEN -#CREATURE:FORGOTTEN_BEAST_649:KIDNEY -#CREATURE:FORGOTTEN_BEAST_650:MUSCLE - CREATURE:FORGOTTEN_BEAST_650:EYE -"CREATURE:FORGOTTEN_BEAST_650:BRAIN -!CREATURE:FORGOTTEN_BEAST_650:LUNG -"CREATURE:FORGOTTEN_BEAST_650:HEART -"CREATURE:FORGOTTEN_BEAST_650:LIVER - CREATURE:FORGOTTEN_BEAST_650:GUT -$CREATURE:FORGOTTEN_BEAST_650:STOMACH -$CREATURE:FORGOTTEN_BEAST_650:GIZZARD -%CREATURE:FORGOTTEN_BEAST_650:PANCREAS -#CREATURE:FORGOTTEN_BEAST_650:SPLEEN -#CREATURE:FORGOTTEN_BEAST_650:KIDNEY -#CREATURE:FORGOTTEN_BEAST_651:MUSCLE - CREATURE:FORGOTTEN_BEAST_651:EYE -"CREATURE:FORGOTTEN_BEAST_651:BRAIN -!CREATURE:FORGOTTEN_BEAST_651:LUNG -"CREATURE:FORGOTTEN_BEAST_651:HEART -"CREATURE:FORGOTTEN_BEAST_651:LIVER - CREATURE:FORGOTTEN_BEAST_651:GUT -$CREATURE:FORGOTTEN_BEAST_651:STOMACH -$CREATURE:FORGOTTEN_BEAST_651:GIZZARD -%CREATURE:FORGOTTEN_BEAST_651:PANCREAS -#CREATURE:FORGOTTEN_BEAST_651:SPLEEN -#CREATURE:FORGOTTEN_BEAST_651:KIDNEY -#CREATURE:FORGOTTEN_BEAST_652:MUSCLE - CREATURE:FORGOTTEN_BEAST_652:EYE -"CREATURE:FORGOTTEN_BEAST_652:BRAIN -!CREATURE:FORGOTTEN_BEAST_652:LUNG -"CREATURE:FORGOTTEN_BEAST_652:HEART -"CREATURE:FORGOTTEN_BEAST_652:LIVER - CREATURE:FORGOTTEN_BEAST_652:GUT -$CREATURE:FORGOTTEN_BEAST_652:STOMACH -$CREATURE:FORGOTTEN_BEAST_652:GIZZARD -%CREATURE:FORGOTTEN_BEAST_652:PANCREAS -#CREATURE:FORGOTTEN_BEAST_652:SPLEEN -#CREATURE:FORGOTTEN_BEAST_652:KIDNEY -#CREATURE:FORGOTTEN_BEAST_656:MUSCLE - CREATURE:FORGOTTEN_BEAST_656:EYE -"CREATURE:FORGOTTEN_BEAST_656:BRAIN -!CREATURE:FORGOTTEN_BEAST_656:LUNG -"CREATURE:FORGOTTEN_BEAST_656:HEART -"CREATURE:FORGOTTEN_BEAST_656:LIVER - CREATURE:FORGOTTEN_BEAST_656:GUT -$CREATURE:FORGOTTEN_BEAST_656:STOMACH -$CREATURE:FORGOTTEN_BEAST_656:GIZZARD -%CREATURE:FORGOTTEN_BEAST_656:PANCREAS -#CREATURE:FORGOTTEN_BEAST_656:SPLEEN -#CREATURE:FORGOTTEN_BEAST_656:KIDNEY -#CREATURE:FORGOTTEN_BEAST_658:MUSCLE - CREATURE:FORGOTTEN_BEAST_658:EYE -"CREATURE:FORGOTTEN_BEAST_658:BRAIN -!CREATURE:FORGOTTEN_BEAST_658:LUNG -"CREATURE:FORGOTTEN_BEAST_658:HEART -"CREATURE:FORGOTTEN_BEAST_658:LIVER - CREATURE:FORGOTTEN_BEAST_658:GUT -$CREATURE:FORGOTTEN_BEAST_658:STOMACH -$CREATURE:FORGOTTEN_BEAST_658:GIZZARD -%CREATURE:FORGOTTEN_BEAST_658:PANCREAS -#CREATURE:FORGOTTEN_BEAST_658:SPLEEN -#CREATURE:FORGOTTEN_BEAST_658:KIDNEY -#CREATURE:FORGOTTEN_BEAST_659:MUSCLE - CREATURE:FORGOTTEN_BEAST_659:EYE -"CREATURE:FORGOTTEN_BEAST_659:BRAIN -!CREATURE:FORGOTTEN_BEAST_659:LUNG -"CREATURE:FORGOTTEN_BEAST_659:HEART -"CREATURE:FORGOTTEN_BEAST_659:LIVER - CREATURE:FORGOTTEN_BEAST_659:GUT -$CREATURE:FORGOTTEN_BEAST_659:STOMACH -$CREATURE:FORGOTTEN_BEAST_659:GIZZARD -%CREATURE:FORGOTTEN_BEAST_659:PANCREAS -#CREATURE:FORGOTTEN_BEAST_659:SPLEEN -#CREATURE:FORGOTTEN_BEAST_659:KIDNEY -#CREATURE:FORGOTTEN_BEAST_661:MUSCLE - CREATURE:FORGOTTEN_BEAST_661:EYE -"CREATURE:FORGOTTEN_BEAST_661:BRAIN -!CREATURE:FORGOTTEN_BEAST_661:LUNG -"CREATURE:FORGOTTEN_BEAST_661:HEART -"CREATURE:FORGOTTEN_BEAST_661:LIVER - CREATURE:FORGOTTEN_BEAST_661:GUT -$CREATURE:FORGOTTEN_BEAST_661:STOMACH -$CREATURE:FORGOTTEN_BEAST_661:GIZZARD -%CREATURE:FORGOTTEN_BEAST_661:PANCREAS -#CREATURE:FORGOTTEN_BEAST_661:SPLEEN -#CREATURE:FORGOTTEN_BEAST_661:KIDNEY -#CREATURE:FORGOTTEN_BEAST_663:MUSCLE - CREATURE:FORGOTTEN_BEAST_663:EYE -"CREATURE:FORGOTTEN_BEAST_663:BRAIN -!CREATURE:FORGOTTEN_BEAST_663:LUNG -"CREATURE:FORGOTTEN_BEAST_663:HEART -"CREATURE:FORGOTTEN_BEAST_663:LIVER - CREATURE:FORGOTTEN_BEAST_663:GUT -$CREATURE:FORGOTTEN_BEAST_663:STOMACH -$CREATURE:FORGOTTEN_BEAST_663:GIZZARD -%CREATURE:FORGOTTEN_BEAST_663:PANCREAS -#CREATURE:FORGOTTEN_BEAST_663:SPLEEN -#CREATURE:FORGOTTEN_BEAST_663:KIDNEY -#CREATURE:FORGOTTEN_BEAST_664:MUSCLE - CREATURE:FORGOTTEN_BEAST_664:EYE -"CREATURE:FORGOTTEN_BEAST_664:BRAIN -!CREATURE:FORGOTTEN_BEAST_664:LUNG -"CREATURE:FORGOTTEN_BEAST_664:HEART -"CREATURE:FORGOTTEN_BEAST_664:LIVER - CREATURE:FORGOTTEN_BEAST_664:GUT -$CREATURE:FORGOTTEN_BEAST_664:STOMACH -$CREATURE:FORGOTTEN_BEAST_664:GIZZARD -%CREATURE:FORGOTTEN_BEAST_664:PANCREAS -#CREATURE:FORGOTTEN_BEAST_664:SPLEEN -#CREATURE:FORGOTTEN_BEAST_664:KIDNEY -#CREATURE:FORGOTTEN_BEAST_666:MUSCLE - CREATURE:FORGOTTEN_BEAST_666:EYE -"CREATURE:FORGOTTEN_BEAST_666:BRAIN -!CREATURE:FORGOTTEN_BEAST_666:LUNG -"CREATURE:FORGOTTEN_BEAST_666:HEART -"CREATURE:FORGOTTEN_BEAST_666:LIVER - CREATURE:FORGOTTEN_BEAST_666:GUT -$CREATURE:FORGOTTEN_BEAST_666:STOMACH -$CREATURE:FORGOTTEN_BEAST_666:GIZZARD -%CREATURE:FORGOTTEN_BEAST_666:PANCREAS -#CREATURE:FORGOTTEN_BEAST_666:SPLEEN -#CREATURE:FORGOTTEN_BEAST_666:KIDNEY -#CREATURE:FORGOTTEN_BEAST_667:MUSCLE - CREATURE:FORGOTTEN_BEAST_667:EYE -"CREATURE:FORGOTTEN_BEAST_667:BRAIN -!CREATURE:FORGOTTEN_BEAST_667:LUNG -"CREATURE:FORGOTTEN_BEAST_667:HEART -"CREATURE:FORGOTTEN_BEAST_667:LIVER - CREATURE:FORGOTTEN_BEAST_667:GUT -$CREATURE:FORGOTTEN_BEAST_667:STOMACH -$CREATURE:FORGOTTEN_BEAST_667:GIZZARD -%CREATURE:FORGOTTEN_BEAST_667:PANCREAS -#CREATURE:FORGOTTEN_BEAST_667:SPLEEN -#CREATURE:FORGOTTEN_BEAST_667:KIDNEY -#CREATURE:FORGOTTEN_BEAST_669:MUSCLE - CREATURE:FORGOTTEN_BEAST_669:EYE -"CREATURE:FORGOTTEN_BEAST_669:BRAIN -!CREATURE:FORGOTTEN_BEAST_669:LUNG -"CREATURE:FORGOTTEN_BEAST_669:HEART -"CREATURE:FORGOTTEN_BEAST_669:LIVER - CREATURE:FORGOTTEN_BEAST_669:GUT -$CREATURE:FORGOTTEN_BEAST_669:STOMACH -$CREATURE:FORGOTTEN_BEAST_669:GIZZARD -%CREATURE:FORGOTTEN_BEAST_669:PANCREAS -#CREATURE:FORGOTTEN_BEAST_669:SPLEEN -#CREATURE:FORGOTTEN_BEAST_669:KIDNEY -#CREATURE:FORGOTTEN_BEAST_670:MUSCLE - CREATURE:FORGOTTEN_BEAST_670:EYE -"CREATURE:FORGOTTEN_BEAST_670:BRAIN -!CREATURE:FORGOTTEN_BEAST_670:LUNG -"CREATURE:FORGOTTEN_BEAST_670:HEART -"CREATURE:FORGOTTEN_BEAST_670:LIVER - CREATURE:FORGOTTEN_BEAST_670:GUT -$CREATURE:FORGOTTEN_BEAST_670:STOMACH -$CREATURE:FORGOTTEN_BEAST_670:GIZZARD -%CREATURE:FORGOTTEN_BEAST_670:PANCREAS -#CREATURE:FORGOTTEN_BEAST_670:SPLEEN -#CREATURE:FORGOTTEN_BEAST_670:KIDNEY -#CREATURE:FORGOTTEN_BEAST_671:MUSCLE - CREATURE:FORGOTTEN_BEAST_671:EYE -"CREATURE:FORGOTTEN_BEAST_671:BRAIN -!CREATURE:FORGOTTEN_BEAST_671:LUNG -"CREATURE:FORGOTTEN_BEAST_671:HEART -"CREATURE:FORGOTTEN_BEAST_671:LIVER - CREATURE:FORGOTTEN_BEAST_671:GUT -$CREATURE:FORGOTTEN_BEAST_671:STOMACH -$CREATURE:FORGOTTEN_BEAST_671:GIZZARD -%CREATURE:FORGOTTEN_BEAST_671:PANCREAS -#CREATURE:FORGOTTEN_BEAST_671:SPLEEN -#CREATURE:FORGOTTEN_BEAST_671:KIDNEY -#CREATURE:FORGOTTEN_BEAST_674:MUSCLE - CREATURE:FORGOTTEN_BEAST_674:EYE -"CREATURE:FORGOTTEN_BEAST_674:BRAIN -!CREATURE:FORGOTTEN_BEAST_674:LUNG -"CREATURE:FORGOTTEN_BEAST_674:HEART -"CREATURE:FORGOTTEN_BEAST_674:LIVER - CREATURE:FORGOTTEN_BEAST_674:GUT -$CREATURE:FORGOTTEN_BEAST_674:STOMACH -$CREATURE:FORGOTTEN_BEAST_674:GIZZARD -%CREATURE:FORGOTTEN_BEAST_674:PANCREAS -#CREATURE:FORGOTTEN_BEAST_674:SPLEEN -#CREATURE:FORGOTTEN_BEAST_674:KIDNEY -#CREATURE:FORGOTTEN_BEAST_675:MUSCLE - CREATURE:FORGOTTEN_BEAST_675:EYE -"CREATURE:FORGOTTEN_BEAST_675:BRAIN -!CREATURE:FORGOTTEN_BEAST_675:LUNG -"CREATURE:FORGOTTEN_BEAST_675:HEART -"CREATURE:FORGOTTEN_BEAST_675:LIVER - CREATURE:FORGOTTEN_BEAST_675:GUT -$CREATURE:FORGOTTEN_BEAST_675:STOMACH -$CREATURE:FORGOTTEN_BEAST_675:GIZZARD -%CREATURE:FORGOTTEN_BEAST_675:PANCREAS -#CREATURE:FORGOTTEN_BEAST_675:SPLEEN -#CREATURE:FORGOTTEN_BEAST_675:KIDNEY -#CREATURE:FORGOTTEN_BEAST_678:MUSCLE - CREATURE:FORGOTTEN_BEAST_678:EYE -"CREATURE:FORGOTTEN_BEAST_678:BRAIN -!CREATURE:FORGOTTEN_BEAST_678:LUNG -"CREATURE:FORGOTTEN_BEAST_678:HEART -"CREATURE:FORGOTTEN_BEAST_678:LIVER - CREATURE:FORGOTTEN_BEAST_678:GUT -$CREATURE:FORGOTTEN_BEAST_678:STOMACH -$CREATURE:FORGOTTEN_BEAST_678:GIZZARD -%CREATURE:FORGOTTEN_BEAST_678:PANCREAS -#CREATURE:FORGOTTEN_BEAST_678:SPLEEN -#CREATURE:FORGOTTEN_BEAST_678:KIDNEY -#CREATURE:FORGOTTEN_BEAST_680:MUSCLE - CREATURE:FORGOTTEN_BEAST_680:EYE -"CREATURE:FORGOTTEN_BEAST_680:BRAIN -!CREATURE:FORGOTTEN_BEAST_680:LUNG -"CREATURE:FORGOTTEN_BEAST_680:HEART -"CREATURE:FORGOTTEN_BEAST_680:LIVER - CREATURE:FORGOTTEN_BEAST_680:GUT -$CREATURE:FORGOTTEN_BEAST_680:STOMACH -$CREATURE:FORGOTTEN_BEAST_680:GIZZARD -%CREATURE:FORGOTTEN_BEAST_680:PANCREAS -#CREATURE:FORGOTTEN_BEAST_680:SPLEEN -#CREATURE:FORGOTTEN_BEAST_680:KIDNEY -#CREATURE:FORGOTTEN_BEAST_681:MUSCLE - CREATURE:FORGOTTEN_BEAST_681:EYE -"CREATURE:FORGOTTEN_BEAST_681:BRAIN -!CREATURE:FORGOTTEN_BEAST_681:LUNG -"CREATURE:FORGOTTEN_BEAST_681:HEART -"CREATURE:FORGOTTEN_BEAST_681:LIVER - CREATURE:FORGOTTEN_BEAST_681:GUT -$CREATURE:FORGOTTEN_BEAST_681:STOMACH -$CREATURE:FORGOTTEN_BEAST_681:GIZZARD -%CREATURE:FORGOTTEN_BEAST_681:PANCREAS -#CREATURE:FORGOTTEN_BEAST_681:SPLEEN -#CREATURE:FORGOTTEN_BEAST_681:KIDNEY -#CREATURE:FORGOTTEN_BEAST_682:MUSCLE - CREATURE:FORGOTTEN_BEAST_682:EYE -"CREATURE:FORGOTTEN_BEAST_682:BRAIN -!CREATURE:FORGOTTEN_BEAST_682:LUNG -"CREATURE:FORGOTTEN_BEAST_682:HEART -"CREATURE:FORGOTTEN_BEAST_682:LIVER - CREATURE:FORGOTTEN_BEAST_682:GUT -$CREATURE:FORGOTTEN_BEAST_682:STOMACH -$CREATURE:FORGOTTEN_BEAST_682:GIZZARD -%CREATURE:FORGOTTEN_BEAST_682:PANCREAS -#CREATURE:FORGOTTEN_BEAST_682:SPLEEN -#CREATURE:FORGOTTEN_BEAST_682:KIDNEY -#CREATURE:FORGOTTEN_BEAST_684:MUSCLE - CREATURE:FORGOTTEN_BEAST_684:EYE -"CREATURE:FORGOTTEN_BEAST_684:BRAIN -!CREATURE:FORGOTTEN_BEAST_684:LUNG -"CREATURE:FORGOTTEN_BEAST_684:HEART -"CREATURE:FORGOTTEN_BEAST_684:LIVER - CREATURE:FORGOTTEN_BEAST_684:GUT -$CREATURE:FORGOTTEN_BEAST_684:STOMACH -$CREATURE:FORGOTTEN_BEAST_684:GIZZARD -%CREATURE:FORGOTTEN_BEAST_684:PANCREAS -#CREATURE:FORGOTTEN_BEAST_684:SPLEEN -#CREATURE:FORGOTTEN_BEAST_684:KIDNEY -#CREATURE:FORGOTTEN_BEAST_685:MUSCLE - CREATURE:FORGOTTEN_BEAST_685:EYE -"CREATURE:FORGOTTEN_BEAST_685:BRAIN -!CREATURE:FORGOTTEN_BEAST_685:LUNG -"CREATURE:FORGOTTEN_BEAST_685:HEART -"CREATURE:FORGOTTEN_BEAST_685:LIVER - CREATURE:FORGOTTEN_BEAST_685:GUT -$CREATURE:FORGOTTEN_BEAST_685:STOMACH -$CREATURE:FORGOTTEN_BEAST_685:GIZZARD -%CREATURE:FORGOTTEN_BEAST_685:PANCREAS -#CREATURE:FORGOTTEN_BEAST_685:SPLEEN -#CREATURE:FORGOTTEN_BEAST_685:KIDNEY -#CREATURE:FORGOTTEN_BEAST_686:MUSCLE - CREATURE:FORGOTTEN_BEAST_686:EYE -"CREATURE:FORGOTTEN_BEAST_686:BRAIN -!CREATURE:FORGOTTEN_BEAST_686:LUNG -"CREATURE:FORGOTTEN_BEAST_686:HEART -"CREATURE:FORGOTTEN_BEAST_686:LIVER - CREATURE:FORGOTTEN_BEAST_686:GUT -$CREATURE:FORGOTTEN_BEAST_686:STOMACH -$CREATURE:FORGOTTEN_BEAST_686:GIZZARD -%CREATURE:FORGOTTEN_BEAST_686:PANCREAS -#CREATURE:FORGOTTEN_BEAST_686:SPLEEN -#CREATURE:FORGOTTEN_BEAST_686:KIDNEY -#CREATURE:FORGOTTEN_BEAST_687:MUSCLE - CREATURE:FORGOTTEN_BEAST_687:EYE -"CREATURE:FORGOTTEN_BEAST_687:BRAIN -!CREATURE:FORGOTTEN_BEAST_687:LUNG -"CREATURE:FORGOTTEN_BEAST_687:HEART -"CREATURE:FORGOTTEN_BEAST_687:LIVER - CREATURE:FORGOTTEN_BEAST_687:GUT -$CREATURE:FORGOTTEN_BEAST_687:STOMACH -$CREATURE:FORGOTTEN_BEAST_687:GIZZARD -%CREATURE:FORGOTTEN_BEAST_687:PANCREAS -#CREATURE:FORGOTTEN_BEAST_687:SPLEEN -#CREATURE:FORGOTTEN_BEAST_687:KIDNEY -#CREATURE:FORGOTTEN_BEAST_688:MUSCLE - CREATURE:FORGOTTEN_BEAST_688:EYE -"CREATURE:FORGOTTEN_BEAST_688:BRAIN -!CREATURE:FORGOTTEN_BEAST_688:LUNG -"CREATURE:FORGOTTEN_BEAST_688:HEART -"CREATURE:FORGOTTEN_BEAST_688:LIVER - CREATURE:FORGOTTEN_BEAST_688:GUT -$CREATURE:FORGOTTEN_BEAST_688:STOMACH -$CREATURE:FORGOTTEN_BEAST_688:GIZZARD -%CREATURE:FORGOTTEN_BEAST_688:PANCREAS -#CREATURE:FORGOTTEN_BEAST_688:SPLEEN -#CREATURE:FORGOTTEN_BEAST_688:KIDNEY -#CREATURE:FORGOTTEN_BEAST_689:MUSCLE - CREATURE:FORGOTTEN_BEAST_689:EYE -"CREATURE:FORGOTTEN_BEAST_689:BRAIN -!CREATURE:FORGOTTEN_BEAST_689:LUNG -"CREATURE:FORGOTTEN_BEAST_689:HEART -"CREATURE:FORGOTTEN_BEAST_689:LIVER - CREATURE:FORGOTTEN_BEAST_689:GUT -$CREATURE:FORGOTTEN_BEAST_689:STOMACH -$CREATURE:FORGOTTEN_BEAST_689:GIZZARD -%CREATURE:FORGOTTEN_BEAST_689:PANCREAS -#CREATURE:FORGOTTEN_BEAST_689:SPLEEN -#CREATURE:FORGOTTEN_BEAST_689:KIDNEY -#CREATURE:FORGOTTEN_BEAST_691:MUSCLE - CREATURE:FORGOTTEN_BEAST_691:EYE -"CREATURE:FORGOTTEN_BEAST_691:BRAIN -!CREATURE:FORGOTTEN_BEAST_691:LUNG -"CREATURE:FORGOTTEN_BEAST_691:HEART -"CREATURE:FORGOTTEN_BEAST_691:LIVER - CREATURE:FORGOTTEN_BEAST_691:GUT -$CREATURE:FORGOTTEN_BEAST_691:STOMACH -$CREATURE:FORGOTTEN_BEAST_691:GIZZARD -%CREATURE:FORGOTTEN_BEAST_691:PANCREAS -#CREATURE:FORGOTTEN_BEAST_691:SPLEEN -#CREATURE:FORGOTTEN_BEAST_691:KIDNEY -#CREATURE:FORGOTTEN_BEAST_692:MUSCLE - CREATURE:FORGOTTEN_BEAST_692:EYE -"CREATURE:FORGOTTEN_BEAST_692:BRAIN -!CREATURE:FORGOTTEN_BEAST_692:LUNG -"CREATURE:FORGOTTEN_BEAST_692:HEART -"CREATURE:FORGOTTEN_BEAST_692:LIVER - CREATURE:FORGOTTEN_BEAST_692:GUT -$CREATURE:FORGOTTEN_BEAST_692:STOMACH -$CREATURE:FORGOTTEN_BEAST_692:GIZZARD -%CREATURE:FORGOTTEN_BEAST_692:PANCREAS -#CREATURE:FORGOTTEN_BEAST_692:SPLEEN -#CREATURE:FORGOTTEN_BEAST_692:KIDNEY -#CREATURE:FORGOTTEN_BEAST_695:MUSCLE - CREATURE:FORGOTTEN_BEAST_695:EYE -"CREATURE:FORGOTTEN_BEAST_695:BRAIN -!CREATURE:FORGOTTEN_BEAST_695:LUNG -"CREATURE:FORGOTTEN_BEAST_695:HEART -"CREATURE:FORGOTTEN_BEAST_695:LIVER - CREATURE:FORGOTTEN_BEAST_695:GUT -$CREATURE:FORGOTTEN_BEAST_695:STOMACH -$CREATURE:FORGOTTEN_BEAST_695:GIZZARD -%CREATURE:FORGOTTEN_BEAST_695:PANCREAS -#CREATURE:FORGOTTEN_BEAST_695:SPLEEN -#CREATURE:FORGOTTEN_BEAST_695:KIDNEY -#CREATURE:FORGOTTEN_BEAST_696:MUSCLE - CREATURE:FORGOTTEN_BEAST_696:EYE -"CREATURE:FORGOTTEN_BEAST_696:BRAIN -!CREATURE:FORGOTTEN_BEAST_696:LUNG -"CREATURE:FORGOTTEN_BEAST_696:HEART -"CREATURE:FORGOTTEN_BEAST_696:LIVER - CREATURE:FORGOTTEN_BEAST_696:GUT -$CREATURE:FORGOTTEN_BEAST_696:STOMACH -$CREATURE:FORGOTTEN_BEAST_696:GIZZARD -%CREATURE:FORGOTTEN_BEAST_696:PANCREAS -#CREATURE:FORGOTTEN_BEAST_696:SPLEEN -#CREATURE:FORGOTTEN_BEAST_696:KIDNEY -#CREATURE:FORGOTTEN_BEAST_697:MUSCLE - CREATURE:FORGOTTEN_BEAST_697:EYE -"CREATURE:FORGOTTEN_BEAST_697:BRAIN -!CREATURE:FORGOTTEN_BEAST_697:LUNG -"CREATURE:FORGOTTEN_BEAST_697:HEART -"CREATURE:FORGOTTEN_BEAST_697:LIVER - CREATURE:FORGOTTEN_BEAST_697:GUT -$CREATURE:FORGOTTEN_BEAST_697:STOMACH -$CREATURE:FORGOTTEN_BEAST_697:GIZZARD -%CREATURE:FORGOTTEN_BEAST_697:PANCREAS -#CREATURE:FORGOTTEN_BEAST_697:SPLEEN -#CREATURE:FORGOTTEN_BEAST_697:KIDNEY -#CREATURE:FORGOTTEN_BEAST_698:MUSCLE - CREATURE:FORGOTTEN_BEAST_698:EYE -"CREATURE:FORGOTTEN_BEAST_698:BRAIN -!CREATURE:FORGOTTEN_BEAST_698:LUNG -"CREATURE:FORGOTTEN_BEAST_698:HEART -"CREATURE:FORGOTTEN_BEAST_698:LIVER - CREATURE:FORGOTTEN_BEAST_698:GUT -$CREATURE:FORGOTTEN_BEAST_698:STOMACH -$CREATURE:FORGOTTEN_BEAST_698:GIZZARD -%CREATURE:FORGOTTEN_BEAST_698:PANCREAS -#CREATURE:FORGOTTEN_BEAST_698:SPLEEN -#CREATURE:FORGOTTEN_BEAST_698:KIDNEY -#CREATURE:FORGOTTEN_BEAST_699:MUSCLE - CREATURE:FORGOTTEN_BEAST_699:EYE -"CREATURE:FORGOTTEN_BEAST_699:BRAIN -!CREATURE:FORGOTTEN_BEAST_699:LUNG -"CREATURE:FORGOTTEN_BEAST_699:HEART -"CREATURE:FORGOTTEN_BEAST_699:LIVER - CREATURE:FORGOTTEN_BEAST_699:GUT -$CREATURE:FORGOTTEN_BEAST_699:STOMACH -$CREATURE:FORGOTTEN_BEAST_699:GIZZARD -%CREATURE:FORGOTTEN_BEAST_699:PANCREAS -#CREATURE:FORGOTTEN_BEAST_699:SPLEEN -#CREATURE:FORGOTTEN_BEAST_699:KIDNEY -#CREATURE:FORGOTTEN_BEAST_700:MUSCLE - CREATURE:FORGOTTEN_BEAST_700:EYE -"CREATURE:FORGOTTEN_BEAST_700:BRAIN -!CREATURE:FORGOTTEN_BEAST_700:LUNG -"CREATURE:FORGOTTEN_BEAST_700:HEART -"CREATURE:FORGOTTEN_BEAST_700:LIVER - CREATURE:FORGOTTEN_BEAST_700:GUT -$CREATURE:FORGOTTEN_BEAST_700:STOMACH -$CREATURE:FORGOTTEN_BEAST_700:GIZZARD -%CREATURE:FORGOTTEN_BEAST_700:PANCREAS -#CREATURE:FORGOTTEN_BEAST_700:SPLEEN -#CREATURE:FORGOTTEN_BEAST_700:KIDNEY -#CREATURE:FORGOTTEN_BEAST_701:MUSCLE - CREATURE:FORGOTTEN_BEAST_701:EYE -"CREATURE:FORGOTTEN_BEAST_701:BRAIN -!CREATURE:FORGOTTEN_BEAST_701:LUNG -"CREATURE:FORGOTTEN_BEAST_701:HEART -"CREATURE:FORGOTTEN_BEAST_701:LIVER - CREATURE:FORGOTTEN_BEAST_701:GUT -$CREATURE:FORGOTTEN_BEAST_701:STOMACH -$CREATURE:FORGOTTEN_BEAST_701:GIZZARD -%CREATURE:FORGOTTEN_BEAST_701:PANCREAS -#CREATURE:FORGOTTEN_BEAST_701:SPLEEN -#CREATURE:FORGOTTEN_BEAST_701:KIDNEY -#CREATURE:FORGOTTEN_BEAST_702:MUSCLE - CREATURE:FORGOTTEN_BEAST_702:EYE -"CREATURE:FORGOTTEN_BEAST_702:BRAIN -!CREATURE:FORGOTTEN_BEAST_702:LUNG -"CREATURE:FORGOTTEN_BEAST_702:HEART -"CREATURE:FORGOTTEN_BEAST_702:LIVER - CREATURE:FORGOTTEN_BEAST_702:GUT -$CREATURE:FORGOTTEN_BEAST_702:STOMACH -$CREATURE:FORGOTTEN_BEAST_702:GIZZARD -%CREATURE:FORGOTTEN_BEAST_702:PANCREAS -#CREATURE:FORGOTTEN_BEAST_702:SPLEEN -#CREATURE:FORGOTTEN_BEAST_702:KIDNEY -#CREATURE:FORGOTTEN_BEAST_704:MUSCLE - CREATURE:FORGOTTEN_BEAST_704:EYE -"CREATURE:FORGOTTEN_BEAST_704:BRAIN -!CREATURE:FORGOTTEN_BEAST_704:LUNG -"CREATURE:FORGOTTEN_BEAST_704:HEART -"CREATURE:FORGOTTEN_BEAST_704:LIVER - CREATURE:FORGOTTEN_BEAST_704:GUT -$CREATURE:FORGOTTEN_BEAST_704:STOMACH -$CREATURE:FORGOTTEN_BEAST_704:GIZZARD -%CREATURE:FORGOTTEN_BEAST_704:PANCREAS -#CREATURE:FORGOTTEN_BEAST_704:SPLEEN -#CREATURE:FORGOTTEN_BEAST_704:KIDNEY -#CREATURE:FORGOTTEN_BEAST_706:MUSCLE - CREATURE:FORGOTTEN_BEAST_706:EYE -"CREATURE:FORGOTTEN_BEAST_706:BRAIN -!CREATURE:FORGOTTEN_BEAST_706:LUNG -"CREATURE:FORGOTTEN_BEAST_706:HEART -"CREATURE:FORGOTTEN_BEAST_706:LIVER - CREATURE:FORGOTTEN_BEAST_706:GUT -$CREATURE:FORGOTTEN_BEAST_706:STOMACH -$CREATURE:FORGOTTEN_BEAST_706:GIZZARD -%CREATURE:FORGOTTEN_BEAST_706:PANCREAS -#CREATURE:FORGOTTEN_BEAST_706:SPLEEN -#CREATURE:FORGOTTEN_BEAST_706:KIDNEY -#CREATURE:FORGOTTEN_BEAST_708:MUSCLE - CREATURE:FORGOTTEN_BEAST_708:EYE -"CREATURE:FORGOTTEN_BEAST_708:BRAIN -!CREATURE:FORGOTTEN_BEAST_708:LUNG -"CREATURE:FORGOTTEN_BEAST_708:HEART -"CREATURE:FORGOTTEN_BEAST_708:LIVER - CREATURE:FORGOTTEN_BEAST_708:GUT -$CREATURE:FORGOTTEN_BEAST_708:STOMACH -$CREATURE:FORGOTTEN_BEAST_708:GIZZARD -%CREATURE:FORGOTTEN_BEAST_708:PANCREAS -#CREATURE:FORGOTTEN_BEAST_708:SPLEEN -#CREATURE:FORGOTTEN_BEAST_708:KIDNEY -#CREATURE:FORGOTTEN_BEAST_709:MUSCLE - CREATURE:FORGOTTEN_BEAST_709:EYE -"CREATURE:FORGOTTEN_BEAST_709:BRAIN -!CREATURE:FORGOTTEN_BEAST_709:LUNG -"CREATURE:FORGOTTEN_BEAST_709:HEART -"CREATURE:FORGOTTEN_BEAST_709:LIVER - CREATURE:FORGOTTEN_BEAST_709:GUT -$CREATURE:FORGOTTEN_BEAST_709:STOMACH -$CREATURE:FORGOTTEN_BEAST_709:GIZZARD -%CREATURE:FORGOTTEN_BEAST_709:PANCREAS -#CREATURE:FORGOTTEN_BEAST_709:SPLEEN -#CREATURE:FORGOTTEN_BEAST_709:KIDNEY -#CREATURE:FORGOTTEN_BEAST_711:MUSCLE - CREATURE:FORGOTTEN_BEAST_711:EYE -"CREATURE:FORGOTTEN_BEAST_711:BRAIN -!CREATURE:FORGOTTEN_BEAST_711:LUNG -"CREATURE:FORGOTTEN_BEAST_711:HEART -"CREATURE:FORGOTTEN_BEAST_711:LIVER - CREATURE:FORGOTTEN_BEAST_711:GUT -$CREATURE:FORGOTTEN_BEAST_711:STOMACH -$CREATURE:FORGOTTEN_BEAST_711:GIZZARD -%CREATURE:FORGOTTEN_BEAST_711:PANCREAS -#CREATURE:FORGOTTEN_BEAST_711:SPLEEN -#CREATURE:FORGOTTEN_BEAST_711:KIDNEY -#CREATURE:FORGOTTEN_BEAST_712:MUSCLE - CREATURE:FORGOTTEN_BEAST_712:EYE -"CREATURE:FORGOTTEN_BEAST_712:BRAIN -!CREATURE:FORGOTTEN_BEAST_712:LUNG -"CREATURE:FORGOTTEN_BEAST_712:HEART -"CREATURE:FORGOTTEN_BEAST_712:LIVER - CREATURE:FORGOTTEN_BEAST_712:GUT -$CREATURE:FORGOTTEN_BEAST_712:STOMACH -$CREATURE:FORGOTTEN_BEAST_712:GIZZARD -%CREATURE:FORGOTTEN_BEAST_712:PANCREAS -#CREATURE:FORGOTTEN_BEAST_712:SPLEEN -#CREATURE:FORGOTTEN_BEAST_712:KIDNEY -#CREATURE:FORGOTTEN_BEAST_713:MUSCLE - CREATURE:FORGOTTEN_BEAST_713:EYE -"CREATURE:FORGOTTEN_BEAST_713:BRAIN -!CREATURE:FORGOTTEN_BEAST_713:LUNG -"CREATURE:FORGOTTEN_BEAST_713:HEART -"CREATURE:FORGOTTEN_BEAST_713:LIVER - CREATURE:FORGOTTEN_BEAST_713:GUT -$CREATURE:FORGOTTEN_BEAST_713:STOMACH -$CREATURE:FORGOTTEN_BEAST_713:GIZZARD -%CREATURE:FORGOTTEN_BEAST_713:PANCREAS -#CREATURE:FORGOTTEN_BEAST_713:SPLEEN -#CREATURE:FORGOTTEN_BEAST_713:KIDNEY -#CREATURE:FORGOTTEN_BEAST_714:MUSCLE - CREATURE:FORGOTTEN_BEAST_714:EYE -"CREATURE:FORGOTTEN_BEAST_714:BRAIN -!CREATURE:FORGOTTEN_BEAST_714:LUNG -"CREATURE:FORGOTTEN_BEAST_714:HEART -"CREATURE:FORGOTTEN_BEAST_714:LIVER - CREATURE:FORGOTTEN_BEAST_714:GUT -$CREATURE:FORGOTTEN_BEAST_714:STOMACH -$CREATURE:FORGOTTEN_BEAST_714:GIZZARD -%CREATURE:FORGOTTEN_BEAST_714:PANCREAS -#CREATURE:FORGOTTEN_BEAST_714:SPLEEN -#CREATURE:FORGOTTEN_BEAST_714:KIDNEY -#CREATURE:FORGOTTEN_BEAST_715:MUSCLE - CREATURE:FORGOTTEN_BEAST_715:EYE -"CREATURE:FORGOTTEN_BEAST_715:BRAIN -!CREATURE:FORGOTTEN_BEAST_715:LUNG -"CREATURE:FORGOTTEN_BEAST_715:HEART -"CREATURE:FORGOTTEN_BEAST_715:LIVER - CREATURE:FORGOTTEN_BEAST_715:GUT -$CREATURE:FORGOTTEN_BEAST_715:STOMACH -$CREATURE:FORGOTTEN_BEAST_715:GIZZARD -%CREATURE:FORGOTTEN_BEAST_715:PANCREAS -#CREATURE:FORGOTTEN_BEAST_715:SPLEEN -#CREATURE:FORGOTTEN_BEAST_715:KIDNEY -#CREATURE:FORGOTTEN_BEAST_717:MUSCLE - CREATURE:FORGOTTEN_BEAST_717:EYE -"CREATURE:FORGOTTEN_BEAST_717:BRAIN -!CREATURE:FORGOTTEN_BEAST_717:LUNG -"CREATURE:FORGOTTEN_BEAST_717:HEART -"CREATURE:FORGOTTEN_BEAST_717:LIVER - CREATURE:FORGOTTEN_BEAST_717:GUT -$CREATURE:FORGOTTEN_BEAST_717:STOMACH -$CREATURE:FORGOTTEN_BEAST_717:GIZZARD -%CREATURE:FORGOTTEN_BEAST_717:PANCREAS -#CREATURE:FORGOTTEN_BEAST_717:SPLEEN -#CREATURE:FORGOTTEN_BEAST_717:KIDNEY -#CREATURE:FORGOTTEN_BEAST_718:MUSCLE - CREATURE:FORGOTTEN_BEAST_718:EYE -"CREATURE:FORGOTTEN_BEAST_718:BRAIN -!CREATURE:FORGOTTEN_BEAST_718:LUNG -"CREATURE:FORGOTTEN_BEAST_718:HEART -"CREATURE:FORGOTTEN_BEAST_718:LIVER - CREATURE:FORGOTTEN_BEAST_718:GUT -$CREATURE:FORGOTTEN_BEAST_718:STOMACH -$CREATURE:FORGOTTEN_BEAST_718:GIZZARD -%CREATURE:FORGOTTEN_BEAST_718:PANCREAS -#CREATURE:FORGOTTEN_BEAST_718:SPLEEN -#CREATURE:FORGOTTEN_BEAST_718:KIDNEY -#CREATURE:FORGOTTEN_BEAST_719:MUSCLE - CREATURE:FORGOTTEN_BEAST_719:EYE -"CREATURE:FORGOTTEN_BEAST_719:BRAIN -!CREATURE:FORGOTTEN_BEAST_719:LUNG -"CREATURE:FORGOTTEN_BEAST_719:HEART -"CREATURE:FORGOTTEN_BEAST_719:LIVER - CREATURE:FORGOTTEN_BEAST_719:GUT -$CREATURE:FORGOTTEN_BEAST_719:STOMACH -$CREATURE:FORGOTTEN_BEAST_719:GIZZARD -%CREATURE:FORGOTTEN_BEAST_719:PANCREAS -#CREATURE:FORGOTTEN_BEAST_719:SPLEEN -#CREATURE:FORGOTTEN_BEAST_719:KIDNEY -#CREATURE:FORGOTTEN_BEAST_720:MUSCLE - CREATURE:FORGOTTEN_BEAST_720:EYE -"CREATURE:FORGOTTEN_BEAST_720:BRAIN -!CREATURE:FORGOTTEN_BEAST_720:LUNG -"CREATURE:FORGOTTEN_BEAST_720:HEART -"CREATURE:FORGOTTEN_BEAST_720:LIVER - CREATURE:FORGOTTEN_BEAST_720:GUT -$CREATURE:FORGOTTEN_BEAST_720:STOMACH -$CREATURE:FORGOTTEN_BEAST_720:GIZZARD -%CREATURE:FORGOTTEN_BEAST_720:PANCREAS -#CREATURE:FORGOTTEN_BEAST_720:SPLEEN -#CREATURE:FORGOTTEN_BEAST_720:KIDNEY -#CREATURE:FORGOTTEN_BEAST_721:MUSCLE - CREATURE:FORGOTTEN_BEAST_721:EYE -"CREATURE:FORGOTTEN_BEAST_721:BRAIN -!CREATURE:FORGOTTEN_BEAST_721:LUNG -"CREATURE:FORGOTTEN_BEAST_721:HEART -"CREATURE:FORGOTTEN_BEAST_721:LIVER - CREATURE:FORGOTTEN_BEAST_721:GUT -$CREATURE:FORGOTTEN_BEAST_721:STOMACH -$CREATURE:FORGOTTEN_BEAST_721:GIZZARD -%CREATURE:FORGOTTEN_BEAST_721:PANCREAS -#CREATURE:FORGOTTEN_BEAST_721:SPLEEN -#CREATURE:FORGOTTEN_BEAST_721:KIDNEY -#CREATURE:FORGOTTEN_BEAST_722:MUSCLE - CREATURE:FORGOTTEN_BEAST_722:EYE -"CREATURE:FORGOTTEN_BEAST_722:BRAIN -!CREATURE:FORGOTTEN_BEAST_722:LUNG -"CREATURE:FORGOTTEN_BEAST_722:HEART -"CREATURE:FORGOTTEN_BEAST_722:LIVER - CREATURE:FORGOTTEN_BEAST_722:GUT -$CREATURE:FORGOTTEN_BEAST_722:STOMACH -$CREATURE:FORGOTTEN_BEAST_722:GIZZARD -%CREATURE:FORGOTTEN_BEAST_722:PANCREAS -#CREATURE:FORGOTTEN_BEAST_722:SPLEEN -#CREATURE:FORGOTTEN_BEAST_722:KIDNEY -#CREATURE:FORGOTTEN_BEAST_723:MUSCLE - CREATURE:FORGOTTEN_BEAST_723:EYE -"CREATURE:FORGOTTEN_BEAST_723:BRAIN -!CREATURE:FORGOTTEN_BEAST_723:LUNG -"CREATURE:FORGOTTEN_BEAST_723:HEART -"CREATURE:FORGOTTEN_BEAST_723:LIVER - CREATURE:FORGOTTEN_BEAST_723:GUT -$CREATURE:FORGOTTEN_BEAST_723:STOMACH -$CREATURE:FORGOTTEN_BEAST_723:GIZZARD -%CREATURE:FORGOTTEN_BEAST_723:PANCREAS -#CREATURE:FORGOTTEN_BEAST_723:SPLEEN -#CREATURE:FORGOTTEN_BEAST_723:KIDNEY -#CREATURE:FORGOTTEN_BEAST_724:MUSCLE - CREATURE:FORGOTTEN_BEAST_724:EYE -"CREATURE:FORGOTTEN_BEAST_724:BRAIN -!CREATURE:FORGOTTEN_BEAST_724:LUNG -"CREATURE:FORGOTTEN_BEAST_724:HEART -"CREATURE:FORGOTTEN_BEAST_724:LIVER - CREATURE:FORGOTTEN_BEAST_724:GUT -$CREATURE:FORGOTTEN_BEAST_724:STOMACH -$CREATURE:FORGOTTEN_BEAST_724:GIZZARD -%CREATURE:FORGOTTEN_BEAST_724:PANCREAS -#CREATURE:FORGOTTEN_BEAST_724:SPLEEN -#CREATURE:FORGOTTEN_BEAST_724:KIDNEY -#CREATURE:FORGOTTEN_BEAST_726:MUSCLE - CREATURE:FORGOTTEN_BEAST_726:EYE -"CREATURE:FORGOTTEN_BEAST_726:BRAIN -!CREATURE:FORGOTTEN_BEAST_726:LUNG -"CREATURE:FORGOTTEN_BEAST_726:HEART -"CREATURE:FORGOTTEN_BEAST_726:LIVER - CREATURE:FORGOTTEN_BEAST_726:GUT -$CREATURE:FORGOTTEN_BEAST_726:STOMACH -$CREATURE:FORGOTTEN_BEAST_726:GIZZARD -%CREATURE:FORGOTTEN_BEAST_726:PANCREAS -#CREATURE:FORGOTTEN_BEAST_726:SPLEEN -#CREATURE:FORGOTTEN_BEAST_726:KIDNEY -#CREATURE:FORGOTTEN_BEAST_730:MUSCLE - CREATURE:FORGOTTEN_BEAST_730:EYE -"CREATURE:FORGOTTEN_BEAST_730:BRAIN -!CREATURE:FORGOTTEN_BEAST_730:LUNG -"CREATURE:FORGOTTEN_BEAST_730:HEART -"CREATURE:FORGOTTEN_BEAST_730:LIVER - CREATURE:FORGOTTEN_BEAST_730:GUT -$CREATURE:FORGOTTEN_BEAST_730:STOMACH -$CREATURE:FORGOTTEN_BEAST_730:GIZZARD -%CREATURE:FORGOTTEN_BEAST_730:PANCREAS -#CREATURE:FORGOTTEN_BEAST_730:SPLEEN -#CREATURE:FORGOTTEN_BEAST_730:KIDNEY -#CREATURE:FORGOTTEN_BEAST_731:MUSCLE - CREATURE:FORGOTTEN_BEAST_731:EYE -"CREATURE:FORGOTTEN_BEAST_731:BRAIN -!CREATURE:FORGOTTEN_BEAST_731:LUNG -"CREATURE:FORGOTTEN_BEAST_731:HEART -"CREATURE:FORGOTTEN_BEAST_731:LIVER - CREATURE:FORGOTTEN_BEAST_731:GUT -$CREATURE:FORGOTTEN_BEAST_731:STOMACH -$CREATURE:FORGOTTEN_BEAST_731:GIZZARD -%CREATURE:FORGOTTEN_BEAST_731:PANCREAS -#CREATURE:FORGOTTEN_BEAST_731:SPLEEN -#CREATURE:FORGOTTEN_BEAST_731:KIDNEY -#CREATURE:FORGOTTEN_BEAST_734:MUSCLE - CREATURE:FORGOTTEN_BEAST_734:EYE -"CREATURE:FORGOTTEN_BEAST_734:BRAIN -!CREATURE:FORGOTTEN_BEAST_734:LUNG -"CREATURE:FORGOTTEN_BEAST_734:HEART -"CREATURE:FORGOTTEN_BEAST_734:LIVER - CREATURE:FORGOTTEN_BEAST_734:GUT -$CREATURE:FORGOTTEN_BEAST_734:STOMACH -$CREATURE:FORGOTTEN_BEAST_734:GIZZARD -%CREATURE:FORGOTTEN_BEAST_734:PANCREAS -#CREATURE:FORGOTTEN_BEAST_734:SPLEEN -#CREATURE:FORGOTTEN_BEAST_734:KIDNEY -#CREATURE:FORGOTTEN_BEAST_735:MUSCLE - CREATURE:FORGOTTEN_BEAST_735:EYE -"CREATURE:FORGOTTEN_BEAST_735:BRAIN -!CREATURE:FORGOTTEN_BEAST_735:LUNG -"CREATURE:FORGOTTEN_BEAST_735:HEART -"CREATURE:FORGOTTEN_BEAST_735:LIVER - CREATURE:FORGOTTEN_BEAST_735:GUT -$CREATURE:FORGOTTEN_BEAST_735:STOMACH -$CREATURE:FORGOTTEN_BEAST_735:GIZZARD -%CREATURE:FORGOTTEN_BEAST_735:PANCREAS -#CREATURE:FORGOTTEN_BEAST_735:SPLEEN -#CREATURE:FORGOTTEN_BEAST_735:KIDNEY -#CREATURE:FORGOTTEN_BEAST_737:MUSCLE - CREATURE:FORGOTTEN_BEAST_737:EYE -"CREATURE:FORGOTTEN_BEAST_737:BRAIN -!CREATURE:FORGOTTEN_BEAST_737:LUNG -"CREATURE:FORGOTTEN_BEAST_737:HEART -"CREATURE:FORGOTTEN_BEAST_737:LIVER - CREATURE:FORGOTTEN_BEAST_737:GUT -$CREATURE:FORGOTTEN_BEAST_737:STOMACH -$CREATURE:FORGOTTEN_BEAST_737:GIZZARD -%CREATURE:FORGOTTEN_BEAST_737:PANCREAS -#CREATURE:FORGOTTEN_BEAST_737:SPLEEN -#CREATURE:FORGOTTEN_BEAST_737:KIDNEY -#CREATURE:FORGOTTEN_BEAST_738:MUSCLE - CREATURE:FORGOTTEN_BEAST_738:EYE -"CREATURE:FORGOTTEN_BEAST_738:BRAIN -!CREATURE:FORGOTTEN_BEAST_738:LUNG -"CREATURE:FORGOTTEN_BEAST_738:HEART -"CREATURE:FORGOTTEN_BEAST_738:LIVER - CREATURE:FORGOTTEN_BEAST_738:GUT -$CREATURE:FORGOTTEN_BEAST_738:STOMACH -$CREATURE:FORGOTTEN_BEAST_738:GIZZARD -%CREATURE:FORGOTTEN_BEAST_738:PANCREAS -#CREATURE:FORGOTTEN_BEAST_738:SPLEEN -#CREATURE:FORGOTTEN_BEAST_738:KIDNEY -#CREATURE:FORGOTTEN_BEAST_739:MUSCLE - CREATURE:FORGOTTEN_BEAST_739:EYE -"CREATURE:FORGOTTEN_BEAST_739:BRAIN -!CREATURE:FORGOTTEN_BEAST_739:LUNG -"CREATURE:FORGOTTEN_BEAST_739:HEART -"CREATURE:FORGOTTEN_BEAST_739:LIVER - CREATURE:FORGOTTEN_BEAST_739:GUT -$CREATURE:FORGOTTEN_BEAST_739:STOMACH -$CREATURE:FORGOTTEN_BEAST_739:GIZZARD -%CREATURE:FORGOTTEN_BEAST_739:PANCREAS -#CREATURE:FORGOTTEN_BEAST_739:SPLEEN -#CREATURE:FORGOTTEN_BEAST_739:KIDNEY -#CREATURE:FORGOTTEN_BEAST_740:MUSCLE - CREATURE:FORGOTTEN_BEAST_740:EYE -"CREATURE:FORGOTTEN_BEAST_740:BRAIN -!CREATURE:FORGOTTEN_BEAST_740:LUNG -"CREATURE:FORGOTTEN_BEAST_740:HEART -"CREATURE:FORGOTTEN_BEAST_740:LIVER - CREATURE:FORGOTTEN_BEAST_740:GUT -$CREATURE:FORGOTTEN_BEAST_740:STOMACH -$CREATURE:FORGOTTEN_BEAST_740:GIZZARD -%CREATURE:FORGOTTEN_BEAST_740:PANCREAS -#CREATURE:FORGOTTEN_BEAST_740:SPLEEN -#CREATURE:FORGOTTEN_BEAST_740:KIDNEY -#CREATURE:FORGOTTEN_BEAST_741:MUSCLE - CREATURE:FORGOTTEN_BEAST_741:EYE -"CREATURE:FORGOTTEN_BEAST_741:BRAIN -!CREATURE:FORGOTTEN_BEAST_741:LUNG -"CREATURE:FORGOTTEN_BEAST_741:HEART -"CREATURE:FORGOTTEN_BEAST_741:LIVER - CREATURE:FORGOTTEN_BEAST_741:GUT -$CREATURE:FORGOTTEN_BEAST_741:STOMACH -$CREATURE:FORGOTTEN_BEAST_741:GIZZARD -%CREATURE:FORGOTTEN_BEAST_741:PANCREAS -#CREATURE:FORGOTTEN_BEAST_741:SPLEEN -#CREATURE:FORGOTTEN_BEAST_741:KIDNEY -#CREATURE:FORGOTTEN_BEAST_742:MUSCLE - CREATURE:FORGOTTEN_BEAST_742:EYE -"CREATURE:FORGOTTEN_BEAST_742:BRAIN -!CREATURE:FORGOTTEN_BEAST_742:LUNG -"CREATURE:FORGOTTEN_BEAST_742:HEART -"CREATURE:FORGOTTEN_BEAST_742:LIVER - CREATURE:FORGOTTEN_BEAST_742:GUT -$CREATURE:FORGOTTEN_BEAST_742:STOMACH -$CREATURE:FORGOTTEN_BEAST_742:GIZZARD -%CREATURE:FORGOTTEN_BEAST_742:PANCREAS -#CREATURE:FORGOTTEN_BEAST_742:SPLEEN -#CREATURE:FORGOTTEN_BEAST_742:KIDNEY -#CREATURE:FORGOTTEN_BEAST_743:MUSCLE - CREATURE:FORGOTTEN_BEAST_743:EYE -"CREATURE:FORGOTTEN_BEAST_743:BRAIN -!CREATURE:FORGOTTEN_BEAST_743:LUNG -"CREATURE:FORGOTTEN_BEAST_743:HEART -"CREATURE:FORGOTTEN_BEAST_743:LIVER - CREATURE:FORGOTTEN_BEAST_743:GUT -$CREATURE:FORGOTTEN_BEAST_743:STOMACH -$CREATURE:FORGOTTEN_BEAST_743:GIZZARD -%CREATURE:FORGOTTEN_BEAST_743:PANCREAS -#CREATURE:FORGOTTEN_BEAST_743:SPLEEN -#CREATURE:FORGOTTEN_BEAST_743:KIDNEY -#CREATURE:FORGOTTEN_BEAST_744:MUSCLE - CREATURE:FORGOTTEN_BEAST_744:EYE -"CREATURE:FORGOTTEN_BEAST_744:BRAIN -!CREATURE:FORGOTTEN_BEAST_744:LUNG -"CREATURE:FORGOTTEN_BEAST_744:HEART -"CREATURE:FORGOTTEN_BEAST_744:LIVER - CREATURE:FORGOTTEN_BEAST_744:GUT -$CREATURE:FORGOTTEN_BEAST_744:STOMACH -$CREATURE:FORGOTTEN_BEAST_744:GIZZARD -%CREATURE:FORGOTTEN_BEAST_744:PANCREAS -#CREATURE:FORGOTTEN_BEAST_744:SPLEEN -#CREATURE:FORGOTTEN_BEAST_744:KIDNEY -#CREATURE:FORGOTTEN_BEAST_746:MUSCLE - CREATURE:FORGOTTEN_BEAST_746:EYE -"CREATURE:FORGOTTEN_BEAST_746:BRAIN -!CREATURE:FORGOTTEN_BEAST_746:LUNG -"CREATURE:FORGOTTEN_BEAST_746:HEART -"CREATURE:FORGOTTEN_BEAST_746:LIVER - CREATURE:FORGOTTEN_BEAST_746:GUT -$CREATURE:FORGOTTEN_BEAST_746:STOMACH -$CREATURE:FORGOTTEN_BEAST_746:GIZZARD -%CREATURE:FORGOTTEN_BEAST_746:PANCREAS -#CREATURE:FORGOTTEN_BEAST_746:SPLEEN -#CREATURE:FORGOTTEN_BEAST_746:KIDNEY -#CREATURE:FORGOTTEN_BEAST_747:MUSCLE - CREATURE:FORGOTTEN_BEAST_747:EYE -"CREATURE:FORGOTTEN_BEAST_747:BRAIN -!CREATURE:FORGOTTEN_BEAST_747:LUNG -"CREATURE:FORGOTTEN_BEAST_747:HEART -"CREATURE:FORGOTTEN_BEAST_747:LIVER - CREATURE:FORGOTTEN_BEAST_747:GUT -$CREATURE:FORGOTTEN_BEAST_747:STOMACH -$CREATURE:FORGOTTEN_BEAST_747:GIZZARD -%CREATURE:FORGOTTEN_BEAST_747:PANCREAS -#CREATURE:FORGOTTEN_BEAST_747:SPLEEN -#CREATURE:FORGOTTEN_BEAST_747:KIDNEY -#CREATURE:FORGOTTEN_BEAST_749:MUSCLE - CREATURE:FORGOTTEN_BEAST_749:EYE -"CREATURE:FORGOTTEN_BEAST_749:BRAIN -!CREATURE:FORGOTTEN_BEAST_749:LUNG -"CREATURE:FORGOTTEN_BEAST_749:HEART -"CREATURE:FORGOTTEN_BEAST_749:LIVER - CREATURE:FORGOTTEN_BEAST_749:GUT -$CREATURE:FORGOTTEN_BEAST_749:STOMACH -$CREATURE:FORGOTTEN_BEAST_749:GIZZARD -%CREATURE:FORGOTTEN_BEAST_749:PANCREAS -#CREATURE:FORGOTTEN_BEAST_749:SPLEEN -#CREATURE:FORGOTTEN_BEAST_749:KIDNEY -#CREATURE:FORGOTTEN_BEAST_750:MUSCLE - CREATURE:FORGOTTEN_BEAST_750:EYE -"CREATURE:FORGOTTEN_BEAST_750:BRAIN -!CREATURE:FORGOTTEN_BEAST_750:LUNG -"CREATURE:FORGOTTEN_BEAST_750:HEART -"CREATURE:FORGOTTEN_BEAST_750:LIVER - CREATURE:FORGOTTEN_BEAST_750:GUT -$CREATURE:FORGOTTEN_BEAST_750:STOMACH -$CREATURE:FORGOTTEN_BEAST_750:GIZZARD -%CREATURE:FORGOTTEN_BEAST_750:PANCREAS -#CREATURE:FORGOTTEN_BEAST_750:SPLEEN -#CREATURE:FORGOTTEN_BEAST_750:KIDNEY -#CREATURE:FORGOTTEN_BEAST_751:MUSCLE - CREATURE:FORGOTTEN_BEAST_751:EYE -"CREATURE:FORGOTTEN_BEAST_751:BRAIN -!CREATURE:FORGOTTEN_BEAST_751:LUNG -"CREATURE:FORGOTTEN_BEAST_751:HEART -"CREATURE:FORGOTTEN_BEAST_751:LIVER - CREATURE:FORGOTTEN_BEAST_751:GUT -$CREATURE:FORGOTTEN_BEAST_751:STOMACH -$CREATURE:FORGOTTEN_BEAST_751:GIZZARD -%CREATURE:FORGOTTEN_BEAST_751:PANCREAS -#CREATURE:FORGOTTEN_BEAST_751:SPLEEN -#CREATURE:FORGOTTEN_BEAST_751:KIDNEY -#CREATURE:FORGOTTEN_BEAST_752:MUSCLE - CREATURE:FORGOTTEN_BEAST_752:EYE -"CREATURE:FORGOTTEN_BEAST_752:BRAIN -!CREATURE:FORGOTTEN_BEAST_752:LUNG -"CREATURE:FORGOTTEN_BEAST_752:HEART -"CREATURE:FORGOTTEN_BEAST_752:LIVER - CREATURE:FORGOTTEN_BEAST_752:GUT -$CREATURE:FORGOTTEN_BEAST_752:STOMACH -$CREATURE:FORGOTTEN_BEAST_752:GIZZARD -%CREATURE:FORGOTTEN_BEAST_752:PANCREAS -#CREATURE:FORGOTTEN_BEAST_752:SPLEEN -#CREATURE:FORGOTTEN_BEAST_752:KIDNEY -#CREATURE:FORGOTTEN_BEAST_753:MUSCLE - CREATURE:FORGOTTEN_BEAST_753:EYE -"CREATURE:FORGOTTEN_BEAST_753:BRAIN -!CREATURE:FORGOTTEN_BEAST_753:LUNG -"CREATURE:FORGOTTEN_BEAST_753:HEART -"CREATURE:FORGOTTEN_BEAST_753:LIVER - CREATURE:FORGOTTEN_BEAST_753:GUT -$CREATURE:FORGOTTEN_BEAST_753:STOMACH -$CREATURE:FORGOTTEN_BEAST_753:GIZZARD -%CREATURE:FORGOTTEN_BEAST_753:PANCREAS -#CREATURE:FORGOTTEN_BEAST_753:SPLEEN -#CREATURE:FORGOTTEN_BEAST_753:KIDNEY -#CREATURE:FORGOTTEN_BEAST_754:MUSCLE - CREATURE:FORGOTTEN_BEAST_754:EYE -"CREATURE:FORGOTTEN_BEAST_754:BRAIN -!CREATURE:FORGOTTEN_BEAST_754:LUNG -"CREATURE:FORGOTTEN_BEAST_754:HEART -"CREATURE:FORGOTTEN_BEAST_754:LIVER - CREATURE:FORGOTTEN_BEAST_754:GUT -$CREATURE:FORGOTTEN_BEAST_754:STOMACH -$CREATURE:FORGOTTEN_BEAST_754:GIZZARD -%CREATURE:FORGOTTEN_BEAST_754:PANCREAS -#CREATURE:FORGOTTEN_BEAST_754:SPLEEN -#CREATURE:FORGOTTEN_BEAST_754:KIDNEY -#CREATURE:FORGOTTEN_BEAST_755:MUSCLE - CREATURE:FORGOTTEN_BEAST_755:EYE -"CREATURE:FORGOTTEN_BEAST_755:BRAIN -!CREATURE:FORGOTTEN_BEAST_755:LUNG -"CREATURE:FORGOTTEN_BEAST_755:HEART -"CREATURE:FORGOTTEN_BEAST_755:LIVER - CREATURE:FORGOTTEN_BEAST_755:GUT -$CREATURE:FORGOTTEN_BEAST_755:STOMACH -$CREATURE:FORGOTTEN_BEAST_755:GIZZARD -%CREATURE:FORGOTTEN_BEAST_755:PANCREAS -#CREATURE:FORGOTTEN_BEAST_755:SPLEEN -#CREATURE:FORGOTTEN_BEAST_755:KIDNEY -#CREATURE:FORGOTTEN_BEAST_756:MUSCLE - CREATURE:FORGOTTEN_BEAST_756:EYE -"CREATURE:FORGOTTEN_BEAST_756:BRAIN -!CREATURE:FORGOTTEN_BEAST_756:LUNG -"CREATURE:FORGOTTEN_BEAST_756:HEART -"CREATURE:FORGOTTEN_BEAST_756:LIVER - CREATURE:FORGOTTEN_BEAST_756:GUT -$CREATURE:FORGOTTEN_BEAST_756:STOMACH -$CREATURE:FORGOTTEN_BEAST_756:GIZZARD -%CREATURE:FORGOTTEN_BEAST_756:PANCREAS -#CREATURE:FORGOTTEN_BEAST_756:SPLEEN -#CREATURE:FORGOTTEN_BEAST_756:KIDNEY -#CREATURE:FORGOTTEN_BEAST_757:MUSCLE - CREATURE:FORGOTTEN_BEAST_757:EYE -"CREATURE:FORGOTTEN_BEAST_757:BRAIN -!CREATURE:FORGOTTEN_BEAST_757:LUNG -"CREATURE:FORGOTTEN_BEAST_757:HEART -"CREATURE:FORGOTTEN_BEAST_757:LIVER - CREATURE:FORGOTTEN_BEAST_757:GUT -$CREATURE:FORGOTTEN_BEAST_757:STOMACH -$CREATURE:FORGOTTEN_BEAST_757:GIZZARD -%CREATURE:FORGOTTEN_BEAST_757:PANCREAS -#CREATURE:FORGOTTEN_BEAST_757:SPLEEN -#CREATURE:FORGOTTEN_BEAST_757:KIDNEY -#CREATURE:FORGOTTEN_BEAST_758:MUSCLE - CREATURE:FORGOTTEN_BEAST_758:EYE -"CREATURE:FORGOTTEN_BEAST_758:BRAIN -!CREATURE:FORGOTTEN_BEAST_758:LUNG -"CREATURE:FORGOTTEN_BEAST_758:HEART -"CREATURE:FORGOTTEN_BEAST_758:LIVER - CREATURE:FORGOTTEN_BEAST_758:GUT -$CREATURE:FORGOTTEN_BEAST_758:STOMACH -$CREATURE:FORGOTTEN_BEAST_758:GIZZARD -%CREATURE:FORGOTTEN_BEAST_758:PANCREAS -#CREATURE:FORGOTTEN_BEAST_758:SPLEEN -#CREATURE:FORGOTTEN_BEAST_758:KIDNEY -#CREATURE:FORGOTTEN_BEAST_759:MUSCLE - CREATURE:FORGOTTEN_BEAST_759:EYE -"CREATURE:FORGOTTEN_BEAST_759:BRAIN -!CREATURE:FORGOTTEN_BEAST_759:LUNG -"CREATURE:FORGOTTEN_BEAST_759:HEART -"CREATURE:FORGOTTEN_BEAST_759:LIVER - CREATURE:FORGOTTEN_BEAST_759:GUT -$CREATURE:FORGOTTEN_BEAST_759:STOMACH -$CREATURE:FORGOTTEN_BEAST_759:GIZZARD -%CREATURE:FORGOTTEN_BEAST_759:PANCREAS -#CREATURE:FORGOTTEN_BEAST_759:SPLEEN -#CREATURE:FORGOTTEN_BEAST_759:KIDNEY -#CREATURE:FORGOTTEN_BEAST_760:MUSCLE - CREATURE:FORGOTTEN_BEAST_760:EYE -"CREATURE:FORGOTTEN_BEAST_760:BRAIN -!CREATURE:FORGOTTEN_BEAST_760:LUNG -"CREATURE:FORGOTTEN_BEAST_760:HEART -"CREATURE:FORGOTTEN_BEAST_760:LIVER - CREATURE:FORGOTTEN_BEAST_760:GUT -$CREATURE:FORGOTTEN_BEAST_760:STOMACH -$CREATURE:FORGOTTEN_BEAST_760:GIZZARD -%CREATURE:FORGOTTEN_BEAST_760:PANCREAS -#CREATURE:FORGOTTEN_BEAST_760:SPLEEN -#CREATURE:FORGOTTEN_BEAST_760:KIDNEY -#CREATURE:FORGOTTEN_BEAST_761:MUSCLE - CREATURE:FORGOTTEN_BEAST_761:EYE -"CREATURE:FORGOTTEN_BEAST_761:BRAIN -!CREATURE:FORGOTTEN_BEAST_761:LUNG -"CREATURE:FORGOTTEN_BEAST_761:HEART -"CREATURE:FORGOTTEN_BEAST_761:LIVER - CREATURE:FORGOTTEN_BEAST_761:GUT -$CREATURE:FORGOTTEN_BEAST_761:STOMACH -$CREATURE:FORGOTTEN_BEAST_761:GIZZARD -%CREATURE:FORGOTTEN_BEAST_761:PANCREAS -#CREATURE:FORGOTTEN_BEAST_761:SPLEEN -#CREATURE:FORGOTTEN_BEAST_761:KIDNEY -#CREATURE:FORGOTTEN_BEAST_762:MUSCLE - CREATURE:FORGOTTEN_BEAST_762:EYE -"CREATURE:FORGOTTEN_BEAST_762:BRAIN -!CREATURE:FORGOTTEN_BEAST_762:LUNG -"CREATURE:FORGOTTEN_BEAST_762:HEART -"CREATURE:FORGOTTEN_BEAST_762:LIVER - CREATURE:FORGOTTEN_BEAST_762:GUT -$CREATURE:FORGOTTEN_BEAST_762:STOMACH -$CREATURE:FORGOTTEN_BEAST_762:GIZZARD -%CREATURE:FORGOTTEN_BEAST_762:PANCREAS -#CREATURE:FORGOTTEN_BEAST_762:SPLEEN -#CREATURE:FORGOTTEN_BEAST_762:KIDNEY -#CREATURE:FORGOTTEN_BEAST_764:MUSCLE - CREATURE:FORGOTTEN_BEAST_764:EYE -"CREATURE:FORGOTTEN_BEAST_764:BRAIN -!CREATURE:FORGOTTEN_BEAST_764:LUNG -"CREATURE:FORGOTTEN_BEAST_764:HEART -"CREATURE:FORGOTTEN_BEAST_764:LIVER - CREATURE:FORGOTTEN_BEAST_764:GUT -$CREATURE:FORGOTTEN_BEAST_764:STOMACH -$CREATURE:FORGOTTEN_BEAST_764:GIZZARD -%CREATURE:FORGOTTEN_BEAST_764:PANCREAS -#CREATURE:FORGOTTEN_BEAST_764:SPLEEN -#CREATURE:FORGOTTEN_BEAST_764:KIDNEY -#CREATURE:FORGOTTEN_BEAST_767:MUSCLE - CREATURE:FORGOTTEN_BEAST_767:EYE -"CREATURE:FORGOTTEN_BEAST_767:BRAIN -!CREATURE:FORGOTTEN_BEAST_767:LUNG -"CREATURE:FORGOTTEN_BEAST_767:HEART -"CREATURE:FORGOTTEN_BEAST_767:LIVER - CREATURE:FORGOTTEN_BEAST_767:GUT -$CREATURE:FORGOTTEN_BEAST_767:STOMACH -$CREATURE:FORGOTTEN_BEAST_767:GIZZARD -%CREATURE:FORGOTTEN_BEAST_767:PANCREAS -#CREATURE:FORGOTTEN_BEAST_767:SPLEEN -#CREATURE:FORGOTTEN_BEAST_767:KIDNEY -#CREATURE:FORGOTTEN_BEAST_768:MUSCLE - CREATURE:FORGOTTEN_BEAST_768:EYE -"CREATURE:FORGOTTEN_BEAST_768:BRAIN -!CREATURE:FORGOTTEN_BEAST_768:LUNG -"CREATURE:FORGOTTEN_BEAST_768:HEART -"CREATURE:FORGOTTEN_BEAST_768:LIVER - CREATURE:FORGOTTEN_BEAST_768:GUT -$CREATURE:FORGOTTEN_BEAST_768:STOMACH -$CREATURE:FORGOTTEN_BEAST_768:GIZZARD -%CREATURE:FORGOTTEN_BEAST_768:PANCREAS -#CREATURE:FORGOTTEN_BEAST_768:SPLEEN -#CREATURE:FORGOTTEN_BEAST_768:KIDNEY -#CREATURE:FORGOTTEN_BEAST_769:MUSCLE - CREATURE:FORGOTTEN_BEAST_769:EYE -"CREATURE:FORGOTTEN_BEAST_769:BRAIN -!CREATURE:FORGOTTEN_BEAST_769:LUNG -"CREATURE:FORGOTTEN_BEAST_769:HEART -"CREATURE:FORGOTTEN_BEAST_769:LIVER - CREATURE:FORGOTTEN_BEAST_769:GUT -$CREATURE:FORGOTTEN_BEAST_769:STOMACH -$CREATURE:FORGOTTEN_BEAST_769:GIZZARD -%CREATURE:FORGOTTEN_BEAST_769:PANCREAS -#CREATURE:FORGOTTEN_BEAST_769:SPLEEN -#CREATURE:FORGOTTEN_BEAST_769:KIDNEY -#CREATURE:FORGOTTEN_BEAST_770:MUSCLE - CREATURE:FORGOTTEN_BEAST_770:EYE -"CREATURE:FORGOTTEN_BEAST_770:BRAIN -!CREATURE:FORGOTTEN_BEAST_770:LUNG -"CREATURE:FORGOTTEN_BEAST_770:HEART -"CREATURE:FORGOTTEN_BEAST_770:LIVER - CREATURE:FORGOTTEN_BEAST_770:GUT -$CREATURE:FORGOTTEN_BEAST_770:STOMACH -$CREATURE:FORGOTTEN_BEAST_770:GIZZARD -%CREATURE:FORGOTTEN_BEAST_770:PANCREAS -#CREATURE:FORGOTTEN_BEAST_770:SPLEEN -#CREATURE:FORGOTTEN_BEAST_770:KIDNEY -#CREATURE:FORGOTTEN_BEAST_773:MUSCLE - CREATURE:FORGOTTEN_BEAST_773:EYE -"CREATURE:FORGOTTEN_BEAST_773:BRAIN -!CREATURE:FORGOTTEN_BEAST_773:LUNG -"CREATURE:FORGOTTEN_BEAST_773:HEART -"CREATURE:FORGOTTEN_BEAST_773:LIVER - CREATURE:FORGOTTEN_BEAST_773:GUT -$CREATURE:FORGOTTEN_BEAST_773:STOMACH -$CREATURE:FORGOTTEN_BEAST_773:GIZZARD -%CREATURE:FORGOTTEN_BEAST_773:PANCREAS -#CREATURE:FORGOTTEN_BEAST_773:SPLEEN -#CREATURE:FORGOTTEN_BEAST_773:KIDNEY -#CREATURE:FORGOTTEN_BEAST_774:MUSCLE - CREATURE:FORGOTTEN_BEAST_774:EYE -"CREATURE:FORGOTTEN_BEAST_774:BRAIN -!CREATURE:FORGOTTEN_BEAST_774:LUNG -"CREATURE:FORGOTTEN_BEAST_774:HEART -"CREATURE:FORGOTTEN_BEAST_774:LIVER - CREATURE:FORGOTTEN_BEAST_774:GUT -$CREATURE:FORGOTTEN_BEAST_774:STOMACH -$CREATURE:FORGOTTEN_BEAST_774:GIZZARD -%CREATURE:FORGOTTEN_BEAST_774:PANCREAS -#CREATURE:FORGOTTEN_BEAST_774:SPLEEN -#CREATURE:FORGOTTEN_BEAST_774:KIDNEY -#CREATURE:FORGOTTEN_BEAST_776:MUSCLE - CREATURE:FORGOTTEN_BEAST_776:EYE -"CREATURE:FORGOTTEN_BEAST_776:BRAIN -!CREATURE:FORGOTTEN_BEAST_776:LUNG -"CREATURE:FORGOTTEN_BEAST_776:HEART -"CREATURE:FORGOTTEN_BEAST_776:LIVER - CREATURE:FORGOTTEN_BEAST_776:GUT -$CREATURE:FORGOTTEN_BEAST_776:STOMACH -$CREATURE:FORGOTTEN_BEAST_776:GIZZARD -%CREATURE:FORGOTTEN_BEAST_776:PANCREAS -#CREATURE:FORGOTTEN_BEAST_776:SPLEEN -#CREATURE:FORGOTTEN_BEAST_776:KIDNEY -#CREATURE:FORGOTTEN_BEAST_777:MUSCLE - CREATURE:FORGOTTEN_BEAST_777:EYE -"CREATURE:FORGOTTEN_BEAST_777:BRAIN -!CREATURE:FORGOTTEN_BEAST_777:LUNG -"CREATURE:FORGOTTEN_BEAST_777:HEART -"CREATURE:FORGOTTEN_BEAST_777:LIVER - CREATURE:FORGOTTEN_BEAST_777:GUT -$CREATURE:FORGOTTEN_BEAST_777:STOMACH -$CREATURE:FORGOTTEN_BEAST_777:GIZZARD -%CREATURE:FORGOTTEN_BEAST_777:PANCREAS -#CREATURE:FORGOTTEN_BEAST_777:SPLEEN -#CREATURE:FORGOTTEN_BEAST_777:KIDNEY -#CREATURE:FORGOTTEN_BEAST_778:MUSCLE - CREATURE:FORGOTTEN_BEAST_778:EYE -"CREATURE:FORGOTTEN_BEAST_778:BRAIN -!CREATURE:FORGOTTEN_BEAST_778:LUNG -"CREATURE:FORGOTTEN_BEAST_778:HEART -"CREATURE:FORGOTTEN_BEAST_778:LIVER - CREATURE:FORGOTTEN_BEAST_778:GUT -$CREATURE:FORGOTTEN_BEAST_778:STOMACH -$CREATURE:FORGOTTEN_BEAST_778:GIZZARD -%CREATURE:FORGOTTEN_BEAST_778:PANCREAS -#CREATURE:FORGOTTEN_BEAST_778:SPLEEN -#CREATURE:FORGOTTEN_BEAST_778:KIDNEY -#CREATURE:FORGOTTEN_BEAST_779:MUSCLE - CREATURE:FORGOTTEN_BEAST_779:EYE -"CREATURE:FORGOTTEN_BEAST_779:BRAIN -!CREATURE:FORGOTTEN_BEAST_779:LUNG -"CREATURE:FORGOTTEN_BEAST_779:HEART -"CREATURE:FORGOTTEN_BEAST_779:LIVER - CREATURE:FORGOTTEN_BEAST_779:GUT -$CREATURE:FORGOTTEN_BEAST_779:STOMACH -$CREATURE:FORGOTTEN_BEAST_779:GIZZARD -%CREATURE:FORGOTTEN_BEAST_779:PANCREAS -#CREATURE:FORGOTTEN_BEAST_779:SPLEEN -#CREATURE:FORGOTTEN_BEAST_779:KIDNEY -#CREATURE:FORGOTTEN_BEAST_780:MUSCLE - CREATURE:FORGOTTEN_BEAST_780:EYE -"CREATURE:FORGOTTEN_BEAST_780:BRAIN -!CREATURE:FORGOTTEN_BEAST_780:LUNG -"CREATURE:FORGOTTEN_BEAST_780:HEART -"CREATURE:FORGOTTEN_BEAST_780:LIVER - CREATURE:FORGOTTEN_BEAST_780:GUT -$CREATURE:FORGOTTEN_BEAST_780:STOMACH -$CREATURE:FORGOTTEN_BEAST_780:GIZZARD -%CREATURE:FORGOTTEN_BEAST_780:PANCREAS -#CREATURE:FORGOTTEN_BEAST_780:SPLEEN -#CREATURE:FORGOTTEN_BEAST_780:KIDNEY -#CREATURE:FORGOTTEN_BEAST_781:MUSCLE - CREATURE:FORGOTTEN_BEAST_781:EYE -"CREATURE:FORGOTTEN_BEAST_781:BRAIN -!CREATURE:FORGOTTEN_BEAST_781:LUNG -"CREATURE:FORGOTTEN_BEAST_781:HEART -"CREATURE:FORGOTTEN_BEAST_781:LIVER - CREATURE:FORGOTTEN_BEAST_781:GUT -$CREATURE:FORGOTTEN_BEAST_781:STOMACH -$CREATURE:FORGOTTEN_BEAST_781:GIZZARD -%CREATURE:FORGOTTEN_BEAST_781:PANCREAS -#CREATURE:FORGOTTEN_BEAST_781:SPLEEN -#CREATURE:FORGOTTEN_BEAST_781:KIDNEY -#CREATURE:FORGOTTEN_BEAST_782:MUSCLE - CREATURE:FORGOTTEN_BEAST_782:EYE -"CREATURE:FORGOTTEN_BEAST_782:BRAIN -!CREATURE:FORGOTTEN_BEAST_782:LUNG -"CREATURE:FORGOTTEN_BEAST_782:HEART -"CREATURE:FORGOTTEN_BEAST_782:LIVER - CREATURE:FORGOTTEN_BEAST_782:GUT -$CREATURE:FORGOTTEN_BEAST_782:STOMACH -$CREATURE:FORGOTTEN_BEAST_782:GIZZARD -%CREATURE:FORGOTTEN_BEAST_782:PANCREAS -#CREATURE:FORGOTTEN_BEAST_782:SPLEEN -#CREATURE:FORGOTTEN_BEAST_782:KIDNEY -#CREATURE:FORGOTTEN_BEAST_783:MUSCLE - CREATURE:FORGOTTEN_BEAST_783:EYE -"CREATURE:FORGOTTEN_BEAST_783:BRAIN -!CREATURE:FORGOTTEN_BEAST_783:LUNG -"CREATURE:FORGOTTEN_BEAST_783:HEART -"CREATURE:FORGOTTEN_BEAST_783:LIVER - CREATURE:FORGOTTEN_BEAST_783:GUT -$CREATURE:FORGOTTEN_BEAST_783:STOMACH -$CREATURE:FORGOTTEN_BEAST_783:GIZZARD -%CREATURE:FORGOTTEN_BEAST_783:PANCREAS -#CREATURE:FORGOTTEN_BEAST_783:SPLEEN -#CREATURE:FORGOTTEN_BEAST_783:KIDNEY -#CREATURE:FORGOTTEN_BEAST_784:MUSCLE - CREATURE:FORGOTTEN_BEAST_784:EYE -"CREATURE:FORGOTTEN_BEAST_784:BRAIN -!CREATURE:FORGOTTEN_BEAST_784:LUNG -"CREATURE:FORGOTTEN_BEAST_784:HEART -"CREATURE:FORGOTTEN_BEAST_784:LIVER - CREATURE:FORGOTTEN_BEAST_784:GUT -$CREATURE:FORGOTTEN_BEAST_784:STOMACH -$CREATURE:FORGOTTEN_BEAST_784:GIZZARD -%CREATURE:FORGOTTEN_BEAST_784:PANCREAS -#CREATURE:FORGOTTEN_BEAST_784:SPLEEN -#CREATURE:FORGOTTEN_BEAST_784:KIDNEY -#CREATURE:FORGOTTEN_BEAST_785:MUSCLE - CREATURE:FORGOTTEN_BEAST_785:EYE -"CREATURE:FORGOTTEN_BEAST_785:BRAIN -!CREATURE:FORGOTTEN_BEAST_785:LUNG -"CREATURE:FORGOTTEN_BEAST_785:HEART -"CREATURE:FORGOTTEN_BEAST_785:LIVER - CREATURE:FORGOTTEN_BEAST_785:GUT -$CREATURE:FORGOTTEN_BEAST_785:STOMACH -$CREATURE:FORGOTTEN_BEAST_785:GIZZARD -%CREATURE:FORGOTTEN_BEAST_785:PANCREAS -#CREATURE:FORGOTTEN_BEAST_785:SPLEEN -#CREATURE:FORGOTTEN_BEAST_785:KIDNEY -#CREATURE:FORGOTTEN_BEAST_786:MUSCLE - CREATURE:FORGOTTEN_BEAST_786:EYE -"CREATURE:FORGOTTEN_BEAST_786:BRAIN -!CREATURE:FORGOTTEN_BEAST_786:LUNG -"CREATURE:FORGOTTEN_BEAST_786:HEART -"CREATURE:FORGOTTEN_BEAST_786:LIVER - CREATURE:FORGOTTEN_BEAST_786:GUT -$CREATURE:FORGOTTEN_BEAST_786:STOMACH -$CREATURE:FORGOTTEN_BEAST_786:GIZZARD -%CREATURE:FORGOTTEN_BEAST_786:PANCREAS -#CREATURE:FORGOTTEN_BEAST_786:SPLEEN -#CREATURE:FORGOTTEN_BEAST_786:KIDNEY -#CREATURE:FORGOTTEN_BEAST_787:MUSCLE - CREATURE:FORGOTTEN_BEAST_787:EYE -"CREATURE:FORGOTTEN_BEAST_787:BRAIN -!CREATURE:FORGOTTEN_BEAST_787:LUNG -"CREATURE:FORGOTTEN_BEAST_787:HEART -"CREATURE:FORGOTTEN_BEAST_787:LIVER - CREATURE:FORGOTTEN_BEAST_787:GUT -$CREATURE:FORGOTTEN_BEAST_787:STOMACH -$CREATURE:FORGOTTEN_BEAST_787:GIZZARD -%CREATURE:FORGOTTEN_BEAST_787:PANCREAS -#CREATURE:FORGOTTEN_BEAST_787:SPLEEN -#CREATURE:FORGOTTEN_BEAST_787:KIDNEY -#CREATURE:FORGOTTEN_BEAST_789:MUSCLE - CREATURE:FORGOTTEN_BEAST_789:EYE -"CREATURE:FORGOTTEN_BEAST_789:BRAIN -!CREATURE:FORGOTTEN_BEAST_789:LUNG -"CREATURE:FORGOTTEN_BEAST_789:HEART -"CREATURE:FORGOTTEN_BEAST_789:LIVER - CREATURE:FORGOTTEN_BEAST_789:GUT -$CREATURE:FORGOTTEN_BEAST_789:STOMACH -$CREATURE:FORGOTTEN_BEAST_789:GIZZARD -%CREATURE:FORGOTTEN_BEAST_789:PANCREAS -#CREATURE:FORGOTTEN_BEAST_789:SPLEEN -#CREATURE:FORGOTTEN_BEAST_789:KIDNEY -#CREATURE:FORGOTTEN_BEAST_792:MUSCLE - CREATURE:FORGOTTEN_BEAST_792:EYE -"CREATURE:FORGOTTEN_BEAST_792:BRAIN -!CREATURE:FORGOTTEN_BEAST_792:LUNG -"CREATURE:FORGOTTEN_BEAST_792:HEART -"CREATURE:FORGOTTEN_BEAST_792:LIVER - CREATURE:FORGOTTEN_BEAST_792:GUT -$CREATURE:FORGOTTEN_BEAST_792:STOMACH -$CREATURE:FORGOTTEN_BEAST_792:GIZZARD -%CREATURE:FORGOTTEN_BEAST_792:PANCREAS -#CREATURE:FORGOTTEN_BEAST_792:SPLEEN -#CREATURE:FORGOTTEN_BEAST_792:KIDNEY -#CREATURE:FORGOTTEN_BEAST_793:MUSCLE - CREATURE:FORGOTTEN_BEAST_793:EYE -"CREATURE:FORGOTTEN_BEAST_793:BRAIN -!CREATURE:FORGOTTEN_BEAST_793:LUNG -"CREATURE:FORGOTTEN_BEAST_793:HEART -"CREATURE:FORGOTTEN_BEAST_793:LIVER - CREATURE:FORGOTTEN_BEAST_793:GUT -$CREATURE:FORGOTTEN_BEAST_793:STOMACH -$CREATURE:FORGOTTEN_BEAST_793:GIZZARD -%CREATURE:FORGOTTEN_BEAST_793:PANCREAS -#CREATURE:FORGOTTEN_BEAST_793:SPLEEN -#CREATURE:FORGOTTEN_BEAST_793:KIDNEY -#CREATURE:FORGOTTEN_BEAST_794:MUSCLE - CREATURE:FORGOTTEN_BEAST_794:EYE -"CREATURE:FORGOTTEN_BEAST_794:BRAIN -!CREATURE:FORGOTTEN_BEAST_794:LUNG -"CREATURE:FORGOTTEN_BEAST_794:HEART -"CREATURE:FORGOTTEN_BEAST_794:LIVER - CREATURE:FORGOTTEN_BEAST_794:GUT -$CREATURE:FORGOTTEN_BEAST_794:STOMACH -$CREATURE:FORGOTTEN_BEAST_794:GIZZARD -%CREATURE:FORGOTTEN_BEAST_794:PANCREAS -#CREATURE:FORGOTTEN_BEAST_794:SPLEEN -#CREATURE:FORGOTTEN_BEAST_794:KIDNEY -#CREATURE:FORGOTTEN_BEAST_795:MUSCLE - CREATURE:FORGOTTEN_BEAST_795:EYE -"CREATURE:FORGOTTEN_BEAST_795:BRAIN -!CREATURE:FORGOTTEN_BEAST_795:LUNG -"CREATURE:FORGOTTEN_BEAST_795:HEART -"CREATURE:FORGOTTEN_BEAST_795:LIVER - CREATURE:FORGOTTEN_BEAST_795:GUT -$CREATURE:FORGOTTEN_BEAST_795:STOMACH -$CREATURE:FORGOTTEN_BEAST_795:GIZZARD -%CREATURE:FORGOTTEN_BEAST_795:PANCREAS -#CREATURE:FORGOTTEN_BEAST_795:SPLEEN -#CREATURE:FORGOTTEN_BEAST_795:KIDNEY -#CREATURE:FORGOTTEN_BEAST_796:MUSCLE - CREATURE:FORGOTTEN_BEAST_796:EYE -"CREATURE:FORGOTTEN_BEAST_796:BRAIN -!CREATURE:FORGOTTEN_BEAST_796:LUNG -"CREATURE:FORGOTTEN_BEAST_796:HEART -"CREATURE:FORGOTTEN_BEAST_796:LIVER - CREATURE:FORGOTTEN_BEAST_796:GUT -$CREATURE:FORGOTTEN_BEAST_796:STOMACH -$CREATURE:FORGOTTEN_BEAST_796:GIZZARD -%CREATURE:FORGOTTEN_BEAST_796:PANCREAS -#CREATURE:FORGOTTEN_BEAST_796:SPLEEN -#CREATURE:FORGOTTEN_BEAST_796:KIDNEY -#CREATURE:FORGOTTEN_BEAST_799:MUSCLE - CREATURE:FORGOTTEN_BEAST_799:EYE -"CREATURE:FORGOTTEN_BEAST_799:BRAIN -!CREATURE:FORGOTTEN_BEAST_799:LUNG -"CREATURE:FORGOTTEN_BEAST_799:HEART -"CREATURE:FORGOTTEN_BEAST_799:LIVER - CREATURE:FORGOTTEN_BEAST_799:GUT -$CREATURE:FORGOTTEN_BEAST_799:STOMACH -$CREATURE:FORGOTTEN_BEAST_799:GIZZARD -%CREATURE:FORGOTTEN_BEAST_799:PANCREAS -#CREATURE:FORGOTTEN_BEAST_799:SPLEEN -#CREATURE:FORGOTTEN_BEAST_799:KIDNEY -#CREATURE:FORGOTTEN_BEAST_800:MUSCLE - CREATURE:FORGOTTEN_BEAST_800:EYE -"CREATURE:FORGOTTEN_BEAST_800:BRAIN -!CREATURE:FORGOTTEN_BEAST_800:LUNG -"CREATURE:FORGOTTEN_BEAST_800:HEART -"CREATURE:FORGOTTEN_BEAST_800:LIVER - CREATURE:FORGOTTEN_BEAST_800:GUT -$CREATURE:FORGOTTEN_BEAST_800:STOMACH -$CREATURE:FORGOTTEN_BEAST_800:GIZZARD -%CREATURE:FORGOTTEN_BEAST_800:PANCREAS -#CREATURE:FORGOTTEN_BEAST_800:SPLEEN -#CREATURE:FORGOTTEN_BEAST_800:KIDNEY -#CREATURE:FORGOTTEN_BEAST_801:MUSCLE - CREATURE:FORGOTTEN_BEAST_801:EYE -"CREATURE:FORGOTTEN_BEAST_801:BRAIN -!CREATURE:FORGOTTEN_BEAST_801:LUNG -"CREATURE:FORGOTTEN_BEAST_801:HEART -"CREATURE:FORGOTTEN_BEAST_801:LIVER - CREATURE:FORGOTTEN_BEAST_801:GUT -$CREATURE:FORGOTTEN_BEAST_801:STOMACH -$CREATURE:FORGOTTEN_BEAST_801:GIZZARD -%CREATURE:FORGOTTEN_BEAST_801:PANCREAS -#CREATURE:FORGOTTEN_BEAST_801:SPLEEN -#CREATURE:FORGOTTEN_BEAST_801:KIDNEY -#CREATURE:FORGOTTEN_BEAST_802:MUSCLE - CREATURE:FORGOTTEN_BEAST_802:EYE -"CREATURE:FORGOTTEN_BEAST_802:BRAIN -!CREATURE:FORGOTTEN_BEAST_802:LUNG -"CREATURE:FORGOTTEN_BEAST_802:HEART -"CREATURE:FORGOTTEN_BEAST_802:LIVER - CREATURE:FORGOTTEN_BEAST_802:GUT -$CREATURE:FORGOTTEN_BEAST_802:STOMACH -$CREATURE:FORGOTTEN_BEAST_802:GIZZARD -%CREATURE:FORGOTTEN_BEAST_802:PANCREAS -#CREATURE:FORGOTTEN_BEAST_802:SPLEEN -#CREATURE:FORGOTTEN_BEAST_802:KIDNEY -#CREATURE:FORGOTTEN_BEAST_803:MUSCLE - CREATURE:FORGOTTEN_BEAST_803:EYE -"CREATURE:FORGOTTEN_BEAST_803:BRAIN -!CREATURE:FORGOTTEN_BEAST_803:LUNG -"CREATURE:FORGOTTEN_BEAST_803:HEART -"CREATURE:FORGOTTEN_BEAST_803:LIVER - CREATURE:FORGOTTEN_BEAST_803:GUT -$CREATURE:FORGOTTEN_BEAST_803:STOMACH -$CREATURE:FORGOTTEN_BEAST_803:GIZZARD -%CREATURE:FORGOTTEN_BEAST_803:PANCREAS -#CREATURE:FORGOTTEN_BEAST_803:SPLEEN -#CREATURE:FORGOTTEN_BEAST_803:KIDNEY -#CREATURE:FORGOTTEN_BEAST_804:MUSCLE - CREATURE:FORGOTTEN_BEAST_804:EYE -"CREATURE:FORGOTTEN_BEAST_804:BRAIN -!CREATURE:FORGOTTEN_BEAST_804:LUNG -"CREATURE:FORGOTTEN_BEAST_804:HEART -"CREATURE:FORGOTTEN_BEAST_804:LIVER - CREATURE:FORGOTTEN_BEAST_804:GUT -$CREATURE:FORGOTTEN_BEAST_804:STOMACH -$CREATURE:FORGOTTEN_BEAST_804:GIZZARD -%CREATURE:FORGOTTEN_BEAST_804:PANCREAS -#CREATURE:FORGOTTEN_BEAST_804:SPLEEN -#CREATURE:FORGOTTEN_BEAST_804:KIDNEY -#CREATURE:FORGOTTEN_BEAST_806:MUSCLE - CREATURE:FORGOTTEN_BEAST_806:EYE -"CREATURE:FORGOTTEN_BEAST_806:BRAIN -!CREATURE:FORGOTTEN_BEAST_806:LUNG -"CREATURE:FORGOTTEN_BEAST_806:HEART -"CREATURE:FORGOTTEN_BEAST_806:LIVER - CREATURE:FORGOTTEN_BEAST_806:GUT -$CREATURE:FORGOTTEN_BEAST_806:STOMACH -$CREATURE:FORGOTTEN_BEAST_806:GIZZARD -%CREATURE:FORGOTTEN_BEAST_806:PANCREAS -#CREATURE:FORGOTTEN_BEAST_806:SPLEEN -#CREATURE:FORGOTTEN_BEAST_806:KIDNEY -#CREATURE:FORGOTTEN_BEAST_807:MUSCLE - CREATURE:FORGOTTEN_BEAST_807:EYE -"CREATURE:FORGOTTEN_BEAST_807:BRAIN -!CREATURE:FORGOTTEN_BEAST_807:LUNG -"CREATURE:FORGOTTEN_BEAST_807:HEART -"CREATURE:FORGOTTEN_BEAST_807:LIVER - CREATURE:FORGOTTEN_BEAST_807:GUT -$CREATURE:FORGOTTEN_BEAST_807:STOMACH -$CREATURE:FORGOTTEN_BEAST_807:GIZZARD -%CREATURE:FORGOTTEN_BEAST_807:PANCREAS -#CREATURE:FORGOTTEN_BEAST_807:SPLEEN -#CREATURE:FORGOTTEN_BEAST_807:KIDNEY -#CREATURE:FORGOTTEN_BEAST_809:MUSCLE - CREATURE:FORGOTTEN_BEAST_809:EYE -"CREATURE:FORGOTTEN_BEAST_809:BRAIN -!CREATURE:FORGOTTEN_BEAST_809:LUNG -"CREATURE:FORGOTTEN_BEAST_809:HEART -"CREATURE:FORGOTTEN_BEAST_809:LIVER - CREATURE:FORGOTTEN_BEAST_809:GUT -$CREATURE:FORGOTTEN_BEAST_809:STOMACH -$CREATURE:FORGOTTEN_BEAST_809:GIZZARD -%CREATURE:FORGOTTEN_BEAST_809:PANCREAS -#CREATURE:FORGOTTEN_BEAST_809:SPLEEN -#CREATURE:FORGOTTEN_BEAST_809:KIDNEY -#CREATURE:FORGOTTEN_BEAST_810:MUSCLE - CREATURE:FORGOTTEN_BEAST_810:EYE -"CREATURE:FORGOTTEN_BEAST_810:BRAIN -!CREATURE:FORGOTTEN_BEAST_810:LUNG -"CREATURE:FORGOTTEN_BEAST_810:HEART -"CREATURE:FORGOTTEN_BEAST_810:LIVER - CREATURE:FORGOTTEN_BEAST_810:GUT -$CREATURE:FORGOTTEN_BEAST_810:STOMACH -$CREATURE:FORGOTTEN_BEAST_810:GIZZARD -%CREATURE:FORGOTTEN_BEAST_810:PANCREAS -#CREATURE:FORGOTTEN_BEAST_810:SPLEEN -#CREATURE:FORGOTTEN_BEAST_810:KIDNEY -#CREATURE:FORGOTTEN_BEAST_811:MUSCLE - CREATURE:FORGOTTEN_BEAST_811:EYE -"CREATURE:FORGOTTEN_BEAST_811:BRAIN -!CREATURE:FORGOTTEN_BEAST_811:LUNG -"CREATURE:FORGOTTEN_BEAST_811:HEART -"CREATURE:FORGOTTEN_BEAST_811:LIVER - CREATURE:FORGOTTEN_BEAST_811:GUT -$CREATURE:FORGOTTEN_BEAST_811:STOMACH -$CREATURE:FORGOTTEN_BEAST_811:GIZZARD -%CREATURE:FORGOTTEN_BEAST_811:PANCREAS -#CREATURE:FORGOTTEN_BEAST_811:SPLEEN -#CREATURE:FORGOTTEN_BEAST_811:KIDNEY -#CREATURE:FORGOTTEN_BEAST_812:MUSCLE - CREATURE:FORGOTTEN_BEAST_812:EYE -"CREATURE:FORGOTTEN_BEAST_812:BRAIN -!CREATURE:FORGOTTEN_BEAST_812:LUNG -"CREATURE:FORGOTTEN_BEAST_812:HEART -"CREATURE:FORGOTTEN_BEAST_812:LIVER - CREATURE:FORGOTTEN_BEAST_812:GUT -$CREATURE:FORGOTTEN_BEAST_812:STOMACH -$CREATURE:FORGOTTEN_BEAST_812:GIZZARD -%CREATURE:FORGOTTEN_BEAST_812:PANCREAS -#CREATURE:FORGOTTEN_BEAST_812:SPLEEN -#CREATURE:FORGOTTEN_BEAST_812:KIDNEY -#CREATURE:FORGOTTEN_BEAST_815:MUSCLE - CREATURE:FORGOTTEN_BEAST_815:EYE -"CREATURE:FORGOTTEN_BEAST_815:BRAIN -!CREATURE:FORGOTTEN_BEAST_815:LUNG -"CREATURE:FORGOTTEN_BEAST_815:HEART -"CREATURE:FORGOTTEN_BEAST_815:LIVER - CREATURE:FORGOTTEN_BEAST_815:GUT -$CREATURE:FORGOTTEN_BEAST_815:STOMACH -$CREATURE:FORGOTTEN_BEAST_815:GIZZARD -%CREATURE:FORGOTTEN_BEAST_815:PANCREAS -#CREATURE:FORGOTTEN_BEAST_815:SPLEEN -#CREATURE:FORGOTTEN_BEAST_815:KIDNEY -#CREATURE:FORGOTTEN_BEAST_817:MUSCLE - CREATURE:FORGOTTEN_BEAST_817:EYE -"CREATURE:FORGOTTEN_BEAST_817:BRAIN -!CREATURE:FORGOTTEN_BEAST_817:LUNG -"CREATURE:FORGOTTEN_BEAST_817:HEART -"CREATURE:FORGOTTEN_BEAST_817:LIVER - CREATURE:FORGOTTEN_BEAST_817:GUT -$CREATURE:FORGOTTEN_BEAST_817:STOMACH -$CREATURE:FORGOTTEN_BEAST_817:GIZZARD -%CREATURE:FORGOTTEN_BEAST_817:PANCREAS -#CREATURE:FORGOTTEN_BEAST_817:SPLEEN -#CREATURE:FORGOTTEN_BEAST_817:KIDNEY -#CREATURE:FORGOTTEN_BEAST_818:MUSCLE - CREATURE:FORGOTTEN_BEAST_818:EYE -"CREATURE:FORGOTTEN_BEAST_818:BRAIN -!CREATURE:FORGOTTEN_BEAST_818:LUNG -"CREATURE:FORGOTTEN_BEAST_818:HEART -"CREATURE:FORGOTTEN_BEAST_818:LIVER - CREATURE:FORGOTTEN_BEAST_818:GUT -$CREATURE:FORGOTTEN_BEAST_818:STOMACH -$CREATURE:FORGOTTEN_BEAST_818:GIZZARD -%CREATURE:FORGOTTEN_BEAST_818:PANCREAS -#CREATURE:FORGOTTEN_BEAST_818:SPLEEN -#CREATURE:FORGOTTEN_BEAST_818:KIDNEY -#CREATURE:FORGOTTEN_BEAST_819:MUSCLE - CREATURE:FORGOTTEN_BEAST_819:EYE -"CREATURE:FORGOTTEN_BEAST_819:BRAIN -!CREATURE:FORGOTTEN_BEAST_819:LUNG -"CREATURE:FORGOTTEN_BEAST_819:HEART -"CREATURE:FORGOTTEN_BEAST_819:LIVER - CREATURE:FORGOTTEN_BEAST_819:GUT -$CREATURE:FORGOTTEN_BEAST_819:STOMACH -$CREATURE:FORGOTTEN_BEAST_819:GIZZARD -%CREATURE:FORGOTTEN_BEAST_819:PANCREAS -#CREATURE:FORGOTTEN_BEAST_819:SPLEEN -#CREATURE:FORGOTTEN_BEAST_819:KIDNEY -#CREATURE:FORGOTTEN_BEAST_820:MUSCLE - CREATURE:FORGOTTEN_BEAST_820:EYE -"CREATURE:FORGOTTEN_BEAST_820:BRAIN -!CREATURE:FORGOTTEN_BEAST_820:LUNG -"CREATURE:FORGOTTEN_BEAST_820:HEART -"CREATURE:FORGOTTEN_BEAST_820:LIVER - CREATURE:FORGOTTEN_BEAST_820:GUT -$CREATURE:FORGOTTEN_BEAST_820:STOMACH -$CREATURE:FORGOTTEN_BEAST_820:GIZZARD -%CREATURE:FORGOTTEN_BEAST_820:PANCREAS -#CREATURE:FORGOTTEN_BEAST_820:SPLEEN -#CREATURE:FORGOTTEN_BEAST_820:KIDNEY -#CREATURE:FORGOTTEN_BEAST_821:MUSCLE - CREATURE:FORGOTTEN_BEAST_821:EYE -"CREATURE:FORGOTTEN_BEAST_821:BRAIN -!CREATURE:FORGOTTEN_BEAST_821:LUNG -"CREATURE:FORGOTTEN_BEAST_821:HEART -"CREATURE:FORGOTTEN_BEAST_821:LIVER - CREATURE:FORGOTTEN_BEAST_821:GUT -$CREATURE:FORGOTTEN_BEAST_821:STOMACH -$CREATURE:FORGOTTEN_BEAST_821:GIZZARD -%CREATURE:FORGOTTEN_BEAST_821:PANCREAS -#CREATURE:FORGOTTEN_BEAST_821:SPLEEN -#CREATURE:FORGOTTEN_BEAST_821:KIDNEY -#CREATURE:FORGOTTEN_BEAST_822:MUSCLE - CREATURE:FORGOTTEN_BEAST_822:EYE -"CREATURE:FORGOTTEN_BEAST_822:BRAIN -!CREATURE:FORGOTTEN_BEAST_822:LUNG -"CREATURE:FORGOTTEN_BEAST_822:HEART -"CREATURE:FORGOTTEN_BEAST_822:LIVER - CREATURE:FORGOTTEN_BEAST_822:GUT -$CREATURE:FORGOTTEN_BEAST_822:STOMACH -$CREATURE:FORGOTTEN_BEAST_822:GIZZARD -%CREATURE:FORGOTTEN_BEAST_822:PANCREAS -#CREATURE:FORGOTTEN_BEAST_822:SPLEEN -#CREATURE:FORGOTTEN_BEAST_822:KIDNEY -#CREATURE:FORGOTTEN_BEAST_824:MUSCLE - CREATURE:FORGOTTEN_BEAST_824:EYE -"CREATURE:FORGOTTEN_BEAST_824:BRAIN -!CREATURE:FORGOTTEN_BEAST_824:LUNG -"CREATURE:FORGOTTEN_BEAST_824:HEART -"CREATURE:FORGOTTEN_BEAST_824:LIVER - CREATURE:FORGOTTEN_BEAST_824:GUT -$CREATURE:FORGOTTEN_BEAST_824:STOMACH -$CREATURE:FORGOTTEN_BEAST_824:GIZZARD -%CREATURE:FORGOTTEN_BEAST_824:PANCREAS -#CREATURE:FORGOTTEN_BEAST_824:SPLEEN -#CREATURE:FORGOTTEN_BEAST_824:KIDNEY -#CREATURE:FORGOTTEN_BEAST_825:MUSCLE - CREATURE:FORGOTTEN_BEAST_825:EYE -"CREATURE:FORGOTTEN_BEAST_825:BRAIN -!CREATURE:FORGOTTEN_BEAST_825:LUNG -"CREATURE:FORGOTTEN_BEAST_825:HEART -"CREATURE:FORGOTTEN_BEAST_825:LIVER - CREATURE:FORGOTTEN_BEAST_825:GUT -$CREATURE:FORGOTTEN_BEAST_825:STOMACH -$CREATURE:FORGOTTEN_BEAST_825:GIZZARD -%CREATURE:FORGOTTEN_BEAST_825:PANCREAS -#CREATURE:FORGOTTEN_BEAST_825:SPLEEN -#CREATURE:FORGOTTEN_BEAST_825:KIDNEY -#CREATURE:FORGOTTEN_BEAST_826:MUSCLE - CREATURE:FORGOTTEN_BEAST_826:EYE -"CREATURE:FORGOTTEN_BEAST_826:BRAIN -!CREATURE:FORGOTTEN_BEAST_826:LUNG -"CREATURE:FORGOTTEN_BEAST_826:HEART -"CREATURE:FORGOTTEN_BEAST_826:LIVER - CREATURE:FORGOTTEN_BEAST_826:GUT -$CREATURE:FORGOTTEN_BEAST_826:STOMACH -$CREATURE:FORGOTTEN_BEAST_826:GIZZARD -%CREATURE:FORGOTTEN_BEAST_826:PANCREAS -#CREATURE:FORGOTTEN_BEAST_826:SPLEEN -#CREATURE:FORGOTTEN_BEAST_826:KIDNEY -#CREATURE:FORGOTTEN_BEAST_827:MUSCLE - CREATURE:FORGOTTEN_BEAST_827:EYE -"CREATURE:FORGOTTEN_BEAST_827:BRAIN -!CREATURE:FORGOTTEN_BEAST_827:LUNG -"CREATURE:FORGOTTEN_BEAST_827:HEART -"CREATURE:FORGOTTEN_BEAST_827:LIVER - CREATURE:FORGOTTEN_BEAST_827:GUT -$CREATURE:FORGOTTEN_BEAST_827:STOMACH -$CREATURE:FORGOTTEN_BEAST_827:GIZZARD -%CREATURE:FORGOTTEN_BEAST_827:PANCREAS -#CREATURE:FORGOTTEN_BEAST_827:SPLEEN -#CREATURE:FORGOTTEN_BEAST_827:KIDNEY -#CREATURE:FORGOTTEN_BEAST_828:MUSCLE - CREATURE:FORGOTTEN_BEAST_828:EYE -"CREATURE:FORGOTTEN_BEAST_828:BRAIN -!CREATURE:FORGOTTEN_BEAST_828:LUNG -"CREATURE:FORGOTTEN_BEAST_828:HEART -"CREATURE:FORGOTTEN_BEAST_828:LIVER - CREATURE:FORGOTTEN_BEAST_828:GUT -$CREATURE:FORGOTTEN_BEAST_828:STOMACH -$CREATURE:FORGOTTEN_BEAST_828:GIZZARD -%CREATURE:FORGOTTEN_BEAST_828:PANCREAS -#CREATURE:FORGOTTEN_BEAST_828:SPLEEN -#CREATURE:FORGOTTEN_BEAST_828:KIDNEY -#CREATURE:FORGOTTEN_BEAST_831:MUSCLE - CREATURE:FORGOTTEN_BEAST_831:EYE -"CREATURE:FORGOTTEN_BEAST_831:BRAIN -!CREATURE:FORGOTTEN_BEAST_831:LUNG -"CREATURE:FORGOTTEN_BEAST_831:HEART -"CREATURE:FORGOTTEN_BEAST_831:LIVER - CREATURE:FORGOTTEN_BEAST_831:GUT -$CREATURE:FORGOTTEN_BEAST_831:STOMACH -$CREATURE:FORGOTTEN_BEAST_831:GIZZARD -%CREATURE:FORGOTTEN_BEAST_831:PANCREAS -#CREATURE:FORGOTTEN_BEAST_831:SPLEEN -#CREATURE:FORGOTTEN_BEAST_831:KIDNEY -#CREATURE:FORGOTTEN_BEAST_833:MUSCLE - CREATURE:FORGOTTEN_BEAST_833:EYE -"CREATURE:FORGOTTEN_BEAST_833:BRAIN -!CREATURE:FORGOTTEN_BEAST_833:LUNG -"CREATURE:FORGOTTEN_BEAST_833:HEART -"CREATURE:FORGOTTEN_BEAST_833:LIVER - CREATURE:FORGOTTEN_BEAST_833:GUT -$CREATURE:FORGOTTEN_BEAST_833:STOMACH -$CREATURE:FORGOTTEN_BEAST_833:GIZZARD -%CREATURE:FORGOTTEN_BEAST_833:PANCREAS -#CREATURE:FORGOTTEN_BEAST_833:SPLEEN -#CREATURE:FORGOTTEN_BEAST_833:KIDNEY -#CREATURE:FORGOTTEN_BEAST_835:MUSCLE - CREATURE:FORGOTTEN_BEAST_835:EYE -"CREATURE:FORGOTTEN_BEAST_835:BRAIN -!CREATURE:FORGOTTEN_BEAST_835:LUNG -"CREATURE:FORGOTTEN_BEAST_835:HEART -"CREATURE:FORGOTTEN_BEAST_835:LIVER - CREATURE:FORGOTTEN_BEAST_835:GUT -$CREATURE:FORGOTTEN_BEAST_835:STOMACH -$CREATURE:FORGOTTEN_BEAST_835:GIZZARD -%CREATURE:FORGOTTEN_BEAST_835:PANCREAS -#CREATURE:FORGOTTEN_BEAST_835:SPLEEN -#CREATURE:FORGOTTEN_BEAST_835:KIDNEY -#CREATURE:FORGOTTEN_BEAST_837:MUSCLE - CREATURE:FORGOTTEN_BEAST_837:EYE -"CREATURE:FORGOTTEN_BEAST_837:BRAIN -!CREATURE:FORGOTTEN_BEAST_837:LUNG -"CREATURE:FORGOTTEN_BEAST_837:HEART -"CREATURE:FORGOTTEN_BEAST_837:LIVER - CREATURE:FORGOTTEN_BEAST_837:GUT -$CREATURE:FORGOTTEN_BEAST_837:STOMACH -$CREATURE:FORGOTTEN_BEAST_837:GIZZARD -%CREATURE:FORGOTTEN_BEAST_837:PANCREAS -#CREATURE:FORGOTTEN_BEAST_837:SPLEEN -#CREATURE:FORGOTTEN_BEAST_837:KIDNEY -#CREATURE:FORGOTTEN_BEAST_838:MUSCLE - CREATURE:FORGOTTEN_BEAST_838:EYE -"CREATURE:FORGOTTEN_BEAST_838:BRAIN -!CREATURE:FORGOTTEN_BEAST_838:LUNG -"CREATURE:FORGOTTEN_BEAST_838:HEART -"CREATURE:FORGOTTEN_BEAST_838:LIVER - CREATURE:FORGOTTEN_BEAST_838:GUT -$CREATURE:FORGOTTEN_BEAST_838:STOMACH -$CREATURE:FORGOTTEN_BEAST_838:GIZZARD -%CREATURE:FORGOTTEN_BEAST_838:PANCREAS -#CREATURE:FORGOTTEN_BEAST_838:SPLEEN -#CREATURE:FORGOTTEN_BEAST_838:KIDNEY -#CREATURE:FORGOTTEN_BEAST_842:MUSCLE - CREATURE:FORGOTTEN_BEAST_842:EYE -"CREATURE:FORGOTTEN_BEAST_842:BRAIN -!CREATURE:FORGOTTEN_BEAST_842:LUNG -"CREATURE:FORGOTTEN_BEAST_842:HEART -"CREATURE:FORGOTTEN_BEAST_842:LIVER - CREATURE:FORGOTTEN_BEAST_842:GUT -$CREATURE:FORGOTTEN_BEAST_842:STOMACH -$CREATURE:FORGOTTEN_BEAST_842:GIZZARD -%CREATURE:FORGOTTEN_BEAST_842:PANCREAS -#CREATURE:FORGOTTEN_BEAST_842:SPLEEN -#CREATURE:FORGOTTEN_BEAST_842:KIDNEY -#CREATURE:FORGOTTEN_BEAST_843:MUSCLE - CREATURE:FORGOTTEN_BEAST_843:EYE -"CREATURE:FORGOTTEN_BEAST_843:BRAIN -!CREATURE:FORGOTTEN_BEAST_843:LUNG -"CREATURE:FORGOTTEN_BEAST_843:HEART -"CREATURE:FORGOTTEN_BEAST_843:LIVER - CREATURE:FORGOTTEN_BEAST_843:GUT -$CREATURE:FORGOTTEN_BEAST_843:STOMACH -$CREATURE:FORGOTTEN_BEAST_843:GIZZARD -%CREATURE:FORGOTTEN_BEAST_843:PANCREAS -#CREATURE:FORGOTTEN_BEAST_843:SPLEEN -#CREATURE:FORGOTTEN_BEAST_843:KIDNEY -#CREATURE:FORGOTTEN_BEAST_844:MUSCLE - CREATURE:FORGOTTEN_BEAST_844:EYE -"CREATURE:FORGOTTEN_BEAST_844:BRAIN -!CREATURE:FORGOTTEN_BEAST_844:LUNG -"CREATURE:FORGOTTEN_BEAST_844:HEART -"CREATURE:FORGOTTEN_BEAST_844:LIVER - CREATURE:FORGOTTEN_BEAST_844:GUT -$CREATURE:FORGOTTEN_BEAST_844:STOMACH -$CREATURE:FORGOTTEN_BEAST_844:GIZZARD -%CREATURE:FORGOTTEN_BEAST_844:PANCREAS -#CREATURE:FORGOTTEN_BEAST_844:SPLEEN -#CREATURE:FORGOTTEN_BEAST_844:KIDNEY -#CREATURE:FORGOTTEN_BEAST_845:MUSCLE - CREATURE:FORGOTTEN_BEAST_845:EYE -"CREATURE:FORGOTTEN_BEAST_845:BRAIN -!CREATURE:FORGOTTEN_BEAST_845:LUNG -"CREATURE:FORGOTTEN_BEAST_845:HEART -"CREATURE:FORGOTTEN_BEAST_845:LIVER - CREATURE:FORGOTTEN_BEAST_845:GUT -$CREATURE:FORGOTTEN_BEAST_845:STOMACH -$CREATURE:FORGOTTEN_BEAST_845:GIZZARD -%CREATURE:FORGOTTEN_BEAST_845:PANCREAS -#CREATURE:FORGOTTEN_BEAST_845:SPLEEN -#CREATURE:FORGOTTEN_BEAST_845:KIDNEY -#CREATURE:FORGOTTEN_BEAST_846:MUSCLE - CREATURE:FORGOTTEN_BEAST_846:EYE -"CREATURE:FORGOTTEN_BEAST_846:BRAIN -!CREATURE:FORGOTTEN_BEAST_846:LUNG -"CREATURE:FORGOTTEN_BEAST_846:HEART -"CREATURE:FORGOTTEN_BEAST_846:LIVER - CREATURE:FORGOTTEN_BEAST_846:GUT -$CREATURE:FORGOTTEN_BEAST_846:STOMACH -$CREATURE:FORGOTTEN_BEAST_846:GIZZARD -%CREATURE:FORGOTTEN_BEAST_846:PANCREAS -#CREATURE:FORGOTTEN_BEAST_846:SPLEEN -#CREATURE:FORGOTTEN_BEAST_846:KIDNEY -#CREATURE:FORGOTTEN_BEAST_847:MUSCLE - CREATURE:FORGOTTEN_BEAST_847:EYE -"CREATURE:FORGOTTEN_BEAST_847:BRAIN -!CREATURE:FORGOTTEN_BEAST_847:LUNG -"CREATURE:FORGOTTEN_BEAST_847:HEART -"CREATURE:FORGOTTEN_BEAST_847:LIVER - CREATURE:FORGOTTEN_BEAST_847:GUT -$CREATURE:FORGOTTEN_BEAST_847:STOMACH -$CREATURE:FORGOTTEN_BEAST_847:GIZZARD -%CREATURE:FORGOTTEN_BEAST_847:PANCREAS -#CREATURE:FORGOTTEN_BEAST_847:SPLEEN -#CREATURE:FORGOTTEN_BEAST_847:KIDNEY -#CREATURE:FORGOTTEN_BEAST_848:MUSCLE - CREATURE:FORGOTTEN_BEAST_848:EYE -"CREATURE:FORGOTTEN_BEAST_848:BRAIN -!CREATURE:FORGOTTEN_BEAST_848:LUNG -"CREATURE:FORGOTTEN_BEAST_848:HEART -"CREATURE:FORGOTTEN_BEAST_848:LIVER - CREATURE:FORGOTTEN_BEAST_848:GUT -$CREATURE:FORGOTTEN_BEAST_848:STOMACH -$CREATURE:FORGOTTEN_BEAST_848:GIZZARD -%CREATURE:FORGOTTEN_BEAST_848:PANCREAS -#CREATURE:FORGOTTEN_BEAST_848:SPLEEN -#CREATURE:FORGOTTEN_BEAST_848:KIDNEY -#CREATURE:FORGOTTEN_BEAST_849:MUSCLE - CREATURE:FORGOTTEN_BEAST_849:EYE -"CREATURE:FORGOTTEN_BEAST_849:BRAIN -!CREATURE:FORGOTTEN_BEAST_849:LUNG -"CREATURE:FORGOTTEN_BEAST_849:HEART -"CREATURE:FORGOTTEN_BEAST_849:LIVER - CREATURE:FORGOTTEN_BEAST_849:GUT -$CREATURE:FORGOTTEN_BEAST_849:STOMACH -$CREATURE:FORGOTTEN_BEAST_849:GIZZARD -%CREATURE:FORGOTTEN_BEAST_849:PANCREAS -#CREATURE:FORGOTTEN_BEAST_849:SPLEEN -#CREATURE:FORGOTTEN_BEAST_849:KIDNEY -#CREATURE:FORGOTTEN_BEAST_851:MUSCLE - CREATURE:FORGOTTEN_BEAST_851:EYE -"CREATURE:FORGOTTEN_BEAST_851:BRAIN -!CREATURE:FORGOTTEN_BEAST_851:LUNG -"CREATURE:FORGOTTEN_BEAST_851:HEART -"CREATURE:FORGOTTEN_BEAST_851:LIVER - CREATURE:FORGOTTEN_BEAST_851:GUT -$CREATURE:FORGOTTEN_BEAST_851:STOMACH -$CREATURE:FORGOTTEN_BEAST_851:GIZZARD -%CREATURE:FORGOTTEN_BEAST_851:PANCREAS -#CREATURE:FORGOTTEN_BEAST_851:SPLEEN -#CREATURE:FORGOTTEN_BEAST_851:KIDNEY -#CREATURE:FORGOTTEN_BEAST_853:MUSCLE - CREATURE:FORGOTTEN_BEAST_853:EYE -"CREATURE:FORGOTTEN_BEAST_853:BRAIN -!CREATURE:FORGOTTEN_BEAST_853:LUNG -"CREATURE:FORGOTTEN_BEAST_853:HEART -"CREATURE:FORGOTTEN_BEAST_853:LIVER - CREATURE:FORGOTTEN_BEAST_853:GUT -$CREATURE:FORGOTTEN_BEAST_853:STOMACH -$CREATURE:FORGOTTEN_BEAST_853:GIZZARD -%CREATURE:FORGOTTEN_BEAST_853:PANCREAS -#CREATURE:FORGOTTEN_BEAST_853:SPLEEN -#CREATURE:FORGOTTEN_BEAST_853:KIDNEY -#CREATURE:FORGOTTEN_BEAST_854:MUSCLE - CREATURE:FORGOTTEN_BEAST_854:EYE -"CREATURE:FORGOTTEN_BEAST_854:BRAIN -!CREATURE:FORGOTTEN_BEAST_854:LUNG -"CREATURE:FORGOTTEN_BEAST_854:HEART -"CREATURE:FORGOTTEN_BEAST_854:LIVER - CREATURE:FORGOTTEN_BEAST_854:GUT -$CREATURE:FORGOTTEN_BEAST_854:STOMACH -$CREATURE:FORGOTTEN_BEAST_854:GIZZARD -%CREATURE:FORGOTTEN_BEAST_854:PANCREAS -#CREATURE:FORGOTTEN_BEAST_854:SPLEEN -#CREATURE:FORGOTTEN_BEAST_854:KIDNEY -#CREATURE:FORGOTTEN_BEAST_855:MUSCLE - CREATURE:FORGOTTEN_BEAST_855:EYE -"CREATURE:FORGOTTEN_BEAST_855:BRAIN -!CREATURE:FORGOTTEN_BEAST_855:LUNG -"CREATURE:FORGOTTEN_BEAST_855:HEART -"CREATURE:FORGOTTEN_BEAST_855:LIVER - CREATURE:FORGOTTEN_BEAST_855:GUT -$CREATURE:FORGOTTEN_BEAST_855:STOMACH -$CREATURE:FORGOTTEN_BEAST_855:GIZZARD -%CREATURE:FORGOTTEN_BEAST_855:PANCREAS -#CREATURE:FORGOTTEN_BEAST_855:SPLEEN -#CREATURE:FORGOTTEN_BEAST_855:KIDNEY -#CREATURE:FORGOTTEN_BEAST_857:MUSCLE - CREATURE:FORGOTTEN_BEAST_857:EYE -"CREATURE:FORGOTTEN_BEAST_857:BRAIN -!CREATURE:FORGOTTEN_BEAST_857:LUNG -"CREATURE:FORGOTTEN_BEAST_857:HEART -"CREATURE:FORGOTTEN_BEAST_857:LIVER - CREATURE:FORGOTTEN_BEAST_857:GUT -$CREATURE:FORGOTTEN_BEAST_857:STOMACH -$CREATURE:FORGOTTEN_BEAST_857:GIZZARD -%CREATURE:FORGOTTEN_BEAST_857:PANCREAS -#CREATURE:FORGOTTEN_BEAST_857:SPLEEN -#CREATURE:FORGOTTEN_BEAST_857:KIDNEY -#CREATURE:FORGOTTEN_BEAST_858:MUSCLE - CREATURE:FORGOTTEN_BEAST_858:EYE -"CREATURE:FORGOTTEN_BEAST_858:BRAIN -!CREATURE:FORGOTTEN_BEAST_858:LUNG -"CREATURE:FORGOTTEN_BEAST_858:HEART -"CREATURE:FORGOTTEN_BEAST_858:LIVER - CREATURE:FORGOTTEN_BEAST_858:GUT -$CREATURE:FORGOTTEN_BEAST_858:STOMACH -$CREATURE:FORGOTTEN_BEAST_858:GIZZARD -%CREATURE:FORGOTTEN_BEAST_858:PANCREAS -#CREATURE:FORGOTTEN_BEAST_858:SPLEEN -#CREATURE:FORGOTTEN_BEAST_858:KIDNEY -#CREATURE:FORGOTTEN_BEAST_859:MUSCLE - CREATURE:FORGOTTEN_BEAST_859:EYE -"CREATURE:FORGOTTEN_BEAST_859:BRAIN -!CREATURE:FORGOTTEN_BEAST_859:LUNG -"CREATURE:FORGOTTEN_BEAST_859:HEART -"CREATURE:FORGOTTEN_BEAST_859:LIVER - CREATURE:FORGOTTEN_BEAST_859:GUT -$CREATURE:FORGOTTEN_BEAST_859:STOMACH -$CREATURE:FORGOTTEN_BEAST_859:GIZZARD -%CREATURE:FORGOTTEN_BEAST_859:PANCREAS -#CREATURE:FORGOTTEN_BEAST_859:SPLEEN -#CREATURE:FORGOTTEN_BEAST_859:KIDNEY -#CREATURE:FORGOTTEN_BEAST_860:MUSCLE - CREATURE:FORGOTTEN_BEAST_860:EYE -"CREATURE:FORGOTTEN_BEAST_860:BRAIN -!CREATURE:FORGOTTEN_BEAST_860:LUNG -"CREATURE:FORGOTTEN_BEAST_860:HEART -"CREATURE:FORGOTTEN_BEAST_860:LIVER - CREATURE:FORGOTTEN_BEAST_860:GUT -$CREATURE:FORGOTTEN_BEAST_860:STOMACH -$CREATURE:FORGOTTEN_BEAST_860:GIZZARD -%CREATURE:FORGOTTEN_BEAST_860:PANCREAS -#CREATURE:FORGOTTEN_BEAST_860:SPLEEN -#CREATURE:FORGOTTEN_BEAST_860:KIDNEY -#CREATURE:FORGOTTEN_BEAST_861:MUSCLE - CREATURE:FORGOTTEN_BEAST_861:EYE -"CREATURE:FORGOTTEN_BEAST_861:BRAIN -!CREATURE:FORGOTTEN_BEAST_861:LUNG -"CREATURE:FORGOTTEN_BEAST_861:HEART -"CREATURE:FORGOTTEN_BEAST_861:LIVER - CREATURE:FORGOTTEN_BEAST_861:GUT -$CREATURE:FORGOTTEN_BEAST_861:STOMACH -$CREATURE:FORGOTTEN_BEAST_861:GIZZARD -%CREATURE:FORGOTTEN_BEAST_861:PANCREAS -#CREATURE:FORGOTTEN_BEAST_861:SPLEEN -#CREATURE:FORGOTTEN_BEAST_861:KIDNEY -#CREATURE:FORGOTTEN_BEAST_862:MUSCLE - CREATURE:FORGOTTEN_BEAST_862:EYE -"CREATURE:FORGOTTEN_BEAST_862:BRAIN -!CREATURE:FORGOTTEN_BEAST_862:LUNG -"CREATURE:FORGOTTEN_BEAST_862:HEART -"CREATURE:FORGOTTEN_BEAST_862:LIVER - CREATURE:FORGOTTEN_BEAST_862:GUT -$CREATURE:FORGOTTEN_BEAST_862:STOMACH -$CREATURE:FORGOTTEN_BEAST_862:GIZZARD -%CREATURE:FORGOTTEN_BEAST_862:PANCREAS -#CREATURE:FORGOTTEN_BEAST_862:SPLEEN -#CREATURE:FORGOTTEN_BEAST_862:KIDNEY -#CREATURE:FORGOTTEN_BEAST_865:MUSCLE - CREATURE:FORGOTTEN_BEAST_865:EYE -"CREATURE:FORGOTTEN_BEAST_865:BRAIN -!CREATURE:FORGOTTEN_BEAST_865:LUNG -"CREATURE:FORGOTTEN_BEAST_865:HEART -"CREATURE:FORGOTTEN_BEAST_865:LIVER - CREATURE:FORGOTTEN_BEAST_865:GUT -$CREATURE:FORGOTTEN_BEAST_865:STOMACH -$CREATURE:FORGOTTEN_BEAST_865:GIZZARD -%CREATURE:FORGOTTEN_BEAST_865:PANCREAS -#CREATURE:FORGOTTEN_BEAST_865:SPLEEN -#CREATURE:FORGOTTEN_BEAST_865:KIDNEY -#CREATURE:FORGOTTEN_BEAST_866:MUSCLE - CREATURE:FORGOTTEN_BEAST_866:EYE -"CREATURE:FORGOTTEN_BEAST_866:BRAIN -!CREATURE:FORGOTTEN_BEAST_866:LUNG -"CREATURE:FORGOTTEN_BEAST_866:HEART -"CREATURE:FORGOTTEN_BEAST_866:LIVER - CREATURE:FORGOTTEN_BEAST_866:GUT -$CREATURE:FORGOTTEN_BEAST_866:STOMACH -$CREATURE:FORGOTTEN_BEAST_866:GIZZARD -%CREATURE:FORGOTTEN_BEAST_866:PANCREAS -#CREATURE:FORGOTTEN_BEAST_866:SPLEEN -#CREATURE:FORGOTTEN_BEAST_866:KIDNEY -#CREATURE:FORGOTTEN_BEAST_867:MUSCLE - CREATURE:FORGOTTEN_BEAST_867:EYE -"CREATURE:FORGOTTEN_BEAST_867:BRAIN -!CREATURE:FORGOTTEN_BEAST_867:LUNG -"CREATURE:FORGOTTEN_BEAST_867:HEART -"CREATURE:FORGOTTEN_BEAST_867:LIVER - CREATURE:FORGOTTEN_BEAST_867:GUT -$CREATURE:FORGOTTEN_BEAST_867:STOMACH -$CREATURE:FORGOTTEN_BEAST_867:GIZZARD -%CREATURE:FORGOTTEN_BEAST_867:PANCREAS -#CREATURE:FORGOTTEN_BEAST_867:SPLEEN -#CREATURE:FORGOTTEN_BEAST_867:KIDNEY -CREATURE:TITAN_1:MUSCLE -CREATURE:TITAN_1:EYE -CREATURE:TITAN_1:BRAIN -CREATURE:TITAN_1:LUNG -CREATURE:TITAN_1:HEART -CREATURE:TITAN_1:LIVER -CREATURE:TITAN_1:GUT -CREATURE:TITAN_1:STOMACH -CREATURE:TITAN_1:GIZZARD -CREATURE:TITAN_1:PANCREAS -CREATURE:TITAN_1:SPLEEN -CREATURE:TITAN_1:KIDNEY -CREATURE:TITAN_3:MUSCLE -CREATURE:TITAN_3:EYE -CREATURE:TITAN_3:BRAIN -CREATURE:TITAN_3:LUNG -CREATURE:TITAN_3:HEART -CREATURE:TITAN_3:LIVER -CREATURE:TITAN_3:GUT -CREATURE:TITAN_3:STOMACH -CREATURE:TITAN_3:GIZZARD -CREATURE:TITAN_3:PANCREAS -CREATURE:TITAN_3:SPLEEN -CREATURE:TITAN_3:KIDNEY -CREATURE:TITAN_4:MUSCLE -CREATURE:TITAN_4:EYE -CREATURE:TITAN_4:BRAIN -CREATURE:TITAN_4:LUNG -CREATURE:TITAN_4:HEART -CREATURE:TITAN_4:LIVER -CREATURE:TITAN_4:GUT -CREATURE:TITAN_4:STOMACH -CREATURE:TITAN_4:GIZZARD -CREATURE:TITAN_4:PANCREAS -CREATURE:TITAN_4:SPLEEN -CREATURE:TITAN_4:KIDNEY -CREATURE:TITAN_6:MUSCLE -CREATURE:TITAN_6:EYE -CREATURE:TITAN_6:BRAIN -CREATURE:TITAN_6:LUNG -CREATURE:TITAN_6:HEART -CREATURE:TITAN_6:LIVER -CREATURE:TITAN_6:GUT -CREATURE:TITAN_6:STOMACH -CREATURE:TITAN_6:GIZZARD -CREATURE:TITAN_6:PANCREAS -CREATURE:TITAN_6:SPLEEN -CREATURE:TITAN_6:KIDNEY -CREATURE:TITAN_7:MUSCLE -CREATURE:TITAN_7:EYE -CREATURE:TITAN_7:BRAIN -CREATURE:TITAN_7:LUNG -CREATURE:TITAN_7:HEART -CREATURE:TITAN_7:LIVER -CREATURE:TITAN_7:GUT -CREATURE:TITAN_7:STOMACH -CREATURE:TITAN_7:GIZZARD -CREATURE:TITAN_7:PANCREAS -CREATURE:TITAN_7:SPLEEN -CREATURE:TITAN_7:KIDNEY -CREATURE:TITAN_8:MUSCLE -CREATURE:TITAN_8:EYE -CREATURE:TITAN_8:BRAIN -CREATURE:TITAN_8:LUNG -CREATURE:TITAN_8:HEART -CREATURE:TITAN_8:LIVER -CREATURE:TITAN_8:GUT -CREATURE:TITAN_8:STOMACH -CREATURE:TITAN_8:GIZZARD -CREATURE:TITAN_8:PANCREAS -CREATURE:TITAN_8:SPLEEN -CREATURE:TITAN_8:KIDNEY -CREATURE:TITAN_9:MUSCLE -CREATURE:TITAN_9:EYE -CREATURE:TITAN_9:BRAIN -CREATURE:TITAN_9:LUNG -CREATURE:TITAN_9:HEART -CREATURE:TITAN_9:LIVER -CREATURE:TITAN_9:GUT -CREATURE:TITAN_9:STOMACH -CREATURE:TITAN_9:GIZZARD -CREATURE:TITAN_9:PANCREAS -CREATURE:TITAN_9:SPLEEN -CREATURE:TITAN_9:KIDNEY -CREATURE:TITAN_10:MUSCLE -CREATURE:TITAN_10:EYE -CREATURE:TITAN_10:BRAIN -CREATURE:TITAN_10:LUNG -CREATURE:TITAN_10:HEART -CREATURE:TITAN_10:LIVER -CREATURE:TITAN_10:GUT -CREATURE:TITAN_10:STOMACH -CREATURE:TITAN_10:GIZZARD -CREATURE:TITAN_10:PANCREAS -CREATURE:TITAN_10:SPLEEN -CREATURE:TITAN_10:KIDNEY -CREATURE:TITAN_11:MUSCLE -CREATURE:TITAN_11:EYE -CREATURE:TITAN_11:BRAIN -CREATURE:TITAN_11:LUNG -CREATURE:TITAN_11:HEART -CREATURE:TITAN_11:LIVER -CREATURE:TITAN_11:GUT -CREATURE:TITAN_11:STOMACH -CREATURE:TITAN_11:GIZZARD -CREATURE:TITAN_11:PANCREAS -CREATURE:TITAN_11:SPLEEN -CREATURE:TITAN_11:KIDNEY -CREATURE:TITAN_12:MUSCLE -CREATURE:TITAN_12:EYE -CREATURE:TITAN_12:BRAIN -CREATURE:TITAN_12:LUNG -CREATURE:TITAN_12:HEART -CREATURE:TITAN_12:LIVER -CREATURE:TITAN_12:GUT -CREATURE:TITAN_12:STOMACH -CREATURE:TITAN_12:GIZZARD -CREATURE:TITAN_12:PANCREAS -CREATURE:TITAN_12:SPLEEN -CREATURE:TITAN_12:KIDNEY -CREATURE:TITAN_13:MUSCLE -CREATURE:TITAN_13:EYE -CREATURE:TITAN_13:BRAIN -CREATURE:TITAN_13:LUNG -CREATURE:TITAN_13:HEART -CREATURE:TITAN_13:LIVER -CREATURE:TITAN_13:GUT -CREATURE:TITAN_13:STOMACH -CREATURE:TITAN_13:GIZZARD -CREATURE:TITAN_13:PANCREAS -CREATURE:TITAN_13:SPLEEN -CREATURE:TITAN_13:KIDNEY -CREATURE:TITAN_14:MUSCLE -CREATURE:TITAN_14:EYE -CREATURE:TITAN_14:BRAIN -CREATURE:TITAN_14:LUNG -CREATURE:TITAN_14:HEART -CREATURE:TITAN_14:LIVER -CREATURE:TITAN_14:GUT -CREATURE:TITAN_14:STOMACH -CREATURE:TITAN_14:GIZZARD -CREATURE:TITAN_14:PANCREAS -CREATURE:TITAN_14:SPLEEN -CREATURE:TITAN_14:KIDNEY -CREATURE:TITAN_15:MUSCLE -CREATURE:TITAN_15:EYE -CREATURE:TITAN_15:BRAIN -CREATURE:TITAN_15:LUNG -CREATURE:TITAN_15:HEART -CREATURE:TITAN_15:LIVER -CREATURE:TITAN_15:GUT -CREATURE:TITAN_15:STOMACH -CREATURE:TITAN_15:GIZZARD -CREATURE:TITAN_15:PANCREAS -CREATURE:TITAN_15:SPLEEN -CREATURE:TITAN_15:KIDNEY -CREATURE:TITAN_18:MUSCLE -CREATURE:TITAN_18:EYE -CREATURE:TITAN_18:BRAIN -CREATURE:TITAN_18:LUNG -CREATURE:TITAN_18:HEART -CREATURE:TITAN_18:LIVER -CREATURE:TITAN_18:GUT -CREATURE:TITAN_18:STOMACH -CREATURE:TITAN_18:GIZZARD -CREATURE:TITAN_18:PANCREAS -CREATURE:TITAN_18:SPLEEN -CREATURE:TITAN_18:KIDNEY -CREATURE:TITAN_19:MUSCLE -CREATURE:TITAN_19:EYE -CREATURE:TITAN_19:BRAIN -CREATURE:TITAN_19:LUNG -CREATURE:TITAN_19:HEART -CREATURE:TITAN_19:LIVER -CREATURE:TITAN_19:GUT -CREATURE:TITAN_19:STOMACH -CREATURE:TITAN_19:GIZZARD -CREATURE:TITAN_19:PANCREAS -CREATURE:TITAN_19:SPLEEN -CREATURE:TITAN_19:KIDNEY -CREATURE:TITAN_21:MUSCLE -CREATURE:TITAN_21:EYE -CREATURE:TITAN_21:BRAIN -CREATURE:TITAN_21:LUNG -CREATURE:TITAN_21:HEART -CREATURE:TITAN_21:LIVER -CREATURE:TITAN_21:GUT -CREATURE:TITAN_21:STOMACH -CREATURE:TITAN_21:GIZZARD -CREATURE:TITAN_21:PANCREAS -CREATURE:TITAN_21:SPLEEN -CREATURE:TITAN_21:KIDNEY -CREATURE:TITAN_23:MUSCLE -CREATURE:TITAN_23:EYE -CREATURE:TITAN_23:BRAIN -CREATURE:TITAN_23:LUNG -CREATURE:TITAN_23:HEART -CREATURE:TITAN_23:LIVER -CREATURE:TITAN_23:GUT -CREATURE:TITAN_23:STOMACH -CREATURE:TITAN_23:GIZZARD -CREATURE:TITAN_23:PANCREAS -CREATURE:TITAN_23:SPLEEN -CREATURE:TITAN_23:KIDNEY -CREATURE:TITAN_24:MUSCLE -CREATURE:TITAN_24:EYE -CREATURE:TITAN_24:BRAIN -CREATURE:TITAN_24:LUNG -CREATURE:TITAN_24:HEART -CREATURE:TITAN_24:LIVER -CREATURE:TITAN_24:GUT -CREATURE:TITAN_24:STOMACH -CREATURE:TITAN_24:GIZZARD -CREATURE:TITAN_24:PANCREAS -CREATURE:TITAN_24:SPLEEN -CREATURE:TITAN_24:KIDNEY -CREATURE:TITAN_25:MUSCLE -CREATURE:TITAN_25:EYE -CREATURE:TITAN_25:BRAIN -CREATURE:TITAN_25:LUNG -CREATURE:TITAN_25:HEART -CREATURE:TITAN_25:LIVER -CREATURE:TITAN_25:GUT -CREATURE:TITAN_25:STOMACH -CREATURE:TITAN_25:GIZZARD -CREATURE:TITAN_25:PANCREAS -CREATURE:TITAN_25:SPLEEN -CREATURE:TITAN_25:KIDNEY -CREATURE:TITAN_26:MUSCLE -CREATURE:TITAN_26:EYE -CREATURE:TITAN_26:BRAIN -CREATURE:TITAN_26:LUNG -CREATURE:TITAN_26:HEART -CREATURE:TITAN_26:LIVER -CREATURE:TITAN_26:GUT -CREATURE:TITAN_26:STOMACH -CREATURE:TITAN_26:GIZZARD -CREATURE:TITAN_26:PANCREAS -CREATURE:TITAN_26:SPLEEN -CREATURE:TITAN_26:KIDNEY -CREATURE:TITAN_27:MUSCLE -CREATURE:TITAN_27:EYE -CREATURE:TITAN_27:BRAIN -CREATURE:TITAN_27:LUNG -CREATURE:TITAN_27:HEART -CREATURE:TITAN_27:LIVER -CREATURE:TITAN_27:GUT -CREATURE:TITAN_27:STOMACH -CREATURE:TITAN_27:GIZZARD -CREATURE:TITAN_27:PANCREAS -CREATURE:TITAN_27:SPLEEN -CREATURE:TITAN_27:KIDNEY -CREATURE:TITAN_28:MUSCLE -CREATURE:TITAN_28:EYE -CREATURE:TITAN_28:BRAIN -CREATURE:TITAN_28:LUNG -CREATURE:TITAN_28:HEART -CREATURE:TITAN_28:LIVER -CREATURE:TITAN_28:GUT -CREATURE:TITAN_28:STOMACH -CREATURE:TITAN_28:GIZZARD -CREATURE:TITAN_28:PANCREAS -CREATURE:TITAN_28:SPLEEN -CREATURE:TITAN_28:KIDNEY -CREATURE:TITAN_29:MUSCLE -CREATURE:TITAN_29:EYE -CREATURE:TITAN_29:BRAIN -CREATURE:TITAN_29:LUNG -CREATURE:TITAN_29:HEART -CREATURE:TITAN_29:LIVER -CREATURE:TITAN_29:GUT -CREATURE:TITAN_29:STOMACH -CREATURE:TITAN_29:GIZZARD -CREATURE:TITAN_29:PANCREAS -CREATURE:TITAN_29:SPLEEN -CREATURE:TITAN_29:KIDNEY -CREATURE:TITAN_30:MUSCLE -CREATURE:TITAN_30:EYE -CREATURE:TITAN_30:BRAIN -CREATURE:TITAN_30:LUNG -CREATURE:TITAN_30:HEART -CREATURE:TITAN_30:LIVER -CREATURE:TITAN_30:GUT -CREATURE:TITAN_30:STOMACH -CREATURE:TITAN_30:GIZZARD -CREATURE:TITAN_30:PANCREAS -CREATURE:TITAN_30:SPLEEN -CREATURE:TITAN_30:KIDNEY -CREATURE:TITAN_32:MUSCLE -CREATURE:TITAN_32:EYE -CREATURE:TITAN_32:BRAIN -CREATURE:TITAN_32:LUNG -CREATURE:TITAN_32:HEART -CREATURE:TITAN_32:LIVER -CREATURE:TITAN_32:GUT -CREATURE:TITAN_32:STOMACH -CREATURE:TITAN_32:GIZZARD -CREATURE:TITAN_32:PANCREAS -CREATURE:TITAN_32:SPLEEN -CREATURE:TITAN_32:KIDNEY -CREATURE:TITAN_33:MUSCLE -CREATURE:TITAN_33:EYE -CREATURE:TITAN_33:BRAIN -CREATURE:TITAN_33:LUNG -CREATURE:TITAN_33:HEART -CREATURE:TITAN_33:LIVER -CREATURE:TITAN_33:GUT -CREATURE:TITAN_33:STOMACH -CREATURE:TITAN_33:GIZZARD -CREATURE:TITAN_33:PANCREAS -CREATURE:TITAN_33:SPLEEN -CREATURE:TITAN_33:KIDNEY -CREATURE:DEMON_7:MUSCLE -CREATURE:DEMON_7:EYE -CREATURE:DEMON_7:BRAIN -CREATURE:DEMON_7:LUNG -CREATURE:DEMON_7:HEART -CREATURE:DEMON_7:LIVER -CREATURE:DEMON_7:GUT -CREATURE:DEMON_7:STOMACH -CREATURE:DEMON_7:GIZZARD -CREATURE:DEMON_7:PANCREAS -CREATURE:DEMON_7:SPLEEN -CREATURE:DEMON_7:KIDNEY -CREATURE:DEMON_8:MUSCLE -CREATURE:DEMON_8:EYE -CREATURE:DEMON_8:BRAIN -CREATURE:DEMON_8:LUNG -CREATURE:DEMON_8:HEART -CREATURE:DEMON_8:LIVER -CREATURE:DEMON_8:GUT -CREATURE:DEMON_8:STOMACH -CREATURE:DEMON_8:GIZZARD -CREATURE:DEMON_8:PANCREAS -CREATURE:DEMON_8:SPLEEN -CREATURE:DEMON_8:KIDNEY -CREATURE:DEMON_9:MUSCLE -CREATURE:DEMON_9:EYE -CREATURE:DEMON_9:BRAIN -CREATURE:DEMON_9:LUNG -CREATURE:DEMON_9:HEART -CREATURE:DEMON_9:LIVER -CREATURE:DEMON_9:GUT -CREATURE:DEMON_9:STOMACH -CREATURE:DEMON_9:GIZZARD -CREATURE:DEMON_9:PANCREAS -CREATURE:DEMON_9:SPLEEN -CREATURE:DEMON_9:KIDNEY -CREATURE:DEMON_10:MUSCLE -CREATURE:DEMON_10:EYE -CREATURE:DEMON_10:BRAIN -CREATURE:DEMON_10:LUNG -CREATURE:DEMON_10:HEART -CREATURE:DEMON_10:LIVER -CREATURE:DEMON_10:GUT -CREATURE:DEMON_10:STOMACH -CREATURE:DEMON_10:GIZZARD -CREATURE:DEMON_10:PANCREAS -CREATURE:DEMON_10:SPLEEN -CREATURE:DEMON_10:KIDNEY -CREATURE:DEMON_11:MUSCLE -CREATURE:DEMON_11:EYE -CREATURE:DEMON_11:BRAIN -CREATURE:DEMON_11:LUNG -CREATURE:DEMON_11:HEART -CREATURE:DEMON_11:LIVER -CREATURE:DEMON_11:GUT -CREATURE:DEMON_11:STOMACH -CREATURE:DEMON_11:GIZZARD -CREATURE:DEMON_11:PANCREAS -CREATURE:DEMON_11:SPLEEN -CREATURE:DEMON_11:KIDNEY -CREATURE:DEMON_12:MUSCLE -CREATURE:DEMON_12:EYE -CREATURE:DEMON_12:BRAIN -CREATURE:DEMON_12:LUNG -CREATURE:DEMON_12:HEART -CREATURE:DEMON_12:LIVER -CREATURE:DEMON_12:GUT -CREATURE:DEMON_12:STOMACH -CREATURE:DEMON_12:GIZZARD -CREATURE:DEMON_12:PANCREAS -CREATURE:DEMON_12:SPLEEN -CREATURE:DEMON_12:KIDNEY -CREATURE:DEMON_13:MUSCLE -CREATURE:DEMON_13:EYE -CREATURE:DEMON_13:BRAIN -CREATURE:DEMON_13:LUNG -CREATURE:DEMON_13:HEART -CREATURE:DEMON_13:LIVER -CREATURE:DEMON_13:GUT -CREATURE:DEMON_13:STOMACH -CREATURE:DEMON_13:GIZZARD -CREATURE:DEMON_13:PANCREAS -CREATURE:DEMON_13:SPLEEN -CREATURE:DEMON_13:KIDNEY -CREATURE:DEMON_14:MUSCLE -CREATURE:DEMON_14:EYE -CREATURE:DEMON_14:BRAIN -CREATURE:DEMON_14:LUNG -CREATURE:DEMON_14:HEART -CREATURE:DEMON_14:LIVER -CREATURE:DEMON_14:GUT -CREATURE:DEMON_14:STOMACH -CREATURE:DEMON_14:GIZZARD -CREATURE:DEMON_14:PANCREAS -CREATURE:DEMON_14:SPLEEN -CREATURE:DEMON_14:KIDNEY -CREATURE:DEMON_15:MUSCLE -CREATURE:DEMON_15:EYE -CREATURE:DEMON_15:BRAIN -CREATURE:DEMON_15:LUNG -CREATURE:DEMON_15:HEART -CREATURE:DEMON_15:LIVER -CREATURE:DEMON_15:GUT -CREATURE:DEMON_15:STOMACH -CREATURE:DEMON_15:GIZZARD -CREATURE:DEMON_15:PANCREAS -CREATURE:DEMON_15:SPLEEN -CREATURE:DEMON_15:KIDNEY -CREATURE:DEMON_16:MUSCLE -CREATURE:DEMON_16:EYE -CREATURE:DEMON_16:BRAIN -CREATURE:DEMON_16:LUNG -CREATURE:DEMON_16:HEART -CREATURE:DEMON_16:LIVER -CREATURE:DEMON_16:GUT -CREATURE:DEMON_16:STOMACH -CREATURE:DEMON_16:GIZZARD -CREATURE:DEMON_16:PANCREAS -CREATURE:DEMON_16:SPLEEN -CREATURE:DEMON_16:KIDNEY -CREATURE:DEMON_17:MUSCLE -CREATURE:DEMON_17:EYE -CREATURE:DEMON_17:BRAIN -CREATURE:DEMON_17:LUNG -CREATURE:DEMON_17:HEART -CREATURE:DEMON_17:LIVER -CREATURE:DEMON_17:GUT -CREATURE:DEMON_17:STOMACH -CREATURE:DEMON_17:GIZZARD -CREATURE:DEMON_17:PANCREAS -CREATURE:DEMON_17:SPLEEN -CREATURE:DEMON_17:KIDNEY -CREATURE:DEMON_18:MUSCLE -CREATURE:DEMON_18:EYE -CREATURE:DEMON_18:BRAIN -CREATURE:DEMON_18:LUNG -CREATURE:DEMON_18:HEART -CREATURE:DEMON_18:LIVER -CREATURE:DEMON_18:GUT -CREATURE:DEMON_18:STOMACH -CREATURE:DEMON_18:GIZZARD -CREATURE:DEMON_18:PANCREAS -CREATURE:DEMON_18:SPLEEN -CREATURE:DEMON_18:KIDNEY -CREATURE:DEMON_19:MUSCLE -CREATURE:DEMON_19:EYE -CREATURE:DEMON_19:BRAIN -CREATURE:DEMON_19:LUNG -CREATURE:DEMON_19:HEART -CREATURE:DEMON_19:LIVER -CREATURE:DEMON_19:GUT -CREATURE:DEMON_19:STOMACH -CREATURE:DEMON_19:GIZZARD -CREATURE:DEMON_19:PANCREAS -CREATURE:DEMON_19:SPLEEN -CREATURE:DEMON_19:KIDNEY -CREATURE:DEMON_20:MUSCLE -CREATURE:DEMON_20:EYE -CREATURE:DEMON_20:BRAIN -CREATURE:DEMON_20:LUNG -CREATURE:DEMON_20:HEART -CREATURE:DEMON_20:LIVER -CREATURE:DEMON_20:GUT -CREATURE:DEMON_20:STOMACH -CREATURE:DEMON_20:GIZZARD -CREATURE:DEMON_20:PANCREAS -CREATURE:DEMON_20:SPLEEN -CREATURE:DEMON_20:KIDNEY -CREATURE:DEMON_21:MUSCLE -CREATURE:DEMON_21:EYE -CREATURE:DEMON_21:BRAIN -CREATURE:DEMON_21:LUNG -CREATURE:DEMON_21:HEART -CREATURE:DEMON_21:LIVER -CREATURE:DEMON_21:GUT -CREATURE:DEMON_21:STOMACH -CREATURE:DEMON_21:GIZZARD -CREATURE:DEMON_21:PANCREAS -CREATURE:DEMON_21:SPLEEN -CREATURE:DEMON_21:KIDNEY -CREATURE:DEMON_22:MUSCLE -CREATURE:DEMON_22:EYE -CREATURE:DEMON_22:BRAIN -CREATURE:DEMON_22:LUNG -CREATURE:DEMON_22:HEART -CREATURE:DEMON_22:LIVER -CREATURE:DEMON_22:GUT -CREATURE:DEMON_22:STOMACH -CREATURE:DEMON_22:GIZZARD -CREATURE:DEMON_22:PANCREAS -CREATURE:DEMON_22:SPLEEN -CREATURE:DEMON_22:KIDNEY -CREATURE:DEMON_23:MUSCLE -CREATURE:DEMON_23:EYE -CREATURE:DEMON_23:BRAIN -CREATURE:DEMON_23:LUNG -CREATURE:DEMON_23:HEART -CREATURE:DEMON_23:LIVER -CREATURE:DEMON_23:GUT -CREATURE:DEMON_23:STOMACH -CREATURE:DEMON_23:GIZZARD -CREATURE:DEMON_23:PANCREAS -CREATURE:DEMON_23:SPLEEN -CREATURE:DEMON_23:KIDNEY -CREATURE:DEMON_24:MUSCLE -CREATURE:DEMON_24:EYE -CREATURE:DEMON_24:BRAIN -CREATURE:DEMON_24:LUNG -CREATURE:DEMON_24:HEART -CREATURE:DEMON_24:LIVER -CREATURE:DEMON_24:GUT -CREATURE:DEMON_24:STOMACH -CREATURE:DEMON_24:GIZZARD -CREATURE:DEMON_24:PANCREAS -CREATURE:DEMON_24:SPLEEN -CREATURE:DEMON_24:KIDNEY -CREATURE:DEMON_25:MUSCLE -CREATURE:DEMON_25:EYE -CREATURE:DEMON_25:BRAIN -CREATURE:DEMON_25:LUNG -CREATURE:DEMON_25:HEART -CREATURE:DEMON_25:LIVER -CREATURE:DEMON_25:GUT -CREATURE:DEMON_25:STOMACH -CREATURE:DEMON_25:GIZZARD -CREATURE:DEMON_25:PANCREAS -CREATURE:DEMON_25:SPLEEN -CREATURE:DEMON_25:KIDNEY -CREATURE:DEMON_26:MUSCLE -CREATURE:DEMON_26:EYE -CREATURE:DEMON_26:BRAIN -CREATURE:DEMON_26:LUNG -CREATURE:DEMON_26:HEART -CREATURE:DEMON_26:LIVER -CREATURE:DEMON_26:GUT -CREATURE:DEMON_26:STOMACH -CREATURE:DEMON_26:GIZZARD -CREATURE:DEMON_26:PANCREAS -CREATURE:DEMON_26:SPLEEN -CREATURE:DEMON_26:KIDNEY -CREATURE:DEMON_27:MUSCLE -CREATURE:DEMON_27:EYE -CREATURE:DEMON_27:BRAIN -CREATURE:DEMON_27:LUNG -CREATURE:DEMON_27:HEART -CREATURE:DEMON_27:LIVER -CREATURE:DEMON_27:GUT -CREATURE:DEMON_27:STOMACH -CREATURE:DEMON_27:GIZZARD -CREATURE:DEMON_27:PANCREAS -CREATURE:DEMON_27:SPLEEN -CREATURE:DEMON_27:KIDNEY -CREATURE:DEMON_28:MUSCLE -CREATURE:DEMON_28:EYE -CREATURE:DEMON_28:BRAIN -CREATURE:DEMON_28:LUNG -CREATURE:DEMON_28:HEART -CREATURE:DEMON_28:LIVER -CREATURE:DEMON_28:GUT -CREATURE:DEMON_28:STOMACH -CREATURE:DEMON_28:GIZZARD -CREATURE:DEMON_28:PANCREAS -CREATURE:DEMON_28:SPLEEN -CREATURE:DEMON_28:KIDNEY -CREATURE:DEMON_29:MUSCLE -CREATURE:DEMON_29:EYE -CREATURE:DEMON_29:BRAIN -CREATURE:DEMON_29:LUNG -CREATURE:DEMON_29:HEART -CREATURE:DEMON_29:LIVER -CREATURE:DEMON_29:GUT -CREATURE:DEMON_29:STOMACH -CREATURE:DEMON_29:GIZZARD -CREATURE:DEMON_29:PANCREAS -CREATURE:DEMON_29:SPLEEN -CREATURE:DEMON_29:KIDNEY -CREATURE:DEMON_31:MUSCLE -CREATURE:DEMON_31:EYE -CREATURE:DEMON_31:BRAIN -CREATURE:DEMON_31:LUNG -CREATURE:DEMON_31:HEART -CREATURE:DEMON_31:LIVER -CREATURE:DEMON_31:GUT -CREATURE:DEMON_31:STOMACH -CREATURE:DEMON_31:GIZZARD -CREATURE:DEMON_31:PANCREAS -CREATURE:DEMON_31:SPLEEN -CREATURE:DEMON_31:KIDNEY -CREATURE:DEMON_32:MUSCLE -CREATURE:DEMON_32:EYE -CREATURE:DEMON_32:BRAIN -CREATURE:DEMON_32:LUNG -CREATURE:DEMON_32:HEART -CREATURE:DEMON_32:LIVER -CREATURE:DEMON_32:GUT -CREATURE:DEMON_32:STOMACH -CREATURE:DEMON_32:GIZZARD -CREATURE:DEMON_32:PANCREAS -CREATURE:DEMON_32:SPLEEN -CREATURE:DEMON_32:KIDNEY -CREATURE:DEMON_38:MUSCLE -CREATURE:DEMON_38:EYE -CREATURE:DEMON_38:BRAIN -CREATURE:DEMON_38:LUNG -CREATURE:DEMON_38:HEART -CREATURE:DEMON_38:LIVER -CREATURE:DEMON_38:GUT -CREATURE:DEMON_38:STOMACH -CREATURE:DEMON_38:GIZZARD -CREATURE:DEMON_38:PANCREAS -CREATURE:DEMON_38:SPLEEN -CREATURE:DEMON_38:KIDNEY -CREATURE:DEMON_39:MUSCLE -CREATURE:DEMON_39:EYE -CREATURE:DEMON_39:BRAIN -CREATURE:DEMON_39:LUNG -CREATURE:DEMON_39:HEART -CREATURE:DEMON_39:LIVER -CREATURE:DEMON_39:GUT -CREATURE:DEMON_39:STOMACH -CREATURE:DEMON_39:GIZZARD -CREATURE:DEMON_39:PANCREAS -CREATURE:DEMON_39:SPLEEN -CREATURE:DEMON_39:KIDNEY -CREATURE:DEMON_40:MUSCLE -CREATURE:DEMON_40:EYE -CREATURE:DEMON_40:BRAIN -CREATURE:DEMON_40:LUNG -CREATURE:DEMON_40:HEART -CREATURE:DEMON_40:LIVER -CREATURE:DEMON_40:GUT -CREATURE:DEMON_40:STOMACH -CREATURE:DEMON_40:GIZZARD -CREATURE:DEMON_40:PANCREAS -CREATURE:DEMON_40:SPLEEN -CREATURE:DEMON_40:KIDNEY -CREATURE:DEMON_41:MUSCLE -CREATURE:DEMON_41:EYE -CREATURE:DEMON_41:BRAIN -CREATURE:DEMON_41:LUNG -CREATURE:DEMON_41:HEART -CREATURE:DEMON_41:LIVER -CREATURE:DEMON_41:GUT -CREATURE:DEMON_41:STOMACH -CREATURE:DEMON_41:GIZZARD -CREATURE:DEMON_41:PANCREAS -CREATURE:DEMON_41:SPLEEN -CREATURE:DEMON_41:KIDNEY -CREATURE:DEMON_42:MUSCLE -CREATURE:DEMON_42:EYE -CREATURE:DEMON_42:BRAIN -CREATURE:DEMON_42:LUNG -CREATURE:DEMON_42:HEART -CREATURE:DEMON_42:LIVER -CREATURE:DEMON_42:GUT -CREATURE:DEMON_42:STOMACH -CREATURE:DEMON_42:GIZZARD -CREATURE:DEMON_42:PANCREAS -CREATURE:DEMON_42:SPLEEN -CREATURE:DEMON_42:KIDNEY -CREATURE:DEMON_43:MUSCLE -CREATURE:DEMON_43:EYE -CREATURE:DEMON_43:BRAIN -CREATURE:DEMON_43:LUNG -CREATURE:DEMON_43:HEART -CREATURE:DEMON_43:LIVER -CREATURE:DEMON_43:GUT -CREATURE:DEMON_43:STOMACH -CREATURE:DEMON_43:GIZZARD -CREATURE:DEMON_43:PANCREAS -CREATURE:DEMON_43:SPLEEN -CREATURE:DEMON_43:KIDNEY -CREATURE:DEMON_44:MUSCLE -CREATURE:DEMON_44:EYE -CREATURE:DEMON_44:BRAIN -CREATURE:DEMON_44:LUNG -CREATURE:DEMON_44:HEART -CREATURE:DEMON_44:LIVER -CREATURE:DEMON_44:GUT -CREATURE:DEMON_44:STOMACH -CREATURE:DEMON_44:GIZZARD -CREATURE:DEMON_44:PANCREAS -CREATURE:DEMON_44:SPLEEN -CREATURE:DEMON_44:KIDNEY -CREATURE:DEMON_45:MUSCLE -CREATURE:DEMON_45:EYE -CREATURE:DEMON_45:BRAIN -CREATURE:DEMON_45:LUNG -CREATURE:DEMON_45:HEART -CREATURE:DEMON_45:LIVER -CREATURE:DEMON_45:GUT -CREATURE:DEMON_45:STOMACH -CREATURE:DEMON_45:GIZZARD -CREATURE:DEMON_45:PANCREAS -CREATURE:DEMON_45:SPLEEN -CREATURE:DEMON_45:KIDNEY -CREATURE:DEMON_47:MUSCLE -CREATURE:DEMON_47:EYE -CREATURE:DEMON_47:BRAIN -CREATURE:DEMON_47:LUNG -CREATURE:DEMON_47:HEART -CREATURE:DEMON_47:LIVER -CREATURE:DEMON_47:GUT -CREATURE:DEMON_47:STOMACH -CREATURE:DEMON_47:GIZZARD -CREATURE:DEMON_47:PANCREAS -CREATURE:DEMON_47:SPLEEN -CREATURE:DEMON_47:KIDNEY -CREATURE:DEMON_48:MUSCLE -CREATURE:DEMON_48:EYE -CREATURE:DEMON_48:BRAIN -CREATURE:DEMON_48:LUNG -CREATURE:DEMON_48:HEART -CREATURE:DEMON_48:LIVER -CREATURE:DEMON_48:GUT -CREATURE:DEMON_48:STOMACH -CREATURE:DEMON_48:GIZZARD -CREATURE:DEMON_48:PANCREAS -CREATURE:DEMON_48:SPLEEN -CREATURE:DEMON_48:KIDNEY -CREATURE:DEMON_49:MUSCLE -CREATURE:DEMON_49:EYE -CREATURE:DEMON_49:BRAIN -CREATURE:DEMON_49:LUNG -CREATURE:DEMON_49:HEART -CREATURE:DEMON_49:LIVER -CREATURE:DEMON_49:GUT -CREATURE:DEMON_49:STOMACH -CREATURE:DEMON_49:GIZZARD -CREATURE:DEMON_49:PANCREAS -CREATURE:DEMON_49:SPLEEN -CREATURE:DEMON_49:KIDNEY -CREATURE:DEMON_52:MUSCLE -CREATURE:DEMON_52:EYE -CREATURE:DEMON_52:BRAIN -CREATURE:DEMON_52:LUNG -CREATURE:DEMON_52:HEART -CREATURE:DEMON_52:LIVER -CREATURE:DEMON_52:GUT -CREATURE:DEMON_52:STOMACH -CREATURE:DEMON_52:GIZZARD -CREATURE:DEMON_52:PANCREAS -CREATURE:DEMON_52:SPLEEN -CREATURE:DEMON_52:KIDNEY - CREATURE:NIGHT_CREATURE_1:MUSCLE -CREATURE:NIGHT_CREATURE_1:EYE -CREATURE:NIGHT_CREATURE_1:BRAIN -CREATURE:NIGHT_CREATURE_1:LUNG -CREATURE:NIGHT_CREATURE_1:HEART -CREATURE:NIGHT_CREATURE_1:LIVER -CREATURE:NIGHT_CREATURE_1:GUT -!CREATURE:NIGHT_CREATURE_1:STOMACH -!CREATURE:NIGHT_CREATURE_1:GIZZARD -"CREATURE:NIGHT_CREATURE_1:PANCREAS - CREATURE:NIGHT_CREATURE_1:SPLEEN - CREATURE:NIGHT_CREATURE_1:KIDNEY - CREATURE:NIGHT_CREATURE_2:MUSCLE -CREATURE:NIGHT_CREATURE_2:EYE -CREATURE:NIGHT_CREATURE_2:BRAIN -CREATURE:NIGHT_CREATURE_2:LUNG -CREATURE:NIGHT_CREATURE_2:HEART -CREATURE:NIGHT_CREATURE_2:LIVER -CREATURE:NIGHT_CREATURE_2:GUT -!CREATURE:NIGHT_CREATURE_2:STOMACH -!CREATURE:NIGHT_CREATURE_2:GIZZARD -"CREATURE:NIGHT_CREATURE_2:PANCREAS - CREATURE:NIGHT_CREATURE_2:SPLEEN - CREATURE:NIGHT_CREATURE_2:KIDNEY - CREATURE:NIGHT_CREATURE_3:MUSCLE -CREATURE:NIGHT_CREATURE_3:EYE -CREATURE:NIGHT_CREATURE_3:BRAIN -CREATURE:NIGHT_CREATURE_3:LUNG -CREATURE:NIGHT_CREATURE_3:HEART -CREATURE:NIGHT_CREATURE_3:LIVER -CREATURE:NIGHT_CREATURE_3:GUT -!CREATURE:NIGHT_CREATURE_3:STOMACH -!CREATURE:NIGHT_CREATURE_3:GIZZARD -"CREATURE:NIGHT_CREATURE_3:PANCREAS - CREATURE:NIGHT_CREATURE_3:SPLEEN - CREATURE:NIGHT_CREATURE_3:KIDNEY - CREATURE:NIGHT_CREATURE_4:MUSCLE -CREATURE:NIGHT_CREATURE_4:EYE -CREATURE:NIGHT_CREATURE_4:BRAIN -CREATURE:NIGHT_CREATURE_4:LUNG -CREATURE:NIGHT_CREATURE_4:HEART -CREATURE:NIGHT_CREATURE_4:LIVER -CREATURE:NIGHT_CREATURE_4:GUT -!CREATURE:NIGHT_CREATURE_4:STOMACH -!CREATURE:NIGHT_CREATURE_4:GIZZARD -"CREATURE:NIGHT_CREATURE_4:PANCREAS - CREATURE:NIGHT_CREATURE_4:SPLEEN - CREATURE:NIGHT_CREATURE_4:KIDNEY - CREATURE:NIGHT_CREATURE_5:MUSCLE -CREATURE:NIGHT_CREATURE_5:EYE -CREATURE:NIGHT_CREATURE_5:BRAIN -CREATURE:NIGHT_CREATURE_5:LUNG -CREATURE:NIGHT_CREATURE_5:HEART -CREATURE:NIGHT_CREATURE_5:LIVER -CREATURE:NIGHT_CREATURE_5:GUT -!CREATURE:NIGHT_CREATURE_5:STOMACH -!CREATURE:NIGHT_CREATURE_5:GIZZARD -"CREATURE:NIGHT_CREATURE_5:PANCREAS - CREATURE:NIGHT_CREATURE_5:SPLEEN - CREATURE:NIGHT_CREATURE_5:KIDNEY - CREATURE:NIGHT_CREATURE_6:MUSCLE -CREATURE:NIGHT_CREATURE_6:EYE -CREATURE:NIGHT_CREATURE_6:BRAIN -CREATURE:NIGHT_CREATURE_6:LUNG -CREATURE:NIGHT_CREATURE_6:HEART -CREATURE:NIGHT_CREATURE_6:LIVER -CREATURE:NIGHT_CREATURE_6:GUT -!CREATURE:NIGHT_CREATURE_6:STOMACH -!CREATURE:NIGHT_CREATURE_6:GIZZARD -"CREATURE:NIGHT_CREATURE_6:PANCREAS - CREATURE:NIGHT_CREATURE_6:SPLEEN - CREATURE:NIGHT_CREATURE_6:KIDNEY - CREATURE:NIGHT_CREATURE_7:MUSCLE -CREATURE:NIGHT_CREATURE_7:EYE -CREATURE:NIGHT_CREATURE_7:BRAIN -CREATURE:NIGHT_CREATURE_7:LUNG -CREATURE:NIGHT_CREATURE_7:HEART -CREATURE:NIGHT_CREATURE_7:LIVER -CREATURE:NIGHT_CREATURE_7:GUT -!CREATURE:NIGHT_CREATURE_7:STOMACH -!CREATURE:NIGHT_CREATURE_7:GIZZARD -"CREATURE:NIGHT_CREATURE_7:PANCREAS - CREATURE:NIGHT_CREATURE_7:SPLEEN - CREATURE:NIGHT_CREATURE_7:KIDNEY - CREATURE:NIGHT_CREATURE_8:MUSCLE -CREATURE:NIGHT_CREATURE_8:EYE -CREATURE:NIGHT_CREATURE_8:BRAIN -CREATURE:NIGHT_CREATURE_8:LUNG -CREATURE:NIGHT_CREATURE_8:HEART -CREATURE:NIGHT_CREATURE_8:LIVER -CREATURE:NIGHT_CREATURE_8:GUT -!CREATURE:NIGHT_CREATURE_8:STOMACH -!CREATURE:NIGHT_CREATURE_8:GIZZARD -"CREATURE:NIGHT_CREATURE_8:PANCREAS - CREATURE:NIGHT_CREATURE_8:SPLEEN - CREATURE:NIGHT_CREATURE_8:KIDNEY - CREATURE:NIGHT_CREATURE_9:MUSCLE -CREATURE:NIGHT_CREATURE_9:EYE -CREATURE:NIGHT_CREATURE_9:BRAIN -CREATURE:NIGHT_CREATURE_9:LUNG -CREATURE:NIGHT_CREATURE_9:HEART -CREATURE:NIGHT_CREATURE_9:LIVER -CREATURE:NIGHT_CREATURE_9:GUT -!CREATURE:NIGHT_CREATURE_9:STOMACH -!CREATURE:NIGHT_CREATURE_9:GIZZARD -"CREATURE:NIGHT_CREATURE_9:PANCREAS - CREATURE:NIGHT_CREATURE_9:SPLEEN - CREATURE:NIGHT_CREATURE_9:KIDNEY -!CREATURE:NIGHT_CREATURE_10:MUSCLE -CREATURE:NIGHT_CREATURE_10:EYE - CREATURE:NIGHT_CREATURE_10:BRAIN -CREATURE:NIGHT_CREATURE_10:LUNG - CREATURE:NIGHT_CREATURE_10:HEART - CREATURE:NIGHT_CREATURE_10:LIVER -CREATURE:NIGHT_CREATURE_10:GUT -"CREATURE:NIGHT_CREATURE_10:STOMACH -"CREATURE:NIGHT_CREATURE_10:GIZZARD -#CREATURE:NIGHT_CREATURE_10:PANCREAS -!CREATURE:NIGHT_CREATURE_10:SPLEEN -!CREATURE:NIGHT_CREATURE_10:KIDNEY -!CREATURE:NIGHT_CREATURE_11:MUSCLE -CREATURE:NIGHT_CREATURE_11:EYE - CREATURE:NIGHT_CREATURE_11:BRAIN -CREATURE:NIGHT_CREATURE_11:LUNG - CREATURE:NIGHT_CREATURE_11:HEART - CREATURE:NIGHT_CREATURE_11:LIVER -CREATURE:NIGHT_CREATURE_11:GUT -"CREATURE:NIGHT_CREATURE_11:STOMACH -"CREATURE:NIGHT_CREATURE_11:GIZZARD -#CREATURE:NIGHT_CREATURE_11:PANCREAS -!CREATURE:NIGHT_CREATURE_11:SPLEEN -!CREATURE:NIGHT_CREATURE_11:KIDNEY -!CREATURE:NIGHT_CREATURE_12:MUSCLE -CREATURE:NIGHT_CREATURE_12:EYE - CREATURE:NIGHT_CREATURE_12:BRAIN -CREATURE:NIGHT_CREATURE_12:LUNG - CREATURE:NIGHT_CREATURE_12:HEART - CREATURE:NIGHT_CREATURE_12:LIVER -CREATURE:NIGHT_CREATURE_12:GUT -"CREATURE:NIGHT_CREATURE_12:STOMACH -"CREATURE:NIGHT_CREATURE_12:GIZZARD -#CREATURE:NIGHT_CREATURE_12:PANCREAS -!CREATURE:NIGHT_CREATURE_12:SPLEEN -!CREATURE:NIGHT_CREATURE_12:KIDNEY -!CREATURE:NIGHT_CREATURE_13:MUSCLE -CREATURE:NIGHT_CREATURE_13:EYE - CREATURE:NIGHT_CREATURE_13:BRAIN -CREATURE:NIGHT_CREATURE_13:LUNG - CREATURE:NIGHT_CREATURE_13:HEART - CREATURE:NIGHT_CREATURE_13:LIVER -CREATURE:NIGHT_CREATURE_13:GUT -"CREATURE:NIGHT_CREATURE_13:STOMACH -"CREATURE:NIGHT_CREATURE_13:GIZZARD -#CREATURE:NIGHT_CREATURE_13:PANCREAS -!CREATURE:NIGHT_CREATURE_13:SPLEEN -!CREATURE:NIGHT_CREATURE_13:KIDNEY -!CREATURE:NIGHT_CREATURE_14:MUSCLE -CREATURE:NIGHT_CREATURE_14:EYE - CREATURE:NIGHT_CREATURE_14:BRAIN -CREATURE:NIGHT_CREATURE_14:LUNG - CREATURE:NIGHT_CREATURE_14:HEART - CREATURE:NIGHT_CREATURE_14:LIVER -CREATURE:NIGHT_CREATURE_14:GUT -"CREATURE:NIGHT_CREATURE_14:STOMACH -"CREATURE:NIGHT_CREATURE_14:GIZZARD -#CREATURE:NIGHT_CREATURE_14:PANCREAS -!CREATURE:NIGHT_CREATURE_14:SPLEEN -!CREATURE:NIGHT_CREATURE_14:KIDNEY -!CREATURE:NIGHT_CREATURE_15:MUSCLE -CREATURE:NIGHT_CREATURE_15:EYE - CREATURE:NIGHT_CREATURE_15:BRAIN -CREATURE:NIGHT_CREATURE_15:LUNG - CREATURE:NIGHT_CREATURE_15:HEART - CREATURE:NIGHT_CREATURE_15:LIVER -CREATURE:NIGHT_CREATURE_15:GUT -"CREATURE:NIGHT_CREATURE_15:STOMACH -"CREATURE:NIGHT_CREATURE_15:GIZZARD -#CREATURE:NIGHT_CREATURE_15:PANCREAS -!CREATURE:NIGHT_CREATURE_15:SPLEEN -!CREATURE:NIGHT_CREATURE_15:KIDNEY -!CREATURE:NIGHT_CREATURE_16:MUSCLE -CREATURE:NIGHT_CREATURE_16:EYE - CREATURE:NIGHT_CREATURE_16:BRAIN -CREATURE:NIGHT_CREATURE_16:LUNG - CREATURE:NIGHT_CREATURE_16:HEART - CREATURE:NIGHT_CREATURE_16:LIVER -CREATURE:NIGHT_CREATURE_16:GUT -"CREATURE:NIGHT_CREATURE_16:STOMACH -"CREATURE:NIGHT_CREATURE_16:GIZZARD -#CREATURE:NIGHT_CREATURE_16:PANCREAS -!CREATURE:NIGHT_CREATURE_16:SPLEEN -!CREATURE:NIGHT_CREATURE_16:KIDNEY -!CREATURE:NIGHT_CREATURE_17:MUSCLE -CREATURE:NIGHT_CREATURE_17:EYE - CREATURE:NIGHT_CREATURE_17:BRAIN -CREATURE:NIGHT_CREATURE_17:LUNG - CREATURE:NIGHT_CREATURE_17:HEART - CREATURE:NIGHT_CREATURE_17:LIVER -CREATURE:NIGHT_CREATURE_17:GUT -"CREATURE:NIGHT_CREATURE_17:STOMACH -"CREATURE:NIGHT_CREATURE_17:GIZZARD -#CREATURE:NIGHT_CREATURE_17:PANCREAS -!CREATURE:NIGHT_CREATURE_17:SPLEEN -!CREATURE:NIGHT_CREATURE_17:KIDNEY -!CREATURE:NIGHT_CREATURE_18:MUSCLE -CREATURE:NIGHT_CREATURE_18:EYE - CREATURE:NIGHT_CREATURE_18:BRAIN -CREATURE:NIGHT_CREATURE_18:LUNG - CREATURE:NIGHT_CREATURE_18:HEART - CREATURE:NIGHT_CREATURE_18:LIVER -CREATURE:NIGHT_CREATURE_18:GUT -"CREATURE:NIGHT_CREATURE_18:STOMACH -"CREATURE:NIGHT_CREATURE_18:GIZZARD -#CREATURE:NIGHT_CREATURE_18:PANCREAS -!CREATURE:NIGHT_CREATURE_18:SPLEEN -!CREATURE:NIGHT_CREATURE_18:KIDNEY -!CREATURE:NIGHT_CREATURE_19:MUSCLE -CREATURE:NIGHT_CREATURE_19:EYE - CREATURE:NIGHT_CREATURE_19:BRAIN -CREATURE:NIGHT_CREATURE_19:LUNG - CREATURE:NIGHT_CREATURE_19:HEART - CREATURE:NIGHT_CREATURE_19:LIVER -CREATURE:NIGHT_CREATURE_19:GUT -"CREATURE:NIGHT_CREATURE_19:STOMACH -"CREATURE:NIGHT_CREATURE_19:GIZZARD -#CREATURE:NIGHT_CREATURE_19:PANCREAS -!CREATURE:NIGHT_CREATURE_19:SPLEEN -!CREATURE:NIGHT_CREATURE_19:KIDNEY -!CREATURE:NIGHT_CREATURE_20:MUSCLE -CREATURE:NIGHT_CREATURE_20:EYE - CREATURE:NIGHT_CREATURE_20:BRAIN -CREATURE:NIGHT_CREATURE_20:LUNG - CREATURE:NIGHT_CREATURE_20:HEART - CREATURE:NIGHT_CREATURE_20:LIVER -CREATURE:NIGHT_CREATURE_20:GUT -"CREATURE:NIGHT_CREATURE_20:STOMACH -"CREATURE:NIGHT_CREATURE_20:GIZZARD -#CREATURE:NIGHT_CREATURE_20:PANCREAS -!CREATURE:NIGHT_CREATURE_20:SPLEEN -!CREATURE:NIGHT_CREATURE_20:KIDNEY -!CREATURE:NIGHT_CREATURE_21:MUSCLE -CREATURE:NIGHT_CREATURE_21:EYE - CREATURE:NIGHT_CREATURE_21:BRAIN -CREATURE:NIGHT_CREATURE_21:LUNG - CREATURE:NIGHT_CREATURE_21:HEART - CREATURE:NIGHT_CREATURE_21:LIVER -CREATURE:NIGHT_CREATURE_21:GUT -"CREATURE:NIGHT_CREATURE_21:STOMACH -"CREATURE:NIGHT_CREATURE_21:GIZZARD -#CREATURE:NIGHT_CREATURE_21:PANCREAS -!CREATURE:NIGHT_CREATURE_21:SPLEEN -!CREATURE:NIGHT_CREATURE_21:KIDNEY -!CREATURE:NIGHT_CREATURE_22:MUSCLE -CREATURE:NIGHT_CREATURE_22:EYE - CREATURE:NIGHT_CREATURE_22:BRAIN -CREATURE:NIGHT_CREATURE_22:LUNG - CREATURE:NIGHT_CREATURE_22:HEART - CREATURE:NIGHT_CREATURE_22:LIVER -CREATURE:NIGHT_CREATURE_22:GUT -"CREATURE:NIGHT_CREATURE_22:STOMACH -"CREATURE:NIGHT_CREATURE_22:GIZZARD -#CREATURE:NIGHT_CREATURE_22:PANCREAS -!CREATURE:NIGHT_CREATURE_22:SPLEEN -!CREATURE:NIGHT_CREATURE_22:KIDNEY -!CREATURE:NIGHT_CREATURE_23:MUSCLE -CREATURE:NIGHT_CREATURE_23:EYE - CREATURE:NIGHT_CREATURE_23:BRAIN -CREATURE:NIGHT_CREATURE_23:LUNG - CREATURE:NIGHT_CREATURE_23:HEART - CREATURE:NIGHT_CREATURE_23:LIVER -CREATURE:NIGHT_CREATURE_23:GUT -"CREATURE:NIGHT_CREATURE_23:STOMACH -"CREATURE:NIGHT_CREATURE_23:GIZZARD -#CREATURE:NIGHT_CREATURE_23:PANCREAS -!CREATURE:NIGHT_CREATURE_23:SPLEEN -!CREATURE:NIGHT_CREATURE_23:KIDNEY -!CREATURE:NIGHT_CREATURE_24:MUSCLE -CREATURE:NIGHT_CREATURE_24:EYE - CREATURE:NIGHT_CREATURE_24:BRAIN -CREATURE:NIGHT_CREATURE_24:LUNG - CREATURE:NIGHT_CREATURE_24:HEART - CREATURE:NIGHT_CREATURE_24:LIVER -CREATURE:NIGHT_CREATURE_24:GUT -"CREATURE:NIGHT_CREATURE_24:STOMACH -"CREATURE:NIGHT_CREATURE_24:GIZZARD -#CREATURE:NIGHT_CREATURE_24:PANCREAS -!CREATURE:NIGHT_CREATURE_24:SPLEEN -!CREATURE:NIGHT_CREATURE_24:KIDNEY -!CREATURE:NIGHT_CREATURE_25:MUSCLE -CREATURE:NIGHT_CREATURE_25:EYE - CREATURE:NIGHT_CREATURE_25:BRAIN -CREATURE:NIGHT_CREATURE_25:LUNG - CREATURE:NIGHT_CREATURE_25:HEART - CREATURE:NIGHT_CREATURE_25:LIVER -CREATURE:NIGHT_CREATURE_25:GUT -"CREATURE:NIGHT_CREATURE_25:STOMACH -"CREATURE:NIGHT_CREATURE_25:GIZZARD -#CREATURE:NIGHT_CREATURE_25:PANCREAS -!CREATURE:NIGHT_CREATURE_25:SPLEEN -!CREATURE:NIGHT_CREATURE_25:KIDNEY -!CREATURE:NIGHT_CREATURE_26:MUSCLE -CREATURE:NIGHT_CREATURE_26:EYE - CREATURE:NIGHT_CREATURE_26:BRAIN -CREATURE:NIGHT_CREATURE_26:LUNG - CREATURE:NIGHT_CREATURE_26:HEART - CREATURE:NIGHT_CREATURE_26:LIVER -CREATURE:NIGHT_CREATURE_26:GUT -"CREATURE:NIGHT_CREATURE_26:STOMACH -"CREATURE:NIGHT_CREATURE_26:GIZZARD -#CREATURE:NIGHT_CREATURE_26:PANCREAS -!CREATURE:NIGHT_CREATURE_26:SPLEEN -!CREATURE:NIGHT_CREATURE_26:KIDNEY -!CREATURE:NIGHT_CREATURE_27:MUSCLE -CREATURE:NIGHT_CREATURE_27:EYE - CREATURE:NIGHT_CREATURE_27:BRAIN -CREATURE:NIGHT_CREATURE_27:LUNG - CREATURE:NIGHT_CREATURE_27:HEART - CREATURE:NIGHT_CREATURE_27:LIVER -CREATURE:NIGHT_CREATURE_27:GUT -"CREATURE:NIGHT_CREATURE_27:STOMACH -"CREATURE:NIGHT_CREATURE_27:GIZZARD -#CREATURE:NIGHT_CREATURE_27:PANCREAS -!CREATURE:NIGHT_CREATURE_27:SPLEEN -!CREATURE:NIGHT_CREATURE_27:KIDNEY -!CREATURE:NIGHT_CREATURE_28:MUSCLE -CREATURE:NIGHT_CREATURE_28:EYE - CREATURE:NIGHT_CREATURE_28:BRAIN -CREATURE:NIGHT_CREATURE_28:LUNG - CREATURE:NIGHT_CREATURE_28:HEART - CREATURE:NIGHT_CREATURE_28:LIVER -CREATURE:NIGHT_CREATURE_28:GUT -"CREATURE:NIGHT_CREATURE_28:STOMACH -"CREATURE:NIGHT_CREATURE_28:GIZZARD -#CREATURE:NIGHT_CREATURE_28:PANCREAS -!CREATURE:NIGHT_CREATURE_28:SPLEEN -!CREATURE:NIGHT_CREATURE_28:KIDNEY -!CREATURE:NIGHT_CREATURE_29:MUSCLE -CREATURE:NIGHT_CREATURE_29:EYE - CREATURE:NIGHT_CREATURE_29:BRAIN -CREATURE:NIGHT_CREATURE_29:LUNG - CREATURE:NIGHT_CREATURE_29:HEART - CREATURE:NIGHT_CREATURE_29:LIVER -CREATURE:NIGHT_CREATURE_29:GUT -"CREATURE:NIGHT_CREATURE_29:STOMACH -"CREATURE:NIGHT_CREATURE_29:GIZZARD -#CREATURE:NIGHT_CREATURE_29:PANCREAS -!CREATURE:NIGHT_CREATURE_29:SPLEEN -!CREATURE:NIGHT_CREATURE_29:KIDNEY -!CREATURE:NIGHT_CREATURE_30:MUSCLE -CREATURE:NIGHT_CREATURE_30:EYE - CREATURE:NIGHT_CREATURE_30:BRAIN -CREATURE:NIGHT_CREATURE_30:LUNG - CREATURE:NIGHT_CREATURE_30:HEART - CREATURE:NIGHT_CREATURE_30:LIVER -CREATURE:NIGHT_CREATURE_30:GUT -"CREATURE:NIGHT_CREATURE_30:STOMACH -"CREATURE:NIGHT_CREATURE_30:GIZZARD -#CREATURE:NIGHT_CREATURE_30:PANCREAS -!CREATURE:NIGHT_CREATURE_30:SPLEEN -!CREATURE:NIGHT_CREATURE_30:KIDNEY -!CREATURE:NIGHT_CREATURE_31:MUSCLE -CREATURE:NIGHT_CREATURE_31:EYE - CREATURE:NIGHT_CREATURE_31:BRAIN -CREATURE:NIGHT_CREATURE_31:LUNG - CREATURE:NIGHT_CREATURE_31:HEART - CREATURE:NIGHT_CREATURE_31:LIVER -CREATURE:NIGHT_CREATURE_31:GUT -"CREATURE:NIGHT_CREATURE_31:STOMACH -"CREATURE:NIGHT_CREATURE_31:GIZZARD -#CREATURE:NIGHT_CREATURE_31:PANCREAS -!CREATURE:NIGHT_CREATURE_31:SPLEEN -!CREATURE:NIGHT_CREATURE_31:KIDNEY -!CREATURE:NIGHT_CREATURE_32:MUSCLE -CREATURE:NIGHT_CREATURE_32:EYE - CREATURE:NIGHT_CREATURE_32:BRAIN -CREATURE:NIGHT_CREATURE_32:LUNG - CREATURE:NIGHT_CREATURE_32:HEART - CREATURE:NIGHT_CREATURE_32:LIVER -CREATURE:NIGHT_CREATURE_32:GUT -"CREATURE:NIGHT_CREATURE_32:STOMACH -"CREATURE:NIGHT_CREATURE_32:GIZZARD -#CREATURE:NIGHT_CREATURE_32:PANCREAS -!CREATURE:NIGHT_CREATURE_32:SPLEEN -!CREATURE:NIGHT_CREATURE_32:KIDNEY -!CREATURE:NIGHT_CREATURE_33:MUSCLE -CREATURE:NIGHT_CREATURE_33:EYE - CREATURE:NIGHT_CREATURE_33:BRAIN -CREATURE:NIGHT_CREATURE_33:LUNG - CREATURE:NIGHT_CREATURE_33:HEART - CREATURE:NIGHT_CREATURE_33:LIVER -CREATURE:NIGHT_CREATURE_33:GUT -"CREATURE:NIGHT_CREATURE_33:STOMACH -"CREATURE:NIGHT_CREATURE_33:GIZZARD -#CREATURE:NIGHT_CREATURE_33:PANCREAS -!CREATURE:NIGHT_CREATURE_33:SPLEEN -!CREATURE:NIGHT_CREATURE_33:KIDNEY -!CREATURE:NIGHT_CREATURE_34:MUSCLE -CREATURE:NIGHT_CREATURE_34:EYE - CREATURE:NIGHT_CREATURE_34:BRAIN -CREATURE:NIGHT_CREATURE_34:LUNG - CREATURE:NIGHT_CREATURE_34:HEART - CREATURE:NIGHT_CREATURE_34:LIVER -CREATURE:NIGHT_CREATURE_34:GUT -"CREATURE:NIGHT_CREATURE_34:STOMACH -"CREATURE:NIGHT_CREATURE_34:GIZZARD -#CREATURE:NIGHT_CREATURE_34:PANCREAS -!CREATURE:NIGHT_CREATURE_34:SPLEEN -!CREATURE:NIGHT_CREATURE_34:KIDNEY -!CREATURE:NIGHT_CREATURE_35:MUSCLE -CREATURE:NIGHT_CREATURE_35:EYE - CREATURE:NIGHT_CREATURE_35:BRAIN -CREATURE:NIGHT_CREATURE_35:LUNG - CREATURE:NIGHT_CREATURE_35:HEART - CREATURE:NIGHT_CREATURE_35:LIVER -CREATURE:NIGHT_CREATURE_35:GUT -"CREATURE:NIGHT_CREATURE_35:STOMACH -"CREATURE:NIGHT_CREATURE_35:GIZZARD -#CREATURE:NIGHT_CREATURE_35:PANCREAS -!CREATURE:NIGHT_CREATURE_35:SPLEEN -!CREATURE:NIGHT_CREATURE_35:KIDNEY -!CREATURE:NIGHT_CREATURE_36:MUSCLE -CREATURE:NIGHT_CREATURE_36:EYE - CREATURE:NIGHT_CREATURE_36:BRAIN -CREATURE:NIGHT_CREATURE_36:LUNG - CREATURE:NIGHT_CREATURE_36:HEART - CREATURE:NIGHT_CREATURE_36:LIVER -CREATURE:NIGHT_CREATURE_36:GUT -"CREATURE:NIGHT_CREATURE_36:STOMACH -"CREATURE:NIGHT_CREATURE_36:GIZZARD -#CREATURE:NIGHT_CREATURE_36:PANCREAS -!CREATURE:NIGHT_CREATURE_36:SPLEEN -!CREATURE:NIGHT_CREATURE_36:KIDNEY -!CREATURE:NIGHT_CREATURE_37:MUSCLE -CREATURE:NIGHT_CREATURE_37:EYE - CREATURE:NIGHT_CREATURE_37:BRAIN -CREATURE:NIGHT_CREATURE_37:LUNG - CREATURE:NIGHT_CREATURE_37:HEART - CREATURE:NIGHT_CREATURE_37:LIVER -CREATURE:NIGHT_CREATURE_37:GUT -"CREATURE:NIGHT_CREATURE_37:STOMACH -"CREATURE:NIGHT_CREATURE_37:GIZZARD -#CREATURE:NIGHT_CREATURE_37:PANCREAS -!CREATURE:NIGHT_CREATURE_37:SPLEEN -!CREATURE:NIGHT_CREATURE_37:KIDNEY -!CREATURE:NIGHT_CREATURE_38:MUSCLE -CREATURE:NIGHT_CREATURE_38:EYE - CREATURE:NIGHT_CREATURE_38:BRAIN -CREATURE:NIGHT_CREATURE_38:LUNG - CREATURE:NIGHT_CREATURE_38:HEART - CREATURE:NIGHT_CREATURE_38:LIVER -CREATURE:NIGHT_CREATURE_38:GUT -"CREATURE:NIGHT_CREATURE_38:STOMACH -"CREATURE:NIGHT_CREATURE_38:GIZZARD -#CREATURE:NIGHT_CREATURE_38:PANCREAS -!CREATURE:NIGHT_CREATURE_38:SPLEEN -!CREATURE:NIGHT_CREATURE_38:KIDNEY -!CREATURE:NIGHT_CREATURE_39:MUSCLE -CREATURE:NIGHT_CREATURE_39:EYE - CREATURE:NIGHT_CREATURE_39:BRAIN -CREATURE:NIGHT_CREATURE_39:LUNG - CREATURE:NIGHT_CREATURE_39:HEART - CREATURE:NIGHT_CREATURE_39:LIVER -CREATURE:NIGHT_CREATURE_39:GUT -"CREATURE:NIGHT_CREATURE_39:STOMACH -"CREATURE:NIGHT_CREATURE_39:GIZZARD -#CREATURE:NIGHT_CREATURE_39:PANCREAS -!CREATURE:NIGHT_CREATURE_39:SPLEEN -!CREATURE:NIGHT_CREATURE_39:KIDNEY -!CREATURE:NIGHT_CREATURE_40:MUSCLE -CREATURE:NIGHT_CREATURE_40:EYE - CREATURE:NIGHT_CREATURE_40:BRAIN -CREATURE:NIGHT_CREATURE_40:LUNG - CREATURE:NIGHT_CREATURE_40:HEART - CREATURE:NIGHT_CREATURE_40:LIVER -CREATURE:NIGHT_CREATURE_40:GUT -"CREATURE:NIGHT_CREATURE_40:STOMACH -"CREATURE:NIGHT_CREATURE_40:GIZZARD -#CREATURE:NIGHT_CREATURE_40:PANCREAS -!CREATURE:NIGHT_CREATURE_40:SPLEEN -!CREATURE:NIGHT_CREATURE_40:KIDNEY -!CREATURE:NIGHT_CREATURE_41:MUSCLE -CREATURE:NIGHT_CREATURE_41:EYE - CREATURE:NIGHT_CREATURE_41:BRAIN -CREATURE:NIGHT_CREATURE_41:LUNG - CREATURE:NIGHT_CREATURE_41:HEART - CREATURE:NIGHT_CREATURE_41:LIVER -CREATURE:NIGHT_CREATURE_41:GUT -"CREATURE:NIGHT_CREATURE_41:STOMACH -"CREATURE:NIGHT_CREATURE_41:GIZZARD -#CREATURE:NIGHT_CREATURE_41:PANCREAS -!CREATURE:NIGHT_CREATURE_41:SPLEEN -!CREATURE:NIGHT_CREATURE_41:KIDNEY -!CREATURE:NIGHT_CREATURE_42:MUSCLE -CREATURE:NIGHT_CREATURE_42:EYE - CREATURE:NIGHT_CREATURE_42:BRAIN -CREATURE:NIGHT_CREATURE_42:LUNG - CREATURE:NIGHT_CREATURE_42:HEART - CREATURE:NIGHT_CREATURE_42:LIVER -CREATURE:NIGHT_CREATURE_42:GUT -"CREATURE:NIGHT_CREATURE_42:STOMACH -"CREATURE:NIGHT_CREATURE_42:GIZZARD -#CREATURE:NIGHT_CREATURE_42:PANCREAS -!CREATURE:NIGHT_CREATURE_42:SPLEEN -!CREATURE:NIGHT_CREATURE_42:KIDNEY -!CREATURE:NIGHT_CREATURE_43:MUSCLE -CREATURE:NIGHT_CREATURE_43:EYE - CREATURE:NIGHT_CREATURE_43:BRAIN -CREATURE:NIGHT_CREATURE_43:LUNG - CREATURE:NIGHT_CREATURE_43:HEART - CREATURE:NIGHT_CREATURE_43:LIVER -CREATURE:NIGHT_CREATURE_43:GUT -"CREATURE:NIGHT_CREATURE_43:STOMACH -"CREATURE:NIGHT_CREATURE_43:GIZZARD -#CREATURE:NIGHT_CREATURE_43:PANCREAS -!CREATURE:NIGHT_CREATURE_43:SPLEEN -!CREATURE:NIGHT_CREATURE_43:KIDNEY -!CREATURE:NIGHT_CREATURE_44:MUSCLE -CREATURE:NIGHT_CREATURE_44:EYE - CREATURE:NIGHT_CREATURE_44:BRAIN -CREATURE:NIGHT_CREATURE_44:LUNG - CREATURE:NIGHT_CREATURE_44:HEART - CREATURE:NIGHT_CREATURE_44:LIVER -CREATURE:NIGHT_CREATURE_44:GUT -"CREATURE:NIGHT_CREATURE_44:STOMACH -"CREATURE:NIGHT_CREATURE_44:GIZZARD -#CREATURE:NIGHT_CREATURE_44:PANCREAS -!CREATURE:NIGHT_CREATURE_44:SPLEEN -!CREATURE:NIGHT_CREATURE_44:KIDNEY -!CREATURE:NIGHT_CREATURE_45:MUSCLE -CREATURE:NIGHT_CREATURE_45:EYE - CREATURE:NIGHT_CREATURE_45:BRAIN -CREATURE:NIGHT_CREATURE_45:LUNG - CREATURE:NIGHT_CREATURE_45:HEART - CREATURE:NIGHT_CREATURE_45:LIVER -CREATURE:NIGHT_CREATURE_45:GUT -"CREATURE:NIGHT_CREATURE_45:STOMACH -"CREATURE:NIGHT_CREATURE_45:GIZZARD -#CREATURE:NIGHT_CREATURE_45:PANCREAS -!CREATURE:NIGHT_CREATURE_45:SPLEEN -!CREATURE:NIGHT_CREATURE_45:KIDNEY -!CREATURE:NIGHT_CREATURE_46:MUSCLE -CREATURE:NIGHT_CREATURE_46:EYE - CREATURE:NIGHT_CREATURE_46:BRAIN -CREATURE:NIGHT_CREATURE_46:LUNG - CREATURE:NIGHT_CREATURE_46:HEART - CREATURE:NIGHT_CREATURE_46:LIVER -CREATURE:NIGHT_CREATURE_46:GUT -"CREATURE:NIGHT_CREATURE_46:STOMACH -"CREATURE:NIGHT_CREATURE_46:GIZZARD -#CREATURE:NIGHT_CREATURE_46:PANCREAS -!CREATURE:NIGHT_CREATURE_46:SPLEEN -!CREATURE:NIGHT_CREATURE_46:KIDNEY -!CREATURE:NIGHT_CREATURE_47:MUSCLE -CREATURE:NIGHT_CREATURE_47:EYE - CREATURE:NIGHT_CREATURE_47:BRAIN -CREATURE:NIGHT_CREATURE_47:LUNG - CREATURE:NIGHT_CREATURE_47:HEART - CREATURE:NIGHT_CREATURE_47:LIVER -CREATURE:NIGHT_CREATURE_47:GUT -"CREATURE:NIGHT_CREATURE_47:STOMACH -"CREATURE:NIGHT_CREATURE_47:GIZZARD -#CREATURE:NIGHT_CREATURE_47:PANCREAS -!CREATURE:NIGHT_CREATURE_47:SPLEEN -!CREATURE:NIGHT_CREATURE_47:KIDNEY -!CREATURE:NIGHT_CREATURE_48:MUSCLE -CREATURE:NIGHT_CREATURE_48:EYE - CREATURE:NIGHT_CREATURE_48:BRAIN -CREATURE:NIGHT_CREATURE_48:LUNG - CREATURE:NIGHT_CREATURE_48:HEART - CREATURE:NIGHT_CREATURE_48:LIVER -CREATURE:NIGHT_CREATURE_48:GUT -"CREATURE:NIGHT_CREATURE_48:STOMACH -"CREATURE:NIGHT_CREATURE_48:GIZZARD -#CREATURE:NIGHT_CREATURE_48:PANCREAS -!CREATURE:NIGHT_CREATURE_48:SPLEEN -!CREATURE:NIGHT_CREATURE_48:KIDNEY -!CREATURE:NIGHT_CREATURE_49:MUSCLE -CREATURE:NIGHT_CREATURE_49:EYE - CREATURE:NIGHT_CREATURE_49:BRAIN -CREATURE:NIGHT_CREATURE_49:LUNG - CREATURE:NIGHT_CREATURE_49:HEART - CREATURE:NIGHT_CREATURE_49:LIVER -CREATURE:NIGHT_CREATURE_49:GUT -"CREATURE:NIGHT_CREATURE_49:STOMACH -"CREATURE:NIGHT_CREATURE_49:GIZZARD -#CREATURE:NIGHT_CREATURE_49:PANCREAS -!CREATURE:NIGHT_CREATURE_49:SPLEEN -!CREATURE:NIGHT_CREATURE_49:KIDNEY -!CREATURE:NIGHT_CREATURE_50:MUSCLE -CREATURE:NIGHT_CREATURE_50:EYE - CREATURE:NIGHT_CREATURE_50:BRAIN -CREATURE:NIGHT_CREATURE_50:LUNG - CREATURE:NIGHT_CREATURE_50:HEART - CREATURE:NIGHT_CREATURE_50:LIVER -CREATURE:NIGHT_CREATURE_50:GUT -"CREATURE:NIGHT_CREATURE_50:STOMACH -"CREATURE:NIGHT_CREATURE_50:GIZZARD -#CREATURE:NIGHT_CREATURE_50:PANCREAS -!CREATURE:NIGHT_CREATURE_50:SPLEEN -!CREATURE:NIGHT_CREATURE_50:KIDNEY -!CREATURE:NIGHT_CREATURE_51:MUSCLE -CREATURE:NIGHT_CREATURE_51:EYE - CREATURE:NIGHT_CREATURE_51:BRAIN -CREATURE:NIGHT_CREATURE_51:LUNG - CREATURE:NIGHT_CREATURE_51:HEART - CREATURE:NIGHT_CREATURE_51:LIVER -CREATURE:NIGHT_CREATURE_51:GUT -"CREATURE:NIGHT_CREATURE_51:STOMACH -"CREATURE:NIGHT_CREATURE_51:GIZZARD -#CREATURE:NIGHT_CREATURE_51:PANCREAS -!CREATURE:NIGHT_CREATURE_51:SPLEEN -!CREATURE:NIGHT_CREATURE_51:KIDNEY -!CREATURE:NIGHT_CREATURE_52:MUSCLE -CREATURE:NIGHT_CREATURE_52:EYE - CREATURE:NIGHT_CREATURE_52:BRAIN -CREATURE:NIGHT_CREATURE_52:LUNG - CREATURE:NIGHT_CREATURE_52:HEART - CREATURE:NIGHT_CREATURE_52:LIVER -CREATURE:NIGHT_CREATURE_52:GUT -"CREATURE:NIGHT_CREATURE_52:STOMACH -"CREATURE:NIGHT_CREATURE_52:GIZZARD -#CREATURE:NIGHT_CREATURE_52:PANCREAS -!CREATURE:NIGHT_CREATURE_52:SPLEEN -!CREATURE:NIGHT_CREATURE_52:KIDNEY -!CREATURE:NIGHT_CREATURE_53:MUSCLE -CREATURE:NIGHT_CREATURE_53:EYE - CREATURE:NIGHT_CREATURE_53:BRAIN -CREATURE:NIGHT_CREATURE_53:LUNG - CREATURE:NIGHT_CREATURE_53:HEART - CREATURE:NIGHT_CREATURE_53:LIVER -CREATURE:NIGHT_CREATURE_53:GUT -"CREATURE:NIGHT_CREATURE_53:STOMACH -"CREATURE:NIGHT_CREATURE_53:GIZZARD -#CREATURE:NIGHT_CREATURE_53:PANCREAS -!CREATURE:NIGHT_CREATURE_53:SPLEEN -!CREATURE:NIGHT_CREATURE_53:KIDNEY -!CREATURE:NIGHT_CREATURE_54:MUSCLE -CREATURE:NIGHT_CREATURE_54:EYE - CREATURE:NIGHT_CREATURE_54:BRAIN -CREATURE:NIGHT_CREATURE_54:LUNG - CREATURE:NIGHT_CREATURE_54:HEART - CREATURE:NIGHT_CREATURE_54:LIVER -CREATURE:NIGHT_CREATURE_54:GUT -"CREATURE:NIGHT_CREATURE_54:STOMACH -"CREATURE:NIGHT_CREATURE_54:GIZZARD -#CREATURE:NIGHT_CREATURE_54:PANCREAS -!CREATURE:NIGHT_CREATURE_54:SPLEEN -!CREATURE:NIGHT_CREATURE_54:KIDNEY -!CREATURE:NIGHT_CREATURE_55:MUSCLE -CREATURE:NIGHT_CREATURE_55:EYE - CREATURE:NIGHT_CREATURE_55:BRAIN -CREATURE:NIGHT_CREATURE_55:LUNG - CREATURE:NIGHT_CREATURE_55:HEART - CREATURE:NIGHT_CREATURE_55:LIVER -CREATURE:NIGHT_CREATURE_55:GUT -"CREATURE:NIGHT_CREATURE_55:STOMACH -"CREATURE:NIGHT_CREATURE_55:GIZZARD -#CREATURE:NIGHT_CREATURE_55:PANCREAS -!CREATURE:NIGHT_CREATURE_55:SPLEEN -!CREATURE:NIGHT_CREATURE_55:KIDNEY -!CREATURE:NIGHT_CREATURE_56:MUSCLE -CREATURE:NIGHT_CREATURE_56:EYE - CREATURE:NIGHT_CREATURE_56:BRAIN -CREATURE:NIGHT_CREATURE_56:LUNG - CREATURE:NIGHT_CREATURE_56:HEART - CREATURE:NIGHT_CREATURE_56:LIVER -CREATURE:NIGHT_CREATURE_56:GUT -"CREATURE:NIGHT_CREATURE_56:STOMACH -"CREATURE:NIGHT_CREATURE_56:GIZZARD -#CREATURE:NIGHT_CREATURE_56:PANCREAS -!CREATURE:NIGHT_CREATURE_56:SPLEEN -!CREATURE:NIGHT_CREATURE_56:KIDNEY -!CREATURE:NIGHT_CREATURE_57:MUSCLE -CREATURE:NIGHT_CREATURE_57:EYE - CREATURE:NIGHT_CREATURE_57:BRAIN -CREATURE:NIGHT_CREATURE_57:LUNG - CREATURE:NIGHT_CREATURE_57:HEART - CREATURE:NIGHT_CREATURE_57:LIVER -CREATURE:NIGHT_CREATURE_57:GUT -"CREATURE:NIGHT_CREATURE_57:STOMACH -"CREATURE:NIGHT_CREATURE_57:GIZZARD -#CREATURE:NIGHT_CREATURE_57:PANCREAS -!CREATURE:NIGHT_CREATURE_57:SPLEEN -!CREATURE:NIGHT_CREATURE_57:KIDNEY -!CREATURE:NIGHT_CREATURE_58:MUSCLE -CREATURE:NIGHT_CREATURE_58:EYE - CREATURE:NIGHT_CREATURE_58:BRAIN -CREATURE:NIGHT_CREATURE_58:LUNG - CREATURE:NIGHT_CREATURE_58:HEART - CREATURE:NIGHT_CREATURE_58:LIVER -CREATURE:NIGHT_CREATURE_58:GUT -"CREATURE:NIGHT_CREATURE_58:STOMACH -"CREATURE:NIGHT_CREATURE_58:GIZZARD -#CREATURE:NIGHT_CREATURE_58:PANCREAS -!CREATURE:NIGHT_CREATURE_58:SPLEEN -!CREATURE:NIGHT_CREATURE_58:KIDNEY -!CREATURE:NIGHT_CREATURE_59:MUSCLE -CREATURE:NIGHT_CREATURE_59:EYE - CREATURE:NIGHT_CREATURE_59:BRAIN -CREATURE:NIGHT_CREATURE_59:LUNG - CREATURE:NIGHT_CREATURE_59:HEART - CREATURE:NIGHT_CREATURE_59:LIVER -CREATURE:NIGHT_CREATURE_59:GUT -"CREATURE:NIGHT_CREATURE_59:STOMACH -"CREATURE:NIGHT_CREATURE_59:GIZZARD -#CREATURE:NIGHT_CREATURE_59:PANCREAS -!CREATURE:NIGHT_CREATURE_59:SPLEEN -!CREATURE:NIGHT_CREATURE_59:KIDNEY -!CREATURE:NIGHT_CREATURE_60:MUSCLE -CREATURE:NIGHT_CREATURE_60:EYE - CREATURE:NIGHT_CREATURE_60:BRAIN -CREATURE:NIGHT_CREATURE_60:LUNG - CREATURE:NIGHT_CREATURE_60:HEART - CREATURE:NIGHT_CREATURE_60:LIVER -CREATURE:NIGHT_CREATURE_60:GUT -"CREATURE:NIGHT_CREATURE_60:STOMACH -"CREATURE:NIGHT_CREATURE_60:GIZZARD -#CREATURE:NIGHT_CREATURE_60:PANCREAS -!CREATURE:NIGHT_CREATURE_60:SPLEEN -!CREATURE:NIGHT_CREATURE_60:KIDNEY -!CREATURE:NIGHT_CREATURE_61:MUSCLE -CREATURE:NIGHT_CREATURE_61:EYE - CREATURE:NIGHT_CREATURE_61:BRAIN -CREATURE:NIGHT_CREATURE_61:LUNG - CREATURE:NIGHT_CREATURE_61:HEART - CREATURE:NIGHT_CREATURE_61:LIVER -CREATURE:NIGHT_CREATURE_61:GUT -"CREATURE:NIGHT_CREATURE_61:STOMACH -"CREATURE:NIGHT_CREATURE_61:GIZZARD -#CREATURE:NIGHT_CREATURE_61:PANCREAS -!CREATURE:NIGHT_CREATURE_61:SPLEEN -!CREATURE:NIGHT_CREATURE_61:KIDNEY -!CREATURE:NIGHT_CREATURE_62:MUSCLE -CREATURE:NIGHT_CREATURE_62:EYE - CREATURE:NIGHT_CREATURE_62:BRAIN -CREATURE:NIGHT_CREATURE_62:LUNG - CREATURE:NIGHT_CREATURE_62:HEART - CREATURE:NIGHT_CREATURE_62:LIVER -CREATURE:NIGHT_CREATURE_62:GUT -"CREATURE:NIGHT_CREATURE_62:STOMACH -"CREATURE:NIGHT_CREATURE_62:GIZZARD -#CREATURE:NIGHT_CREATURE_62:PANCREAS -!CREATURE:NIGHT_CREATURE_62:SPLEEN -!CREATURE:NIGHT_CREATURE_62:KIDNEY -!CREATURE:NIGHT_CREATURE_63:MUSCLE -CREATURE:NIGHT_CREATURE_63:EYE - CREATURE:NIGHT_CREATURE_63:BRAIN -CREATURE:NIGHT_CREATURE_63:LUNG - CREATURE:NIGHT_CREATURE_63:HEART - CREATURE:NIGHT_CREATURE_63:LIVER -CREATURE:NIGHT_CREATURE_63:GUT -"CREATURE:NIGHT_CREATURE_63:STOMACH -"CREATURE:NIGHT_CREATURE_63:GIZZARD -#CREATURE:NIGHT_CREATURE_63:PANCREAS -!CREATURE:NIGHT_CREATURE_63:SPLEEN -!CREATURE:NIGHT_CREATURE_63:KIDNEY -!CREATURE:NIGHT_CREATURE_64:MUSCLE -CREATURE:NIGHT_CREATURE_64:EYE - CREATURE:NIGHT_CREATURE_64:BRAIN -CREATURE:NIGHT_CREATURE_64:LUNG - CREATURE:NIGHT_CREATURE_64:HEART - CREATURE:NIGHT_CREATURE_64:LIVER -CREATURE:NIGHT_CREATURE_64:GUT -"CREATURE:NIGHT_CREATURE_64:STOMACH -"CREATURE:NIGHT_CREATURE_64:GIZZARD -#CREATURE:NIGHT_CREATURE_64:PANCREAS -!CREATURE:NIGHT_CREATURE_64:SPLEEN -!CREATURE:NIGHT_CREATURE_64:KIDNEY -!CREATURE:NIGHT_CREATURE_65:MUSCLE -CREATURE:NIGHT_CREATURE_65:EYE - CREATURE:NIGHT_CREATURE_65:BRAIN -CREATURE:NIGHT_CREATURE_65:LUNG - CREATURE:NIGHT_CREATURE_65:HEART - CREATURE:NIGHT_CREATURE_65:LIVER -CREATURE:NIGHT_CREATURE_65:GUT -"CREATURE:NIGHT_CREATURE_65:STOMACH -"CREATURE:NIGHT_CREATURE_65:GIZZARD -#CREATURE:NIGHT_CREATURE_65:PANCREAS -!CREATURE:NIGHT_CREATURE_65:SPLEEN -!CREATURE:NIGHT_CREATURE_65:KIDNEY -!CREATURE:NIGHT_CREATURE_66:MUSCLE -CREATURE:NIGHT_CREATURE_66:EYE - CREATURE:NIGHT_CREATURE_66:BRAIN -CREATURE:NIGHT_CREATURE_66:LUNG - CREATURE:NIGHT_CREATURE_66:HEART - CREATURE:NIGHT_CREATURE_66:LIVER -CREATURE:NIGHT_CREATURE_66:GUT -"CREATURE:NIGHT_CREATURE_66:STOMACH -"CREATURE:NIGHT_CREATURE_66:GIZZARD -#CREATURE:NIGHT_CREATURE_66:PANCREAS -!CREATURE:NIGHT_CREATURE_66:SPLEEN -!CREATURE:NIGHT_CREATURE_66:KIDNEY -!CREATURE:NIGHT_CREATURE_67:MUSCLE -CREATURE:NIGHT_CREATURE_67:EYE - CREATURE:NIGHT_CREATURE_67:BRAIN -CREATURE:NIGHT_CREATURE_67:LUNG - CREATURE:NIGHT_CREATURE_67:HEART - CREATURE:NIGHT_CREATURE_67:LIVER -CREATURE:NIGHT_CREATURE_67:GUT -"CREATURE:NIGHT_CREATURE_67:STOMACH -"CREATURE:NIGHT_CREATURE_67:GIZZARD -#CREATURE:NIGHT_CREATURE_67:PANCREAS -!CREATURE:NIGHT_CREATURE_67:SPLEEN -!CREATURE:NIGHT_CREATURE_67:KIDNEY -!CREATURE:NIGHT_CREATURE_68:MUSCLE -CREATURE:NIGHT_CREATURE_68:EYE - CREATURE:NIGHT_CREATURE_68:BRAIN -CREATURE:NIGHT_CREATURE_68:LUNG - CREATURE:NIGHT_CREATURE_68:HEART - CREATURE:NIGHT_CREATURE_68:LIVER -CREATURE:NIGHT_CREATURE_68:GUT -"CREATURE:NIGHT_CREATURE_68:STOMACH -"CREATURE:NIGHT_CREATURE_68:GIZZARD -#CREATURE:NIGHT_CREATURE_68:PANCREAS -!CREATURE:NIGHT_CREATURE_68:SPLEEN -!CREATURE:NIGHT_CREATURE_68:KIDNEY -!CREATURE:NIGHT_CREATURE_69:MUSCLE -CREATURE:NIGHT_CREATURE_69:EYE - CREATURE:NIGHT_CREATURE_69:BRAIN -CREATURE:NIGHT_CREATURE_69:LUNG - CREATURE:NIGHT_CREATURE_69:HEART - CREATURE:NIGHT_CREATURE_69:LIVER -CREATURE:NIGHT_CREATURE_69:GUT -"CREATURE:NIGHT_CREATURE_69:STOMACH -"CREATURE:NIGHT_CREATURE_69:GIZZARD -#CREATURE:NIGHT_CREATURE_69:PANCREAS -!CREATURE:NIGHT_CREATURE_69:SPLEEN -!CREATURE:NIGHT_CREATURE_69:KIDNEY -!CREATURE:NIGHT_CREATURE_70:MUSCLE -CREATURE:NIGHT_CREATURE_70:EYE - CREATURE:NIGHT_CREATURE_70:BRAIN -CREATURE:NIGHT_CREATURE_70:LUNG - CREATURE:NIGHT_CREATURE_70:HEART - CREATURE:NIGHT_CREATURE_70:LIVER -CREATURE:NIGHT_CREATURE_70:GUT -"CREATURE:NIGHT_CREATURE_70:STOMACH -"CREATURE:NIGHT_CREATURE_70:GIZZARD -#CREATURE:NIGHT_CREATURE_70:PANCREAS -!CREATURE:NIGHT_CREATURE_70:SPLEEN -!CREATURE:NIGHT_CREATURE_70:KIDNEY -!CREATURE:NIGHT_CREATURE_71:MUSCLE -CREATURE:NIGHT_CREATURE_71:EYE - CREATURE:NIGHT_CREATURE_71:BRAIN -CREATURE:NIGHT_CREATURE_71:LUNG - CREATURE:NIGHT_CREATURE_71:HEART - CREATURE:NIGHT_CREATURE_71:LIVER -CREATURE:NIGHT_CREATURE_71:GUT -"CREATURE:NIGHT_CREATURE_71:STOMACH -"CREATURE:NIGHT_CREATURE_71:GIZZARD -#CREATURE:NIGHT_CREATURE_71:PANCREAS -!CREATURE:NIGHT_CREATURE_71:SPLEEN -!CREATURE:NIGHT_CREATURE_71:KIDNEY -!CREATURE:NIGHT_CREATURE_72:MUSCLE -CREATURE:NIGHT_CREATURE_72:EYE - CREATURE:NIGHT_CREATURE_72:BRAIN -CREATURE:NIGHT_CREATURE_72:LUNG - CREATURE:NIGHT_CREATURE_72:HEART - CREATURE:NIGHT_CREATURE_72:LIVER -CREATURE:NIGHT_CREATURE_72:GUT -"CREATURE:NIGHT_CREATURE_72:STOMACH -"CREATURE:NIGHT_CREATURE_72:GIZZARD -#CREATURE:NIGHT_CREATURE_72:PANCREAS -!CREATURE:NIGHT_CREATURE_72:SPLEEN -!CREATURE:NIGHT_CREATURE_72:KIDNEY -!CREATURE:NIGHT_CREATURE_73:MUSCLE -CREATURE:NIGHT_CREATURE_73:EYE - CREATURE:NIGHT_CREATURE_73:BRAIN -CREATURE:NIGHT_CREATURE_73:LUNG - CREATURE:NIGHT_CREATURE_73:HEART - CREATURE:NIGHT_CREATURE_73:LIVER -CREATURE:NIGHT_CREATURE_73:GUT -"CREATURE:NIGHT_CREATURE_73:STOMACH -"CREATURE:NIGHT_CREATURE_73:GIZZARD -#CREATURE:NIGHT_CREATURE_73:PANCREAS -!CREATURE:NIGHT_CREATURE_73:SPLEEN -!CREATURE:NIGHT_CREATURE_73:KIDNEY -!CREATURE:NIGHT_CREATURE_74:MUSCLE -CREATURE:NIGHT_CREATURE_74:EYE - CREATURE:NIGHT_CREATURE_74:BRAIN -CREATURE:NIGHT_CREATURE_74:LUNG - CREATURE:NIGHT_CREATURE_74:HEART - CREATURE:NIGHT_CREATURE_74:LIVER -CREATURE:NIGHT_CREATURE_74:GUT -"CREATURE:NIGHT_CREATURE_74:STOMACH -"CREATURE:NIGHT_CREATURE_74:GIZZARD -#CREATURE:NIGHT_CREATURE_74:PANCREAS -!CREATURE:NIGHT_CREATURE_74:SPLEEN -!CREATURE:NIGHT_CREATURE_74:KIDNEY -!CREATURE:NIGHT_CREATURE_75:MUSCLE -CREATURE:NIGHT_CREATURE_75:EYE - CREATURE:NIGHT_CREATURE_75:BRAIN -CREATURE:NIGHT_CREATURE_75:LUNG - CREATURE:NIGHT_CREATURE_75:HEART - CREATURE:NIGHT_CREATURE_75:LIVER -CREATURE:NIGHT_CREATURE_75:GUT -"CREATURE:NIGHT_CREATURE_75:STOMACH -"CREATURE:NIGHT_CREATURE_75:GIZZARD -#CREATURE:NIGHT_CREATURE_75:PANCREAS -!CREATURE:NIGHT_CREATURE_75:SPLEEN -!CREATURE:NIGHT_CREATURE_75:KIDNEY -!CREATURE:NIGHT_CREATURE_76:MUSCLE -CREATURE:NIGHT_CREATURE_76:EYE - CREATURE:NIGHT_CREATURE_76:BRAIN -CREATURE:NIGHT_CREATURE_76:LUNG - CREATURE:NIGHT_CREATURE_76:HEART - CREATURE:NIGHT_CREATURE_76:LIVER -CREATURE:NIGHT_CREATURE_76:GUT -"CREATURE:NIGHT_CREATURE_76:STOMACH -"CREATURE:NIGHT_CREATURE_76:GIZZARD -#CREATURE:NIGHT_CREATURE_76:PANCREAS -!CREATURE:NIGHT_CREATURE_76:SPLEEN -!CREATURE:NIGHT_CREATURE_76:KIDNEY -!CREATURE:NIGHT_CREATURE_77:MUSCLE -CREATURE:NIGHT_CREATURE_77:EYE - CREATURE:NIGHT_CREATURE_77:BRAIN -CREATURE:NIGHT_CREATURE_77:LUNG - CREATURE:NIGHT_CREATURE_77:HEART - CREATURE:NIGHT_CREATURE_77:LIVER -CREATURE:NIGHT_CREATURE_77:GUT -"CREATURE:NIGHT_CREATURE_77:STOMACH -"CREATURE:NIGHT_CREATURE_77:GIZZARD -#CREATURE:NIGHT_CREATURE_77:PANCREAS -!CREATURE:NIGHT_CREATURE_77:SPLEEN -!CREATURE:NIGHT_CREATURE_77:KIDNEY -!CREATURE:NIGHT_CREATURE_78:MUSCLE -CREATURE:NIGHT_CREATURE_78:EYE - CREATURE:NIGHT_CREATURE_78:BRAIN -CREATURE:NIGHT_CREATURE_78:LUNG - CREATURE:NIGHT_CREATURE_78:HEART - CREATURE:NIGHT_CREATURE_78:LIVER -CREATURE:NIGHT_CREATURE_78:GUT -"CREATURE:NIGHT_CREATURE_78:STOMACH -"CREATURE:NIGHT_CREATURE_78:GIZZARD -#CREATURE:NIGHT_CREATURE_78:PANCREAS -!CREATURE:NIGHT_CREATURE_78:SPLEEN -!CREATURE:NIGHT_CREATURE_78:KIDNEY -!CREATURE:NIGHT_CREATURE_79:MUSCLE -CREATURE:NIGHT_CREATURE_79:EYE - CREATURE:NIGHT_CREATURE_79:BRAIN -CREATURE:NIGHT_CREATURE_79:LUNG - CREATURE:NIGHT_CREATURE_79:HEART - CREATURE:NIGHT_CREATURE_79:LIVER -CREATURE:NIGHT_CREATURE_79:GUT -"CREATURE:NIGHT_CREATURE_79:STOMACH -"CREATURE:NIGHT_CREATURE_79:GIZZARD -#CREATURE:NIGHT_CREATURE_79:PANCREAS -!CREATURE:NIGHT_CREATURE_79:SPLEEN -!CREATURE:NIGHT_CREATURE_79:KIDNEY -!CREATURE:NIGHT_CREATURE_80:MUSCLE -CREATURE:NIGHT_CREATURE_80:EYE - CREATURE:NIGHT_CREATURE_80:BRAIN -CREATURE:NIGHT_CREATURE_80:LUNG - CREATURE:NIGHT_CREATURE_80:HEART - CREATURE:NIGHT_CREATURE_80:LIVER -CREATURE:NIGHT_CREATURE_80:GUT -"CREATURE:NIGHT_CREATURE_80:STOMACH -"CREATURE:NIGHT_CREATURE_80:GIZZARD -#CREATURE:NIGHT_CREATURE_80:PANCREAS -!CREATURE:NIGHT_CREATURE_80:SPLEEN -!CREATURE:NIGHT_CREATURE_80:KIDNEY -!CREATURE:NIGHT_CREATURE_81:MUSCLE -CREATURE:NIGHT_CREATURE_81:EYE - CREATURE:NIGHT_CREATURE_81:BRAIN -CREATURE:NIGHT_CREATURE_81:LUNG - CREATURE:NIGHT_CREATURE_81:HEART - CREATURE:NIGHT_CREATURE_81:LIVER -CREATURE:NIGHT_CREATURE_81:GUT -"CREATURE:NIGHT_CREATURE_81:STOMACH -"CREATURE:NIGHT_CREATURE_81:GIZZARD -#CREATURE:NIGHT_CREATURE_81:PANCREAS -!CREATURE:NIGHT_CREATURE_81:SPLEEN -!CREATURE:NIGHT_CREATURE_81:KIDNEY -!CREATURE:NIGHT_CREATURE_82:MUSCLE -CREATURE:NIGHT_CREATURE_82:EYE - CREATURE:NIGHT_CREATURE_82:BRAIN -CREATURE:NIGHT_CREATURE_82:LUNG - CREATURE:NIGHT_CREATURE_82:HEART - CREATURE:NIGHT_CREATURE_82:LIVER -CREATURE:NIGHT_CREATURE_82:GUT -"CREATURE:NIGHT_CREATURE_82:STOMACH -"CREATURE:NIGHT_CREATURE_82:GIZZARD -#CREATURE:NIGHT_CREATURE_82:PANCREAS -!CREATURE:NIGHT_CREATURE_82:SPLEEN -!CREATURE:NIGHT_CREATURE_82:KIDNEY -!CREATURE:NIGHT_CREATURE_83:MUSCLE -CREATURE:NIGHT_CREATURE_83:EYE - CREATURE:NIGHT_CREATURE_83:BRAIN -CREATURE:NIGHT_CREATURE_83:LUNG - CREATURE:NIGHT_CREATURE_83:HEART - CREATURE:NIGHT_CREATURE_83:LIVER -CREATURE:NIGHT_CREATURE_83:GUT -"CREATURE:NIGHT_CREATURE_83:STOMACH -"CREATURE:NIGHT_CREATURE_83:GIZZARD -#CREATURE:NIGHT_CREATURE_83:PANCREAS -!CREATURE:NIGHT_CREATURE_83:SPLEEN -!CREATURE:NIGHT_CREATURE_83:KIDNEY -!CREATURE:NIGHT_CREATURE_84:MUSCLE -CREATURE:NIGHT_CREATURE_84:EYE - CREATURE:NIGHT_CREATURE_84:BRAIN -CREATURE:NIGHT_CREATURE_84:LUNG - CREATURE:NIGHT_CREATURE_84:HEART - CREATURE:NIGHT_CREATURE_84:LIVER -CREATURE:NIGHT_CREATURE_84:GUT -"CREATURE:NIGHT_CREATURE_84:STOMACH -"CREATURE:NIGHT_CREATURE_84:GIZZARD -#CREATURE:NIGHT_CREATURE_84:PANCREAS -!CREATURE:NIGHT_CREATURE_84:SPLEEN -!CREATURE:NIGHT_CREATURE_84:KIDNEY -!CREATURE:NIGHT_CREATURE_85:MUSCLE -CREATURE:NIGHT_CREATURE_85:EYE - CREATURE:NIGHT_CREATURE_85:BRAIN -CREATURE:NIGHT_CREATURE_85:LUNG - CREATURE:NIGHT_CREATURE_85:HEART - CREATURE:NIGHT_CREATURE_85:LIVER -CREATURE:NIGHT_CREATURE_85:GUT -"CREATURE:NIGHT_CREATURE_85:STOMACH -"CREATURE:NIGHT_CREATURE_85:GIZZARD -#CREATURE:NIGHT_CREATURE_85:PANCREAS -!CREATURE:NIGHT_CREATURE_85:SPLEEN -!CREATURE:NIGHT_CREATURE_85:KIDNEY -!CREATURE:NIGHT_CREATURE_86:MUSCLE -CREATURE:NIGHT_CREATURE_86:EYE - CREATURE:NIGHT_CREATURE_86:BRAIN -CREATURE:NIGHT_CREATURE_86:LUNG - CREATURE:NIGHT_CREATURE_86:HEART - CREATURE:NIGHT_CREATURE_86:LIVER -CREATURE:NIGHT_CREATURE_86:GUT -"CREATURE:NIGHT_CREATURE_86:STOMACH -"CREATURE:NIGHT_CREATURE_86:GIZZARD -#CREATURE:NIGHT_CREATURE_86:PANCREAS -!CREATURE:NIGHT_CREATURE_86:SPLEEN -!CREATURE:NIGHT_CREATURE_86:KIDNEY -!CREATURE:NIGHT_CREATURE_87:MUSCLE -CREATURE:NIGHT_CREATURE_87:EYE - CREATURE:NIGHT_CREATURE_87:BRAIN -CREATURE:NIGHT_CREATURE_87:LUNG - CREATURE:NIGHT_CREATURE_87:HEART - CREATURE:NIGHT_CREATURE_87:LIVER -CREATURE:NIGHT_CREATURE_87:GUT -"CREATURE:NIGHT_CREATURE_87:STOMACH -"CREATURE:NIGHT_CREATURE_87:GIZZARD -#CREATURE:NIGHT_CREATURE_87:PANCREAS -!CREATURE:NIGHT_CREATURE_87:SPLEEN -!CREATURE:NIGHT_CREATURE_87:KIDNEY -!CREATURE:NIGHT_CREATURE_88:MUSCLE -CREATURE:NIGHT_CREATURE_88:EYE - CREATURE:NIGHT_CREATURE_88:BRAIN -CREATURE:NIGHT_CREATURE_88:LUNG - CREATURE:NIGHT_CREATURE_88:HEART - CREATURE:NIGHT_CREATURE_88:LIVER -CREATURE:NIGHT_CREATURE_88:GUT -"CREATURE:NIGHT_CREATURE_88:STOMACH -"CREATURE:NIGHT_CREATURE_88:GIZZARD -#CREATURE:NIGHT_CREATURE_88:PANCREAS -!CREATURE:NIGHT_CREATURE_88:SPLEEN -!CREATURE:NIGHT_CREATURE_88:KIDNEY -!CREATURE:NIGHT_CREATURE_89:MUSCLE -CREATURE:NIGHT_CREATURE_89:EYE - CREATURE:NIGHT_CREATURE_89:BRAIN -CREATURE:NIGHT_CREATURE_89:LUNG - CREATURE:NIGHT_CREATURE_89:HEART - CREATURE:NIGHT_CREATURE_89:LIVER -CREATURE:NIGHT_CREATURE_89:GUT -"CREATURE:NIGHT_CREATURE_89:STOMACH -"CREATURE:NIGHT_CREATURE_89:GIZZARD -#CREATURE:NIGHT_CREATURE_89:PANCREAS -!CREATURE:NIGHT_CREATURE_89:SPLEEN -!CREATURE:NIGHT_CREATURE_89:KIDNEY -!CREATURE:NIGHT_CREATURE_90:MUSCLE -CREATURE:NIGHT_CREATURE_90:EYE - CREATURE:NIGHT_CREATURE_90:BRAIN -CREATURE:NIGHT_CREATURE_90:LUNG - CREATURE:NIGHT_CREATURE_90:HEART - CREATURE:NIGHT_CREATURE_90:LIVER -CREATURE:NIGHT_CREATURE_90:GUT -"CREATURE:NIGHT_CREATURE_90:STOMACH -"CREATURE:NIGHT_CREATURE_90:GIZZARD -#CREATURE:NIGHT_CREATURE_90:PANCREAS -!CREATURE:NIGHT_CREATURE_90:SPLEEN -!CREATURE:NIGHT_CREATURE_90:KIDNEY -!CREATURE:NIGHT_CREATURE_91:MUSCLE -CREATURE:NIGHT_CREATURE_91:EYE - CREATURE:NIGHT_CREATURE_91:BRAIN -CREATURE:NIGHT_CREATURE_91:LUNG - CREATURE:NIGHT_CREATURE_91:HEART - CREATURE:NIGHT_CREATURE_91:LIVER -CREATURE:NIGHT_CREATURE_91:GUT -"CREATURE:NIGHT_CREATURE_91:STOMACH -"CREATURE:NIGHT_CREATURE_91:GIZZARD -#CREATURE:NIGHT_CREATURE_91:PANCREAS -!CREATURE:NIGHT_CREATURE_91:SPLEEN -!CREATURE:NIGHT_CREATURE_91:KIDNEY -!CREATURE:NIGHT_CREATURE_92:MUSCLE -CREATURE:NIGHT_CREATURE_92:EYE - CREATURE:NIGHT_CREATURE_92:BRAIN -CREATURE:NIGHT_CREATURE_92:LUNG - CREATURE:NIGHT_CREATURE_92:HEART - CREATURE:NIGHT_CREATURE_92:LIVER -CREATURE:NIGHT_CREATURE_92:GUT -"CREATURE:NIGHT_CREATURE_92:STOMACH -"CREATURE:NIGHT_CREATURE_92:GIZZARD -#CREATURE:NIGHT_CREATURE_92:PANCREAS -!CREATURE:NIGHT_CREATURE_92:SPLEEN -!CREATURE:NIGHT_CREATURE_92:KIDNEY -!CREATURE:NIGHT_CREATURE_93:MUSCLE -CREATURE:NIGHT_CREATURE_93:EYE - CREATURE:NIGHT_CREATURE_93:BRAIN -CREATURE:NIGHT_CREATURE_93:LUNG - CREATURE:NIGHT_CREATURE_93:HEART - CREATURE:NIGHT_CREATURE_93:LIVER -CREATURE:NIGHT_CREATURE_93:GUT -"CREATURE:NIGHT_CREATURE_93:STOMACH -"CREATURE:NIGHT_CREATURE_93:GIZZARD -#CREATURE:NIGHT_CREATURE_93:PANCREAS -!CREATURE:NIGHT_CREATURE_93:SPLEEN -!CREATURE:NIGHT_CREATURE_93:KIDNEY -!CREATURE:NIGHT_CREATURE_94:MUSCLE -CREATURE:NIGHT_CREATURE_94:EYE - CREATURE:NIGHT_CREATURE_94:BRAIN -CREATURE:NIGHT_CREATURE_94:LUNG - CREATURE:NIGHT_CREATURE_94:HEART - CREATURE:NIGHT_CREATURE_94:LIVER -CREATURE:NIGHT_CREATURE_94:GUT -"CREATURE:NIGHT_CREATURE_94:STOMACH -"CREATURE:NIGHT_CREATURE_94:GIZZARD -#CREATURE:NIGHT_CREATURE_94:PANCREAS -!CREATURE:NIGHT_CREATURE_94:SPLEEN -!CREATURE:NIGHT_CREATURE_94:KIDNEY -!CREATURE:NIGHT_CREATURE_95:MUSCLE -CREATURE:NIGHT_CREATURE_95:EYE - CREATURE:NIGHT_CREATURE_95:BRAIN -CREATURE:NIGHT_CREATURE_95:LUNG - CREATURE:NIGHT_CREATURE_95:HEART - CREATURE:NIGHT_CREATURE_95:LIVER -CREATURE:NIGHT_CREATURE_95:GUT -"CREATURE:NIGHT_CREATURE_95:STOMACH -"CREATURE:NIGHT_CREATURE_95:GIZZARD -#CREATURE:NIGHT_CREATURE_95:PANCREAS -!CREATURE:NIGHT_CREATURE_95:SPLEEN -!CREATURE:NIGHT_CREATURE_95:KIDNEY -!CREATURE:NIGHT_CREATURE_96:MUSCLE -CREATURE:NIGHT_CREATURE_96:EYE - CREATURE:NIGHT_CREATURE_96:BRAIN -CREATURE:NIGHT_CREATURE_96:LUNG - CREATURE:NIGHT_CREATURE_96:HEART - CREATURE:NIGHT_CREATURE_96:LIVER -CREATURE:NIGHT_CREATURE_96:GUT -"CREATURE:NIGHT_CREATURE_96:STOMACH -"CREATURE:NIGHT_CREATURE_96:GIZZARD -#CREATURE:NIGHT_CREATURE_96:PANCREAS -!CREATURE:NIGHT_CREATURE_96:SPLEEN -!CREATURE:NIGHT_CREATURE_96:KIDNEY -!CREATURE:NIGHT_CREATURE_97:MUSCLE -CREATURE:NIGHT_CREATURE_97:EYE - CREATURE:NIGHT_CREATURE_97:BRAIN -CREATURE:NIGHT_CREATURE_97:LUNG - CREATURE:NIGHT_CREATURE_97:HEART - CREATURE:NIGHT_CREATURE_97:LIVER -CREATURE:NIGHT_CREATURE_97:GUT -"CREATURE:NIGHT_CREATURE_97:STOMACH -"CREATURE:NIGHT_CREATURE_97:GIZZARD -#CREATURE:NIGHT_CREATURE_97:PANCREAS -!CREATURE:NIGHT_CREATURE_97:SPLEEN -!CREATURE:NIGHT_CREATURE_97:KIDNEY -!CREATURE:NIGHT_CREATURE_98:MUSCLE -CREATURE:NIGHT_CREATURE_98:EYE - CREATURE:NIGHT_CREATURE_98:BRAIN -CREATURE:NIGHT_CREATURE_98:LUNG - CREATURE:NIGHT_CREATURE_98:HEART - CREATURE:NIGHT_CREATURE_98:LIVER -CREATURE:NIGHT_CREATURE_98:GUT -"CREATURE:NIGHT_CREATURE_98:STOMACH -"CREATURE:NIGHT_CREATURE_98:GIZZARD -#CREATURE:NIGHT_CREATURE_98:PANCREAS -!CREATURE:NIGHT_CREATURE_98:SPLEEN -!CREATURE:NIGHT_CREATURE_98:KIDNEY -!CREATURE:NIGHT_CREATURE_99:MUSCLE -CREATURE:NIGHT_CREATURE_99:EYE - CREATURE:NIGHT_CREATURE_99:BRAIN -CREATURE:NIGHT_CREATURE_99:LUNG - CREATURE:NIGHT_CREATURE_99:HEART - CREATURE:NIGHT_CREATURE_99:LIVER -CREATURE:NIGHT_CREATURE_99:GUT -"CREATURE:NIGHT_CREATURE_99:STOMACH -"CREATURE:NIGHT_CREATURE_99:GIZZARD -#CREATURE:NIGHT_CREATURE_99:PANCREAS -!CREATURE:NIGHT_CREATURE_99:SPLEEN -!CREATURE:NIGHT_CREATURE_99:KIDNEY -"CREATURE:NIGHT_CREATURE_100:MUSCLE -CREATURE:NIGHT_CREATURE_100:EYE -!CREATURE:NIGHT_CREATURE_100:BRAIN - CREATURE:NIGHT_CREATURE_100:LUNG -!CREATURE:NIGHT_CREATURE_100:HEART -!CREATURE:NIGHT_CREATURE_100:LIVER -CREATURE:NIGHT_CREATURE_100:GUT -#CREATURE:NIGHT_CREATURE_100:STOMACH -#CREATURE:NIGHT_CREATURE_100:GIZZARD -$CREATURE:NIGHT_CREATURE_100:PANCREAS -"CREATURE:NIGHT_CREATURE_100:SPLEEN -"CREATURE:NIGHT_CREATURE_100:KIDNEY -"CREATURE:NIGHT_CREATURE_101:MUSCLE -CREATURE:NIGHT_CREATURE_101:EYE -!CREATURE:NIGHT_CREATURE_101:BRAIN - CREATURE:NIGHT_CREATURE_101:LUNG -!CREATURE:NIGHT_CREATURE_101:HEART -!CREATURE:NIGHT_CREATURE_101:LIVER -CREATURE:NIGHT_CREATURE_101:GUT -#CREATURE:NIGHT_CREATURE_101:STOMACH -#CREATURE:NIGHT_CREATURE_101:GIZZARD -$CREATURE:NIGHT_CREATURE_101:PANCREAS -"CREATURE:NIGHT_CREATURE_101:SPLEEN -"CREATURE:NIGHT_CREATURE_101:KIDNEY -"CREATURE:NIGHT_CREATURE_102:MUSCLE -CREATURE:NIGHT_CREATURE_102:EYE -!CREATURE:NIGHT_CREATURE_102:BRAIN - CREATURE:NIGHT_CREATURE_102:LUNG -!CREATURE:NIGHT_CREATURE_102:HEART -!CREATURE:NIGHT_CREATURE_102:LIVER -CREATURE:NIGHT_CREATURE_102:GUT -#CREATURE:NIGHT_CREATURE_102:STOMACH -#CREATURE:NIGHT_CREATURE_102:GIZZARD -$CREATURE:NIGHT_CREATURE_102:PANCREAS -"CREATURE:NIGHT_CREATURE_102:SPLEEN -"CREATURE:NIGHT_CREATURE_102:KIDNEY -"CREATURE:NIGHT_CREATURE_103:MUSCLE -CREATURE:NIGHT_CREATURE_103:EYE -!CREATURE:NIGHT_CREATURE_103:BRAIN - CREATURE:NIGHT_CREATURE_103:LUNG -!CREATURE:NIGHT_CREATURE_103:HEART -!CREATURE:NIGHT_CREATURE_103:LIVER -CREATURE:NIGHT_CREATURE_103:GUT -#CREATURE:NIGHT_CREATURE_103:STOMACH -#CREATURE:NIGHT_CREATURE_103:GIZZARD -$CREATURE:NIGHT_CREATURE_103:PANCREAS -"CREATURE:NIGHT_CREATURE_103:SPLEEN -"CREATURE:NIGHT_CREATURE_103:KIDNEY -"CREATURE:NIGHT_CREATURE_104:MUSCLE -CREATURE:NIGHT_CREATURE_104:EYE -!CREATURE:NIGHT_CREATURE_104:BRAIN - CREATURE:NIGHT_CREATURE_104:LUNG -!CREATURE:NIGHT_CREATURE_104:HEART -!CREATURE:NIGHT_CREATURE_104:LIVER -CREATURE:NIGHT_CREATURE_104:GUT -#CREATURE:NIGHT_CREATURE_104:STOMACH -#CREATURE:NIGHT_CREATURE_104:GIZZARD -$CREATURE:NIGHT_CREATURE_104:PANCREAS -"CREATURE:NIGHT_CREATURE_104:SPLEEN -"CREATURE:NIGHT_CREATURE_104:KIDNEY -CREATURE:HF1248 DIVINE_1:MUSCLE -CREATURE:HF1248 DIVINE_1:EYE -CREATURE:HF1248 DIVINE_1:BRAIN -CREATURE:HF1248 DIVINE_1:LUNG -CREATURE:HF1248 DIVINE_1:HEART -CREATURE:HF1248 DIVINE_1:LIVER -CREATURE:HF1248 DIVINE_1:GUT - CREATURE:HF1248 DIVINE_1:STOMACH - CREATURE:HF1248 DIVINE_1:GIZZARD -!CREATURE:HF1248 DIVINE_1:PANCREAS -CREATURE:HF1248 DIVINE_1:SPLEEN -CREATURE:HF1248 DIVINE_1:KIDNEY -CREATURE:HF1248 DIVINE_2:MUSCLE -CREATURE:HF1248 DIVINE_2:EYE -CREATURE:HF1248 DIVINE_2:BRAIN -CREATURE:HF1248 DIVINE_2:LUNG -CREATURE:HF1248 DIVINE_2:HEART -CREATURE:HF1248 DIVINE_2:LIVER -CREATURE:HF1248 DIVINE_2:GUT - CREATURE:HF1248 DIVINE_2:STOMACH - CREATURE:HF1248 DIVINE_2:GIZZARD -!CREATURE:HF1248 DIVINE_2:PANCREAS -CREATURE:HF1248 DIVINE_2:SPLEEN -CREATURE:HF1248 DIVINE_2:KIDNEY -CREATURE:HF1248 DIVINE_3:MUSCLE -CREATURE:HF1248 DIVINE_3:EYE -CREATURE:HF1248 DIVINE_3:BRAIN -CREATURE:HF1248 DIVINE_3:LUNG -CREATURE:HF1248 DIVINE_3:HEART -CREATURE:HF1248 DIVINE_3:LIVER -CREATURE:HF1248 DIVINE_3:GUT - CREATURE:HF1248 DIVINE_3:STOMACH - CREATURE:HF1248 DIVINE_3:GIZZARD -!CREATURE:HF1248 DIVINE_3:PANCREAS -CREATURE:HF1248 DIVINE_3:SPLEEN -CREATURE:HF1248 DIVINE_3:KIDNEY -CREATURE:HF1108 DIVINE_2:MUSCLE -CREATURE:HF1108 DIVINE_2:EYE -CREATURE:HF1108 DIVINE_2:BRAIN -CREATURE:HF1108 DIVINE_2:LUNG -CREATURE:HF1108 DIVINE_2:HEART -CREATURE:HF1108 DIVINE_2:LIVER -CREATURE:HF1108 DIVINE_2:GUT - CREATURE:HF1108 DIVINE_2:STOMACH - CREATURE:HF1108 DIVINE_2:GIZZARD -!CREATURE:HF1108 DIVINE_2:PANCREAS -CREATURE:HF1108 DIVINE_2:SPLEEN -CREATURE:HF1108 DIVINE_2:KIDNEY -CREATURE:HF1249 DIVINE_1:MUSCLE -CREATURE:HF1249 DIVINE_1:EYE -CREATURE:HF1249 DIVINE_1:BRAIN -CREATURE:HF1249 DIVINE_1:LUNG -CREATURE:HF1249 DIVINE_1:HEART -CREATURE:HF1249 DIVINE_1:LIVER -CREATURE:HF1249 DIVINE_1:GUT - CREATURE:HF1249 DIVINE_1:STOMACH - CREATURE:HF1249 DIVINE_1:GIZZARD -!CREATURE:HF1249 DIVINE_1:PANCREAS -CREATURE:HF1249 DIVINE_1:SPLEEN -CREATURE:HF1249 DIVINE_1:KIDNEY -CREATURE:HF1249 DIVINE_3:MUSCLE -CREATURE:HF1249 DIVINE_3:EYE -CREATURE:HF1249 DIVINE_3:BRAIN -CREATURE:HF1249 DIVINE_3:LUNG -CREATURE:HF1249 DIVINE_3:HEART -CREATURE:HF1249 DIVINE_3:LIVER -CREATURE:HF1249 DIVINE_3:GUT - CREATURE:HF1249 DIVINE_3:STOMACH - CREATURE:HF1249 DIVINE_3:GIZZARD -!CREATURE:HF1249 DIVINE_3:PANCREAS -CREATURE:HF1249 DIVINE_3:SPLEEN -CREATURE:HF1249 DIVINE_3:KIDNEY -CREATURE:HF1345 DIVINE_3:MUSCLE -CREATURE:HF1345 DIVINE_3:EYE -CREATURE:HF1345 DIVINE_3:BRAIN -CREATURE:HF1345 DIVINE_3:LUNG -CREATURE:HF1345 DIVINE_3:HEART -CREATURE:HF1345 DIVINE_3:LIVER -CREATURE:HF1345 DIVINE_3:GUT - CREATURE:HF1345 DIVINE_3:STOMACH - CREATURE:HF1345 DIVINE_3:GIZZARD -!CREATURE:HF1345 DIVINE_3:PANCREAS -CREATURE:HF1345 DIVINE_3:SPLEEN -CREATURE:HF1345 DIVINE_3:KIDNEYCUTTLEFISH:FEMALECUTTLEFISH:MALENAUTILUS:FEMALE NAUTILUS:MALEMOGHOPPER:FEMALEMOGHOPPER:MALEPOND_TURTLE:FEMALEPOND_TURTLE:MALEMUSSEL:DEFAULTOYSTER:DEFAULTFISH_SALMON:FEMALEFISH_SALMON:MALEFISH_CLOWNFISH:FEMALEFISH_CLOWNFISH:MALEFISH_HAGFISH:FEMALEFISH_HAGFISH:MALEFISH_LAMPREY_BROOK:FEMALEFISH_LAMPREY_BROOK:MALEFISH_RAY_BAT:FEMALEFISH_RAY_BAT:MALEFISH_RAY_THORNBACK:FEMALEFISH_RAY_THORNBACK:MALEFISH_RATFISH_SPOTTED:FEMALEFISH_RATFISH_SPOTTED:MALEFISH_HERRING:FEMALEFISH_HERRING:MALEFISH_SHAD:FEMALEFISH_SHAD:MALEFISH_ANCHOVY:FEMALEFISH_ANCHOVY:MALEFISH_TROUT_STEELHEAD:FEMALEFISH_TROUT_STEELHEAD:MALEFISH_HAKE:FEMALEFISH_HAKE:MALEFISH_SEAHORSE:FEMALEFISH_SEAHORSE:MALEFISH_GLASSEYE:FEMALEFISH_GLASSEYE:MALE FISH_PUFFER_WHITE_SPOTTED:FEMALEFISH_PUFFER_WHITE_SPOTTED:MALEFISH_SOLE:FEMALEFISH_SOLE:MALEFISH_FLOUNDER:FEMALEFISH_FLOUNDER:MALEFISH_MACKEREL:FEMALEFISH_MACKEREL:MALEJELLYFISH_SEA_NETTLE:DEFAULT SQUID:FEMALE -SQUID:MALEFISH_LUNGFISH:FEMALEFISH_LUNGFISH:MALEFISH_LOACH_CLOWN:FEMALEFISH_LOACH_CLOWN:MALEFISH_BULLHEAD_BROWN:FEMALEFISH_BULLHEAD_BROWN:MALEFISH_BULLHEAD_YELLOW:FEMALEFISH_BULLHEAD_YELLOW:MALEFISH_BULLHEAD_BLACK:FEMALEFISH_BULLHEAD_BLACK:MALEFISH_KNIFEFISH_BANDED:FEMALEFISH_KNIFEFISH_BANDED:MALEFISH_CHAR:FEMALEFISH_CHAR:MALEFISH_TROUT_RAINBOW:FEMALEFISH_TROUT_RAINBOW:MALEFISH_MOLLY_SAILFIN:FEMALEFISH_MOLLY_SAILFIN:MALEFISH_GUPPY:FEMALEFISH_GUPPY:MALEFISH_PERCH:FEMALEFISH_PERCH:MALEFISH_CAVE:FEMALEFISH_CAVE:MALELOBSTER_CAVE:FEMALELOBSTER_CAVE:MALEBIRD_BLUEJAY:FEMALEBLUEJAY_MAN:FEMALEGIANT_BLUEJAY:FEMALEBIRD_CARDINAL:FEMALECARDINAL_MAN:FEMALEGIANT_CARDINAL:FEMALEBIRD_GRACKLE:FEMALEGRACKLE_MAN:FEMALEGIANT_GRACKLE:FEMALEBIRD_ORIOLE:FEMALEORIOLE_MAN:FEMALEGIANT_ORIOLE:FEMALEBIRD_RW_BLACKBIRD:FEMALERW_BLACKBIRD_MAN:FEMALEGIANT_RW_BLACKBIRD:FEMALEBIRD_PENGUIN:FEMALEBIRD_PENGUIN_LITTLE:FEMALEBIRD_PENGUIN_EMPEROR:FEMALEPENGUIN MAN:FEMALEBIRD_PENGUIN_GIANT:FEMALEBIRD_FALCON_PEREGRINE:FEMALEPEREGRINE FALCON MAN:FEMALEGIANT PEREGRINE FALCON:FEMALEBIRD_KIWI:FEMALEKIWI MAN:FEMALEBIRD_KIWI_GIANT:FEMALEBIRD_OSTRICH:FEMALEOSTRICH MAN:FEMALEBIRD_OSTRICH_GIANT:FEMALEBIRD_CROW:FEMALECROW_MAN:FEMALEGIANT_CROW:FEMALEBIRD_RAVEN:FEMALERAVEN_MAN:FEMALEGIANT_RAVEN:FEMALEBIRD_CASSOWARY:FEMALECASSOWARY_MAN:FEMALEGIANT_CASSOWARY:FEMALEBIRD_KEA:FEMALEKEA_MAN:FEMALEGIANT_KEA:FEMALEBIRD_OWL_SNOWY:FEMALESNOWY_OWL_MAN:FEMALEGIANT_SNOWY_OWL:FEMALESPARROW:FEMALESPARROW_MAN:FEMALEGIANT_SPARROW:FEMALEBIRD_STORK_WHITE:FEMALEWHITE_STORK_MAN:FEMALEGIANT_WHITE_STORK:FEMALEBIRD_LOON:FEMALELOON_MAN:FEMALEGIANT_LOON:FEMALEBIRD_OWL_BARN:FEMALEBARN_OWL_MAN:FEMALEGIANT_BARN_OWL:FEMALEBIRD_PARAKEET:FEMALEPARAKEET_MAN:FEMALEGIANT_PARAKEET:FEMALEBIRD_KAKAPO:FEMALEKAKAPO_MAN:FEMALEGIANT_KAKAPO:FEMALEBIRD_PARROT_GREY:FEMALEGREY_PARROT_MAN:FEMALEGIANT_GREY_PARROT:FEMALEBIRD_PUFFIN:FEMALEPUFFIN_MAN:FEMALEGIANT_PUFFIN:FEMALEBIRD_SWAN:FEMALESWAN_MAN:FEMALEGIANT_SWAN:FEMALEBIRD_LORIKEET:FEMALELORIKEET_MAN:FEMALEGIANT_LORIKEET:FEMALEBIRD_WREN:FEMALEWREN_MAN:FEMALEGIANT_WREN:FEMALEBIRD_OSPREY:FEMALEOSPREY_MAN:FEMALEGIANT_OSPREY:FEMALEBIRD_EMU:FEMALEEMU_MAN:FEMALEGIANT_EMU:FEMALEBIRD_COCKATIEL:FEMALECOCKATIEL_MAN:FEMALEGIANT_COCKATIEL:FEMALE BIRD_LOVEBIRD_PEACH-FACED:FEMALEPEACH-FACED_LOVEBIRD_MAN:FEMALE!GIANT_PEACH-FACED_LOVEBIRD:FEMALEBIRD_MAGPIE:FEMALEMAGPIE_MAN:FEMALEGIANT_MAGPIE:FEMALEBIRD_KESTREL:FEMALEKESTREL_MAN:FEMALEGIANT_KESTREL:FEMALEBIRD_ALBATROSS:FEMALEALBATROSS_MAN:FEMALEGIANT_ALBATROSS:FEMALEBIRD_OWL_GREAT_HORNED:FEMALEGREAT_HORNED_OWL_MAN:FEMALEGIANT_GREAT_HORNED_OWL:FEMALEBIRD_EAGLE:FEMALEEAGLE_MAN:FEMALEGIANT_EAGLE:FEMALEBIRD_HORNBILL:FEMALEHORNBILL_MAN:FEMALEGIANT_HORNBILL:FEMALEBIRD_LOVEBIRD_MASKED:FEMALEMASKED_LOVEBIRD_MAN:FEMALEGIANT_MASKED_LOVEBIRD:FEMALEBIRD_BUSHTIT:FEMALEBUSHTIT_MAN:FEMALEGIANT_BUSHTIT:FEMALEDESERT TORTOISE:FEMALEDESERT_TORTOISE_MAN:FEMALEGIANT_DESERT_TORTOISE:FEMALEGILA_MONSTER:FEMALEGILA_MONSTER_MAN:FEMALEGIANT_GILA_MONSTER:FEMALEBIRD_CHICKEN:FEMALEBIRD_DUCK:FEMALEBIRD_GOOSE:FEMALEBIRD_GUINEAFOWL:FEMALEBIRD_PEAFOWL_BLUE:FEMALEBIRD_TURKEY:FEMALEPLATYPUS:FEMALEPLATYPUS MAN:FEMALEPLATYPUS, GIANT:FEMALEALLIGATOR:FEMALEALLIGATOR_MAN:FEMALEGIANT_ALLIGATOR:FEMALEBIRD_BUZZARD:FEMALEBUZZARD_MAN:FEMALEGIANT_BUZZARD:FEMALECROCODILE_SALTWATER:FEMALECROCODILE_SALTWATER_MAN:FEMALE GIANT_CROCODILE_SALTWATER:FEMALEBIRD_VULTURE:FEMALEVULTURE_MAN:FEMALEGIANT_VULTURE:FEMALEGIANT TORTOISE:FEMALEGIANT TORTOISE MAN:FEMALEGIGANTIC TORTOISE:FEMALECRUNDLE:FEMALEELK_BIRD:FEMALEHELMET_SNAKE:FEMALEJABBERER:FEMALECAVE_DRAGON:FEMALELIZARD_RHINO_TWO_LEGGED:FEMALE SKINK:FEMALESKINK_MAN:FEMALEGIANT_SKINK:FEMALECHAMELEON:FEMALECHAMELEON_MAN:FEMALEGIANT_CHAMELEON:FEMALE ANOLE:FEMALEANOLE_MAN:FEMALEGIANT_ANOLE:FEMALE IGUANA:FEMALEIGUANA_MAN:FEMALEGIANT_IGUANA:FEMALESNAPPING TURTLE:FEMALE ALLIGATOR SNAPPING TURTLE:FEMALESNAPPING_TURTLE_MAN:FEMALEGIANT_SNAPPING_TURTLE:FEMALEPOND_TURTLE:FEMALEPOND_TURTLE_MAN:FEMALEGIANT_POND_TURTLE:FEMALE KOBOLD:FEMALE DRAGON:FEMALEBEAK_DOG:FEMALESEA_SERPENT:FEMALEBIRD_ROC:FEMALECROCODILE_CAVE:FEMALEBIRD_SWALLOW_CAVE:FEMALECAVE_SWALLOW_MAN:FEMALEBIRD_SWALLOW_CAVE_GIANT:FEMALEREPTILE_MAN:FEMALESERPENT_MAN:FEMALE ADDER:FEMALEADDER_MAN:FEMALEGIANT_ADDER:FEMALEECHIDNA:FEMALEECHIDNA_MAN:FEMALEGIANT_ECHIDNA:FEMALEKINGSNAKE:FEMALEKINGSNAKE_MAN:FEMALEGIANT_KINGSNAKE:FEMALEMONITOR_LIZARD:FEMALEMONITOR_LIZARD_MAN:FEMALEGIANT_MONITOR_LIZARD:FEMALEKING_COBRA:FEMALEKING_COBRA_MAN:FEMALEGIANT_KING_COBRA:FEMALEBLACK_MAMBA:FEMALEBLACK_MAMBA_MAN:FEMALEGIANT_BLACK_MAMBA:FEMALEBUSHMASTER:FEMALEBUSHMASTER_MAN:FEMALEGIANT_BUSHMASTER:FEMALE PYTHON:FEMALEPYTHON_MAN:FEMALEGIANT_PYTHON:FEMALENAGA:FEMALE_TWONAGA:FEMALE_FOURNAGA:FEMALE_SIX"#PLANT:SINGLE-GRAIN_WHEAT:STRUCTURAL" PLANT:TWO-GRAIN_WHEAT:STRUCTURAL"PLANT:SOFT_WHEAT:STRUCTURAL"PLANT:HARD_WHEAT:STRUCTURAL"PLANT:SPELT:STRUCTURAL"PLANT:BARLEY:STRUCTURAL"PLANT:BUCKWHEAT:STRUCTURAL"PLANT:OATS:STRUCTURAL"PLANT:ALFALFA:STRUCTURAL"PLANT:RYE:STRUCTURAL"PLANT:SORGHUM:STRUCTURAL"PLANT:RICE:STRUCTURAL"PLANT:MAIZE:STRUCTURAL"PLANT:QUINOA:STRUCTURAL"PLANT:KANIWA:STRUCTURAL"PLANT:BITTER_VETCH:STRUCTURAL"!PLANT:PENDANT_AMARANTH:STRUCTURAL"PLANT:BLOOD_AMARANTH:STRUCTURAL" PLANT:PURPLE_AMARANTH:STRUCTURAL"PLANT:RED_SPINACH:STRUCTURAL"'PLANT:ELEPHANT-HEAD_AMARANTH:STRUCTURAL"PLANT:PEARL_MILLET:STRUCTURAL"PLANT:WHITE_MILLET:STRUCTURAL"PLANT:FINGER_MILLET:STRUCTURAL"PLANT:FOXTAIL_MILLET:STRUCTURAL"PLANT:FONIO:STRUCTURAL"PLANT:TEFF:STRUCTURAL"PLANT:FLAX:STRUCTURAL"PLANT:JUTE:STRUCTURAL"PLANT:HEMP:STRUCTURAL"PLANT:COTTON:STRUCTURAL"PLANT:RAMIE:STRUCTURAL"PLANT:KENAF:STRUCTURAL"PLANT:PAPYRUS_SEDGE:STRUCTURAL"PLANT:ARTICHOKE:STRUCTURAL"PLANT:ASPARAGUS:STRUCTURAL""PLANT:BAMBARA_GROUNDNUT:STRUCTURAL"PLANT:STRING_BEAN:STRUCTURAL"PLANT:BROAD_BEAN:STRUCTURAL"PLANT:BEET:STRUCTURAL"PLANT:BITTER_MELON:STRUCTURAL"PLANT:CABBAGE:STRUCTURAL"PLANT:CAPER:STRUCTURAL"PLANT:WILD_CARROT:STRUCTURAL"PLANT:CASSAVA:STRUCTURAL"PLANT:CELERY:STRUCTURAL"PLANT:CHICKPEA:STRUCTURAL"PLANT:CHICORY:STRUCTURAL"PLANT:COWPEA:STRUCTURAL"PLANT:CUCUMBER:STRUCTURAL"PLANT:EGGPLANT:STRUCTURAL"PLANT:GARDEN_CRESS:STRUCTURAL"PLANT:GARLIC:STRUCTURAL"PLANT:HORNED_MELON:STRUCTURAL"PLANT:LEEK:STRUCTURAL"PLANT:LENTIL:STRUCTURAL"PLANT:LETTUCE:STRUCTURAL"PLANT:MUNG_BEAN:STRUCTURAL"PLANT:MUSKMELON:STRUCTURAL"PLANT:ONION:STRUCTURAL"PLANT:PARSNIP:STRUCTURAL"PLANT:PEA:STRUCTURAL"PLANT:PEANUT:STRUCTURAL"PLANT:PEPPER:STRUCTURAL"PLANT:POTATO:STRUCTURAL"PLANT:RADISH:STRUCTURAL"PLANT:RED_BEAN:STRUCTURAL"PLANT:RHUBARB:STRUCTURAL"PLANT:SOYBEAN:STRUCTURAL"PLANT:SPINACH:STRUCTURAL"PLANT:SQUASH:STRUCTURAL"PLANT:SWEET_POTATO:STRUCTURAL"PLANT:TARO:STRUCTURAL"PLANT:TOMATO:STRUCTURAL"PLANT:TOMATILLO:STRUCTURAL"PLANT:TURNIP:STRUCTURAL"PLANT:URAD_BEAN:STRUCTURAL"PLANT:WATERMELON:STRUCTURAL"PLANT:WINTER_MELON:STRUCTURAL"PLANT:LESSER_YAM:STRUCTURAL"PLANT:LONG_YAM:STRUCTURAL"PLANT:PURPLE_YAM:STRUCTURAL"PLANT:WHITE_YAM:STRUCTURAL"PLANT:PASSION_FRUIT:STRUCTURAL"PLANT:GRAPE:STRUCTURAL"PLANT:CRANBERRY:STRUCTURAL"PLANT:BILBERRY:STRUCTURAL"PLANT:BLUEBERRY:STRUCTURAL"PLANT:BLACKBERRY:STRUCTURAL"PLANT:RASPBERRY:STRUCTURAL"PLANT:PINEAPPLE:STRUCTURAL"PLANT:MEADOW-GRASS:STRUCTURAL"PLANT:HAIR GRASS:STRUCTURAL"PLANT:BENTGRASS:STRUCTURAL"PLANT:RYEGRASS:STRUCTURAL"PLANT:FESCUE GRASS:STRUCTURAL"PLANT:REEDGRASS:STRUCTURAL"PLANT:KNOTGRASS:STRUCTURAL"PLANT:ZOYSIA:STRUCTURAL""PLANT:DOG'S TOOTH GRASS:STRUCTURAL"PLANT:DALLISGRASS:STRUCTURAL"PLANT:CARPETGRASS:STRUCTURAL"PLANT:SATINTAIL:STRUCTURAL"PLANT:GRAMA:STRUCTURAL"PLANT:DROPSEED GRASS:STRUCTURAL"PLANT:NEEDLE GRASS:STRUCTURAL"$PLANT:BABY TOES SUCCULENT:STRUCTURAL"PLANT:PEBBLE PLANTS:STRUCTURAL"PLANT:BLUE SEDGE:STRUCTURAL"PLANT:FIELD SEDGE:STRUCTURAL""PLANT:PURPLE MOOR GRASS:STRUCTURAL"PLANT:VELVET GRASS:STRUCTURAL"PLANT:MEADOWSWEET:STRUCTURAL"PLANT:RUSH:STRUCTURAL"PLANT:MARSH THISTLE:STRUCTURAL"PLANT:COMMON REED:STRUCTURAL"PLANT:CATTAIL:STRUCTURAL"PLANT:SAWGRASS:STRUCTURAL"PLANT:COTTONGRASS:STRUCTURAL"'PLANT:WHITE MOUNTAIN HEATHER:STRUCTURAL"PLANT:MOUNTAIN AVENS:STRUCTURAL"PLANT:CLOUDBERRY:STRUCTURAL"PLANT:WORMY TENDRILS:STRUCTURAL"PLANT:EYEBALL:STRUCTURAL"PLANT:BUBBLE BULBS:STRUCTURAL"PLANT:DOWNY GRASS:STRUCTURAL"PLANT:CAVE MOSS:STRUCTURAL"PLANT:FLOOR FUNGI:STRUCTURAL"PLANT:UNDERLICHEN:STRUCTURAL"PLANT:BAMBOO, ARROW:STRUCTURAL"PLANT:BAMBOO, GOLDEN:STRUCTURAL"PLANT:BAMBOO, HEDGE:STRUCTURAL"PLANT:ABACA:STRUCTURAL"PLANT:BANANA:STRUCTURAL"PLANT:CARAMBOLA:STRUCTURAL"PLANT:CASHEW:STRUCTURAL"PLANT:COFFEE:STRUCTURAL"PLANT:DURIAN:STRUCTURAL"PLANT:GUAVA:STRUCTURAL"PLANT:PAPAYA:STRUCTURAL"PLANT:PARADISE_NUT:STRUCTURAL"PLANT:RAMBUTAN:STRUCTURAL"PLANT:TEA:STRUCTURAL"PLANT:AVOCADO:STRUCTURAL"PLANT:LIME:STRUCTURAL"PLANT:POMELO:STRUCTURAL"PLANT:CITRON:STRUCTURAL"PLANT:ORANGE:STRUCTURAL"PLANT:BITTER_ORANGE:STRUCTURAL"PLANT:FINGER_LIME:STRUCTURAL"PLANT:ROUND_LIME:STRUCTURAL"PLANT:DESERT_LIME:STRUCTURAL"PLANT:KUMQUAT:STRUCTURAL"PLANT:CUSTARD-APPLE:STRUCTURAL"PLANT:DATE_PALM:STRUCTURAL"PLANT:LYCHEE:STRUCTURAL"PLANT:MACADAMIA:STRUCTURAL"PLANT:OLIVE:STRUCTURAL"PLANT:POMEGRANATE:STRUCTURAL"PLANT:ALMOND:STRUCTURAL"PLANT:APPLE:STRUCTURAL"PLANT:APRICOT:STRUCTURAL"PLANT:BAYBERRY:STRUCTURAL"PLANT:CHERRY:STRUCTURAL"PLANT:GINKGO:STRUCTURAL"PLANT:HAZEL:STRUCTURAL"PLANT:PEACH:STRUCTURAL"PLANT:PEAR:STRUCTURAL"PLANT:PECAN:STRUCTURAL"PLANT:PERSIMMON:STRUCTURAL"PLANT:PLUM:STRUCTURAL"PLANT:SAND_PEAR:STRUCTURAL"PLANT:WALNUT:STRUCTURAL"&PLANT:MUSHROOM_HELMET_PLUMP:STRUCTURAL"PLANT:GRASS_TAIL_PIG:STRUCTURAL"!PLANT:GRASS_WHEAT_CAVE:STRUCTURAL"PLANT:POD_SWEET:STRUCTURAL"PLANT:BUSH_QUARRY:STRUCTURAL"PLANT:ROOT_MUCK:STRUCTURAL"PLANT:TUBER_BLOATED:STRUCTURAL"PLANT:BULB_KOBOLD:STRUCTURAL" PLANT:BERRIES_PRICKLE:STRUCTURAL"PLANT:BERRIES_STRAW:STRUCTURAL"PLANT:GRASS_LONGLAND:STRUCTURAL"PLANT:HERB_VALLEY:STRUCTURAL"PLANT:WEED_RAT:STRUCTURAL"PLANT:BERRIES_FISHER:STRUCTURAL"PLANT:REED_ROPE:STRUCTURAL"$PLANT:MUSHROOM_CUP_DIMPLE:STRUCTURAL"PLANT:WEED_BLADE:STRUCTURAL"PLANT:ROOT_HIDE:STRUCTURAL"PLANT:SLIVER_BARB:STRUCTURAL"PLANT:BERRY_SUN:STRUCTURAL"PLANT:VINE_WHIP:STRUCTURAL"PLANT:MANGROVE:STRUCTURAL"PLANT:SAGUARO:STRUCTURAL"PLANT:PINE:STRUCTURAL"PLANT:CEDAR:STRUCTURAL"PLANT:OAK:STRUCTURAL"PLANT:MAHOGANY:STRUCTURAL"PLANT:ACACIA:STRUCTURAL"PLANT:KAPOK:STRUCTURAL"PLANT:MAPLE:STRUCTURAL"PLANT:WILLOW:STRUCTURAL"PLANT:TOWER_CAP:STRUCTURAL"PLANT:BLACK_CAP:STRUCTURAL"PLANT:NETHER_CAP:STRUCTURAL"PLANT:GOBLIN_CAP:STRUCTURAL"PLANT:FUNGIWOOD:STRUCTURAL"PLANT:TUNNEL_TUBE:STRUCTURAL"PLANT:SPORE_TREE:STRUCTURAL"PLANT:BLOOD_THORN:STRUCTURAL"PLANT:GLUMPRONG:STRUCTURAL"PLANT:FEATHER:STRUCTURAL"PLANT:HIGHWOOD:STRUCTURAL"PLANT:LARCH:STRUCTURAL"PLANT:CHESTNUT:STRUCTURAL"PLANT:ALDER:STRUCTURAL"PLANT:BIRCH:STRUCTURAL"PLANT:ASH:STRUCTURAL"PLANT:CANDLENUT:STRUCTURAL"PLANT:MANGO:STRUCTURAL"PLANT:RUBBER:STRUCTURAL"PLANT:CACAO:STRUCTURAL"PLANT:PALM:STRUCTURAL*PLANT:SINGLE-GRAIN_WHEAT:DRINK*PLANT:TWO-GRAIN_WHEAT:DRINK*PLANT:SOFT_WHEAT:DRINK*PLANT:HARD_WHEAT:DRINK*PLANT:SPELT:DRINK*PLANT:BARLEY:DRINK*PLANT:BUCKWHEAT:DRINK*PLANT:RYE:DRINK*PLANT:SORGHUM:DRINK*PLANT:RICE:DRINK*PLANT:MAIZE:DRINK*PLANT:QUINOA:DRINK*PLANT:KANIWA:DRINK*PLANT:PENDANT_AMARANTH:DRINK*PLANT:BLOOD_AMARANTH:DRINK*PLANT:PURPLE_AMARANTH:DRINK*PLANT:PEARL_MILLET:DRINK*PLANT:WHITE_MILLET:DRINK*PLANT:FINGER_MILLET:DRINK*PLANT:FOXTAIL_MILLET:DRINK*PLANT:FONIO:DRINK*PLANT:TEFF:DRINK*PLANT:ARTICHOKE:DRINK*PLANT:BEET:DRINK*PLANT:WILD_CARROT:DRINK*PLANT:CASSAVA:DRINK*PLANT:PARSNIP:DRINK*PLANT:POTATO:DRINK*PLANT:RADISH:DRINK*PLANT:SWEET_POTATO:DRINK*PLANT:TOMATO:DRINK*PLANT:TOMATILLO:DRINK*PLANT:TURNIP:DRINK*PLANT:PASSION_FRUIT:DRINK*PLANT:GRAPE:DRINK*PLANT:CRANBERRY:DRINK*PLANT:BILBERRY:DRINK*PLANT:BLUEBERRY:DRINK*PLANT:BLACKBERRY:DRINK*PLANT:RASPBERRY:DRINK*PLANT:PINEAPPLE:DRINK*PLANT:BANANA:DRINK*PLANT:CARAMBOLA:DRINK*PLANT:DURIAN:DRINK*PLANT:GUAVA:DRINK*PLANT:PAPAYA:DRINK*PLANT:RAMBUTAN:DRINK*PLANT:CUSTARD-APPLE:DRINK*PLANT:DATE_PALM:DRINK*PLANT:LYCHEE:DRINK*PLANT:POMEGRANATE:DRINK*PLANT:APPLE:DRINK*PLANT:APRICOT:DRINK*PLANT:BAYBERRY:DRINK*PLANT:CHERRY:DRINK*PLANT:PEACH:DRINK*PLANT:PEAR:DRINK*PLANT:PERSIMMON:DRINK*PLANT:PLUM:DRINK*PLANT:SAND_PEAR:DRINK*!PLANT:MUSHROOM_HELMET_PLUMP:DRINK*PLANT:GRASS_TAIL_PIG:DRINK*PLANT:GRASS_WHEAT_CAVE:DRINK*PLANT:POD_SWEET:DRINK*PLANT:ROOT_MUCK:DRINK*PLANT:TUBER_BLOATED:DRINK*PLANT:BERRIES_PRICKLE:DRINK*PLANT:BERRIES_STRAW:DRINK*PLANT:GRASS_LONGLAND:DRINK*PLANT:WEED_RAT:DRINK*PLANT:BERRIES_FISHER:DRINK*PLANT:REED_ROPE:DRINK*PLANT:SLIVER_BARB:DRINK*PLANT:BERRY_SUN:DRINK*PLANT:VINE_WHIP:DRINK*PLANT:MANGO:DRINK2CREATURE:HONEY_BEE:MEAD2CREATURE:BUMBLEBEE:MEADBCREATURE:DONKEY:CHEESEBCREATURE:HORSE:CHEESEBCREATURE:COW:CHEESEBCREATURE:SHEEP:CHEESEBCREATURE:PIG:CHEESEBCREATURE:GOAT:CHEESEBCREATURE:WATER_BUFFALO:CHEESEBCREATURE:REINDEER:CHEESEBCREATURE:YAK:CHEESEBCREATURE:LLAMA:CHEESEBCREATURE:ALPACA:CHEESEBCREATURE:CAMEL_1_HUMP:CHEESEB CREATURE:CAMEL_1_HUMP_MAN:CHEESEB"CREATURE:GIANT_CAMEL_1_HUMP:CHEESEBCREATURE:CAMEL_2_HUMP:CHEESEB CREATURE:CAMEL_2_HUMP_MAN:CHEESEB"CREATURE:GIANT_CAMEL_2_HUMP:CHEESEBCREATURE:MAGGOT_PURRING:CHEESEBCREATURE:KANGAROO:CHEESEBCREATURE:KANGAROO_MAN:CHEESEBCREATURE:GIANT_KANGAROO:CHEESEBCREATURE:TAPIR:CHEESEBCREATURE:TAPIR_MAN:CHEESEBCREATURE:GIANT_TAPIR:CHEESEJPLANT:SINGLE-GRAIN_WHEAT:SEEDJPLANT:TWO-GRAIN_WHEAT:SEEDJPLANT:SOFT_WHEAT:SEEDJPLANT:HARD_WHEAT:SEEDJPLANT:SPELT:SEEDJPLANT:BARLEY:SEEDJPLANT:BUCKWHEAT:SEEDJPLANT:OATS:SEEDJPLANT:ALFALFA:SEEDJPLANT:RYE:SEEDJPLANT:SORGHUM:SEEDJPLANT:RICE:SEEDJPLANT:MAIZE:SEEDJPLANT:QUINOA:SEEDJPLANT:KANIWA:SEEDJPLANT:BITTER_VETCH:SEEDJPLANT:PENDANT_AMARANTH:SEEDJPLANT:BLOOD_AMARANTH:SEEDJPLANT:PURPLE_AMARANTH:SEEDJPLANT:RED_SPINACH:SEEDJ!PLANT:ELEPHANT-HEAD_AMARANTH:SEEDJPLANT:PEARL_MILLET:SEEDJPLANT:WHITE_MILLET:SEEDJPLANT:FINGER_MILLET:SEEDJPLANT:FOXTAIL_MILLET:SEEDJPLANT:FONIO:SEEDJPLANT:TEFF:SEEDJPLANT:FLAX:SEEDJPLANT:JUTE:SEEDJPLANT:HEMP:SEEDJPLANT:COTTON:SEEDJPLANT:RAMIE:SEEDJPLANT:KENAF:SEEDJPLANT:PAPYRUS_SEDGE:SEEDJPLANT:ARTICHOKE:SEEDJPLANT:ASPARAGUS:SEEDJPLANT:BAMBARA_GROUNDNUT:SEEDJPLANT:STRING_BEAN:SEEDJPLANT:BROAD_BEAN:SEEDJPLANT:BEET:SEEDJPLANT:BITTER_MELON:SEEDJPLANT:CABBAGE:SEEDJPLANT:CAPER:SEEDJPLANT:WILD_CARROT:SEEDJPLANT:CASSAVA:SEEDJPLANT:CELERY:SEEDJPLANT:CHICKPEA:SEEDJPLANT:CHICORY:SEEDJPLANT:COWPEA:SEEDJPLANT:CUCUMBER:SEEDJPLANT:EGGPLANT:SEEDJPLANT:GARDEN_CRESS:SEEDJPLANT:GARLIC:SEEDJPLANT:HORNED_MELON:SEEDJPLANT:LEEK:SEEDJPLANT:LENTIL:SEEDJPLANT:LETTUCE:SEEDJPLANT:MUNG_BEAN:SEEDJPLANT:MUSKMELON:SEEDJPLANT:ONION:SEEDJPLANT:PARSNIP:SEEDJPLANT:PEA:SEEDJPLANT:PEANUT:SEEDJPLANT:PEPPER:SEEDJPLANT:POTATO:SEEDJPLANT:RADISH:SEEDJPLANT:RED_BEAN:SEEDJPLANT:RHUBARB:SEEDJPLANT:SOYBEAN:SEEDJPLANT:SPINACH:SEEDJPLANT:SQUASH:SEEDJPLANT:SWEET_POTATO:SEEDJPLANT:TARO:SEEDJPLANT:TOMATO:SEEDJPLANT:TOMATILLO:SEEDJPLANT:TURNIP:SEEDJPLANT:URAD_BEAN:SEEDJPLANT:WATERMELON:SEEDJPLANT:WINTER_MELON:SEEDJPLANT:LESSER_YAM:SEEDJPLANT:LONG_YAM:SEEDJPLANT:PURPLE_YAM:SEEDJPLANT:WHITE_YAM:SEEDJPLANT:PASSION_FRUIT:SEEDJPLANT:GRAPE:SEEDJPLANT:CRANBERRY:SEEDJPLANT:BILBERRY:SEEDJPLANT:BLUEBERRY:SEEDJPLANT:BLACKBERRY:SEEDJPLANT:RASPBERRY:SEEDJPLANT:PINEAPPLE:SEEDJPLANT:ABACA:SEEDJPLANT:BANANA:SEEDJPLANT:CARAMBOLA:SEEDJPLANT:CASHEW:SEEDJPLANT:COFFEE:SEEDJPLANT:DURIAN:SEEDJPLANT:GUAVA:SEEDJPLANT:PAPAYA:SEEDJPLANT:PARADISE_NUT:SEEDJPLANT:RAMBUTAN:SEEDJPLANT:TEA:SEEDJPLANT:AVOCADO:SEEDJPLANT:LIME:SEEDJPLANT:POMELO:SEEDJPLANT:CITRON:SEEDJPLANT:ORANGE:SEEDJPLANT:BITTER_ORANGE:SEEDJPLANT:FINGER_LIME:SEEDJPLANT:ROUND_LIME:SEEDJPLANT:DESERT_LIME:SEEDJPLANT:KUMQUAT:SEEDJPLANT:CUSTARD-APPLE:SEEDJPLANT:DATE_PALM:SEEDJPLANT:LYCHEE:SEEDJPLANT:MACADAMIA:SEEDJPLANT:OLIVE:SEEDJPLANT:POMEGRANATE:SEEDJPLANT:ALMOND:SEEDJPLANT:APPLE:SEEDJPLANT:APRICOT:SEEDJPLANT:BAYBERRY:SEEDJPLANT:CHERRY:SEEDJPLANT:GINKGO:SEEDJPLANT:HAZEL:SEEDJPLANT:PEACH:SEEDJPLANT:PEAR:SEEDJPLANT:PECAN:SEEDJPLANT:PERSIMMON:SEEDJPLANT:PLUM:SEEDJPLANT:SAND_PEAR:SEEDJPLANT:WALNUT:SEEDJ PLANT:MUSHROOM_HELMET_PLUMP:SEEDJPLANT:GRASS_TAIL_PIG:SEEDJPLANT:GRASS_WHEAT_CAVE:SEEDJPLANT:POD_SWEET:SEEDJPLANT:BUSH_QUARRY:SEEDJPLANT:ROOT_MUCK:SEEDJPLANT:TUBER_BLOATED:SEEDJPLANT:BERRIES_PRICKLE:SEEDJPLANT:BERRIES_STRAW:SEEDJPLANT:GRASS_LONGLAND:SEEDJPLANT:WEED_RAT:SEEDJPLANT:BERRIES_FISHER:SEEDJPLANT:REED_ROPE:SEEDJPLANT:MUSHROOM_CUP_DIMPLE:SEEDJPLANT:WEED_BLADE:SEEDJPLANT:ROOT_HIDE:SEEDJPLANT:SLIVER_BARB:SEEDJPLANT:BERRY_SUN:SEEDJPLANT:VINE_WHIP:SEEDJPLANT:OAK:SEEDJPLANT:ACACIA:SEEDJPLANT:CHESTNUT:SEEDJPLANT:CANDLENUT:SEEDJPLANT:MANGO:SEEDJPLANT:CACAO:SEEDRPLANT:BITTER_VETCH:LEAFRPLANT:BLOOD_AMARANTH:LEAFRPLANT:PURPLE_AMARANTH:LEAFRPLANT:RED_SPINACH:LEAFR!PLANT:ELEPHANT-HEAD_AMARANTH:LEAFRPLANT:ARTICHOKE:HEARTRPLANT:BITTER_MELON:LEAFRPLANT:BITTER_MELON:FRUITRPLANT:CAPER:LEAFRPLANT:CAPER:BUDRPLANT:CAPER:FRUITRPLANT:CUCUMBER:FRUITRPLANT:EGGPLANT:FRUITRPLANT:GARDEN_CRESS:LEAFRPLANT:GARLIC:BULBRPLANT:HORNED_MELON:FRUITRPLANT:LETTUCE:LEAFRPLANT:MUSKMELON:FRUITRPLANT:ONION:BULBRPLANT:PEPPER:FRUITRPLANT:SPINACH:LEAFRPLANT:SQUASH:FRUITRPLANT:TOMATO:FRUITRPLANT:TOMATILLO:FRUITRPLANT:WATERMELON:FRUITRPLANT:WINTER_MELON:FRUITRPLANT:PASSION_FRUIT:FRUITRPLANT:GRAPE:FRUITRPLANT:CRANBERRY:FRUITRPLANT:BILBERRY:FRUITRPLANT:BLUEBERRY:FRUITRPLANT:BLACKBERRY:FRUITRPLANT:RASPBERRY:FRUITRPLANT:PINEAPPLE:FRUITRPLANT:BANANA:FRUITRPLANT:CARAMBOLA:FRUITRPLANT:CASHEW:FRUITRPLANT:COFFEE:FRUITRPLANT:DURIAN:FRUITRPLANT:GUAVA:FRUITRPLANT:PAPAYA:FRUITRPLANT:PARADISE_NUT:FRUITRPLANT:RAMBUTAN:FRUITRPLANT:AVOCADO:FRUITRPLANT:LIME:FRUITRPLANT:POMELO:FRUITRPLANT:CITRON:FRUITRPLANT:ORANGE:FRUITRPLANT:BITTER_ORANGE:FRUITRPLANT:FINGER_LIME:FRUITRPLANT:ROUND_LIME:FRUITRPLANT:DESERT_LIME:FRUITRPLANT:KUMQUAT:FRUITRPLANT:CUSTARD-APPLE:FRUITRPLANT:DATE_PALM:FLOWERRPLANT:DATE_PALM:FRUITRPLANT:LYCHEE:FRUITRPLANT:OLIVE:FRUITRPLANT:POMEGRANATE:FRUITRPLANT:APPLE:FRUITRPLANT:APRICOT:FRUITRPLANT:BAYBERRY:FRUITRPLANT:CHERRY:FRUITRPLANT:PEACH:FRUITRPLANT:PEAR:FRUITRPLANT:PERSIMMON:FRUITRPLANT:PLUM:FRUITRPLANT:SAND_PEAR:FRUITRPLANT:BUSH_QUARRY:LEAFRPLANT:BERRIES_STRAW:FRUITRPLANT:SAGUARO:FRUITRPLANT:FEATHER:EGGRPLANT:MANGO:FRUITRPLANT:PALM:NUTZPLANT:SINGLE-GRAIN_WHEAT:MILLZPLANT:TWO-GRAIN_WHEAT:MILLZPLANT:SOFT_WHEAT:MILLZPLANT:HARD_WHEAT:MILLZPLANT:SPELT:MILLZPLANT:BARLEY:MILLZPLANT:BUCKWHEAT:MILLZPLANT:OATS:MILLZPLANT:RYE:MILLZPLANT:SORGHUM:MILLZPLANT:RICE:MILLZPLANT:MAIZE:MILLZPLANT:QUINOA:MILLZPLANT:KANIWA:MILLZPLANT:PENDANT_AMARANTH:MILLZPLANT:BLOOD_AMARANTH:MILLZPLANT:PURPLE_AMARANTH:MILLZPLANT:PEARL_MILLET:MILLZPLANT:WHITE_MILLET:MILLZPLANT:FINGER_MILLET:MILLZPLANT:FOXTAIL_MILLET:MILLZPLANT:FONIO:MILLZPLANT:TEFF:MILLZPLANT:FLAX:MILLZPLANT:HEMP:MILLZPLANT:GRASS_WHEAT_CAVE:MILLZPLANT:POD_SWEET:MILLZPLANT:GRASS_LONGLAND:MILLZPLANT:MUSHROOM_CUP_DIMPLE:MILLZPLANT:WEED_BLADE:MILLZPLANT:ROOT_HIDE:MILLZPLANT:SLIVER_BARB:MILLZPLANT:VINE_WHIP:MILLjCREATURE:TOAD:FATjCREATURE:TOAD:TALLOWjCREATURE:TOAD_MAN:FATjCREATURE:TOAD_MAN:TALLOWjCREATURE:GIANT_TOAD:FATjCREATURE:GIANT_TOAD:TALLOWjCREATURE:WORM:FATjCREATURE:WORM:TALLOWjCREATURE:WORM_MAN:FATjCREATURE:WORM_MAN:TALLOWjCREATURE:BIRD_BLUEJAY:FATjCREATURE:BIRD_BLUEJAY:TALLOWjCREATURE:BLUEJAY_MAN:FATjCREATURE:BLUEJAY_MAN:TALLOWjCREATURE:GIANT_BLUEJAY:FATjCREATURE:GIANT_BLUEJAY:TALLOWjCREATURE:BIRD_CARDINAL:FATjCREATURE:BIRD_CARDINAL:TALLOWjCREATURE:CARDINAL_MAN:FATjCREATURE:CARDINAL_MAN:TALLOWjCREATURE:GIANT_CARDINAL:FATjCREATURE:GIANT_CARDINAL:TALLOWjCREATURE:BIRD_GRACKLE:FATjCREATURE:BIRD_GRACKLE:TALLOWjCREATURE:GRACKLE_MAN:FATjCREATURE:GRACKLE_MAN:TALLOWjCREATURE:GIANT_GRACKLE:FATjCREATURE:GIANT_GRACKLE:TALLOWjCREATURE:BIRD_ORIOLE:FATjCREATURE:BIRD_ORIOLE:TALLOWjCREATURE:ORIOLE_MAN:FATjCREATURE:ORIOLE_MAN:TALLOWjCREATURE:GIANT_ORIOLE:FATjCREATURE:GIANT_ORIOLE:TALLOWjCREATURE:BIRD_RW_BLACKBIRD:FATj!CREATURE:BIRD_RW_BLACKBIRD:TALLOWjCREATURE:RW_BLACKBIRD_MAN:FATj CREATURE:RW_BLACKBIRD_MAN:TALLOWjCREATURE:GIANT_RW_BLACKBIRD:FATj"CREATURE:GIANT_RW_BLACKBIRD:TALLOWjCREATURE:BIRD_PENGUIN:FATjCREATURE:BIRD_PENGUIN:TALLOWj CREATURE:BIRD_PENGUIN_LITTLE:FATj#CREATURE:BIRD_PENGUIN_LITTLE:TALLOWj!CREATURE:BIRD_PENGUIN_EMPEROR:FATj$CREATURE:BIRD_PENGUIN_EMPEROR:TALLOWjCREATURE:PENGUIN MAN:FATjCREATURE:PENGUIN MAN:TALLOWjCREATURE:BIRD_PENGUIN_GIANT:FATj"CREATURE:BIRD_PENGUIN_GIANT:TALLOWj"CREATURE:BIRD_FALCON_PEREGRINE:FATj%CREATURE:BIRD_FALCON_PEREGRINE:TALLOWj!CREATURE:PEREGRINE FALCON MAN:FATj$CREATURE:PEREGRINE FALCON MAN:TALLOWj#CREATURE:GIANT PEREGRINE FALCON:FATj&CREATURE:GIANT PEREGRINE FALCON:TALLOWjCREATURE:BIRD_KIWI:FATjCREATURE:BIRD_KIWI:TALLOWjCREATURE:KIWI MAN:FATjCREATURE:KIWI MAN:TALLOWjCREATURE:BIRD_KIWI_GIANT:FATjCREATURE:BIRD_KIWI_GIANT:TALLOWjCREATURE:BIRD_OSTRICH:FATjCREATURE:BIRD_OSTRICH:TALLOWjCREATURE:OSTRICH MAN:FATjCREATURE:OSTRICH MAN:TALLOWjCREATURE:BIRD_OSTRICH_GIANT:FATj"CREATURE:BIRD_OSTRICH_GIANT:TALLOWjCREATURE:BIRD_CROW:FATjCREATURE:BIRD_CROW:TALLOWjCREATURE:CROW_MAN:FATjCREATURE:CROW_MAN:TALLOWjCREATURE:GIANT_CROW:FATjCREATURE:GIANT_CROW:TALLOWjCREATURE:BIRD_RAVEN:FATjCREATURE:BIRD_RAVEN:TALLOWjCREATURE:RAVEN_MAN:FATjCREATURE:RAVEN_MAN:TALLOWjCREATURE:GIANT_RAVEN:FATjCREATURE:GIANT_RAVEN:TALLOWjCREATURE:BIRD_CASSOWARY:FATjCREATURE:BIRD_CASSOWARY:TALLOWjCREATURE:CASSOWARY_MAN:FATjCREATURE:CASSOWARY_MAN:TALLOWjCREATURE:GIANT_CASSOWARY:FATjCREATURE:GIANT_CASSOWARY:TALLOWjCREATURE:BIRD_KEA:FATjCREATURE:BIRD_KEA:TALLOWjCREATURE:KEA_MAN:FATjCREATURE:KEA_MAN:TALLOWjCREATURE:GIANT_KEA:FATjCREATURE:GIANT_KEA:TALLOWjCREATURE:BIRD_OWL_SNOWY:FATjCREATURE:BIRD_OWL_SNOWY:TALLOWjCREATURE:SNOWY_OWL_MAN:FATjCREATURE:SNOWY_OWL_MAN:TALLOWjCREATURE:GIANT_SNOWY_OWL:FATjCREATURE:GIANT_SNOWY_OWL:TALLOWjCREATURE:SPARROW:FATjCREATURE:SPARROW:TALLOWjCREATURE:SPARROW_MAN:FATjCREATURE:SPARROW_MAN:TALLOWjCREATURE:GIANT_SPARROW:FATjCREATURE:GIANT_SPARROW:TALLOWjCREATURE:BIRD_STORK_WHITE:FATj CREATURE:BIRD_STORK_WHITE:TALLOWjCREATURE:WHITE_STORK_MAN:FATjCREATURE:WHITE_STORK_MAN:TALLOWjCREATURE:GIANT_WHITE_STORK:FATj!CREATURE:GIANT_WHITE_STORK:TALLOWjCREATURE:BIRD_LOON:FATjCREATURE:BIRD_LOON:TALLOWjCREATURE:LOON_MAN:FATjCREATURE:LOON_MAN:TALLOWjCREATURE:GIANT_LOON:FATjCREATURE:GIANT_LOON:TALLOWjCREATURE:BIRD_OWL_BARN:FATjCREATURE:BIRD_OWL_BARN:TALLOWjCREATURE:BARN_OWL_MAN:FATjCREATURE:BARN_OWL_MAN:TALLOWjCREATURE:GIANT_BARN_OWL:FATjCREATURE:GIANT_BARN_OWL:TALLOWjCREATURE:BIRD_PARAKEET:FATjCREATURE:BIRD_PARAKEET:TALLOWjCREATURE:PARAKEET_MAN:FATjCREATURE:PARAKEET_MAN:TALLOWjCREATURE:GIANT_PARAKEET:FATjCREATURE:GIANT_PARAKEET:TALLOWjCREATURE:BIRD_KAKAPO:FATjCREATURE:BIRD_KAKAPO:TALLOWjCREATURE:KAKAPO_MAN:FATjCREATURE:KAKAPO_MAN:TALLOWjCREATURE:GIANT_KAKAPO:FATjCREATURE:GIANT_KAKAPO:TALLOWjCREATURE:BIRD_PARROT_GREY:FATj CREATURE:BIRD_PARROT_GREY:TALLOWjCREATURE:GREY_PARROT_MAN:FATjCREATURE:GREY_PARROT_MAN:TALLOWjCREATURE:GIANT_GREY_PARROT:FATj!CREATURE:GIANT_GREY_PARROT:TALLOWjCREATURE:BIRD_PUFFIN:FATjCREATURE:BIRD_PUFFIN:TALLOWjCREATURE:PUFFIN_MAN:FATjCREATURE:PUFFIN_MAN:TALLOWjCREATURE:GIANT_PUFFIN:FATjCREATURE:GIANT_PUFFIN:TALLOWjCREATURE:BIRD_SWAN:FATjCREATURE:BIRD_SWAN:TALLOWjCREATURE:SWAN_MAN:FATjCREATURE:SWAN_MAN:TALLOWjCREATURE:GIANT_SWAN:FATjCREATURE:GIANT_SWAN:TALLOWjCREATURE:BIRD_LORIKEET:FATjCREATURE:BIRD_LORIKEET:TALLOWjCREATURE:LORIKEET_MAN:FATjCREATURE:LORIKEET_MAN:TALLOWjCREATURE:GIANT_LORIKEET:FATjCREATURE:GIANT_LORIKEET:TALLOWjCREATURE:BIRD_WREN:FATjCREATURE:BIRD_WREN:TALLOWjCREATURE:WREN_MAN:FATjCREATURE:WREN_MAN:TALLOWjCREATURE:GIANT_WREN:FATjCREATURE:GIANT_WREN:TALLOWjCREATURE:BIRD_OSPREY:FATjCREATURE:BIRD_OSPREY:TALLOWjCREATURE:OSPREY_MAN:FATjCREATURE:OSPREY_MAN:TALLOWjCREATURE:GIANT_OSPREY:FATjCREATURE:GIANT_OSPREY:TALLOWjCREATURE:BIRD_EMU:FATjCREATURE:BIRD_EMU:TALLOWjCREATURE:EMU_MAN:FATjCREATURE:EMU_MAN:TALLOWjCREATURE:GIANT_EMU:FATjCREATURE:GIANT_EMU:TALLOWjCREATURE:BIRD_COCKATIEL:FATjCREATURE:BIRD_COCKATIEL:TALLOWjCREATURE:COCKATIEL_MAN:FATjCREATURE:COCKATIEL_MAN:TALLOWjCREATURE:GIANT_COCKATIEL:FATjCREATURE:GIANT_COCKATIEL:TALLOWj&CREATURE:BIRD_LOVEBIRD_PEACH-FACED:FATj)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:TALLOWj%CREATURE:PEACH-FACED_LOVEBIRD_MAN:FATj(CREATURE:PEACH-FACED_LOVEBIRD_MAN:TALLOWj'CREATURE:GIANT_PEACH-FACED_LOVEBIRD:FATj*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:TALLOWjCREATURE:BIRD_MAGPIE:FATjCREATURE:BIRD_MAGPIE:TALLOWjCREATURE:MAGPIE_MAN:FATjCREATURE:MAGPIE_MAN:TALLOWjCREATURE:GIANT_MAGPIE:FATjCREATURE:GIANT_MAGPIE:TALLOWjCREATURE:BIRD_KESTREL:FATjCREATURE:BIRD_KESTREL:TALLOWjCREATURE:KESTREL_MAN:FATjCREATURE:KESTREL_MAN:TALLOWjCREATURE:GIANT_KESTREL:FATjCREATURE:GIANT_KESTREL:TALLOWjCREATURE:BIRD_ALBATROSS:FATjCREATURE:BIRD_ALBATROSS:TALLOWjCREATURE:ALBATROSS_MAN:FATjCREATURE:ALBATROSS_MAN:TALLOWjCREATURE:GIANT_ALBATROSS:FATjCREATURE:GIANT_ALBATROSS:TALLOWj"CREATURE:BIRD_OWL_GREAT_HORNED:FATj%CREATURE:BIRD_OWL_GREAT_HORNED:TALLOWj!CREATURE:GREAT_HORNED_OWL_MAN:FATj$CREATURE:GREAT_HORNED_OWL_MAN:TALLOWj#CREATURE:GIANT_GREAT_HORNED_OWL:FATj&CREATURE:GIANT_GREAT_HORNED_OWL:TALLOWjCREATURE:BIRD_EAGLE:FATjCREATURE:BIRD_EAGLE:TALLOWjCREATURE:EAGLE_MAN:FATjCREATURE:EAGLE_MAN:TALLOWjCREATURE:GIANT_EAGLE:FATjCREATURE:GIANT_EAGLE:TALLOWjCREATURE:BIRD_HORNBILL:FATjCREATURE:BIRD_HORNBILL:TALLOWjCREATURE:HORNBILL_MAN:FATjCREATURE:HORNBILL_MAN:TALLOWjCREATURE:GIANT_HORNBILL:FATjCREATURE:GIANT_HORNBILL:TALLOWj!CREATURE:BIRD_LOVEBIRD_MASKED:FATj$CREATURE:BIRD_LOVEBIRD_MASKED:TALLOWj CREATURE:MASKED_LOVEBIRD_MAN:FATj#CREATURE:MASKED_LOVEBIRD_MAN:TALLOWj"CREATURE:GIANT_MASKED_LOVEBIRD:FATj%CREATURE:GIANT_MASKED_LOVEBIRD:TALLOWjCREATURE:BIRD_BUSHTIT:FATjCREATURE:BIRD_BUSHTIT:TALLOWjCREATURE:BUSHTIT_MAN:FATjCREATURE:BUSHTIT_MAN:TALLOWjCREATURE:GIANT_BUSHTIT:FATjCREATURE:GIANT_BUSHTIT:TALLOWjCREATURE:DAMSELFLY:FATjCREATURE:DAMSELFLY:TALLOWjCREATURE:DAMSELFLY_MAN:FATjCREATURE:DAMSELFLY_MAN:TALLOWjCREATURE:GIANT_DAMSELFLY:FATjCREATURE:GIANT_DAMSELFLY:TALLOWjCREATURE:MOTH:FATjCREATURE:MOTH:TALLOWjCREATURE:MOTH_MAN:FATjCREATURE:MOTH_MAN:TALLOWjCREATURE:GIANT_MOTH:FATjCREATURE:GIANT_MOTH:TALLOWjCREATURE:GRASSHOPPER:FATjCREATURE:GRASSHOPPER:TALLOWjCREATURE:GRASSHOPPER_MAN:FATjCREATURE:GRASSHOPPER_MAN:TALLOWjCREATURE:GIANT_GRASSHOPPER:FATj!CREATURE:GIANT_GRASSHOPPER:TALLOWjCREATURE:BARK_SCORPION:FATjCREATURE:BARK_SCORPION:TALLOWjCREATURE:BARK_SCORPION_MAN:FATj!CREATURE:BARK_SCORPION_MAN:TALLOWj CREATURE:GIANT_BARK_SCORPION:FATj#CREATURE:GIANT_BARK_SCORPION:TALLOWjCREATURE:MANTIS:FATjCREATURE:MANTIS:TALLOWjCREATURE:MANTIS_MAN:FATjCREATURE:MANTIS_MAN:TALLOWjCREATURE:GIANT_MANTIS:FATjCREATURE:GIANT_MANTIS:TALLOWjCREATURE:TICK:FATjCREATURE:TICK:TALLOWjCREATURE:TICK_MAN:FATjCREATURE:TICK_MAN:TALLOWjCREATURE:GIANT_TICK:FATjCREATURE:GIANT_TICK:TALLOWjCREATURE:LOUSE:FATjCREATURE:LOUSE:TALLOWjCREATURE:LOUSE_MAN:FATjCREATURE:LOUSE_MAN:TALLOWjCREATURE:GIANT_LOUSE:FATjCREATURE:GIANT_LOUSE:TALLOWjCREATURE:THRIPS:FATjCREATURE:THRIPS:TALLOWjCREATURE:THRIPS_MAN:FATjCREATURE:THRIPS_MAN:TALLOWjCREATURE:GIANT_THRIPS:FATjCREATURE:GIANT_THRIPS:TALLOWjCREATURE:SLUG:FATjCREATURE:SLUG:TALLOWjCREATURE:SLUG_MAN:FATjCREATURE:SLUG_MAN:TALLOWjCREATURE:GIANT_SLUG:FATjCREATURE:GIANT_SLUG:TALLOWjCREATURE:MOSQUITO:FATjCREATURE:MOSQUITO:TALLOWjCREATURE:MOSQUITO_MAN:FATjCREATURE:MOSQUITO_MAN:TALLOWjCREATURE:GIANT_MOSQUITO:FATjCREATURE:GIANT_MOSQUITO:TALLOWjCREATURE:SPIDER_JUMPING:FATjCREATURE:SPIDER_JUMPING:TALLOWjCREATURE:JUMPING_SPIDER_MAN:FATj"CREATURE:JUMPING_SPIDER_MAN:TALLOWj!CREATURE:GIANT_JUMPING_SPIDER:FATj$CREATURE:GIANT_JUMPING_SPIDER:TALLOWjCREATURE:TERMITE:FATjCREATURE:TERMITE:TALLOWjCREATURE:MOON_SNAIL:FATjCREATURE:MOON_SNAIL:TALLOWjCREATURE:MOON_SNAIL_MAN:FATjCREATURE:MOON_SNAIL_MAN:TALLOWjCREATURE:GIANT_MOON_SNAIL:FATj CREATURE:GIANT_MOON_SNAIL:TALLOWj!CREATURE:SPIDER_BROWN_RECLUSE:FATj$CREATURE:SPIDER_BROWN_RECLUSE:TALLOWj%CREATURE:BROWN_RECLUSE_SPIDER_MAN:FATj(CREATURE:BROWN_RECLUSE_SPIDER_MAN:TALLOWj'CREATURE:GIANT_BROWN_RECLUSE_SPIDER:FATj*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:TALLOWjCREATURE:SNAIL:FATjCREATURE:SNAIL:TALLOWjCREATURE:SNAIL_MAN:FATjCREATURE:SNAIL_MAN:TALLOWjCREATURE:GIANT_SNAIL:FATjCREATURE:GIANT_SNAIL:TALLOWjCREATURE:GECKO_LEOPARD:FATjCREATURE:GECKO_LEOPARD:TALLOWjCREATURE:LEOPARD_GECKO_MAN:FATj!CREATURE:LEOPARD_GECKO_MAN:TALLOWj CREATURE:GIANT_LEOPARD_GECKO:FATj#CREATURE:GIANT_LEOPARD_GECKO:TALLOWjCREATURE:DESERT TORTOISE:FATjCREATURE:DESERT TORTOISE:TALLOWj CREATURE:DESERT_TORTOISE_MAN:FATj#CREATURE:DESERT_TORTOISE_MAN:TALLOWj"CREATURE:GIANT_DESERT_TORTOISE:FATj%CREATURE:GIANT_DESERT_TORTOISE:TALLOWjCREATURE:GILA_MONSTER:FATjCREATURE:GILA_MONSTER:TALLOWjCREATURE:GILA_MONSTER_MAN:FATj CREATURE:GILA_MONSTER_MAN:TALLOWjCREATURE:GIANT_GILA_MONSTER:FATj"CREATURE:GIANT_GILA_MONSTER:TALLOWjCREATURE:DOG:FATjCREATURE:DOG:TALLOWjCREATURE:CAT:FATjCREATURE:CAT:TALLOWjCREATURE:MULE:FATjCREATURE:MULE:TALLOWjCREATURE:DONKEY:FATjCREATURE:DONKEY:TALLOWjCREATURE:HORSE:FATjCREATURE:HORSE:TALLOWjCREATURE:COW:FATjCREATURE:COW:TALLOWjCREATURE:SHEEP:FATjCREATURE:SHEEP:TALLOWjCREATURE:PIG:FATjCREATURE:PIG:TALLOWjCREATURE:GOAT:FATjCREATURE:GOAT:TALLOWjCREATURE:BIRD_CHICKEN:FATjCREATURE:BIRD_CHICKEN:TALLOWjCREATURE:CAVY:FATjCREATURE:CAVY:TALLOWjCREATURE:BIRD_DUCK:FATjCREATURE:BIRD_DUCK:TALLOWjCREATURE:WATER_BUFFALO:FATjCREATURE:WATER_BUFFALO:TALLOWjCREATURE:REINDEER:FATjCREATURE:REINDEER:TALLOWjCREATURE:BIRD_GOOSE:FATjCREATURE:BIRD_GOOSE:TALLOWjCREATURE:YAK:FATjCREATURE:YAK:TALLOWjCREATURE:LLAMA:FATjCREATURE:LLAMA:TALLOWjCREATURE:ALPACA:FATjCREATURE:ALPACA:TALLOWjCREATURE:BIRD_GUINEAFOWL:FATjCREATURE:BIRD_GUINEAFOWL:TALLOWjCREATURE:BIRD_PEAFOWL_BLUE:FATj!CREATURE:BIRD_PEAFOWL_BLUE:TALLOWjCREATURE:BIRD_TURKEY:FATjCREATURE:BIRD_TURKEY:TALLOWjCREATURE:RABBIT:FATjCREATURE:RABBIT:TALLOWjCREATURE:FLY:FATjCREATURE:FLY:TALLOWjCREATURE:FLY_MAN:FATjCREATURE:FLY_MAN:TALLOWjCREATURE:GIANT_FLY:FATjCREATURE:GIANT_FLY:TALLOWjCREATURE:ROACH_LARGE:FATjCREATURE:ROACH_LARGE:TALLOWjCREATURE:ROACH_MAN:FATjCREATURE:ROACH_MAN:TALLOWjCREATURE:GIANT_ROACH:FATjCREATURE:GIANT_ROACH:TALLOWjCREATURE:BEETLE:FATjCREATURE:BEETLE:TALLOWjCREATURE:BEETLE_MAN:FATjCREATURE:BEETLE_MAN:TALLOWjCREATURE:GIANT_BEETLE:FATjCREATURE:GIANT_BEETLE:TALLOWjCREATURE:ANT:FATjCREATURE:ANT:TALLOWjCREATURE:BUTTERFLY_MONARCH:FATj!CREATURE:BUTTERFLY_MONARCH:TALLOWj"CREATURE:BUTTERFLY_MONARCH_MAN:FATj%CREATURE:BUTTERFLY_MONARCH_MAN:TALLOWj$CREATURE:GIANT_BUTTERFLY_MONARCH:FATj'CREATURE:GIANT_BUTTERFLY_MONARCH:TALLOWjCREATURE:FIREFLY:FATjCREATURE:FIREFLY:TALLOWjCREATURE:FIREFLY_MAN:FATjCREATURE:FIREFLY_MAN:TALLOWjCREATURE:GIANT_FIREFLY:FATjCREATURE:GIANT_FIREFLY:TALLOWjCREATURE:DRAGONFLY:FATjCREATURE:DRAGONFLY:TALLOWjCREATURE:DRAGONFLY_MAN:FATjCREATURE:DRAGONFLY_MAN:TALLOWjCREATURE:GIANT_DRAGONFLY:FATjCREATURE:GIANT_DRAGONFLY:TALLOWjCREATURE:HONEY_BEE:WAXjCREATURE:HONEY_BEE:FATjCREATURE:HONEY_BEE:TALLOWjCREATURE:BUMBLEBEE:WAXjCREATURE:BUMBLEBEE:FATjCREATURE:BUMBLEBEE:TALLOWjCREATURE:GOAT_MOUNTAIN:FATjCREATURE:GOAT_MOUNTAIN:TALLOWjCREATURE:GOAT_MOUNTAIN_MAN:FATj!CREATURE:GOAT_MOUNTAIN_MAN:TALLOWj CREATURE:GIANT_GOAT_MOUNTAIN:FATj#CREATURE:GIANT_GOAT_MOUNTAIN:TALLOWjCREATURE:MARMOT_HOARY:FATjCREATURE:MARMOT_HOARY:TALLOWjCREATURE:MARMOT_HOARY_MAN:FATj CREATURE:MARMOT_HOARY_MAN:TALLOWjCREATURE:GIANT_MARMOT_HOARY:FATj"CREATURE:GIANT_MARMOT_HOARY:TALLOWjCREATURE:GNOME_MOUNTAIN:FATjCREATURE:GNOME_MOUNTAIN:TALLOWjCREATURE:GNOME_DARK:FATjCREATURE:GNOME_DARK:TALLOWjCREATURE:WALRUS:FATjCREATURE:WALRUS:TALLOWjCREATURE:WALRUS_MAN:FATjCREATURE:WALRUS_MAN:TALLOWjCREATURE:GIANT_WALRUS:FATjCREATURE:GIANT_WALRUS:TALLOWjCREATURE:FISH_LAMPREY_SEA:FATj CREATURE:FISH_LAMPREY_SEA:TALLOWjCREATURE:SHARK_GREAT_WHITE:FATj!CREATURE:SHARK_GREAT_WHITE:TALLOWjCREATURE:SHARK_FRILL:FATjCREATURE:SHARK_FRILL:TALLOWj CREATURE:SHARK_SPINY_DOGFISH:FATj#CREATURE:SHARK_SPINY_DOGFISH:TALLOWj$CREATURE:SHARK_WOBBEGONG_SPOTTED:FATj'CREATURE:SHARK_WOBBEGONG_SPOTTED:TALLOWjCREATURE:SHARK_WHALE:FATjCREATURE:SHARK_WHALE:TALLOWjCREATURE:SHARK_BASKING:FATjCREATURE:SHARK_BASKING:TALLOWjCREATURE:SHARK_NURSE:FATjCREATURE:SHARK_NURSE:TALLOWj CREATURE:SHARK_MAKO_SHORTFIN:FATj#CREATURE:SHARK_MAKO_SHORTFIN:TALLOWjCREATURE:SHARK_MAKO_LONGFIN:FATj"CREATURE:SHARK_MAKO_LONGFIN:TALLOWjCREATURE:SHARK_TIGER:FATjCREATURE:SHARK_TIGER:TALLOWjCREATURE:SHARK_BULL:FATjCREATURE:SHARK_BULL:TALLOWj CREATURE:SHARK_REEF_BLACKTIP:FATj#CREATURE:SHARK_REEF_BLACKTIP:TALLOWj CREATURE:SHARK_REEF_WHITETIP:FATj#CREATURE:SHARK_REEF_WHITETIP:TALLOWjCREATURE:SHARK_BLUE:FATjCREATURE:SHARK_BLUE:TALLOWjCREATURE:SHARK_HAMMERHEAD:FATj CREATURE:SHARK_HAMMERHEAD:TALLOWjCREATURE:SHARK_ANGEL:FATjCREATURE:SHARK_ANGEL:TALLOWjCREATURE:FISH_SKATE_COMMON:FATj!CREATURE:FISH_SKATE_COMMON:TALLOWjCREATURE:FISH_RAY_MANTA:FATjCREATURE:FISH_RAY_MANTA:TALLOWjCREATURE:FISH_STINGRAY:FATjCREATURE:FISH_STINGRAY:TALLOWjCREATURE:FISH_COELACANTH:FATjCREATURE:FISH_COELACANTH:TALLOWjCREATURE:FISH_STURGEON:FATjCREATURE:FISH_STURGEON:TALLOWjCREATURE:FISH_CONGER_EEL:FATjCREATURE:FISH_CONGER_EEL:TALLOWjCREATURE:FISH_MILKFISH:FATjCREATURE:FISH_MILKFISH:TALLOWjCREATURE:FISH_COD:FATjCREATURE:FISH_COD:TALLOWjCREATURE:FISH_OPAH:FATjCREATURE:FISH_OPAH:TALLOWjCREATURE:FISH_GROUPER_GIANT:FATj"CREATURE:FISH_GROUPER_GIANT:TALLOWjCREATURE:FISH_BLUEFISH:FATjCREATURE:FISH_BLUEFISH:TALLOWjCREATURE:FISH_SUNFISH_OCEAN:FATj"CREATURE:FISH_SUNFISH_OCEAN:TALLOWjCREATURE:FISH_SWORDFISH:FATjCREATURE:FISH_SWORDFISH:TALLOWjCREATURE:FISH_MARLIN:FATjCREATURE:FISH_MARLIN:TALLOWjCREATURE:FISH_HALIBUT:FATjCREATURE:FISH_HALIBUT:TALLOWj!CREATURE:FISH_BARRACUDA_GREAT:FATj$CREATURE:FISH_BARRACUDA_GREAT:TALLOWjCREATURE:FISH_TUNA_BLUEFIN:FATj!CREATURE:FISH_TUNA_BLUEFIN:TALLOWjCREATURE:NARWHAL:FATjCREATURE:NARWHAL:TALLOWjCREATURE:NARWHAL MAN:FATjCREATURE:NARWHAL MAN:TALLOWjCREATURE:NARWHAL, GIANT:FATjCREATURE:NARWHAL, GIANT:TALLOWjCREATURE:HIPPO:FATjCREATURE:HIPPO:TALLOWjCREATURE:HIPPO_MAN:FATjCREATURE:HIPPO_MAN:TALLOWjCREATURE:GIANT_HIPPO:FATjCREATURE:GIANT_HIPPO:TALLOWjCREATURE:FISH_GAR_LONGNOSE:FATj!CREATURE:FISH_GAR_LONGNOSE:TALLOWjCREATURE:FISH_CARP:FATjCREATURE:FISH_CARP:TALLOWjCREATURE:FISH_TIGERFISH:FATjCREATURE:FISH_TIGERFISH:TALLOWjCREATURE:FISH_PIKE:FATjCREATURE:FISH_PIKE:TALLOWjCREATURE:PLATYPUS:FATjCREATURE:PLATYPUS:TALLOWjCREATURE:PLATYPUS MAN:FATjCREATURE:PLATYPUS MAN:TALLOWjCREATURE:PLATYPUS, GIANT:FATjCREATURE:PLATYPUS, GIANT:TALLOWjCREATURE:BEAR_GRIZZLY:FATjCREATURE:BEAR_GRIZZLY:TALLOWjCREATURE:BEAR_GRIZZLY_MAN:FATj CREATURE:BEAR_GRIZZLY_MAN:TALLOWjCREATURE:GIANT_BEAR_GRIZZLY:FATj"CREATURE:GIANT_BEAR_GRIZZLY:TALLOWjCREATURE:BEAR_BLACK:FATjCREATURE:BEAR_BLACK:TALLOWjCREATURE:BEAR_BLACK_MAN:FATjCREATURE:BEAR_BLACK_MAN:TALLOWjCREATURE:GIANT_BEAR_BLACK:FATj CREATURE:GIANT_BEAR_BLACK:TALLOWjCREATURE:DEER:FATjCREATURE:DEER:TALLOWjCREATURE:DEER_MAN:FATjCREATURE:DEER_MAN:TALLOWjCREATURE:GIANT_DEER:FATjCREATURE:GIANT_DEER:TALLOWjCREATURE:FOX:FATjCREATURE:FOX:TALLOWjCREATURE:FOX_MAN:FATjCREATURE:FOX_MAN:TALLOWjCREATURE:GIANT_FOX:FATjCREATURE:GIANT_FOX:TALLOWjCREATURE:RACCOON:FATjCREATURE:RACCOON:TALLOWjCREATURE:RACCOON_MAN:FATjCREATURE:RACCOON_MAN:TALLOWjCREATURE:GIANT_RACCOON:FATjCREATURE:GIANT_RACCOON:TALLOWjCREATURE:MACAQUE_RHESUS:FATjCREATURE:MACAQUE_RHESUS:TALLOWjCREATURE:MACAQUE_RHESUS_MAN:FATj"CREATURE:MACAQUE_RHESUS_MAN:TALLOWj!CREATURE:GIANT_MACAQUE_RHESUS:FATj$CREATURE:GIANT_MACAQUE_RHESUS:TALLOWjCREATURE:COUGAR:FATjCREATURE:COUGAR:TALLOWjCREATURE:COUGAR_MAN:FATjCREATURE:COUGAR_MAN:TALLOWjCREATURE:GIANT_COUGAR:FATjCREATURE:GIANT_COUGAR:TALLOWjCREATURE:WOLF:FATjCREATURE:WOLF:TALLOWjCREATURE:WOLF_MAN:FATjCREATURE:WOLF_MAN:TALLOWjCREATURE:GIANT_WOLF:FATjCREATURE:GIANT_WOLF:TALLOWjCREATURE:GROUNDHOG:FATjCREATURE:GROUNDHOG:TALLOWjCREATURE:GROUNDHOG_MAN:FATjCREATURE:GROUNDHOG_MAN:TALLOWjCREATURE:GIANT_GROUNDHOG:FATjCREATURE:GIANT_GROUNDHOG:TALLOWjCREATURE:ALLIGATOR:FATjCREATURE:ALLIGATOR:TALLOWjCREATURE:ALLIGATOR_MAN:FATjCREATURE:ALLIGATOR_MAN:TALLOWjCREATURE:GIANT_ALLIGATOR:FATjCREATURE:GIANT_ALLIGATOR:TALLOWjCREATURE:BIRD_BUZZARD:FATjCREATURE:BIRD_BUZZARD:TALLOWjCREATURE:BUZZARD_MAN:FATjCREATURE:BUZZARD_MAN:TALLOWjCREATURE:GIANT_BUZZARD:FATjCREATURE:GIANT_BUZZARD:TALLOWjCREATURE:PANDA:FATjCREATURE:PANDA:TALLOWjCREATURE:PANDA, GIGANTIC:FATjCREATURE:PANDA, GIGANTIC:TALLOWjCREATURE:PANDA MAN:FATjCREATURE:PANDA MAN:TALLOWjCREATURE:CAPYBARA:FATjCREATURE:CAPYBARA:TALLOWjCREATURE:CAPYBARA, GIANT:FATjCREATURE:CAPYBARA, GIANT:TALLOWjCREATURE:CAPYBARA MAN:FATjCREATURE:CAPYBARA MAN:TALLOWjCREATURE:BADGER:FATjCREATURE:BADGER:TALLOWjCREATURE:BADGER MAN:FATjCREATURE:BADGER MAN:TALLOWjCREATURE:BADGER, GIANT:FATjCREATURE:BADGER, GIANT:TALLOWjCREATURE:MOOSE:FATjCREATURE:MOOSE:TALLOWjCREATURE:MOOSE MAN:FATjCREATURE:MOOSE MAN:TALLOWjCREATURE:MOOSE, GIANT:FATjCREATURE:MOOSE, GIANT:TALLOWjCREATURE:RED PANDA:FATjCREATURE:RED PANDA:TALLOWjCREATURE:RED PANDA MAN:FATjCREATURE:RED PANDA MAN:TALLOWjCREATURE:RED PANDA, GIANT:FATj CREATURE:RED PANDA, GIANT:TALLOWjCREATURE:ELEPHANT:FATjCREATURE:ELEPHANT:TALLOWjCREATURE:ELEPHANT_MAN:FATjCREATURE:ELEPHANT_MAN:TALLOWjCREATURE:GIANT_ELEPHANT:FATjCREATURE:GIANT_ELEPHANT:TALLOWjCREATURE:WARTHOG:FATjCREATURE:WARTHOG:TALLOWjCREATURE:WARTHOG_MAN:FATjCREATURE:WARTHOG_MAN:TALLOWjCREATURE:GIANT_WARTHOG:FATjCREATURE:GIANT_WARTHOG:TALLOWjCREATURE:LION:FATjCREATURE:LION:TALLOWjCREATURE:LION_MAN:FATjCREATURE:LION_MAN:TALLOWjCREATURE:GIANT_LION:FATjCREATURE:GIANT_LION:TALLOWjCREATURE:LEOPARD:FATjCREATURE:LEOPARD:TALLOWjCREATURE:LEOPARD_MAN:FATjCREATURE:LEOPARD_MAN:TALLOWjCREATURE:GIANT_LEOPARD:FATjCREATURE:GIANT_LEOPARD:TALLOWjCREATURE:JAGUAR:FATjCREATURE:JAGUAR:TALLOWjCREATURE:JAGUAR_MAN:FATjCREATURE:JAGUAR_MAN:TALLOWjCREATURE:GIANT_JAGUAR:FATjCREATURE:GIANT_JAGUAR:TALLOWjCREATURE:TIGER:FATjCREATURE:TIGER:TALLOWjCREATURE:TIGER_MAN:FATjCREATURE:TIGER_MAN:TALLOWjCREATURE:GIANT_TIGER:FATjCREATURE:GIANT_TIGER:TALLOWjCREATURE:CHEETAH:FATjCREATURE:CHEETAH:TALLOWjCREATURE:CHEETAH_MAN:FATjCREATURE:CHEETAH_MAN:TALLOWjCREATURE:GIANT_CHEETAH:FATjCREATURE:GIANT_CHEETAH:TALLOWjCREATURE:GAZELLE:FATjCREATURE:GAZELLE:TALLOWjCREATURE:GAZELLE_MAN:FATjCREATURE:GAZELLE_MAN:TALLOWjCREATURE:GIANT_GAZELLE:FATjCREATURE:GIANT_GAZELLE:TALLOWjCREATURE:MANDRILL:FATjCREATURE:MANDRILL:TALLOWjCREATURE:MANDRILL_MAN:FATjCREATURE:MANDRILL_MAN:TALLOWjCREATURE:GIANT_MANDRILL:FATjCREATURE:GIANT_MANDRILL:TALLOWjCREATURE:CHIMPANZEE:FATjCREATURE:CHIMPANZEE:TALLOWjCREATURE:BONOBO:FATjCREATURE:BONOBO:TALLOWjCREATURE:GORILLA:FATjCREATURE:GORILLA:TALLOWjCREATURE:ORANGUTAN:FATjCREATURE:ORANGUTAN:TALLOWjCREATURE:GIBBON_SIAMANG:FATjCREATURE:GIBBON_SIAMANG:TALLOWj CREATURE:GIBBON_WHITE_HANDED:FATj#CREATURE:GIBBON_WHITE_HANDED:TALLOWj CREATURE:GIBBON_BLACK_HANDED:FATj#CREATURE:GIBBON_BLACK_HANDED:TALLOWjCREATURE:GIBBON_GRAY:FATjCREATURE:GIBBON_GRAY:TALLOWjCREATURE:GIBBON_SILVERY:FATjCREATURE:GIBBON_SILVERY:TALLOWjCREATURE:GIBBON_PILEATED:FATjCREATURE:GIBBON_PILEATED:TALLOWjCREATURE:GIBBON_BILOU:FATjCREATURE:GIBBON_BILOU:TALLOWj CREATURE:GIBBON_WHITE_BROWED:FATj#CREATURE:GIBBON_WHITE_BROWED:TALLOWj!CREATURE:GIBBON_BLACK_CRESTED:FATj$CREATURE:GIBBON_BLACK_CRESTED:TALLOWjCREATURE:CAMEL_1_HUMP:FATjCREATURE:CAMEL_1_HUMP:TALLOWjCREATURE:CAMEL_1_HUMP_MAN:FATj CREATURE:CAMEL_1_HUMP_MAN:TALLOWjCREATURE:GIANT_CAMEL_1_HUMP:FATj"CREATURE:GIANT_CAMEL_1_HUMP:TALLOWjCREATURE:CAMEL_2_HUMP:FATjCREATURE:CAMEL_2_HUMP:TALLOWjCREATURE:CAMEL_2_HUMP_MAN:FATj CREATURE:CAMEL_2_HUMP_MAN:TALLOWjCREATURE:GIANT_CAMEL_2_HUMP:FATj"CREATURE:GIANT_CAMEL_2_HUMP:TALLOWj CREATURE:CROCODILE_SALTWATER:FATj#CREATURE:CROCODILE_SALTWATER:TALLOWj$CREATURE:CROCODILE_SALTWATER_MAN:FATj'CREATURE:CROCODILE_SALTWATER_MAN:TALLOWj&CREATURE:GIANT_CROCODILE_SALTWATER:FATj)CREATURE:GIANT_CROCODILE_SALTWATER:TALLOWjCREATURE:BIRD_VULTURE:FATjCREATURE:BIRD_VULTURE:TALLOWjCREATURE:VULTURE_MAN:FATjCREATURE:VULTURE_MAN:TALLOWjCREATURE:GIANT_VULTURE:FATjCREATURE:GIANT_VULTURE:TALLOWjCREATURE:RHINOCEROS:FATjCREATURE:RHINOCEROS:TALLOWjCREATURE:RHINOCEROS_MAN:FATjCREATURE:RHINOCEROS_MAN:TALLOWjCREATURE:GIANT_RHINOCEROS:FATj CREATURE:GIANT_RHINOCEROS:TALLOWjCREATURE:GIRAFFE:FATjCREATURE:GIRAFFE:TALLOWjCREATURE:GIRAFFE_MAN:FATjCREATURE:GIRAFFE_MAN:TALLOWjCREATURE:GIANT_GIRAFFE:FATjCREATURE:GIANT_GIRAFFE:TALLOWjCREATURE:HONEY BADGER:FATjCREATURE:HONEY BADGER:TALLOWjCREATURE:HONEY BADGER MAN:FATj CREATURE:HONEY BADGER MAN:TALLOWj CREATURE:HONEY BADGER, GIANT:FATj#CREATURE:HONEY BADGER, GIANT:TALLOWjCREATURE:GIANT TORTOISE:FATjCREATURE:GIANT TORTOISE:TALLOWjCREATURE:GIANT TORTOISE MAN:FATj"CREATURE:GIANT TORTOISE MAN:TALLOWjCREATURE:GIGANTIC TORTOISE:FATj!CREATURE:GIGANTIC TORTOISE:TALLOWjCREATURE:ARMADILLO:FATjCREATURE:ARMADILLO:TALLOWjCREATURE:ARMADILLO MAN:FATjCREATURE:ARMADILLO MAN:TALLOWjCREATURE:ARMADILLO, GIANT:FATj CREATURE:ARMADILLO, GIANT:TALLOWjCREATURE:MUSKOX:FATjCREATURE:MUSKOX:TALLOWjCREATURE:MUSKOX_MAN:FATjCREATURE:MUSKOX_MAN:TALLOWjCREATURE:GIANT_MUSKOX:FATjCREATURE:GIANT_MUSKOX:TALLOWjCREATURE:ELK:FATjCREATURE:ELK:TALLOWjCREATURE:ELK_MAN:FATjCREATURE:ELK_MAN:TALLOWjCREATURE:GIANT_ELK:FATjCREATURE:GIANT_ELK:TALLOWjCREATURE:BEAR_POLAR:FATjCREATURE:BEAR_POLAR:TALLOWjCREATURE:BEAR_POLAR_MAN:FATjCREATURE:BEAR_POLAR_MAN:TALLOWjCREATURE:GIANT_BEAR_POLAR:FATj CREATURE:GIANT_BEAR_POLAR:TALLOWjCREATURE:WOLVERINE:FATjCREATURE:WOLVERINE:TALLOWjCREATURE:WOLVERINE_MAN:FATjCREATURE:WOLVERINE_MAN:TALLOWjCREATURE:GIANT_WOLVERINE:FATjCREATURE:GIANT_WOLVERINE:TALLOWjCREATURE:CHINCHILLA:FATjCREATURE:CHINCHILLA:TALLOWjCREATURE:CHINCHILLA_MAN:FATjCREATURE:CHINCHILLA_MAN:TALLOWjCREATURE:GIANT_CHINCHILLA:FATj CREATURE:GIANT_CHINCHILLA:TALLOWjCREATURE:FLOATING_GUTS:FATjCREATURE:FLOATING_GUTS:TALLOWjCREATURE:DRUNIAN:FATjCREATURE:DRUNIAN:TALLOWjCREATURE:CREEPING_EYE:FATjCREATURE:CREEPING_EYE:TALLOWj#CREATURE:VORACIOUS_CAVE_CRAWLER:FATj&CREATURE:VORACIOUS_CAVE_CRAWLER:TALLOWjCREATURE:BLIND_CAVE_OGRE:FATjCREATURE:BLIND_CAVE_OGRE:TALLOWjCREATURE:CAP_HOPPER:FATjCREATURE:CAP_HOPPER:TALLOWjCREATURE:MAGMA_CRAB:FATjCREATURE:MAGMA_CRAB:TALLOWjCREATURE:CRUNDLE:FATjCREATURE:CRUNDLE:TALLOWjCREATURE:HUNGRY_HEAD:FATjCREATURE:HUNGRY_HEAD:TALLOWjCREATURE:ELK_BIRD:FATjCREATURE:ELK_BIRD:TALLOWjCREATURE:HELMET_SNAKE:FATjCREATURE:HELMET_SNAKE:TALLOWjCREATURE:GREEN_DEVOURER:FATjCREATURE:GREEN_DEVOURER:TALLOWjCREATURE:RUTHERER:FATjCREATURE:RUTHERER:TALLOWjCREATURE:CREEPY_CRAWLER:FATjCREATURE:CREEPY_CRAWLER:TALLOWjCREATURE:DRALTHA:FATjCREATURE:DRALTHA:TALLOWjCREATURE:GIANT_EARTHWORM:FATjCREATURE:GIANT_EARTHWORM:TALLOWjCREATURE:BUGBAT:FATjCREATURE:BUGBAT:TALLOWjCREATURE:MANERA:FATjCREATURE:MANERA:TALLOWjCREATURE:MOLEMARIAN:FATjCREATURE:MOLEMARIAN:TALLOWjCREATURE:JABBERER:FATjCREATURE:JABBERER:TALLOWjCREATURE:POND_GRABBER:FATjCREATURE:POND_GRABBER:TALLOWjCREATURE:BLIND_CAVE_BEAR:FATjCREATURE:BLIND_CAVE_BEAR:TALLOWjCREATURE:CAVE_DRAGON:FATjCREATURE:CAVE_DRAGON:TALLOWjCREATURE:REACHER:FATjCREATURE:REACHER:TALLOWjCREATURE:GORLAK:FATjCREATURE:GORLAK:TALLOWjCREATURE:OCTOPUS:FATjCREATURE:OCTOPUS:TALLOWjCREATURE:OCTOPUS_MAN:FATjCREATURE:OCTOPUS_MAN:TALLOWjCREATURE:GIANT_OCTOPUS:FATjCREATURE:GIANT_OCTOPUS:TALLOWjCREATURE:CRAB:FATjCREATURE:CRAB:TALLOWjCREATURE:CRAB_MAN:FATjCREATURE:CRAB_MAN:TALLOWjCREATURE:GIANT_CRAB:FATjCREATURE:GIANT_CRAB:TALLOWjCREATURE:LEOPARD_SEAL:FATjCREATURE:LEOPARD_SEAL:TALLOWjCREATURE:LEOPARD_SEAL_MAN:FATj CREATURE:LEOPARD_SEAL_MAN:TALLOWjCREATURE:GIANT_LEOPARD_SEAL:FATj"CREATURE:GIANT_LEOPARD_SEAL:TALLOWjCREATURE:CUTTLEFISH:FATjCREATURE:CUTTLEFISH:TALLOWjCREATURE:CUTTLEFISH_MAN:FATjCREATURE:CUTTLEFISH_MAN:TALLOWjCREATURE:GIANT_CUTTLEFISH:FATj CREATURE:GIANT_CUTTLEFISH:TALLOWjCREATURE:ORCA:FATjCREATURE:ORCA:TALLOWjCREATURE:ORCA_MAN:FATjCREATURE:ORCA_MAN:TALLOWjCREATURE:GIANT_ORCA:FATjCREATURE:GIANT_ORCA:TALLOWjCREATURE:HORSESHOE_CRAB:FATjCREATURE:HORSESHOE_CRAB:TALLOWjCREATURE:HORSESHOE_CRAB_MAN:FATj"CREATURE:HORSESHOE_CRAB_MAN:TALLOWj!CREATURE:GIANT_HORSESHOE_CRAB:FATj$CREATURE:GIANT_HORSESHOE_CRAB:TALLOWjCREATURE:SPERM_WHALE:FATjCREATURE:SPERM_WHALE:TALLOWjCREATURE:SPERM_WHALE_MAN:FATjCREATURE:SPERM_WHALE_MAN:TALLOWjCREATURE:GIANT_SPERM_WHALE:FATj!CREATURE:GIANT_SPERM_WHALE:TALLOWjCREATURE:ELEPHANT_SEAL:FATjCREATURE:ELEPHANT_SEAL:TALLOWjCREATURE:ELEPHANT_SEAL_MAN:FATj!CREATURE:ELEPHANT_SEAL_MAN:TALLOWj CREATURE:GIANT_ELEPHANT_SEAL:FATj#CREATURE:GIANT_ELEPHANT_SEAL:TALLOWjCREATURE:HARP_SEAL:FATjCREATURE:HARP_SEAL:TALLOWjCREATURE:HARP_SEAL_MAN:FATjCREATURE:HARP_SEAL_MAN:TALLOWjCREATURE:GIANT_HARP_SEAL:FATjCREATURE:GIANT_HARP_SEAL:TALLOWjCREATURE:NAUTILUS:FATjCREATURE:NAUTILUS:TALLOWjCREATURE:NAUTILUS_MAN:FATjCREATURE:NAUTILUS_MAN:TALLOWjCREATURE:GIANT_NAUTILUS:FATjCREATURE:GIANT_NAUTILUS:TALLOWjCREATURE:FOXSQUIRREL:FATjCREATURE:FOXSQUIRREL:TALLOWjCREATURE:MOGHOPPER:FATjCREATURE:MOGHOPPER:TALLOWjCREATURE:RAT_DEMON:FATjCREATURE:RAT_DEMON:TALLOWjCREATURE:WAMBLER_FLUFFY:TALLOWjCREATURE:WAMBLER_FLUFFY:FATj$CREATURE:LIZARD_RHINO_TWO_LEGGED:FATj'CREATURE:LIZARD_RHINO_TWO_LEGGED:TALLOWjCREATURE:WORM_KNUCKLE:FATjCREATURE:WORM_KNUCKLE:TALLOWjCREATURE:SPIDER_PHANTOM:FATjCREATURE:SPIDER_PHANTOM:TALLOWjCREATURE:FLY_ACORN:FATjCREATURE:FLY_ACORN:TALLOWjCREATURE:GNAT_BLOOD:FATjCREATURE:GNAT_BLOOD:TALLOWjCREATURE:LIZARD:FATjCREATURE:LIZARD:TALLOWjCREATURE:LIZARD_MAN:FATjCREATURE:LIZARD_MAN:TALLOWjCREATURE:GIANT_LIZARD:FATjCREATURE:GIANT_LIZARD:TALLOWjCREATURE:SKINK:FATjCREATURE:SKINK:TALLOWjCREATURE:SKINK_MAN:FATjCREATURE:SKINK_MAN:TALLOWjCREATURE:GIANT_SKINK:FATjCREATURE:GIANT_SKINK:TALLOWjCREATURE:CHAMELEON:FATjCREATURE:CHAMELEON:TALLOWjCREATURE:CHAMELEON_MAN:FATjCREATURE:CHAMELEON_MAN:TALLOWjCREATURE:GIANT_CHAMELEON:FATjCREATURE:GIANT_CHAMELEON:TALLOWjCREATURE:ANOLE:FATjCREATURE:ANOLE:TALLOWjCREATURE:ANOLE_MAN:FATjCREATURE:ANOLE_MAN:TALLOWjCREATURE:GIANT_ANOLE:FATjCREATURE:GIANT_ANOLE:TALLOWjCREATURE:IGUANA:FATjCREATURE:IGUANA:TALLOWjCREATURE:IGUANA_MAN:FATjCREATURE:IGUANA_MAN:TALLOWjCREATURE:GIANT_IGUANA:FATjCREATURE:GIANT_IGUANA:TALLOWjCREATURE:RIVER OTTER:FATjCREATURE:RIVER OTTER:TALLOWjCREATURE:SEA OTTER:FATjCREATURE:SEA OTTER:TALLOWjCREATURE:OTTER_MAN:FATjCREATURE:OTTER_MAN:TALLOWjCREATURE:GIANT_OTTER:FATjCREATURE:GIANT_OTTER:TALLOWjCREATURE:SNAPPING TURTLE:FATjCREATURE:SNAPPING TURTLE:TALLOWj&CREATURE:ALLIGATOR SNAPPING TURTLE:FATj)CREATURE:ALLIGATOR SNAPPING TURTLE:TALLOWj CREATURE:SNAPPING_TURTLE_MAN:FATj#CREATURE:SNAPPING_TURTLE_MAN:TALLOWj"CREATURE:GIANT_SNAPPING_TURTLE:FATj%CREATURE:GIANT_SNAPPING_TURTLE:TALLOWjCREATURE:BEAVER:FATjCREATURE:BEAVER:TALLOWjCREATURE:BEAVER_MAN:FATjCREATURE:BEAVER_MAN:TALLOWjCREATURE:GIANT_BEAVER:FATjCREATURE:GIANT_BEAVER:TALLOWjCREATURE:LEECH:FATjCREATURE:LEECH:TALLOWjCREATURE:LEECH_MAN:FATjCREATURE:LEECH_MAN:TALLOWjCREATURE:GIANT_LEECH:FATjCREATURE:GIANT_LEECH:TALLOWjCREATURE:AXOLOTL:FATjCREATURE:AXOLOTL:TALLOWjCREATURE:AXOLOTL_MAN:FATjCREATURE:AXOLOTL_MAN:TALLOWjCREATURE:GIANT_AXOLOTL:FATjCREATURE:GIANT_AXOLOTL:TALLOWjCREATURE:MINK:FATjCREATURE:MINK:TALLOWjCREATURE:MINK_MAN:FATjCREATURE:MINK_MAN:TALLOWjCREATURE:GIANT_MINK:FATjCREATURE:GIANT_MINK:TALLOWjCREATURE:POND_TURTLE:FATjCREATURE:POND_TURTLE:TALLOWjCREATURE:POND_TURTLE_MAN:FATjCREATURE:POND_TURTLE_MAN:TALLOWjCREATURE:GIANT_POND_TURTLE:FATj!CREATURE:GIANT_POND_TURTLE:TALLOWjCREATURE:RAT:FATjCREATURE:RAT:TALLOWjCREATURE:RAT_MAN:FATjCREATURE:RAT_MAN:TALLOWjCREATURE:SQUIRREL_GRAY:FATjCREATURE:SQUIRREL_GRAY:TALLOWjCREATURE:SQUIRREL_GRAY_MAN:FATj!CREATURE:SQUIRREL_GRAY_MAN:TALLOWj CREATURE:GIANT_SQUIRREL_GRAY:FATj#CREATURE:GIANT_SQUIRREL_GRAY:TALLOWjCREATURE:SQUIRREL_RED:FATjCREATURE:SQUIRREL_RED:TALLOWjCREATURE:SQUIRREL_RED_MAN:FATj CREATURE:SQUIRREL_RED_MAN:TALLOWjCREATURE:GIANT_SQUIRREL_RED:FATj"CREATURE:GIANT_SQUIRREL_RED:TALLOWjCREATURE:CHIPMUNK:FATjCREATURE:CHIPMUNK:TALLOWjCREATURE:CHIPMUNK_MAN:FATjCREATURE:CHIPMUNK_MAN:TALLOWjCREATURE:GIANT_CHIPMUNK:FATjCREATURE:GIANT_CHIPMUNK:TALLOWjCREATURE:HAMSTER:FATjCREATURE:HAMSTER:TALLOWjCREATURE:HAMSTER_MAN:FATjCREATURE:HAMSTER_MAN:TALLOWjCREATURE:GIANT_HAMSTER:FATjCREATURE:GIANT_HAMSTER:TALLOWjCREATURE:HEDGEHOG:FATjCREATURE:HEDGEHOG:TALLOWjCREATURE:HEDGEHOG_MAN:FATjCREATURE:HEDGEHOG_MAN:TALLOWjCREATURE:GIANT_HEDGEHOG:FATjCREATURE:GIANT_HEDGEHOG:TALLOWjCREATURE:SQUIRREL_FLYING:FATjCREATURE:SQUIRREL_FLYING:TALLOWj CREATURE:FLYING_SQUIRREL_MAN:FATj#CREATURE:FLYING_SQUIRREL_MAN:TALLOWj"CREATURE:GIANT_FLYING_SQUIRREL:FATj%CREATURE:GIANT_FLYING_SQUIRREL:TALLOWjCREATURE:MUSSEL:FATjCREATURE:MUSSEL:TALLOWjCREATURE:OYSTER:FATjCREATURE:OYSTER:TALLOWjCREATURE:FISH_SALMON:FATjCREATURE:FISH_SALMON:TALLOWjCREATURE:FISH_CLOWNFISH:FATjCREATURE:FISH_CLOWNFISH:TALLOWjCREATURE:FISH_HAGFISH:FATjCREATURE:FISH_HAGFISH:TALLOWjCREATURE:FISH_LAMPREY_BROOK:FATj"CREATURE:FISH_LAMPREY_BROOK:TALLOWjCREATURE:FISH_RAY_BAT:FATjCREATURE:FISH_RAY_BAT:TALLOWjCREATURE:FISH_RAY_THORNBACK:FATj"CREATURE:FISH_RAY_THORNBACK:TALLOWj!CREATURE:FISH_RATFISH_SPOTTED:FATj$CREATURE:FISH_RATFISH_SPOTTED:TALLOWjCREATURE:FISH_HERRING:FATjCREATURE:FISH_HERRING:TALLOWjCREATURE:FISH_SHAD:FATjCREATURE:FISH_SHAD:TALLOWjCREATURE:FISH_ANCHOVY:FATjCREATURE:FISH_ANCHOVY:TALLOWj!CREATURE:FISH_TROUT_STEELHEAD:FATj$CREATURE:FISH_TROUT_STEELHEAD:TALLOWjCREATURE:FISH_HAKE:FATjCREATURE:FISH_HAKE:TALLOWjCREATURE:FISH_SEAHORSE:FATjCREATURE:FISH_SEAHORSE:TALLOWjCREATURE:FISH_GLASSEYE:FATjCREATURE:FISH_GLASSEYE:TALLOWj&CREATURE:FISH_PUFFER_WHITE_SPOTTED:FATj)CREATURE:FISH_PUFFER_WHITE_SPOTTED:TALLOWjCREATURE:FISH_SOLE:FATjCREATURE:FISH_SOLE:TALLOWjCREATURE:FISH_FLOUNDER:FATjCREATURE:FISH_FLOUNDER:TALLOWjCREATURE:FISH_MACKEREL:FATjCREATURE:FISH_MACKEREL:TALLOWj!CREATURE:JELLYFISH_SEA_NETTLE:FATj$CREATURE:JELLYFISH_SEA_NETTLE:TALLOWjCREATURE:SQUID:FATjCREATURE:SQUID:TALLOWjCREATURE:SQUID MAN:FATjCREATURE:SQUID MAN:TALLOWjCREATURE:GIGANTIC SQUID:FATjCREATURE:GIGANTIC SQUID:TALLOWjCREATURE:FISH_LUNGFISH:FATjCREATURE:FISH_LUNGFISH:TALLOWjCREATURE:FISH_LOACH_CLOWN:FATj CREATURE:FISH_LOACH_CLOWN:TALLOWj CREATURE:FISH_BULLHEAD_BROWN:FATj#CREATURE:FISH_BULLHEAD_BROWN:TALLOWj!CREATURE:FISH_BULLHEAD_YELLOW:FATj$CREATURE:FISH_BULLHEAD_YELLOW:TALLOWj CREATURE:FISH_BULLHEAD_BLACK:FATj#CREATURE:FISH_BULLHEAD_BLACK:TALLOWj"CREATURE:FISH_KNIFEFISH_BANDED:FATj%CREATURE:FISH_KNIFEFISH_BANDED:TALLOWjCREATURE:FISH_CHAR:FATjCREATURE:FISH_CHAR:TALLOWjCREATURE:FISH_TROUT_RAINBOW:FATj"CREATURE:FISH_TROUT_RAINBOW:TALLOWjCREATURE:FISH_MOLLY_SAILFIN:FATj"CREATURE:FISH_MOLLY_SAILFIN:TALLOWjCREATURE:FISH_GUPPY:FATjCREATURE:FISH_GUPPY:TALLOWjCREATURE:FISH_PERCH:FATjCREATURE:FISH_PERCH:TALLOWjCREATURE:DWARF:FATjCREATURE:DWARF:TALLOWjCREATURE:HUMAN:FATjCREATURE:HUMAN:TALLOWjCREATURE:ELF:FATjCREATURE:ELF:TALLOWjCREATURE:GOBLIN:FATjCREATURE:GOBLIN:TALLOWjCREATURE:KOBOLD:FATjCREATURE:KOBOLD:TALLOWjCREATURE:GREMLIN:FATjCREATURE:GREMLIN:TALLOWjCREATURE:TROLL:FATjCREATURE:TROLL:TALLOWjCREATURE:OGRE:FATjCREATURE:OGRE:TALLOWjCREATURE:UNICORN:FATjCREATURE:UNICORN:TALLOWjCREATURE:DRAGON:FATjCREATURE:DRAGON:TALLOWjCREATURE:SATYR:FATjCREATURE:SATYR:TALLOWjCREATURE:GIANT:FATjCREATURE:GIANT:TALLOWjCREATURE:CYCLOPS:FATjCREATURE:CYCLOPS:TALLOWjCREATURE:ETTIN:FATjCREATURE:ETTIN:TALLOWjCREATURE:MINOTAUR:FATjCREATURE:MINOTAUR:TALLOWjCREATURE:YETI:FATjCREATURE:YETI:TALLOWjCREATURE:SASQUATCH:FATjCREATURE:SASQUATCH:TALLOWjCREATURE:BLIZZARD_MAN:FATjCREATURE:BLIZZARD_MAN:TALLOWjCREATURE:WOLF_ICE:FATjCREATURE:WOLF_ICE:TALLOWjCREATURE:FAIRY:FATjCREATURE:FAIRY:TALLOWjCREATURE:PIXIE:FATjCREATURE:PIXIE:TALLOWjCREATURE:BEAK_DOG:FATjCREATURE:BEAK_DOG:TALLOWjCREATURE:GRIMELING:FATjCREATURE:GRIMELING:TALLOWjCREATURE:BLENDEC_FOUL:FATjCREATURE:BLENDEC_FOUL:TALLOWjCREATURE:STRANGLER:FATjCREATURE:STRANGLER:TALLOWjCREATURE:NIGHTWING:FATjCREATURE:NIGHTWING:TALLOWjCREATURE:HARPY:FATjCREATURE:HARPY:TALLOWjCREATURE:HYDRA:FATjCREATURE:HYDRA:TALLOWjCREATURE:MERPERSON:FATjCREATURE:MERPERSON:TALLOWjCREATURE:SEA_SERPENT:FATjCREATURE:SEA_SERPENT:TALLOWjCREATURE:SEA_MONSTER:FATjCREATURE:SEA_MONSTER:TALLOWjCREATURE:BIRD_ROC:FATjCREATURE:BIRD_ROC:TALLOWjCREATURE:CROCODILE_CAVE:FATjCREATURE:CROCODILE_CAVE:TALLOWjCREATURE:TOAD_GIANT_CAVE:FATjCREATURE:TOAD_GIANT_CAVE:TALLOWjCREATURE:OLM_GIANT:FATjCREATURE:OLM_GIANT:TALLOWjCREATURE:BAT_GIANT:FATjCREATURE:BAT_GIANT:TALLOWjCREATURE:RAT_GIANT:FATjCREATURE:RAT_GIANT:TALLOWjCREATURE:RAT_LARGE:FATjCREATURE:RAT_LARGE:TALLOWjCREATURE:MOLE_DOG_NAKED:FATjCREATURE:MOLE_DOG_NAKED:TALLOWjCREATURE:TROGLODYTE:FATjCREATURE:TROGLODYTE:TALLOWjCREATURE:MOLE_GIANT:FATjCREATURE:MOLE_GIANT:TALLOWjCREATURE:IMP_FIRE:FATjCREATURE:IMP_FIRE:TALLOWjCREATURE:SPIDER_CAVE_GIANT:FATj!CREATURE:SPIDER_CAVE_GIANT:TALLOWjCREATURE:SPIDER_CAVE:FATjCREATURE:SPIDER_CAVE:TALLOWjCREATURE:FISH_CAVE:FATjCREATURE:FISH_CAVE:TALLOWjCREATURE:CAVE_FISH_MAN:FATjCREATURE:CAVE_FISH_MAN:TALLOWjCREATURE:LOBSTER_CAVE:FATjCREATURE:LOBSTER_CAVE:TALLOWjCREATURE:OLM:FATjCREATURE:OLM:TALLOWjCREATURE:OLM_MAN:FATjCREATURE:OLM_MAN:TALLOWjCREATURE:BAT:FATjCREATURE:BAT:TALLOWjCREATURE:BAT_MAN:FATjCREATURE:BAT_MAN:TALLOWjCREATURE:MAGGOT_PURRING:FATjCREATURE:MAGGOT_PURRING:TALLOWjCREATURE:BIRD_SWALLOW_CAVE:FATj!CREATURE:BIRD_SWALLOW_CAVE:TALLOWjCREATURE:CAVE_SWALLOW_MAN:FATj CREATURE:CAVE_SWALLOW_MAN:TALLOWj$CREATURE:BIRD_SWALLOW_CAVE_GIANT:FATj'CREATURE:BIRD_SWALLOW_CAVE_GIANT:TALLOWjCREATURE:AMPHIBIAN_MAN:FATjCREATURE:AMPHIBIAN_MAN:TALLOWjCREATURE:REPTILE_MAN:FATjCREATURE:REPTILE_MAN:TALLOWjCREATURE:SERPENT_MAN:FATjCREATURE:SERPENT_MAN:TALLOWjCREATURE:ANT_MAN:FATjCREATURE:ANT_MAN:TALLOWjCREATURE:RODENT MAN:FATjCREATURE:RODENT MAN:TALLOWjCREATURE:WILD_BOAR:FATjCREATURE:WILD_BOAR:TALLOWjCREATURE:WILD_BOAR_MAN:FATjCREATURE:WILD_BOAR_MAN:TALLOWjCREATURE:GIANT_WILD_BOAR:FATjCREATURE:GIANT_WILD_BOAR:TALLOWjCREATURE:COYOTE:FATjCREATURE:COYOTE:TALLOWjCREATURE:COYOTE_MAN:FATjCREATURE:COYOTE_MAN:TALLOWjCREATURE:GIANT_COYOTE:FATjCREATURE:GIANT_COYOTE:TALLOWjCREATURE:KANGAROO:FATjCREATURE:KANGAROO:TALLOWjCREATURE:KANGAROO_MAN:FATjCREATURE:KANGAROO_MAN:TALLOWjCREATURE:GIANT_KANGAROO:FATjCREATURE:GIANT_KANGAROO:TALLOWjCREATURE:KOALA:FATjCREATURE:KOALA:TALLOWjCREATURE:KOALA_MAN:FATjCREATURE:KOALA_MAN:TALLOWjCREATURE:GIANT_KOALA:FATjCREATURE:GIANT_KOALA:TALLOWjCREATURE:ADDER:FATjCREATURE:ADDER:TALLOWjCREATURE:ADDER_MAN:FATjCREATURE:ADDER_MAN:TALLOWjCREATURE:GIANT_ADDER:FATjCREATURE:GIANT_ADDER:TALLOWjCREATURE:ECHIDNA:FATjCREATURE:ECHIDNA:TALLOWjCREATURE:ECHIDNA_MAN:FATjCREATURE:ECHIDNA_MAN:TALLOWjCREATURE:GIANT_ECHIDNA:FATjCREATURE:GIANT_ECHIDNA:TALLOWjCREATURE:PORCUPINE:FATjCREATURE:PORCUPINE:TALLOWjCREATURE:PORCUPINE_MAN:FATjCREATURE:PORCUPINE_MAN:TALLOWjCREATURE:GIANT_PORCUPINE:FATjCREATURE:GIANT_PORCUPINE:TALLOWjCREATURE:KINGSNAKE:FATjCREATURE:KINGSNAKE:TALLOWjCREATURE:KINGSNAKE_MAN:FATjCREATURE:KINGSNAKE_MAN:TALLOWjCREATURE:GIANT_KINGSNAKE:FATjCREATURE:GIANT_KINGSNAKE:TALLOWjCREATURE:GRAY_LANGUR:FATjCREATURE:GRAY_LANGUR:TALLOWjCREATURE:GRAY_LANGUR_MAN:FATjCREATURE:GRAY_LANGUR_MAN:TALLOWjCREATURE:GIANT_GRAY_LANGUR:FATj!CREATURE:GIANT_GRAY_LANGUR:TALLOWjCREATURE:BOBCAT:FATjCREATURE:BOBCAT:TALLOWjCREATURE:BOBCAT_MAN:FATjCREATURE:BOBCAT_MAN:TALLOWjCREATURE:GIANT_BOBCAT:FATjCREATURE:GIANT_BOBCAT:TALLOWjCREATURE:SKUNK:FATjCREATURE:SKUNK:TALLOWjCREATURE:SKUNK_MAN:FATjCREATURE:SKUNK_MAN:TALLOWjCREATURE:GIANT_SKUNK:FATjCREATURE:GIANT_SKUNK:TALLOWjCREATURE:GREEN_TREE_FROG:FATjCREATURE:GREEN_TREE_FROG:TALLOWj CREATURE:GREEN_TREE_FROG_MAN:FATj#CREATURE:GREEN_TREE_FROG_MAN:TALLOWj"CREATURE:GIANT_GREEN_TREE_FROG:FATj%CREATURE:GIANT_GREEN_TREE_FROG:TALLOWjCREATURE:HARE:FATjCREATURE:HARE:TALLOWjCREATURE:HARE_MAN:FATjCREATURE:HARE_MAN:TALLOWjCREATURE:GIANT_HARE:FATjCREATURE:GIANT_HARE:TALLOWjCREATURE:RATTLESNAKE:FATjCREATURE:RATTLESNAKE:TALLOWjCREATURE:RATTLESNAKE_MAN:FATjCREATURE:RATTLESNAKE_MAN:TALLOWjCREATURE:GIANT_RATTLESNAKE:FATj!CREATURE:GIANT_RATTLESNAKE:TALLOWjCREATURE:WEASEL:FATjCREATURE:WEASEL:TALLOWjCREATURE:WEASEL_MAN:FATjCREATURE:WEASEL_MAN:TALLOWjCREATURE:GIANT_WEASEL:FATjCREATURE:GIANT_WEASEL:TALLOWjCREATURE:COPPERHEAD_SNAKE:FATj CREATURE:COPPERHEAD_SNAKE:TALLOWj!CREATURE:COPPERHEAD_SNAKE_MAN:FATj$CREATURE:COPPERHEAD_SNAKE_MAN:TALLOWj#CREATURE:GIANT_COPPERHEAD_SNAKE:FATj&CREATURE:GIANT_COPPERHEAD_SNAKE:TALLOWjCREATURE:IBEX:FATjCREATURE:IBEX:TALLOWjCREATURE:IBEX_MAN:FATjCREATURE:IBEX_MAN:TALLOWjCREATURE:GIANT_IBEX:FATjCREATURE:GIANT_IBEX:TALLOWjCREATURE:WOMBAT:FATjCREATURE:WOMBAT:TALLOWjCREATURE:WOMBAT_MAN:FATjCREATURE:WOMBAT_MAN:TALLOWjCREATURE:GIANT_WOMBAT:FATjCREATURE:GIANT_WOMBAT:TALLOWjCREATURE:DINGO:FATjCREATURE:DINGO:TALLOWjCREATURE:DINGO_MAN:FATjCREATURE:DINGO_MAN:TALLOWjCREATURE:GIANT_DINGO:FATjCREATURE:GIANT_DINGO:TALLOWjCREATURE:COATI:FATjCREATURE:COATI:TALLOWjCREATURE:COATI_MAN:FATjCREATURE:COATI_MAN:TALLOWjCREATURE:GIANT_COATI:FATjCREATURE:GIANT_COATI:TALLOWjCREATURE:OPOSSUM:FATjCREATURE:OPOSSUM:TALLOWjCREATURE:OPOSSUM_MAN:FATjCREATURE:OPOSSUM_MAN:TALLOWjCREATURE:GIANT_OPOSSUM:FATjCREATURE:GIANT_OPOSSUM:TALLOWjCREATURE:MONGOOSE:FATjCREATURE:MONGOOSE:TALLOWjCREATURE:MONGOOSE_MAN:FATjCREATURE:MONGOOSE_MAN:TALLOWjCREATURE:GIANT_MONGOOSE:FATjCREATURE:GIANT_MONGOOSE:TALLOWjCREATURE:HYENA:FATjCREATURE:HYENA:TALLOWjCREATURE:HYENA_MAN:FATjCREATURE:HYENA_MAN:TALLOWjCREATURE:GIANT_HYENA:FATjCREATURE:GIANT_HYENA:TALLOWjCREATURE:ANACONDA:FATjCREATURE:ANACONDA:TALLOWjCREATURE:ANACONDA_MAN:FATjCREATURE:ANACONDA_MAN:TALLOWjCREATURE:GIANT_ANACONDA:FATjCREATURE:GIANT_ANACONDA:TALLOWjCREATURE:MONITOR_LIZARD:FATjCREATURE:MONITOR_LIZARD:TALLOWjCREATURE:MONITOR_LIZARD_MAN:FATj"CREATURE:MONITOR_LIZARD_MAN:TALLOWj!CREATURE:GIANT_MONITOR_LIZARD:FATj$CREATURE:GIANT_MONITOR_LIZARD:TALLOWjCREATURE:KING_COBRA:FATjCREATURE:KING_COBRA:TALLOWjCREATURE:KING_COBRA_MAN:FATjCREATURE:KING_COBRA_MAN:TALLOWjCREATURE:GIANT_KING_COBRA:FATj CREATURE:GIANT_KING_COBRA:TALLOWjCREATURE:OCELOT:FATjCREATURE:OCELOT:TALLOWjCREATURE:OCELOT_MAN:FATjCREATURE:OCELOT_MAN:TALLOWjCREATURE:GIANT_OCELOT:FATjCREATURE:GIANT_OCELOT:TALLOWjCREATURE:JACKAL:FATjCREATURE:JACKAL:TALLOWjCREATURE:JACKAL_MAN:FATjCREATURE:JACKAL_MAN:TALLOWjCREATURE:GIANT_JACKAL:FATjCREATURE:GIANT_JACKAL:TALLOWjCREATURE:CAPUCHIN:FATjCREATURE:CAPUCHIN:TALLOWjCREATURE:CAPUCHIN_MAN:FATjCREATURE:CAPUCHIN_MAN:TALLOWjCREATURE:GIANT_CAPUCHIN:FATjCREATURE:GIANT_CAPUCHIN:TALLOWjCREATURE:SLOTH:FATjCREATURE:SLOTH:TALLOWjCREATURE:SLOTH_MAN:FATjCREATURE:SLOTH_MAN:TALLOWjCREATURE:GIANT_SLOTH:FATjCREATURE:GIANT_SLOTH:TALLOWjCREATURE:SPIDER_MONKEY:FATjCREATURE:SPIDER_MONKEY:TALLOWjCREATURE:SPIDER_MONKEY_MAN:FATj!CREATURE:SPIDER_MONKEY_MAN:TALLOWj CREATURE:GIANT_SPIDER_MONKEY:FATj#CREATURE:GIANT_SPIDER_MONKEY:TALLOWjCREATURE:PANGOLIN:FATjCREATURE:PANGOLIN:TALLOWjCREATURE:PANGOLIN_MAN:FATjCREATURE:PANGOLIN_MAN:TALLOWjCREATURE:GIANT_PANGOLIN:FATjCREATURE:GIANT_PANGOLIN:TALLOWjCREATURE:BLACK_MAMBA:FATjCREATURE:BLACK_MAMBA:TALLOWjCREATURE:BLACK_MAMBA_MAN:FATjCREATURE:BLACK_MAMBA_MAN:TALLOWjCREATURE:GIANT_BLACK_MAMBA:FATj!CREATURE:GIANT_BLACK_MAMBA:TALLOWjCREATURE:BEAR_SLOTH:FATjCREATURE:BEAR_SLOTH:TALLOWjCREATURE:SLOTH_BEAR_MAN:FATjCREATURE:SLOTH_BEAR_MAN:TALLOWjCREATURE:GIANT_SLOTH_BEAR:FATj CREATURE:GIANT_SLOTH_BEAR:TALLOWjCREATURE:AYE-AYE:FATjCREATURE:AYE-AYE:TALLOWjCREATURE:AYE-AYE_MAN:FATjCREATURE:AYE-AYE_MAN:TALLOWjCREATURE:GIANT_AYE-AYE:FATjCREATURE:GIANT_AYE-AYE:TALLOWjCREATURE:BUSHMASTER:FATjCREATURE:BUSHMASTER:TALLOWjCREATURE:BUSHMASTER_MAN:FATjCREATURE:BUSHMASTER_MAN:TALLOWjCREATURE:GIANT_BUSHMASTER:FATj CREATURE:GIANT_BUSHMASTER:TALLOWjCREATURE:PYTHON:FATjCREATURE:PYTHON:TALLOWjCREATURE:PYTHON_MAN:FATjCREATURE:PYTHON_MAN:TALLOWjCREATURE:GIANT_PYTHON:FATjCREATURE:GIANT_PYTHON:TALLOWjCREATURE:TAPIR:FATjCREATURE:TAPIR:TALLOWjCREATURE:TAPIR_MAN:FATjCREATURE:TAPIR_MAN:TALLOWjCREATURE:GIANT_TAPIR:FATjCREATURE:GIANT_TAPIR:TALLOWjCREATURE:IMPALA:FATjCREATURE:IMPALA:TALLOWjCREATURE:IMPALA_MAN:FATjCREATURE:IMPALA_MAN:TALLOWjCREATURE:GIANT_IMPALA:FATjCREATURE:GIANT_IMPALA:TALLOWjCREATURE:AARDVARK:FATjCREATURE:AARDVARK:TALLOWjCREATURE:AARDVARK_MAN:FATjCREATURE:AARDVARK_MAN:TALLOWjCREATURE:GIANT_AARDVARK:FATjCREATURE:GIANT_AARDVARK:TALLOWjCREATURE:LION_TAMARIN:FATjCREATURE:LION_TAMARIN:TALLOWjCREATURE:LION_TAMARIN_MAN:FATj CREATURE:LION_TAMARIN_MAN:TALLOWjCREATURE:GIANT_LION_TAMARIN:FATj"CREATURE:GIANT_LION_TAMARIN:TALLOWjCREATURE:STOAT:FATjCREATURE:STOAT:TALLOWjCREATURE:STOAT_MAN:FATjCREATURE:STOAT_MAN:TALLOWjCREATURE:GIANT_STOAT:FATjCREATURE:GIANT_STOAT:TALLOWjCREATURE:LYNX:FATjCREATURE:LYNX:TALLOWjCREATURE:LYNX_MAN:FATjCREATURE:LYNX_MAN:TALLOWjCREATURE:GIANT_LYNX:FATjCREATURE:GIANT_LYNX:TALLOWjCREATURE:GNOLL:FATjCREATURE:GNOLL:TALLOWjCREATURE:NAGA:FATjCREATURE:NAGA:TALLOWjCREATURE:FORGOTTEN_BEAST_2:FATj!CREATURE:FORGOTTEN_BEAST_2:TALLOWjCREATURE:FORGOTTEN_BEAST_4:FATj!CREATURE:FORGOTTEN_BEAST_4:TALLOWjCREATURE:FORGOTTEN_BEAST_5:FATj!CREATURE:FORGOTTEN_BEAST_5:TALLOWjCREATURE:FORGOTTEN_BEAST_6:FATj!CREATURE:FORGOTTEN_BEAST_6:TALLOWjCREATURE:FORGOTTEN_BEAST_7:FATj!CREATURE:FORGOTTEN_BEAST_7:TALLOWjCREATURE:FORGOTTEN_BEAST_10:FATj"CREATURE:FORGOTTEN_BEAST_10:TALLOWjCREATURE:FORGOTTEN_BEAST_12:FATj"CREATURE:FORGOTTEN_BEAST_12:TALLOWjCREATURE:FORGOTTEN_BEAST_13:FATj"CREATURE:FORGOTTEN_BEAST_13:TALLOWjCREATURE:FORGOTTEN_BEAST_16:FATj"CREATURE:FORGOTTEN_BEAST_16:TALLOWjCREATURE:FORGOTTEN_BEAST_17:FATj"CREATURE:FORGOTTEN_BEAST_17:TALLOWjCREATURE:FORGOTTEN_BEAST_18:FATj"CREATURE:FORGOTTEN_BEAST_18:TALLOWjCREATURE:FORGOTTEN_BEAST_19:FATj"CREATURE:FORGOTTEN_BEAST_19:TALLOWjCREATURE:FORGOTTEN_BEAST_20:FATj"CREATURE:FORGOTTEN_BEAST_20:TALLOWjCREATURE:FORGOTTEN_BEAST_22:FATj"CREATURE:FORGOTTEN_BEAST_22:TALLOWjCREATURE:FORGOTTEN_BEAST_23:FATj"CREATURE:FORGOTTEN_BEAST_23:TALLOWjCREATURE:FORGOTTEN_BEAST_24:FATj"CREATURE:FORGOTTEN_BEAST_24:TALLOWjCREATURE:FORGOTTEN_BEAST_25:FATj"CREATURE:FORGOTTEN_BEAST_25:TALLOWjCREATURE:FORGOTTEN_BEAST_26:FATj"CREATURE:FORGOTTEN_BEAST_26:TALLOWjCREATURE:FORGOTTEN_BEAST_27:FATj"CREATURE:FORGOTTEN_BEAST_27:TALLOWjCREATURE:FORGOTTEN_BEAST_28:FATj"CREATURE:FORGOTTEN_BEAST_28:TALLOWjCREATURE:FORGOTTEN_BEAST_29:FATj"CREATURE:FORGOTTEN_BEAST_29:TALLOWjCREATURE:FORGOTTEN_BEAST_32:FATj"CREATURE:FORGOTTEN_BEAST_32:TALLOWjCREATURE:FORGOTTEN_BEAST_33:FATj"CREATURE:FORGOTTEN_BEAST_33:TALLOWjCREATURE:FORGOTTEN_BEAST_34:FATj"CREATURE:FORGOTTEN_BEAST_34:TALLOWjCREATURE:FORGOTTEN_BEAST_35:FATj"CREATURE:FORGOTTEN_BEAST_35:TALLOWjCREATURE:FORGOTTEN_BEAST_36:FATj"CREATURE:FORGOTTEN_BEAST_36:TALLOWjCREATURE:FORGOTTEN_BEAST_39:FATj"CREATURE:FORGOTTEN_BEAST_39:TALLOWjCREATURE:FORGOTTEN_BEAST_41:FATj"CREATURE:FORGOTTEN_BEAST_41:TALLOWjCREATURE:FORGOTTEN_BEAST_42:FATj"CREATURE:FORGOTTEN_BEAST_42:TALLOWjCREATURE:FORGOTTEN_BEAST_43:FATj"CREATURE:FORGOTTEN_BEAST_43:TALLOWjCREATURE:FORGOTTEN_BEAST_44:FATj"CREATURE:FORGOTTEN_BEAST_44:TALLOWjCREATURE:FORGOTTEN_BEAST_45:FATj"CREATURE:FORGOTTEN_BEAST_45:TALLOWjCREATURE:FORGOTTEN_BEAST_47:FATj"CREATURE:FORGOTTEN_BEAST_47:TALLOWjCREATURE:FORGOTTEN_BEAST_50:FATj"CREATURE:FORGOTTEN_BEAST_50:TALLOWjCREATURE:FORGOTTEN_BEAST_52:FATj"CREATURE:FORGOTTEN_BEAST_52:TALLOWjCREATURE:FORGOTTEN_BEAST_53:FATj"CREATURE:FORGOTTEN_BEAST_53:TALLOWjCREATURE:FORGOTTEN_BEAST_55:FATj"CREATURE:FORGOTTEN_BEAST_55:TALLOWjCREATURE:FORGOTTEN_BEAST_56:FATj"CREATURE:FORGOTTEN_BEAST_56:TALLOWjCREATURE:FORGOTTEN_BEAST_58:FATj"CREATURE:FORGOTTEN_BEAST_58:TALLOWjCREATURE:FORGOTTEN_BEAST_59:FATj"CREATURE:FORGOTTEN_BEAST_59:TALLOWjCREATURE:FORGOTTEN_BEAST_60:FATj"CREATURE:FORGOTTEN_BEAST_60:TALLOWjCREATURE:FORGOTTEN_BEAST_61:FATj"CREATURE:FORGOTTEN_BEAST_61:TALLOWjCREATURE:FORGOTTEN_BEAST_64:FATj"CREATURE:FORGOTTEN_BEAST_64:TALLOWjCREATURE:FORGOTTEN_BEAST_66:FATj"CREATURE:FORGOTTEN_BEAST_66:TALLOWjCREATURE:FORGOTTEN_BEAST_67:FATj"CREATURE:FORGOTTEN_BEAST_67:TALLOWjCREATURE:FORGOTTEN_BEAST_69:FATj"CREATURE:FORGOTTEN_BEAST_69:TALLOWjCREATURE:FORGOTTEN_BEAST_71:FATj"CREATURE:FORGOTTEN_BEAST_71:TALLOWjCREATURE:FORGOTTEN_BEAST_72:FATj"CREATURE:FORGOTTEN_BEAST_72:TALLOWjCREATURE:FORGOTTEN_BEAST_73:FATj"CREATURE:FORGOTTEN_BEAST_73:TALLOWjCREATURE:FORGOTTEN_BEAST_74:FATj"CREATURE:FORGOTTEN_BEAST_74:TALLOWjCREATURE:FORGOTTEN_BEAST_75:FATj"CREATURE:FORGOTTEN_BEAST_75:TALLOWjCREATURE:FORGOTTEN_BEAST_78:FATj"CREATURE:FORGOTTEN_BEAST_78:TALLOWjCREATURE:FORGOTTEN_BEAST_80:FATj"CREATURE:FORGOTTEN_BEAST_80:TALLOWjCREATURE:FORGOTTEN_BEAST_81:FATj"CREATURE:FORGOTTEN_BEAST_81:TALLOWjCREATURE:FORGOTTEN_BEAST_82:FATj"CREATURE:FORGOTTEN_BEAST_82:TALLOWjCREATURE:FORGOTTEN_BEAST_83:FATj"CREATURE:FORGOTTEN_BEAST_83:TALLOWjCREATURE:FORGOTTEN_BEAST_84:FATj"CREATURE:FORGOTTEN_BEAST_84:TALLOWjCREATURE:FORGOTTEN_BEAST_86:FATj"CREATURE:FORGOTTEN_BEAST_86:TALLOWjCREATURE:FORGOTTEN_BEAST_87:FATj"CREATURE:FORGOTTEN_BEAST_87:TALLOWjCREATURE:FORGOTTEN_BEAST_89:FATj"CREATURE:FORGOTTEN_BEAST_89:TALLOWjCREATURE:FORGOTTEN_BEAST_90:FATj"CREATURE:FORGOTTEN_BEAST_90:TALLOWjCREATURE:FORGOTTEN_BEAST_92:FATj"CREATURE:FORGOTTEN_BEAST_92:TALLOWjCREATURE:FORGOTTEN_BEAST_94:FATj"CREATURE:FORGOTTEN_BEAST_94:TALLOWjCREATURE:FORGOTTEN_BEAST_95:FATj"CREATURE:FORGOTTEN_BEAST_95:TALLOWjCREATURE:FORGOTTEN_BEAST_96:FATj"CREATURE:FORGOTTEN_BEAST_96:TALLOWjCREATURE:FORGOTTEN_BEAST_97:FATj"CREATURE:FORGOTTEN_BEAST_97:TALLOWjCREATURE:FORGOTTEN_BEAST_98:FATj"CREATURE:FORGOTTEN_BEAST_98:TALLOWj CREATURE:FORGOTTEN_BEAST_100:FATj#CREATURE:FORGOTTEN_BEAST_100:TALLOWj CREATURE:FORGOTTEN_BEAST_105:FATj#CREATURE:FORGOTTEN_BEAST_105:TALLOWj CREATURE:FORGOTTEN_BEAST_106:FATj#CREATURE:FORGOTTEN_BEAST_106:TALLOWj CREATURE:FORGOTTEN_BEAST_107:FATj#CREATURE:FORGOTTEN_BEAST_107:TALLOWj CREATURE:FORGOTTEN_BEAST_108:FATj#CREATURE:FORGOTTEN_BEAST_108:TALLOWj CREATURE:FORGOTTEN_BEAST_109:FATj#CREATURE:FORGOTTEN_BEAST_109:TALLOWj CREATURE:FORGOTTEN_BEAST_111:FATj#CREATURE:FORGOTTEN_BEAST_111:TALLOWj CREATURE:FORGOTTEN_BEAST_112:FATj#CREATURE:FORGOTTEN_BEAST_112:TALLOWj CREATURE:FORGOTTEN_BEAST_113:FATj#CREATURE:FORGOTTEN_BEAST_113:TALLOWj CREATURE:FORGOTTEN_BEAST_114:FATj#CREATURE:FORGOTTEN_BEAST_114:TALLOWj CREATURE:FORGOTTEN_BEAST_115:FATj#CREATURE:FORGOTTEN_BEAST_115:TALLOWj CREATURE:FORGOTTEN_BEAST_116:FATj#CREATURE:FORGOTTEN_BEAST_116:TALLOWj CREATURE:FORGOTTEN_BEAST_117:FATj#CREATURE:FORGOTTEN_BEAST_117:TALLOWj CREATURE:FORGOTTEN_BEAST_118:FATj#CREATURE:FORGOTTEN_BEAST_118:TALLOWj CREATURE:FORGOTTEN_BEAST_119:FATj#CREATURE:FORGOTTEN_BEAST_119:TALLOWj CREATURE:FORGOTTEN_BEAST_120:FATj#CREATURE:FORGOTTEN_BEAST_120:TALLOWj CREATURE:FORGOTTEN_BEAST_122:FATj#CREATURE:FORGOTTEN_BEAST_122:TALLOWj CREATURE:FORGOTTEN_BEAST_123:FATj#CREATURE:FORGOTTEN_BEAST_123:TALLOWj CREATURE:FORGOTTEN_BEAST_124:FATj#CREATURE:FORGOTTEN_BEAST_124:TALLOWj CREATURE:FORGOTTEN_BEAST_125:FATj#CREATURE:FORGOTTEN_BEAST_125:TALLOWj CREATURE:FORGOTTEN_BEAST_127:FATj#CREATURE:FORGOTTEN_BEAST_127:TALLOWj CREATURE:FORGOTTEN_BEAST_128:FATj#CREATURE:FORGOTTEN_BEAST_128:TALLOWj CREATURE:FORGOTTEN_BEAST_130:FATj#CREATURE:FORGOTTEN_BEAST_130:TALLOWj CREATURE:FORGOTTEN_BEAST_131:FATj#CREATURE:FORGOTTEN_BEAST_131:TALLOWj CREATURE:FORGOTTEN_BEAST_132:FATj#CREATURE:FORGOTTEN_BEAST_132:TALLOWj CREATURE:FORGOTTEN_BEAST_133:FATj#CREATURE:FORGOTTEN_BEAST_133:TALLOWj CREATURE:FORGOTTEN_BEAST_134:FATj#CREATURE:FORGOTTEN_BEAST_134:TALLOWj CREATURE:FORGOTTEN_BEAST_135:FATj#CREATURE:FORGOTTEN_BEAST_135:TALLOWj CREATURE:FORGOTTEN_BEAST_137:FATj#CREATURE:FORGOTTEN_BEAST_137:TALLOWj CREATURE:FORGOTTEN_BEAST_138:FATj#CREATURE:FORGOTTEN_BEAST_138:TALLOWj CREATURE:FORGOTTEN_BEAST_139:FATj#CREATURE:FORGOTTEN_BEAST_139:TALLOWj CREATURE:FORGOTTEN_BEAST_141:FATj#CREATURE:FORGOTTEN_BEAST_141:TALLOWj CREATURE:FORGOTTEN_BEAST_142:FATj#CREATURE:FORGOTTEN_BEAST_142:TALLOWj CREATURE:FORGOTTEN_BEAST_144:FATj#CREATURE:FORGOTTEN_BEAST_144:TALLOWj CREATURE:FORGOTTEN_BEAST_146:FATj#CREATURE:FORGOTTEN_BEAST_146:TALLOWj CREATURE:FORGOTTEN_BEAST_148:FATj#CREATURE:FORGOTTEN_BEAST_148:TALLOWj CREATURE:FORGOTTEN_BEAST_149:FATj#CREATURE:FORGOTTEN_BEAST_149:TALLOWj CREATURE:FORGOTTEN_BEAST_150:FATj#CREATURE:FORGOTTEN_BEAST_150:TALLOWj CREATURE:FORGOTTEN_BEAST_152:FATj#CREATURE:FORGOTTEN_BEAST_152:TALLOWj CREATURE:FORGOTTEN_BEAST_154:FATj#CREATURE:FORGOTTEN_BEAST_154:TALLOWj CREATURE:FORGOTTEN_BEAST_156:FATj#CREATURE:FORGOTTEN_BEAST_156:TALLOWj CREATURE:FORGOTTEN_BEAST_157:FATj#CREATURE:FORGOTTEN_BEAST_157:TALLOWj CREATURE:FORGOTTEN_BEAST_158:FATj#CREATURE:FORGOTTEN_BEAST_158:TALLOWj CREATURE:FORGOTTEN_BEAST_159:FATj#CREATURE:FORGOTTEN_BEAST_159:TALLOWj CREATURE:FORGOTTEN_BEAST_161:FATj#CREATURE:FORGOTTEN_BEAST_161:TALLOWj CREATURE:FORGOTTEN_BEAST_162:FATj#CREATURE:FORGOTTEN_BEAST_162:TALLOWj CREATURE:FORGOTTEN_BEAST_163:FATj#CREATURE:FORGOTTEN_BEAST_163:TALLOWj CREATURE:FORGOTTEN_BEAST_165:FATj#CREATURE:FORGOTTEN_BEAST_165:TALLOWj CREATURE:FORGOTTEN_BEAST_167:FATj#CREATURE:FORGOTTEN_BEAST_167:TALLOWj CREATURE:FORGOTTEN_BEAST_168:FATj#CREATURE:FORGOTTEN_BEAST_168:TALLOWj CREATURE:FORGOTTEN_BEAST_169:FATj#CREATURE:FORGOTTEN_BEAST_169:TALLOWj CREATURE:FORGOTTEN_BEAST_170:FATj#CREATURE:FORGOTTEN_BEAST_170:TALLOWj CREATURE:FORGOTTEN_BEAST_171:FATj#CREATURE:FORGOTTEN_BEAST_171:TALLOWj CREATURE:FORGOTTEN_BEAST_172:FATj#CREATURE:FORGOTTEN_BEAST_172:TALLOWj CREATURE:FORGOTTEN_BEAST_173:FATj#CREATURE:FORGOTTEN_BEAST_173:TALLOWj CREATURE:FORGOTTEN_BEAST_176:FATj#CREATURE:FORGOTTEN_BEAST_176:TALLOWj CREATURE:FORGOTTEN_BEAST_177:FATj#CREATURE:FORGOTTEN_BEAST_177:TALLOWj CREATURE:FORGOTTEN_BEAST_178:FATj#CREATURE:FORGOTTEN_BEAST_178:TALLOWj CREATURE:FORGOTTEN_BEAST_179:FATj#CREATURE:FORGOTTEN_BEAST_179:TALLOWj CREATURE:FORGOTTEN_BEAST_180:FATj#CREATURE:FORGOTTEN_BEAST_180:TALLOWj CREATURE:FORGOTTEN_BEAST_181:FATj#CREATURE:FORGOTTEN_BEAST_181:TALLOWj CREATURE:FORGOTTEN_BEAST_182:FATj#CREATURE:FORGOTTEN_BEAST_182:TALLOWj CREATURE:FORGOTTEN_BEAST_183:FATj#CREATURE:FORGOTTEN_BEAST_183:TALLOWj CREATURE:FORGOTTEN_BEAST_184:FATj#CREATURE:FORGOTTEN_BEAST_184:TALLOWj CREATURE:FORGOTTEN_BEAST_185:FATj#CREATURE:FORGOTTEN_BEAST_185:TALLOWj CREATURE:FORGOTTEN_BEAST_186:FATj#CREATURE:FORGOTTEN_BEAST_186:TALLOWj CREATURE:FORGOTTEN_BEAST_188:FATj#CREATURE:FORGOTTEN_BEAST_188:TALLOWj CREATURE:FORGOTTEN_BEAST_189:FATj#CREATURE:FORGOTTEN_BEAST_189:TALLOWj CREATURE:FORGOTTEN_BEAST_190:FATj#CREATURE:FORGOTTEN_BEAST_190:TALLOWj CREATURE:FORGOTTEN_BEAST_191:FATj#CREATURE:FORGOTTEN_BEAST_191:TALLOWj CREATURE:FORGOTTEN_BEAST_193:FATj#CREATURE:FORGOTTEN_BEAST_193:TALLOWj CREATURE:FORGOTTEN_BEAST_194:FATj#CREATURE:FORGOTTEN_BEAST_194:TALLOWj CREATURE:FORGOTTEN_BEAST_195:FATj#CREATURE:FORGOTTEN_BEAST_195:TALLOWj CREATURE:FORGOTTEN_BEAST_196:FATj#CREATURE:FORGOTTEN_BEAST_196:TALLOWj CREATURE:FORGOTTEN_BEAST_197:FATj#CREATURE:FORGOTTEN_BEAST_197:TALLOWj CREATURE:FORGOTTEN_BEAST_199:FATj#CREATURE:FORGOTTEN_BEAST_199:TALLOWj CREATURE:FORGOTTEN_BEAST_200:FATj#CREATURE:FORGOTTEN_BEAST_200:TALLOWj CREATURE:FORGOTTEN_BEAST_201:FATj#CREATURE:FORGOTTEN_BEAST_201:TALLOWj CREATURE:FORGOTTEN_BEAST_204:FATj#CREATURE:FORGOTTEN_BEAST_204:TALLOWj CREATURE:FORGOTTEN_BEAST_205:FATj#CREATURE:FORGOTTEN_BEAST_205:TALLOWj CREATURE:FORGOTTEN_BEAST_206:FATj#CREATURE:FORGOTTEN_BEAST_206:TALLOWj CREATURE:FORGOTTEN_BEAST_207:FATj#CREATURE:FORGOTTEN_BEAST_207:TALLOWj CREATURE:FORGOTTEN_BEAST_208:FATj#CREATURE:FORGOTTEN_BEAST_208:TALLOWj CREATURE:FORGOTTEN_BEAST_209:FATj#CREATURE:FORGOTTEN_BEAST_209:TALLOWj CREATURE:FORGOTTEN_BEAST_210:FATj#CREATURE:FORGOTTEN_BEAST_210:TALLOWj CREATURE:FORGOTTEN_BEAST_211:FATj#CREATURE:FORGOTTEN_BEAST_211:TALLOWj CREATURE:FORGOTTEN_BEAST_212:FATj#CREATURE:FORGOTTEN_BEAST_212:TALLOWj CREATURE:FORGOTTEN_BEAST_213:FATj#CREATURE:FORGOTTEN_BEAST_213:TALLOWj CREATURE:FORGOTTEN_BEAST_214:FATj#CREATURE:FORGOTTEN_BEAST_214:TALLOWj CREATURE:FORGOTTEN_BEAST_215:FATj#CREATURE:FORGOTTEN_BEAST_215:TALLOWj CREATURE:FORGOTTEN_BEAST_216:FATj#CREATURE:FORGOTTEN_BEAST_216:TALLOWj CREATURE:FORGOTTEN_BEAST_217:FATj#CREATURE:FORGOTTEN_BEAST_217:TALLOWj CREATURE:FORGOTTEN_BEAST_218:FATj#CREATURE:FORGOTTEN_BEAST_218:TALLOWj CREATURE:FORGOTTEN_BEAST_220:FATj#CREATURE:FORGOTTEN_BEAST_220:TALLOWj CREATURE:FORGOTTEN_BEAST_221:FATj#CREATURE:FORGOTTEN_BEAST_221:TALLOWj CREATURE:FORGOTTEN_BEAST_224:FATj#CREATURE:FORGOTTEN_BEAST_224:TALLOWj CREATURE:FORGOTTEN_BEAST_225:FATj#CREATURE:FORGOTTEN_BEAST_225:TALLOWj CREATURE:FORGOTTEN_BEAST_228:FATj#CREATURE:FORGOTTEN_BEAST_228:TALLOWj CREATURE:FORGOTTEN_BEAST_231:FATj#CREATURE:FORGOTTEN_BEAST_231:TALLOWj CREATURE:FORGOTTEN_BEAST_232:FATj#CREATURE:FORGOTTEN_BEAST_232:TALLOWj CREATURE:FORGOTTEN_BEAST_235:FATj#CREATURE:FORGOTTEN_BEAST_235:TALLOWj CREATURE:FORGOTTEN_BEAST_236:FATj#CREATURE:FORGOTTEN_BEAST_236:TALLOWj CREATURE:FORGOTTEN_BEAST_237:FATj#CREATURE:FORGOTTEN_BEAST_237:TALLOWj CREATURE:FORGOTTEN_BEAST_239:FATj#CREATURE:FORGOTTEN_BEAST_239:TALLOWj CREATURE:FORGOTTEN_BEAST_240:FATj#CREATURE:FORGOTTEN_BEAST_240:TALLOWj CREATURE:FORGOTTEN_BEAST_242:FATj#CREATURE:FORGOTTEN_BEAST_242:TALLOWj CREATURE:FORGOTTEN_BEAST_243:FATj#CREATURE:FORGOTTEN_BEAST_243:TALLOWj CREATURE:FORGOTTEN_BEAST_244:FATj#CREATURE:FORGOTTEN_BEAST_244:TALLOWj CREATURE:FORGOTTEN_BEAST_245:FATj#CREATURE:FORGOTTEN_BEAST_245:TALLOWj CREATURE:FORGOTTEN_BEAST_246:FATj#CREATURE:FORGOTTEN_BEAST_246:TALLOWj CREATURE:FORGOTTEN_BEAST_247:FATj#CREATURE:FORGOTTEN_BEAST_247:TALLOWj CREATURE:FORGOTTEN_BEAST_248:FATj#CREATURE:FORGOTTEN_BEAST_248:TALLOWj CREATURE:FORGOTTEN_BEAST_249:FATj#CREATURE:FORGOTTEN_BEAST_249:TALLOWj CREATURE:FORGOTTEN_BEAST_253:FATj#CREATURE:FORGOTTEN_BEAST_253:TALLOWj CREATURE:FORGOTTEN_BEAST_255:FATj#CREATURE:FORGOTTEN_BEAST_255:TALLOWj CREATURE:FORGOTTEN_BEAST_256:FATj#CREATURE:FORGOTTEN_BEAST_256:TALLOWj CREATURE:FORGOTTEN_BEAST_259:FATj#CREATURE:FORGOTTEN_BEAST_259:TALLOWj CREATURE:FORGOTTEN_BEAST_260:FATj#CREATURE:FORGOTTEN_BEAST_260:TALLOWj CREATURE:FORGOTTEN_BEAST_263:FATj#CREATURE:FORGOTTEN_BEAST_263:TALLOWj CREATURE:FORGOTTEN_BEAST_264:FATj#CREATURE:FORGOTTEN_BEAST_264:TALLOWj CREATURE:FORGOTTEN_BEAST_265:FATj#CREATURE:FORGOTTEN_BEAST_265:TALLOWj CREATURE:FORGOTTEN_BEAST_266:FATj#CREATURE:FORGOTTEN_BEAST_266:TALLOWj CREATURE:FORGOTTEN_BEAST_268:FATj#CREATURE:FORGOTTEN_BEAST_268:TALLOWj CREATURE:FORGOTTEN_BEAST_269:FATj#CREATURE:FORGOTTEN_BEAST_269:TALLOWj CREATURE:FORGOTTEN_BEAST_270:FATj#CREATURE:FORGOTTEN_BEAST_270:TALLOWj CREATURE:FORGOTTEN_BEAST_271:FATj#CREATURE:FORGOTTEN_BEAST_271:TALLOWj CREATURE:FORGOTTEN_BEAST_272:FATj#CREATURE:FORGOTTEN_BEAST_272:TALLOWj CREATURE:FORGOTTEN_BEAST_273:FATj#CREATURE:FORGOTTEN_BEAST_273:TALLOWj CREATURE:FORGOTTEN_BEAST_274:FATj#CREATURE:FORGOTTEN_BEAST_274:TALLOWj CREATURE:FORGOTTEN_BEAST_275:FATj#CREATURE:FORGOTTEN_BEAST_275:TALLOWj CREATURE:FORGOTTEN_BEAST_276:FATj#CREATURE:FORGOTTEN_BEAST_276:TALLOWj CREATURE:FORGOTTEN_BEAST_277:FATj#CREATURE:FORGOTTEN_BEAST_277:TALLOWj CREATURE:FORGOTTEN_BEAST_279:FATj#CREATURE:FORGOTTEN_BEAST_279:TALLOWj CREATURE:FORGOTTEN_BEAST_280:FATj#CREATURE:FORGOTTEN_BEAST_280:TALLOWj CREATURE:FORGOTTEN_BEAST_281:FATj#CREATURE:FORGOTTEN_BEAST_281:TALLOWj CREATURE:FORGOTTEN_BEAST_282:FATj#CREATURE:FORGOTTEN_BEAST_282:TALLOWj CREATURE:FORGOTTEN_BEAST_283:FATj#CREATURE:FORGOTTEN_BEAST_283:TALLOWj CREATURE:FORGOTTEN_BEAST_285:FATj#CREATURE:FORGOTTEN_BEAST_285:TALLOWj CREATURE:FORGOTTEN_BEAST_286:FATj#CREATURE:FORGOTTEN_BEAST_286:TALLOWj CREATURE:FORGOTTEN_BEAST_287:FATj#CREATURE:FORGOTTEN_BEAST_287:TALLOWj CREATURE:FORGOTTEN_BEAST_288:FATj#CREATURE:FORGOTTEN_BEAST_288:TALLOWj CREATURE:FORGOTTEN_BEAST_289:FATj#CREATURE:FORGOTTEN_BEAST_289:TALLOWj CREATURE:FORGOTTEN_BEAST_290:FATj#CREATURE:FORGOTTEN_BEAST_290:TALLOWj CREATURE:FORGOTTEN_BEAST_293:FATj#CREATURE:FORGOTTEN_BEAST_293:TALLOWj CREATURE:FORGOTTEN_BEAST_295:FATj#CREATURE:FORGOTTEN_BEAST_295:TALLOWj CREATURE:FORGOTTEN_BEAST_297:FATj#CREATURE:FORGOTTEN_BEAST_297:TALLOWj CREATURE:FORGOTTEN_BEAST_300:FATj#CREATURE:FORGOTTEN_BEAST_300:TALLOWj CREATURE:FORGOTTEN_BEAST_302:FATj#CREATURE:FORGOTTEN_BEAST_302:TALLOWj CREATURE:FORGOTTEN_BEAST_306:FATj#CREATURE:FORGOTTEN_BEAST_306:TALLOWj CREATURE:FORGOTTEN_BEAST_307:FATj#CREATURE:FORGOTTEN_BEAST_307:TALLOWj CREATURE:FORGOTTEN_BEAST_310:FATj#CREATURE:FORGOTTEN_BEAST_310:TALLOWj CREATURE:FORGOTTEN_BEAST_311:FATj#CREATURE:FORGOTTEN_BEAST_311:TALLOWj CREATURE:FORGOTTEN_BEAST_312:FATj#CREATURE:FORGOTTEN_BEAST_312:TALLOWj CREATURE:FORGOTTEN_BEAST_314:FATj#CREATURE:FORGOTTEN_BEAST_314:TALLOWj CREATURE:FORGOTTEN_BEAST_316:FATj#CREATURE:FORGOTTEN_BEAST_316:TALLOWj CREATURE:FORGOTTEN_BEAST_317:FATj#CREATURE:FORGOTTEN_BEAST_317:TALLOWj CREATURE:FORGOTTEN_BEAST_318:FATj#CREATURE:FORGOTTEN_BEAST_318:TALLOWj CREATURE:FORGOTTEN_BEAST_320:FATj#CREATURE:FORGOTTEN_BEAST_320:TALLOWj CREATURE:FORGOTTEN_BEAST_321:FATj#CREATURE:FORGOTTEN_BEAST_321:TALLOWj CREATURE:FORGOTTEN_BEAST_322:FATj#CREATURE:FORGOTTEN_BEAST_322:TALLOWj CREATURE:FORGOTTEN_BEAST_323:FATj#CREATURE:FORGOTTEN_BEAST_323:TALLOWj CREATURE:FORGOTTEN_BEAST_324:FATj#CREATURE:FORGOTTEN_BEAST_324:TALLOWj CREATURE:FORGOTTEN_BEAST_325:FATj#CREATURE:FORGOTTEN_BEAST_325:TALLOWj CREATURE:FORGOTTEN_BEAST_326:FATj#CREATURE:FORGOTTEN_BEAST_326:TALLOWj CREATURE:FORGOTTEN_BEAST_327:FATj#CREATURE:FORGOTTEN_BEAST_327:TALLOWj CREATURE:FORGOTTEN_BEAST_328:FATj#CREATURE:FORGOTTEN_BEAST_328:TALLOWj CREATURE:FORGOTTEN_BEAST_329:FATj#CREATURE:FORGOTTEN_BEAST_329:TALLOWj CREATURE:FORGOTTEN_BEAST_330:FATj#CREATURE:FORGOTTEN_BEAST_330:TALLOWj CREATURE:FORGOTTEN_BEAST_332:FATj#CREATURE:FORGOTTEN_BEAST_332:TALLOWj CREATURE:FORGOTTEN_BEAST_333:FATj#CREATURE:FORGOTTEN_BEAST_333:TALLOWj CREATURE:FORGOTTEN_BEAST_335:FATj#CREATURE:FORGOTTEN_BEAST_335:TALLOWj CREATURE:FORGOTTEN_BEAST_336:FATj#CREATURE:FORGOTTEN_BEAST_336:TALLOWj CREATURE:FORGOTTEN_BEAST_337:FATj#CREATURE:FORGOTTEN_BEAST_337:TALLOWj CREATURE:FORGOTTEN_BEAST_338:FATj#CREATURE:FORGOTTEN_BEAST_338:TALLOWj CREATURE:FORGOTTEN_BEAST_339:FATj#CREATURE:FORGOTTEN_BEAST_339:TALLOWj CREATURE:FORGOTTEN_BEAST_341:FATj#CREATURE:FORGOTTEN_BEAST_341:TALLOWj CREATURE:FORGOTTEN_BEAST_342:FATj#CREATURE:FORGOTTEN_BEAST_342:TALLOWj CREATURE:FORGOTTEN_BEAST_343:FATj#CREATURE:FORGOTTEN_BEAST_343:TALLOWj CREATURE:FORGOTTEN_BEAST_344:FATj#CREATURE:FORGOTTEN_BEAST_344:TALLOWj CREATURE:FORGOTTEN_BEAST_347:FATj#CREATURE:FORGOTTEN_BEAST_347:TALLOWj CREATURE:FORGOTTEN_BEAST_348:FATj#CREATURE:FORGOTTEN_BEAST_348:TALLOWj CREATURE:FORGOTTEN_BEAST_349:FATj#CREATURE:FORGOTTEN_BEAST_349:TALLOWj CREATURE:FORGOTTEN_BEAST_351:FATj#CREATURE:FORGOTTEN_BEAST_351:TALLOWj CREATURE:FORGOTTEN_BEAST_352:FATj#CREATURE:FORGOTTEN_BEAST_352:TALLOWj CREATURE:FORGOTTEN_BEAST_353:FATj#CREATURE:FORGOTTEN_BEAST_353:TALLOWj CREATURE:FORGOTTEN_BEAST_354:FATj#CREATURE:FORGOTTEN_BEAST_354:TALLOWj CREATURE:FORGOTTEN_BEAST_355:FATj#CREATURE:FORGOTTEN_BEAST_355:TALLOWj CREATURE:FORGOTTEN_BEAST_356:FATj#CREATURE:FORGOTTEN_BEAST_356:TALLOWj CREATURE:FORGOTTEN_BEAST_357:FATj#CREATURE:FORGOTTEN_BEAST_357:TALLOWj CREATURE:FORGOTTEN_BEAST_358:FATj#CREATURE:FORGOTTEN_BEAST_358:TALLOWj CREATURE:FORGOTTEN_BEAST_359:FATj#CREATURE:FORGOTTEN_BEAST_359:TALLOWj CREATURE:FORGOTTEN_BEAST_361:FATj#CREATURE:FORGOTTEN_BEAST_361:TALLOWj CREATURE:FORGOTTEN_BEAST_363:FATj#CREATURE:FORGOTTEN_BEAST_363:TALLOWj CREATURE:FORGOTTEN_BEAST_364:FATj#CREATURE:FORGOTTEN_BEAST_364:TALLOWj CREATURE:FORGOTTEN_BEAST_365:FATj#CREATURE:FORGOTTEN_BEAST_365:TALLOWj CREATURE:FORGOTTEN_BEAST_366:FATj#CREATURE:FORGOTTEN_BEAST_366:TALLOWj CREATURE:FORGOTTEN_BEAST_367:FATj#CREATURE:FORGOTTEN_BEAST_367:TALLOWj CREATURE:FORGOTTEN_BEAST_368:FATj#CREATURE:FORGOTTEN_BEAST_368:TALLOWj CREATURE:FORGOTTEN_BEAST_370:FATj#CREATURE:FORGOTTEN_BEAST_370:TALLOWj CREATURE:FORGOTTEN_BEAST_372:FATj#CREATURE:FORGOTTEN_BEAST_372:TALLOWj CREATURE:FORGOTTEN_BEAST_374:FATj#CREATURE:FORGOTTEN_BEAST_374:TALLOWj CREATURE:FORGOTTEN_BEAST_375:FATj#CREATURE:FORGOTTEN_BEAST_375:TALLOWj CREATURE:FORGOTTEN_BEAST_377:FATj#CREATURE:FORGOTTEN_BEAST_377:TALLOWj CREATURE:FORGOTTEN_BEAST_378:FATj#CREATURE:FORGOTTEN_BEAST_378:TALLOWj CREATURE:FORGOTTEN_BEAST_379:FATj#CREATURE:FORGOTTEN_BEAST_379:TALLOWj CREATURE:FORGOTTEN_BEAST_380:FATj#CREATURE:FORGOTTEN_BEAST_380:TALLOWj CREATURE:FORGOTTEN_BEAST_381:FATj#CREATURE:FORGOTTEN_BEAST_381:TALLOWj CREATURE:FORGOTTEN_BEAST_382:FATj#CREATURE:FORGOTTEN_BEAST_382:TALLOWj CREATURE:FORGOTTEN_BEAST_383:FATj#CREATURE:FORGOTTEN_BEAST_383:TALLOWj CREATURE:FORGOTTEN_BEAST_384:FATj#CREATURE:FORGOTTEN_BEAST_384:TALLOWj CREATURE:FORGOTTEN_BEAST_385:FATj#CREATURE:FORGOTTEN_BEAST_385:TALLOWj CREATURE:FORGOTTEN_BEAST_386:FATj#CREATURE:FORGOTTEN_BEAST_386:TALLOWj CREATURE:FORGOTTEN_BEAST_387:FATj#CREATURE:FORGOTTEN_BEAST_387:TALLOWj CREATURE:FORGOTTEN_BEAST_388:FATj#CREATURE:FORGOTTEN_BEAST_388:TALLOWj CREATURE:FORGOTTEN_BEAST_389:FATj#CREATURE:FORGOTTEN_BEAST_389:TALLOWj CREATURE:FORGOTTEN_BEAST_390:FATj#CREATURE:FORGOTTEN_BEAST_390:TALLOWj CREATURE:FORGOTTEN_BEAST_391:FATj#CREATURE:FORGOTTEN_BEAST_391:TALLOWj CREATURE:FORGOTTEN_BEAST_392:FATj#CREATURE:FORGOTTEN_BEAST_392:TALLOWj CREATURE:FORGOTTEN_BEAST_393:FATj#CREATURE:FORGOTTEN_BEAST_393:TALLOWj CREATURE:FORGOTTEN_BEAST_396:FATj#CREATURE:FORGOTTEN_BEAST_396:TALLOWj CREATURE:FORGOTTEN_BEAST_397:FATj#CREATURE:FORGOTTEN_BEAST_397:TALLOWj CREATURE:FORGOTTEN_BEAST_398:FATj#CREATURE:FORGOTTEN_BEAST_398:TALLOWj CREATURE:FORGOTTEN_BEAST_400:FATj#CREATURE:FORGOTTEN_BEAST_400:TALLOWj CREATURE:FORGOTTEN_BEAST_403:FATj#CREATURE:FORGOTTEN_BEAST_403:TALLOWj CREATURE:FORGOTTEN_BEAST_404:FATj#CREATURE:FORGOTTEN_BEAST_404:TALLOWj CREATURE:FORGOTTEN_BEAST_405:FATj#CREATURE:FORGOTTEN_BEAST_405:TALLOWj CREATURE:FORGOTTEN_BEAST_406:FATj#CREATURE:FORGOTTEN_BEAST_406:TALLOWj CREATURE:FORGOTTEN_BEAST_407:FATj#CREATURE:FORGOTTEN_BEAST_407:TALLOWj CREATURE:FORGOTTEN_BEAST_408:FATj#CREATURE:FORGOTTEN_BEAST_408:TALLOWj CREATURE:FORGOTTEN_BEAST_409:FATj#CREATURE:FORGOTTEN_BEAST_409:TALLOWj CREATURE:FORGOTTEN_BEAST_410:FATj#CREATURE:FORGOTTEN_BEAST_410:TALLOWj CREATURE:FORGOTTEN_BEAST_411:FATj#CREATURE:FORGOTTEN_BEAST_411:TALLOWj CREATURE:FORGOTTEN_BEAST_412:FATj#CREATURE:FORGOTTEN_BEAST_412:TALLOWj CREATURE:FORGOTTEN_BEAST_413:FATj#CREATURE:FORGOTTEN_BEAST_413:TALLOWj CREATURE:FORGOTTEN_BEAST_414:FATj#CREATURE:FORGOTTEN_BEAST_414:TALLOWj CREATURE:FORGOTTEN_BEAST_416:FATj#CREATURE:FORGOTTEN_BEAST_416:TALLOWj CREATURE:FORGOTTEN_BEAST_417:FATj#CREATURE:FORGOTTEN_BEAST_417:TALLOWj CREATURE:FORGOTTEN_BEAST_418:FATj#CREATURE:FORGOTTEN_BEAST_418:TALLOWj CREATURE:FORGOTTEN_BEAST_420:FATj#CREATURE:FORGOTTEN_BEAST_420:TALLOWj CREATURE:FORGOTTEN_BEAST_421:FATj#CREATURE:FORGOTTEN_BEAST_421:TALLOWj CREATURE:FORGOTTEN_BEAST_422:FATj#CREATURE:FORGOTTEN_BEAST_422:TALLOWj CREATURE:FORGOTTEN_BEAST_423:FATj#CREATURE:FORGOTTEN_BEAST_423:TALLOWj CREATURE:FORGOTTEN_BEAST_424:FATj#CREATURE:FORGOTTEN_BEAST_424:TALLOWj CREATURE:FORGOTTEN_BEAST_427:FATj#CREATURE:FORGOTTEN_BEAST_427:TALLOWj CREATURE:FORGOTTEN_BEAST_429:FATj#CREATURE:FORGOTTEN_BEAST_429:TALLOWj CREATURE:FORGOTTEN_BEAST_430:FATj#CREATURE:FORGOTTEN_BEAST_430:TALLOWj CREATURE:FORGOTTEN_BEAST_432:FATj#CREATURE:FORGOTTEN_BEAST_432:TALLOWj CREATURE:FORGOTTEN_BEAST_433:FATj#CREATURE:FORGOTTEN_BEAST_433:TALLOWj CREATURE:FORGOTTEN_BEAST_434:FATj#CREATURE:FORGOTTEN_BEAST_434:TALLOWj CREATURE:FORGOTTEN_BEAST_435:FATj#CREATURE:FORGOTTEN_BEAST_435:TALLOWj CREATURE:FORGOTTEN_BEAST_436:FATj#CREATURE:FORGOTTEN_BEAST_436:TALLOWj CREATURE:FORGOTTEN_BEAST_437:FATj#CREATURE:FORGOTTEN_BEAST_437:TALLOWj CREATURE:FORGOTTEN_BEAST_438:FATj#CREATURE:FORGOTTEN_BEAST_438:TALLOWj CREATURE:FORGOTTEN_BEAST_440:FATj#CREATURE:FORGOTTEN_BEAST_440:TALLOWj CREATURE:FORGOTTEN_BEAST_441:FATj#CREATURE:FORGOTTEN_BEAST_441:TALLOWj CREATURE:FORGOTTEN_BEAST_442:FATj#CREATURE:FORGOTTEN_BEAST_442:TALLOWj CREATURE:FORGOTTEN_BEAST_444:FATj#CREATURE:FORGOTTEN_BEAST_444:TALLOWj CREATURE:FORGOTTEN_BEAST_446:FATj#CREATURE:FORGOTTEN_BEAST_446:TALLOWj CREATURE:FORGOTTEN_BEAST_447:FATj#CREATURE:FORGOTTEN_BEAST_447:TALLOWj CREATURE:FORGOTTEN_BEAST_448:FATj#CREATURE:FORGOTTEN_BEAST_448:TALLOWj CREATURE:FORGOTTEN_BEAST_449:FATj#CREATURE:FORGOTTEN_BEAST_449:TALLOWj CREATURE:FORGOTTEN_BEAST_450:FATj#CREATURE:FORGOTTEN_BEAST_450:TALLOWj CREATURE:FORGOTTEN_BEAST_451:FATj#CREATURE:FORGOTTEN_BEAST_451:TALLOWj CREATURE:FORGOTTEN_BEAST_453:FATj#CREATURE:FORGOTTEN_BEAST_453:TALLOWj CREATURE:FORGOTTEN_BEAST_454:FATj#CREATURE:FORGOTTEN_BEAST_454:TALLOWj CREATURE:FORGOTTEN_BEAST_455:FATj#CREATURE:FORGOTTEN_BEAST_455:TALLOWj CREATURE:FORGOTTEN_BEAST_457:FATj#CREATURE:FORGOTTEN_BEAST_457:TALLOWj CREATURE:FORGOTTEN_BEAST_459:FATj#CREATURE:FORGOTTEN_BEAST_459:TALLOWj CREATURE:FORGOTTEN_BEAST_461:FATj#CREATURE:FORGOTTEN_BEAST_461:TALLOWj CREATURE:FORGOTTEN_BEAST_462:FATj#CREATURE:FORGOTTEN_BEAST_462:TALLOWj CREATURE:FORGOTTEN_BEAST_463:FATj#CREATURE:FORGOTTEN_BEAST_463:TALLOWj CREATURE:FORGOTTEN_BEAST_465:FATj#CREATURE:FORGOTTEN_BEAST_465:TALLOWj CREATURE:FORGOTTEN_BEAST_466:FATj#CREATURE:FORGOTTEN_BEAST_466:TALLOWj CREATURE:FORGOTTEN_BEAST_468:FATj#CREATURE:FORGOTTEN_BEAST_468:TALLOWj CREATURE:FORGOTTEN_BEAST_469:FATj#CREATURE:FORGOTTEN_BEAST_469:TALLOWj CREATURE:FORGOTTEN_BEAST_470:FATj#CREATURE:FORGOTTEN_BEAST_470:TALLOWj CREATURE:FORGOTTEN_BEAST_471:FATj#CREATURE:FORGOTTEN_BEAST_471:TALLOWj CREATURE:FORGOTTEN_BEAST_472:FATj#CREATURE:FORGOTTEN_BEAST_472:TALLOWj CREATURE:FORGOTTEN_BEAST_474:FATj#CREATURE:FORGOTTEN_BEAST_474:TALLOWj CREATURE:FORGOTTEN_BEAST_475:FATj#CREATURE:FORGOTTEN_BEAST_475:TALLOWj CREATURE:FORGOTTEN_BEAST_476:FATj#CREATURE:FORGOTTEN_BEAST_476:TALLOWj CREATURE:FORGOTTEN_BEAST_478:FATj#CREATURE:FORGOTTEN_BEAST_478:TALLOWj CREATURE:FORGOTTEN_BEAST_479:FATj#CREATURE:FORGOTTEN_BEAST_479:TALLOWj CREATURE:FORGOTTEN_BEAST_480:FATj#CREATURE:FORGOTTEN_BEAST_480:TALLOWj CREATURE:FORGOTTEN_BEAST_481:FATj#CREATURE:FORGOTTEN_BEAST_481:TALLOWj CREATURE:FORGOTTEN_BEAST_483:FATj#CREATURE:FORGOTTEN_BEAST_483:TALLOWj CREATURE:FORGOTTEN_BEAST_486:FATj#CREATURE:FORGOTTEN_BEAST_486:TALLOWj CREATURE:FORGOTTEN_BEAST_487:FATj#CREATURE:FORGOTTEN_BEAST_487:TALLOWj CREATURE:FORGOTTEN_BEAST_489:FATj#CREATURE:FORGOTTEN_BEAST_489:TALLOWj CREATURE:FORGOTTEN_BEAST_492:FATj#CREATURE:FORGOTTEN_BEAST_492:TALLOWj CREATURE:FORGOTTEN_BEAST_494:FATj#CREATURE:FORGOTTEN_BEAST_494:TALLOWj CREATURE:FORGOTTEN_BEAST_495:FATj#CREATURE:FORGOTTEN_BEAST_495:TALLOWj CREATURE:FORGOTTEN_BEAST_496:FATj#CREATURE:FORGOTTEN_BEAST_496:TALLOWj CREATURE:FORGOTTEN_BEAST_497:FATj#CREATURE:FORGOTTEN_BEAST_497:TALLOWj CREATURE:FORGOTTEN_BEAST_498:FATj#CREATURE:FORGOTTEN_BEAST_498:TALLOWj CREATURE:FORGOTTEN_BEAST_499:FATj#CREATURE:FORGOTTEN_BEAST_499:TALLOWj CREATURE:FORGOTTEN_BEAST_501:FATj#CREATURE:FORGOTTEN_BEAST_501:TALLOWj CREATURE:FORGOTTEN_BEAST_503:FATj#CREATURE:FORGOTTEN_BEAST_503:TALLOWj CREATURE:FORGOTTEN_BEAST_504:FATj#CREATURE:FORGOTTEN_BEAST_504:TALLOWj CREATURE:FORGOTTEN_BEAST_505:FATj#CREATURE:FORGOTTEN_BEAST_505:TALLOWj CREATURE:FORGOTTEN_BEAST_507:FATj#CREATURE:FORGOTTEN_BEAST_507:TALLOWj CREATURE:FORGOTTEN_BEAST_508:FATj#CREATURE:FORGOTTEN_BEAST_508:TALLOWj CREATURE:FORGOTTEN_BEAST_510:FATj#CREATURE:FORGOTTEN_BEAST_510:TALLOWj CREATURE:FORGOTTEN_BEAST_512:FATj#CREATURE:FORGOTTEN_BEAST_512:TALLOWj CREATURE:FORGOTTEN_BEAST_513:FATj#CREATURE:FORGOTTEN_BEAST_513:TALLOWj CREATURE:FORGOTTEN_BEAST_514:FATj#CREATURE:FORGOTTEN_BEAST_514:TALLOWj CREATURE:FORGOTTEN_BEAST_515:FATj#CREATURE:FORGOTTEN_BEAST_515:TALLOWj CREATURE:FORGOTTEN_BEAST_516:FATj#CREATURE:FORGOTTEN_BEAST_516:TALLOWj CREATURE:FORGOTTEN_BEAST_517:FATj#CREATURE:FORGOTTEN_BEAST_517:TALLOWj CREATURE:FORGOTTEN_BEAST_518:FATj#CREATURE:FORGOTTEN_BEAST_518:TALLOWj CREATURE:FORGOTTEN_BEAST_519:FATj#CREATURE:FORGOTTEN_BEAST_519:TALLOWj CREATURE:FORGOTTEN_BEAST_520:FATj#CREATURE:FORGOTTEN_BEAST_520:TALLOWj CREATURE:FORGOTTEN_BEAST_521:FATj#CREATURE:FORGOTTEN_BEAST_521:TALLOWj CREATURE:FORGOTTEN_BEAST_522:FATj#CREATURE:FORGOTTEN_BEAST_522:TALLOWj CREATURE:FORGOTTEN_BEAST_523:FATj#CREATURE:FORGOTTEN_BEAST_523:TALLOWj CREATURE:FORGOTTEN_BEAST_525:FATj#CREATURE:FORGOTTEN_BEAST_525:TALLOWj CREATURE:FORGOTTEN_BEAST_526:FATj#CREATURE:FORGOTTEN_BEAST_526:TALLOWj CREATURE:FORGOTTEN_BEAST_527:FATj#CREATURE:FORGOTTEN_BEAST_527:TALLOWj CREATURE:FORGOTTEN_BEAST_528:FATj#CREATURE:FORGOTTEN_BEAST_528:TALLOWj CREATURE:FORGOTTEN_BEAST_529:FATj#CREATURE:FORGOTTEN_BEAST_529:TALLOWj CREATURE:FORGOTTEN_BEAST_530:FATj#CREATURE:FORGOTTEN_BEAST_530:TALLOWj CREATURE:FORGOTTEN_BEAST_531:FATj#CREATURE:FORGOTTEN_BEAST_531:TALLOWj CREATURE:FORGOTTEN_BEAST_532:FATj#CREATURE:FORGOTTEN_BEAST_532:TALLOWj CREATURE:FORGOTTEN_BEAST_533:FATj#CREATURE:FORGOTTEN_BEAST_533:TALLOWj CREATURE:FORGOTTEN_BEAST_534:FATj#CREATURE:FORGOTTEN_BEAST_534:TALLOWj CREATURE:FORGOTTEN_BEAST_535:FATj#CREATURE:FORGOTTEN_BEAST_535:TALLOWj CREATURE:FORGOTTEN_BEAST_536:FATj#CREATURE:FORGOTTEN_BEAST_536:TALLOWj CREATURE:FORGOTTEN_BEAST_539:FATj#CREATURE:FORGOTTEN_BEAST_539:TALLOWj CREATURE:FORGOTTEN_BEAST_540:FATj#CREATURE:FORGOTTEN_BEAST_540:TALLOWj CREATURE:FORGOTTEN_BEAST_541:FATj#CREATURE:FORGOTTEN_BEAST_541:TALLOWj CREATURE:FORGOTTEN_BEAST_543:FATj#CREATURE:FORGOTTEN_BEAST_543:TALLOWj CREATURE:FORGOTTEN_BEAST_545:FATj#CREATURE:FORGOTTEN_BEAST_545:TALLOWj CREATURE:FORGOTTEN_BEAST_546:FATj#CREATURE:FORGOTTEN_BEAST_546:TALLOWj CREATURE:FORGOTTEN_BEAST_547:FATj#CREATURE:FORGOTTEN_BEAST_547:TALLOWj CREATURE:FORGOTTEN_BEAST_548:FATj#CREATURE:FORGOTTEN_BEAST_548:TALLOWj CREATURE:FORGOTTEN_BEAST_549:FATj#CREATURE:FORGOTTEN_BEAST_549:TALLOWj CREATURE:FORGOTTEN_BEAST_550:FATj#CREATURE:FORGOTTEN_BEAST_550:TALLOWj CREATURE:FORGOTTEN_BEAST_551:FATj#CREATURE:FORGOTTEN_BEAST_551:TALLOWj CREATURE:FORGOTTEN_BEAST_554:FATj#CREATURE:FORGOTTEN_BEAST_554:TALLOWj CREATURE:FORGOTTEN_BEAST_555:FATj#CREATURE:FORGOTTEN_BEAST_555:TALLOWj CREATURE:FORGOTTEN_BEAST_556:FATj#CREATURE:FORGOTTEN_BEAST_556:TALLOWj CREATURE:FORGOTTEN_BEAST_557:FATj#CREATURE:FORGOTTEN_BEAST_557:TALLOWj CREATURE:FORGOTTEN_BEAST_561:FATj#CREATURE:FORGOTTEN_BEAST_561:TALLOWj CREATURE:FORGOTTEN_BEAST_562:FATj#CREATURE:FORGOTTEN_BEAST_562:TALLOWj CREATURE:FORGOTTEN_BEAST_564:FATj#CREATURE:FORGOTTEN_BEAST_564:TALLOWj CREATURE:FORGOTTEN_BEAST_569:FATj#CREATURE:FORGOTTEN_BEAST_569:TALLOWj CREATURE:FORGOTTEN_BEAST_570:FATj#CREATURE:FORGOTTEN_BEAST_570:TALLOWj CREATURE:FORGOTTEN_BEAST_571:FATj#CREATURE:FORGOTTEN_BEAST_571:TALLOWj CREATURE:FORGOTTEN_BEAST_572:FATj#CREATURE:FORGOTTEN_BEAST_572:TALLOWj CREATURE:FORGOTTEN_BEAST_574:FATj#CREATURE:FORGOTTEN_BEAST_574:TALLOWj CREATURE:FORGOTTEN_BEAST_576:FATj#CREATURE:FORGOTTEN_BEAST_576:TALLOWj CREATURE:FORGOTTEN_BEAST_578:FATj#CREATURE:FORGOTTEN_BEAST_578:TALLOWj CREATURE:FORGOTTEN_BEAST_579:FATj#CREATURE:FORGOTTEN_BEAST_579:TALLOWj CREATURE:FORGOTTEN_BEAST_580:FATj#CREATURE:FORGOTTEN_BEAST_580:TALLOWj CREATURE:FORGOTTEN_BEAST_581:FATj#CREATURE:FORGOTTEN_BEAST_581:TALLOWj CREATURE:FORGOTTEN_BEAST_583:FATj#CREATURE:FORGOTTEN_BEAST_583:TALLOWj CREATURE:FORGOTTEN_BEAST_584:FATj#CREATURE:FORGOTTEN_BEAST_584:TALLOWj CREATURE:FORGOTTEN_BEAST_586:FATj#CREATURE:FORGOTTEN_BEAST_586:TALLOWj CREATURE:FORGOTTEN_BEAST_588:FATj#CREATURE:FORGOTTEN_BEAST_588:TALLOWj CREATURE:FORGOTTEN_BEAST_589:FATj#CREATURE:FORGOTTEN_BEAST_589:TALLOWj CREATURE:FORGOTTEN_BEAST_590:FATj#CREATURE:FORGOTTEN_BEAST_590:TALLOWj CREATURE:FORGOTTEN_BEAST_592:FATj#CREATURE:FORGOTTEN_BEAST_592:TALLOWj CREATURE:FORGOTTEN_BEAST_593:FATj#CREATURE:FORGOTTEN_BEAST_593:TALLOWj CREATURE:FORGOTTEN_BEAST_594:FATj#CREATURE:FORGOTTEN_BEAST_594:TALLOWj CREATURE:FORGOTTEN_BEAST_595:FATj#CREATURE:FORGOTTEN_BEAST_595:TALLOWj CREATURE:FORGOTTEN_BEAST_596:FATj#CREATURE:FORGOTTEN_BEAST_596:TALLOWj CREATURE:FORGOTTEN_BEAST_597:FATj#CREATURE:FORGOTTEN_BEAST_597:TALLOWj CREATURE:FORGOTTEN_BEAST_599:FATj#CREATURE:FORGOTTEN_BEAST_599:TALLOWj CREATURE:FORGOTTEN_BEAST_600:FATj#CREATURE:FORGOTTEN_BEAST_600:TALLOWj CREATURE:FORGOTTEN_BEAST_601:FATj#CREATURE:FORGOTTEN_BEAST_601:TALLOWj CREATURE:FORGOTTEN_BEAST_603:FATj#CREATURE:FORGOTTEN_BEAST_603:TALLOWj CREATURE:FORGOTTEN_BEAST_605:FATj#CREATURE:FORGOTTEN_BEAST_605:TALLOWj CREATURE:FORGOTTEN_BEAST_607:FATj#CREATURE:FORGOTTEN_BEAST_607:TALLOWj CREATURE:FORGOTTEN_BEAST_608:FATj#CREATURE:FORGOTTEN_BEAST_608:TALLOWj CREATURE:FORGOTTEN_BEAST_609:FATj#CREATURE:FORGOTTEN_BEAST_609:TALLOWj CREATURE:FORGOTTEN_BEAST_610:FATj#CREATURE:FORGOTTEN_BEAST_610:TALLOWj CREATURE:FORGOTTEN_BEAST_611:FATj#CREATURE:FORGOTTEN_BEAST_611:TALLOWj CREATURE:FORGOTTEN_BEAST_612:FATj#CREATURE:FORGOTTEN_BEAST_612:TALLOWj CREATURE:FORGOTTEN_BEAST_613:FATj#CREATURE:FORGOTTEN_BEAST_613:TALLOWj CREATURE:FORGOTTEN_BEAST_614:FATj#CREATURE:FORGOTTEN_BEAST_614:TALLOWj CREATURE:FORGOTTEN_BEAST_616:FATj#CREATURE:FORGOTTEN_BEAST_616:TALLOWj CREATURE:FORGOTTEN_BEAST_619:FATj#CREATURE:FORGOTTEN_BEAST_619:TALLOWj CREATURE:FORGOTTEN_BEAST_620:FATj#CREATURE:FORGOTTEN_BEAST_620:TALLOWj CREATURE:FORGOTTEN_BEAST_621:FATj#CREATURE:FORGOTTEN_BEAST_621:TALLOWj CREATURE:FORGOTTEN_BEAST_623:FATj#CREATURE:FORGOTTEN_BEAST_623:TALLOWj CREATURE:FORGOTTEN_BEAST_624:FATj#CREATURE:FORGOTTEN_BEAST_624:TALLOWj CREATURE:FORGOTTEN_BEAST_625:FATj#CREATURE:FORGOTTEN_BEAST_625:TALLOWj CREATURE:FORGOTTEN_BEAST_626:FATj#CREATURE:FORGOTTEN_BEAST_626:TALLOWj CREATURE:FORGOTTEN_BEAST_627:FATj#CREATURE:FORGOTTEN_BEAST_627:TALLOWj CREATURE:FORGOTTEN_BEAST_628:FATj#CREATURE:FORGOTTEN_BEAST_628:TALLOWj CREATURE:FORGOTTEN_BEAST_629:FATj#CREATURE:FORGOTTEN_BEAST_629:TALLOWj CREATURE:FORGOTTEN_BEAST_630:FATj#CREATURE:FORGOTTEN_BEAST_630:TALLOWj CREATURE:FORGOTTEN_BEAST_631:FATj#CREATURE:FORGOTTEN_BEAST_631:TALLOWj CREATURE:FORGOTTEN_BEAST_632:FATj#CREATURE:FORGOTTEN_BEAST_632:TALLOWj CREATURE:FORGOTTEN_BEAST_633:FATj#CREATURE:FORGOTTEN_BEAST_633:TALLOWj CREATURE:FORGOTTEN_BEAST_634:FATj#CREATURE:FORGOTTEN_BEAST_634:TALLOWj CREATURE:FORGOTTEN_BEAST_635:FATj#CREATURE:FORGOTTEN_BEAST_635:TALLOWj CREATURE:FORGOTTEN_BEAST_636:FATj#CREATURE:FORGOTTEN_BEAST_636:TALLOWj CREATURE:FORGOTTEN_BEAST_637:FATj#CREATURE:FORGOTTEN_BEAST_637:TALLOWj CREATURE:FORGOTTEN_BEAST_639:FATj#CREATURE:FORGOTTEN_BEAST_639:TALLOWj CREATURE:FORGOTTEN_BEAST_640:FATj#CREATURE:FORGOTTEN_BEAST_640:TALLOWj CREATURE:FORGOTTEN_BEAST_643:FATj#CREATURE:FORGOTTEN_BEAST_643:TALLOWj CREATURE:FORGOTTEN_BEAST_644:FATj#CREATURE:FORGOTTEN_BEAST_644:TALLOWj CREATURE:FORGOTTEN_BEAST_645:FATj#CREATURE:FORGOTTEN_BEAST_645:TALLOWj CREATURE:FORGOTTEN_BEAST_646:FATj#CREATURE:FORGOTTEN_BEAST_646:TALLOWj CREATURE:FORGOTTEN_BEAST_647:FATj#CREATURE:FORGOTTEN_BEAST_647:TALLOWj CREATURE:FORGOTTEN_BEAST_649:FATj#CREATURE:FORGOTTEN_BEAST_649:TALLOWj CREATURE:FORGOTTEN_BEAST_650:FATj#CREATURE:FORGOTTEN_BEAST_650:TALLOWj CREATURE:FORGOTTEN_BEAST_651:FATj#CREATURE:FORGOTTEN_BEAST_651:TALLOWj CREATURE:FORGOTTEN_BEAST_652:FATj#CREATURE:FORGOTTEN_BEAST_652:TALLOWj CREATURE:FORGOTTEN_BEAST_656:FATj#CREATURE:FORGOTTEN_BEAST_656:TALLOWj CREATURE:FORGOTTEN_BEAST_658:FATj#CREATURE:FORGOTTEN_BEAST_658:TALLOWj CREATURE:FORGOTTEN_BEAST_659:FATj#CREATURE:FORGOTTEN_BEAST_659:TALLOWj CREATURE:FORGOTTEN_BEAST_661:FATj#CREATURE:FORGOTTEN_BEAST_661:TALLOWj CREATURE:FORGOTTEN_BEAST_663:FATj#CREATURE:FORGOTTEN_BEAST_663:TALLOWj CREATURE:FORGOTTEN_BEAST_664:FATj#CREATURE:FORGOTTEN_BEAST_664:TALLOWj CREATURE:FORGOTTEN_BEAST_666:FATj#CREATURE:FORGOTTEN_BEAST_666:TALLOWj CREATURE:FORGOTTEN_BEAST_667:FATj#CREATURE:FORGOTTEN_BEAST_667:TALLOWj CREATURE:FORGOTTEN_BEAST_669:FATj#CREATURE:FORGOTTEN_BEAST_669:TALLOWj CREATURE:FORGOTTEN_BEAST_670:FATj#CREATURE:FORGOTTEN_BEAST_670:TALLOWj CREATURE:FORGOTTEN_BEAST_671:FATj#CREATURE:FORGOTTEN_BEAST_671:TALLOWj CREATURE:FORGOTTEN_BEAST_674:FATj#CREATURE:FORGOTTEN_BEAST_674:TALLOWj CREATURE:FORGOTTEN_BEAST_675:FATj#CREATURE:FORGOTTEN_BEAST_675:TALLOWj CREATURE:FORGOTTEN_BEAST_678:FATj#CREATURE:FORGOTTEN_BEAST_678:TALLOWj CREATURE:FORGOTTEN_BEAST_680:FATj#CREATURE:FORGOTTEN_BEAST_680:TALLOWj CREATURE:FORGOTTEN_BEAST_681:FATj#CREATURE:FORGOTTEN_BEAST_681:TALLOWj CREATURE:FORGOTTEN_BEAST_682:FATj#CREATURE:FORGOTTEN_BEAST_682:TALLOWj CREATURE:FORGOTTEN_BEAST_684:FATj#CREATURE:FORGOTTEN_BEAST_684:TALLOWj CREATURE:FORGOTTEN_BEAST_685:FATj#CREATURE:FORGOTTEN_BEAST_685:TALLOWj CREATURE:FORGOTTEN_BEAST_686:FATj#CREATURE:FORGOTTEN_BEAST_686:TALLOWj CREATURE:FORGOTTEN_BEAST_687:FATj#CREATURE:FORGOTTEN_BEAST_687:TALLOWj CREATURE:FORGOTTEN_BEAST_688:FATj#CREATURE:FORGOTTEN_BEAST_688:TALLOWj CREATURE:FORGOTTEN_BEAST_689:FATj#CREATURE:FORGOTTEN_BEAST_689:TALLOWj CREATURE:FORGOTTEN_BEAST_691:FATj#CREATURE:FORGOTTEN_BEAST_691:TALLOWj CREATURE:FORGOTTEN_BEAST_692:FATj#CREATURE:FORGOTTEN_BEAST_692:TALLOWj CREATURE:FORGOTTEN_BEAST_695:FATj#CREATURE:FORGOTTEN_BEAST_695:TALLOWj CREATURE:FORGOTTEN_BEAST_696:FATj#CREATURE:FORGOTTEN_BEAST_696:TALLOWj CREATURE:FORGOTTEN_BEAST_697:FATj#CREATURE:FORGOTTEN_BEAST_697:TALLOWj CREATURE:FORGOTTEN_BEAST_698:FATj#CREATURE:FORGOTTEN_BEAST_698:TALLOWj CREATURE:FORGOTTEN_BEAST_699:FATj#CREATURE:FORGOTTEN_BEAST_699:TALLOWj CREATURE:FORGOTTEN_BEAST_700:FATj#CREATURE:FORGOTTEN_BEAST_700:TALLOWj CREATURE:FORGOTTEN_BEAST_701:FATj#CREATURE:FORGOTTEN_BEAST_701:TALLOWj CREATURE:FORGOTTEN_BEAST_702:FATj#CREATURE:FORGOTTEN_BEAST_702:TALLOWj CREATURE:FORGOTTEN_BEAST_704:FATj#CREATURE:FORGOTTEN_BEAST_704:TALLOWj CREATURE:FORGOTTEN_BEAST_706:FATj#CREATURE:FORGOTTEN_BEAST_706:TALLOWj CREATURE:FORGOTTEN_BEAST_708:FATj#CREATURE:FORGOTTEN_BEAST_708:TALLOWj CREATURE:FORGOTTEN_BEAST_709:FATj#CREATURE:FORGOTTEN_BEAST_709:TALLOWj CREATURE:FORGOTTEN_BEAST_711:FATj#CREATURE:FORGOTTEN_BEAST_711:TALLOWj CREATURE:FORGOTTEN_BEAST_712:FATj#CREATURE:FORGOTTEN_BEAST_712:TALLOWj CREATURE:FORGOTTEN_BEAST_713:FATj#CREATURE:FORGOTTEN_BEAST_713:TALLOWj CREATURE:FORGOTTEN_BEAST_714:FATj#CREATURE:FORGOTTEN_BEAST_714:TALLOWj CREATURE:FORGOTTEN_BEAST_715:FATj#CREATURE:FORGOTTEN_BEAST_715:TALLOWj CREATURE:FORGOTTEN_BEAST_717:FATj#CREATURE:FORGOTTEN_BEAST_717:TALLOWj CREATURE:FORGOTTEN_BEAST_718:FATj#CREATURE:FORGOTTEN_BEAST_718:TALLOWj CREATURE:FORGOTTEN_BEAST_719:FATj#CREATURE:FORGOTTEN_BEAST_719:TALLOWj CREATURE:FORGOTTEN_BEAST_720:FATj#CREATURE:FORGOTTEN_BEAST_720:TALLOWj CREATURE:FORGOTTEN_BEAST_721:FATj#CREATURE:FORGOTTEN_BEAST_721:TALLOWj CREATURE:FORGOTTEN_BEAST_722:FATj#CREATURE:FORGOTTEN_BEAST_722:TALLOWj CREATURE:FORGOTTEN_BEAST_723:FATj#CREATURE:FORGOTTEN_BEAST_723:TALLOWj CREATURE:FORGOTTEN_BEAST_724:FATj#CREATURE:FORGOTTEN_BEAST_724:TALLOWj CREATURE:FORGOTTEN_BEAST_726:FATj#CREATURE:FORGOTTEN_BEAST_726:TALLOWj CREATURE:FORGOTTEN_BEAST_730:FATj#CREATURE:FORGOTTEN_BEAST_730:TALLOWj CREATURE:FORGOTTEN_BEAST_731:FATj#CREATURE:FORGOTTEN_BEAST_731:TALLOWj CREATURE:FORGOTTEN_BEAST_734:FATj#CREATURE:FORGOTTEN_BEAST_734:TALLOWj CREATURE:FORGOTTEN_BEAST_735:FATj#CREATURE:FORGOTTEN_BEAST_735:TALLOWj CREATURE:FORGOTTEN_BEAST_737:FATj#CREATURE:FORGOTTEN_BEAST_737:TALLOWj CREATURE:FORGOTTEN_BEAST_738:FATj#CREATURE:FORGOTTEN_BEAST_738:TALLOWj CREATURE:FORGOTTEN_BEAST_739:FATj#CREATURE:FORGOTTEN_BEAST_739:TALLOWj CREATURE:FORGOTTEN_BEAST_740:FATj#CREATURE:FORGOTTEN_BEAST_740:TALLOWj CREATURE:FORGOTTEN_BEAST_741:FATj#CREATURE:FORGOTTEN_BEAST_741:TALLOWj CREATURE:FORGOTTEN_BEAST_742:FATj#CREATURE:FORGOTTEN_BEAST_742:TALLOWj CREATURE:FORGOTTEN_BEAST_743:FATj#CREATURE:FORGOTTEN_BEAST_743:TALLOWj CREATURE:FORGOTTEN_BEAST_744:FATj#CREATURE:FORGOTTEN_BEAST_744:TALLOWj CREATURE:FORGOTTEN_BEAST_746:FATj#CREATURE:FORGOTTEN_BEAST_746:TALLOWj CREATURE:FORGOTTEN_BEAST_747:FATj#CREATURE:FORGOTTEN_BEAST_747:TALLOWj CREATURE:FORGOTTEN_BEAST_749:FATj#CREATURE:FORGOTTEN_BEAST_749:TALLOWj CREATURE:FORGOTTEN_BEAST_750:FATj#CREATURE:FORGOTTEN_BEAST_750:TALLOWj CREATURE:FORGOTTEN_BEAST_751:FATj#CREATURE:FORGOTTEN_BEAST_751:TALLOWj CREATURE:FORGOTTEN_BEAST_752:FATj#CREATURE:FORGOTTEN_BEAST_752:TALLOWj CREATURE:FORGOTTEN_BEAST_753:FATj#CREATURE:FORGOTTEN_BEAST_753:TALLOWj CREATURE:FORGOTTEN_BEAST_754:FATj#CREATURE:FORGOTTEN_BEAST_754:TALLOWj CREATURE:FORGOTTEN_BEAST_755:FATj#CREATURE:FORGOTTEN_BEAST_755:TALLOWj CREATURE:FORGOTTEN_BEAST_756:FATj#CREATURE:FORGOTTEN_BEAST_756:TALLOWj CREATURE:FORGOTTEN_BEAST_757:FATj#CREATURE:FORGOTTEN_BEAST_757:TALLOWj CREATURE:FORGOTTEN_BEAST_758:FATj#CREATURE:FORGOTTEN_BEAST_758:TALLOWj CREATURE:FORGOTTEN_BEAST_759:FATj#CREATURE:FORGOTTEN_BEAST_759:TALLOWj CREATURE:FORGOTTEN_BEAST_760:FATj#CREATURE:FORGOTTEN_BEAST_760:TALLOWj CREATURE:FORGOTTEN_BEAST_761:FATj#CREATURE:FORGOTTEN_BEAST_761:TALLOWj CREATURE:FORGOTTEN_BEAST_762:FATj#CREATURE:FORGOTTEN_BEAST_762:TALLOWj CREATURE:FORGOTTEN_BEAST_764:FATj#CREATURE:FORGOTTEN_BEAST_764:TALLOWj CREATURE:FORGOTTEN_BEAST_767:FATj#CREATURE:FORGOTTEN_BEAST_767:TALLOWj CREATURE:FORGOTTEN_BEAST_768:FATj#CREATURE:FORGOTTEN_BEAST_768:TALLOWj CREATURE:FORGOTTEN_BEAST_769:FATj#CREATURE:FORGOTTEN_BEAST_769:TALLOWj CREATURE:FORGOTTEN_BEAST_770:FATj#CREATURE:FORGOTTEN_BEAST_770:TALLOWj CREATURE:FORGOTTEN_BEAST_773:FATj#CREATURE:FORGOTTEN_BEAST_773:TALLOWj CREATURE:FORGOTTEN_BEAST_774:FATj#CREATURE:FORGOTTEN_BEAST_774:TALLOWj CREATURE:FORGOTTEN_BEAST_776:FATj#CREATURE:FORGOTTEN_BEAST_776:TALLOWj CREATURE:FORGOTTEN_BEAST_777:FATj#CREATURE:FORGOTTEN_BEAST_777:TALLOWj CREATURE:FORGOTTEN_BEAST_778:FATj#CREATURE:FORGOTTEN_BEAST_778:TALLOWj CREATURE:FORGOTTEN_BEAST_779:FATj#CREATURE:FORGOTTEN_BEAST_779:TALLOWj CREATURE:FORGOTTEN_BEAST_780:FATj#CREATURE:FORGOTTEN_BEAST_780:TALLOWj CREATURE:FORGOTTEN_BEAST_781:FATj#CREATURE:FORGOTTEN_BEAST_781:TALLOWj CREATURE:FORGOTTEN_BEAST_782:FATj#CREATURE:FORGOTTEN_BEAST_782:TALLOWj CREATURE:FORGOTTEN_BEAST_783:FATj#CREATURE:FORGOTTEN_BEAST_783:TALLOWj CREATURE:FORGOTTEN_BEAST_784:FATj#CREATURE:FORGOTTEN_BEAST_784:TALLOWj CREATURE:FORGOTTEN_BEAST_785:FATj#CREATURE:FORGOTTEN_BEAST_785:TALLOWj CREATURE:FORGOTTEN_BEAST_786:FATj#CREATURE:FORGOTTEN_BEAST_786:TALLOWj CREATURE:FORGOTTEN_BEAST_787:FATj#CREATURE:FORGOTTEN_BEAST_787:TALLOWj CREATURE:FORGOTTEN_BEAST_789:FATj#CREATURE:FORGOTTEN_BEAST_789:TALLOWj CREATURE:FORGOTTEN_BEAST_792:FATj#CREATURE:FORGOTTEN_BEAST_792:TALLOWj CREATURE:FORGOTTEN_BEAST_793:FATj#CREATURE:FORGOTTEN_BEAST_793:TALLOWj CREATURE:FORGOTTEN_BEAST_794:FATj#CREATURE:FORGOTTEN_BEAST_794:TALLOWj CREATURE:FORGOTTEN_BEAST_795:FATj#CREATURE:FORGOTTEN_BEAST_795:TALLOWj CREATURE:FORGOTTEN_BEAST_796:FATj#CREATURE:FORGOTTEN_BEAST_796:TALLOWj CREATURE:FORGOTTEN_BEAST_799:FATj#CREATURE:FORGOTTEN_BEAST_799:TALLOWj CREATURE:FORGOTTEN_BEAST_800:FATj#CREATURE:FORGOTTEN_BEAST_800:TALLOWj CREATURE:FORGOTTEN_BEAST_801:FATj#CREATURE:FORGOTTEN_BEAST_801:TALLOWj CREATURE:FORGOTTEN_BEAST_802:FATj#CREATURE:FORGOTTEN_BEAST_802:TALLOWj CREATURE:FORGOTTEN_BEAST_803:FATj#CREATURE:FORGOTTEN_BEAST_803:TALLOWj CREATURE:FORGOTTEN_BEAST_804:FATj#CREATURE:FORGOTTEN_BEAST_804:TALLOWj CREATURE:FORGOTTEN_BEAST_806:FATj#CREATURE:FORGOTTEN_BEAST_806:TALLOWj CREATURE:FORGOTTEN_BEAST_807:FATj#CREATURE:FORGOTTEN_BEAST_807:TALLOWj CREATURE:FORGOTTEN_BEAST_809:FATj#CREATURE:FORGOTTEN_BEAST_809:TALLOWj CREATURE:FORGOTTEN_BEAST_810:FATj#CREATURE:FORGOTTEN_BEAST_810:TALLOWj CREATURE:FORGOTTEN_BEAST_811:FATj#CREATURE:FORGOTTEN_BEAST_811:TALLOWj CREATURE:FORGOTTEN_BEAST_812:FATj#CREATURE:FORGOTTEN_BEAST_812:TALLOWj CREATURE:FORGOTTEN_BEAST_815:FATj#CREATURE:FORGOTTEN_BEAST_815:TALLOWj CREATURE:FORGOTTEN_BEAST_817:FATj#CREATURE:FORGOTTEN_BEAST_817:TALLOWj CREATURE:FORGOTTEN_BEAST_818:FATj#CREATURE:FORGOTTEN_BEAST_818:TALLOWj CREATURE:FORGOTTEN_BEAST_819:FATj#CREATURE:FORGOTTEN_BEAST_819:TALLOWj CREATURE:FORGOTTEN_BEAST_820:FATj#CREATURE:FORGOTTEN_BEAST_820:TALLOWj CREATURE:FORGOTTEN_BEAST_821:FATj#CREATURE:FORGOTTEN_BEAST_821:TALLOWj CREATURE:FORGOTTEN_BEAST_822:FATj#CREATURE:FORGOTTEN_BEAST_822:TALLOWj CREATURE:FORGOTTEN_BEAST_824:FATj#CREATURE:FORGOTTEN_BEAST_824:TALLOWj CREATURE:FORGOTTEN_BEAST_825:FATj#CREATURE:FORGOTTEN_BEAST_825:TALLOWj CREATURE:FORGOTTEN_BEAST_826:FATj#CREATURE:FORGOTTEN_BEAST_826:TALLOWj CREATURE:FORGOTTEN_BEAST_827:FATj#CREATURE:FORGOTTEN_BEAST_827:TALLOWj CREATURE:FORGOTTEN_BEAST_828:FATj#CREATURE:FORGOTTEN_BEAST_828:TALLOWj CREATURE:FORGOTTEN_BEAST_831:FATj#CREATURE:FORGOTTEN_BEAST_831:TALLOWj CREATURE:FORGOTTEN_BEAST_833:FATj#CREATURE:FORGOTTEN_BEAST_833:TALLOWj CREATURE:FORGOTTEN_BEAST_835:FATj#CREATURE:FORGOTTEN_BEAST_835:TALLOWj CREATURE:FORGOTTEN_BEAST_837:FATj#CREATURE:FORGOTTEN_BEAST_837:TALLOWj CREATURE:FORGOTTEN_BEAST_838:FATj#CREATURE:FORGOTTEN_BEAST_838:TALLOWj CREATURE:FORGOTTEN_BEAST_842:FATj#CREATURE:FORGOTTEN_BEAST_842:TALLOWj CREATURE:FORGOTTEN_BEAST_843:FATj#CREATURE:FORGOTTEN_BEAST_843:TALLOWj CREATURE:FORGOTTEN_BEAST_844:FATj#CREATURE:FORGOTTEN_BEAST_844:TALLOWj CREATURE:FORGOTTEN_BEAST_845:FATj#CREATURE:FORGOTTEN_BEAST_845:TALLOWj CREATURE:FORGOTTEN_BEAST_846:FATj#CREATURE:FORGOTTEN_BEAST_846:TALLOWj CREATURE:FORGOTTEN_BEAST_847:FATj#CREATURE:FORGOTTEN_BEAST_847:TALLOWj CREATURE:FORGOTTEN_BEAST_848:FATj#CREATURE:FORGOTTEN_BEAST_848:TALLOWj CREATURE:FORGOTTEN_BEAST_849:FATj#CREATURE:FORGOTTEN_BEAST_849:TALLOWj CREATURE:FORGOTTEN_BEAST_851:FATj#CREATURE:FORGOTTEN_BEAST_851:TALLOWj CREATURE:FORGOTTEN_BEAST_853:FATj#CREATURE:FORGOTTEN_BEAST_853:TALLOWj CREATURE:FORGOTTEN_BEAST_854:FATj#CREATURE:FORGOTTEN_BEAST_854:TALLOWj CREATURE:FORGOTTEN_BEAST_855:FATj#CREATURE:FORGOTTEN_BEAST_855:TALLOWj CREATURE:FORGOTTEN_BEAST_857:FATj#CREATURE:FORGOTTEN_BEAST_857:TALLOWj CREATURE:FORGOTTEN_BEAST_858:FATj#CREATURE:FORGOTTEN_BEAST_858:TALLOWj CREATURE:FORGOTTEN_BEAST_859:FATj#CREATURE:FORGOTTEN_BEAST_859:TALLOWj CREATURE:FORGOTTEN_BEAST_860:FATj#CREATURE:FORGOTTEN_BEAST_860:TALLOWj CREATURE:FORGOTTEN_BEAST_861:FATj#CREATURE:FORGOTTEN_BEAST_861:TALLOWj CREATURE:FORGOTTEN_BEAST_862:FATj#CREATURE:FORGOTTEN_BEAST_862:TALLOWj CREATURE:FORGOTTEN_BEAST_865:FATj#CREATURE:FORGOTTEN_BEAST_865:TALLOWj CREATURE:FORGOTTEN_BEAST_866:FATj#CREATURE:FORGOTTEN_BEAST_866:TALLOWj CREATURE:FORGOTTEN_BEAST_867:FATj#CREATURE:FORGOTTEN_BEAST_867:TALLOWjCREATURE:TITAN_1:FATjCREATURE:TITAN_1:TALLOWjCREATURE:TITAN_3:FATjCREATURE:TITAN_3:TALLOWjCREATURE:TITAN_4:FATjCREATURE:TITAN_4:TALLOWjCREATURE:TITAN_6:FATjCREATURE:TITAN_6:TALLOWjCREATURE:TITAN_7:FATjCREATURE:TITAN_7:TALLOWjCREATURE:TITAN_8:FATjCREATURE:TITAN_8:TALLOWjCREATURE:TITAN_9:FATjCREATURE:TITAN_9:TALLOWjCREATURE:TITAN_10:FATjCREATURE:TITAN_10:TALLOWjCREATURE:TITAN_11:FATjCREATURE:TITAN_11:TALLOWjCREATURE:TITAN_12:FATjCREATURE:TITAN_12:TALLOWjCREATURE:TITAN_13:FATjCREATURE:TITAN_13:TALLOWjCREATURE:TITAN_14:FATjCREATURE:TITAN_14:TALLOWjCREATURE:TITAN_15:FATjCREATURE:TITAN_15:TALLOWjCREATURE:TITAN_18:FATjCREATURE:TITAN_18:TALLOWjCREATURE:TITAN_19:FATjCREATURE:TITAN_19:TALLOWjCREATURE:TITAN_21:FATjCREATURE:TITAN_21:TALLOWjCREATURE:TITAN_23:FATjCREATURE:TITAN_23:TALLOWjCREATURE:TITAN_24:FATjCREATURE:TITAN_24:TALLOWjCREATURE:TITAN_25:FATjCREATURE:TITAN_25:TALLOWjCREATURE:TITAN_26:FATjCREATURE:TITAN_26:TALLOWjCREATURE:TITAN_27:FATjCREATURE:TITAN_27:TALLOWjCREATURE:TITAN_28:FATjCREATURE:TITAN_28:TALLOWjCREATURE:TITAN_29:FATjCREATURE:TITAN_29:TALLOWjCREATURE:TITAN_30:FATjCREATURE:TITAN_30:TALLOWjCREATURE:TITAN_32:FATjCREATURE:TITAN_32:TALLOWjCREATURE:TITAN_33:FATjCREATURE:TITAN_33:TALLOWjCREATURE:DEMON_7:FATjCREATURE:DEMON_7:TALLOWjCREATURE:DEMON_8:FATjCREATURE:DEMON_8:TALLOWjCREATURE:DEMON_9:FATjCREATURE:DEMON_9:TALLOWjCREATURE:DEMON_10:FATjCREATURE:DEMON_10:TALLOWjCREATURE:DEMON_11:FATjCREATURE:DEMON_11:TALLOWjCREATURE:DEMON_12:FATjCREATURE:DEMON_12:TALLOWjCREATURE:DEMON_13:FATjCREATURE:DEMON_13:TALLOWjCREATURE:DEMON_14:FATjCREATURE:DEMON_14:TALLOWjCREATURE:DEMON_15:FATjCREATURE:DEMON_15:TALLOWjCREATURE:DEMON_16:FATjCREATURE:DEMON_16:TALLOWjCREATURE:DEMON_17:FATjCREATURE:DEMON_17:TALLOWjCREATURE:DEMON_18:FATjCREATURE:DEMON_18:TALLOWjCREATURE:DEMON_19:FATjCREATURE:DEMON_19:TALLOWjCREATURE:DEMON_20:FATjCREATURE:DEMON_20:TALLOWjCREATURE:DEMON_21:FATjCREATURE:DEMON_21:TALLOWjCREATURE:DEMON_22:FATjCREATURE:DEMON_22:TALLOWjCREATURE:DEMON_23:FATjCREATURE:DEMON_23:TALLOWjCREATURE:DEMON_24:FATjCREATURE:DEMON_24:TALLOWjCREATURE:DEMON_25:FATjCREATURE:DEMON_25:TALLOWjCREATURE:DEMON_26:FATjCREATURE:DEMON_26:TALLOWjCREATURE:DEMON_27:FATjCREATURE:DEMON_27:TALLOWjCREATURE:DEMON_28:FATjCREATURE:DEMON_28:TALLOWjCREATURE:DEMON_29:FATjCREATURE:DEMON_29:TALLOWjCREATURE:DEMON_31:FATjCREATURE:DEMON_31:TALLOWjCREATURE:DEMON_32:FATjCREATURE:DEMON_32:TALLOWjCREATURE:DEMON_38:FATjCREATURE:DEMON_38:TALLOWjCREATURE:DEMON_39:FATjCREATURE:DEMON_39:TALLOWjCREATURE:DEMON_40:FATjCREATURE:DEMON_40:TALLOWjCREATURE:DEMON_41:FATjCREATURE:DEMON_41:TALLOWjCREATURE:DEMON_42:FATjCREATURE:DEMON_42:TALLOWjCREATURE:DEMON_43:FATjCREATURE:DEMON_43:TALLOWjCREATURE:DEMON_44:FATjCREATURE:DEMON_44:TALLOWjCREATURE:DEMON_45:FATjCREATURE:DEMON_45:TALLOWjCREATURE:DEMON_47:FATjCREATURE:DEMON_47:TALLOWjCREATURE:DEMON_48:FATjCREATURE:DEMON_48:TALLOWjCREATURE:DEMON_49:FATjCREATURE:DEMON_49:TALLOWjCREATURE:DEMON_52:FATjCREATURE:DEMON_52:TALLOWjCREATURE:NIGHT_CREATURE_1:FATj CREATURE:NIGHT_CREATURE_1:TALLOWjCREATURE:NIGHT_CREATURE_2:FATj CREATURE:NIGHT_CREATURE_2:TALLOWjCREATURE:NIGHT_CREATURE_3:FATj CREATURE:NIGHT_CREATURE_3:TALLOWjCREATURE:NIGHT_CREATURE_4:FATj CREATURE:NIGHT_CREATURE_4:TALLOWjCREATURE:NIGHT_CREATURE_5:FATj CREATURE:NIGHT_CREATURE_5:TALLOWjCREATURE:NIGHT_CREATURE_6:FATj CREATURE:NIGHT_CREATURE_6:TALLOWjCREATURE:NIGHT_CREATURE_7:FATj CREATURE:NIGHT_CREATURE_7:TALLOWjCREATURE:NIGHT_CREATURE_8:FATj CREATURE:NIGHT_CREATURE_8:TALLOWjCREATURE:NIGHT_CREATURE_9:FATj CREATURE:NIGHT_CREATURE_9:TALLOWjCREATURE:NIGHT_CREATURE_10:FATj!CREATURE:NIGHT_CREATURE_10:TALLOWjCREATURE:NIGHT_CREATURE_11:FATj!CREATURE:NIGHT_CREATURE_11:TALLOWjCREATURE:NIGHT_CREATURE_12:FATj!CREATURE:NIGHT_CREATURE_12:TALLOWjCREATURE:NIGHT_CREATURE_13:FATj!CREATURE:NIGHT_CREATURE_13:TALLOWjCREATURE:NIGHT_CREATURE_14:FATj!CREATURE:NIGHT_CREATURE_14:TALLOWjCREATURE:NIGHT_CREATURE_15:FATj!CREATURE:NIGHT_CREATURE_15:TALLOWjCREATURE:NIGHT_CREATURE_16:FATj!CREATURE:NIGHT_CREATURE_16:TALLOWjCREATURE:NIGHT_CREATURE_17:FATj!CREATURE:NIGHT_CREATURE_17:TALLOWjCREATURE:NIGHT_CREATURE_18:FATj!CREATURE:NIGHT_CREATURE_18:TALLOWjCREATURE:NIGHT_CREATURE_19:FATj!CREATURE:NIGHT_CREATURE_19:TALLOWjCREATURE:NIGHT_CREATURE_20:FATj!CREATURE:NIGHT_CREATURE_20:TALLOWjCREATURE:NIGHT_CREATURE_21:FATj!CREATURE:NIGHT_CREATURE_21:TALLOWjCREATURE:NIGHT_CREATURE_22:FATj!CREATURE:NIGHT_CREATURE_22:TALLOWjCREATURE:NIGHT_CREATURE_23:FATj!CREATURE:NIGHT_CREATURE_23:TALLOWjCREATURE:NIGHT_CREATURE_24:FATj!CREATURE:NIGHT_CREATURE_24:TALLOWjCREATURE:NIGHT_CREATURE_25:FATj!CREATURE:NIGHT_CREATURE_25:TALLOWjCREATURE:NIGHT_CREATURE_26:FATj!CREATURE:NIGHT_CREATURE_26:TALLOWjCREATURE:NIGHT_CREATURE_27:FATj!CREATURE:NIGHT_CREATURE_27:TALLOWjCREATURE:NIGHT_CREATURE_28:FATj!CREATURE:NIGHT_CREATURE_28:TALLOWjCREATURE:NIGHT_CREATURE_29:FATj!CREATURE:NIGHT_CREATURE_29:TALLOWjCREATURE:NIGHT_CREATURE_30:FATj!CREATURE:NIGHT_CREATURE_30:TALLOWjCREATURE:NIGHT_CREATURE_31:FATj!CREATURE:NIGHT_CREATURE_31:TALLOWjCREATURE:NIGHT_CREATURE_32:FATj!CREATURE:NIGHT_CREATURE_32:TALLOWjCREATURE:NIGHT_CREATURE_33:FATj!CREATURE:NIGHT_CREATURE_33:TALLOWjCREATURE:NIGHT_CREATURE_34:FATj!CREATURE:NIGHT_CREATURE_34:TALLOWjCREATURE:NIGHT_CREATURE_35:FATj!CREATURE:NIGHT_CREATURE_35:TALLOWjCREATURE:NIGHT_CREATURE_36:FATj!CREATURE:NIGHT_CREATURE_36:TALLOWjCREATURE:NIGHT_CREATURE_37:FATj!CREATURE:NIGHT_CREATURE_37:TALLOWjCREATURE:NIGHT_CREATURE_38:FATj!CREATURE:NIGHT_CREATURE_38:TALLOWjCREATURE:NIGHT_CREATURE_39:FATj!CREATURE:NIGHT_CREATURE_39:TALLOWjCREATURE:NIGHT_CREATURE_40:FATj!CREATURE:NIGHT_CREATURE_40:TALLOWjCREATURE:NIGHT_CREATURE_41:FATj!CREATURE:NIGHT_CREATURE_41:TALLOWjCREATURE:NIGHT_CREATURE_42:FATj!CREATURE:NIGHT_CREATURE_42:TALLOWjCREATURE:NIGHT_CREATURE_43:FATj!CREATURE:NIGHT_CREATURE_43:TALLOWjCREATURE:NIGHT_CREATURE_44:FATj!CREATURE:NIGHT_CREATURE_44:TALLOWjCREATURE:NIGHT_CREATURE_45:FATj!CREATURE:NIGHT_CREATURE_45:TALLOWjCREATURE:NIGHT_CREATURE_46:FATj!CREATURE:NIGHT_CREATURE_46:TALLOWjCREATURE:NIGHT_CREATURE_47:FATj!CREATURE:NIGHT_CREATURE_47:TALLOWjCREATURE:NIGHT_CREATURE_48:FATj!CREATURE:NIGHT_CREATURE_48:TALLOWjCREATURE:NIGHT_CREATURE_49:FATj!CREATURE:NIGHT_CREATURE_49:TALLOWjCREATURE:NIGHT_CREATURE_50:FATj!CREATURE:NIGHT_CREATURE_50:TALLOWjCREATURE:NIGHT_CREATURE_51:FATj!CREATURE:NIGHT_CREATURE_51:TALLOWjCREATURE:NIGHT_CREATURE_52:FATj!CREATURE:NIGHT_CREATURE_52:TALLOWjCREATURE:NIGHT_CREATURE_53:FATj!CREATURE:NIGHT_CREATURE_53:TALLOWjCREATURE:NIGHT_CREATURE_54:FATj!CREATURE:NIGHT_CREATURE_54:TALLOWjCREATURE:NIGHT_CREATURE_55:FATj!CREATURE:NIGHT_CREATURE_55:TALLOWjCREATURE:NIGHT_CREATURE_56:FATj!CREATURE:NIGHT_CREATURE_56:TALLOWjCREATURE:NIGHT_CREATURE_57:FATj!CREATURE:NIGHT_CREATURE_57:TALLOWjCREATURE:NIGHT_CREATURE_58:FATj!CREATURE:NIGHT_CREATURE_58:TALLOWjCREATURE:NIGHT_CREATURE_59:FATj!CREATURE:NIGHT_CREATURE_59:TALLOWjCREATURE:NIGHT_CREATURE_60:FATj!CREATURE:NIGHT_CREATURE_60:TALLOWjCREATURE:NIGHT_CREATURE_61:FATj!CREATURE:NIGHT_CREATURE_61:TALLOWjCREATURE:NIGHT_CREATURE_62:FATj!CREATURE:NIGHT_CREATURE_62:TALLOWjCREATURE:NIGHT_CREATURE_63:FATj!CREATURE:NIGHT_CREATURE_63:TALLOWjCREATURE:NIGHT_CREATURE_64:FATj!CREATURE:NIGHT_CREATURE_64:TALLOWjCREATURE:NIGHT_CREATURE_65:FATj!CREATURE:NIGHT_CREATURE_65:TALLOWjCREATURE:NIGHT_CREATURE_66:FATj!CREATURE:NIGHT_CREATURE_66:TALLOWjCREATURE:NIGHT_CREATURE_67:FATj!CREATURE:NIGHT_CREATURE_67:TALLOWjCREATURE:NIGHT_CREATURE_68:FATj!CREATURE:NIGHT_CREATURE_68:TALLOWjCREATURE:NIGHT_CREATURE_69:FATj!CREATURE:NIGHT_CREATURE_69:TALLOWjCREATURE:NIGHT_CREATURE_70:FATj!CREATURE:NIGHT_CREATURE_70:TALLOWjCREATURE:NIGHT_CREATURE_71:FATj!CREATURE:NIGHT_CREATURE_71:TALLOWjCREATURE:NIGHT_CREATURE_72:FATj!CREATURE:NIGHT_CREATURE_72:TALLOWjCREATURE:NIGHT_CREATURE_73:FATj!CREATURE:NIGHT_CREATURE_73:TALLOWjCREATURE:NIGHT_CREATURE_74:FATj!CREATURE:NIGHT_CREATURE_74:TALLOWjCREATURE:NIGHT_CREATURE_75:FATj!CREATURE:NIGHT_CREATURE_75:TALLOWjCREATURE:NIGHT_CREATURE_76:FATj!CREATURE:NIGHT_CREATURE_76:TALLOWjCREATURE:NIGHT_CREATURE_77:FATj!CREATURE:NIGHT_CREATURE_77:TALLOWjCREATURE:NIGHT_CREATURE_78:FATj!CREATURE:NIGHT_CREATURE_78:TALLOWjCREATURE:NIGHT_CREATURE_79:FATj!CREATURE:NIGHT_CREATURE_79:TALLOWjCREATURE:NIGHT_CREATURE_80:FATj!CREATURE:NIGHT_CREATURE_80:TALLOWjCREATURE:NIGHT_CREATURE_81:FATj!CREATURE:NIGHT_CREATURE_81:TALLOWjCREATURE:NIGHT_CREATURE_82:FATj!CREATURE:NIGHT_CREATURE_82:TALLOWjCREATURE:NIGHT_CREATURE_83:FATj!CREATURE:NIGHT_CREATURE_83:TALLOWjCREATURE:NIGHT_CREATURE_84:FATj!CREATURE:NIGHT_CREATURE_84:TALLOWjCREATURE:NIGHT_CREATURE_85:FATj!CREATURE:NIGHT_CREATURE_85:TALLOWjCREATURE:NIGHT_CREATURE_86:FATj!CREATURE:NIGHT_CREATURE_86:TALLOWjCREATURE:NIGHT_CREATURE_87:FATj!CREATURE:NIGHT_CREATURE_87:TALLOWjCREATURE:NIGHT_CREATURE_88:FATj!CREATURE:NIGHT_CREATURE_88:TALLOWjCREATURE:NIGHT_CREATURE_89:FATj!CREATURE:NIGHT_CREATURE_89:TALLOWjCREATURE:NIGHT_CREATURE_90:FATj!CREATURE:NIGHT_CREATURE_90:TALLOWjCREATURE:NIGHT_CREATURE_91:FATj!CREATURE:NIGHT_CREATURE_91:TALLOWjCREATURE:NIGHT_CREATURE_92:FATj!CREATURE:NIGHT_CREATURE_92:TALLOWjCREATURE:NIGHT_CREATURE_93:FATj!CREATURE:NIGHT_CREATURE_93:TALLOWjCREATURE:NIGHT_CREATURE_94:FATj!CREATURE:NIGHT_CREATURE_94:TALLOWjCREATURE:NIGHT_CREATURE_95:FATj!CREATURE:NIGHT_CREATURE_95:TALLOWjCREATURE:NIGHT_CREATURE_96:FATj!CREATURE:NIGHT_CREATURE_96:TALLOWjCREATURE:NIGHT_CREATURE_97:FATj!CREATURE:NIGHT_CREATURE_97:TALLOWjCREATURE:NIGHT_CREATURE_98:FATj!CREATURE:NIGHT_CREATURE_98:TALLOWjCREATURE:NIGHT_CREATURE_99:FATj!CREATURE:NIGHT_CREATURE_99:TALLOWjCREATURE:NIGHT_CREATURE_100:FATj"CREATURE:NIGHT_CREATURE_100:TALLOWjCREATURE:NIGHT_CREATURE_101:FATj"CREATURE:NIGHT_CREATURE_101:TALLOWjCREATURE:NIGHT_CREATURE_102:FATj"CREATURE:NIGHT_CREATURE_102:TALLOWjCREATURE:NIGHT_CREATURE_103:FATj"CREATURE:NIGHT_CREATURE_103:TALLOWjCREATURE:NIGHT_CREATURE_104:FATj"CREATURE:NIGHT_CREATURE_104:TALLOWjCREATURE:HF1248 DIVINE_1:FATjCREATURE:HF1248 DIVINE_1:TALLOWjCREATURE:HF1248 DIVINE_2:FATjCREATURE:HF1248 DIVINE_2:TALLOWjCREATURE:HF1248 DIVINE_3:FATjCREATURE:HF1248 DIVINE_3:TALLOWjCREATURE:HF1108 DIVINE_2:FATjCREATURE:HF1108 DIVINE_2:TALLOWjCREATURE:HF1249 DIVINE_1:FATjCREATURE:HF1249 DIVINE_1:TALLOWjCREATURE:HF1249 DIVINE_3:FATjCREATURE:HF1249 DIVINE_3:TALLOWjCREATURE:HF1345 DIVINE_3:FATjCREATURE:HF1345 DIVINE_3:TALLOWrPLANT:FLAX:SEEDrPLANT:FLAX:THREADrPLANT:JUTE:THREADrPLANT:HEMP:SEEDrPLANT:HEMP:THREADrPLANT:COTTON:SEEDrPLANT:COTTON:THREADrPLANT:RAMIE:THREADrPLANT:KENAF:SEEDrPLANT:KENAF:THREADrPLANT:GRASS_TAIL_PIG:THREADrPLANT:BUSH_QUARRY:SEEDrPLANT:REED_ROPE:THREADzPLANT:FLAX:SEEDzPLANT:HEMP:SEEDzPLANT:COTTON:SEEDzPLANT:KENAF:SEEDzPLANT:OLIVE:FRUITzPLANT:BUSH_QUARRY:SEEDzCREATURE:HONEY_BEE:WAX‚PLANT:FLAX:OIL‚PLANT:HEMP:OIL‚PLANT:COTTON:OIL‚PLANT:KENAF:OIL‚PLANT:OLIVE:OIL‚PLANT:POD_SWEET:EXTRACT‚PLANT:BUSH_QUARRY:OIL‚PLANT:BULB_KOBOLD:EXTRACT‚PLANT:HERB_VALLEY:EXTRACTŠCREATURE:BARK_SCORPION:VENOMŠ CREATURE:BARK_SCORPION_MAN:VENOMŠ"CREATURE:GIANT_BARK_SCORPION:VENOMŠ#CREATURE:SPIDER_BROWN_RECLUSE:VENOMŠ'CREATURE:BROWN_RECLUSE_SPIDER_MAN:VENOMŠ)CREATURE:GIANT_BROWN_RECLUSE_SPIDER:VENOMŠCREATURE:GILA_MONSTER:VENOMŠCREATURE:GILA_MONSTER_MAN:VENOMŠ!CREATURE:GIANT_GILA_MONSTER:VENOMŠCREATURE:DONKEY:MILKŠCREATURE:HORSE:MILKŠCREATURE:COW:MILKŠCREATURE:SHEEP:MILKŠCREATURE:PIG:MILKŠCREATURE:GOAT:MILKŠCREATURE:WATER_BUFFALO:MILKŠCREATURE:REINDEER:MILKŠCREATURE:YAK:MILKŠCREATURE:LLAMA:MILKŠCREATURE:ALPACA:MILKŠCREATURE:HONEY_BEE:ROYAL_JELLYŠCREATURE:HONEY_BEE:HONEYŠCREATURE:HONEY_BEE:VENOMŠCREATURE:BUMBLEBEE:ROYAL_JELLYŠCREATURE:BUMBLEBEE:HONEYŠCREATURE:BUMBLEBEE:VENOMŠCREATURE:PLATYPUS:VENOMŠCREATURE:PLATYPUS MAN:VENOMŠCREATURE:PLATYPUS, GIANT:VENOMŠCREATURE:CAMEL_1_HUMP:MILKŠCREATURE:CAMEL_1_HUMP_MAN:MILKŠ CREATURE:GIANT_CAMEL_1_HUMP:MILKŠCREATURE:CAMEL_2_HUMP:MILKŠCREATURE:CAMEL_2_HUMP_MAN:MILKŠ CREATURE:GIANT_CAMEL_2_HUMP:MILKŠCREATURE:HELMET_SNAKE:VENOMŠCREATURE:CAVE_FLOATER:POD_JUICEŠCREATURE:CAVE_BLOB:FLUIDŠCREATURE:OCTOPUS:INKŠCREATURE:OCTOPUS_MAN:INKŠCREATURE:GIANT_OCTOPUS:INKŠCREATURE:CUTTLEFISH:INKŠCREATURE:CUTTLEFISH_MAN:INKŠCREATURE:GIANT_CUTTLEFISH:INKŠCREATURE:MOGHOPPER:MOG_JUICEŠCREATURE:SPIDER_PHANTOM:VENOMŠCREATURE:SQUID:INKŠCREATURE:SQUID MAN:INKŠCREATURE:GIGANTIC SQUID:INKŠ!CREATURE:SPIDER_CAVE_GIANT:POISONŠCREATURE:SPIDER_CAVE:VENOMŠCREATURE:MAGGOT_PURRING:MILKŠCREATURE:ELEMENTMAN_IRON:GASŠCREATURE:SERPENT_MAN:VENOMŠCREATURE:KANGAROO:MILKŠCREATURE:KANGAROO_MAN:MILKŠCREATURE:GIANT_KANGAROO:MILKŠCREATURE:ADDER:VENOMŠCREATURE:ADDER_MAN:VENOMŠCREATURE:GIANT_ADDER:VENOMŠCREATURE:RATTLESNAKE:VENOMŠCREATURE:RATTLESNAKE_MAN:VENOMŠ CREATURE:GIANT_RATTLESNAKE:VENOMŠCREATURE:COPPERHEAD_SNAKE:VENOMŠ#CREATURE:COPPERHEAD_SNAKE_MAN:VENOMŠ%CREATURE:GIANT_COPPERHEAD_SNAKE:VENOMŠCREATURE:KING_COBRA:VENOMŠCREATURE:KING_COBRA_MAN:VENOMŠCREATURE:GIANT_KING_COBRA:VENOMŠCREATURE:BLACK_MAMBA:VENOMŠCREATURE:BLACK_MAMBA_MAN:VENOMŠ CREATURE:GIANT_BLACK_MAMBA:VENOMŠCREATURE:BUSHMASTER:VENOMŠCREATURE:BUSHMASTER_MAN:VENOMŠCREATURE:GIANT_BUSHMASTER:VENOMŠCREATURE:TAPIR:MILKŠCREATURE:TAPIR_MAN:MILKŠCREATURE:GIANT_TAPIR:MILKŠ!CREATURE:FORGOTTEN_BEAST_2:POISONŠ!CREATURE:FORGOTTEN_BEAST_4:POISONŠ!CREATURE:FORGOTTEN_BEAST_5:POISONŠ!CREATURE:FORGOTTEN_BEAST_7:POISONŠ!CREATURE:FORGOTTEN_BEAST_8:POISONŠ!CREATURE:FORGOTTEN_BEAST_9:POISONŠ"CREATURE:FORGOTTEN_BEAST_11:POISONŠ"CREATURE:FORGOTTEN_BEAST_13:POISONŠ"CREATURE:FORGOTTEN_BEAST_15:POISONŠ"CREATURE:FORGOTTEN_BEAST_16:POISONŠ"CREATURE:FORGOTTEN_BEAST_18:POISONŠ"CREATURE:FORGOTTEN_BEAST_19:POISONŠ"CREATURE:FORGOTTEN_BEAST_20:POISONŠ"CREATURE:FORGOTTEN_BEAST_21:POISONŠ"CREATURE:FORGOTTEN_BEAST_22:POISONŠ"CREATURE:FORGOTTEN_BEAST_24:POISONŠ"CREATURE:FORGOTTEN_BEAST_25:POISONŠ"CREATURE:FORGOTTEN_BEAST_27:POISONŠ"CREATURE:FORGOTTEN_BEAST_28:POISONŠ"CREATURE:FORGOTTEN_BEAST_33:POISONŠ"CREATURE:FORGOTTEN_BEAST_36:POISONŠ"CREATURE:FORGOTTEN_BEAST_37:POISONŠ"CREATURE:FORGOTTEN_BEAST_39:POISONŠ"CREATURE:FORGOTTEN_BEAST_40:POISONŠ"CREATURE:FORGOTTEN_BEAST_41:POISONŠ"CREATURE:FORGOTTEN_BEAST_43:POISONŠ"CREATURE:FORGOTTEN_BEAST_44:POISONŠ"CREATURE:FORGOTTEN_BEAST_45:POISONŠ"CREATURE:FORGOTTEN_BEAST_46:POISONŠ"CREATURE:FORGOTTEN_BEAST_48:POISONŠ"CREATURE:FORGOTTEN_BEAST_49:POISONŠ"CREATURE:FORGOTTEN_BEAST_50:POISONŠ"CREATURE:FORGOTTEN_BEAST_51:POISONŠ"CREATURE:FORGOTTEN_BEAST_56:POISONŠ"CREATURE:FORGOTTEN_BEAST_59:POISONŠ"CREATURE:FORGOTTEN_BEAST_61:POISONŠ"CREATURE:FORGOTTEN_BEAST_62:POISONŠ"CREATURE:FORGOTTEN_BEAST_64:POISONŠ"CREATURE:FORGOTTEN_BEAST_66:POISONŠ"CREATURE:FORGOTTEN_BEAST_67:POISONŠ"CREATURE:FORGOTTEN_BEAST_69:POISONŠ"CREATURE:FORGOTTEN_BEAST_70:POISONŠ"CREATURE:FORGOTTEN_BEAST_72:POISONŠ"CREATURE:FORGOTTEN_BEAST_73:POISONŠ"CREATURE:FORGOTTEN_BEAST_75:POISONŠ"CREATURE:FORGOTTEN_BEAST_77:POISONŠ"CREATURE:FORGOTTEN_BEAST_78:POISONŠ"CREATURE:FORGOTTEN_BEAST_80:POISONŠ"CREATURE:FORGOTTEN_BEAST_82:POISONŠ"CREATURE:FORGOTTEN_BEAST_84:POISONŠ"CREATURE:FORGOTTEN_BEAST_88:POISONŠ"CREATURE:FORGOTTEN_BEAST_89:POISONŠ"CREATURE:FORGOTTEN_BEAST_90:POISONŠ"CREATURE:FORGOTTEN_BEAST_92:POISONŠ"CREATURE:FORGOTTEN_BEAST_93:POISONŠ"CREATURE:FORGOTTEN_BEAST_94:POISONŠ"CREATURE:FORGOTTEN_BEAST_95:POISONŠ"CREATURE:FORGOTTEN_BEAST_97:POISONŠ"CREATURE:FORGOTTEN_BEAST_98:POISONŠ#CREATURE:FORGOTTEN_BEAST_100:POISONŠ#CREATURE:FORGOTTEN_BEAST_104:POISONŠ#CREATURE:FORGOTTEN_BEAST_105:POISONŠ#CREATURE:FORGOTTEN_BEAST_106:POISONŠ#CREATURE:FORGOTTEN_BEAST_107:POISONŠ#CREATURE:FORGOTTEN_BEAST_108:POISONŠ#CREATURE:FORGOTTEN_BEAST_110:POISONŠ#CREATURE:FORGOTTEN_BEAST_114:POISONŠ#CREATURE:FORGOTTEN_BEAST_115:POISONŠ#CREATURE:FORGOTTEN_BEAST_116:POISONŠ#CREATURE:FORGOTTEN_BEAST_117:POISONŠ#CREATURE:FORGOTTEN_BEAST_118:POISONŠ#CREATURE:FORGOTTEN_BEAST_120:POISONŠ#CREATURE:FORGOTTEN_BEAST_122:POISONŠ#CREATURE:FORGOTTEN_BEAST_123:POISONŠ#CREATURE:FORGOTTEN_BEAST_124:POISONŠ#CREATURE:FORGOTTEN_BEAST_126:POISONŠ#CREATURE:FORGOTTEN_BEAST_127:POISONŠ#CREATURE:FORGOTTEN_BEAST_130:POISONŠ#CREATURE:FORGOTTEN_BEAST_133:POISONŠ#CREATURE:FORGOTTEN_BEAST_134:POISONŠ#CREATURE:FORGOTTEN_BEAST_135:POISONŠ#CREATURE:FORGOTTEN_BEAST_136:POISONŠ#CREATURE:FORGOTTEN_BEAST_138:POISONŠ#CREATURE:FORGOTTEN_BEAST_139:POISONŠ#CREATURE:FORGOTTEN_BEAST_141:POISONŠ#CREATURE:FORGOTTEN_BEAST_143:POISONŠ#CREATURE:FORGOTTEN_BEAST_145:POISONŠ#CREATURE:FORGOTTEN_BEAST_147:POISONŠ#CREATURE:FORGOTTEN_BEAST_148:POISONŠ#CREATURE:FORGOTTEN_BEAST_151:POISONŠ#CREATURE:FORGOTTEN_BEAST_154:POISONŠ#CREATURE:FORGOTTEN_BEAST_155:POISONŠ#CREATURE:FORGOTTEN_BEAST_156:POISONŠ#CREATURE:FORGOTTEN_BEAST_157:POISONŠ#CREATURE:FORGOTTEN_BEAST_158:POISONŠ#CREATURE:FORGOTTEN_BEAST_160:POISONŠ#CREATURE:FORGOTTEN_BEAST_163:POISONŠ#CREATURE:FORGOTTEN_BEAST_165:POISONŠ#CREATURE:FORGOTTEN_BEAST_167:POISONŠ#CREATURE:FORGOTTEN_BEAST_170:POISONŠ#CREATURE:FORGOTTEN_BEAST_171:POISONŠ#CREATURE:FORGOTTEN_BEAST_172:POISONŠ#CREATURE:FORGOTTEN_BEAST_175:POISONŠ#CREATURE:FORGOTTEN_BEAST_176:POISONŠ#CREATURE:FORGOTTEN_BEAST_178:POISONŠ#CREATURE:FORGOTTEN_BEAST_179:POISONŠ#CREATURE:FORGOTTEN_BEAST_182:POISONŠ#CREATURE:FORGOTTEN_BEAST_183:POISONŠ#CREATURE:FORGOTTEN_BEAST_184:POISONŠ#CREATURE:FORGOTTEN_BEAST_185:POISONŠ#CREATURE:FORGOTTEN_BEAST_187:POISONŠ#CREATURE:FORGOTTEN_BEAST_188:POISONŠ#CREATURE:FORGOTTEN_BEAST_189:POISONŠ#CREATURE:FORGOTTEN_BEAST_190:POISONŠ#CREATURE:FORGOTTEN_BEAST_191:POISONŠ#CREATURE:FORGOTTEN_BEAST_192:POISONŠ#CREATURE:FORGOTTEN_BEAST_193:POISONŠ#CREATURE:FORGOTTEN_BEAST_194:POISONŠ#CREATURE:FORGOTTEN_BEAST_196:POISONŠ#CREATURE:FORGOTTEN_BEAST_197:POISONŠ#CREATURE:FORGOTTEN_BEAST_198:POISONŠ#CREATURE:FORGOTTEN_BEAST_199:POISONŠ#CREATURE:FORGOTTEN_BEAST_201:POISONŠ#CREATURE:FORGOTTEN_BEAST_203:POISONŠ#CREATURE:FORGOTTEN_BEAST_206:POISONŠ#CREATURE:FORGOTTEN_BEAST_207:POISONŠ#CREATURE:FORGOTTEN_BEAST_208:POISONŠ#CREATURE:FORGOTTEN_BEAST_210:POISONŠ#CREATURE:FORGOTTEN_BEAST_212:POISONŠ#CREATURE:FORGOTTEN_BEAST_213:POISONŠ#CREATURE:FORGOTTEN_BEAST_215:POISONŠ#CREATURE:FORGOTTEN_BEAST_216:POISONŠ#CREATURE:FORGOTTEN_BEAST_217:POISONŠ#CREATURE:FORGOTTEN_BEAST_218:POISONŠ#CREATURE:FORGOTTEN_BEAST_219:POISONŠ#CREATURE:FORGOTTEN_BEAST_220:POISONŠ#CREATURE:FORGOTTEN_BEAST_221:POISONŠ#CREATURE:FORGOTTEN_BEAST_223:POISONŠ#CREATURE:FORGOTTEN_BEAST_225:POISONŠ#CREATURE:FORGOTTEN_BEAST_227:POISONŠ#CREATURE:FORGOTTEN_BEAST_228:POISONŠ#CREATURE:FORGOTTEN_BEAST_230:POISONŠ#CREATURE:FORGOTTEN_BEAST_232:POISONŠ#CREATURE:FORGOTTEN_BEAST_233:POISONŠ#CREATURE:FORGOTTEN_BEAST_234:POISONŠ#CREATURE:FORGOTTEN_BEAST_236:POISONŠ#CREATURE:FORGOTTEN_BEAST_237:POISONŠ#CREATURE:FORGOTTEN_BEAST_240:POISONŠ#CREATURE:FORGOTTEN_BEAST_243:POISONŠ#CREATURE:FORGOTTEN_BEAST_244:POISONŠ#CREATURE:FORGOTTEN_BEAST_245:POISONŠ#CREATURE:FORGOTTEN_BEAST_247:POISONŠ#CREATURE:FORGOTTEN_BEAST_249:POISONŠ#CREATURE:FORGOTTEN_BEAST_252:POISONŠ#CREATURE:FORGOTTEN_BEAST_253:POISONŠ#CREATURE:FORGOTTEN_BEAST_254:POISONŠ#CREATURE:FORGOTTEN_BEAST_255:POISONŠ#CREATURE:FORGOTTEN_BEAST_256:POISONŠ#CREATURE:FORGOTTEN_BEAST_257:POISONŠ#CREATURE:FORGOTTEN_BEAST_258:POISONŠ#CREATURE:FORGOTTEN_BEAST_259:POISONŠ#CREATURE:FORGOTTEN_BEAST_260:POISONŠ#CREATURE:FORGOTTEN_BEAST_263:POISONŠ#CREATURE:FORGOTTEN_BEAST_264:POISONŠ#CREATURE:FORGOTTEN_BEAST_265:POISONŠ#CREATURE:FORGOTTEN_BEAST_266:POISONŠ#CREATURE:FORGOTTEN_BEAST_268:POISONŠ#CREATURE:FORGOTTEN_BEAST_269:POISONŠ#CREATURE:FORGOTTEN_BEAST_270:POISONŠ#CREATURE:FORGOTTEN_BEAST_271:POISONŠ#CREATURE:FORGOTTEN_BEAST_272:POISONŠ#CREATURE:FORGOTTEN_BEAST_273:POISONŠ#CREATURE:FORGOTTEN_BEAST_274:POISONŠ#CREATURE:FORGOTTEN_BEAST_275:POISONŠ#CREATURE:FORGOTTEN_BEAST_276:POISONŠ#CREATURE:FORGOTTEN_BEAST_278:POISONŠ#CREATURE:FORGOTTEN_BEAST_279:POISONŠ#CREATURE:FORGOTTEN_BEAST_283:POISONŠ#CREATURE:FORGOTTEN_BEAST_286:POISONŠ#CREATURE:FORGOTTEN_BEAST_288:POISONŠ#CREATURE:FORGOTTEN_BEAST_291:POISONŠ#CREATURE:FORGOTTEN_BEAST_295:POISONŠ#CREATURE:FORGOTTEN_BEAST_296:POISONŠ#CREATURE:FORGOTTEN_BEAST_297:POISONŠ#CREATURE:FORGOTTEN_BEAST_299:POISONŠ#CREATURE:FORGOTTEN_BEAST_300:POISONŠ#CREATURE:FORGOTTEN_BEAST_301:POISONŠ#CREATURE:FORGOTTEN_BEAST_302:POISONŠ#CREATURE:FORGOTTEN_BEAST_303:POISONŠ#CREATURE:FORGOTTEN_BEAST_305:POISONŠ#CREATURE:FORGOTTEN_BEAST_306:POISONŠ#CREATURE:FORGOTTEN_BEAST_307:POISONŠ#CREATURE:FORGOTTEN_BEAST_310:POISONŠ#CREATURE:FORGOTTEN_BEAST_312:POISONŠ#CREATURE:FORGOTTEN_BEAST_314:POISONŠ#CREATURE:FORGOTTEN_BEAST_315:POISONŠ#CREATURE:FORGOTTEN_BEAST_316:POISONŠ#CREATURE:FORGOTTEN_BEAST_319:POISONŠ#CREATURE:FORGOTTEN_BEAST_321:POISONŠ#CREATURE:FORGOTTEN_BEAST_322:POISONŠ#CREATURE:FORGOTTEN_BEAST_323:POISONŠ#CREATURE:FORGOTTEN_BEAST_324:POISONŠ#CREATURE:FORGOTTEN_BEAST_325:POISONŠ#CREATURE:FORGOTTEN_BEAST_327:POISONŠ#CREATURE:FORGOTTEN_BEAST_328:POISONŠ#CREATURE:FORGOTTEN_BEAST_329:POISONŠ#CREATURE:FORGOTTEN_BEAST_332:POISONŠ#CREATURE:FORGOTTEN_BEAST_334:POISONŠ#CREATURE:FORGOTTEN_BEAST_335:POISONŠ#CREATURE:FORGOTTEN_BEAST_336:POISONŠ#CREATURE:FORGOTTEN_BEAST_337:POISONŠ#CREATURE:FORGOTTEN_BEAST_338:POISONŠ#CREATURE:FORGOTTEN_BEAST_339:POISONŠ#CREATURE:FORGOTTEN_BEAST_341:POISONŠ#CREATURE:FORGOTTEN_BEAST_342:POISONŠ#CREATURE:FORGOTTEN_BEAST_343:POISONŠ#CREATURE:FORGOTTEN_BEAST_344:POISONŠ#CREATURE:FORGOTTEN_BEAST_348:POISONŠ#CREATURE:FORGOTTEN_BEAST_349:POISONŠ#CREATURE:FORGOTTEN_BEAST_351:POISONŠ#CREATURE:FORGOTTEN_BEAST_352:POISONŠ#CREATURE:FORGOTTEN_BEAST_353:POISONŠ#CREATURE:FORGOTTEN_BEAST_354:POISONŠ#CREATURE:FORGOTTEN_BEAST_355:POISONŠ#CREATURE:FORGOTTEN_BEAST_356:POISONŠ#CREATURE:FORGOTTEN_BEAST_357:POISONŠ#CREATURE:FORGOTTEN_BEAST_358:POISONŠ#CREATURE:FORGOTTEN_BEAST_359:POISONŠ#CREATURE:FORGOTTEN_BEAST_361:POISONŠ#CREATURE:FORGOTTEN_BEAST_363:POISONŠ#CREATURE:FORGOTTEN_BEAST_364:POISONŠ#CREATURE:FORGOTTEN_BEAST_365:POISONŠ#CREATURE:FORGOTTEN_BEAST_366:POISONŠ#CREATURE:FORGOTTEN_BEAST_367:POISONŠ#CREATURE:FORGOTTEN_BEAST_368:POISONŠ#CREATURE:FORGOTTEN_BEAST_369:POISONŠ#CREATURE:FORGOTTEN_BEAST_370:POISONŠ#CREATURE:FORGOTTEN_BEAST_375:POISONŠ#CREATURE:FORGOTTEN_BEAST_377:POISONŠ#CREATURE:FORGOTTEN_BEAST_378:POISONŠ#CREATURE:FORGOTTEN_BEAST_382:POISONŠ#CREATURE:FORGOTTEN_BEAST_385:POISONŠ#CREATURE:FORGOTTEN_BEAST_386:POISONŠ#CREATURE:FORGOTTEN_BEAST_387:POISONŠ#CREATURE:FORGOTTEN_BEAST_388:POISONŠ#CREATURE:FORGOTTEN_BEAST_389:POISONŠ#CREATURE:FORGOTTEN_BEAST_390:POISONŠ#CREATURE:FORGOTTEN_BEAST_391:POISONŠ#CREATURE:FORGOTTEN_BEAST_392:POISONŠ#CREATURE:FORGOTTEN_BEAST_394:POISONŠ#CREATURE:FORGOTTEN_BEAST_395:POISONŠ#CREATURE:FORGOTTEN_BEAST_398:POISONŠ#CREATURE:FORGOTTEN_BEAST_399:POISONŠ#CREATURE:FORGOTTEN_BEAST_400:POISONŠ#CREATURE:FORGOTTEN_BEAST_402:POISONŠ#CREATURE:FORGOTTEN_BEAST_405:POISONŠ#CREATURE:FORGOTTEN_BEAST_408:POISONŠ#CREATURE:FORGOTTEN_BEAST_409:POISONŠ#CREATURE:FORGOTTEN_BEAST_410:POISONŠ#CREATURE:FORGOTTEN_BEAST_412:POISONŠ#CREATURE:FORGOTTEN_BEAST_413:POISONŠ#CREATURE:FORGOTTEN_BEAST_414:POISONŠ#CREATURE:FORGOTTEN_BEAST_415:POISONŠ#CREATURE:FORGOTTEN_BEAST_416:POISONŠ#CREATURE:FORGOTTEN_BEAST_417:POISONŠ#CREATURE:FORGOTTEN_BEAST_418:POISONŠ#CREATURE:FORGOTTEN_BEAST_419:POISONŠ#CREATURE:FORGOTTEN_BEAST_420:POISONŠ#CREATURE:FORGOTTEN_BEAST_421:POISONŠ#CREATURE:FORGOTTEN_BEAST_422:POISONŠ#CREATURE:FORGOTTEN_BEAST_423:POISONŠ#CREATURE:FORGOTTEN_BEAST_425:POISONŠ#CREATURE:FORGOTTEN_BEAST_431:POISONŠ#CREATURE:FORGOTTEN_BEAST_433:POISONŠ#CREATURE:FORGOTTEN_BEAST_434:POISONŠ#CREATURE:FORGOTTEN_BEAST_436:POISONŠ#CREATURE:FORGOTTEN_BEAST_437:POISONŠ#CREATURE:FORGOTTEN_BEAST_439:POISONŠ#CREATURE:FORGOTTEN_BEAST_441:POISONŠ#CREATURE:FORGOTTEN_BEAST_443:POISONŠ#CREATURE:FORGOTTEN_BEAST_444:POISONŠ#CREATURE:FORGOTTEN_BEAST_445:POISONŠ#CREATURE:FORGOTTEN_BEAST_447:POISONŠ#CREATURE:FORGOTTEN_BEAST_449:POISONŠ#CREATURE:FORGOTTEN_BEAST_450:POISONŠ#CREATURE:FORGOTTEN_BEAST_451:POISONŠ#CREATURE:FORGOTTEN_BEAST_452:POISONŠ#CREATURE:FORGOTTEN_BEAST_453:POISONŠ#CREATURE:FORGOTTEN_BEAST_454:POISONŠ#CREATURE:FORGOTTEN_BEAST_455:POISONŠ#CREATURE:FORGOTTEN_BEAST_459:POISONŠ#CREATURE:FORGOTTEN_BEAST_461:POISONŠ#CREATURE:FORGOTTEN_BEAST_462:POISONŠ#CREATURE:FORGOTTEN_BEAST_463:POISONŠ#CREATURE:FORGOTTEN_BEAST_464:POISONŠ#CREATURE:FORGOTTEN_BEAST_469:POISONŠ#CREATURE:FORGOTTEN_BEAST_470:POISONŠ#CREATURE:FORGOTTEN_BEAST_474:POISONŠ#CREATURE:FORGOTTEN_BEAST_475:POISONŠ#CREATURE:FORGOTTEN_BEAST_476:POISONŠ#CREATURE:FORGOTTEN_BEAST_477:POISONŠ#CREATURE:FORGOTTEN_BEAST_478:POISONŠ#CREATURE:FORGOTTEN_BEAST_480:POISONŠ#CREATURE:FORGOTTEN_BEAST_481:POISONŠ#CREATURE:FORGOTTEN_BEAST_482:POISONŠ#CREATURE:FORGOTTEN_BEAST_483:POISONŠ#CREATURE:FORGOTTEN_BEAST_484:POISONŠ#CREATURE:FORGOTTEN_BEAST_485:POISONŠ#CREATURE:FORGOTTEN_BEAST_487:POISONŠ#CREATURE:FORGOTTEN_BEAST_489:POISONŠ#CREATURE:FORGOTTEN_BEAST_492:POISONŠ#CREATURE:FORGOTTEN_BEAST_494:POISONŠ#CREATURE:FORGOTTEN_BEAST_495:POISONŠ#CREATURE:FORGOTTEN_BEAST_497:POISONŠ#CREATURE:FORGOTTEN_BEAST_498:POISONŠ#CREATURE:FORGOTTEN_BEAST_499:POISONŠ#CREATURE:FORGOTTEN_BEAST_500:POISONŠ#CREATURE:FORGOTTEN_BEAST_501:POISONŠ#CREATURE:FORGOTTEN_BEAST_502:POISONŠ#CREATURE:FORGOTTEN_BEAST_503:POISONŠ#CREATURE:FORGOTTEN_BEAST_505:POISONŠ#CREATURE:FORGOTTEN_BEAST_506:POISONŠ#CREATURE:FORGOTTEN_BEAST_507:POISONŠ#CREATURE:FORGOTTEN_BEAST_509:POISONŠ#CREATURE:FORGOTTEN_BEAST_512:POISONŠ#CREATURE:FORGOTTEN_BEAST_513:POISONŠ#CREATURE:FORGOTTEN_BEAST_514:POISONŠ#CREATURE:FORGOTTEN_BEAST_515:POISONŠ#CREATURE:FORGOTTEN_BEAST_516:POISONŠ#CREATURE:FORGOTTEN_BEAST_518:POISONŠ#CREATURE:FORGOTTEN_BEAST_519:POISONŠ#CREATURE:FORGOTTEN_BEAST_522:POISONŠ#CREATURE:FORGOTTEN_BEAST_524:POISONŠ#CREATURE:FORGOTTEN_BEAST_525:POISONŠ#CREATURE:FORGOTTEN_BEAST_526:POISONŠ#CREATURE:FORGOTTEN_BEAST_527:POISONŠ#CREATURE:FORGOTTEN_BEAST_528:POISONŠ#CREATURE:FORGOTTEN_BEAST_529:POISONŠ#CREATURE:FORGOTTEN_BEAST_532:POISONŠ#CREATURE:FORGOTTEN_BEAST_533:POISONŠ#CREATURE:FORGOTTEN_BEAST_534:POISONŠ#CREATURE:FORGOTTEN_BEAST_535:POISONŠ#CREATURE:FORGOTTEN_BEAST_536:POISONŠ#CREATURE:FORGOTTEN_BEAST_537:POISONŠ#CREATURE:FORGOTTEN_BEAST_539:POISONŠ#CREATURE:FORGOTTEN_BEAST_540:POISONŠ#CREATURE:FORGOTTEN_BEAST_543:POISONŠ#CREATURE:FORGOTTEN_BEAST_544:POISONŠ#CREATURE:FORGOTTEN_BEAST_545:POISONŠ#CREATURE:FORGOTTEN_BEAST_546:POISONŠ#CREATURE:FORGOTTEN_BEAST_547:POISONŠ#CREATURE:FORGOTTEN_BEAST_548:POISONŠ#CREATURE:FORGOTTEN_BEAST_550:POISONŠ#CREATURE:FORGOTTEN_BEAST_551:POISONŠ#CREATURE:FORGOTTEN_BEAST_554:POISONŠ#CREATURE:FORGOTTEN_BEAST_555:POISONŠ#CREATURE:FORGOTTEN_BEAST_557:POISONŠ#CREATURE:FORGOTTEN_BEAST_558:POISONŠ#CREATURE:FORGOTTEN_BEAST_559:POISONŠ#CREATURE:FORGOTTEN_BEAST_565:POISONŠ#CREATURE:FORGOTTEN_BEAST_569:POISONŠ#CREATURE:FORGOTTEN_BEAST_570:POISONŠ#CREATURE:FORGOTTEN_BEAST_571:POISONŠ#CREATURE:FORGOTTEN_BEAST_572:POISONŠ#CREATURE:FORGOTTEN_BEAST_574:POISONŠ#CREATURE:FORGOTTEN_BEAST_577:POISONŠ#CREATURE:FORGOTTEN_BEAST_578:POISONŠ#CREATURE:FORGOTTEN_BEAST_579:POISONŠ#CREATURE:FORGOTTEN_BEAST_581:POISONŠ#CREATURE:FORGOTTEN_BEAST_583:POISONŠ#CREATURE:FORGOTTEN_BEAST_584:POISONŠ#CREATURE:FORGOTTEN_BEAST_585:POISONŠ#CREATURE:FORGOTTEN_BEAST_586:POISONŠ#CREATURE:FORGOTTEN_BEAST_587:POISONŠ#CREATURE:FORGOTTEN_BEAST_589:POISONŠ#CREATURE:FORGOTTEN_BEAST_590:POISONŠ#CREATURE:FORGOTTEN_BEAST_591:POISONŠ#CREATURE:FORGOTTEN_BEAST_592:POISONŠ#CREATURE:FORGOTTEN_BEAST_593:POISONŠ#CREATURE:FORGOTTEN_BEAST_594:POISONŠ#CREATURE:FORGOTTEN_BEAST_595:POISONŠ#CREATURE:FORGOTTEN_BEAST_597:POISONŠ#CREATURE:FORGOTTEN_BEAST_598:POISONŠ#CREATURE:FORGOTTEN_BEAST_599:POISONŠ#CREATURE:FORGOTTEN_BEAST_601:POISONŠ#CREATURE:FORGOTTEN_BEAST_602:POISONŠ#CREATURE:FORGOTTEN_BEAST_604:POISONŠ#CREATURE:FORGOTTEN_BEAST_607:POISONŠ#CREATURE:FORGOTTEN_BEAST_608:POISONŠ#CREATURE:FORGOTTEN_BEAST_609:POISONŠ#CREATURE:FORGOTTEN_BEAST_610:POISONŠ#CREATURE:FORGOTTEN_BEAST_618:POISONŠ#CREATURE:FORGOTTEN_BEAST_620:POISONŠ#CREATURE:FORGOTTEN_BEAST_621:POISONŠ#CREATURE:FORGOTTEN_BEAST_622:POISONŠ#CREATURE:FORGOTTEN_BEAST_624:POISONŠ#CREATURE:FORGOTTEN_BEAST_626:POISONŠ#CREATURE:FORGOTTEN_BEAST_627:POISONŠ#CREATURE:FORGOTTEN_BEAST_629:POISONŠ#CREATURE:FORGOTTEN_BEAST_632:POISONŠ#CREATURE:FORGOTTEN_BEAST_633:POISONŠ#CREATURE:FORGOTTEN_BEAST_634:POISONŠ#CREATURE:FORGOTTEN_BEAST_636:POISONŠ#CREATURE:FORGOTTEN_BEAST_638:POISONŠ#CREATURE:FORGOTTEN_BEAST_640:POISONŠ#CREATURE:FORGOTTEN_BEAST_641:POISONŠ#CREATURE:FORGOTTEN_BEAST_644:POISONŠ#CREATURE:FORGOTTEN_BEAST_645:POISONŠ#CREATURE:FORGOTTEN_BEAST_646:POISONŠ#CREATURE:FORGOTTEN_BEAST_647:POISONŠ#CREATURE:FORGOTTEN_BEAST_648:POISONŠ#CREATURE:FORGOTTEN_BEAST_649:POISONŠ#CREATURE:FORGOTTEN_BEAST_652:POISONŠ#CREATURE:FORGOTTEN_BEAST_656:POISONŠ#CREATURE:FORGOTTEN_BEAST_658:POISONŠ#CREATURE:FORGOTTEN_BEAST_659:POISONŠ#CREATURE:FORGOTTEN_BEAST_660:POISONŠ#CREATURE:FORGOTTEN_BEAST_663:POISONŠ#CREATURE:FORGOTTEN_BEAST_664:POISONŠ#CREATURE:FORGOTTEN_BEAST_666:POISONŠ#CREATURE:FORGOTTEN_BEAST_667:POISONŠ#CREATURE:FORGOTTEN_BEAST_668:POISONŠ#CREATURE:FORGOTTEN_BEAST_669:POISONŠ#CREATURE:FORGOTTEN_BEAST_670:POISONŠ#CREATURE:FORGOTTEN_BEAST_671:POISONŠ#CREATURE:FORGOTTEN_BEAST_674:POISONŠ#CREATURE:FORGOTTEN_BEAST_675:POISONŠ#CREATURE:FORGOTTEN_BEAST_678:POISONŠ#CREATURE:FORGOTTEN_BEAST_679:POISONŠ#CREATURE:FORGOTTEN_BEAST_681:POISONŠ#CREATURE:FORGOTTEN_BEAST_682:POISONŠ#CREATURE:FORGOTTEN_BEAST_686:POISONŠ#CREATURE:FORGOTTEN_BEAST_688:POISONŠ#CREATURE:FORGOTTEN_BEAST_689:POISONŠ#CREATURE:FORGOTTEN_BEAST_691:POISONŠ#CREATURE:FORGOTTEN_BEAST_692:POISONŠ#CREATURE:FORGOTTEN_BEAST_696:POISONŠ#CREATURE:FORGOTTEN_BEAST_698:POISONŠ#CREATURE:FORGOTTEN_BEAST_699:POISONŠ#CREATURE:FORGOTTEN_BEAST_702:POISONŠ#CREATURE:FORGOTTEN_BEAST_703:POISONŠ#CREATURE:FORGOTTEN_BEAST_704:POISONŠ#CREATURE:FORGOTTEN_BEAST_705:POISONŠ#CREATURE:FORGOTTEN_BEAST_707:POISONŠ#CREATURE:FORGOTTEN_BEAST_708:POISONŠ#CREATURE:FORGOTTEN_BEAST_709:POISONŠ#CREATURE:FORGOTTEN_BEAST_711:POISONŠ#CREATURE:FORGOTTEN_BEAST_712:POISONŠ#CREATURE:FORGOTTEN_BEAST_713:POISONŠ#CREATURE:FORGOTTEN_BEAST_714:POISONŠ#CREATURE:FORGOTTEN_BEAST_715:POISONŠ#CREATURE:FORGOTTEN_BEAST_718:POISONŠ#CREATURE:FORGOTTEN_BEAST_719:POISONŠ#CREATURE:FORGOTTEN_BEAST_721:POISONŠ#CREATURE:FORGOTTEN_BEAST_722:POISONŠ#CREATURE:FORGOTTEN_BEAST_724:POISONŠ#CREATURE:FORGOTTEN_BEAST_725:POISONŠ#CREATURE:FORGOTTEN_BEAST_726:POISONŠ#CREATURE:FORGOTTEN_BEAST_727:POISONŠ#CREATURE:FORGOTTEN_BEAST_731:POISONŠ#CREATURE:FORGOTTEN_BEAST_732:POISONŠ#CREATURE:FORGOTTEN_BEAST_733:POISONŠ#CREATURE:FORGOTTEN_BEAST_735:POISONŠ#CREATURE:FORGOTTEN_BEAST_737:POISONŠ#CREATURE:FORGOTTEN_BEAST_738:POISONŠ#CREATURE:FORGOTTEN_BEAST_739:POISONŠ#CREATURE:FORGOTTEN_BEAST_740:POISONŠ#CREATURE:FORGOTTEN_BEAST_741:POISONŠ#CREATURE:FORGOTTEN_BEAST_742:POISONŠ#CREATURE:FORGOTTEN_BEAST_744:POISONŠ#CREATURE:FORGOTTEN_BEAST_746:POISONŠ#CREATURE:FORGOTTEN_BEAST_748:POISONŠ#CREATURE:FORGOTTEN_BEAST_749:POISONŠ#CREATURE:FORGOTTEN_BEAST_750:POISONŠ#CREATURE:FORGOTTEN_BEAST_751:POISONŠ#CREATURE:FORGOTTEN_BEAST_752:POISONŠ#CREATURE:FORGOTTEN_BEAST_753:POISONŠ#CREATURE:FORGOTTEN_BEAST_756:POISONŠ#CREATURE:FORGOTTEN_BEAST_758:POISONŠ#CREATURE:FORGOTTEN_BEAST_760:POISONŠ#CREATURE:FORGOTTEN_BEAST_761:POISONŠ#CREATURE:FORGOTTEN_BEAST_762:POISONŠ#CREATURE:FORGOTTEN_BEAST_764:POISONŠ#CREATURE:FORGOTTEN_BEAST_766:POISONŠ#CREATURE:FORGOTTEN_BEAST_767:POISONŠ#CREATURE:FORGOTTEN_BEAST_769:POISONŠ#CREATURE:FORGOTTEN_BEAST_770:POISONŠ#CREATURE:FORGOTTEN_BEAST_771:POISONŠ#CREATURE:FORGOTTEN_BEAST_772:POISONŠ#CREATURE:FORGOTTEN_BEAST_773:POISONŠ#CREATURE:FORGOTTEN_BEAST_777:POISONŠ#CREATURE:FORGOTTEN_BEAST_778:POISONŠ#CREATURE:FORGOTTEN_BEAST_779:POISONŠ#CREATURE:FORGOTTEN_BEAST_780:POISONŠ#CREATURE:FORGOTTEN_BEAST_781:POISONŠ#CREATURE:FORGOTTEN_BEAST_783:POISONŠ#CREATURE:FORGOTTEN_BEAST_785:POISONŠ#CREATURE:FORGOTTEN_BEAST_787:POISONŠ#CREATURE:FORGOTTEN_BEAST_788:POISONŠ#CREATURE:FORGOTTEN_BEAST_790:POISONŠ#CREATURE:FORGOTTEN_BEAST_792:POISONŠ#CREATURE:FORGOTTEN_BEAST_793:POISONŠ#CREATURE:FORGOTTEN_BEAST_794:POISONŠ#CREATURE:FORGOTTEN_BEAST_795:POISONŠ#CREATURE:FORGOTTEN_BEAST_796:POISONŠ#CREATURE:FORGOTTEN_BEAST_798:POISONŠ#CREATURE:FORGOTTEN_BEAST_799:POISONŠ#CREATURE:FORGOTTEN_BEAST_800:POISONŠ#CREATURE:FORGOTTEN_BEAST_801:POISONŠ#CREATURE:FORGOTTEN_BEAST_802:POISONŠ#CREATURE:FORGOTTEN_BEAST_803:POISONŠ#CREATURE:FORGOTTEN_BEAST_806:POISONŠ#CREATURE:FORGOTTEN_BEAST_807:POISONŠ#CREATURE:FORGOTTEN_BEAST_810:POISONŠ#CREATURE:FORGOTTEN_BEAST_811:POISONŠ#CREATURE:FORGOTTEN_BEAST_813:POISONŠ#CREATURE:FORGOTTEN_BEAST_815:POISONŠ#CREATURE:FORGOTTEN_BEAST_818:POISONŠ#CREATURE:FORGOTTEN_BEAST_819:POISONŠ#CREATURE:FORGOTTEN_BEAST_820:POISONŠ#CREATURE:FORGOTTEN_BEAST_822:POISONŠ#CREATURE:FORGOTTEN_BEAST_823:POISONŠ#CREATURE:FORGOTTEN_BEAST_825:POISONŠ#CREATURE:FORGOTTEN_BEAST_826:POISONŠ#CREATURE:FORGOTTEN_BEAST_828:POISONŠ#CREATURE:FORGOTTEN_BEAST_830:POISONŠ#CREATURE:FORGOTTEN_BEAST_831:POISONŠ#CREATURE:FORGOTTEN_BEAST_832:POISONŠ#CREATURE:FORGOTTEN_BEAST_835:POISONŠ#CREATURE:FORGOTTEN_BEAST_836:POISONŠ#CREATURE:FORGOTTEN_BEAST_837:POISONŠ#CREATURE:FORGOTTEN_BEAST_839:POISONŠ#CREATURE:FORGOTTEN_BEAST_840:POISONŠ#CREATURE:FORGOTTEN_BEAST_841:POISONŠ#CREATURE:FORGOTTEN_BEAST_844:POISONŠ#CREATURE:FORGOTTEN_BEAST_845:POISONŠ#CREATURE:FORGOTTEN_BEAST_846:POISONŠ#CREATURE:FORGOTTEN_BEAST_847:POISONŠ#CREATURE:FORGOTTEN_BEAST_848:POISONŠ#CREATURE:FORGOTTEN_BEAST_849:POISONŠ#CREATURE:FORGOTTEN_BEAST_850:POISONŠ#CREATURE:FORGOTTEN_BEAST_851:POISONŠ#CREATURE:FORGOTTEN_BEAST_852:POISONŠ#CREATURE:FORGOTTEN_BEAST_853:POISONŠ#CREATURE:FORGOTTEN_BEAST_854:POISONŠ#CREATURE:FORGOTTEN_BEAST_856:POISONŠ#CREATURE:FORGOTTEN_BEAST_857:POISONŠ#CREATURE:FORGOTTEN_BEAST_859:POISONŠ#CREATURE:FORGOTTEN_BEAST_861:POISONŠ#CREATURE:FORGOTTEN_BEAST_862:POISONŠ#CREATURE:FORGOTTEN_BEAST_865:POISONŠCREATURE:TITAN_1:POISONŠCREATURE:TITAN_2:POISONŠCREATURE:TITAN_3:POISONŠCREATURE:TITAN_5:POISONŠCREATURE:TITAN_9:POISONŠCREATURE:TITAN_10:POISONŠCREATURE:TITAN_11:POISONŠCREATURE:TITAN_12:POISONŠCREATURE:TITAN_13:POISONŠCREATURE:TITAN_14:POISONŠCREATURE:TITAN_17:POISONŠCREATURE:TITAN_18:POISONŠCREATURE:TITAN_19:POISONŠCREATURE:TITAN_20:POISONŠCREATURE:TITAN_24:POISONŠCREATURE:TITAN_25:POISONŠCREATURE:TITAN_27:POISONŠCREATURE:TITAN_29:POISONŠCREATURE:TITAN_30:POISONŠCREATURE:TITAN_31:POISONŠCREATURE:DEMON_2:POISONŠCREATURE:DEMON_4:POISONŠCREATURE:DEMON_6:POISONŠCREATURE:DEMON_7:POISONŠCREATURE:DEMON_8:POISONŠCREATURE:DEMON_9:POISONŠCREATURE:DEMON_10:POISONŠCREATURE:DEMON_11:POISONŠCREATURE:DEMON_12:POISONŠCREATURE:DEMON_14:POISONŠCREATURE:DEMON_15:POISONŠCREATURE:DEMON_16:POISONŠCREATURE:DEMON_17:POISONŠCREATURE:DEMON_18:POISONŠCREATURE:DEMON_20:POISONŠCREATURE:DEMON_21:POISONŠCREATURE:DEMON_22:POISONŠCREATURE:DEMON_24:POISONŠCREATURE:DEMON_25:POISONŠCREATURE:DEMON_26:POISONŠCREATURE:DEMON_28:POISONŠCREATURE:DEMON_30:POISONŠCREATURE:DEMON_31:POISONŠCREATURE:DEMON_32:POISONŠCREATURE:DEMON_40:POISONŠCREATURE:DEMON_41:POISONŠCREATURE:DEMON_43:POISONŠCREATURE:DEMON_44:POISONŠCREATURE:DEMON_45:POISONŠCREATURE:DEMON_47:POISONŠCREATURE:DEMON_49:POISONŠ!CREATURE:NIGHT_CREATURE_64:POISONŠ!CREATURE:NIGHT_CREATURE_66:POISONŠ!CREATURE:NIGHT_CREATURE_75:POISONŠ!CREATURE:NIGHT_CREATURE_83:POISONŠ!CREATURE:NIGHT_CREATURE_85:POISONŠCREATURE:HF1108 DIVINE_3:POISONŠCREATURE:HF1249 DIVINE_3:POISONŠCREATURE:HF1345 DIVINE_1:POISON’LYE’INORGANIC:MILK_OF_LIME˜¢CUTTLEFISH:FEMALE¢CUTTLEFISH:MALE¢NAUTILUS:FEMALE¢ NAUTILUS:MALE¢MOGHOPPER:FEMALE¢MOGHOPPER:MALE¢POND_TURTLE:FEMALE¢POND_TURTLE:MALE¢MUSSEL:DEFAULT¢OYSTER:DEFAULT¢FISH_SALMON:FEMALE¢FISH_SALMON:MALE¢FISH_CLOWNFISH:FEMALE¢FISH_CLOWNFISH:MALE¢FISH_HAGFISH:FEMALE¢FISH_HAGFISH:MALE¢FISH_LAMPREY_BROOK:FEMALE¢FISH_LAMPREY_BROOK:MALE¢FISH_RAY_BAT:FEMALE¢FISH_RAY_BAT:MALE¢FISH_RAY_THORNBACK:FEMALE¢FISH_RAY_THORNBACK:MALE¢FISH_RATFISH_SPOTTED:FEMALE¢FISH_RATFISH_SPOTTED:MALE¢FISH_HERRING:FEMALE¢FISH_HERRING:MALE¢FISH_SHAD:FEMALE¢FISH_SHAD:MALE¢FISH_ANCHOVY:FEMALE¢FISH_ANCHOVY:MALE¢FISH_TROUT_STEELHEAD:FEMALE¢FISH_TROUT_STEELHEAD:MALE¢FISH_HAKE:FEMALE¢FISH_HAKE:MALE¢FISH_SEAHORSE:FEMALE¢FISH_SEAHORSE:MALE¢FISH_GLASSEYE:FEMALE¢FISH_GLASSEYE:MALE¢ FISH_PUFFER_WHITE_SPOTTED:FEMALE¢FISH_PUFFER_WHITE_SPOTTED:MALE¢FISH_SOLE:FEMALE¢FISH_SOLE:MALE¢FISH_FLOUNDER:FEMALE¢FISH_FLOUNDER:MALE¢FISH_MACKEREL:FEMALE¢FISH_MACKEREL:MALE¢JELLYFISH_SEA_NETTLE:DEFAULT¢ SQUID:FEMALE¢ -SQUID:MALE¢FISH_LUNGFISH:FEMALE¢FISH_LUNGFISH:MALE¢FISH_LOACH_CLOWN:FEMALE¢FISH_LOACH_CLOWN:MALE¢FISH_BULLHEAD_BROWN:FEMALE¢FISH_BULLHEAD_BROWN:MALE¢FISH_BULLHEAD_YELLOW:FEMALE¢FISH_BULLHEAD_YELLOW:MALE¢FISH_BULLHEAD_BLACK:FEMALE¢FISH_BULLHEAD_BLACK:MALE¢FISH_KNIFEFISH_BANDED:FEMALE¢FISH_KNIFEFISH_BANDED:MALE¢FISH_CHAR:FEMALE¢FISH_CHAR:MALE¢FISH_TROUT_RAINBOW:FEMALE¢FISH_TROUT_RAINBOW:MALE¢FISH_MOLLY_SAILFIN:FEMALE¢FISH_MOLLY_SAILFIN:MALE¢FISH_GUPPY:FEMALE¢FISH_GUPPY:MALE¢FISH_PERCH:FEMALE¢FISH_PERCH:MALE¢FISH_CAVE:FEMALE¢FISH_CAVE:MALE¢LOBSTER_CAVE:FEMALE¢LOBSTER_CAVE:MALE˜ ¨°¸À \ No newline at end of file +¨ \ No newline at end of file diff --git a/data/stockpiles/category_furniture.dfstock b/data/stockpiles/category_furniture.dfstock index 6c52781fb100a82428db8d6c2005d998841ffe1a..fdec022edf4e8b3fe20d910b5532343cd0acd15b 100644 GIT binary patch literal 4 Lcmb1QvS0)N0OJ5w literal 3259 zcmZ`*O>Y}F5Va7?v8gz1)X%8s#Yf$OAPCaiESK74#U(c+*NSpk7>NuRFq}YjfaYKH z*kgfSdhD^s{aWXN^r5Qyf202BU*V3IUFZ!8pu*1|j zFHR*zDk~F9@-c|hgpAPvP6>W1MBw&en;tG8acPZBsWBeVLPn=17TA z;z+v5L|btLJ+XqNU?>s&*Ol^CoD|xa5S>**kT0BvkWGb{DJq?X`l)y*DbZF%(In%oIJ#M`C>4)NU8*{$)?@K#f^5{V>?u>? zxPS$TAjJT}$~0OK$P*WHY$$*yv3>T;)KBNa1L>ktlUmx88BgP}H_6xSTSmjt6$SxeKAuH=ARrW)UR;q15QR z9XNVDF>3Vn&Idzt9u%^ga>HaQFaU;`%2g`r-K-^`EHeFcV*~+vqiuF4{O#sbV!+{g zowTnuxOYL-ZH1m}z(j|liuvgK9q3L2i;Pk#K)NOoOa+M8cr*HOqe3cAg_bp#1lHLZ zc{TcB_jB8Y|M#(UUMfSV1&e_~-f$6YU?3dY1)HX6^>ayNHm*QMi=tW!GJ=JiP*i3^ zqMSSGyhEY`naCxLF4Rhr)kHd*8CM`JQE?We4yXpkY)kZM>;hz_JmZ+3wzk9RL+X%97FnhZ<%Y2Rz z-}N=f97Dh3y;T}L&f+P^W4Lo4a#f(CyV{X{*CvPYHpAG`s?87UYm2+*>yF?}xPsSB zxcgx}!5B9RLdWG>5oI8dB|aBI*^p}63yDdFt1Zm>TRKeLM8{@GxGL~c8=bWQGU;_F z=tT5Fcfd()En&HEib{uF)h3QnG%D=YT&3F%HrwQG%CKr=^D@8^TL#z@l8@1!RU9$> zWtFBFSgY1x^)>+FbW^g^)^meOwMFx!E zmYgrqwKj4rM4hx(nRQSQ^=c~3S})Aa`4$PWHdl?t?qcm}Op(xGur@He#T;Kn-5ov! zWq6Hj+sOTDbg)E}bMH*mI>?A#YP`MBI_PWecS6;hM&~Y%?spSw;{;EIH~cyRJa0Au zGv82UZHEOuC5W~`GSQ%XiG*({a99^^l_VyMO@c`a9g5QpC~4?gL+)9lPcGtDB4E=d;0VaeXb9 z7niq-_h+Y*tDD93=ey%^`1EnHy!x;_8>nC3Ek52}eYm-}K07Hcez{#NFF#$M9nhj{et{^=e3+dKFVF(_?_ diff --git a/data/stockpiles/category_gems.dfstock b/data/stockpiles/category_gems.dfstock index 8d7b229a1145587d2dc0cda9e7cda76c80d8ed76..8a6483e26ec58e0d0c04b28768ea7becf941e944 100644 GIT binary patch literal 4 Lcma!I(qIGt0mA@> literal 7728 zcmZ`;yOQL%5!`m~;-uB;b=NvYbkNa1DCB@7hQu~VZ~?GqI8OhP%rj-mlqpl6ro@9p zlAJTU0Ms+9kVL=#@{jMn@3e64*;y&a^+%(nu;2ZVRvNoFFZ37Qxc-P6zw9?&f1^fY zJ29%}?`B-yuAj^A!B}NRmbj)jR$u>6_Gr<_o9Kj>*WVXut!H`rDq4B{QxR%0DRz~V3Eglj|Y|0_UFsbpL zgj54R*J}+(`l9SAC4-ZmhgD3qsjYb(q1a8*UVki(OserS^yC#f;RbVGZr^5M{ri=o zd=+mg9P;`Vl{?i_3^f8g7O68jZ~a~I0!6b5b*Bt1Qfu?N`bGA=>ZFy+4kS@_Qx;&R zoo*gaaI(^=F*!wzFfBH~=1Lxgp1XN7+d=5y+D~Pzvtqe5z0i#e_2#7(RfQ_mWOPuu zT#txO>8JtsBqZanJ-lHHe=WvejP>e8HNvBHvlS};p!9t$8Wm_T=A4UUCU!AtNJvON z?SL{$F-c9;0^`#fhzY5ABtqWY zSzcpy-EZ&8Hoyh#Pjb}UOp@_3NR2TG9Q>uETaB&i3r5WL%TI%OCTi}v` zBD8#ihp`4^&WrIqRhWdh1jFqJ%|Dm2Ka|nZ-!Ls5rn(QIQr%-jx$czj`#ioM^fYQ5 z#ehaFnIS7Znt!?FNtg0Kx&MlJk#7zLZye;ghLnq}<&5?)aOIfOOvdv84w3F`j{>|i za-twrfF@N|Qlg{8+PPK@V%Ya;oU7-a=?vy$tO9Akko%jvg;`Gth$LqPF?E0#r0>Cv z*lB@@F@P1Ie3aM>8-S0Cnux-w3IT}EDPWS37o||}0=Cw(?zj*K%t;!@Ek_df=HYeP z-JpxiNvh>TjeLJc9lfwxew(pEd5SAQ>W8ie3KJJ7Ev*VjZJ&B!v#a!F;n5-oX@zc? zI-%SPPg956qCcchTE$_tI8$BR5FJpqeKLg9nCRg_RZEAnS6ZBEPmc8H-jCYODzUfYuYF|6Q^W zvnUG>#J>7m##bWtEJ?(^qH-5v&+0c~UlEj;*jF?pBCBSdh1j#S5POyuV$b$BV$Z^j z*jF@!iP)dTk=R$1p}@^qyOcSg9iIR}NUdFw{(xm_}`=M3d5BhgH@zY=>k zRfs+BT!}sJEX1B+pNM@$H&(hn3LU9_o!Ilzf!MRQqhL^H6r$mR3`Q;XJUVrC%(#f zS&2Q59qv zn*L!WCyH;JmDqE`hOHaH{-v5*z@X{*mJE4v1eGM7n)o{x>54H%nNiYGqGpm`2Z1n zzOy5bXKxCzXH;h;rS4qZmDscUX|Cqn(dLcVb7?kWf7X!Wq}z8T_Uv>a_AD*Lo{LJv zo;N3c95Bhqi&CIB68qEkICxIdIBq$Ts3Z@s)9y&@Ptr*2xpkwCjo6>G1F0Xnz7l&j zu##fEd?bBY80wkWGj$3)m?zRom^p2$iHHJD{boe5Q6rFMEl=ewTcpT=uk(jWZhC3`ULf?Hi;4%!@M+DWR>c=8=1QHYF@xBe z?U(q)&%0_71E72$ad=dP`p@M9q!r;Eo^_epDHL({fs?NNLpV+!VxIa%hnFALkIxz9 zJWIM!D(6^&5C0lPAy0d}v-yKxNWY26q02E$F@?u7*rX4R5oaIr8OAQ-FN*<{2fhml ze|e!X6YguSI9`+m{NG2^0YIbR26a;a)zOOve-Ghk6ccrA44x@!<>vuKm>ri|t1bpe zyR&Qfn8oMhJaCTU#a4@*!;Pr{+Tbuj)JtzgFXJN{rwM-`jnAA7LPWPr4wmSX4F6&^ z)ei+TU!`aL)7;{Tu}iJNA2zUy2XNUw!^a7`4w&P`0bWvD*H#Tl1=aVi!e0{+Qxs6G z+%WrI9b3gpp@9gtWbA~l9X-pOS9Lr})&W7(E4V~Y2yW4jAR=Jmu1JkWX9LN@6mh$a zO2ce8OqsQ_SNq{J2z0{{=(q;c=%7Rd2B_!gAOU?+zUk3A=xg@77V}#$ea4Z%e@+oc zG!biK#Ty?-fDb&xF~S2>vK>9X<@J4i!=W>f;)?L_GDyv?oajp%I&|pJp+i5-Af+lnnhmFgqtR?& zzC7KX-(rX~KtTO9L+Epf!R$bEWdYx8k$zkQUv5~o;C7kX!E{rOqQ1--T#-E-db31+ z*y`yR1{b{{DCR7B_mNR#O!U^)P}u|EF6Y-9v>j}YYO-$$VGqXvp`vOCP2?BtFtvbLDZj(sqgvqU_Q;zto2 ztnp9|yjcF&#Ca!`pn#_7^L|4E=U&Y`MAx4`-oP2%)zC8^bWDjq2&;K;!2FNuI}_k6 zSmabqb!4F-SFs!Xd1Ij?xLIKtJ!4~*PwjAM1B&Q%l*CeF7`@a;V+f#NM_5P2rmcj9 zMT;o}*7Qe$Hd2^ZHKeGrlY3zFp!?GHmwy?x~@g7Y-Kg8He z>TL7WWEEN~Lcf#{%3jbbL(NlSAcfhF#o}>Q$bcJI5MiW9iMKWsiRFT-rHbxsL4L0) zoLId)xod?)Jx%MPiCi`f@)gOuKy=);CJSY0EVw^D9)@6!M%R{>(;ivBoMAJj!&wBv<6~D%w}8tbN4b7vRt~qzKK15(|DP z5UJR>h$Lb?YQD5w!$`X^3X+dz`_*wo=X%-LUi)q3W#yY!LGfVOusk=}OfRSX>Oip5 zvLeC(tXE4Gt|oYxp$8Akj6z7hhx`s=Z|Yx*RFQYBO!E!OXL&`%gf(h*5xgAx9?7)) Xr>^_ib-%jP@9y-c>;879f8FUn@-gPF diff --git a/data/stockpiles/category_weapons.dfstock b/data/stockpiles/category_weapons.dfstock index 6a84bf0c70272c8ceb65ef1e09343904535383e4..8f46ee61994481053c52c7ca6a6dcaaa44921bc4 100644 GIT binary patch literal 5 McmZo-Wb$AH00Sxj&j0`b literal 3838 zcmZ`*O>f*p7|u#Tl#RkifHvI{P*+Hx+`3g&+7sjP#2$7$V`j$LY%W&RCQ_s*t(H`x zSAGFUjvP61abvy8h#atGBhExg8AV$ z5pK$|3_VcHX7P9(H&x)pI&h?zRMFlQ#%ifE=h2~9BOI5749r+eA!2Wt3>JppMS>>C z=ma0f1Or4mkysPGm8sBv_%NcLA-`j>_bq}cxhcvP^{c-+lT3RAn<$owz3*4_H7TKu zElh1YRl5xPeH&W(0Bn0EQI%k3OpRmZ#P6>Ll=TD#o95b`y(GDZzOG z8+)BZDYLqXj?xtKQdb3)6H0JzLEG(gAvFH7`z^)HV`z?~7UKsSWFYEDiSZX348`U& zDO5G3j+v^8CKzI}Ek(UqwL~hcg7L%72@eJY8{`j$vwQB+Go0x*Zg-SffG&JA(~SX7jmG zYf=eo5+L z#WDa$GrAXC89=oj1;-|XYC|UT%?-A0B7ImAI488#N@h)hM0%MDCK0pHUvRhznkCHA zK5>Jy(-e+C)C(M{T%_v(;zg!cTZVKao0krr$T7g3;B<}tB>b4zFDsm|@JPVS({)qA zuRM|>t;5x#?FG!srmUpaiIqibg7kMP3%s%#Oc966)rN-!5JE-;uOkISP*28|m|E>A z&3TzLs>C}Wh<>fvb8~eZNCcQTo9&i3>|_x5~o`DSsrBYu5#{`Thb&Gp&U;YY>UFE{6li+5Lt_t@g*^5X2(&E(eN vgj$^RElzGNPN>C6XK`|C@rS{WgWm^(KL&$82U~v)_WvGi{WIA5cd+$8%10;W diff --git a/data/stockpiles/category_wood.dfstock b/data/stockpiles/category_wood.dfstock index b9480efb3eb8137b5b563f7c2b5ec2889330862c..5613a0f0770cb0bc4913da8f26c6de0f400b0792 100644 GIT binary patch literal 4 Lcmb(5VejUvNg_IN)|D|7;yv)L0}!fM*GMTQl(6pGG)q?Sy~1;hXR<-<=#7d z{iZ;nMuRYFOtN4ySTJf15;C~Xw8_r*I}wbw6^{v&Yxt0K#*9ydPgy#wUujRR!nJp| zDrM(N2p4>?LCf$Kd=!Yxkpc^(uZ0tgTrfF;ckOGXvj4x{?p~s0KYE~WOZIiXSz4*o z*X6Rb_@!ZmchlC2#eT+|1Hojm(w;Fy7{k-dLVb#&(PH!nii3X;+&U8{uS+eAQp?GT z7YD!vbMb~njK-&?Ql3J1e(lbu#WAQEKPZ68gl2{}*-F0{(LH4Rk zWZW;|PhWaDJT~>)_L?*ge`#8b6}h6U bYnK6hABG>p@N+o*8cx55)1Tq=cR2k6Qg5E1 From e59d60612c0407c298a7eb6d1219775b34643115 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 15:52:23 -0700 Subject: [PATCH 0866/2222] rename traps and cages --- .../{emptycages.dfstock => cages.dfstock} | Bin .../{emptyanimaltraps.dfstock => traps.dfstock} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename data/stockpiles/{emptycages.dfstock => cages.dfstock} (100%) rename data/stockpiles/{emptyanimaltraps.dfstock => traps.dfstock} (100%) diff --git a/data/stockpiles/emptycages.dfstock b/data/stockpiles/cages.dfstock similarity index 100% rename from data/stockpiles/emptycages.dfstock rename to data/stockpiles/cages.dfstock diff --git a/data/stockpiles/emptyanimaltraps.dfstock b/data/stockpiles/traps.dfstock similarity index 100% rename from data/stockpiles/emptyanimaltraps.dfstock rename to data/stockpiles/traps.dfstock From 4af59f865ab59e60ea1e28bde7d9ac91c3aadf93 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 15:52:38 -0700 Subject: [PATCH 0867/2222] update traps and cages --- data/stockpiles/cages.dfstock | Bin 27 -> 6 bytes data/stockpiles/traps.dfstock | Bin 27 -> 6 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/cages.dfstock b/data/stockpiles/cages.dfstock index 3a66abbd68612b0e514eac558c890cdbaaa6b3d1..0ca677985c949139e96301fb25e673409af302b7 100644 GIT binary patch literal 6 Ncmd;L;b0VC0001)04M+e literal 27 jcmd;L;b0VCn83(5gOPCoBf|j diff --git a/data/stockpiles/traps.dfstock b/data/stockpiles/traps.dfstock index d9121dc8aa9a79a2205e2a71192d620506ede5bb..ed1ba6776305c3f9bf020e9293d471a1cd50f24f 100644 GIT binary patch literal 6 Ncmd;L;b0J81ONb+04M+e literal 27 jcmd;L;b0J8oWRI9gOPCoBf| Date: Mon, 20 Mar 2023 16:04:35 -0700 Subject: [PATCH 0868/2222] update food stockpile defs --- data/stockpiles/booze.dfstock | Bin 0 -> 1809 bytes data/stockpiles/dye.dfstock | Bin 127 -> 106 bytes data/stockpiles/miscliquid.dfstock | Bin 57 -> 36 bytes data/stockpiles/plants.dfstock | Bin 3177 -> 3156 bytes data/stockpiles/preparedmeals.dfstock | Bin 26 -> 5 bytes data/stockpiles/seeds.dfstock | Bin 3395 -> 3374 bytes data/stockpiles/unpreparedfish.dfstock | Bin 1720 -> 1699 bytes data/stockpiles/wax.dfstock | Bin 98 -> 29 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/stockpiles/booze.dfstock diff --git a/data/stockpiles/booze.dfstock b/data/stockpiles/booze.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..736560f98897ed4fd903e871eef8c6b109542b88 GIT binary patch literal 1809 zcmZuyxsKZ~5KVp{$L82v2#|JxZBn`$Nt8vHSB9h=r{N!^&gW)qz0pd>LWO`g_nU)v ze_uU(PmV&_WgGU+JnornXs5|g*$r&N;o<8nFQ@p7@FR@lST;t$C*om)zlDA>PW|7_ zP)F>{88JTqtNme}%Ig{Cvw_|_xQo1>st5Q6TDDg;MD=9UuJ3NVjON|;4M{7&fE$=savVKdFz}}Iv_bswwSHt^K3IpqYQ6F z$YYGZ3)vfoD1MB=#w|m|j3W>bPF!q1#e=d0Dm$~JcRwd_vTkTSF-OfCt>`IdIx2er z6LAhUZD~%iP|=J)J=0*bk_#sCNusysv`h3)9#Fv*6FzSomGfXF1N+8El%)oK+W zOST{>2XNd=-#f#sBl;e#w{DH<;JRwoFxH`S)F0M3)R}U622M5@N-{lL;yMcQZ5t=8 zAm2r&F!&rQTMAIs+Uiq(pq41nSxr&c#a}IpF*xgMF0#{)>$FhBKr*n2oUS2!yK_Go ztpL-LqBlZu5Q}YYYqyvkN^JXO%9M3FulYhK3G~z-RGSR-I#8Hqfq2VaGMugVQJGRj zx7-xem?;cxW&dLy0$=mQG5R(c=P$!5hC1z7O({3$cHPco_Xv;UGRv-qPIl_3oFHbB z({kN`ZO#&}G%p7dFd9^<5Yyx!3AY18xI`DmtjX;{=wTTu*r_lVrW5Qosk$ z8X>zmG+y2cy@!Z2$}*c9uK8*am-WI(WM_|;#ECmZK>61`w_LS1fRC~p^0F*P1?~0} l_E`O&_dUxiR@v;P7|glJ72A13!%EOq-#OXTS@7@Wi~spv^2`7L literal 0 HcmV?d00001 diff --git a/data/stockpiles/dye.dfstock b/data/stockpiles/dye.dfstock index e8ef137d85db9a0f916a1bc5c210afb5a2acafaa..67b785d2034ee72a972af38b627fee5f0802b8bc 100644 GIT binary patch delta 4 Lcmb==nvewm1jqs8 delta 26 icmd0*pO7Uwfst_rBjW-_i4}|t8yFdOFftrqWB>qWK?bq_ diff --git a/data/stockpiles/miscliquid.dfstock b/data/stockpiles/miscliquid.dfstock index 56ef41c076b6afb1babccf74af65d5f006121ca3..dbf75a072e91902d3ee934f909c0dbff29b30471 100644 GIT binary patch delta 4 LcmcCinVqC1qI*$ diff --git a/data/stockpiles/plants.dfstock b/data/stockpiles/plants.dfstock index ac8573dd1d78fda8cf974e0571c7024bc1db6eca..ca55f19f98b1b203bf5bef88dd677ab137699e65 100644 GIT binary patch delta 7 OcmaDUaYbT72oC@bJ_7y# delta 29 lcmca2@ls+#2#@FlM#dS8j0+egRxmPbU}V_A$Z&v>0RV^I2h0Ef diff --git a/data/stockpiles/preparedmeals.dfstock b/data/stockpiles/preparedmeals.dfstock index 1df044d4855420be0cf579d5c3295a3b20ac4cb2..657c93f389775ef3f798afb5ec15a8225c6eb22f 100644 GIT binary patch literal 5 McmWe)p25fn00K4uumAu6 literal 26 gcmWe)p25gCfsqkNE?|^c!N{swN7e-9xnh4KLU{e delta 29 lcmZ1{by#YH90RVh~2VVdH diff --git a/data/stockpiles/unpreparedfish.dfstock b/data/stockpiles/unpreparedfish.dfstock index 465a95dddaf4044c86f9ec7b958739c01d39f275..13841da90175fdd324b56e32f3b3c4bd5209fdfc 100644 GIT binary patch delta 7 OcmdnNyO?*wVm1H_90Jb( delta 29 lcmZ3?yMuSbVm8qUjEplF85b~0tYBo=z{s$Jk>LO%0|0@t2b}-_ diff --git a/data/stockpiles/wax.dfstock b/data/stockpiles/wax.dfstock index f7ec69912e41e23f568923909774ee2d863ee8f4..35c69f6ca89bb85b5bbc6e13b7fa03c5b1f6ffec 100644 GIT binary patch literal 29 kcmWfVt`c(&a&-&|4RW>e@b`0#jCXQ%wF-BPn8C;Z0CMUF!~g&Q literal 98 zcmWgg&JuGDa&-&|4RW>e@b`0#jCXQ%wF-BPKoWNf^>y-bg^E||kU#z{mgqNAVh5 From fde51e5948603287d8d3db97b176dfc906f816e1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 16:05:07 -0700 Subject: [PATCH 0869/2222] fix some debug strings --- plugins/stockpiles/OrganicMatLookup.cpp | 8 ++++---- plugins/stockpiles/StockpileSerializer.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/stockpiles/OrganicMatLookup.cpp b/plugins/stockpiles/OrganicMatLookup.cpp index 2367eddb5..a9e2210c9 100644 --- a/plugins/stockpiles/OrganicMatLookup.cpp +++ b/plugins/stockpiles/OrganicMatLookup.cpp @@ -21,7 +21,7 @@ namespace DFHack { */ void OrganicMatLookup::food_mat_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type food_idx, FoodMat& food_mat) { - DEBUG(log).print("food_lookup: food_idx(%zd) ", food_idx); + DEBUG(log).print("food_lookup: food_idx(%zd)\n", food_idx); df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; int32_t main_idx = table.organic_indexes[mat_category][food_idx]; @@ -31,11 +31,11 @@ void OrganicMatLookup::food_mat_by_idx(organic_mat_category::organic_mat_categor mat_category == organic_mat_category::Eggs) { food_mat.creature = raws.creatures.all[type]; food_mat.caste = food_mat.creature->caste[main_idx]; - DEBUG(log).print("special creature type(%d) caste(%d)", type, main_idx); + DEBUG(log).print("special creature type(%d) caste(%d)\n", type, main_idx); } else { food_mat.material.decode(type, main_idx); - DEBUG(log).print("type(%d) index(%d) token(%s)", type, main_idx, food_mat.material.getToken().c_str()); + DEBUG(log).print("type(%d) index(%d) token(%s)\n", type, main_idx, food_mat.material.getToken().c_str()); } } std::string OrganicMatLookup::food_token_by_idx(organic_mat_category::organic_mat_category mat_category, std::vector::size_type idx) { @@ -74,7 +74,7 @@ void OrganicMatLookup::food_build_map() { int16_t OrganicMatLookup::food_idx_by_token(organic_mat_category::organic_mat_category mat_category, const std::string& token) { df::world_raws& raws = world->raws; df::special_mat_table table = raws.mat_table; - DEBUG(log).print("food_idx_by_token: "); + DEBUG(log).print("food_idx_by_token:\n"); if (mat_category == organic_mat_category::Fish || mat_category == organic_mat_category::UnpreparedFish || mat_category == organic_mat_category::Eggs) { diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 04b046a3e..0cdfad13f 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1642,8 +1642,8 @@ bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { food_pair p = food_map((organic_mat_category)mat_category); if (!p.valid) continue; - DEBUG(log).print("food: %s\n", traits::key_table[mat_category]); - all = serialize_list_organic_mat(p.set_value, p.stockpile_values, (organic_mat_category)mat_category) && all; + all = serialize_list_organic_mat(p.set_value, p.stockpile_values, + (organic_mat_category)mat_category) && all; } return all; From 63f6d0bc72a350b7afd9711248f4e9c6bb825776 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 16:05:18 -0700 Subject: [PATCH 0870/2222] remove obsolete stockpile defs --- data/stockpiles/drinkanimal.dfstock | Bin 76 -> 0 bytes data/stockpiles/drinkplant.dfstock | Bin 1780 -> 0 bytes data/stockpiles/tallow.dfstock | 1 - 3 files changed, 1 deletion(-) delete mode 100644 data/stockpiles/drinkanimal.dfstock delete mode 100644 data/stockpiles/drinkplant.dfstock delete mode 100644 data/stockpiles/tallow.dfstock diff --git a/data/stockpiles/drinkanimal.dfstock b/data/stockpiles/drinkanimal.dfstock deleted file mode 100644 index f3cfabebf17ec146a46a1e43b5808916d6d4c525..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 zcmWeKH4=9Ya&-&|4RW>e@b`0#jCXQ%weod!bU~7E3iWmJafM0DU}TuU$T)+MaRH;m R3Py$vj0`&%84fTq008$;6Da@y diff --git a/data/stockpiles/drinkplant.dfstock b/data/stockpiles/drinkplant.dfstock deleted file mode 100644 index c03acc8bcbe6006b47659f0c9271ce1e85dbd204..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1780 zcmZuyIgZpo6m1^>0mC*dLLwb$E-=}*U2d1UHfrgnN$N_36A}^<5^@j@!|(R{Zo4&d zVCnt+{iSaHJ-hoH47vP}wA&hWzm23_J&cO-Lm#!<-F=+o`QV=rzJ;+L^2!MKNU>kx zFQFfl5&t(6)KWC+h?sAHm2S68<@p51qk`Tmxbty477OqNv~(wGh-yiH7YI?m7VSL2 z5ZI2KJ_z<{au$^9nWajWBL7ENe3+!I@xH&7fwxd%FzQ-(D{i^!YPB&+=z!!f>a5n1 z_mj=QjZ$$fLLP(vmPubZMDc@n+OHYPY8ZfkaEe)Xqu&Wjpt4nSdhezr4%+m!qZqy9 zjaGD&677XOfPrFiI;?39K2w&AKz*cMCm|P1qytB{h88&+h}54;dQ4yV=(`zZG&15ozFMR##ldvxT463nP(?-k%bu*dhYTKliz&BE12; n=iT6!Wj-oswOVG{A5HVvG(Vf$U(M!sv-#6({x+Nc>Qm}N diff --git a/data/stockpiles/tallow.dfstock b/data/stockpiles/tallow.dfstock deleted file mode 100644 index 2dbd68d31..000000000 --- a/data/stockpiles/tallow.dfstock +++ /dev/null @@ -1 +0,0 @@ -¾ŽjCREATURE:TOAD:TALLOWjCREATURE:TOAD_MAN:TALLOWjCREATURE:GIANT_TOAD:TALLOWjCREATURE:WORM:TALLOWjCREATURE:WORM_MAN:TALLOWjCREATURE:BIRD_BLUEJAY:TALLOWjCREATURE:BLUEJAY_MAN:TALLOWjCREATURE:GIANT_BLUEJAY:TALLOWjCREATURE:BIRD_CARDINAL:TALLOWjCREATURE:CARDINAL_MAN:TALLOWjCREATURE:GIANT_CARDINAL:TALLOWjCREATURE:BIRD_GRACKLE:TALLOWjCREATURE:GRACKLE_MAN:TALLOWjCREATURE:GIANT_GRACKLE:TALLOWjCREATURE:BIRD_ORIOLE:TALLOWjCREATURE:ORIOLE_MAN:TALLOWjCREATURE:GIANT_ORIOLE:TALLOWj!CREATURE:BIRD_RW_BLACKBIRD:TALLOWj CREATURE:RW_BLACKBIRD_MAN:TALLOWj"CREATURE:GIANT_RW_BLACKBIRD:TALLOWjCREATURE:BIRD_PENGUIN:TALLOWj#CREATURE:BIRD_PENGUIN_LITTLE:TALLOWj$CREATURE:BIRD_PENGUIN_EMPEROR:TALLOWjCREATURE:PENGUIN MAN:TALLOWj"CREATURE:BIRD_PENGUIN_GIANT:TALLOWj%CREATURE:BIRD_FALCON_PEREGRINE:TALLOWj$CREATURE:PEREGRINE FALCON MAN:TALLOWj&CREATURE:GIANT PEREGRINE FALCON:TALLOWjCREATURE:BIRD_KIWI:TALLOWjCREATURE:KIWI MAN:TALLOWjCREATURE:BIRD_KIWI_GIANT:TALLOWjCREATURE:BIRD_OSTRICH:TALLOWjCREATURE:OSTRICH MAN:TALLOWj"CREATURE:BIRD_OSTRICH_GIANT:TALLOWjCREATURE:BIRD_CROW:TALLOWjCREATURE:CROW_MAN:TALLOWjCREATURE:GIANT_CROW:TALLOWjCREATURE:BIRD_RAVEN:TALLOWjCREATURE:RAVEN_MAN:TALLOWjCREATURE:GIANT_RAVEN:TALLOWjCREATURE:BIRD_CASSOWARY:TALLOWjCREATURE:CASSOWARY_MAN:TALLOWjCREATURE:GIANT_CASSOWARY:TALLOWjCREATURE:BIRD_KEA:TALLOWjCREATURE:KEA_MAN:TALLOWjCREATURE:GIANT_KEA:TALLOWjCREATURE:BIRD_OWL_SNOWY:TALLOWjCREATURE:SNOWY_OWL_MAN:TALLOWjCREATURE:GIANT_SNOWY_OWL:TALLOWjCREATURE:SPARROW:TALLOWjCREATURE:SPARROW_MAN:TALLOWjCREATURE:GIANT_SPARROW:TALLOWj CREATURE:BIRD_STORK_WHITE:TALLOWjCREATURE:WHITE_STORK_MAN:TALLOWj!CREATURE:GIANT_WHITE_STORK:TALLOWjCREATURE:BIRD_LOON:TALLOWjCREATURE:LOON_MAN:TALLOWjCREATURE:GIANT_LOON:TALLOWjCREATURE:BIRD_OWL_BARN:TALLOWjCREATURE:BARN_OWL_MAN:TALLOWjCREATURE:GIANT_BARN_OWL:TALLOWjCREATURE:BIRD_PARAKEET:TALLOWjCREATURE:PARAKEET_MAN:TALLOWjCREATURE:GIANT_PARAKEET:TALLOWjCREATURE:BIRD_KAKAPO:TALLOWjCREATURE:KAKAPO_MAN:TALLOWjCREATURE:GIANT_KAKAPO:TALLOWj CREATURE:BIRD_PARROT_GREY:TALLOWjCREATURE:GREY_PARROT_MAN:TALLOWj!CREATURE:GIANT_GREY_PARROT:TALLOWjCREATURE:BIRD_PUFFIN:TALLOWjCREATURE:PUFFIN_MAN:TALLOWjCREATURE:GIANT_PUFFIN:TALLOWjCREATURE:BIRD_SWAN:TALLOWjCREATURE:SWAN_MAN:TALLOWjCREATURE:GIANT_SWAN:TALLOWjCREATURE:BIRD_LORIKEET:TALLOWjCREATURE:LORIKEET_MAN:TALLOWjCREATURE:GIANT_LORIKEET:TALLOWjCREATURE:BIRD_WREN:TALLOWjCREATURE:WREN_MAN:TALLOWjCREATURE:GIANT_WREN:TALLOWjCREATURE:BIRD_OSPREY:TALLOWjCREATURE:OSPREY_MAN:TALLOWjCREATURE:GIANT_OSPREY:TALLOWjCREATURE:BIRD_EMU:TALLOWjCREATURE:EMU_MAN:TALLOWjCREATURE:GIANT_EMU:TALLOWjCREATURE:BIRD_COCKATIEL:TALLOWjCREATURE:COCKATIEL_MAN:TALLOWjCREATURE:GIANT_COCKATIEL:TALLOWj)CREATURE:BIRD_LOVEBIRD_PEACH-FACED:TALLOWj(CREATURE:PEACH-FACED_LOVEBIRD_MAN:TALLOWj*CREATURE:GIANT_PEACH-FACED_LOVEBIRD:TALLOWjCREATURE:BIRD_MAGPIE:TALLOWjCREATURE:MAGPIE_MAN:TALLOWjCREATURE:GIANT_MAGPIE:TALLOWjCREATURE:BIRD_KESTREL:TALLOWjCREATURE:KESTREL_MAN:TALLOWjCREATURE:GIANT_KESTREL:TALLOWjCREATURE:BIRD_ALBATROSS:TALLOWjCREATURE:ALBATROSS_MAN:TALLOWjCREATURE:GIANT_ALBATROSS:TALLOWj%CREATURE:BIRD_OWL_GREAT_HORNED:TALLOWj$CREATURE:GREAT_HORNED_OWL_MAN:TALLOWj&CREATURE:GIANT_GREAT_HORNED_OWL:TALLOWjCREATURE:BIRD_EAGLE:TALLOWjCREATURE:EAGLE_MAN:TALLOWjCREATURE:GIANT_EAGLE:TALLOWjCREATURE:BIRD_HORNBILL:TALLOWjCREATURE:HORNBILL_MAN:TALLOWjCREATURE:GIANT_HORNBILL:TALLOWj$CREATURE:BIRD_LOVEBIRD_MASKED:TALLOWj#CREATURE:MASKED_LOVEBIRD_MAN:TALLOWj%CREATURE:GIANT_MASKED_LOVEBIRD:TALLOWjCREATURE:BIRD_BUSHTIT:TALLOWjCREATURE:BUSHTIT_MAN:TALLOWjCREATURE:GIANT_BUSHTIT:TALLOWjCREATURE:DAMSELFLY:TALLOWjCREATURE:DAMSELFLY_MAN:TALLOWjCREATURE:GIANT_DAMSELFLY:TALLOWjCREATURE:MOTH:TALLOWjCREATURE:MOTH_MAN:TALLOWjCREATURE:GIANT_MOTH:TALLOWjCREATURE:GRASSHOPPER:TALLOWjCREATURE:GRASSHOPPER_MAN:TALLOWj!CREATURE:GIANT_GRASSHOPPER:TALLOWjCREATURE:BARK_SCORPION:TALLOWj!CREATURE:BARK_SCORPION_MAN:TALLOWj#CREATURE:GIANT_BARK_SCORPION:TALLOWjCREATURE:MANTIS:TALLOWjCREATURE:MANTIS_MAN:TALLOWjCREATURE:GIANT_MANTIS:TALLOWjCREATURE:TICK:TALLOWjCREATURE:TICK_MAN:TALLOWjCREATURE:GIANT_TICK:TALLOWjCREATURE:LOUSE:TALLOWjCREATURE:LOUSE_MAN:TALLOWjCREATURE:GIANT_LOUSE:TALLOWjCREATURE:THRIPS:TALLOWjCREATURE:THRIPS_MAN:TALLOWjCREATURE:GIANT_THRIPS:TALLOWjCREATURE:SLUG:TALLOWjCREATURE:SLUG_MAN:TALLOWjCREATURE:GIANT_SLUG:TALLOWjCREATURE:MOSQUITO:TALLOWjCREATURE:MOSQUITO_MAN:TALLOWjCREATURE:GIANT_MOSQUITO:TALLOWjCREATURE:SPIDER_JUMPING:TALLOWj"CREATURE:JUMPING_SPIDER_MAN:TALLOWj$CREATURE:GIANT_JUMPING_SPIDER:TALLOWjCREATURE:TERMITE:TALLOWjCREATURE:MOON_SNAIL:TALLOWjCREATURE:MOON_SNAIL_MAN:TALLOWj CREATURE:GIANT_MOON_SNAIL:TALLOWj$CREATURE:SPIDER_BROWN_RECLUSE:TALLOWj(CREATURE:BROWN_RECLUSE_SPIDER_MAN:TALLOWj*CREATURE:GIANT_BROWN_RECLUSE_SPIDER:TALLOWjCREATURE:SNAIL:TALLOWjCREATURE:SNAIL_MAN:TALLOWjCREATURE:GIANT_SNAIL:TALLOWjCREATURE:GECKO_LEOPARD:TALLOWj!CREATURE:LEOPARD_GECKO_MAN:TALLOWj#CREATURE:GIANT_LEOPARD_GECKO:TALLOWjCREATURE:DESERT TORTOISE:TALLOWj#CREATURE:DESERT_TORTOISE_MAN:TALLOWj%CREATURE:GIANT_DESERT_TORTOISE:TALLOWjCREATURE:GILA_MONSTER:TALLOWj CREATURE:GILA_MONSTER_MAN:TALLOWj"CREATURE:GIANT_GILA_MONSTER:TALLOWjCREATURE:DOG:TALLOWjCREATURE:CAT:TALLOWjCREATURE:MULE:TALLOWjCREATURE:DONKEY:TALLOWjCREATURE:HORSE:TALLOWjCREATURE:COW:TALLOWjCREATURE:SHEEP:TALLOWjCREATURE:PIG:TALLOWjCREATURE:GOAT:TALLOWjCREATURE:BIRD_CHICKEN:TALLOWjCREATURE:CAVY:TALLOWjCREATURE:BIRD_DUCK:TALLOWjCREATURE:WATER_BUFFALO:TALLOWjCREATURE:REINDEER:TALLOWjCREATURE:BIRD_GOOSE:TALLOWjCREATURE:YAK:TALLOWjCREATURE:LLAMA:TALLOWjCREATURE:ALPACA:TALLOWjCREATURE:BIRD_GUINEAFOWL:TALLOWj!CREATURE:BIRD_PEAFOWL_BLUE:TALLOWjCREATURE:BIRD_TURKEY:TALLOWjCREATURE:RABBIT:TALLOWjCREATURE:FLY:TALLOWjCREATURE:FLY_MAN:TALLOWjCREATURE:GIANT_FLY:TALLOWjCREATURE:ROACH_LARGE:TALLOWjCREATURE:ROACH_MAN:TALLOWjCREATURE:GIANT_ROACH:TALLOWjCREATURE:BEETLE:TALLOWjCREATURE:BEETLE_MAN:TALLOWjCREATURE:GIANT_BEETLE:TALLOWjCREATURE:ANT:TALLOWj!CREATURE:BUTTERFLY_MONARCH:TALLOWj%CREATURE:BUTTERFLY_MONARCH_MAN:TALLOWj'CREATURE:GIANT_BUTTERFLY_MONARCH:TALLOWjCREATURE:FIREFLY:TALLOWjCREATURE:FIREFLY_MAN:TALLOWjCREATURE:GIANT_FIREFLY:TALLOWjCREATURE:DRAGONFLY:TALLOWjCREATURE:DRAGONFLY_MAN:TALLOWjCREATURE:GIANT_DRAGONFLY:TALLOWjCREATURE:HONEY_BEE:TALLOWjCREATURE:BUMBLEBEE:TALLOWjCREATURE:GOAT_MOUNTAIN:TALLOWj!CREATURE:GOAT_MOUNTAIN_MAN:TALLOWj#CREATURE:GIANT_GOAT_MOUNTAIN:TALLOWjCREATURE:MARMOT_HOARY:TALLOWj CREATURE:MARMOT_HOARY_MAN:TALLOWj"CREATURE:GIANT_MARMOT_HOARY:TALLOWjCREATURE:GNOME_MOUNTAIN:TALLOWjCREATURE:GNOME_DARK:TALLOWjCREATURE:WALRUS:TALLOWjCREATURE:WALRUS_MAN:TALLOWjCREATURE:GIANT_WALRUS:TALLOWj CREATURE:FISH_LAMPREY_SEA:TALLOWj!CREATURE:SHARK_GREAT_WHITE:TALLOWjCREATURE:SHARK_FRILL:TALLOWj#CREATURE:SHARK_SPINY_DOGFISH:TALLOWj'CREATURE:SHARK_WOBBEGONG_SPOTTED:TALLOWjCREATURE:SHARK_WHALE:TALLOWjCREATURE:SHARK_BASKING:TALLOWjCREATURE:SHARK_NURSE:TALLOWj#CREATURE:SHARK_MAKO_SHORTFIN:TALLOWj"CREATURE:SHARK_MAKO_LONGFIN:TALLOWjCREATURE:SHARK_TIGER:TALLOWjCREATURE:SHARK_BULL:TALLOWj#CREATURE:SHARK_REEF_BLACKTIP:TALLOWj#CREATURE:SHARK_REEF_WHITETIP:TALLOWjCREATURE:SHARK_BLUE:TALLOWj CREATURE:SHARK_HAMMERHEAD:TALLOWjCREATURE:SHARK_ANGEL:TALLOWj!CREATURE:FISH_SKATE_COMMON:TALLOWjCREATURE:FISH_RAY_MANTA:TALLOWjCREATURE:FISH_STINGRAY:TALLOWjCREATURE:FISH_COELACANTH:TALLOWjCREATURE:FISH_STURGEON:TALLOWjCREATURE:FISH_CONGER_EEL:TALLOWjCREATURE:FISH_MILKFISH:TALLOWjCREATURE:FISH_COD:TALLOWjCREATURE:FISH_OPAH:TALLOWj"CREATURE:FISH_GROUPER_GIANT:TALLOWjCREATURE:FISH_BLUEFISH:TALLOWj"CREATURE:FISH_SUNFISH_OCEAN:TALLOWjCREATURE:FISH_SWORDFISH:TALLOWjCREATURE:FISH_MARLIN:TALLOWjCREATURE:FISH_HALIBUT:TALLOWj$CREATURE:FISH_BARRACUDA_GREAT:TALLOWj!CREATURE:FISH_TUNA_BLUEFIN:TALLOWjCREATURE:NARWHAL:TALLOWjCREATURE:NARWHAL MAN:TALLOWjCREATURE:NARWHAL, GIANT:TALLOWjCREATURE:HIPPO:TALLOWjCREATURE:HIPPO_MAN:TALLOWjCREATURE:GIANT_HIPPO:TALLOWj!CREATURE:FISH_GAR_LONGNOSE:TALLOWjCREATURE:FISH_CARP:TALLOWjCREATURE:FISH_TIGERFISH:TALLOWjCREATURE:FISH_PIKE:TALLOWjCREATURE:PLATYPUS:TALLOWjCREATURE:PLATYPUS MAN:TALLOWjCREATURE:PLATYPUS, GIANT:TALLOWjCREATURE:BEAR_GRIZZLY:TALLOWj CREATURE:BEAR_GRIZZLY_MAN:TALLOWj"CREATURE:GIANT_BEAR_GRIZZLY:TALLOWjCREATURE:BEAR_BLACK:TALLOWjCREATURE:BEAR_BLACK_MAN:TALLOWj CREATURE:GIANT_BEAR_BLACK:TALLOWjCREATURE:DEER:TALLOWjCREATURE:DEER_MAN:TALLOWjCREATURE:GIANT_DEER:TALLOWjCREATURE:FOX:TALLOWjCREATURE:FOX_MAN:TALLOWjCREATURE:GIANT_FOX:TALLOWjCREATURE:RACCOON:TALLOWjCREATURE:RACCOON_MAN:TALLOWjCREATURE:GIANT_RACCOON:TALLOWjCREATURE:MACAQUE_RHESUS:TALLOWj"CREATURE:MACAQUE_RHESUS_MAN:TALLOWj$CREATURE:GIANT_MACAQUE_RHESUS:TALLOWjCREATURE:COUGAR:TALLOWjCREATURE:COUGAR_MAN:TALLOWjCREATURE:GIANT_COUGAR:TALLOWjCREATURE:WOLF:TALLOWjCREATURE:WOLF_MAN:TALLOWjCREATURE:GIANT_WOLF:TALLOWjCREATURE:GROUNDHOG:TALLOWjCREATURE:GROUNDHOG_MAN:TALLOWjCREATURE:GIANT_GROUNDHOG:TALLOWjCREATURE:ALLIGATOR:TALLOWjCREATURE:ALLIGATOR_MAN:TALLOWjCREATURE:GIANT_ALLIGATOR:TALLOWjCREATURE:BIRD_BUZZARD:TALLOWjCREATURE:BUZZARD_MAN:TALLOWjCREATURE:GIANT_BUZZARD:TALLOWjCREATURE:PANDA:TALLOWjCREATURE:PANDA, GIGANTIC:TALLOWjCREATURE:PANDA MAN:TALLOWjCREATURE:CAPYBARA:TALLOWjCREATURE:CAPYBARA, GIANT:TALLOWjCREATURE:CAPYBARA MAN:TALLOWjCREATURE:BADGER:TALLOWjCREATURE:BADGER MAN:TALLOWjCREATURE:BADGER, GIANT:TALLOWjCREATURE:MOOSE:TALLOWjCREATURE:MOOSE MAN:TALLOWjCREATURE:MOOSE, GIANT:TALLOWjCREATURE:RED PANDA:TALLOWjCREATURE:RED PANDA MAN:TALLOWj CREATURE:RED PANDA, GIANT:TALLOWjCREATURE:ELEPHANT:TALLOWjCREATURE:ELEPHANT_MAN:TALLOWjCREATURE:GIANT_ELEPHANT:TALLOWjCREATURE:WARTHOG:TALLOWjCREATURE:WARTHOG_MAN:TALLOWjCREATURE:GIANT_WARTHOG:TALLOWjCREATURE:LION:TALLOWjCREATURE:LION_MAN:TALLOWjCREATURE:GIANT_LION:TALLOWjCREATURE:LEOPARD:TALLOWjCREATURE:LEOPARD_MAN:TALLOWjCREATURE:GIANT_LEOPARD:TALLOWjCREATURE:JAGUAR:TALLOWjCREATURE:JAGUAR_MAN:TALLOWjCREATURE:GIANT_JAGUAR:TALLOWjCREATURE:TIGER:TALLOWjCREATURE:TIGER_MAN:TALLOWjCREATURE:GIANT_TIGER:TALLOWjCREATURE:CHEETAH:TALLOWjCREATURE:CHEETAH_MAN:TALLOWjCREATURE:GIANT_CHEETAH:TALLOWjCREATURE:GAZELLE:TALLOWjCREATURE:GAZELLE_MAN:TALLOWjCREATURE:GIANT_GAZELLE:TALLOWjCREATURE:MANDRILL:TALLOWjCREATURE:MANDRILL_MAN:TALLOWjCREATURE:GIANT_MANDRILL:TALLOWjCREATURE:CHIMPANZEE:TALLOWjCREATURE:BONOBO:TALLOWjCREATURE:GORILLA:TALLOWjCREATURE:ORANGUTAN:TALLOWjCREATURE:GIBBON_SIAMANG:TALLOWj#CREATURE:GIBBON_WHITE_HANDED:TALLOWj#CREATURE:GIBBON_BLACK_HANDED:TALLOWjCREATURE:GIBBON_GRAY:TALLOWjCREATURE:GIBBON_SILVERY:TALLOWjCREATURE:GIBBON_PILEATED:TALLOWjCREATURE:GIBBON_BILOU:TALLOWj#CREATURE:GIBBON_WHITE_BROWED:TALLOWj$CREATURE:GIBBON_BLACK_CRESTED:TALLOWjCREATURE:CAMEL_1_HUMP:TALLOWj CREATURE:CAMEL_1_HUMP_MAN:TALLOWj"CREATURE:GIANT_CAMEL_1_HUMP:TALLOWjCREATURE:CAMEL_2_HUMP:TALLOWj CREATURE:CAMEL_2_HUMP_MAN:TALLOWj"CREATURE:GIANT_CAMEL_2_HUMP:TALLOWj#CREATURE:CROCODILE_SALTWATER:TALLOWj'CREATURE:CROCODILE_SALTWATER_MAN:TALLOWj)CREATURE:GIANT_CROCODILE_SALTWATER:TALLOWjCREATURE:BIRD_VULTURE:TALLOWjCREATURE:VULTURE_MAN:TALLOWjCREATURE:GIANT_VULTURE:TALLOWjCREATURE:RHINOCEROS:TALLOWjCREATURE:RHINOCEROS_MAN:TALLOWj CREATURE:GIANT_RHINOCEROS:TALLOWjCREATURE:GIRAFFE:TALLOWjCREATURE:GIRAFFE_MAN:TALLOWjCREATURE:GIANT_GIRAFFE:TALLOWjCREATURE:HONEY BADGER:TALLOWj CREATURE:HONEY BADGER MAN:TALLOWj#CREATURE:HONEY BADGER, GIANT:TALLOWjCREATURE:GIANT TORTOISE:TALLOWj"CREATURE:GIANT TORTOISE MAN:TALLOWj!CREATURE:GIGANTIC TORTOISE:TALLOWjCREATURE:ARMADILLO:TALLOWjCREATURE:ARMADILLO MAN:TALLOWj CREATURE:ARMADILLO, GIANT:TALLOWjCREATURE:MUSKOX:TALLOWjCREATURE:MUSKOX_MAN:TALLOWjCREATURE:GIANT_MUSKOX:TALLOWjCREATURE:ELK:TALLOWjCREATURE:ELK_MAN:TALLOWjCREATURE:GIANT_ELK:TALLOWjCREATURE:BEAR_POLAR:TALLOWjCREATURE:BEAR_POLAR_MAN:TALLOWj CREATURE:GIANT_BEAR_POLAR:TALLOWjCREATURE:WOLVERINE:TALLOWjCREATURE:WOLVERINE_MAN:TALLOWjCREATURE:GIANT_WOLVERINE:TALLOWjCREATURE:CHINCHILLA:TALLOWjCREATURE:CHINCHILLA_MAN:TALLOWj CREATURE:GIANT_CHINCHILLA:TALLOWjCREATURE:FLOATING_GUTS:TALLOWjCREATURE:DRUNIAN:TALLOWjCREATURE:CREEPING_EYE:TALLOWj&CREATURE:VORACIOUS_CAVE_CRAWLER:TALLOWjCREATURE:BLIND_CAVE_OGRE:TALLOWjCREATURE:CAP_HOPPER:TALLOWjCREATURE:MAGMA_CRAB:TALLOWjCREATURE:CRUNDLE:TALLOWjCREATURE:HUNGRY_HEAD:TALLOWjCREATURE:ELK_BIRD:TALLOWjCREATURE:HELMET_SNAKE:TALLOWjCREATURE:GREEN_DEVOURER:TALLOWjCREATURE:RUTHERER:TALLOWjCREATURE:CREEPY_CRAWLER:TALLOWjCREATURE:DRALTHA:TALLOWjCREATURE:GIANT_EARTHWORM:TALLOWjCREATURE:BUGBAT:TALLOWjCREATURE:MANERA:TALLOWjCREATURE:MOLEMARIAN:TALLOWjCREATURE:JABBERER:TALLOWjCREATURE:POND_GRABBER:TALLOWjCREATURE:BLIND_CAVE_BEAR:TALLOWjCREATURE:CAVE_DRAGON:TALLOWjCREATURE:REACHER:TALLOWjCREATURE:GORLAK:TALLOWjCREATURE:OCTOPUS:TALLOWjCREATURE:OCTOPUS_MAN:TALLOWjCREATURE:GIANT_OCTOPUS:TALLOWjCREATURE:CRAB:TALLOWjCREATURE:CRAB_MAN:TALLOWjCREATURE:GIANT_CRAB:TALLOWjCREATURE:LEOPARD_SEAL:TALLOWj CREATURE:LEOPARD_SEAL_MAN:TALLOWj"CREATURE:GIANT_LEOPARD_SEAL:TALLOWjCREATURE:CUTTLEFISH:TALLOWjCREATURE:CUTTLEFISH_MAN:TALLOWj CREATURE:GIANT_CUTTLEFISH:TALLOWjCREATURE:ORCA:TALLOWjCREATURE:ORCA_MAN:TALLOWjCREATURE:GIANT_ORCA:TALLOWjCREATURE:HORSESHOE_CRAB:TALLOWj"CREATURE:HORSESHOE_CRAB_MAN:TALLOWj$CREATURE:GIANT_HORSESHOE_CRAB:TALLOWjCREATURE:SPERM_WHALE:TALLOWjCREATURE:SPERM_WHALE_MAN:TALLOWj!CREATURE:GIANT_SPERM_WHALE:TALLOWjCREATURE:ELEPHANT_SEAL:TALLOWj!CREATURE:ELEPHANT_SEAL_MAN:TALLOWj#CREATURE:GIANT_ELEPHANT_SEAL:TALLOWjCREATURE:HARP_SEAL:TALLOWjCREATURE:HARP_SEAL_MAN:TALLOWjCREATURE:GIANT_HARP_SEAL:TALLOWjCREATURE:NAUTILUS:TALLOWjCREATURE:NAUTILUS_MAN:TALLOWjCREATURE:GIANT_NAUTILUS:TALLOWjCREATURE:FOXSQUIRREL:TALLOWjCREATURE:MOGHOPPER:TALLOWjCREATURE:RAT_DEMON:TALLOWjCREATURE:WAMBLER_FLUFFY:TALLOWj'CREATURE:LIZARD_RHINO_TWO_LEGGED:TALLOWjCREATURE:WORM_KNUCKLE:TALLOWjCREATURE:SPIDER_PHANTOM:TALLOWjCREATURE:FLY_ACORN:TALLOWjCREATURE:GNAT_BLOOD:TALLOWjCREATURE:LIZARD:TALLOWjCREATURE:LIZARD_MAN:TALLOWjCREATURE:GIANT_LIZARD:TALLOWjCREATURE:SKINK:TALLOWjCREATURE:SKINK_MAN:TALLOWjCREATURE:GIANT_SKINK:TALLOWjCREATURE:CHAMELEON:TALLOWjCREATURE:CHAMELEON_MAN:TALLOWjCREATURE:GIANT_CHAMELEON:TALLOWjCREATURE:ANOLE:TALLOWjCREATURE:ANOLE_MAN:TALLOWjCREATURE:GIANT_ANOLE:TALLOWjCREATURE:IGUANA:TALLOWjCREATURE:IGUANA_MAN:TALLOWjCREATURE:GIANT_IGUANA:TALLOWjCREATURE:RIVER OTTER:TALLOWjCREATURE:SEA OTTER:TALLOWjCREATURE:OTTER_MAN:TALLOWjCREATURE:GIANT_OTTER:TALLOWjCREATURE:SNAPPING TURTLE:TALLOWj)CREATURE:ALLIGATOR SNAPPING TURTLE:TALLOWj#CREATURE:SNAPPING_TURTLE_MAN:TALLOWj%CREATURE:GIANT_SNAPPING_TURTLE:TALLOWjCREATURE:BEAVER:TALLOWjCREATURE:BEAVER_MAN:TALLOWjCREATURE:GIANT_BEAVER:TALLOWjCREATURE:LEECH:TALLOWjCREATURE:LEECH_MAN:TALLOWjCREATURE:GIANT_LEECH:TALLOWjCREATURE:AXOLOTL:TALLOWjCREATURE:AXOLOTL_MAN:TALLOWjCREATURE:GIANT_AXOLOTL:TALLOWjCREATURE:MINK:TALLOWjCREATURE:MINK_MAN:TALLOWjCREATURE:GIANT_MINK:TALLOWjCREATURE:POND_TURTLE:TALLOWjCREATURE:POND_TURTLE_MAN:TALLOWj!CREATURE:GIANT_POND_TURTLE:TALLOWjCREATURE:RAT:TALLOWjCREATURE:RAT_MAN:TALLOWjCREATURE:SQUIRREL_GRAY:TALLOWj!CREATURE:SQUIRREL_GRAY_MAN:TALLOWj#CREATURE:GIANT_SQUIRREL_GRAY:TALLOWjCREATURE:SQUIRREL_RED:TALLOWj CREATURE:SQUIRREL_RED_MAN:TALLOWj"CREATURE:GIANT_SQUIRREL_RED:TALLOWjCREATURE:CHIPMUNK:TALLOWjCREATURE:CHIPMUNK_MAN:TALLOWjCREATURE:GIANT_CHIPMUNK:TALLOWjCREATURE:HAMSTER:TALLOWjCREATURE:HAMSTER_MAN:TALLOWjCREATURE:GIANT_HAMSTER:TALLOWjCREATURE:HEDGEHOG:TALLOWjCREATURE:HEDGEHOG_MAN:TALLOWjCREATURE:GIANT_HEDGEHOG:TALLOWjCREATURE:SQUIRREL_FLYING:TALLOWj#CREATURE:FLYING_SQUIRREL_MAN:TALLOWj%CREATURE:GIANT_FLYING_SQUIRREL:TALLOWjCREATURE:MUSSEL:TALLOWjCREATURE:OYSTER:TALLOWjCREATURE:FISH_SALMON:TALLOWjCREATURE:FISH_CLOWNFISH:TALLOWjCREATURE:FISH_HAGFISH:TALLOWj"CREATURE:FISH_LAMPREY_BROOK:TALLOWjCREATURE:FISH_RAY_BAT:TALLOWj"CREATURE:FISH_RAY_THORNBACK:TALLOWj$CREATURE:FISH_RATFISH_SPOTTED:TALLOWjCREATURE:FISH_HERRING:TALLOWjCREATURE:FISH_SHAD:TALLOWjCREATURE:FISH_ANCHOVY:TALLOWj$CREATURE:FISH_TROUT_STEELHEAD:TALLOWjCREATURE:FISH_HAKE:TALLOWjCREATURE:FISH_SEAHORSE:TALLOWjCREATURE:FISH_GLASSEYE:TALLOWj)CREATURE:FISH_PUFFER_WHITE_SPOTTED:TALLOWjCREATURE:FISH_SOLE:TALLOWjCREATURE:FISH_FLOUNDER:TALLOWjCREATURE:FISH_MACKEREL:TALLOWj$CREATURE:JELLYFISH_SEA_NETTLE:TALLOWjCREATURE:SQUID:TALLOWjCREATURE:SQUID MAN:TALLOWjCREATURE:GIGANTIC SQUID:TALLOWjCREATURE:FISH_LUNGFISH:TALLOWj CREATURE:FISH_LOACH_CLOWN:TALLOWj#CREATURE:FISH_BULLHEAD_BROWN:TALLOWj$CREATURE:FISH_BULLHEAD_YELLOW:TALLOWj#CREATURE:FISH_BULLHEAD_BLACK:TALLOWj%CREATURE:FISH_KNIFEFISH_BANDED:TALLOWjCREATURE:FISH_CHAR:TALLOWj"CREATURE:FISH_TROUT_RAINBOW:TALLOWj"CREATURE:FISH_MOLLY_SAILFIN:TALLOWjCREATURE:FISH_GUPPY:TALLOWjCREATURE:FISH_PERCH:TALLOWjCREATURE:DWARF:TALLOWjCREATURE:HUMAN:TALLOWjCREATURE:ELF:TALLOWjCREATURE:GOBLIN:TALLOWjCREATURE:KOBOLD:TALLOWjCREATURE:GREMLIN:TALLOWjCREATURE:TROLL:TALLOWjCREATURE:OGRE:TALLOWjCREATURE:UNICORN:TALLOWjCREATURE:DRAGON:TALLOWjCREATURE:SATYR:TALLOWjCREATURE:GIANT:TALLOWjCREATURE:CYCLOPS:TALLOWjCREATURE:ETTIN:TALLOWjCREATURE:MINOTAUR:TALLOWjCREATURE:YETI:TALLOWjCREATURE:SASQUATCH:TALLOWjCREATURE:BLIZZARD_MAN:TALLOWjCREATURE:WOLF_ICE:TALLOWjCREATURE:FAIRY:TALLOWjCREATURE:PIXIE:TALLOWjCREATURE:BEAK_DOG:TALLOWjCREATURE:GRIMELING:TALLOWjCREATURE:BLENDEC_FOUL:TALLOWjCREATURE:STRANGLER:TALLOWjCREATURE:NIGHTWING:TALLOWjCREATURE:HARPY:TALLOWjCREATURE:HYDRA:TALLOWjCREATURE:MERPERSON:TALLOWjCREATURE:SEA_SERPENT:TALLOWjCREATURE:SEA_MONSTER:TALLOWjCREATURE:BIRD_ROC:TALLOWjCREATURE:CROCODILE_CAVE:TALLOWjCREATURE:TOAD_GIANT_CAVE:TALLOWjCREATURE:OLM_GIANT:TALLOWjCREATURE:BAT_GIANT:TALLOWjCREATURE:RAT_GIANT:TALLOWjCREATURE:RAT_LARGE:TALLOWjCREATURE:MOLE_DOG_NAKED:TALLOWjCREATURE:TROGLODYTE:TALLOWjCREATURE:MOLE_GIANT:TALLOWjCREATURE:IMP_FIRE:TALLOWj!CREATURE:SPIDER_CAVE_GIANT:TALLOWjCREATURE:SPIDER_CAVE:TALLOWjCREATURE:FISH_CAVE:TALLOWjCREATURE:CAVE_FISH_MAN:TALLOWjCREATURE:LOBSTER_CAVE:TALLOWjCREATURE:OLM:TALLOWjCREATURE:OLM_MAN:TALLOWjCREATURE:BAT:TALLOWjCREATURE:BAT_MAN:TALLOWjCREATURE:MAGGOT_PURRING:TALLOWj!CREATURE:BIRD_SWALLOW_CAVE:TALLOWj CREATURE:CAVE_SWALLOW_MAN:TALLOWj'CREATURE:BIRD_SWALLOW_CAVE_GIANT:TALLOWjCREATURE:AMPHIBIAN_MAN:TALLOWjCREATURE:REPTILE_MAN:TALLOWjCREATURE:SERPENT_MAN:TALLOWjCREATURE:ANT_MAN:TALLOWjCREATURE:RODENT MAN:TALLOWjCREATURE:WILD_BOAR:TALLOWjCREATURE:WILD_BOAR_MAN:TALLOWjCREATURE:GIANT_WILD_BOAR:TALLOWjCREATURE:COYOTE:TALLOWjCREATURE:COYOTE_MAN:TALLOWjCREATURE:GIANT_COYOTE:TALLOWjCREATURE:KANGAROO:TALLOWjCREATURE:KANGAROO_MAN:TALLOWjCREATURE:GIANT_KANGAROO:TALLOWjCREATURE:KOALA:TALLOWjCREATURE:KOALA_MAN:TALLOWjCREATURE:GIANT_KOALA:TALLOWjCREATURE:ADDER:TALLOWjCREATURE:ADDER_MAN:TALLOWjCREATURE:GIANT_ADDER:TALLOWjCREATURE:ECHIDNA:TALLOWjCREATURE:ECHIDNA_MAN:TALLOWjCREATURE:GIANT_ECHIDNA:TALLOWjCREATURE:PORCUPINE:TALLOWjCREATURE:PORCUPINE_MAN:TALLOWjCREATURE:GIANT_PORCUPINE:TALLOWjCREATURE:KINGSNAKE:TALLOWjCREATURE:KINGSNAKE_MAN:TALLOWjCREATURE:GIANT_KINGSNAKE:TALLOWjCREATURE:GRAY_LANGUR:TALLOWjCREATURE:GRAY_LANGUR_MAN:TALLOWj!CREATURE:GIANT_GRAY_LANGUR:TALLOWjCREATURE:BOBCAT:TALLOWjCREATURE:BOBCAT_MAN:TALLOWjCREATURE:GIANT_BOBCAT:TALLOWjCREATURE:SKUNK:TALLOWjCREATURE:SKUNK_MAN:TALLOWjCREATURE:GIANT_SKUNK:TALLOWjCREATURE:GREEN_TREE_FROG:TALLOWj#CREATURE:GREEN_TREE_FROG_MAN:TALLOWj%CREATURE:GIANT_GREEN_TREE_FROG:TALLOWjCREATURE:HARE:TALLOWjCREATURE:HARE_MAN:TALLOWjCREATURE:GIANT_HARE:TALLOWjCREATURE:RATTLESNAKE:TALLOWjCREATURE:RATTLESNAKE_MAN:TALLOWj!CREATURE:GIANT_RATTLESNAKE:TALLOWjCREATURE:WEASEL:TALLOWjCREATURE:WEASEL_MAN:TALLOWjCREATURE:GIANT_WEASEL:TALLOWj CREATURE:COPPERHEAD_SNAKE:TALLOWj$CREATURE:COPPERHEAD_SNAKE_MAN:TALLOWj&CREATURE:GIANT_COPPERHEAD_SNAKE:TALLOWjCREATURE:IBEX:TALLOWjCREATURE:IBEX_MAN:TALLOWjCREATURE:GIANT_IBEX:TALLOWjCREATURE:WOMBAT:TALLOWjCREATURE:WOMBAT_MAN:TALLOWjCREATURE:GIANT_WOMBAT:TALLOWjCREATURE:DINGO:TALLOWjCREATURE:DINGO_MAN:TALLOWjCREATURE:GIANT_DINGO:TALLOWjCREATURE:COATI:TALLOWjCREATURE:COATI_MAN:TALLOWjCREATURE:GIANT_COATI:TALLOWjCREATURE:OPOSSUM:TALLOWjCREATURE:OPOSSUM_MAN:TALLOWjCREATURE:GIANT_OPOSSUM:TALLOWjCREATURE:MONGOOSE:TALLOWjCREATURE:MONGOOSE_MAN:TALLOWjCREATURE:GIANT_MONGOOSE:TALLOWjCREATURE:HYENA:TALLOWjCREATURE:HYENA_MAN:TALLOWjCREATURE:GIANT_HYENA:TALLOWjCREATURE:ANACONDA:TALLOWjCREATURE:ANACONDA_MAN:TALLOWjCREATURE:GIANT_ANACONDA:TALLOWjCREATURE:MONITOR_LIZARD:TALLOWj"CREATURE:MONITOR_LIZARD_MAN:TALLOWj$CREATURE:GIANT_MONITOR_LIZARD:TALLOWjCREATURE:KING_COBRA:TALLOWjCREATURE:KING_COBRA_MAN:TALLOWj CREATURE:GIANT_KING_COBRA:TALLOWjCREATURE:OCELOT:TALLOWjCREATURE:OCELOT_MAN:TALLOWjCREATURE:GIANT_OCELOT:TALLOWjCREATURE:JACKAL:TALLOWjCREATURE:JACKAL_MAN:TALLOWjCREATURE:GIANT_JACKAL:TALLOWjCREATURE:CAPUCHIN:TALLOWjCREATURE:CAPUCHIN_MAN:TALLOWjCREATURE:GIANT_CAPUCHIN:TALLOWjCREATURE:SLOTH:TALLOWjCREATURE:SLOTH_MAN:TALLOWjCREATURE:GIANT_SLOTH:TALLOWjCREATURE:SPIDER_MONKEY:TALLOWj!CREATURE:SPIDER_MONKEY_MAN:TALLOWj#CREATURE:GIANT_SPIDER_MONKEY:TALLOWjCREATURE:PANGOLIN:TALLOWjCREATURE:PANGOLIN_MAN:TALLOWjCREATURE:GIANT_PANGOLIN:TALLOWjCREATURE:BLACK_MAMBA:TALLOWjCREATURE:BLACK_MAMBA_MAN:TALLOWj!CREATURE:GIANT_BLACK_MAMBA:TALLOWjCREATURE:BEAR_SLOTH:TALLOWjCREATURE:SLOTH_BEAR_MAN:TALLOWj CREATURE:GIANT_SLOTH_BEAR:TALLOWjCREATURE:AYE-AYE:TALLOWjCREATURE:AYE-AYE_MAN:TALLOWjCREATURE:GIANT_AYE-AYE:TALLOWjCREATURE:BUSHMASTER:TALLOWjCREATURE:BUSHMASTER_MAN:TALLOWj CREATURE:GIANT_BUSHMASTER:TALLOWjCREATURE:PYTHON:TALLOWjCREATURE:PYTHON_MAN:TALLOWjCREATURE:GIANT_PYTHON:TALLOWjCREATURE:TAPIR:TALLOWjCREATURE:TAPIR_MAN:TALLOWjCREATURE:GIANT_TAPIR:TALLOWjCREATURE:IMPALA:TALLOWjCREATURE:IMPALA_MAN:TALLOWjCREATURE:GIANT_IMPALA:TALLOWjCREATURE:AARDVARK:TALLOWjCREATURE:AARDVARK_MAN:TALLOWjCREATURE:GIANT_AARDVARK:TALLOWjCREATURE:LION_TAMARIN:TALLOWj CREATURE:LION_TAMARIN_MAN:TALLOWj"CREATURE:GIANT_LION_TAMARIN:TALLOWjCREATURE:STOAT:TALLOWjCREATURE:STOAT_MAN:TALLOWjCREATURE:GIANT_STOAT:TALLOWjCREATURE:LYNX:TALLOWjCREATURE:LYNX_MAN:TALLOWjCREATURE:GIANT_LYNX:TALLOWjCREATURE:GNOLL:TALLOWjCREATURE:NAGA:TALLOWj!CREATURE:FORGOTTEN_BEAST_2:TALLOWj!CREATURE:FORGOTTEN_BEAST_4:TALLOWj!CREATURE:FORGOTTEN_BEAST_5:TALLOWj!CREATURE:FORGOTTEN_BEAST_6:TALLOWj!CREATURE:FORGOTTEN_BEAST_7:TALLOWj"CREATURE:FORGOTTEN_BEAST_10:TALLOWj"CREATURE:FORGOTTEN_BEAST_12:TALLOWj"CREATURE:FORGOTTEN_BEAST_13:TALLOWj"CREATURE:FORGOTTEN_BEAST_16:TALLOWj"CREATURE:FORGOTTEN_BEAST_17:TALLOWj"CREATURE:FORGOTTEN_BEAST_18:TALLOWj"CREATURE:FORGOTTEN_BEAST_19:TALLOWj"CREATURE:FORGOTTEN_BEAST_20:TALLOWj"CREATURE:FORGOTTEN_BEAST_22:TALLOWj"CREATURE:FORGOTTEN_BEAST_23:TALLOWj"CREATURE:FORGOTTEN_BEAST_24:TALLOWj"CREATURE:FORGOTTEN_BEAST_25:TALLOWj"CREATURE:FORGOTTEN_BEAST_26:TALLOWj"CREATURE:FORGOTTEN_BEAST_27:TALLOWj"CREATURE:FORGOTTEN_BEAST_28:TALLOWj"CREATURE:FORGOTTEN_BEAST_29:TALLOWj"CREATURE:FORGOTTEN_BEAST_32:TALLOWj"CREATURE:FORGOTTEN_BEAST_33:TALLOWj"CREATURE:FORGOTTEN_BEAST_34:TALLOWj"CREATURE:FORGOTTEN_BEAST_35:TALLOWj"CREATURE:FORGOTTEN_BEAST_36:TALLOWj"CREATURE:FORGOTTEN_BEAST_39:TALLOWj"CREATURE:FORGOTTEN_BEAST_41:TALLOWj"CREATURE:FORGOTTEN_BEAST_42:TALLOWj"CREATURE:FORGOTTEN_BEAST_43:TALLOWj"CREATURE:FORGOTTEN_BEAST_44:TALLOWj"CREATURE:FORGOTTEN_BEAST_45:TALLOWj"CREATURE:FORGOTTEN_BEAST_47:TALLOWj"CREATURE:FORGOTTEN_BEAST_50:TALLOWj"CREATURE:FORGOTTEN_BEAST_52:TALLOWj"CREATURE:FORGOTTEN_BEAST_53:TALLOWj"CREATURE:FORGOTTEN_BEAST_55:TALLOWj"CREATURE:FORGOTTEN_BEAST_56:TALLOWj"CREATURE:FORGOTTEN_BEAST_58:TALLOWj"CREATURE:FORGOTTEN_BEAST_59:TALLOWj"CREATURE:FORGOTTEN_BEAST_60:TALLOWj"CREATURE:FORGOTTEN_BEAST_61:TALLOWj"CREATURE:FORGOTTEN_BEAST_64:TALLOWj"CREATURE:FORGOTTEN_BEAST_66:TALLOWj"CREATURE:FORGOTTEN_BEAST_67:TALLOWj"CREATURE:FORGOTTEN_BEAST_69:TALLOWj"CREATURE:FORGOTTEN_BEAST_71:TALLOWj"CREATURE:FORGOTTEN_BEAST_72:TALLOWj"CREATURE:FORGOTTEN_BEAST_73:TALLOWj"CREATURE:FORGOTTEN_BEAST_74:TALLOWj"CREATURE:FORGOTTEN_BEAST_75:TALLOWj"CREATURE:FORGOTTEN_BEAST_78:TALLOWj"CREATURE:FORGOTTEN_BEAST_80:TALLOWj"CREATURE:FORGOTTEN_BEAST_81:TALLOWj"CREATURE:FORGOTTEN_BEAST_82:TALLOWj"CREATURE:FORGOTTEN_BEAST_83:TALLOWj"CREATURE:FORGOTTEN_BEAST_84:TALLOWj"CREATURE:FORGOTTEN_BEAST_86:TALLOWj"CREATURE:FORGOTTEN_BEAST_87:TALLOWj"CREATURE:FORGOTTEN_BEAST_89:TALLOWj"CREATURE:FORGOTTEN_BEAST_90:TALLOWj"CREATURE:FORGOTTEN_BEAST_92:TALLOWj"CREATURE:FORGOTTEN_BEAST_94:TALLOWj"CREATURE:FORGOTTEN_BEAST_95:TALLOWj"CREATURE:FORGOTTEN_BEAST_96:TALLOWj"CREATURE:FORGOTTEN_BEAST_97:TALLOWj"CREATURE:FORGOTTEN_BEAST_98:TALLOWj#CREATURE:FORGOTTEN_BEAST_100:TALLOWj#CREATURE:FORGOTTEN_BEAST_105:TALLOWj#CREATURE:FORGOTTEN_BEAST_106:TALLOWj#CREATURE:FORGOTTEN_BEAST_107:TALLOWj#CREATURE:FORGOTTEN_BEAST_108:TALLOWj#CREATURE:FORGOTTEN_BEAST_109:TALLOWj#CREATURE:FORGOTTEN_BEAST_111:TALLOWj#CREATURE:FORGOTTEN_BEAST_112:TALLOWj#CREATURE:FORGOTTEN_BEAST_113:TALLOWj#CREATURE:FORGOTTEN_BEAST_114:TALLOWj#CREATURE:FORGOTTEN_BEAST_115:TALLOWj#CREATURE:FORGOTTEN_BEAST_116:TALLOWj#CREATURE:FORGOTTEN_BEAST_117:TALLOWj#CREATURE:FORGOTTEN_BEAST_118:TALLOWj#CREATURE:FORGOTTEN_BEAST_119:TALLOWj#CREATURE:FORGOTTEN_BEAST_120:TALLOWj#CREATURE:FORGOTTEN_BEAST_122:TALLOWj#CREATURE:FORGOTTEN_BEAST_123:TALLOWj#CREATURE:FORGOTTEN_BEAST_124:TALLOWj#CREATURE:FORGOTTEN_BEAST_125:TALLOWj#CREATURE:FORGOTTEN_BEAST_127:TALLOWj#CREATURE:FORGOTTEN_BEAST_128:TALLOWj#CREATURE:FORGOTTEN_BEAST_130:TALLOWj#CREATURE:FORGOTTEN_BEAST_131:TALLOWj#CREATURE:FORGOTTEN_BEAST_132:TALLOWj#CREATURE:FORGOTTEN_BEAST_133:TALLOWj#CREATURE:FORGOTTEN_BEAST_134:TALLOWj#CREATURE:FORGOTTEN_BEAST_135:TALLOWj#CREATURE:FORGOTTEN_BEAST_137:TALLOWj#CREATURE:FORGOTTEN_BEAST_138:TALLOWj#CREATURE:FORGOTTEN_BEAST_139:TALLOWj#CREATURE:FORGOTTEN_BEAST_141:TALLOWj#CREATURE:FORGOTTEN_BEAST_142:TALLOWj#CREATURE:FORGOTTEN_BEAST_144:TALLOWj#CREATURE:FORGOTTEN_BEAST_146:TALLOWj#CREATURE:FORGOTTEN_BEAST_148:TALLOWj#CREATURE:FORGOTTEN_BEAST_149:TALLOWj#CREATURE:FORGOTTEN_BEAST_150:TALLOWj#CREATURE:FORGOTTEN_BEAST_152:TALLOWj#CREATURE:FORGOTTEN_BEAST_154:TALLOWj#CREATURE:FORGOTTEN_BEAST_156:TALLOWj#CREATURE:FORGOTTEN_BEAST_157:TALLOWj#CREATURE:FORGOTTEN_BEAST_158:TALLOWj#CREATURE:FORGOTTEN_BEAST_159:TALLOWj#CREATURE:FORGOTTEN_BEAST_161:TALLOWj#CREATURE:FORGOTTEN_BEAST_162:TALLOWj#CREATURE:FORGOTTEN_BEAST_163:TALLOWj#CREATURE:FORGOTTEN_BEAST_165:TALLOWj#CREATURE:FORGOTTEN_BEAST_167:TALLOWj#CREATURE:FORGOTTEN_BEAST_168:TALLOWj#CREATURE:FORGOTTEN_BEAST_169:TALLOWj#CREATURE:FORGOTTEN_BEAST_170:TALLOWj#CREATURE:FORGOTTEN_BEAST_171:TALLOWj#CREATURE:FORGOTTEN_BEAST_172:TALLOWj#CREATURE:FORGOTTEN_BEAST_173:TALLOWj#CREATURE:FORGOTTEN_BEAST_176:TALLOWj#CREATURE:FORGOTTEN_BEAST_177:TALLOWj#CREATURE:FORGOTTEN_BEAST_178:TALLOWj#CREATURE:FORGOTTEN_BEAST_179:TALLOWj#CREATURE:FORGOTTEN_BEAST_180:TALLOWj#CREATURE:FORGOTTEN_BEAST_181:TALLOWj#CREATURE:FORGOTTEN_BEAST_182:TALLOWj#CREATURE:FORGOTTEN_BEAST_183:TALLOWj#CREATURE:FORGOTTEN_BEAST_184:TALLOWj#CREATURE:FORGOTTEN_BEAST_185:TALLOWj#CREATURE:FORGOTTEN_BEAST_186:TALLOWj#CREATURE:FORGOTTEN_BEAST_188:TALLOWj#CREATURE:FORGOTTEN_BEAST_189:TALLOWj#CREATURE:FORGOTTEN_BEAST_190:TALLOWj#CREATURE:FORGOTTEN_BEAST_191:TALLOWj#CREATURE:FORGOTTEN_BEAST_193:TALLOWj#CREATURE:FORGOTTEN_BEAST_194:TALLOWj#CREATURE:FORGOTTEN_BEAST_195:TALLOWj#CREATURE:FORGOTTEN_BEAST_196:TALLOWj#CREATURE:FORGOTTEN_BEAST_197:TALLOWj#CREATURE:FORGOTTEN_BEAST_199:TALLOWj#CREATURE:FORGOTTEN_BEAST_200:TALLOWj#CREATURE:FORGOTTEN_BEAST_201:TALLOWj#CREATURE:FORGOTTEN_BEAST_204:TALLOWj#CREATURE:FORGOTTEN_BEAST_205:TALLOWj#CREATURE:FORGOTTEN_BEAST_206:TALLOWj#CREATURE:FORGOTTEN_BEAST_207:TALLOWj#CREATURE:FORGOTTEN_BEAST_208:TALLOWj#CREATURE:FORGOTTEN_BEAST_209:TALLOWj#CREATURE:FORGOTTEN_BEAST_210:TALLOWj#CREATURE:FORGOTTEN_BEAST_211:TALLOWj#CREATURE:FORGOTTEN_BEAST_212:TALLOWj#CREATURE:FORGOTTEN_BEAST_213:TALLOWj#CREATURE:FORGOTTEN_BEAST_214:TALLOWj#CREATURE:FORGOTTEN_BEAST_215:TALLOWj#CREATURE:FORGOTTEN_BEAST_216:TALLOWj#CREATURE:FORGOTTEN_BEAST_217:TALLOWj#CREATURE:FORGOTTEN_BEAST_218:TALLOWj#CREATURE:FORGOTTEN_BEAST_220:TALLOWj#CREATURE:FORGOTTEN_BEAST_221:TALLOWj#CREATURE:FORGOTTEN_BEAST_224:TALLOWj#CREATURE:FORGOTTEN_BEAST_225:TALLOWj#CREATURE:FORGOTTEN_BEAST_228:TALLOWj#CREATURE:FORGOTTEN_BEAST_231:TALLOWj#CREATURE:FORGOTTEN_BEAST_232:TALLOWj#CREATURE:FORGOTTEN_BEAST_235:TALLOWj#CREATURE:FORGOTTEN_BEAST_236:TALLOWj#CREATURE:FORGOTTEN_BEAST_237:TALLOWj#CREATURE:FORGOTTEN_BEAST_239:TALLOWj#CREATURE:FORGOTTEN_BEAST_240:TALLOWj#CREATURE:FORGOTTEN_BEAST_242:TALLOWj#CREATURE:FORGOTTEN_BEAST_243:TALLOWj#CREATURE:FORGOTTEN_BEAST_244:TALLOWj#CREATURE:FORGOTTEN_BEAST_245:TALLOWj#CREATURE:FORGOTTEN_BEAST_246:TALLOWj#CREATURE:FORGOTTEN_BEAST_247:TALLOWj#CREATURE:FORGOTTEN_BEAST_248:TALLOWj#CREATURE:FORGOTTEN_BEAST_249:TALLOWj#CREATURE:FORGOTTEN_BEAST_253:TALLOWj#CREATURE:FORGOTTEN_BEAST_255:TALLOWj#CREATURE:FORGOTTEN_BEAST_256:TALLOWj#CREATURE:FORGOTTEN_BEAST_259:TALLOWj#CREATURE:FORGOTTEN_BEAST_260:TALLOWj#CREATURE:FORGOTTEN_BEAST_263:TALLOWj#CREATURE:FORGOTTEN_BEAST_264:TALLOWj#CREATURE:FORGOTTEN_BEAST_265:TALLOWj#CREATURE:FORGOTTEN_BEAST_266:TALLOWj#CREATURE:FORGOTTEN_BEAST_268:TALLOWj#CREATURE:FORGOTTEN_BEAST_269:TALLOWj#CREATURE:FORGOTTEN_BEAST_270:TALLOWj#CREATURE:FORGOTTEN_BEAST_271:TALLOWj#CREATURE:FORGOTTEN_BEAST_272:TALLOWj#CREATURE:FORGOTTEN_BEAST_273:TALLOWj#CREATURE:FORGOTTEN_BEAST_274:TALLOWj#CREATURE:FORGOTTEN_BEAST_275:TALLOWj#CREATURE:FORGOTTEN_BEAST_276:TALLOWj#CREATURE:FORGOTTEN_BEAST_277:TALLOWj#CREATURE:FORGOTTEN_BEAST_279:TALLOWj#CREATURE:FORGOTTEN_BEAST_280:TALLOWj#CREATURE:FORGOTTEN_BEAST_281:TALLOWj#CREATURE:FORGOTTEN_BEAST_282:TALLOWj#CREATURE:FORGOTTEN_BEAST_283:TALLOWj#CREATURE:FORGOTTEN_BEAST_285:TALLOWj#CREATURE:FORGOTTEN_BEAST_286:TALLOWj#CREATURE:FORGOTTEN_BEAST_287:TALLOWj#CREATURE:FORGOTTEN_BEAST_288:TALLOWj#CREATURE:FORGOTTEN_BEAST_289:TALLOWj#CREATURE:FORGOTTEN_BEAST_290:TALLOWj#CREATURE:FORGOTTEN_BEAST_293:TALLOWj#CREATURE:FORGOTTEN_BEAST_295:TALLOWj#CREATURE:FORGOTTEN_BEAST_297:TALLOWj#CREATURE:FORGOTTEN_BEAST_300:TALLOWj#CREATURE:FORGOTTEN_BEAST_302:TALLOWj#CREATURE:FORGOTTEN_BEAST_306:TALLOWj#CREATURE:FORGOTTEN_BEAST_307:TALLOWj#CREATURE:FORGOTTEN_BEAST_310:TALLOWj#CREATURE:FORGOTTEN_BEAST_311:TALLOWj#CREATURE:FORGOTTEN_BEAST_312:TALLOWj#CREATURE:FORGOTTEN_BEAST_314:TALLOWj#CREATURE:FORGOTTEN_BEAST_316:TALLOWj#CREATURE:FORGOTTEN_BEAST_317:TALLOWj#CREATURE:FORGOTTEN_BEAST_318:TALLOWj#CREATURE:FORGOTTEN_BEAST_320:TALLOWj#CREATURE:FORGOTTEN_BEAST_321:TALLOWj#CREATURE:FORGOTTEN_BEAST_322:TALLOWj#CREATURE:FORGOTTEN_BEAST_323:TALLOWj#CREATURE:FORGOTTEN_BEAST_324:TALLOWj#CREATURE:FORGOTTEN_BEAST_325:TALLOWj#CREATURE:FORGOTTEN_BEAST_326:TALLOWj#CREATURE:FORGOTTEN_BEAST_327:TALLOWj#CREATURE:FORGOTTEN_BEAST_328:TALLOWj#CREATURE:FORGOTTEN_BEAST_329:TALLOWj#CREATURE:FORGOTTEN_BEAST_330:TALLOWj#CREATURE:FORGOTTEN_BEAST_332:TALLOWj#CREATURE:FORGOTTEN_BEAST_333:TALLOWj#CREATURE:FORGOTTEN_BEAST_335:TALLOWj#CREATURE:FORGOTTEN_BEAST_336:TALLOWj#CREATURE:FORGOTTEN_BEAST_337:TALLOWj#CREATURE:FORGOTTEN_BEAST_338:TALLOWj#CREATURE:FORGOTTEN_BEAST_339:TALLOWj#CREATURE:FORGOTTEN_BEAST_341:TALLOWj#CREATURE:FORGOTTEN_BEAST_342:TALLOWj#CREATURE:FORGOTTEN_BEAST_343:TALLOWj#CREATURE:FORGOTTEN_BEAST_344:TALLOWj#CREATURE:FORGOTTEN_BEAST_347:TALLOWj#CREATURE:FORGOTTEN_BEAST_348:TALLOWj#CREATURE:FORGOTTEN_BEAST_349:TALLOWj#CREATURE:FORGOTTEN_BEAST_351:TALLOWj#CREATURE:FORGOTTEN_BEAST_352:TALLOWj#CREATURE:FORGOTTEN_BEAST_353:TALLOWj#CREATURE:FORGOTTEN_BEAST_354:TALLOWj#CREATURE:FORGOTTEN_BEAST_355:TALLOWj#CREATURE:FORGOTTEN_BEAST_356:TALLOWj#CREATURE:FORGOTTEN_BEAST_357:TALLOWj#CREATURE:FORGOTTEN_BEAST_358:TALLOWj#CREATURE:FORGOTTEN_BEAST_359:TALLOWj#CREATURE:FORGOTTEN_BEAST_361:TALLOWj#CREATURE:FORGOTTEN_BEAST_363:TALLOWj#CREATURE:FORGOTTEN_BEAST_364:TALLOWj#CREATURE:FORGOTTEN_BEAST_365:TALLOWj#CREATURE:FORGOTTEN_BEAST_366:TALLOWj#CREATURE:FORGOTTEN_BEAST_367:TALLOWj#CREATURE:FORGOTTEN_BEAST_368:TALLOWj#CREATURE:FORGOTTEN_BEAST_370:TALLOWj#CREATURE:FORGOTTEN_BEAST_372:TALLOWj#CREATURE:FORGOTTEN_BEAST_374:TALLOWj#CREATURE:FORGOTTEN_BEAST_375:TALLOWj#CREATURE:FORGOTTEN_BEAST_377:TALLOWj#CREATURE:FORGOTTEN_BEAST_378:TALLOWj#CREATURE:FORGOTTEN_BEAST_379:TALLOWj#CREATURE:FORGOTTEN_BEAST_380:TALLOWj#CREATURE:FORGOTTEN_BEAST_381:TALLOWj#CREATURE:FORGOTTEN_BEAST_382:TALLOWj#CREATURE:FORGOTTEN_BEAST_383:TALLOWj#CREATURE:FORGOTTEN_BEAST_384:TALLOWj#CREATURE:FORGOTTEN_BEAST_385:TALLOWj#CREATURE:FORGOTTEN_BEAST_386:TALLOWj#CREATURE:FORGOTTEN_BEAST_387:TALLOWj#CREATURE:FORGOTTEN_BEAST_388:TALLOWj#CREATURE:FORGOTTEN_BEAST_389:TALLOWj#CREATURE:FORGOTTEN_BEAST_390:TALLOWj#CREATURE:FORGOTTEN_BEAST_391:TALLOWj#CREATURE:FORGOTTEN_BEAST_392:TALLOWj#CREATURE:FORGOTTEN_BEAST_393:TALLOWj#CREATURE:FORGOTTEN_BEAST_396:TALLOWj#CREATURE:FORGOTTEN_BEAST_397:TALLOWj#CREATURE:FORGOTTEN_BEAST_398:TALLOWj#CREATURE:FORGOTTEN_BEAST_400:TALLOWj#CREATURE:FORGOTTEN_BEAST_403:TALLOWj#CREATURE:FORGOTTEN_BEAST_404:TALLOWj#CREATURE:FORGOTTEN_BEAST_405:TALLOWj#CREATURE:FORGOTTEN_BEAST_406:TALLOWj#CREATURE:FORGOTTEN_BEAST_407:TALLOWj#CREATURE:FORGOTTEN_BEAST_408:TALLOWj#CREATURE:FORGOTTEN_BEAST_409:TALLOWj#CREATURE:FORGOTTEN_BEAST_410:TALLOWj#CREATURE:FORGOTTEN_BEAST_411:TALLOWj#CREATURE:FORGOTTEN_BEAST_412:TALLOWj#CREATURE:FORGOTTEN_BEAST_413:TALLOWj#CREATURE:FORGOTTEN_BEAST_414:TALLOWj#CREATURE:FORGOTTEN_BEAST_416:TALLOWj#CREATURE:FORGOTTEN_BEAST_417:TALLOWj#CREATURE:FORGOTTEN_BEAST_418:TALLOWj#CREATURE:FORGOTTEN_BEAST_420:TALLOWj#CREATURE:FORGOTTEN_BEAST_421:TALLOWj#CREATURE:FORGOTTEN_BEAST_422:TALLOWj#CREATURE:FORGOTTEN_BEAST_423:TALLOWj#CREATURE:FORGOTTEN_BEAST_424:TALLOWj#CREATURE:FORGOTTEN_BEAST_427:TALLOWj#CREATURE:FORGOTTEN_BEAST_429:TALLOWj#CREATURE:FORGOTTEN_BEAST_430:TALLOWj#CREATURE:FORGOTTEN_BEAST_432:TALLOWj#CREATURE:FORGOTTEN_BEAST_433:TALLOWj#CREATURE:FORGOTTEN_BEAST_434:TALLOWj#CREATURE:FORGOTTEN_BEAST_435:TALLOWj#CREATURE:FORGOTTEN_BEAST_436:TALLOWj#CREATURE:FORGOTTEN_BEAST_437:TALLOWj#CREATURE:FORGOTTEN_BEAST_438:TALLOWj#CREATURE:FORGOTTEN_BEAST_440:TALLOWj#CREATURE:FORGOTTEN_BEAST_441:TALLOWj#CREATURE:FORGOTTEN_BEAST_442:TALLOWj#CREATURE:FORGOTTEN_BEAST_444:TALLOWj#CREATURE:FORGOTTEN_BEAST_446:TALLOWj#CREATURE:FORGOTTEN_BEAST_447:TALLOWj#CREATURE:FORGOTTEN_BEAST_448:TALLOWj#CREATURE:FORGOTTEN_BEAST_449:TALLOWj#CREATURE:FORGOTTEN_BEAST_450:TALLOWj#CREATURE:FORGOTTEN_BEAST_451:TALLOWj#CREATURE:FORGOTTEN_BEAST_453:TALLOWj#CREATURE:FORGOTTEN_BEAST_454:TALLOWj#CREATURE:FORGOTTEN_BEAST_455:TALLOWj#CREATURE:FORGOTTEN_BEAST_457:TALLOWj#CREATURE:FORGOTTEN_BEAST_459:TALLOWj#CREATURE:FORGOTTEN_BEAST_461:TALLOWj#CREATURE:FORGOTTEN_BEAST_462:TALLOWj#CREATURE:FORGOTTEN_BEAST_463:TALLOWj#CREATURE:FORGOTTEN_BEAST_465:TALLOWj#CREATURE:FORGOTTEN_BEAST_466:TALLOWj#CREATURE:FORGOTTEN_BEAST_468:TALLOWj#CREATURE:FORGOTTEN_BEAST_469:TALLOWj#CREATURE:FORGOTTEN_BEAST_470:TALLOWj#CREATURE:FORGOTTEN_BEAST_471:TALLOWj#CREATURE:FORGOTTEN_BEAST_472:TALLOWj#CREATURE:FORGOTTEN_BEAST_474:TALLOWj#CREATURE:FORGOTTEN_BEAST_475:TALLOWj#CREATURE:FORGOTTEN_BEAST_476:TALLOWj#CREATURE:FORGOTTEN_BEAST_478:TALLOWj#CREATURE:FORGOTTEN_BEAST_479:TALLOWj#CREATURE:FORGOTTEN_BEAST_480:TALLOWj#CREATURE:FORGOTTEN_BEAST_481:TALLOWj#CREATURE:FORGOTTEN_BEAST_483:TALLOWj#CREATURE:FORGOTTEN_BEAST_486:TALLOWj#CREATURE:FORGOTTEN_BEAST_487:TALLOWj#CREATURE:FORGOTTEN_BEAST_489:TALLOWj#CREATURE:FORGOTTEN_BEAST_492:TALLOWj#CREATURE:FORGOTTEN_BEAST_494:TALLOWj#CREATURE:FORGOTTEN_BEAST_495:TALLOWj#CREATURE:FORGOTTEN_BEAST_496:TALLOWj#CREATURE:FORGOTTEN_BEAST_497:TALLOWj#CREATURE:FORGOTTEN_BEAST_498:TALLOWj#CREATURE:FORGOTTEN_BEAST_499:TALLOWj#CREATURE:FORGOTTEN_BEAST_501:TALLOWj#CREATURE:FORGOTTEN_BEAST_503:TALLOWj#CREATURE:FORGOTTEN_BEAST_504:TALLOWj#CREATURE:FORGOTTEN_BEAST_505:TALLOWj#CREATURE:FORGOTTEN_BEAST_507:TALLOWj#CREATURE:FORGOTTEN_BEAST_508:TALLOWj#CREATURE:FORGOTTEN_BEAST_510:TALLOWj#CREATURE:FORGOTTEN_BEAST_512:TALLOWj#CREATURE:FORGOTTEN_BEAST_513:TALLOWj#CREATURE:FORGOTTEN_BEAST_514:TALLOWj#CREATURE:FORGOTTEN_BEAST_515:TALLOWj#CREATURE:FORGOTTEN_BEAST_516:TALLOWj#CREATURE:FORGOTTEN_BEAST_517:TALLOWj#CREATURE:FORGOTTEN_BEAST_518:TALLOWj#CREATURE:FORGOTTEN_BEAST_519:TALLOWj#CREATURE:FORGOTTEN_BEAST_520:TALLOWj#CREATURE:FORGOTTEN_BEAST_521:TALLOWj#CREATURE:FORGOTTEN_BEAST_522:TALLOWj#CREATURE:FORGOTTEN_BEAST_523:TALLOWj#CREATURE:FORGOTTEN_BEAST_525:TALLOWj#CREATURE:FORGOTTEN_BEAST_526:TALLOWj#CREATURE:FORGOTTEN_BEAST_527:TALLOWj#CREATURE:FORGOTTEN_BEAST_528:TALLOWj#CREATURE:FORGOTTEN_BEAST_529:TALLOWj#CREATURE:FORGOTTEN_BEAST_530:TALLOWj#CREATURE:FORGOTTEN_BEAST_531:TALLOWj#CREATURE:FORGOTTEN_BEAST_532:TALLOWj#CREATURE:FORGOTTEN_BEAST_533:TALLOWj#CREATURE:FORGOTTEN_BEAST_534:TALLOWj#CREATURE:FORGOTTEN_BEAST_535:TALLOWj#CREATURE:FORGOTTEN_BEAST_536:TALLOWj#CREATURE:FORGOTTEN_BEAST_539:TALLOWj#CREATURE:FORGOTTEN_BEAST_540:TALLOWj#CREATURE:FORGOTTEN_BEAST_541:TALLOWj#CREATURE:FORGOTTEN_BEAST_543:TALLOWj#CREATURE:FORGOTTEN_BEAST_545:TALLOWj#CREATURE:FORGOTTEN_BEAST_546:TALLOWj#CREATURE:FORGOTTEN_BEAST_547:TALLOWj#CREATURE:FORGOTTEN_BEAST_548:TALLOWj#CREATURE:FORGOTTEN_BEAST_549:TALLOWj#CREATURE:FORGOTTEN_BEAST_550:TALLOWj#CREATURE:FORGOTTEN_BEAST_551:TALLOWj#CREATURE:FORGOTTEN_BEAST_554:TALLOWj#CREATURE:FORGOTTEN_BEAST_555:TALLOWj#CREATURE:FORGOTTEN_BEAST_556:TALLOWj#CREATURE:FORGOTTEN_BEAST_557:TALLOWj#CREATURE:FORGOTTEN_BEAST_561:TALLOWj#CREATURE:FORGOTTEN_BEAST_562:TALLOWj#CREATURE:FORGOTTEN_BEAST_564:TALLOWj#CREATURE:FORGOTTEN_BEAST_569:TALLOWj#CREATURE:FORGOTTEN_BEAST_570:TALLOWj#CREATURE:FORGOTTEN_BEAST_571:TALLOWj#CREATURE:FORGOTTEN_BEAST_572:TALLOWj#CREATURE:FORGOTTEN_BEAST_574:TALLOWj#CREATURE:FORGOTTEN_BEAST_576:TALLOWj#CREATURE:FORGOTTEN_BEAST_578:TALLOWj#CREATURE:FORGOTTEN_BEAST_579:TALLOWj#CREATURE:FORGOTTEN_BEAST_580:TALLOWj#CREATURE:FORGOTTEN_BEAST_581:TALLOWj#CREATURE:FORGOTTEN_BEAST_583:TALLOWj#CREATURE:FORGOTTEN_BEAST_584:TALLOWj#CREATURE:FORGOTTEN_BEAST_586:TALLOWj#CREATURE:FORGOTTEN_BEAST_588:TALLOWj#CREATURE:FORGOTTEN_BEAST_589:TALLOWj#CREATURE:FORGOTTEN_BEAST_590:TALLOWj#CREATURE:FORGOTTEN_BEAST_592:TALLOWj#CREATURE:FORGOTTEN_BEAST_593:TALLOWj#CREATURE:FORGOTTEN_BEAST_594:TALLOWj#CREATURE:FORGOTTEN_BEAST_595:TALLOWj#CREATURE:FORGOTTEN_BEAST_596:TALLOWj#CREATURE:FORGOTTEN_BEAST_597:TALLOWj#CREATURE:FORGOTTEN_BEAST_599:TALLOWj#CREATURE:FORGOTTEN_BEAST_600:TALLOWj#CREATURE:FORGOTTEN_BEAST_601:TALLOWj#CREATURE:FORGOTTEN_BEAST_603:TALLOWj#CREATURE:FORGOTTEN_BEAST_605:TALLOWj#CREATURE:FORGOTTEN_BEAST_607:TALLOWj#CREATURE:FORGOTTEN_BEAST_608:TALLOWj#CREATURE:FORGOTTEN_BEAST_609:TALLOWj#CREATURE:FORGOTTEN_BEAST_610:TALLOWj#CREATURE:FORGOTTEN_BEAST_611:TALLOWj#CREATURE:FORGOTTEN_BEAST_612:TALLOWj#CREATURE:FORGOTTEN_BEAST_613:TALLOWj#CREATURE:FORGOTTEN_BEAST_614:TALLOWj#CREATURE:FORGOTTEN_BEAST_616:TALLOWj#CREATURE:FORGOTTEN_BEAST_619:TALLOWj#CREATURE:FORGOTTEN_BEAST_620:TALLOWj#CREATURE:FORGOTTEN_BEAST_621:TALLOWj#CREATURE:FORGOTTEN_BEAST_623:TALLOWj#CREATURE:FORGOTTEN_BEAST_624:TALLOWj#CREATURE:FORGOTTEN_BEAST_625:TALLOWj#CREATURE:FORGOTTEN_BEAST_626:TALLOWj#CREATURE:FORGOTTEN_BEAST_627:TALLOWj#CREATURE:FORGOTTEN_BEAST_628:TALLOWj#CREATURE:FORGOTTEN_BEAST_629:TALLOWj#CREATURE:FORGOTTEN_BEAST_630:TALLOWj#CREATURE:FORGOTTEN_BEAST_631:TALLOWj#CREATURE:FORGOTTEN_BEAST_632:TALLOWj#CREATURE:FORGOTTEN_BEAST_633:TALLOWj#CREATURE:FORGOTTEN_BEAST_634:TALLOWj#CREATURE:FORGOTTEN_BEAST_635:TALLOWj#CREATURE:FORGOTTEN_BEAST_636:TALLOWj#CREATURE:FORGOTTEN_BEAST_637:TALLOWj#CREATURE:FORGOTTEN_BEAST_639:TALLOWj#CREATURE:FORGOTTEN_BEAST_640:TALLOWj#CREATURE:FORGOTTEN_BEAST_643:TALLOWj#CREATURE:FORGOTTEN_BEAST_644:TALLOWj#CREATURE:FORGOTTEN_BEAST_645:TALLOWj#CREATURE:FORGOTTEN_BEAST_646:TALLOWj#CREATURE:FORGOTTEN_BEAST_647:TALLOWj#CREATURE:FORGOTTEN_BEAST_649:TALLOWj#CREATURE:FORGOTTEN_BEAST_650:TALLOWj#CREATURE:FORGOTTEN_BEAST_651:TALLOWj#CREATURE:FORGOTTEN_BEAST_652:TALLOWj#CREATURE:FORGOTTEN_BEAST_656:TALLOWj#CREATURE:FORGOTTEN_BEAST_658:TALLOWj#CREATURE:FORGOTTEN_BEAST_659:TALLOWj#CREATURE:FORGOTTEN_BEAST_661:TALLOWj#CREATURE:FORGOTTEN_BEAST_663:TALLOWj#CREATURE:FORGOTTEN_BEAST_664:TALLOWj#CREATURE:FORGOTTEN_BEAST_666:TALLOWj#CREATURE:FORGOTTEN_BEAST_667:TALLOWj#CREATURE:FORGOTTEN_BEAST_669:TALLOWj#CREATURE:FORGOTTEN_BEAST_670:TALLOWj#CREATURE:FORGOTTEN_BEAST_671:TALLOWj#CREATURE:FORGOTTEN_BEAST_674:TALLOWj#CREATURE:FORGOTTEN_BEAST_675:TALLOWj#CREATURE:FORGOTTEN_BEAST_678:TALLOWj#CREATURE:FORGOTTEN_BEAST_680:TALLOWj#CREATURE:FORGOTTEN_BEAST_681:TALLOWj#CREATURE:FORGOTTEN_BEAST_682:TALLOWj#CREATURE:FORGOTTEN_BEAST_684:TALLOWj#CREATURE:FORGOTTEN_BEAST_685:TALLOWj#CREATURE:FORGOTTEN_BEAST_686:TALLOWj#CREATURE:FORGOTTEN_BEAST_687:TALLOWj#CREATURE:FORGOTTEN_BEAST_688:TALLOWj#CREATURE:FORGOTTEN_BEAST_689:TALLOWj#CREATURE:FORGOTTEN_BEAST_691:TALLOWj#CREATURE:FORGOTTEN_BEAST_692:TALLOWj#CREATURE:FORGOTTEN_BEAST_695:TALLOWj#CREATURE:FORGOTTEN_BEAST_696:TALLOWj#CREATURE:FORGOTTEN_BEAST_697:TALLOWj#CREATURE:FORGOTTEN_BEAST_698:TALLOWj#CREATURE:FORGOTTEN_BEAST_699:TALLOWj#CREATURE:FORGOTTEN_BEAST_700:TALLOWj#CREATURE:FORGOTTEN_BEAST_701:TALLOWj#CREATURE:FORGOTTEN_BEAST_702:TALLOWj#CREATURE:FORGOTTEN_BEAST_704:TALLOWj#CREATURE:FORGOTTEN_BEAST_706:TALLOWj#CREATURE:FORGOTTEN_BEAST_708:TALLOWj#CREATURE:FORGOTTEN_BEAST_709:TALLOWj#CREATURE:FORGOTTEN_BEAST_711:TALLOWj#CREATURE:FORGOTTEN_BEAST_712:TALLOWj#CREATURE:FORGOTTEN_BEAST_713:TALLOWj#CREATURE:FORGOTTEN_BEAST_714:TALLOWj#CREATURE:FORGOTTEN_BEAST_715:TALLOWj#CREATURE:FORGOTTEN_BEAST_717:TALLOWj#CREATURE:FORGOTTEN_BEAST_718:TALLOWj#CREATURE:FORGOTTEN_BEAST_719:TALLOWj#CREATURE:FORGOTTEN_BEAST_720:TALLOWj#CREATURE:FORGOTTEN_BEAST_721:TALLOWj#CREATURE:FORGOTTEN_BEAST_722:TALLOWj#CREATURE:FORGOTTEN_BEAST_723:TALLOWj#CREATURE:FORGOTTEN_BEAST_724:TALLOWj#CREATURE:FORGOTTEN_BEAST_726:TALLOWj#CREATURE:FORGOTTEN_BEAST_730:TALLOWj#CREATURE:FORGOTTEN_BEAST_731:TALLOWj#CREATURE:FORGOTTEN_BEAST_734:TALLOWj#CREATURE:FORGOTTEN_BEAST_735:TALLOWj#CREATURE:FORGOTTEN_BEAST_737:TALLOWj#CREATURE:FORGOTTEN_BEAST_738:TALLOWj#CREATURE:FORGOTTEN_BEAST_739:TALLOWj#CREATURE:FORGOTTEN_BEAST_740:TALLOWj#CREATURE:FORGOTTEN_BEAST_741:TALLOWj#CREATURE:FORGOTTEN_BEAST_742:TALLOWj#CREATURE:FORGOTTEN_BEAST_743:TALLOWj#CREATURE:FORGOTTEN_BEAST_744:TALLOWj#CREATURE:FORGOTTEN_BEAST_746:TALLOWj#CREATURE:FORGOTTEN_BEAST_747:TALLOWj#CREATURE:FORGOTTEN_BEAST_749:TALLOWj#CREATURE:FORGOTTEN_BEAST_750:TALLOWj#CREATURE:FORGOTTEN_BEAST_751:TALLOWj#CREATURE:FORGOTTEN_BEAST_752:TALLOWj#CREATURE:FORGOTTEN_BEAST_753:TALLOWj#CREATURE:FORGOTTEN_BEAST_754:TALLOWj#CREATURE:FORGOTTEN_BEAST_755:TALLOWj#CREATURE:FORGOTTEN_BEAST_756:TALLOWj#CREATURE:FORGOTTEN_BEAST_757:TALLOWj#CREATURE:FORGOTTEN_BEAST_758:TALLOWj#CREATURE:FORGOTTEN_BEAST_759:TALLOWj#CREATURE:FORGOTTEN_BEAST_760:TALLOWj#CREATURE:FORGOTTEN_BEAST_761:TALLOWj#CREATURE:FORGOTTEN_BEAST_762:TALLOWj#CREATURE:FORGOTTEN_BEAST_764:TALLOWj#CREATURE:FORGOTTEN_BEAST_767:TALLOWj#CREATURE:FORGOTTEN_BEAST_768:TALLOWj#CREATURE:FORGOTTEN_BEAST_769:TALLOWj#CREATURE:FORGOTTEN_BEAST_770:TALLOWj#CREATURE:FORGOTTEN_BEAST_773:TALLOWj#CREATURE:FORGOTTEN_BEAST_774:TALLOWj#CREATURE:FORGOTTEN_BEAST_776:TALLOWj#CREATURE:FORGOTTEN_BEAST_777:TALLOWj#CREATURE:FORGOTTEN_BEAST_778:TALLOWj#CREATURE:FORGOTTEN_BEAST_779:TALLOWj#CREATURE:FORGOTTEN_BEAST_780:TALLOWj#CREATURE:FORGOTTEN_BEAST_781:TALLOWj#CREATURE:FORGOTTEN_BEAST_782:TALLOWj#CREATURE:FORGOTTEN_BEAST_783:TALLOWj#CREATURE:FORGOTTEN_BEAST_784:TALLOWj#CREATURE:FORGOTTEN_BEAST_785:TALLOWj#CREATURE:FORGOTTEN_BEAST_786:TALLOWj#CREATURE:FORGOTTEN_BEAST_787:TALLOWj#CREATURE:FORGOTTEN_BEAST_789:TALLOWj#CREATURE:FORGOTTEN_BEAST_792:TALLOWj#CREATURE:FORGOTTEN_BEAST_793:TALLOWj#CREATURE:FORGOTTEN_BEAST_794:TALLOWj#CREATURE:FORGOTTEN_BEAST_795:TALLOWj#CREATURE:FORGOTTEN_BEAST_796:TALLOWj#CREATURE:FORGOTTEN_BEAST_799:TALLOWj#CREATURE:FORGOTTEN_BEAST_800:TALLOWj#CREATURE:FORGOTTEN_BEAST_801:TALLOWj#CREATURE:FORGOTTEN_BEAST_802:TALLOWj#CREATURE:FORGOTTEN_BEAST_803:TALLOWj#CREATURE:FORGOTTEN_BEAST_804:TALLOWj#CREATURE:FORGOTTEN_BEAST_806:TALLOWj#CREATURE:FORGOTTEN_BEAST_807:TALLOWj#CREATURE:FORGOTTEN_BEAST_809:TALLOWj#CREATURE:FORGOTTEN_BEAST_810:TALLOWj#CREATURE:FORGOTTEN_BEAST_811:TALLOWj#CREATURE:FORGOTTEN_BEAST_812:TALLOWj#CREATURE:FORGOTTEN_BEAST_815:TALLOWj#CREATURE:FORGOTTEN_BEAST_817:TALLOWj#CREATURE:FORGOTTEN_BEAST_818:TALLOWj#CREATURE:FORGOTTEN_BEAST_819:TALLOWj#CREATURE:FORGOTTEN_BEAST_820:TALLOWj#CREATURE:FORGOTTEN_BEAST_821:TALLOWj#CREATURE:FORGOTTEN_BEAST_822:TALLOWj#CREATURE:FORGOTTEN_BEAST_824:TALLOWj#CREATURE:FORGOTTEN_BEAST_825:TALLOWj#CREATURE:FORGOTTEN_BEAST_826:TALLOWj#CREATURE:FORGOTTEN_BEAST_827:TALLOWj#CREATURE:FORGOTTEN_BEAST_828:TALLOWj#CREATURE:FORGOTTEN_BEAST_831:TALLOWj#CREATURE:FORGOTTEN_BEAST_833:TALLOWj#CREATURE:FORGOTTEN_BEAST_835:TALLOWj#CREATURE:FORGOTTEN_BEAST_837:TALLOWj#CREATURE:FORGOTTEN_BEAST_838:TALLOWj#CREATURE:FORGOTTEN_BEAST_842:TALLOWj#CREATURE:FORGOTTEN_BEAST_843:TALLOWj#CREATURE:FORGOTTEN_BEAST_844:TALLOWj#CREATURE:FORGOTTEN_BEAST_845:TALLOWj#CREATURE:FORGOTTEN_BEAST_846:TALLOWj#CREATURE:FORGOTTEN_BEAST_847:TALLOWj#CREATURE:FORGOTTEN_BEAST_848:TALLOWj#CREATURE:FORGOTTEN_BEAST_849:TALLOWj#CREATURE:FORGOTTEN_BEAST_851:TALLOWj#CREATURE:FORGOTTEN_BEAST_853:TALLOWj#CREATURE:FORGOTTEN_BEAST_854:TALLOWj#CREATURE:FORGOTTEN_BEAST_855:TALLOWj#CREATURE:FORGOTTEN_BEAST_857:TALLOWj#CREATURE:FORGOTTEN_BEAST_858:TALLOWj#CREATURE:FORGOTTEN_BEAST_859:TALLOWj#CREATURE:FORGOTTEN_BEAST_860:TALLOWj#CREATURE:FORGOTTEN_BEAST_861:TALLOWj#CREATURE:FORGOTTEN_BEAST_862:TALLOWj#CREATURE:FORGOTTEN_BEAST_865:TALLOWj#CREATURE:FORGOTTEN_BEAST_866:TALLOWj#CREATURE:FORGOTTEN_BEAST_867:TALLOWjCREATURE:TITAN_1:TALLOWjCREATURE:TITAN_3:TALLOWjCREATURE:TITAN_4:TALLOWjCREATURE:TITAN_6:TALLOWjCREATURE:TITAN_7:TALLOWjCREATURE:TITAN_8:TALLOWjCREATURE:TITAN_9:TALLOWjCREATURE:TITAN_10:TALLOWjCREATURE:TITAN_11:TALLOWjCREATURE:TITAN_12:TALLOWjCREATURE:TITAN_13:TALLOWjCREATURE:TITAN_14:TALLOWjCREATURE:TITAN_15:TALLOWjCREATURE:TITAN_18:TALLOWjCREATURE:TITAN_19:TALLOWjCREATURE:TITAN_21:TALLOWjCREATURE:TITAN_23:TALLOWjCREATURE:TITAN_24:TALLOWjCREATURE:TITAN_25:TALLOWjCREATURE:TITAN_26:TALLOWjCREATURE:TITAN_27:TALLOWjCREATURE:TITAN_28:TALLOWjCREATURE:TITAN_29:TALLOWjCREATURE:TITAN_30:TALLOWjCREATURE:TITAN_32:TALLOWjCREATURE:TITAN_33:TALLOWjCREATURE:DEMON_7:TALLOWjCREATURE:DEMON_8:TALLOWjCREATURE:DEMON_9:TALLOWjCREATURE:DEMON_10:TALLOWjCREATURE:DEMON_11:TALLOWjCREATURE:DEMON_12:TALLOWjCREATURE:DEMON_13:TALLOWjCREATURE:DEMON_14:TALLOWjCREATURE:DEMON_15:TALLOWjCREATURE:DEMON_16:TALLOWjCREATURE:DEMON_17:TALLOWjCREATURE:DEMON_18:TALLOWjCREATURE:DEMON_19:TALLOWjCREATURE:DEMON_20:TALLOWjCREATURE:DEMON_21:TALLOWjCREATURE:DEMON_22:TALLOWjCREATURE:DEMON_23:TALLOWjCREATURE:DEMON_24:TALLOWjCREATURE:DEMON_25:TALLOWjCREATURE:DEMON_26:TALLOWjCREATURE:DEMON_27:TALLOWjCREATURE:DEMON_28:TALLOWjCREATURE:DEMON_29:TALLOWjCREATURE:DEMON_31:TALLOWjCREATURE:DEMON_32:TALLOWjCREATURE:DEMON_38:TALLOWjCREATURE:DEMON_39:TALLOWjCREATURE:DEMON_40:TALLOWjCREATURE:DEMON_41:TALLOWjCREATURE:DEMON_42:TALLOWjCREATURE:DEMON_43:TALLOWjCREATURE:DEMON_44:TALLOWjCREATURE:DEMON_45:TALLOWjCREATURE:DEMON_47:TALLOWjCREATURE:DEMON_48:TALLOWjCREATURE:DEMON_49:TALLOWjCREATURE:DEMON_52:TALLOWj CREATURE:NIGHT_CREATURE_1:TALLOWj CREATURE:NIGHT_CREATURE_2:TALLOWj CREATURE:NIGHT_CREATURE_3:TALLOWj CREATURE:NIGHT_CREATURE_4:TALLOWj CREATURE:NIGHT_CREATURE_5:TALLOWj CREATURE:NIGHT_CREATURE_6:TALLOWj CREATURE:NIGHT_CREATURE_7:TALLOWj CREATURE:NIGHT_CREATURE_8:TALLOWj CREATURE:NIGHT_CREATURE_9:TALLOWj!CREATURE:NIGHT_CREATURE_10:TALLOWj!CREATURE:NIGHT_CREATURE_11:TALLOWj!CREATURE:NIGHT_CREATURE_12:TALLOWj!CREATURE:NIGHT_CREATURE_13:TALLOWj!CREATURE:NIGHT_CREATURE_14:TALLOWj!CREATURE:NIGHT_CREATURE_15:TALLOWj!CREATURE:NIGHT_CREATURE_16:TALLOWj!CREATURE:NIGHT_CREATURE_17:TALLOWj!CREATURE:NIGHT_CREATURE_18:TALLOWj!CREATURE:NIGHT_CREATURE_19:TALLOWj!CREATURE:NIGHT_CREATURE_20:TALLOWj!CREATURE:NIGHT_CREATURE_21:TALLOWj!CREATURE:NIGHT_CREATURE_22:TALLOWj!CREATURE:NIGHT_CREATURE_23:TALLOWj!CREATURE:NIGHT_CREATURE_24:TALLOWj!CREATURE:NIGHT_CREATURE_25:TALLOWj!CREATURE:NIGHT_CREATURE_26:TALLOWj!CREATURE:NIGHT_CREATURE_27:TALLOWj!CREATURE:NIGHT_CREATURE_28:TALLOWj!CREATURE:NIGHT_CREATURE_29:TALLOWj!CREATURE:NIGHT_CREATURE_30:TALLOWj!CREATURE:NIGHT_CREATURE_31:TALLOWj!CREATURE:NIGHT_CREATURE_32:TALLOWj!CREATURE:NIGHT_CREATURE_33:TALLOWj!CREATURE:NIGHT_CREATURE_34:TALLOWj!CREATURE:NIGHT_CREATURE_35:TALLOWj!CREATURE:NIGHT_CREATURE_36:TALLOWj!CREATURE:NIGHT_CREATURE_37:TALLOWj!CREATURE:NIGHT_CREATURE_38:TALLOWj!CREATURE:NIGHT_CREATURE_39:TALLOWj!CREATURE:NIGHT_CREATURE_40:TALLOWj!CREATURE:NIGHT_CREATURE_41:TALLOWj!CREATURE:NIGHT_CREATURE_42:TALLOWj!CREATURE:NIGHT_CREATURE_43:TALLOWj!CREATURE:NIGHT_CREATURE_44:TALLOWj!CREATURE:NIGHT_CREATURE_45:TALLOWj!CREATURE:NIGHT_CREATURE_46:TALLOWj!CREATURE:NIGHT_CREATURE_47:TALLOWj!CREATURE:NIGHT_CREATURE_48:TALLOWj!CREATURE:NIGHT_CREATURE_49:TALLOWj!CREATURE:NIGHT_CREATURE_50:TALLOWj!CREATURE:NIGHT_CREATURE_51:TALLOWj!CREATURE:NIGHT_CREATURE_52:TALLOWj!CREATURE:NIGHT_CREATURE_53:TALLOWj!CREATURE:NIGHT_CREATURE_54:TALLOWj!CREATURE:NIGHT_CREATURE_55:TALLOWj!CREATURE:NIGHT_CREATURE_56:TALLOWj!CREATURE:NIGHT_CREATURE_57:TALLOWj!CREATURE:NIGHT_CREATURE_58:TALLOWj!CREATURE:NIGHT_CREATURE_59:TALLOWj!CREATURE:NIGHT_CREATURE_60:TALLOWj!CREATURE:NIGHT_CREATURE_61:TALLOWj!CREATURE:NIGHT_CREATURE_62:TALLOWj!CREATURE:NIGHT_CREATURE_63:TALLOWj!CREATURE:NIGHT_CREATURE_64:TALLOWj!CREATURE:NIGHT_CREATURE_65:TALLOWj!CREATURE:NIGHT_CREATURE_66:TALLOWj!CREATURE:NIGHT_CREATURE_67:TALLOWj!CREATURE:NIGHT_CREATURE_68:TALLOWj!CREATURE:NIGHT_CREATURE_69:TALLOWj!CREATURE:NIGHT_CREATURE_70:TALLOWj!CREATURE:NIGHT_CREATURE_71:TALLOWj!CREATURE:NIGHT_CREATURE_72:TALLOWj!CREATURE:NIGHT_CREATURE_73:TALLOWj!CREATURE:NIGHT_CREATURE_74:TALLOWj!CREATURE:NIGHT_CREATURE_75:TALLOWj!CREATURE:NIGHT_CREATURE_76:TALLOWj!CREATURE:NIGHT_CREATURE_77:TALLOWj!CREATURE:NIGHT_CREATURE_78:TALLOWj!CREATURE:NIGHT_CREATURE_79:TALLOWj!CREATURE:NIGHT_CREATURE_80:TALLOWj!CREATURE:NIGHT_CREATURE_81:TALLOWj!CREATURE:NIGHT_CREATURE_82:TALLOWj!CREATURE:NIGHT_CREATURE_83:TALLOWj!CREATURE:NIGHT_CREATURE_84:TALLOWj!CREATURE:NIGHT_CREATURE_85:TALLOWj!CREATURE:NIGHT_CREATURE_86:TALLOWj!CREATURE:NIGHT_CREATURE_87:TALLOWj!CREATURE:NIGHT_CREATURE_88:TALLOWj!CREATURE:NIGHT_CREATURE_89:TALLOWj!CREATURE:NIGHT_CREATURE_90:TALLOWj!CREATURE:NIGHT_CREATURE_91:TALLOWj!CREATURE:NIGHT_CREATURE_92:TALLOWj!CREATURE:NIGHT_CREATURE_93:TALLOWj!CREATURE:NIGHT_CREATURE_94:TALLOWj!CREATURE:NIGHT_CREATURE_95:TALLOWj!CREATURE:NIGHT_CREATURE_96:TALLOWj!CREATURE:NIGHT_CREATURE_97:TALLOWj!CREATURE:NIGHT_CREATURE_98:TALLOWj!CREATURE:NIGHT_CREATURE_99:TALLOWj"CREATURE:NIGHT_CREATURE_100:TALLOWj"CREATURE:NIGHT_CREATURE_101:TALLOWj"CREATURE:NIGHT_CREATURE_102:TALLOWj"CREATURE:NIGHT_CREATURE_103:TALLOWj"CREATURE:NIGHT_CREATURE_104:TALLOWjCREATURE:HF1248 DIVINE_1:TALLOWjCREATURE:HF1248 DIVINE_2:TALLOWjCREATURE:HF1248 DIVINE_3:TALLOWjCREATURE:HF1108 DIVINE_2:TALLOWjCREATURE:HF1249 DIVINE_1:TALLOWjCREATURE:HF1249 DIVINE_3:TALLOWjCREATURE:HF1345 DIVINE_3:TALLOW˜˜ ¨°¸À \ No newline at end of file From febc5299e9b16ff198045096655494b94b4d0398 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 16:51:11 -0700 Subject: [PATCH 0871/2222] don't mangle dfstock files --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4068bc44..94bfad310 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,4 +41,4 @@ repos: entry: python3 ci/authors-rst.py files: docs/about/Authors\.rst pass_filenames: false -exclude: '^(depends/|data/.*\.json$|.*\.diff$)' +exclude: '^(depends/|data/.*\.json$|.*\.diff$|.*\.dfstock$)' From 3661cfdcf0aba43e8487337ab21fe49716f7c26f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 16:51:21 -0700 Subject: [PATCH 0872/2222] update furniture stockpile defs --- data/stockpiles/artifactfurniture.dfstock | Bin 33 -> 12 bytes data/stockpiles/bags.dfstock | Bin 34 -> 206 bytes data/stockpiles/buckets.dfstock | Bin 31 -> 2915 bytes data/stockpiles/masterworkfurniture.dfstock | Bin 34 -> 13 bytes data/stockpiles/pots.dfstock | Bin 33 -> 2921 bytes data/stockpiles/sand.dfstock | Bin 29 -> 211 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/artifactfurniture.dfstock b/data/stockpiles/artifactfurniture.dfstock index 13c32a66ac77a93eba6d0d5ebb57cd5925d932ea..adf198477a5b38c3e2e61096ac3879356b89e8bc 100644 GIT binary patch literal 12 Tcmb2*QsQtdD#=VsOfCTc5iSFL literal 33 pcmb2*QsQtdD#=VsOfH$g$T)+MaRDR43Py$vj0`&%84fTq0056)2p#|c diff --git a/data/stockpiles/bags.dfstock b/data/stockpiles/bags.dfstock index 0e1cc2902e06a52a371eb89831d6d1a822c2642c..6f95e76386fb2c6036a394dc5955984c94782f1c 100644 GIT binary patch literal 206 zcmY++!3u&f7yw`*A!~HmvEz>aTP(4RmWB?ZQw;ru!BF{|kk8mlb!yr1yL`L%Vzb=K zBWG77bNxtEp)?5WU^jFwA=7$n#P(o2bkmn{H4aF& qC&AKJjf1y+XPj7H%+n*aqZ6?tZ(CD)2qVl#_#YvRFdyMJLKr`Ip+j5% literal 34 qcmb2*=Hd+Ybai)i^!4?hz{og*k#PYd!wN=*4U7ys7#R*QG5`R7mZQZ6}z~D!}6-80BZ&wr}ai&O(q>?%>LeM%u03$8D6j=Z5 ze##DIIl7X)M;=|?&vP!7{`>6fFS16gWJu1gt<95pt;vK|XdBApEVD)?vtYYJX`M-C zwIrvLnJ=Z*$(LGED5Xozywa;=MpcHxOjt)cnHNq<(-xY%Pv!!qu0LD|xAuW_e>_{0 zGwCmXfwsfVRfElauLh$De%wP5ZJm&s zFbh@#`7&}3Y+xV~ItD$Nyz|d3(b=#E87-=&H)I6!C1I#6hQK(F+&POzd(wehYE{X- zC7X$`rqH%RTY}^)$SqI>jKvnvWf%jbSG?negW7P34Ix+7MTm*7pbRA7(OkK>cg5U2_<33#=(6yZ$g;8{EC>6~RTghkFqof9N+D<3>eLTsi4j0RkD~ zd%@R{RMRX3HW{vVFw?iN*s_@laY@+5^HFP6bOkc&z7!}CS{N@lp^Tx79H*$T8Auxw zW)uxFI4Wg6O)$P5hb_aVQLJl^NNoAzT@ZYY{wV|ZUpIMc9O--fZBxx{$N3yPlYE9sn4=U@fvgn{7<`o;q zr03WADng8ycqaEkV|VfFDQpoxGE^F2<7L)a*90|QJ{1*4PKho`w_hEs5%tj64HULdEOZ{Z)50V9zDHHOvV|04c_wWDDW!21T1`{N*{+6z9N`*MKV(_U5$oM z99TRTcHbmsi9Bnilds3p zAycmR5@8HLzq-qe)sC)P_FS??Vjp4-e+;i7mUQLu<0i?p_yMcE%p2@NrmXJKX4&Rl z=iBGJf*yNu0(*G^dvyYP&D(T{@%$9{;uQGu6!_{C_=twR&%4dX$NLWtx7)>6)$QMpo88@~?c$tvkN0=C b?;e*=9+%wXa_n*WZX=48SW~1*QtNmxf{}QE1s0pYb%5p% z_jmMBmRBR&J@RQdpYIKo{`>5!ujkh0tq;~wA(M+GPRUiR$%IyD8_MJ&vqmP9V7o(U zok=FOB&U;!FQwMW7g|y%rAscn(u-t5RffYvz@tvC3MZv$3r*f9R|2N4KU@p9@_}@J zyjYPl={L$)SCCQSjdIqc-wkItzmbk3!q)W|!M%ml+{>*pg5SyD^;V|e4t_I-)HvXS zgpXvfs8!K+*>Fr#P^PK4ozzkU2kd&d3#0U>;aJOMkglC8Bh#Nx;Na9;%k;;S51LlI zD0DIBfk`)2mke{cZ$*3BwM=P^&i*eE3S`!ODNrJ`FkWy%8ABO4PElbq zkTxdFC>msNRLcA~!T5R{whWs_v8p{HvE`32BLG!w6E9-UURJC`(=DSew`RX{u3hGyzI+lu)q`?Rt$+{Y`@GS2X8@Rnakfj99bVBsTG`Z%oc1;Ml{l9_tx zYBc=FfyHxS_f2B9cuBBn;X`t|0VSS~0R3V)EOOjzpcmdI212aZc@bm-^Y{-q9=4BS z^7U9cWXjcEB8&m(S9h7Q+R?IQ&n0Ul_95o*$M70rNmm{}ZjwxkAF$fXyumJH%IY3% zmTlg3zJ1Os=&=`Pu$O1BS7)%-yiJD~&(DD`&Vet_fv?Viucw!G`~LoMz5P7BT5cX5 z#CCnR+q|D%&+i{M51$XmOaJNPW_$l(JH3+syxV---G6voKTN-@)_?Cd+q+K>(@Wa! e?(f#`cC#msGwyLV_Bea;IO85?gU8vE$NvDd5*)Yy literal 33 pcmb2*;^Of2^mBD~3<{aR$T)+MaRDR43Py$vj0`&%84fTq004IT2P*&o diff --git a/data/stockpiles/sand.dfstock b/data/stockpiles/sand.dfstock index a934bcb440d029c04c8b28954f7335a39bc375c4..87b2fe9ce693bc680fd9f1d8fbba9c332542d627 100644 GIT binary patch literal 211 zcmY++!3u&f7yw`bp_O#mvEz>aTP#}{tr;qWCmA|nFjW2~ Date: Mon, 20 Mar 2023 16:51:46 -0700 Subject: [PATCH 0873/2222] spacing --- plugins/stockpiles/StockpileSerializer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 0cdfad13f..32546053d 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1705,6 +1705,7 @@ bool StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furni furniture->add_type(f_type); DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); } + all = serialize_list_material( furniture_mat_is_allowed, [&](const std::string& token) { furniture->add_mats(token); }, From 1a8f6fafffe41e068593cbab116fb099613cc1b0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:00:44 -0700 Subject: [PATCH 0874/2222] update refuse stockpile defs --- data/stockpiles/rawhides.dfstock | Bin 730 -> 6 bytes data/stockpiles/tannedhides.dfstock | Bin 741 -> 19 bytes data/stockpiles/usablehair.dfstock | Bin 759 -> 46 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/rawhides.dfstock b/data/stockpiles/rawhides.dfstock index b58c77edecc3fd043a6cd9e855cbe5308d164ccd..eedc71760dd3298bf49d84d89c85247544c5ad73 100644 GIT binary patch literal 6 NcmdO531Ez10009C0N4Nk literal 730 zcmXw1F_POb47A3QY{R+C6 z{o7HytB%x;s)=rsLojm@Iu9~ZXBM{UxdrMb$9biG*1<_3vLTnWc?pHiMZZ$N+c4@* zt>IayA2=m(*f+D*bPm?DD~Tg@h0%g~qtj#q^|^4_5bwfK0mR9JO_vvyjMv4A1I0z0 z2fEXD+@1&hz``>Yw4g@``h=0_*(R}B>0%hqfWJ<5-V%<~v)?NJOEwaVBx>1vg~+6P zpcXkZT?FLo;X0s=)b#T#9?ZhKb#zJM8S<(1!iCz&=A|O!K00L_5$etO&;Gl0uNeOZ!l9lBG5UC~kzBg!&(KuxTQ`nS!ZKfM?(95+`Y zCnUTK?3~2_p5ZN9ndKqu%l{Wh(tW8Ax9Y+(@mh~YJxm#)c%^&PeUx-~n++UVM6y6( z{Ump10X{yTTBDHI!j1t#Y?LF5DHitK7cOQw419u#bE@B|d)p)hx_RqdE?RBK(OFES zH)%sUeFl+7{V_;D+!j?wZFW4+S>~DAL3OR4LNL`Hz91_$AI-O>`Q9`?n$yqb^s71j JZccxi(?5GUsjL71 diff --git a/data/stockpiles/tannedhides.dfstock b/data/stockpiles/tannedhides.dfstock index 226bd0ec29b204e8be8e885e87ec364115542e1b..9c58d41ce6b004c24dbb6c5b0273903845ed2d0c 100644 GIT binary patch literal 19 acmdNA3Id}XUF+Y!=j^;LS#cOY4Z{aor->?VYgw@ zojSvd&@ggJ;&5-~Uc)(9&#okn)E7nx8jQ}f4K(D!WrMv7M@1t}9%%Z!s$_UqD-IA> zaUR{BzTx*Y>N^fzaG(S|Qcx#MM9(sb&7IDM;YRz*Y?m$JNdx<>;y-62u}Gqhy;qP- zx(DhIv(Q-pzCJD^%18}AE#iSJyje%*B%VQ^Ixk$Pn{8fehrAw%cN*R#d4M`au( z%G5{47}PD_Z8!ikV7wn5f5(ExWu>94Nw^N(Da^iTsU0KIGH`%Rtc-@&&7wZN7%m() zS0X0_yo~Ie#Q>h>HCtKaLG06i4=Cxr))v>Q!ZYwvpGG}InZWo?x2XFh>F_oiD71=X z0mHf^H)qj&db&EJpx8l<5kzd1Ba0~(^4(T0W<3maf{071>(sw)k^)`5buJgJGUVti zCNh|`A)Gz}$Rqz4CBW{As-rGD9_b{@Lfxo(uS+4A>Hu93_4zv5G#%L55S#brTibqb T+aGQ7vu%F0&F{AP(>DJA0E(=L diff --git a/data/stockpiles/usablehair.dfstock b/data/stockpiles/usablehair.dfstock index 97528d893bbc238bd0a2fbfd51ed4c0d3eb941da..2082d7dcf6783b380b96d1d586d9dd562afec6b2 100644 GIT binary patch literal 46 zcmdPW|1&Nqi(<|1?$Wuo3JY}0cK)ZZNEI}NjrP709?xun%gD0C?Lm4?lR zNjK^Y&qBkU_HB%I8t92Eod-$yV*cPE?ic`n{ZSBaq?i(=S3ysb+O_= zaS`W%?(`kEhfzPU@Qej5=uv_`VIq3ANo?+PG>m7!pKf+u6OJ^nUn~D(HWG^@>ezdQ z$fR4K4mmR&1?21DG@^~v^usJ3%)+~MbWGwI@~QK}g}R%~OGU`bo_M3-U6Kc>LquJl zMW#%Bbc{h=^4*3VGy}%_>2bHu04@s+Wl6$y=uQ##MN37DD9hjhHL)rh-d2nL^kTSh zTwRHrknl3Ha~1=5hUaW$mWQw}|6d?Ux1~ays|(M>Q#~5>FlB<`J6)n~lce3-Y~au$ zk_8IuC%HHa@ag%~8HL0Sc8m~WqwHBsv9Ry5a52kn;1f)oQ~gf;+bSv0*<0sw(P~4E z&SD~iNh{LnGl)Fu_fZ1kuBbZdvg45sGSAeFs`vUS1XCU03$m_{FkXA-*zQHow~Dcia4Fn}3Fku+IPh From e4ab868dd53980a32594964007b4acac7c85a92d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:02:36 -0700 Subject: [PATCH 0875/2222] remove unrepresentable refuse stockpile defs --- data/stockpiles/bones.dfstock | 157 ------------ data/stockpiles/corpses.dfstock | 157 ------------ data/stockpiles/craftrefuse.dfstock | 364 ---------------------------- data/stockpiles/hair.dfstock | 157 ------------ data/stockpiles/horns.dfstock | Bin 730 -> 0 bytes data/stockpiles/shells.dfstock | 157 ------------ data/stockpiles/skulls.dfstock | 157 ------------ data/stockpiles/teeth.dfstock | 157 ------------ 8 files changed, 1306 deletions(-) delete mode 100644 data/stockpiles/bones.dfstock delete mode 100644 data/stockpiles/corpses.dfstock delete mode 100644 data/stockpiles/craftrefuse.dfstock delete mode 100644 data/stockpiles/hair.dfstock delete mode 100644 data/stockpiles/horns.dfstock delete mode 100644 data/stockpiles/shells.dfstock delete mode 100644 data/stockpiles/skulls.dfstock delete mode 100644 data/stockpiles/teeth.dfstock diff --git a/data/stockpiles/bones.dfstock b/data/stockpiles/bones.dfstock deleted file mode 100644 index aeb2cdcf8..000000000 --- a/data/stockpiles/bones.dfstock +++ /dev/null @@ -1,157 +0,0 @@ -*ÆW -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCH2TOAD2TOAD_MAN2 -GIANT_TOAD2WORM2WORM_MAN2 BIRD_BLUEJAY2 BLUEJAY_MAN2 GIANT_BLUEJAY2 BIRD_CARDINAL2 CARDINAL_MAN2GIANT_CARDINAL2 BIRD_GRACKLE2 GRACKLE_MAN2 GIANT_GRACKLE2 BIRD_ORIOLE2 -ORIOLE_MAN2 GIANT_ORIOLE2BIRD_RW_BLACKBIRD2RW_BLACKBIRD_MAN2GIANT_RW_BLACKBIRD2 BIRD_PENGUIN2BIRD_PENGUIN_LITTLE2BIRD_PENGUIN_EMPEROR2 PENGUIN MAN2BIRD_PENGUIN_GIANT2BIRD_FALCON_PEREGRINE2PEREGRINE FALCON MAN2GIANT PEREGRINE FALCON2 BIRD_KIWI2KIWI MAN2BIRD_KIWI_GIANT2 BIRD_OSTRICH2 OSTRICH MAN2BIRD_OSTRICH_GIANT2 BIRD_CROW2CROW_MAN2 -GIANT_CROW2 -BIRD_RAVEN2 RAVEN_MAN2 GIANT_RAVEN2BIRD_CASSOWARY2 CASSOWARY_MAN2GIANT_CASSOWARY2BIRD_KEA2KEA_MAN2 GIANT_KEA2BIRD_OWL_SNOWY2 SNOWY_OWL_MAN2GIANT_SNOWY_OWL2SPARROW2 SPARROW_MAN2 GIANT_SPARROW2BIRD_STORK_WHITE2WHITE_STORK_MAN2GIANT_WHITE_STORK2 BIRD_LOON2LOON_MAN2 -GIANT_LOON2 BIRD_OWL_BARN2 BARN_OWL_MAN2GIANT_BARN_OWL2 BIRD_PARAKEET2 PARAKEET_MAN2GIANT_PARAKEET2 BIRD_KAKAPO2 -KAKAPO_MAN2 GIANT_KAKAPO2BIRD_PARROT_GREY2GREY_PARROT_MAN2GIANT_GREY_PARROT2 BIRD_PUFFIN2 -PUFFIN_MAN2 GIANT_PUFFIN2 BIRD_SWAN2SWAN_MAN2 -GIANT_SWAN2 BIRD_LORIKEET2 LORIKEET_MAN2GIANT_LORIKEET2 BIRD_WREN2WREN_MAN2 -GIANT_WREN2 BIRD_OSPREY2 -OSPREY_MAN2 GIANT_OSPREY2BIRD_EMU2EMU_MAN2 GIANT_EMU2BIRD_COCKATIEL2 COCKATIEL_MAN2GIANT_COCKATIEL2BIRD_LOVEBIRD_PEACH-FACED2PEACH-FACED_LOVEBIRD_MAN2GIANT_PEACH-FACED_LOVEBIRD2 BIRD_MAGPIE2 -MAGPIE_MAN2 GIANT_MAGPIE2 BIRD_KESTREL2 KESTREL_MAN2 GIANT_KESTREL2BIRD_ALBATROSS2 ALBATROSS_MAN2GIANT_ALBATROSS2BIRD_OWL_GREAT_HORNED2GREAT_HORNED_OWL_MAN2GIANT_GREAT_HORNED_OWL2 -BIRD_EAGLE2 EAGLE_MAN2 GIANT_EAGLE2 BIRD_HORNBILL2 HORNBILL_MAN2GIANT_HORNBILL2BIRD_LOVEBIRD_MASKED2MASKED_LOVEBIRD_MAN2GIANT_MASKED_LOVEBIRD2 BIRD_BUSHTIT2 BUSHTIT_MAN2 GIANT_BUSHTIT2 DAMSELFLY2 DAMSELFLY_MAN2GIANT_DAMSELFLY2MOTH2MOTH_MAN2 -GIANT_MOTH2 GRASSHOPPER2GRASSHOPPER_MAN2GIANT_GRASSHOPPER2 BARK_SCORPION2BARK_SCORPION_MAN2GIANT_BARK_SCORPION2MANTIS2 -MANTIS_MAN2 GIANT_MANTIS2TICK2TICK_MAN2 -GIANT_TICK2LOUSE2 LOUSE_MAN2 GIANT_LOUSE2THRIPS2 -THRIPS_MAN2 GIANT_THRIPS2SLUG2SLUG_MAN2 -GIANT_SLUG2MOSQUITO2 MOSQUITO_MAN2GIANT_MOSQUITO2SPIDER_JUMPING2JUMPING_SPIDER_MAN2GIANT_JUMPING_SPIDER2TERMITE2 -MOON_SNAIL2MOON_SNAIL_MAN2GIANT_MOON_SNAIL2SPIDER_BROWN_RECLUSE2BROWN_RECLUSE_SPIDER_MAN2GIANT_BROWN_RECLUSE_SPIDER2SNAIL2 SNAIL_MAN2 GIANT_SNAIL2 GECKO_LEOPARD2LEOPARD_GECKO_MAN2GIANT_LEOPARD_GECKO2DESERT TORTOISE2DESERT_TORTOISE_MAN2GIANT_DESERT_TORTOISE2 GILA_MONSTER2GILA_MONSTER_MAN2GIANT_GILA_MONSTER2DOG2CAT2MULE2DONKEY2HORSE2COW2SHEEP2PIG2GOAT2 BIRD_CHICKEN2CAVY2 BIRD_DUCK2 WATER_BUFFALO2REINDEER2 -BIRD_GOOSE2YAK2LLAMA2ALPACA2BIRD_GUINEAFOWL2BIRD_PEAFOWL_BLUE2 BIRD_TURKEY2RABBIT2CHIMERA2CENTAUR2GRIFFON2FLY2FLY_MAN2 GIANT_FLY2 ROACH_LARGE2 ROACH_MAN2 GIANT_ROACH2BEETLE2 -BEETLE_MAN2 GIANT_BEETLE2ANT2BUTTERFLY_MONARCH2BUTTERFLY_MONARCH_MAN2GIANT_BUTTERFLY_MONARCH2FIREFLY2 FIREFLY_MAN2 GIANT_FIREFLY2 DRAGONFLY2 DRAGONFLY_MAN2GIANT_DRAGONFLY2 HONEY_BEE2 BUMBLEBEE2 GOAT_MOUNTAIN2GOAT_MOUNTAIN_MAN2GIANT_GOAT_MOUNTAIN2 MARMOT_HOARY2MARMOT_HOARY_MAN2GIANT_MARMOT_HOARY2GNOME_MOUNTAIN2 -GNOME_DARK2WALRUS2 -WALRUS_MAN2 GIANT_WALRUS2FISH_LAMPREY_SEA2SHARK_GREAT_WHITE2 SHARK_FRILL2SHARK_SPINY_DOGFISH2SHARK_WOBBEGONG_SPOTTED2 SHARK_WHALE2 SHARK_BASKING2 SHARK_NURSE2SHARK_MAKO_SHORTFIN2SHARK_MAKO_LONGFIN2 SHARK_TIGER2 -SHARK_BULL2SHARK_REEF_BLACKTIP2SHARK_REEF_WHITETIP2 -SHARK_BLUE2SHARK_HAMMERHEAD2 SHARK_ANGEL2FISH_SKATE_COMMON2FISH_RAY_MANTA2 FISH_STINGRAY2FISH_COELACANTH2 FISH_STURGEON2FISH_CONGER_EEL2 FISH_MILKFISH2FISH_COD2 FISH_OPAH2FISH_GROUPER_GIANT2 FISH_BLUEFISH2FISH_SUNFISH_OCEAN2FISH_SWORDFISH2 FISH_MARLIN2 FISH_HALIBUT2FISH_BARRACUDA_GREAT2FISH_TUNA_BLUEFIN2NARWHAL2 NARWHAL MAN2NARWHAL, GIANT2HIPPO2 HIPPO_MAN2 GIANT_HIPPO2FISH_GAR_LONGNOSE2 FISH_CARP2FISH_TIGERFISH2 FISH_PIKE2PLATYPUS2 PLATYPUS MAN2PLATYPUS, GIANT2 BEAR_GRIZZLY2BEAR_GRIZZLY_MAN2GIANT_BEAR_GRIZZLY2 -BEAR_BLACK2BEAR_BLACK_MAN2GIANT_BEAR_BLACK2DEER2DEER_MAN2 -GIANT_DEER2FOX2FOX_MAN2 GIANT_FOX2RACCOON2 RACCOON_MAN2 GIANT_RACCOON2MACAQUE_RHESUS2MACAQUE_RHESUS_MAN2GIANT_MACAQUE_RHESUS2COUGAR2 -COUGAR_MAN2 GIANT_COUGAR2WOLF2WOLF_MAN2 -GIANT_WOLF2 GROUNDHOG2 GROUNDHOG_MAN2GIANT_GROUNDHOG2 ALLIGATOR2 ALLIGATOR_MAN2GIANT_ALLIGATOR2 BIRD_BUZZARD2 BUZZARD_MAN2 GIANT_BUZZARD2PANDA2PANDA, GIGANTIC2 PANDA MAN2CAPYBARA2CAPYBARA, GIANT2 CAPYBARA MAN2BADGER2 -BADGER MAN2 BADGER, GIANT2MOOSE2 MOOSE MAN2 MOOSE, GIANT2 RED PANDA2 RED PANDA MAN2RED PANDA, GIANT2ELEPHANT2 ELEPHANT_MAN2GIANT_ELEPHANT2WARTHOG2 WARTHOG_MAN2 GIANT_WARTHOG2LION2LION_MAN2 -GIANT_LION2LEOPARD2 LEOPARD_MAN2 GIANT_LEOPARD2JAGUAR2 -JAGUAR_MAN2 GIANT_JAGUAR2TIGER2 TIGER_MAN2 GIANT_TIGER2CHEETAH2 CHEETAH_MAN2 GIANT_CHEETAH2GAZELLE2 GAZELLE_MAN2 GIANT_GAZELLE2MANDRILL2 MANDRILL_MAN2GIANT_MANDRILL2 -CHIMPANZEE2BONOBO2GORILLA2 ORANGUTAN2GIBBON_SIAMANG2GIBBON_WHITE_HANDED2GIBBON_BLACK_HANDED2 GIBBON_GRAY2GIBBON_SILVERY2GIBBON_PILEATED2 GIBBON_BILOU2GIBBON_WHITE_BROWED2GIBBON_BLACK_CRESTED2 CAMEL_1_HUMP2CAMEL_1_HUMP_MAN2GIANT_CAMEL_1_HUMP2 CAMEL_2_HUMP2CAMEL_2_HUMP_MAN2GIANT_CAMEL_2_HUMP2CROCODILE_SALTWATER2CROCODILE_SALTWATER_MAN2GIANT_CROCODILE_SALTWATER2 BIRD_VULTURE2 VULTURE_MAN2 GIANT_VULTURE2 -RHINOCEROS2RHINOCEROS_MAN2GIANT_RHINOCEROS2GIRAFFE2 GIRAFFE_MAN2 GIANT_GIRAFFE2 HONEY BADGER2HONEY BADGER MAN2HONEY BADGER, GIANT2GIANT TORTOISE2GIANT TORTOISE MAN2GIGANTIC TORTOISE2 ARMADILLO2 ARMADILLO MAN2ARMADILLO, GIANT2MUSKOX2 -MUSKOX_MAN2 GIANT_MUSKOX2ELK2ELK_MAN2 GIANT_ELK2 -BEAR_POLAR2BEAR_POLAR_MAN2GIANT_BEAR_POLAR2 WOLVERINE2 WOLVERINE_MAN2GIANT_WOLVERINE2 -CHINCHILLA2CHINCHILLA_MAN2GIANT_CHINCHILLA2 FLOATING_GUTS2DRUNIAN2 CREEPING_EYE2VORACIOUS_CAVE_CRAWLER2BLIND_CAVE_OGRE2 -CAP_HOPPER2 -MAGMA_CRAB2CRUNDLE2 HUNGRY_HEAD2 -FLESH_BALL2ELK_BIRD2 HELMET_SNAKE2GREEN_DEVOURER2RUTHERER2CREEPY_CRAWLER2DRALTHA2GIANT_EARTHWORM2 BLOOD_MAN2BUGBAT2MANERA2 -MOLEMARIAN2JABBERER2 POND_GRABBER2BLIND_CAVE_BEAR2 CAVE_DRAGON2REACHER2ELEMENTMAN_GABBRO2GORLAK2 CAVE_FLOATER2PLUMP_HELMET_MAN2 CAVE_BLOB2ELEMENTMAN_AMETHYST2OCTOPUS2 OCTOPUS_MAN2 GIANT_OCTOPUS2CRAB2CRAB_MAN2 -GIANT_CRAB2 LEOPARD_SEAL2LEOPARD_SEAL_MAN2GIANT_LEOPARD_SEAL2 -CUTTLEFISH2CUTTLEFISH_MAN2GIANT_CUTTLEFISH2ORCA2ORCA_MAN2 -GIANT_ORCA2SPONGE2 -SPONGE_MAN2 GIANT_SPONGE2HORSESHOE_CRAB2HORSESHOE_CRAB_MAN2GIANT_HORSESHOE_CRAB2 SPERM_WHALE2SPERM_WHALE_MAN2GIANT_SPERM_WHALE2 ELEPHANT_SEAL2ELEPHANT_SEAL_MAN2GIANT_ELEPHANT_SEAL2 HARP_SEAL2 HARP_SEAL_MAN2GIANT_HARP_SEAL2NAUTILUS2 NAUTILUS_MAN2GIANT_NAUTILUS2 FOXSQUIRREL2 MOGHOPPER2 RAT_DEMON2WAMBLER_FLUFFY2LIZARD_RHINO_TWO_LEGGED2 WORM_KNUCKLE2SPIDER_PHANTOM2 FLY_ACORN2 -GNAT_BLOOD2LIZARD2 -LIZARD_MAN2 GIANT_LIZARD2SKINK2 SKINK_MAN2 GIANT_SKINK2 CHAMELEON2 CHAMELEON_MAN2GIANT_CHAMELEON2ANOLE2 ANOLE_MAN2 GIANT_ANOLE2IGUANA2 -IGUANA_MAN2 GIANT_IGUANA2 RIVER OTTER2 SEA OTTER2 OTTER_MAN2 GIANT_OTTER2SNAPPING TURTLE2ALLIGATOR SNAPPING TURTLE2SNAPPING_TURTLE_MAN2GIANT_SNAPPING_TURTLE2BEAVER2 -BEAVER_MAN2 GIANT_BEAVER2LEECH2 LEECH_MAN2 GIANT_LEECH2AXOLOTL2 AXOLOTL_MAN2 GIANT_AXOLOTL2MINK2MINK_MAN2 -GIANT_MINK2 POND_TURTLE2POND_TURTLE_MAN2GIANT_POND_TURTLE2RAT2RAT_MAN2 SQUIRREL_GRAY2SQUIRREL_GRAY_MAN2GIANT_SQUIRREL_GRAY2 SQUIRREL_RED2SQUIRREL_RED_MAN2GIANT_SQUIRREL_RED2CHIPMUNK2 CHIPMUNK_MAN2GIANT_CHIPMUNK2HAMSTER2 HAMSTER_MAN2 GIANT_HAMSTER2HEDGEHOG2 HEDGEHOG_MAN2GIANT_HEDGEHOG2SQUIRREL_FLYING2FLYING_SQUIRREL_MAN2GIANT_FLYING_SQUIRREL2MUSSEL2OYSTER2 FISH_SALMON2FISH_CLOWNFISH2 FISH_HAGFISH2FISH_LAMPREY_BROOK2 FISH_RAY_BAT2FISH_RAY_THORNBACK2FISH_RATFISH_SPOTTED2 FISH_HERRING2 FISH_SHAD2 FISH_ANCHOVY2FISH_TROUT_STEELHEAD2 FISH_HAKE2 FISH_SEAHORSE2 FISH_GLASSEYE2FISH_PUFFER_WHITE_SPOTTED2 FISH_SOLE2 FISH_FLOUNDER2 FISH_MACKEREL2JELLYFISH_SEA_NETTLE2SQUID2 SQUID MAN2GIGANTIC SQUID2 FISH_LUNGFISH2FISH_LOACH_CLOWN2FISH_BULLHEAD_BROWN2FISH_BULLHEAD_YELLOW2FISH_BULLHEAD_BLACK2FISH_KNIFEFISH_BANDED2 FISH_CHAR2FISH_TROUT_RAINBOW2FISH_MOLLY_SAILFIN2 -FISH_GUPPY2 -FISH_PERCH2DWARF2HUMAN2ELF2GOBLIN2KOBOLD2GREMLIN2TROLL2OGRE2UNICORN2DRAGON2SATYR2COLOSSUS_BRONZE2GIANT2CYCLOPS2ETTIN2MINOTAUR2YETI2 SASQUATCH2 BLIZZARD_MAN2WOLF_ICE2FAIRY2PIXIE2BEAK_DOG2 GRIMELING2 BLENDEC_FOUL2 STRANGLER2 NIGHTWING2HARPY2HYDRA2 MERPERSON2 SEA_SERPENT2 SEA_MONSTER2BIRD_ROC2CROCODILE_CAVE2TOAD_GIANT_CAVE2 OLM_GIANT2 BAT_GIANT2 RAT_GIANT2 RAT_LARGE2MOLE_DOG_NAKED2 -TROGLODYTE2 -MOLE_GIANT2IMP_FIRE2SPIDER_CAVE_GIANT2 SPIDER_CAVE2 FISH_CAVE2 CAVE_FISH_MAN2 LOBSTER_CAVE2 -SNAKE_FIRE2OLM2OLM_MAN2BAT2BAT_MAN2MAGGOT_PURRING2ELEMENTMAN_FIRE2ELEMENTMAN_MAGMA2ELEMENTMAN_IRON2ELEMENTMAN_MUD2BIRD_SWALLOW_CAVE2CAVE_SWALLOW_MAN2BIRD_SWALLOW_CAVE_GIANT2 AMPHIBIAN_MAN2 REPTILE_MAN2 SERPENT_MAN2ANT_MAN2 -RODENT MAN2 WILD_BOAR2 WILD_BOAR_MAN2GIANT_WILD_BOAR2COYOTE2 -COYOTE_MAN2 GIANT_COYOTE2KANGAROO2 KANGAROO_MAN2GIANT_KANGAROO2KOALA2 KOALA_MAN2 GIANT_KOALA2ADDER2 ADDER_MAN2 GIANT_ADDER2ECHIDNA2 ECHIDNA_MAN2 GIANT_ECHIDNA2 PORCUPINE2 PORCUPINE_MAN2GIANT_PORCUPINE2 KINGSNAKE2 KINGSNAKE_MAN2GIANT_KINGSNAKE2 GRAY_LANGUR2GRAY_LANGUR_MAN2GIANT_GRAY_LANGUR2BOBCAT2 -BOBCAT_MAN2 GIANT_BOBCAT2SKUNK2 SKUNK_MAN2 GIANT_SKUNK2GREEN_TREE_FROG2GREEN_TREE_FROG_MAN2GIANT_GREEN_TREE_FROG2HARE2HARE_MAN2 -GIANT_HARE2 RATTLESNAKE2RATTLESNAKE_MAN2GIANT_RATTLESNAKE2WEASEL2 -WEASEL_MAN2 GIANT_WEASEL2COPPERHEAD_SNAKE2COPPERHEAD_SNAKE_MAN2GIANT_COPPERHEAD_SNAKE2IBEX2IBEX_MAN2 -GIANT_IBEX2WOMBAT2 -WOMBAT_MAN2 GIANT_WOMBAT2DINGO2 DINGO_MAN2 GIANT_DINGO2COATI2 COATI_MAN2 GIANT_COATI2OPOSSUM2 OPOSSUM_MAN2 GIANT_OPOSSUM2MONGOOSE2 MONGOOSE_MAN2GIANT_MONGOOSE2HYENA2 HYENA_MAN2 GIANT_HYENA2ANACONDA2 ANACONDA_MAN2GIANT_ANACONDA2MONITOR_LIZARD2MONITOR_LIZARD_MAN2GIANT_MONITOR_LIZARD2 -KING_COBRA2KING_COBRA_MAN2GIANT_KING_COBRA2OCELOT2 -OCELOT_MAN2 GIANT_OCELOT2JACKAL2 -JACKAL_MAN2 GIANT_JACKAL2CAPUCHIN2 CAPUCHIN_MAN2GIANT_CAPUCHIN2SLOTH2 SLOTH_MAN2 GIANT_SLOTH2 SPIDER_MONKEY2SPIDER_MONKEY_MAN2GIANT_SPIDER_MONKEY2PANGOLIN2 PANGOLIN_MAN2GIANT_PANGOLIN2 BLACK_MAMBA2BLACK_MAMBA_MAN2GIANT_BLACK_MAMBA2 -BEAR_SLOTH2SLOTH_BEAR_MAN2GIANT_SLOTH_BEAR2AYE-AYE2 AYE-AYE_MAN2 GIANT_AYE-AYE2 -BUSHMASTER2BUSHMASTER_MAN2GIANT_BUSHMASTER2PYTHON2 -PYTHON_MAN2 GIANT_PYTHON2TAPIR2 TAPIR_MAN2 GIANT_TAPIR2IMPALA2 -IMPALA_MAN2 GIANT_IMPALA2AARDVARK2 AARDVARK_MAN2GIANT_AARDVARK2 LION_TAMARIN2LION_TAMARIN_MAN2GIANT_LION_TAMARIN2STOAT2 STOAT_MAN2 GIANT_STOAT2LYNX2LYNX_MAN2 -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/corpses.dfstock b/data/stockpiles/corpses.dfstock deleted file mode 100644 index dbaa101c1..000000000 --- a/data/stockpiles/corpses.dfstock +++ /dev/null @@ -1,157 +0,0 @@ -*ÆW -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCHTOADTOAD_MAN -GIANT_TOADWORMWORM_MAN BIRD_BLUEJAY BLUEJAY_MAN GIANT_BLUEJAY BIRD_CARDINAL CARDINAL_MANGIANT_CARDINAL BIRD_GRACKLE GRACKLE_MAN GIANT_GRACKLE BIRD_ORIOLE -ORIOLE_MAN GIANT_ORIOLEBIRD_RW_BLACKBIRDRW_BLACKBIRD_MANGIANT_RW_BLACKBIRD BIRD_PENGUINBIRD_PENGUIN_LITTLEBIRD_PENGUIN_EMPEROR PENGUIN MANBIRD_PENGUIN_GIANTBIRD_FALCON_PEREGRINEPEREGRINE FALCON MANGIANT PEREGRINE FALCON BIRD_KIWIKIWI MANBIRD_KIWI_GIANT BIRD_OSTRICH OSTRICH MANBIRD_OSTRICH_GIANT BIRD_CROWCROW_MAN -GIANT_CROW -BIRD_RAVEN RAVEN_MAN GIANT_RAVENBIRD_CASSOWARY CASSOWARY_MANGIANT_CASSOWARYBIRD_KEAKEA_MAN GIANT_KEABIRD_OWL_SNOWY SNOWY_OWL_MANGIANT_SNOWY_OWLSPARROW SPARROW_MAN GIANT_SPARROWBIRD_STORK_WHITEWHITE_STORK_MANGIANT_WHITE_STORK BIRD_LOONLOON_MAN -GIANT_LOON BIRD_OWL_BARN BARN_OWL_MANGIANT_BARN_OWL BIRD_PARAKEET PARAKEET_MANGIANT_PARAKEET BIRD_KAKAPO -KAKAPO_MAN GIANT_KAKAPOBIRD_PARROT_GREYGREY_PARROT_MANGIANT_GREY_PARROT BIRD_PUFFIN -PUFFIN_MAN GIANT_PUFFIN BIRD_SWANSWAN_MAN -GIANT_SWAN BIRD_LORIKEET LORIKEET_MANGIANT_LORIKEET BIRD_WRENWREN_MAN -GIANT_WREN BIRD_OSPREY -OSPREY_MAN GIANT_OSPREYBIRD_EMUEMU_MAN GIANT_EMUBIRD_COCKATIEL COCKATIEL_MANGIANT_COCKATIELBIRD_LOVEBIRD_PEACH-FACEDPEACH-FACED_LOVEBIRD_MANGIANT_PEACH-FACED_LOVEBIRD BIRD_MAGPIE -MAGPIE_MAN GIANT_MAGPIE BIRD_KESTREL KESTREL_MAN GIANT_KESTRELBIRD_ALBATROSS ALBATROSS_MANGIANT_ALBATROSSBIRD_OWL_GREAT_HORNEDGREAT_HORNED_OWL_MANGIANT_GREAT_HORNED_OWL -BIRD_EAGLE EAGLE_MAN GIANT_EAGLE BIRD_HORNBILL HORNBILL_MANGIANT_HORNBILLBIRD_LOVEBIRD_MASKEDMASKED_LOVEBIRD_MANGIANT_MASKED_LOVEBIRD BIRD_BUSHTIT BUSHTIT_MAN GIANT_BUSHTIT DAMSELFLY DAMSELFLY_MANGIANT_DAMSELFLYMOTHMOTH_MAN -GIANT_MOTH GRASSHOPPERGRASSHOPPER_MANGIANT_GRASSHOPPER BARK_SCORPIONBARK_SCORPION_MANGIANT_BARK_SCORPIONMANTIS -MANTIS_MAN GIANT_MANTISTICKTICK_MAN -GIANT_TICKLOUSE LOUSE_MAN GIANT_LOUSETHRIPS -THRIPS_MAN GIANT_THRIPSSLUGSLUG_MAN -GIANT_SLUGMOSQUITO MOSQUITO_MANGIANT_MOSQUITOSPIDER_JUMPINGJUMPING_SPIDER_MANGIANT_JUMPING_SPIDERTERMITE -MOON_SNAILMOON_SNAIL_MANGIANT_MOON_SNAILSPIDER_BROWN_RECLUSEBROWN_RECLUSE_SPIDER_MANGIANT_BROWN_RECLUSE_SPIDERSNAIL SNAIL_MAN GIANT_SNAIL GECKO_LEOPARDLEOPARD_GECKO_MANGIANT_LEOPARD_GECKODESERT TORTOISEDESERT_TORTOISE_MANGIANT_DESERT_TORTOISE GILA_MONSTERGILA_MONSTER_MANGIANT_GILA_MONSTERDOGCATMULEDONKEYHORSECOWSHEEPPIGGOAT BIRD_CHICKENCAVY BIRD_DUCK WATER_BUFFALOREINDEER -BIRD_GOOSEYAKLLAMAALPACABIRD_GUINEAFOWLBIRD_PEAFOWL_BLUE BIRD_TURKEYRABBITCHIMERACENTAURGRIFFONFLYFLY_MAN GIANT_FLY ROACH_LARGE ROACH_MAN GIANT_ROACHBEETLE -BEETLE_MAN GIANT_BEETLEANTBUTTERFLY_MONARCHBUTTERFLY_MONARCH_MANGIANT_BUTTERFLY_MONARCHFIREFLY FIREFLY_MAN GIANT_FIREFLY DRAGONFLY DRAGONFLY_MANGIANT_DRAGONFLY HONEY_BEE BUMBLEBEE GOAT_MOUNTAINGOAT_MOUNTAIN_MANGIANT_GOAT_MOUNTAIN MARMOT_HOARYMARMOT_HOARY_MANGIANT_MARMOT_HOARYGNOME_MOUNTAIN -GNOME_DARKWALRUS -WALRUS_MAN GIANT_WALRUSFISH_LAMPREY_SEASHARK_GREAT_WHITE SHARK_FRILLSHARK_SPINY_DOGFISHSHARK_WOBBEGONG_SPOTTED SHARK_WHALE SHARK_BASKING SHARK_NURSESHARK_MAKO_SHORTFINSHARK_MAKO_LONGFIN SHARK_TIGER -SHARK_BULLSHARK_REEF_BLACKTIPSHARK_REEF_WHITETIP -SHARK_BLUESHARK_HAMMERHEAD SHARK_ANGELFISH_SKATE_COMMONFISH_RAY_MANTA FISH_STINGRAYFISH_COELACANTH FISH_STURGEONFISH_CONGER_EEL FISH_MILKFISHFISH_COD FISH_OPAHFISH_GROUPER_GIANT FISH_BLUEFISHFISH_SUNFISH_OCEANFISH_SWORDFISH FISH_MARLIN FISH_HALIBUTFISH_BARRACUDA_GREATFISH_TUNA_BLUEFINNARWHAL NARWHAL MANNARWHAL, GIANTHIPPO HIPPO_MAN GIANT_HIPPOFISH_GAR_LONGNOSE FISH_CARPFISH_TIGERFISH FISH_PIKEPLATYPUS PLATYPUS MANPLATYPUS, GIANT BEAR_GRIZZLYBEAR_GRIZZLY_MANGIANT_BEAR_GRIZZLY -BEAR_BLACKBEAR_BLACK_MANGIANT_BEAR_BLACKDEERDEER_MAN -GIANT_DEERFOXFOX_MAN GIANT_FOXRACCOON RACCOON_MAN GIANT_RACCOONMACAQUE_RHESUSMACAQUE_RHESUS_MANGIANT_MACAQUE_RHESUSCOUGAR -COUGAR_MAN GIANT_COUGARWOLFWOLF_MAN -GIANT_WOLF GROUNDHOG GROUNDHOG_MANGIANT_GROUNDHOG ALLIGATOR ALLIGATOR_MANGIANT_ALLIGATOR BIRD_BUZZARD BUZZARD_MAN GIANT_BUZZARDPANDAPANDA, GIGANTIC PANDA MANCAPYBARACAPYBARA, GIANT CAPYBARA MANBADGER -BADGER MAN BADGER, GIANTMOOSE MOOSE MAN MOOSE, GIANT RED PANDA RED PANDA MANRED PANDA, GIANTELEPHANT ELEPHANT_MANGIANT_ELEPHANTWARTHOG WARTHOG_MAN GIANT_WARTHOGLIONLION_MAN -GIANT_LIONLEOPARD LEOPARD_MAN GIANT_LEOPARDJAGUAR -JAGUAR_MAN GIANT_JAGUARTIGER TIGER_MAN GIANT_TIGERCHEETAH CHEETAH_MAN GIANT_CHEETAHGAZELLE GAZELLE_MAN GIANT_GAZELLEMANDRILL MANDRILL_MANGIANT_MANDRILL -CHIMPANZEEBONOBOGORILLA ORANGUTANGIBBON_SIAMANGGIBBON_WHITE_HANDEDGIBBON_BLACK_HANDED GIBBON_GRAYGIBBON_SILVERYGIBBON_PILEATED GIBBON_BILOUGIBBON_WHITE_BROWEDGIBBON_BLACK_CRESTED CAMEL_1_HUMPCAMEL_1_HUMP_MANGIANT_CAMEL_1_HUMP CAMEL_2_HUMPCAMEL_2_HUMP_MANGIANT_CAMEL_2_HUMPCROCODILE_SALTWATERCROCODILE_SALTWATER_MANGIANT_CROCODILE_SALTWATER BIRD_VULTURE VULTURE_MAN GIANT_VULTURE -RHINOCEROSRHINOCEROS_MANGIANT_RHINOCEROSGIRAFFE GIRAFFE_MAN GIANT_GIRAFFE HONEY BADGERHONEY BADGER MANHONEY BADGER, GIANTGIANT TORTOISEGIANT TORTOISE MANGIGANTIC TORTOISE ARMADILLO ARMADILLO MANARMADILLO, GIANTMUSKOX -MUSKOX_MAN GIANT_MUSKOXELKELK_MAN GIANT_ELK -BEAR_POLARBEAR_POLAR_MANGIANT_BEAR_POLAR WOLVERINE WOLVERINE_MANGIANT_WOLVERINE -CHINCHILLACHINCHILLA_MANGIANT_CHINCHILLA FLOATING_GUTSDRUNIAN CREEPING_EYEVORACIOUS_CAVE_CRAWLERBLIND_CAVE_OGRE -CAP_HOPPER -MAGMA_CRABCRUNDLE HUNGRY_HEAD -FLESH_BALLELK_BIRD HELMET_SNAKEGREEN_DEVOURERRUTHERERCREEPY_CRAWLERDRALTHAGIANT_EARTHWORM BLOOD_MANBUGBATMANERA -MOLEMARIANJABBERER POND_GRABBERBLIND_CAVE_BEAR CAVE_DRAGONREACHERELEMENTMAN_GABBROGORLAK CAVE_FLOATERPLUMP_HELMET_MAN CAVE_BLOBELEMENTMAN_AMETHYSTOCTOPUS OCTOPUS_MAN GIANT_OCTOPUSCRABCRAB_MAN -GIANT_CRAB LEOPARD_SEALLEOPARD_SEAL_MANGIANT_LEOPARD_SEAL -CUTTLEFISHCUTTLEFISH_MANGIANT_CUTTLEFISHORCAORCA_MAN -GIANT_ORCASPONGE -SPONGE_MAN GIANT_SPONGEHORSESHOE_CRABHORSESHOE_CRAB_MANGIANT_HORSESHOE_CRAB SPERM_WHALESPERM_WHALE_MANGIANT_SPERM_WHALE ELEPHANT_SEALELEPHANT_SEAL_MANGIANT_ELEPHANT_SEAL HARP_SEAL HARP_SEAL_MANGIANT_HARP_SEALNAUTILUS NAUTILUS_MANGIANT_NAUTILUS FOXSQUIRREL MOGHOPPER RAT_DEMONWAMBLER_FLUFFYLIZARD_RHINO_TWO_LEGGED WORM_KNUCKLESPIDER_PHANTOM FLY_ACORN -GNAT_BLOODLIZARD -LIZARD_MAN GIANT_LIZARDSKINK SKINK_MAN GIANT_SKINK CHAMELEON CHAMELEON_MANGIANT_CHAMELEONANOLE ANOLE_MAN GIANT_ANOLEIGUANA -IGUANA_MAN GIANT_IGUANA RIVER OTTER SEA OTTER OTTER_MAN GIANT_OTTERSNAPPING TURTLEALLIGATOR SNAPPING TURTLESNAPPING_TURTLE_MANGIANT_SNAPPING_TURTLEBEAVER -BEAVER_MAN GIANT_BEAVERLEECH LEECH_MAN GIANT_LEECHAXOLOTL AXOLOTL_MAN GIANT_AXOLOTLMINKMINK_MAN -GIANT_MINK POND_TURTLEPOND_TURTLE_MANGIANT_POND_TURTLERATRAT_MAN SQUIRREL_GRAYSQUIRREL_GRAY_MANGIANT_SQUIRREL_GRAY SQUIRREL_REDSQUIRREL_RED_MANGIANT_SQUIRREL_REDCHIPMUNK CHIPMUNK_MANGIANT_CHIPMUNKHAMSTER HAMSTER_MAN GIANT_HAMSTERHEDGEHOG HEDGEHOG_MANGIANT_HEDGEHOGSQUIRREL_FLYINGFLYING_SQUIRREL_MANGIANT_FLYING_SQUIRRELMUSSELOYSTER FISH_SALMONFISH_CLOWNFISH FISH_HAGFISHFISH_LAMPREY_BROOK FISH_RAY_BATFISH_RAY_THORNBACKFISH_RATFISH_SPOTTED FISH_HERRING FISH_SHAD FISH_ANCHOVYFISH_TROUT_STEELHEAD FISH_HAKE FISH_SEAHORSE FISH_GLASSEYEFISH_PUFFER_WHITE_SPOTTED FISH_SOLE FISH_FLOUNDER FISH_MACKERELJELLYFISH_SEA_NETTLESQUID SQUID MANGIGANTIC SQUID FISH_LUNGFISHFISH_LOACH_CLOWNFISH_BULLHEAD_BROWNFISH_BULLHEAD_YELLOWFISH_BULLHEAD_BLACKFISH_KNIFEFISH_BANDED FISH_CHARFISH_TROUT_RAINBOWFISH_MOLLY_SAILFIN -FISH_GUPPY -FISH_PERCHDWARFHUMANELFGOBLINKOBOLDGREMLINTROLLOGREUNICORNDRAGONSATYRCOLOSSUS_BRONZEGIANTCYCLOPSETTINMINOTAURYETI SASQUATCH BLIZZARD_MANWOLF_ICEFAIRYPIXIEBEAK_DOG GRIMELING BLENDEC_FOUL STRANGLER NIGHTWINGHARPYHYDRA MERPERSON SEA_SERPENT SEA_MONSTERBIRD_ROCCROCODILE_CAVETOAD_GIANT_CAVE OLM_GIANT BAT_GIANT RAT_GIANT RAT_LARGEMOLE_DOG_NAKED -TROGLODYTE -MOLE_GIANTIMP_FIRESPIDER_CAVE_GIANT SPIDER_CAVE FISH_CAVE CAVE_FISH_MAN LOBSTER_CAVE -SNAKE_FIREOLMOLM_MANBATBAT_MANMAGGOT_PURRINGELEMENTMAN_FIREELEMENTMAN_MAGMAELEMENTMAN_IRONELEMENTMAN_MUDBIRD_SWALLOW_CAVECAVE_SWALLOW_MANBIRD_SWALLOW_CAVE_GIANT AMPHIBIAN_MAN REPTILE_MAN SERPENT_MANANT_MAN -RODENT MAN WILD_BOAR WILD_BOAR_MANGIANT_WILD_BOARCOYOTE -COYOTE_MAN GIANT_COYOTEKANGAROO KANGAROO_MANGIANT_KANGAROOKOALA KOALA_MAN GIANT_KOALAADDER ADDER_MAN GIANT_ADDERECHIDNA ECHIDNA_MAN GIANT_ECHIDNA PORCUPINE PORCUPINE_MANGIANT_PORCUPINE KINGSNAKE KINGSNAKE_MANGIANT_KINGSNAKE GRAY_LANGURGRAY_LANGUR_MANGIANT_GRAY_LANGURBOBCAT -BOBCAT_MAN GIANT_BOBCATSKUNK SKUNK_MAN GIANT_SKUNKGREEN_TREE_FROGGREEN_TREE_FROG_MANGIANT_GREEN_TREE_FROGHAREHARE_MAN -GIANT_HARE RATTLESNAKERATTLESNAKE_MANGIANT_RATTLESNAKEWEASEL -WEASEL_MAN GIANT_WEASELCOPPERHEAD_SNAKECOPPERHEAD_SNAKE_MANGIANT_COPPERHEAD_SNAKEIBEXIBEX_MAN -GIANT_IBEXWOMBAT -WOMBAT_MAN GIANT_WOMBATDINGO DINGO_MAN GIANT_DINGOCOATI COATI_MAN GIANT_COATIOPOSSUM OPOSSUM_MAN GIANT_OPOSSUMMONGOOSE MONGOOSE_MANGIANT_MONGOOSEHYENA HYENA_MAN GIANT_HYENAANACONDA ANACONDA_MANGIANT_ANACONDAMONITOR_LIZARDMONITOR_LIZARD_MANGIANT_MONITOR_LIZARD -KING_COBRAKING_COBRA_MANGIANT_KING_COBRAOCELOT -OCELOT_MAN GIANT_OCELOTJACKAL -JACKAL_MAN GIANT_JACKALCAPUCHIN CAPUCHIN_MANGIANT_CAPUCHINSLOTH SLOTH_MAN GIANT_SLOTH SPIDER_MONKEYSPIDER_MONKEY_MANGIANT_SPIDER_MONKEYPANGOLIN PANGOLIN_MANGIANT_PANGOLIN BLACK_MAMBABLACK_MAMBA_MANGIANT_BLACK_MAMBA -BEAR_SLOTHSLOTH_BEAR_MANGIANT_SLOTH_BEARAYE-AYE AYE-AYE_MAN GIANT_AYE-AYE -BUSHMASTERBUSHMASTER_MANGIANT_BUSHMASTERPYTHON -PYTHON_MAN GIANT_PYTHONTAPIR TAPIR_MAN GIANT_TAPIRIMPALA -IMPALA_MAN GIANT_IMPALAAARDVARK AARDVARK_MANGIANT_AARDVARK LION_TAMARINLION_TAMARIN_MANGIANT_LION_TAMARINSTOAT STOAT_MAN GIANT_STOATLYNXLYNX_MAN -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/craftrefuse.dfstock b/data/stockpiles/craftrefuse.dfstock deleted file mode 100644 index f834658d4..000000000 --- a/data/stockpiles/craftrefuse.dfstock +++ /dev/null @@ -1,364 +0,0 @@ -*ïÍ -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCH*TOAD*TOAD_MAN* -GIANT_TOAD*WORM*WORM_MAN* BIRD_BLUEJAY* BLUEJAY_MAN* GIANT_BLUEJAY* BIRD_CARDINAL* CARDINAL_MAN*GIANT_CARDINAL* BIRD_GRACKLE* GRACKLE_MAN* GIANT_GRACKLE* BIRD_ORIOLE* -ORIOLE_MAN* GIANT_ORIOLE*BIRD_RW_BLACKBIRD*RW_BLACKBIRD_MAN*GIANT_RW_BLACKBIRD* BIRD_PENGUIN*BIRD_PENGUIN_LITTLE*BIRD_PENGUIN_EMPEROR* PENGUIN MAN*BIRD_PENGUIN_GIANT*BIRD_FALCON_PEREGRINE*PEREGRINE FALCON MAN*GIANT PEREGRINE FALCON* BIRD_KIWI*KIWI MAN*BIRD_KIWI_GIANT* BIRD_OSTRICH* OSTRICH MAN*BIRD_OSTRICH_GIANT* BIRD_CROW*CROW_MAN* -GIANT_CROW* -BIRD_RAVEN* RAVEN_MAN* GIANT_RAVEN*BIRD_CASSOWARY* CASSOWARY_MAN*GIANT_CASSOWARY*BIRD_KEA*KEA_MAN* GIANT_KEA*BIRD_OWL_SNOWY* SNOWY_OWL_MAN*GIANT_SNOWY_OWL*SPARROW* SPARROW_MAN* GIANT_SPARROW*BIRD_STORK_WHITE*WHITE_STORK_MAN*GIANT_WHITE_STORK* BIRD_LOON*LOON_MAN* -GIANT_LOON* BIRD_OWL_BARN* BARN_OWL_MAN*GIANT_BARN_OWL* BIRD_PARAKEET* PARAKEET_MAN*GIANT_PARAKEET* BIRD_KAKAPO* -KAKAPO_MAN* GIANT_KAKAPO*BIRD_PARROT_GREY*GREY_PARROT_MAN*GIANT_GREY_PARROT* BIRD_PUFFIN* -PUFFIN_MAN* GIANT_PUFFIN* BIRD_SWAN*SWAN_MAN* -GIANT_SWAN* BIRD_LORIKEET* LORIKEET_MAN*GIANT_LORIKEET* BIRD_WREN*WREN_MAN* -GIANT_WREN* BIRD_OSPREY* -OSPREY_MAN* GIANT_OSPREY*BIRD_EMU*EMU_MAN* GIANT_EMU*BIRD_COCKATIEL* COCKATIEL_MAN*GIANT_COCKATIEL*BIRD_LOVEBIRD_PEACH-FACED*PEACH-FACED_LOVEBIRD_MAN*GIANT_PEACH-FACED_LOVEBIRD* BIRD_MAGPIE* -MAGPIE_MAN* GIANT_MAGPIE* BIRD_KESTREL* KESTREL_MAN* GIANT_KESTREL*BIRD_ALBATROSS* ALBATROSS_MAN*GIANT_ALBATROSS*BIRD_OWL_GREAT_HORNED*GREAT_HORNED_OWL_MAN*GIANT_GREAT_HORNED_OWL* -BIRD_EAGLE* EAGLE_MAN* GIANT_EAGLE* BIRD_HORNBILL* HORNBILL_MAN*GIANT_HORNBILL*BIRD_LOVEBIRD_MASKED*MASKED_LOVEBIRD_MAN*GIANT_MASKED_LOVEBIRD* BIRD_BUSHTIT* BUSHTIT_MAN* GIANT_BUSHTIT* DAMSELFLY* DAMSELFLY_MAN*GIANT_DAMSELFLY*MOTH*MOTH_MAN* -GIANT_MOTH* GRASSHOPPER*GRASSHOPPER_MAN*GIANT_GRASSHOPPER* BARK_SCORPION*BARK_SCORPION_MAN*GIANT_BARK_SCORPION*MANTIS* -MANTIS_MAN* GIANT_MANTIS*TICK*TICK_MAN* -GIANT_TICK*LOUSE* LOUSE_MAN* GIANT_LOUSE*THRIPS* -THRIPS_MAN* GIANT_THRIPS*SLUG*SLUG_MAN* -GIANT_SLUG*MOSQUITO* MOSQUITO_MAN*GIANT_MOSQUITO*SPIDER_JUMPING*JUMPING_SPIDER_MAN*GIANT_JUMPING_SPIDER*TERMITE* -MOON_SNAIL*MOON_SNAIL_MAN*GIANT_MOON_SNAIL*SPIDER_BROWN_RECLUSE*BROWN_RECLUSE_SPIDER_MAN*GIANT_BROWN_RECLUSE_SPIDER*SNAIL* SNAIL_MAN* GIANT_SNAIL* GECKO_LEOPARD*LEOPARD_GECKO_MAN*GIANT_LEOPARD_GECKO*DESERT TORTOISE*DESERT_TORTOISE_MAN*GIANT_DESERT_TORTOISE* GILA_MONSTER*GILA_MONSTER_MAN*GIANT_GILA_MONSTER*DOG*CAT*MULE*DONKEY*HORSE*COW*SHEEP*PIG*GOAT* BIRD_CHICKEN*CAVY* BIRD_DUCK* WATER_BUFFALO*REINDEER* -BIRD_GOOSE*YAK*LLAMA*ALPACA*BIRD_GUINEAFOWL*BIRD_PEAFOWL_BLUE* BIRD_TURKEY*RABBIT*CHIMERA*CENTAUR*GRIFFON*FLY*FLY_MAN* GIANT_FLY* ROACH_LARGE* ROACH_MAN* GIANT_ROACH*BEETLE* -BEETLE_MAN* GIANT_BEETLE*ANT*BUTTERFLY_MONARCH*BUTTERFLY_MONARCH_MAN*GIANT_BUTTERFLY_MONARCH*FIREFLY* FIREFLY_MAN* GIANT_FIREFLY* DRAGONFLY* DRAGONFLY_MAN*GIANT_DRAGONFLY* HONEY_BEE* BUMBLEBEE* GOAT_MOUNTAIN*GOAT_MOUNTAIN_MAN*GIANT_GOAT_MOUNTAIN* MARMOT_HOARY*MARMOT_HOARY_MAN*GIANT_MARMOT_HOARY*GNOME_MOUNTAIN* -GNOME_DARK*WALRUS* -WALRUS_MAN* GIANT_WALRUS*FISH_LAMPREY_SEA*SHARK_GREAT_WHITE* SHARK_FRILL*SHARK_SPINY_DOGFISH*SHARK_WOBBEGONG_SPOTTED* SHARK_WHALE* SHARK_BASKING* SHARK_NURSE*SHARK_MAKO_SHORTFIN*SHARK_MAKO_LONGFIN* SHARK_TIGER* -SHARK_BULL*SHARK_REEF_BLACKTIP*SHARK_REEF_WHITETIP* -SHARK_BLUE*SHARK_HAMMERHEAD* SHARK_ANGEL*FISH_SKATE_COMMON*FISH_RAY_MANTA* FISH_STINGRAY*FISH_COELACANTH* FISH_STURGEON*FISH_CONGER_EEL* FISH_MILKFISH*FISH_COD* FISH_OPAH*FISH_GROUPER_GIANT* FISH_BLUEFISH*FISH_SUNFISH_OCEAN*FISH_SWORDFISH* FISH_MARLIN* FISH_HALIBUT*FISH_BARRACUDA_GREAT*FISH_TUNA_BLUEFIN*NARWHAL* NARWHAL MAN*NARWHAL, GIANT*HIPPO* HIPPO_MAN* GIANT_HIPPO*FISH_GAR_LONGNOSE* FISH_CARP*FISH_TIGERFISH* FISH_PIKE*PLATYPUS* PLATYPUS MAN*PLATYPUS, GIANT* BEAR_GRIZZLY*BEAR_GRIZZLY_MAN*GIANT_BEAR_GRIZZLY* -BEAR_BLACK*BEAR_BLACK_MAN*GIANT_BEAR_BLACK*DEER*DEER_MAN* -GIANT_DEER*FOX*FOX_MAN* GIANT_FOX*RACCOON* RACCOON_MAN* GIANT_RACCOON*MACAQUE_RHESUS*MACAQUE_RHESUS_MAN*GIANT_MACAQUE_RHESUS*COUGAR* -COUGAR_MAN* GIANT_COUGAR*WOLF*WOLF_MAN* -GIANT_WOLF* GROUNDHOG* GROUNDHOG_MAN*GIANT_GROUNDHOG* ALLIGATOR* ALLIGATOR_MAN*GIANT_ALLIGATOR* BIRD_BUZZARD* BUZZARD_MAN* GIANT_BUZZARD*PANDA*PANDA, GIGANTIC* PANDA MAN*CAPYBARA*CAPYBARA, GIANT* CAPYBARA MAN*BADGER* -BADGER MAN* BADGER, GIANT*MOOSE* MOOSE MAN* MOOSE, GIANT* RED PANDA* RED PANDA MAN*RED PANDA, GIANT*ELEPHANT* ELEPHANT_MAN*GIANT_ELEPHANT*WARTHOG* WARTHOG_MAN* GIANT_WARTHOG*LION*LION_MAN* -GIANT_LION*LEOPARD* LEOPARD_MAN* GIANT_LEOPARD*JAGUAR* -JAGUAR_MAN* GIANT_JAGUAR*TIGER* TIGER_MAN* GIANT_TIGER*CHEETAH* CHEETAH_MAN* GIANT_CHEETAH*GAZELLE* GAZELLE_MAN* GIANT_GAZELLE*MANDRILL* MANDRILL_MAN*GIANT_MANDRILL* -CHIMPANZEE*BONOBO*GORILLA* ORANGUTAN*GIBBON_SIAMANG*GIBBON_WHITE_HANDED*GIBBON_BLACK_HANDED* GIBBON_GRAY*GIBBON_SILVERY*GIBBON_PILEATED* GIBBON_BILOU*GIBBON_WHITE_BROWED*GIBBON_BLACK_CRESTED* CAMEL_1_HUMP*CAMEL_1_HUMP_MAN*GIANT_CAMEL_1_HUMP* CAMEL_2_HUMP*CAMEL_2_HUMP_MAN*GIANT_CAMEL_2_HUMP*CROCODILE_SALTWATER*CROCODILE_SALTWATER_MAN*GIANT_CROCODILE_SALTWATER* BIRD_VULTURE* VULTURE_MAN* GIANT_VULTURE* -RHINOCEROS*RHINOCEROS_MAN*GIANT_RHINOCEROS*GIRAFFE* GIRAFFE_MAN* GIANT_GIRAFFE* HONEY BADGER*HONEY BADGER MAN*HONEY BADGER, GIANT*GIANT TORTOISE*GIANT TORTOISE MAN*GIGANTIC TORTOISE* ARMADILLO* ARMADILLO MAN*ARMADILLO, GIANT*MUSKOX* -MUSKOX_MAN* GIANT_MUSKOX*ELK*ELK_MAN* GIANT_ELK* -BEAR_POLAR*BEAR_POLAR_MAN*GIANT_BEAR_POLAR* WOLVERINE* WOLVERINE_MAN*GIANT_WOLVERINE* -CHINCHILLA*CHINCHILLA_MAN*GIANT_CHINCHILLA* FLOATING_GUTS*DRUNIAN* CREEPING_EYE*VORACIOUS_CAVE_CRAWLER*BLIND_CAVE_OGRE* -CAP_HOPPER* -MAGMA_CRAB*CRUNDLE* HUNGRY_HEAD* -FLESH_BALL*ELK_BIRD* HELMET_SNAKE*GREEN_DEVOURER*RUTHERER*CREEPY_CRAWLER*DRALTHA*GIANT_EARTHWORM* BLOOD_MAN*BUGBAT*MANERA* -MOLEMARIAN*JABBERER* POND_GRABBER*BLIND_CAVE_BEAR* CAVE_DRAGON*REACHER*ELEMENTMAN_GABBRO*GORLAK* CAVE_FLOATER*PLUMP_HELMET_MAN* CAVE_BLOB*ELEMENTMAN_AMETHYST*OCTOPUS* OCTOPUS_MAN* GIANT_OCTOPUS*CRAB*CRAB_MAN* -GIANT_CRAB* LEOPARD_SEAL*LEOPARD_SEAL_MAN*GIANT_LEOPARD_SEAL* -CUTTLEFISH*CUTTLEFISH_MAN*GIANT_CUTTLEFISH*ORCA*ORCA_MAN* -GIANT_ORCA*SPONGE* -SPONGE_MAN* GIANT_SPONGE*HORSESHOE_CRAB*HORSESHOE_CRAB_MAN*GIANT_HORSESHOE_CRAB* SPERM_WHALE*SPERM_WHALE_MAN*GIANT_SPERM_WHALE* ELEPHANT_SEAL*ELEPHANT_SEAL_MAN*GIANT_ELEPHANT_SEAL* HARP_SEAL* HARP_SEAL_MAN*GIANT_HARP_SEAL*NAUTILUS* NAUTILUS_MAN*GIANT_NAUTILUS* FOXSQUIRREL* MOGHOPPER* RAT_DEMON*WAMBLER_FLUFFY*LIZARD_RHINO_TWO_LEGGED* WORM_KNUCKLE*SPIDER_PHANTOM* FLY_ACORN* -GNAT_BLOOD*LIZARD* -LIZARD_MAN* GIANT_LIZARD*SKINK* SKINK_MAN* GIANT_SKINK* CHAMELEON* CHAMELEON_MAN*GIANT_CHAMELEON*ANOLE* ANOLE_MAN* GIANT_ANOLE*IGUANA* -IGUANA_MAN* GIANT_IGUANA* RIVER OTTER* SEA OTTER* OTTER_MAN* GIANT_OTTER*SNAPPING TURTLE*ALLIGATOR SNAPPING TURTLE*SNAPPING_TURTLE_MAN*GIANT_SNAPPING_TURTLE*BEAVER* -BEAVER_MAN* GIANT_BEAVER*LEECH* LEECH_MAN* GIANT_LEECH*AXOLOTL* AXOLOTL_MAN* GIANT_AXOLOTL*MINK*MINK_MAN* -GIANT_MINK* POND_TURTLE*POND_TURTLE_MAN*GIANT_POND_TURTLE*RAT*RAT_MAN* SQUIRREL_GRAY*SQUIRREL_GRAY_MAN*GIANT_SQUIRREL_GRAY* SQUIRREL_RED*SQUIRREL_RED_MAN*GIANT_SQUIRREL_RED*CHIPMUNK* CHIPMUNK_MAN*GIANT_CHIPMUNK*HAMSTER* HAMSTER_MAN* GIANT_HAMSTER*HEDGEHOG* HEDGEHOG_MAN*GIANT_HEDGEHOG*SQUIRREL_FLYING*FLYING_SQUIRREL_MAN*GIANT_FLYING_SQUIRREL*MUSSEL*OYSTER* FISH_SALMON*FISH_CLOWNFISH* FISH_HAGFISH*FISH_LAMPREY_BROOK* FISH_RAY_BAT*FISH_RAY_THORNBACK*FISH_RATFISH_SPOTTED* FISH_HERRING* FISH_SHAD* FISH_ANCHOVY*FISH_TROUT_STEELHEAD* FISH_HAKE* FISH_SEAHORSE* FISH_GLASSEYE*FISH_PUFFER_WHITE_SPOTTED* FISH_SOLE* FISH_FLOUNDER* FISH_MACKEREL*JELLYFISH_SEA_NETTLE*SQUID* SQUID MAN*GIGANTIC SQUID* FISH_LUNGFISH*FISH_LOACH_CLOWN*FISH_BULLHEAD_BROWN*FISH_BULLHEAD_YELLOW*FISH_BULLHEAD_BLACK*FISH_KNIFEFISH_BANDED* FISH_CHAR*FISH_TROUT_RAINBOW*FISH_MOLLY_SAILFIN* -FISH_GUPPY* -FISH_PERCH*DWARF*HUMAN*ELF*GOBLIN*KOBOLD*GREMLIN*TROLL*OGRE*UNICORN*DRAGON*SATYR*COLOSSUS_BRONZE*GIANT*CYCLOPS*ETTIN*MINOTAUR*YETI* SASQUATCH* BLIZZARD_MAN*WOLF_ICE*FAIRY*PIXIE*BEAK_DOG* GRIMELING* BLENDEC_FOUL* STRANGLER* NIGHTWING*HARPY*HYDRA* MERPERSON* SEA_SERPENT* SEA_MONSTER*BIRD_ROC*CROCODILE_CAVE*TOAD_GIANT_CAVE* OLM_GIANT* BAT_GIANT* RAT_GIANT* RAT_LARGE*MOLE_DOG_NAKED* -TROGLODYTE* -MOLE_GIANT*IMP_FIRE*SPIDER_CAVE_GIANT* SPIDER_CAVE* FISH_CAVE* CAVE_FISH_MAN* LOBSTER_CAVE* -SNAKE_FIRE*OLM*OLM_MAN*BAT*BAT_MAN*MAGGOT_PURRING*ELEMENTMAN_FIRE*ELEMENTMAN_MAGMA*ELEMENTMAN_IRON*ELEMENTMAN_MUD*BIRD_SWALLOW_CAVE*CAVE_SWALLOW_MAN*BIRD_SWALLOW_CAVE_GIANT* AMPHIBIAN_MAN* REPTILE_MAN* SERPENT_MAN*ANT_MAN* -RODENT MAN* WILD_BOAR* WILD_BOAR_MAN*GIANT_WILD_BOAR*COYOTE* -COYOTE_MAN* GIANT_COYOTE*KANGAROO* KANGAROO_MAN*GIANT_KANGAROO*KOALA* KOALA_MAN* GIANT_KOALA*ADDER* ADDER_MAN* GIANT_ADDER*ECHIDNA* ECHIDNA_MAN* GIANT_ECHIDNA* PORCUPINE* PORCUPINE_MAN*GIANT_PORCUPINE* KINGSNAKE* KINGSNAKE_MAN*GIANT_KINGSNAKE* GRAY_LANGUR*GRAY_LANGUR_MAN*GIANT_GRAY_LANGUR*BOBCAT* -BOBCAT_MAN* GIANT_BOBCAT*SKUNK* SKUNK_MAN* GIANT_SKUNK*GREEN_TREE_FROG*GREEN_TREE_FROG_MAN*GIANT_GREEN_TREE_FROG*HARE*HARE_MAN* -GIANT_HARE* RATTLESNAKE*RATTLESNAKE_MAN*GIANT_RATTLESNAKE*WEASEL* -WEASEL_MAN* GIANT_WEASEL*COPPERHEAD_SNAKE*COPPERHEAD_SNAKE_MAN*GIANT_COPPERHEAD_SNAKE*IBEX*IBEX_MAN* -GIANT_IBEX*WOMBAT* -WOMBAT_MAN* GIANT_WOMBAT*DINGO* DINGO_MAN* GIANT_DINGO*COATI* COATI_MAN* GIANT_COATI*OPOSSUM* OPOSSUM_MAN* GIANT_OPOSSUM*MONGOOSE* MONGOOSE_MAN*GIANT_MONGOOSE*HYENA* HYENA_MAN* GIANT_HYENA*ANACONDA* ANACONDA_MAN*GIANT_ANACONDA*MONITOR_LIZARD*MONITOR_LIZARD_MAN*GIANT_MONITOR_LIZARD* -KING_COBRA*KING_COBRA_MAN*GIANT_KING_COBRA*OCELOT* -OCELOT_MAN* GIANT_OCELOT*JACKAL* -JACKAL_MAN* GIANT_JACKAL*CAPUCHIN* CAPUCHIN_MAN*GIANT_CAPUCHIN*SLOTH* SLOTH_MAN* GIANT_SLOTH* SPIDER_MONKEY*SPIDER_MONKEY_MAN*GIANT_SPIDER_MONKEY*PANGOLIN* PANGOLIN_MAN*GIANT_PANGOLIN* BLACK_MAMBA*BLACK_MAMBA_MAN*GIANT_BLACK_MAMBA* -BEAR_SLOTH*SLOTH_BEAR_MAN*GIANT_SLOTH_BEAR*AYE-AYE* AYE-AYE_MAN* GIANT_AYE-AYE* -BUSHMASTER*BUSHMASTER_MAN*GIANT_BUSHMASTER*PYTHON* -PYTHON_MAN* GIANT_PYTHON*TAPIR* TAPIR_MAN* GIANT_TAPIR*IMPALA* -IMPALA_MAN* GIANT_IMPALA*AARDVARK* AARDVARK_MAN*GIANT_AARDVARK* LION_TAMARIN*LION_TAMARIN_MAN*GIANT_LION_TAMARIN*STOAT* STOAT_MAN* GIANT_STOAT*LYNX*LYNX_MAN* -GIANT_LYNX2TOAD2TOAD_MAN2 -GIANT_TOAD2WORM2WORM_MAN2 BIRD_BLUEJAY2 BLUEJAY_MAN2 GIANT_BLUEJAY2 BIRD_CARDINAL2 CARDINAL_MAN2GIANT_CARDINAL2 BIRD_GRACKLE2 GRACKLE_MAN2 GIANT_GRACKLE2 BIRD_ORIOLE2 -ORIOLE_MAN2 GIANT_ORIOLE2BIRD_RW_BLACKBIRD2RW_BLACKBIRD_MAN2GIANT_RW_BLACKBIRD2 BIRD_PENGUIN2BIRD_PENGUIN_LITTLE2BIRD_PENGUIN_EMPEROR2 PENGUIN MAN2BIRD_PENGUIN_GIANT2BIRD_FALCON_PEREGRINE2PEREGRINE FALCON MAN2GIANT PEREGRINE FALCON2 BIRD_KIWI2KIWI MAN2BIRD_KIWI_GIANT2 BIRD_OSTRICH2 OSTRICH MAN2BIRD_OSTRICH_GIANT2 BIRD_CROW2CROW_MAN2 -GIANT_CROW2 -BIRD_RAVEN2 RAVEN_MAN2 GIANT_RAVEN2BIRD_CASSOWARY2 CASSOWARY_MAN2GIANT_CASSOWARY2BIRD_KEA2KEA_MAN2 GIANT_KEA2BIRD_OWL_SNOWY2 SNOWY_OWL_MAN2GIANT_SNOWY_OWL2SPARROW2 SPARROW_MAN2 GIANT_SPARROW2BIRD_STORK_WHITE2WHITE_STORK_MAN2GIANT_WHITE_STORK2 BIRD_LOON2LOON_MAN2 -GIANT_LOON2 BIRD_OWL_BARN2 BARN_OWL_MAN2GIANT_BARN_OWL2 BIRD_PARAKEET2 PARAKEET_MAN2GIANT_PARAKEET2 BIRD_KAKAPO2 -KAKAPO_MAN2 GIANT_KAKAPO2BIRD_PARROT_GREY2GREY_PARROT_MAN2GIANT_GREY_PARROT2 BIRD_PUFFIN2 -PUFFIN_MAN2 GIANT_PUFFIN2 BIRD_SWAN2SWAN_MAN2 -GIANT_SWAN2 BIRD_LORIKEET2 LORIKEET_MAN2GIANT_LORIKEET2 BIRD_WREN2WREN_MAN2 -GIANT_WREN2 BIRD_OSPREY2 -OSPREY_MAN2 GIANT_OSPREY2BIRD_EMU2EMU_MAN2 GIANT_EMU2BIRD_COCKATIEL2 COCKATIEL_MAN2GIANT_COCKATIEL2BIRD_LOVEBIRD_PEACH-FACED2PEACH-FACED_LOVEBIRD_MAN2GIANT_PEACH-FACED_LOVEBIRD2 BIRD_MAGPIE2 -MAGPIE_MAN2 GIANT_MAGPIE2 BIRD_KESTREL2 KESTREL_MAN2 GIANT_KESTREL2BIRD_ALBATROSS2 ALBATROSS_MAN2GIANT_ALBATROSS2BIRD_OWL_GREAT_HORNED2GREAT_HORNED_OWL_MAN2GIANT_GREAT_HORNED_OWL2 -BIRD_EAGLE2 EAGLE_MAN2 GIANT_EAGLE2 BIRD_HORNBILL2 HORNBILL_MAN2GIANT_HORNBILL2BIRD_LOVEBIRD_MASKED2MASKED_LOVEBIRD_MAN2GIANT_MASKED_LOVEBIRD2 BIRD_BUSHTIT2 BUSHTIT_MAN2 GIANT_BUSHTIT2 DAMSELFLY2 DAMSELFLY_MAN2GIANT_DAMSELFLY2MOTH2MOTH_MAN2 -GIANT_MOTH2 GRASSHOPPER2GRASSHOPPER_MAN2GIANT_GRASSHOPPER2 BARK_SCORPION2BARK_SCORPION_MAN2GIANT_BARK_SCORPION2MANTIS2 -MANTIS_MAN2 GIANT_MANTIS2TICK2TICK_MAN2 -GIANT_TICK2LOUSE2 LOUSE_MAN2 GIANT_LOUSE2THRIPS2 -THRIPS_MAN2 GIANT_THRIPS2SLUG2SLUG_MAN2 -GIANT_SLUG2MOSQUITO2 MOSQUITO_MAN2GIANT_MOSQUITO2SPIDER_JUMPING2JUMPING_SPIDER_MAN2GIANT_JUMPING_SPIDER2TERMITE2 -MOON_SNAIL2MOON_SNAIL_MAN2GIANT_MOON_SNAIL2SPIDER_BROWN_RECLUSE2BROWN_RECLUSE_SPIDER_MAN2GIANT_BROWN_RECLUSE_SPIDER2SNAIL2 SNAIL_MAN2 GIANT_SNAIL2 GECKO_LEOPARD2LEOPARD_GECKO_MAN2GIANT_LEOPARD_GECKO2DESERT TORTOISE2DESERT_TORTOISE_MAN2GIANT_DESERT_TORTOISE2 GILA_MONSTER2GILA_MONSTER_MAN2GIANT_GILA_MONSTER2DOG2CAT2MULE2DONKEY2HORSE2COW2SHEEP2PIG2GOAT2 BIRD_CHICKEN2CAVY2 BIRD_DUCK2 WATER_BUFFALO2REINDEER2 -BIRD_GOOSE2YAK2LLAMA2ALPACA2BIRD_GUINEAFOWL2BIRD_PEAFOWL_BLUE2 BIRD_TURKEY2RABBIT2CHIMERA2CENTAUR2GRIFFON2FLY2FLY_MAN2 GIANT_FLY2 ROACH_LARGE2 ROACH_MAN2 GIANT_ROACH2BEETLE2 -BEETLE_MAN2 GIANT_BEETLE2ANT2BUTTERFLY_MONARCH2BUTTERFLY_MONARCH_MAN2GIANT_BUTTERFLY_MONARCH2FIREFLY2 FIREFLY_MAN2 GIANT_FIREFLY2 DRAGONFLY2 DRAGONFLY_MAN2GIANT_DRAGONFLY2 HONEY_BEE2 BUMBLEBEE2 GOAT_MOUNTAIN2GOAT_MOUNTAIN_MAN2GIANT_GOAT_MOUNTAIN2 MARMOT_HOARY2MARMOT_HOARY_MAN2GIANT_MARMOT_HOARY2GNOME_MOUNTAIN2 -GNOME_DARK2WALRUS2 -WALRUS_MAN2 GIANT_WALRUS2FISH_LAMPREY_SEA2SHARK_GREAT_WHITE2 SHARK_FRILL2SHARK_SPINY_DOGFISH2SHARK_WOBBEGONG_SPOTTED2 SHARK_WHALE2 SHARK_BASKING2 SHARK_NURSE2SHARK_MAKO_SHORTFIN2SHARK_MAKO_LONGFIN2 SHARK_TIGER2 -SHARK_BULL2SHARK_REEF_BLACKTIP2SHARK_REEF_WHITETIP2 -SHARK_BLUE2SHARK_HAMMERHEAD2 SHARK_ANGEL2FISH_SKATE_COMMON2FISH_RAY_MANTA2 FISH_STINGRAY2FISH_COELACANTH2 FISH_STURGEON2FISH_CONGER_EEL2 FISH_MILKFISH2FISH_COD2 FISH_OPAH2FISH_GROUPER_GIANT2 FISH_BLUEFISH2FISH_SUNFISH_OCEAN2FISH_SWORDFISH2 FISH_MARLIN2 FISH_HALIBUT2FISH_BARRACUDA_GREAT2FISH_TUNA_BLUEFIN2NARWHAL2 NARWHAL MAN2NARWHAL, GIANT2HIPPO2 HIPPO_MAN2 GIANT_HIPPO2FISH_GAR_LONGNOSE2 FISH_CARP2FISH_TIGERFISH2 FISH_PIKE2PLATYPUS2 PLATYPUS MAN2PLATYPUS, GIANT2 BEAR_GRIZZLY2BEAR_GRIZZLY_MAN2GIANT_BEAR_GRIZZLY2 -BEAR_BLACK2BEAR_BLACK_MAN2GIANT_BEAR_BLACK2DEER2DEER_MAN2 -GIANT_DEER2FOX2FOX_MAN2 GIANT_FOX2RACCOON2 RACCOON_MAN2 GIANT_RACCOON2MACAQUE_RHESUS2MACAQUE_RHESUS_MAN2GIANT_MACAQUE_RHESUS2COUGAR2 -COUGAR_MAN2 GIANT_COUGAR2WOLF2WOLF_MAN2 -GIANT_WOLF2 GROUNDHOG2 GROUNDHOG_MAN2GIANT_GROUNDHOG2 ALLIGATOR2 ALLIGATOR_MAN2GIANT_ALLIGATOR2 BIRD_BUZZARD2 BUZZARD_MAN2 GIANT_BUZZARD2PANDA2PANDA, GIGANTIC2 PANDA MAN2CAPYBARA2CAPYBARA, GIANT2 CAPYBARA MAN2BADGER2 -BADGER MAN2 BADGER, GIANT2MOOSE2 MOOSE MAN2 MOOSE, GIANT2 RED PANDA2 RED PANDA MAN2RED PANDA, GIANT2ELEPHANT2 ELEPHANT_MAN2GIANT_ELEPHANT2WARTHOG2 WARTHOG_MAN2 GIANT_WARTHOG2LION2LION_MAN2 -GIANT_LION2LEOPARD2 LEOPARD_MAN2 GIANT_LEOPARD2JAGUAR2 -JAGUAR_MAN2 GIANT_JAGUAR2TIGER2 TIGER_MAN2 GIANT_TIGER2CHEETAH2 CHEETAH_MAN2 GIANT_CHEETAH2GAZELLE2 GAZELLE_MAN2 GIANT_GAZELLE2MANDRILL2 MANDRILL_MAN2GIANT_MANDRILL2 -CHIMPANZEE2BONOBO2GORILLA2 ORANGUTAN2GIBBON_SIAMANG2GIBBON_WHITE_HANDED2GIBBON_BLACK_HANDED2 GIBBON_GRAY2GIBBON_SILVERY2GIBBON_PILEATED2 GIBBON_BILOU2GIBBON_WHITE_BROWED2GIBBON_BLACK_CRESTED2 CAMEL_1_HUMP2CAMEL_1_HUMP_MAN2GIANT_CAMEL_1_HUMP2 CAMEL_2_HUMP2CAMEL_2_HUMP_MAN2GIANT_CAMEL_2_HUMP2CROCODILE_SALTWATER2CROCODILE_SALTWATER_MAN2GIANT_CROCODILE_SALTWATER2 BIRD_VULTURE2 VULTURE_MAN2 GIANT_VULTURE2 -RHINOCEROS2RHINOCEROS_MAN2GIANT_RHINOCEROS2GIRAFFE2 GIRAFFE_MAN2 GIANT_GIRAFFE2 HONEY BADGER2HONEY BADGER MAN2HONEY BADGER, GIANT2GIANT TORTOISE2GIANT TORTOISE MAN2GIGANTIC TORTOISE2 ARMADILLO2 ARMADILLO MAN2ARMADILLO, GIANT2MUSKOX2 -MUSKOX_MAN2 GIANT_MUSKOX2ELK2ELK_MAN2 GIANT_ELK2 -BEAR_POLAR2BEAR_POLAR_MAN2GIANT_BEAR_POLAR2 WOLVERINE2 WOLVERINE_MAN2GIANT_WOLVERINE2 -CHINCHILLA2CHINCHILLA_MAN2GIANT_CHINCHILLA2 FLOATING_GUTS2DRUNIAN2 CREEPING_EYE2VORACIOUS_CAVE_CRAWLER2BLIND_CAVE_OGRE2 -CAP_HOPPER2 -MAGMA_CRAB2CRUNDLE2 HUNGRY_HEAD2 -FLESH_BALL2ELK_BIRD2 HELMET_SNAKE2GREEN_DEVOURER2RUTHERER2CREEPY_CRAWLER2DRALTHA2GIANT_EARTHWORM2 BLOOD_MAN2BUGBAT2MANERA2 -MOLEMARIAN2JABBERER2 POND_GRABBER2BLIND_CAVE_BEAR2 CAVE_DRAGON2REACHER2ELEMENTMAN_GABBRO2GORLAK2 CAVE_FLOATER2PLUMP_HELMET_MAN2 CAVE_BLOB2ELEMENTMAN_AMETHYST2OCTOPUS2 OCTOPUS_MAN2 GIANT_OCTOPUS2CRAB2CRAB_MAN2 -GIANT_CRAB2 LEOPARD_SEAL2LEOPARD_SEAL_MAN2GIANT_LEOPARD_SEAL2 -CUTTLEFISH2CUTTLEFISH_MAN2GIANT_CUTTLEFISH2ORCA2ORCA_MAN2 -GIANT_ORCA2SPONGE2 -SPONGE_MAN2 GIANT_SPONGE2HORSESHOE_CRAB2HORSESHOE_CRAB_MAN2GIANT_HORSESHOE_CRAB2 SPERM_WHALE2SPERM_WHALE_MAN2GIANT_SPERM_WHALE2 ELEPHANT_SEAL2ELEPHANT_SEAL_MAN2GIANT_ELEPHANT_SEAL2 HARP_SEAL2 HARP_SEAL_MAN2GIANT_HARP_SEAL2NAUTILUS2 NAUTILUS_MAN2GIANT_NAUTILUS2 FOXSQUIRREL2 MOGHOPPER2 RAT_DEMON2WAMBLER_FLUFFY2LIZARD_RHINO_TWO_LEGGED2 WORM_KNUCKLE2SPIDER_PHANTOM2 FLY_ACORN2 -GNAT_BLOOD2LIZARD2 -LIZARD_MAN2 GIANT_LIZARD2SKINK2 SKINK_MAN2 GIANT_SKINK2 CHAMELEON2 CHAMELEON_MAN2GIANT_CHAMELEON2ANOLE2 ANOLE_MAN2 GIANT_ANOLE2IGUANA2 -IGUANA_MAN2 GIANT_IGUANA2 RIVER OTTER2 SEA OTTER2 OTTER_MAN2 GIANT_OTTER2SNAPPING TURTLE2ALLIGATOR SNAPPING TURTLE2SNAPPING_TURTLE_MAN2GIANT_SNAPPING_TURTLE2BEAVER2 -BEAVER_MAN2 GIANT_BEAVER2LEECH2 LEECH_MAN2 GIANT_LEECH2AXOLOTL2 AXOLOTL_MAN2 GIANT_AXOLOTL2MINK2MINK_MAN2 -GIANT_MINK2 POND_TURTLE2POND_TURTLE_MAN2GIANT_POND_TURTLE2RAT2RAT_MAN2 SQUIRREL_GRAY2SQUIRREL_GRAY_MAN2GIANT_SQUIRREL_GRAY2 SQUIRREL_RED2SQUIRREL_RED_MAN2GIANT_SQUIRREL_RED2CHIPMUNK2 CHIPMUNK_MAN2GIANT_CHIPMUNK2HAMSTER2 HAMSTER_MAN2 GIANT_HAMSTER2HEDGEHOG2 HEDGEHOG_MAN2GIANT_HEDGEHOG2SQUIRREL_FLYING2FLYING_SQUIRREL_MAN2GIANT_FLYING_SQUIRREL2MUSSEL2OYSTER2 FISH_SALMON2FISH_CLOWNFISH2 FISH_HAGFISH2FISH_LAMPREY_BROOK2 FISH_RAY_BAT2FISH_RAY_THORNBACK2FISH_RATFISH_SPOTTED2 FISH_HERRING2 FISH_SHAD2 FISH_ANCHOVY2FISH_TROUT_STEELHEAD2 FISH_HAKE2 FISH_SEAHORSE2 FISH_GLASSEYE2FISH_PUFFER_WHITE_SPOTTED2 FISH_SOLE2 FISH_FLOUNDER2 FISH_MACKEREL2JELLYFISH_SEA_NETTLE2SQUID2 SQUID MAN2GIGANTIC SQUID2 FISH_LUNGFISH2FISH_LOACH_CLOWN2FISH_BULLHEAD_BROWN2FISH_BULLHEAD_YELLOW2FISH_BULLHEAD_BLACK2FISH_KNIFEFISH_BANDED2 FISH_CHAR2FISH_TROUT_RAINBOW2FISH_MOLLY_SAILFIN2 -FISH_GUPPY2 -FISH_PERCH2DWARF2HUMAN2ELF2GOBLIN2KOBOLD2GREMLIN2TROLL2OGRE2UNICORN2DRAGON2SATYR2COLOSSUS_BRONZE2GIANT2CYCLOPS2ETTIN2MINOTAUR2YETI2 SASQUATCH2 BLIZZARD_MAN2WOLF_ICE2FAIRY2PIXIE2BEAK_DOG2 GRIMELING2 BLENDEC_FOUL2 STRANGLER2 NIGHTWING2HARPY2HYDRA2 MERPERSON2 SEA_SERPENT2 SEA_MONSTER2BIRD_ROC2CROCODILE_CAVE2TOAD_GIANT_CAVE2 OLM_GIANT2 BAT_GIANT2 RAT_GIANT2 RAT_LARGE2MOLE_DOG_NAKED2 -TROGLODYTE2 -MOLE_GIANT2IMP_FIRE2SPIDER_CAVE_GIANT2 SPIDER_CAVE2 FISH_CAVE2 CAVE_FISH_MAN2 LOBSTER_CAVE2 -SNAKE_FIRE2OLM2OLM_MAN2BAT2BAT_MAN2MAGGOT_PURRING2ELEMENTMAN_FIRE2ELEMENTMAN_MAGMA2ELEMENTMAN_IRON2ELEMENTMAN_MUD2BIRD_SWALLOW_CAVE2CAVE_SWALLOW_MAN2BIRD_SWALLOW_CAVE_GIANT2 AMPHIBIAN_MAN2 REPTILE_MAN2 SERPENT_MAN2ANT_MAN2 -RODENT MAN2 WILD_BOAR2 WILD_BOAR_MAN2GIANT_WILD_BOAR2COYOTE2 -COYOTE_MAN2 GIANT_COYOTE2KANGAROO2 KANGAROO_MAN2GIANT_KANGAROO2KOALA2 KOALA_MAN2 GIANT_KOALA2ADDER2 ADDER_MAN2 GIANT_ADDER2ECHIDNA2 ECHIDNA_MAN2 GIANT_ECHIDNA2 PORCUPINE2 PORCUPINE_MAN2GIANT_PORCUPINE2 KINGSNAKE2 KINGSNAKE_MAN2GIANT_KINGSNAKE2 GRAY_LANGUR2GRAY_LANGUR_MAN2GIANT_GRAY_LANGUR2BOBCAT2 -BOBCAT_MAN2 GIANT_BOBCAT2SKUNK2 SKUNK_MAN2 GIANT_SKUNK2GREEN_TREE_FROG2GREEN_TREE_FROG_MAN2GIANT_GREEN_TREE_FROG2HARE2HARE_MAN2 -GIANT_HARE2 RATTLESNAKE2RATTLESNAKE_MAN2GIANT_RATTLESNAKE2WEASEL2 -WEASEL_MAN2 GIANT_WEASEL2COPPERHEAD_SNAKE2COPPERHEAD_SNAKE_MAN2GIANT_COPPERHEAD_SNAKE2IBEX2IBEX_MAN2 -GIANT_IBEX2WOMBAT2 -WOMBAT_MAN2 GIANT_WOMBAT2DINGO2 DINGO_MAN2 GIANT_DINGO2COATI2 COATI_MAN2 GIANT_COATI2OPOSSUM2 OPOSSUM_MAN2 GIANT_OPOSSUM2MONGOOSE2 MONGOOSE_MAN2GIANT_MONGOOSE2HYENA2 HYENA_MAN2 GIANT_HYENA2ANACONDA2 ANACONDA_MAN2GIANT_ANACONDA2MONITOR_LIZARD2MONITOR_LIZARD_MAN2GIANT_MONITOR_LIZARD2 -KING_COBRA2KING_COBRA_MAN2GIANT_KING_COBRA2OCELOT2 -OCELOT_MAN2 GIANT_OCELOT2JACKAL2 -JACKAL_MAN2 GIANT_JACKAL2CAPUCHIN2 CAPUCHIN_MAN2GIANT_CAPUCHIN2SLOTH2 SLOTH_MAN2 GIANT_SLOTH2 SPIDER_MONKEY2SPIDER_MONKEY_MAN2GIANT_SPIDER_MONKEY2PANGOLIN2 PANGOLIN_MAN2GIANT_PANGOLIN2 BLACK_MAMBA2BLACK_MAMBA_MAN2GIANT_BLACK_MAMBA2 -BEAR_SLOTH2SLOTH_BEAR_MAN2GIANT_SLOTH_BEAR2AYE-AYE2 AYE-AYE_MAN2 GIANT_AYE-AYE2 -BUSHMASTER2BUSHMASTER_MAN2GIANT_BUSHMASTER2PYTHON2 -PYTHON_MAN2 GIANT_PYTHON2TAPIR2 TAPIR_MAN2 GIANT_TAPIR2IMPALA2 -IMPALA_MAN2 GIANT_IMPALA2AARDVARK2 AARDVARK_MAN2GIANT_AARDVARK2 LION_TAMARIN2LION_TAMARIN_MAN2GIANT_LION_TAMARIN2STOAT2 STOAT_MAN2 GIANT_STOAT2LYNX2LYNX_MAN2 -GIANT_LYNX:SHEEP:LLAMA:ALPACA:TROLLBTOADBTOAD_MANB -GIANT_TOADBWORMBWORM_MANB BIRD_BLUEJAYB BLUEJAY_MANB GIANT_BLUEJAYB BIRD_CARDINALB CARDINAL_MANBGIANT_CARDINALB BIRD_GRACKLEB GRACKLE_MANB GIANT_GRACKLEB BIRD_ORIOLEB -ORIOLE_MANB GIANT_ORIOLEBBIRD_RW_BLACKBIRDBRW_BLACKBIRD_MANBGIANT_RW_BLACKBIRDB BIRD_PENGUINBBIRD_PENGUIN_LITTLEBBIRD_PENGUIN_EMPERORB PENGUIN MANBBIRD_PENGUIN_GIANTBBIRD_FALCON_PEREGRINEBPEREGRINE FALCON MANBGIANT PEREGRINE FALCONB BIRD_KIWIBKIWI MANBBIRD_KIWI_GIANTB BIRD_OSTRICHB OSTRICH MANBBIRD_OSTRICH_GIANTB BIRD_CROWBCROW_MANB -GIANT_CROWB -BIRD_RAVENB RAVEN_MANB GIANT_RAVENBBIRD_CASSOWARYB CASSOWARY_MANBGIANT_CASSOWARYBBIRD_KEABKEA_MANB GIANT_KEABBIRD_OWL_SNOWYB SNOWY_OWL_MANBGIANT_SNOWY_OWLBSPARROWB SPARROW_MANB GIANT_SPARROWBBIRD_STORK_WHITEBWHITE_STORK_MANBGIANT_WHITE_STORKB BIRD_LOONBLOON_MANB -GIANT_LOONB BIRD_OWL_BARNB BARN_OWL_MANBGIANT_BARN_OWLB BIRD_PARAKEETB PARAKEET_MANBGIANT_PARAKEETB BIRD_KAKAPOB -KAKAPO_MANB GIANT_KAKAPOBBIRD_PARROT_GREYBGREY_PARROT_MANBGIANT_GREY_PARROTB BIRD_PUFFINB -PUFFIN_MANB GIANT_PUFFINB BIRD_SWANBSWAN_MANB -GIANT_SWANB BIRD_LORIKEETB LORIKEET_MANBGIANT_LORIKEETB BIRD_WRENBWREN_MANB -GIANT_WRENB BIRD_OSPREYB -OSPREY_MANB GIANT_OSPREYBBIRD_EMUBEMU_MANB GIANT_EMUBBIRD_COCKATIELB COCKATIEL_MANBGIANT_COCKATIELBBIRD_LOVEBIRD_PEACH-FACEDBPEACH-FACED_LOVEBIRD_MANBGIANT_PEACH-FACED_LOVEBIRDB BIRD_MAGPIEB -MAGPIE_MANB GIANT_MAGPIEB BIRD_KESTRELB KESTREL_MANB GIANT_KESTRELBBIRD_ALBATROSSB ALBATROSS_MANBGIANT_ALBATROSSBBIRD_OWL_GREAT_HORNEDBGREAT_HORNED_OWL_MANBGIANT_GREAT_HORNED_OWLB -BIRD_EAGLEB EAGLE_MANB GIANT_EAGLEB BIRD_HORNBILLB HORNBILL_MANBGIANT_HORNBILLBBIRD_LOVEBIRD_MASKEDBMASKED_LOVEBIRD_MANBGIANT_MASKED_LOVEBIRDB BIRD_BUSHTITB BUSHTIT_MANB GIANT_BUSHTITB DAMSELFLYB DAMSELFLY_MANBGIANT_DAMSELFLYBMOTHBMOTH_MANB -GIANT_MOTHB GRASSHOPPERBGRASSHOPPER_MANBGIANT_GRASSHOPPERB BARK_SCORPIONBBARK_SCORPION_MANBGIANT_BARK_SCORPIONBMANTISB -MANTIS_MANB GIANT_MANTISBTICKBTICK_MANB -GIANT_TICKBLOUSEB LOUSE_MANB GIANT_LOUSEBTHRIPSB -THRIPS_MANB GIANT_THRIPSBSLUGBSLUG_MANB -GIANT_SLUGBMOSQUITOB MOSQUITO_MANBGIANT_MOSQUITOBSPIDER_JUMPINGBJUMPING_SPIDER_MANBGIANT_JUMPING_SPIDERBTERMITEB -MOON_SNAILBMOON_SNAIL_MANBGIANT_MOON_SNAILBSPIDER_BROWN_RECLUSEBBROWN_RECLUSE_SPIDER_MANBGIANT_BROWN_RECLUSE_SPIDERBSNAILB SNAIL_MANB GIANT_SNAILB GECKO_LEOPARDBLEOPARD_GECKO_MANBGIANT_LEOPARD_GECKOBDESERT TORTOISEBDESERT_TORTOISE_MANBGIANT_DESERT_TORTOISEB GILA_MONSTERBGILA_MONSTER_MANBGIANT_GILA_MONSTERBDOGBCATBMULEBDONKEYBHORSEBCOWBSHEEPBPIGBGOATB BIRD_CHICKENBCAVYB BIRD_DUCKB WATER_BUFFALOBREINDEERB -BIRD_GOOSEBYAKBLLAMABALPACABBIRD_GUINEAFOWLBBIRD_PEAFOWL_BLUEB BIRD_TURKEYBRABBITBCHIMERABCENTAURBGRIFFONBFLYBFLY_MANB GIANT_FLYB ROACH_LARGEB ROACH_MANB GIANT_ROACHBBEETLEB -BEETLE_MANB GIANT_BEETLEBANTBBUTTERFLY_MONARCHBBUTTERFLY_MONARCH_MANBGIANT_BUTTERFLY_MONARCHBFIREFLYB FIREFLY_MANB GIANT_FIREFLYB DRAGONFLYB DRAGONFLY_MANBGIANT_DRAGONFLYB HONEY_BEEB BUMBLEBEEB GOAT_MOUNTAINBGOAT_MOUNTAIN_MANBGIANT_GOAT_MOUNTAINB MARMOT_HOARYBMARMOT_HOARY_MANBGIANT_MARMOT_HOARYBGNOME_MOUNTAINB -GNOME_DARKBWALRUSB -WALRUS_MANB GIANT_WALRUSBFISH_LAMPREY_SEABSHARK_GREAT_WHITEB SHARK_FRILLBSHARK_SPINY_DOGFISHBSHARK_WOBBEGONG_SPOTTEDB SHARK_WHALEB SHARK_BASKINGB SHARK_NURSEBSHARK_MAKO_SHORTFINBSHARK_MAKO_LONGFINB SHARK_TIGERB -SHARK_BULLBSHARK_REEF_BLACKTIPBSHARK_REEF_WHITETIPB -SHARK_BLUEBSHARK_HAMMERHEADB SHARK_ANGELBFISH_SKATE_COMMONBFISH_RAY_MANTAB FISH_STINGRAYBFISH_COELACANTHB FISH_STURGEONBFISH_CONGER_EELB FISH_MILKFISHBFISH_CODB FISH_OPAHBFISH_GROUPER_GIANTB FISH_BLUEFISHBFISH_SUNFISH_OCEANBFISH_SWORDFISHB FISH_MARLINB FISH_HALIBUTBFISH_BARRACUDA_GREATBFISH_TUNA_BLUEFINBNARWHALB NARWHAL MANBNARWHAL, GIANTBHIPPOB HIPPO_MANB GIANT_HIPPOBFISH_GAR_LONGNOSEB FISH_CARPBFISH_TIGERFISHB FISH_PIKEBPLATYPUSB PLATYPUS MANBPLATYPUS, GIANTB BEAR_GRIZZLYBBEAR_GRIZZLY_MANBGIANT_BEAR_GRIZZLYB -BEAR_BLACKBBEAR_BLACK_MANBGIANT_BEAR_BLACKBDEERBDEER_MANB -GIANT_DEERBFOXBFOX_MANB GIANT_FOXBRACCOONB RACCOON_MANB GIANT_RACCOONBMACAQUE_RHESUSBMACAQUE_RHESUS_MANBGIANT_MACAQUE_RHESUSBCOUGARB -COUGAR_MANB GIANT_COUGARBWOLFBWOLF_MANB -GIANT_WOLFB GROUNDHOGB GROUNDHOG_MANBGIANT_GROUNDHOGB ALLIGATORB ALLIGATOR_MANBGIANT_ALLIGATORB BIRD_BUZZARDB BUZZARD_MANB GIANT_BUZZARDBPANDABPANDA, GIGANTICB PANDA MANBCAPYBARABCAPYBARA, GIANTB CAPYBARA MANBBADGERB -BADGER MANB BADGER, GIANTBMOOSEB MOOSE MANB MOOSE, GIANTB RED PANDAB RED PANDA MANBRED PANDA, GIANTBELEPHANTB ELEPHANT_MANBGIANT_ELEPHANTBWARTHOGB WARTHOG_MANB GIANT_WARTHOGBLIONBLION_MANB -GIANT_LIONBLEOPARDB LEOPARD_MANB GIANT_LEOPARDBJAGUARB -JAGUAR_MANB GIANT_JAGUARBTIGERB TIGER_MANB GIANT_TIGERBCHEETAHB CHEETAH_MANB GIANT_CHEETAHBGAZELLEB GAZELLE_MANB GIANT_GAZELLEBMANDRILLB MANDRILL_MANBGIANT_MANDRILLB -CHIMPANZEEBBONOBOBGORILLAB ORANGUTANBGIBBON_SIAMANGBGIBBON_WHITE_HANDEDBGIBBON_BLACK_HANDEDB GIBBON_GRAYBGIBBON_SILVERYBGIBBON_PILEATEDB GIBBON_BILOUBGIBBON_WHITE_BROWEDBGIBBON_BLACK_CRESTEDB CAMEL_1_HUMPBCAMEL_1_HUMP_MANBGIANT_CAMEL_1_HUMPB CAMEL_2_HUMPBCAMEL_2_HUMP_MANBGIANT_CAMEL_2_HUMPBCROCODILE_SALTWATERBCROCODILE_SALTWATER_MANBGIANT_CROCODILE_SALTWATERB BIRD_VULTUREB VULTURE_MANB GIANT_VULTUREB -RHINOCEROSBRHINOCEROS_MANBGIANT_RHINOCEROSBGIRAFFEB GIRAFFE_MANB GIANT_GIRAFFEB HONEY BADGERBHONEY BADGER MANBHONEY BADGER, GIANTBGIANT TORTOISEBGIANT TORTOISE MANBGIGANTIC TORTOISEB ARMADILLOB ARMADILLO MANBARMADILLO, GIANTBMUSKOXB -MUSKOX_MANB GIANT_MUSKOXBELKBELK_MANB GIANT_ELKB -BEAR_POLARBBEAR_POLAR_MANBGIANT_BEAR_POLARB WOLVERINEB WOLVERINE_MANBGIANT_WOLVERINEB -CHINCHILLABCHINCHILLA_MANBGIANT_CHINCHILLAB FLOATING_GUTSBDRUNIANB CREEPING_EYEBVORACIOUS_CAVE_CRAWLERBBLIND_CAVE_OGREB -CAP_HOPPERB -MAGMA_CRABBCRUNDLEB HUNGRY_HEADB -FLESH_BALLBELK_BIRDB HELMET_SNAKEBGREEN_DEVOURERBRUTHERERBCREEPY_CRAWLERBDRALTHABGIANT_EARTHWORMB BLOOD_MANBBUGBATBMANERAB -MOLEMARIANBJABBERERB POND_GRABBERBBLIND_CAVE_BEARB CAVE_DRAGONBREACHERBELEMENTMAN_GABBROBGORLAKB CAVE_FLOATERBPLUMP_HELMET_MANB CAVE_BLOBBELEMENTMAN_AMETHYSTBOCTOPUSB OCTOPUS_MANB GIANT_OCTOPUSBCRABBCRAB_MANB -GIANT_CRABB LEOPARD_SEALBLEOPARD_SEAL_MANBGIANT_LEOPARD_SEALB -CUTTLEFISHBCUTTLEFISH_MANBGIANT_CUTTLEFISHBORCABORCA_MANB -GIANT_ORCABSPONGEB -SPONGE_MANB GIANT_SPONGEBHORSESHOE_CRABBHORSESHOE_CRAB_MANBGIANT_HORSESHOE_CRABB SPERM_WHALEBSPERM_WHALE_MANBGIANT_SPERM_WHALEB ELEPHANT_SEALBELEPHANT_SEAL_MANBGIANT_ELEPHANT_SEALB HARP_SEALB HARP_SEAL_MANBGIANT_HARP_SEALBNAUTILUSB NAUTILUS_MANBGIANT_NAUTILUSB FOXSQUIRRELB MOGHOPPERB RAT_DEMONBWAMBLER_FLUFFYBLIZARD_RHINO_TWO_LEGGEDB WORM_KNUCKLEBSPIDER_PHANTOMB FLY_ACORNB -GNAT_BLOODBLIZARDB -LIZARD_MANB GIANT_LIZARDBSKINKB SKINK_MANB GIANT_SKINKB CHAMELEONB CHAMELEON_MANBGIANT_CHAMELEONBANOLEB ANOLE_MANB GIANT_ANOLEBIGUANAB -IGUANA_MANB GIANT_IGUANAB RIVER OTTERB SEA OTTERB OTTER_MANB GIANT_OTTERBSNAPPING TURTLEBALLIGATOR SNAPPING TURTLEBSNAPPING_TURTLE_MANBGIANT_SNAPPING_TURTLEBBEAVERB -BEAVER_MANB GIANT_BEAVERBLEECHB LEECH_MANB GIANT_LEECHBAXOLOTLB AXOLOTL_MANB GIANT_AXOLOTLBMINKBMINK_MANB -GIANT_MINKB POND_TURTLEBPOND_TURTLE_MANBGIANT_POND_TURTLEBRATBRAT_MANB SQUIRREL_GRAYBSQUIRREL_GRAY_MANBGIANT_SQUIRREL_GRAYB SQUIRREL_REDBSQUIRREL_RED_MANBGIANT_SQUIRREL_REDBCHIPMUNKB CHIPMUNK_MANBGIANT_CHIPMUNKBHAMSTERB HAMSTER_MANB GIANT_HAMSTERBHEDGEHOGB HEDGEHOG_MANBGIANT_HEDGEHOGBSQUIRREL_FLYINGBFLYING_SQUIRREL_MANBGIANT_FLYING_SQUIRRELBMUSSELBOYSTERB FISH_SALMONBFISH_CLOWNFISHB FISH_HAGFISHBFISH_LAMPREY_BROOKB FISH_RAY_BATBFISH_RAY_THORNBACKBFISH_RATFISH_SPOTTEDB FISH_HERRINGB FISH_SHADB FISH_ANCHOVYBFISH_TROUT_STEELHEADB FISH_HAKEB FISH_SEAHORSEB FISH_GLASSEYEBFISH_PUFFER_WHITE_SPOTTEDB FISH_SOLEB FISH_FLOUNDERB FISH_MACKERELBJELLYFISH_SEA_NETTLEBSQUIDB SQUID MANBGIGANTIC SQUIDB FISH_LUNGFISHBFISH_LOACH_CLOWNBFISH_BULLHEAD_BROWNBFISH_BULLHEAD_YELLOWBFISH_BULLHEAD_BLACKBFISH_KNIFEFISH_BANDEDB FISH_CHARBFISH_TROUT_RAINBOWBFISH_MOLLY_SAILFINB -FISH_GUPPYB -FISH_PERCHBDWARFBHUMANBELFBGOBLINBKOBOLDBGREMLINBTROLLBOGREBUNICORNBDRAGONBSATYRBCOLOSSUS_BRONZEBGIANTBCYCLOPSBETTINBMINOTAURBYETIB SASQUATCHB BLIZZARD_MANBWOLF_ICEBFAIRYBPIXIEBBEAK_DOGB GRIMELINGB BLENDEC_FOULB STRANGLERB NIGHTWINGBHARPYBHYDRAB MERPERSONB SEA_SERPENTB SEA_MONSTERBBIRD_ROCBCROCODILE_CAVEBTOAD_GIANT_CAVEB OLM_GIANTB BAT_GIANTB RAT_GIANTB RAT_LARGEBMOLE_DOG_NAKEDB -TROGLODYTEB -MOLE_GIANTBIMP_FIREBSPIDER_CAVE_GIANTB SPIDER_CAVEB FISH_CAVEB CAVE_FISH_MANB LOBSTER_CAVEB -SNAKE_FIREBOLMBOLM_MANBBATBBAT_MANBMAGGOT_PURRINGBELEMENTMAN_FIREBELEMENTMAN_MAGMABELEMENTMAN_IRONBELEMENTMAN_MUDBBIRD_SWALLOW_CAVEBCAVE_SWALLOW_MANBBIRD_SWALLOW_CAVE_GIANTB AMPHIBIAN_MANB REPTILE_MANB SERPENT_MANBANT_MANB -RODENT MANB WILD_BOARB WILD_BOAR_MANBGIANT_WILD_BOARBCOYOTEB -COYOTE_MANB GIANT_COYOTEBKANGAROOB KANGAROO_MANBGIANT_KANGAROOBKOALAB KOALA_MANB GIANT_KOALABADDERB ADDER_MANB GIANT_ADDERBECHIDNAB ECHIDNA_MANB GIANT_ECHIDNAB PORCUPINEB PORCUPINE_MANBGIANT_PORCUPINEB KINGSNAKEB KINGSNAKE_MANBGIANT_KINGSNAKEB GRAY_LANGURBGRAY_LANGUR_MANBGIANT_GRAY_LANGURBBOBCATB -BOBCAT_MANB GIANT_BOBCATBSKUNKB SKUNK_MANB GIANT_SKUNKBGREEN_TREE_FROGBGREEN_TREE_FROG_MANBGIANT_GREEN_TREE_FROGBHAREBHARE_MANB -GIANT_HAREB RATTLESNAKEBRATTLESNAKE_MANBGIANT_RATTLESNAKEBWEASELB -WEASEL_MANB GIANT_WEASELBCOPPERHEAD_SNAKEBCOPPERHEAD_SNAKE_MANBGIANT_COPPERHEAD_SNAKEBIBEXBIBEX_MANB -GIANT_IBEXBWOMBATB -WOMBAT_MANB GIANT_WOMBATBDINGOB DINGO_MANB GIANT_DINGOBCOATIB COATI_MANB GIANT_COATIBOPOSSUMB OPOSSUM_MANB GIANT_OPOSSUMBMONGOOSEB MONGOOSE_MANBGIANT_MONGOOSEBHYENAB HYENA_MANB GIANT_HYENABANACONDAB ANACONDA_MANBGIANT_ANACONDABMONITOR_LIZARDBMONITOR_LIZARD_MANBGIANT_MONITOR_LIZARDB -KING_COBRABKING_COBRA_MANBGIANT_KING_COBRABOCELOTB -OCELOT_MANB GIANT_OCELOTBJACKALB -JACKAL_MANB GIANT_JACKALBCAPUCHINB CAPUCHIN_MANBGIANT_CAPUCHINBSLOTHB SLOTH_MANB GIANT_SLOTHB SPIDER_MONKEYBSPIDER_MONKEY_MANBGIANT_SPIDER_MONKEYBPANGOLINB PANGOLIN_MANBGIANT_PANGOLINB BLACK_MAMBABBLACK_MAMBA_MANBGIANT_BLACK_MAMBAB -BEAR_SLOTHBSLOTH_BEAR_MANBGIANT_SLOTH_BEARBAYE-AYEB AYE-AYE_MANB GIANT_AYE-AYEB -BUSHMASTERBBUSHMASTER_MANBGIANT_BUSHMASTERBPYTHONB -PYTHON_MANB GIANT_PYTHONBTAPIRB TAPIR_MANB GIANT_TAPIRBIMPALAB -IMPALA_MANB GIANT_IMPALABAARDVARKB AARDVARK_MANBGIANT_AARDVARKB LION_TAMARINBLION_TAMARIN_MANBGIANT_LION_TAMARINBSTOATB STOAT_MANB GIANT_STOATBLYNXBLYNX_MANB -GIANT_LYNXJTOADJTOAD_MANJ -GIANT_TOADJWORMJWORM_MANJ BIRD_BLUEJAYJ BLUEJAY_MANJ GIANT_BLUEJAYJ BIRD_CARDINALJ CARDINAL_MANJGIANT_CARDINALJ BIRD_GRACKLEJ GRACKLE_MANJ GIANT_GRACKLEJ BIRD_ORIOLEJ -ORIOLE_MANJ GIANT_ORIOLEJBIRD_RW_BLACKBIRDJRW_BLACKBIRD_MANJGIANT_RW_BLACKBIRDJ BIRD_PENGUINJBIRD_PENGUIN_LITTLEJBIRD_PENGUIN_EMPERORJ PENGUIN MANJBIRD_PENGUIN_GIANTJBIRD_FALCON_PEREGRINEJPEREGRINE FALCON MANJGIANT PEREGRINE FALCONJ BIRD_KIWIJKIWI MANJBIRD_KIWI_GIANTJ BIRD_OSTRICHJ OSTRICH MANJBIRD_OSTRICH_GIANTJ BIRD_CROWJCROW_MANJ -GIANT_CROWJ -BIRD_RAVENJ RAVEN_MANJ GIANT_RAVENJBIRD_CASSOWARYJ CASSOWARY_MANJGIANT_CASSOWARYJBIRD_KEAJKEA_MANJ GIANT_KEAJBIRD_OWL_SNOWYJ SNOWY_OWL_MANJGIANT_SNOWY_OWLJSPARROWJ SPARROW_MANJ GIANT_SPARROWJBIRD_STORK_WHITEJWHITE_STORK_MANJGIANT_WHITE_STORKJ BIRD_LOONJLOON_MANJ -GIANT_LOONJ BIRD_OWL_BARNJ BARN_OWL_MANJGIANT_BARN_OWLJ BIRD_PARAKEETJ PARAKEET_MANJGIANT_PARAKEETJ BIRD_KAKAPOJ -KAKAPO_MANJ GIANT_KAKAPOJBIRD_PARROT_GREYJGREY_PARROT_MANJGIANT_GREY_PARROTJ BIRD_PUFFINJ -PUFFIN_MANJ GIANT_PUFFINJ BIRD_SWANJSWAN_MANJ -GIANT_SWANJ BIRD_LORIKEETJ LORIKEET_MANJGIANT_LORIKEETJ BIRD_WRENJWREN_MANJ -GIANT_WRENJ BIRD_OSPREYJ -OSPREY_MANJ GIANT_OSPREYJBIRD_EMUJEMU_MANJ GIANT_EMUJBIRD_COCKATIELJ COCKATIEL_MANJGIANT_COCKATIELJBIRD_LOVEBIRD_PEACH-FACEDJPEACH-FACED_LOVEBIRD_MANJGIANT_PEACH-FACED_LOVEBIRDJ BIRD_MAGPIEJ -MAGPIE_MANJ GIANT_MAGPIEJ BIRD_KESTRELJ KESTREL_MANJ GIANT_KESTRELJBIRD_ALBATROSSJ ALBATROSS_MANJGIANT_ALBATROSSJBIRD_OWL_GREAT_HORNEDJGREAT_HORNED_OWL_MANJGIANT_GREAT_HORNED_OWLJ -BIRD_EAGLEJ EAGLE_MANJ GIANT_EAGLEJ BIRD_HORNBILLJ HORNBILL_MANJGIANT_HORNBILLJBIRD_LOVEBIRD_MASKEDJMASKED_LOVEBIRD_MANJGIANT_MASKED_LOVEBIRDJ BIRD_BUSHTITJ BUSHTIT_MANJ GIANT_BUSHTITJ DAMSELFLYJ DAMSELFLY_MANJGIANT_DAMSELFLYJMOTHJMOTH_MANJ -GIANT_MOTHJ GRASSHOPPERJGRASSHOPPER_MANJGIANT_GRASSHOPPERJ BARK_SCORPIONJBARK_SCORPION_MANJGIANT_BARK_SCORPIONJMANTISJ -MANTIS_MANJ GIANT_MANTISJTICKJTICK_MANJ -GIANT_TICKJLOUSEJ LOUSE_MANJ GIANT_LOUSEJTHRIPSJ -THRIPS_MANJ GIANT_THRIPSJSLUGJSLUG_MANJ -GIANT_SLUGJMOSQUITOJ MOSQUITO_MANJGIANT_MOSQUITOJSPIDER_JUMPINGJJUMPING_SPIDER_MANJGIANT_JUMPING_SPIDERJTERMITEJ -MOON_SNAILJMOON_SNAIL_MANJGIANT_MOON_SNAILJSPIDER_BROWN_RECLUSEJBROWN_RECLUSE_SPIDER_MANJGIANT_BROWN_RECLUSE_SPIDERJSNAILJ SNAIL_MANJ GIANT_SNAILJ GECKO_LEOPARDJLEOPARD_GECKO_MANJGIANT_LEOPARD_GECKOJDESERT TORTOISEJDESERT_TORTOISE_MANJGIANT_DESERT_TORTOISEJ GILA_MONSTERJGILA_MONSTER_MANJGIANT_GILA_MONSTERJDOGJCATJMULEJDONKEYJHORSEJCOWJSHEEPJPIGJGOATJ BIRD_CHICKENJCAVYJ BIRD_DUCKJ WATER_BUFFALOJREINDEERJ -BIRD_GOOSEJYAKJLLAMAJALPACAJBIRD_GUINEAFOWLJBIRD_PEAFOWL_BLUEJ BIRD_TURKEYJRABBITJCHIMERAJCENTAURJGRIFFONJFLYJFLY_MANJ GIANT_FLYJ ROACH_LARGEJ ROACH_MANJ GIANT_ROACHJBEETLEJ -BEETLE_MANJ GIANT_BEETLEJANTJBUTTERFLY_MONARCHJBUTTERFLY_MONARCH_MANJGIANT_BUTTERFLY_MONARCHJFIREFLYJ FIREFLY_MANJ GIANT_FIREFLYJ DRAGONFLYJ DRAGONFLY_MANJGIANT_DRAGONFLYJ HONEY_BEEJ BUMBLEBEEJ GOAT_MOUNTAINJGOAT_MOUNTAIN_MANJGIANT_GOAT_MOUNTAINJ MARMOT_HOARYJMARMOT_HOARY_MANJGIANT_MARMOT_HOARYJGNOME_MOUNTAINJ -GNOME_DARKJWALRUSJ -WALRUS_MANJ GIANT_WALRUSJFISH_LAMPREY_SEAJSHARK_GREAT_WHITEJ SHARK_FRILLJSHARK_SPINY_DOGFISHJSHARK_WOBBEGONG_SPOTTEDJ SHARK_WHALEJ SHARK_BASKINGJ SHARK_NURSEJSHARK_MAKO_SHORTFINJSHARK_MAKO_LONGFINJ SHARK_TIGERJ -SHARK_BULLJSHARK_REEF_BLACKTIPJSHARK_REEF_WHITETIPJ -SHARK_BLUEJSHARK_HAMMERHEADJ SHARK_ANGELJFISH_SKATE_COMMONJFISH_RAY_MANTAJ FISH_STINGRAYJFISH_COELACANTHJ FISH_STURGEONJFISH_CONGER_EELJ FISH_MILKFISHJFISH_CODJ FISH_OPAHJFISH_GROUPER_GIANTJ FISH_BLUEFISHJFISH_SUNFISH_OCEANJFISH_SWORDFISHJ FISH_MARLINJ FISH_HALIBUTJFISH_BARRACUDA_GREATJFISH_TUNA_BLUEFINJNARWHALJ NARWHAL MANJNARWHAL, GIANTJHIPPOJ HIPPO_MANJ GIANT_HIPPOJFISH_GAR_LONGNOSEJ FISH_CARPJFISH_TIGERFISHJ FISH_PIKEJPLATYPUSJ PLATYPUS MANJPLATYPUS, GIANTJ BEAR_GRIZZLYJBEAR_GRIZZLY_MANJGIANT_BEAR_GRIZZLYJ -BEAR_BLACKJBEAR_BLACK_MANJGIANT_BEAR_BLACKJDEERJDEER_MANJ -GIANT_DEERJFOXJFOX_MANJ GIANT_FOXJRACCOONJ RACCOON_MANJ GIANT_RACCOONJMACAQUE_RHESUSJMACAQUE_RHESUS_MANJGIANT_MACAQUE_RHESUSJCOUGARJ -COUGAR_MANJ GIANT_COUGARJWOLFJWOLF_MANJ -GIANT_WOLFJ GROUNDHOGJ GROUNDHOG_MANJGIANT_GROUNDHOGJ ALLIGATORJ ALLIGATOR_MANJGIANT_ALLIGATORJ BIRD_BUZZARDJ BUZZARD_MANJ GIANT_BUZZARDJPANDAJPANDA, GIGANTICJ PANDA MANJCAPYBARAJCAPYBARA, GIANTJ CAPYBARA MANJBADGERJ -BADGER MANJ BADGER, GIANTJMOOSEJ MOOSE MANJ MOOSE, GIANTJ RED PANDAJ RED PANDA MANJRED PANDA, GIANTJELEPHANTJ ELEPHANT_MANJGIANT_ELEPHANTJWARTHOGJ WARTHOG_MANJ GIANT_WARTHOGJLIONJLION_MANJ -GIANT_LIONJLEOPARDJ LEOPARD_MANJ GIANT_LEOPARDJJAGUARJ -JAGUAR_MANJ GIANT_JAGUARJTIGERJ TIGER_MANJ GIANT_TIGERJCHEETAHJ CHEETAH_MANJ GIANT_CHEETAHJGAZELLEJ GAZELLE_MANJ GIANT_GAZELLEJMANDRILLJ MANDRILL_MANJGIANT_MANDRILLJ -CHIMPANZEEJBONOBOJGORILLAJ ORANGUTANJGIBBON_SIAMANGJGIBBON_WHITE_HANDEDJGIBBON_BLACK_HANDEDJ GIBBON_GRAYJGIBBON_SILVERYJGIBBON_PILEATEDJ GIBBON_BILOUJGIBBON_WHITE_BROWEDJGIBBON_BLACK_CRESTEDJ CAMEL_1_HUMPJCAMEL_1_HUMP_MANJGIANT_CAMEL_1_HUMPJ CAMEL_2_HUMPJCAMEL_2_HUMP_MANJGIANT_CAMEL_2_HUMPJCROCODILE_SALTWATERJCROCODILE_SALTWATER_MANJGIANT_CROCODILE_SALTWATERJ BIRD_VULTUREJ VULTURE_MANJ GIANT_VULTUREJ -RHINOCEROSJRHINOCEROS_MANJGIANT_RHINOCEROSJGIRAFFEJ GIRAFFE_MANJ GIANT_GIRAFFEJ HONEY BADGERJHONEY BADGER MANJHONEY BADGER, GIANTJGIANT TORTOISEJGIANT TORTOISE MANJGIGANTIC TORTOISEJ ARMADILLOJ ARMADILLO MANJARMADILLO, GIANTJMUSKOXJ -MUSKOX_MANJ GIANT_MUSKOXJELKJELK_MANJ GIANT_ELKJ -BEAR_POLARJBEAR_POLAR_MANJGIANT_BEAR_POLARJ WOLVERINEJ WOLVERINE_MANJGIANT_WOLVERINEJ -CHINCHILLAJCHINCHILLA_MANJGIANT_CHINCHILLAJ FLOATING_GUTSJDRUNIANJ CREEPING_EYEJVORACIOUS_CAVE_CRAWLERJBLIND_CAVE_OGREJ -CAP_HOPPERJ -MAGMA_CRABJCRUNDLEJ HUNGRY_HEADJ -FLESH_BALLJELK_BIRDJ HELMET_SNAKEJGREEN_DEVOURERJRUTHERERJCREEPY_CRAWLERJDRALTHAJGIANT_EARTHWORMJ BLOOD_MANJBUGBATJMANERAJ -MOLEMARIANJJABBERERJ POND_GRABBERJBLIND_CAVE_BEARJ CAVE_DRAGONJREACHERJELEMENTMAN_GABBROJGORLAKJ CAVE_FLOATERJPLUMP_HELMET_MANJ CAVE_BLOBJELEMENTMAN_AMETHYSTJOCTOPUSJ OCTOPUS_MANJ GIANT_OCTOPUSJCRABJCRAB_MANJ -GIANT_CRABJ LEOPARD_SEALJLEOPARD_SEAL_MANJGIANT_LEOPARD_SEALJ -CUTTLEFISHJCUTTLEFISH_MANJGIANT_CUTTLEFISHJORCAJORCA_MANJ -GIANT_ORCAJSPONGEJ -SPONGE_MANJ GIANT_SPONGEJHORSESHOE_CRABJHORSESHOE_CRAB_MANJGIANT_HORSESHOE_CRABJ SPERM_WHALEJSPERM_WHALE_MANJGIANT_SPERM_WHALEJ ELEPHANT_SEALJELEPHANT_SEAL_MANJGIANT_ELEPHANT_SEALJ HARP_SEALJ HARP_SEAL_MANJGIANT_HARP_SEALJNAUTILUSJ NAUTILUS_MANJGIANT_NAUTILUSJ FOXSQUIRRELJ MOGHOPPERJ RAT_DEMONJWAMBLER_FLUFFYJLIZARD_RHINO_TWO_LEGGEDJ WORM_KNUCKLEJSPIDER_PHANTOMJ FLY_ACORNJ -GNAT_BLOODJLIZARDJ -LIZARD_MANJ GIANT_LIZARDJSKINKJ SKINK_MANJ GIANT_SKINKJ CHAMELEONJ CHAMELEON_MANJGIANT_CHAMELEONJANOLEJ ANOLE_MANJ GIANT_ANOLEJIGUANAJ -IGUANA_MANJ GIANT_IGUANAJ RIVER OTTERJ SEA OTTERJ OTTER_MANJ GIANT_OTTERJSNAPPING TURTLEJALLIGATOR SNAPPING TURTLEJSNAPPING_TURTLE_MANJGIANT_SNAPPING_TURTLEJBEAVERJ -BEAVER_MANJ GIANT_BEAVERJLEECHJ LEECH_MANJ GIANT_LEECHJAXOLOTLJ AXOLOTL_MANJ GIANT_AXOLOTLJMINKJMINK_MANJ -GIANT_MINKJ POND_TURTLEJPOND_TURTLE_MANJGIANT_POND_TURTLEJRATJRAT_MANJ SQUIRREL_GRAYJSQUIRREL_GRAY_MANJGIANT_SQUIRREL_GRAYJ SQUIRREL_REDJSQUIRREL_RED_MANJGIANT_SQUIRREL_REDJCHIPMUNKJ CHIPMUNK_MANJGIANT_CHIPMUNKJHAMSTERJ HAMSTER_MANJ GIANT_HAMSTERJHEDGEHOGJ HEDGEHOG_MANJGIANT_HEDGEHOGJSQUIRREL_FLYINGJFLYING_SQUIRREL_MANJGIANT_FLYING_SQUIRRELJMUSSELJOYSTERJ FISH_SALMONJFISH_CLOWNFISHJ FISH_HAGFISHJFISH_LAMPREY_BROOKJ FISH_RAY_BATJFISH_RAY_THORNBACKJFISH_RATFISH_SPOTTEDJ FISH_HERRINGJ FISH_SHADJ FISH_ANCHOVYJFISH_TROUT_STEELHEADJ FISH_HAKEJ FISH_SEAHORSEJ FISH_GLASSEYEJFISH_PUFFER_WHITE_SPOTTEDJ FISH_SOLEJ FISH_FLOUNDERJ FISH_MACKERELJJELLYFISH_SEA_NETTLEJSQUIDJ SQUID MANJGIGANTIC SQUIDJ FISH_LUNGFISHJFISH_LOACH_CLOWNJFISH_BULLHEAD_BROWNJFISH_BULLHEAD_YELLOWJFISH_BULLHEAD_BLACKJFISH_KNIFEFISH_BANDEDJ FISH_CHARJFISH_TROUT_RAINBOWJFISH_MOLLY_SAILFINJ -FISH_GUPPYJ -FISH_PERCHJDWARFJHUMANJELFJGOBLINJKOBOLDJGREMLINJTROLLJOGREJUNICORNJDRAGONJSATYRJCOLOSSUS_BRONZEJGIANTJCYCLOPSJETTINJMINOTAURJYETIJ SASQUATCHJ BLIZZARD_MANJWOLF_ICEJFAIRYJPIXIEJBEAK_DOGJ GRIMELINGJ BLENDEC_FOULJ STRANGLERJ NIGHTWINGJHARPYJHYDRAJ MERPERSONJ SEA_SERPENTJ SEA_MONSTERJBIRD_ROCJCROCODILE_CAVEJTOAD_GIANT_CAVEJ OLM_GIANTJ BAT_GIANTJ RAT_GIANTJ RAT_LARGEJMOLE_DOG_NAKEDJ -TROGLODYTEJ -MOLE_GIANTJIMP_FIREJSPIDER_CAVE_GIANTJ SPIDER_CAVEJ FISH_CAVEJ CAVE_FISH_MANJ LOBSTER_CAVEJ -SNAKE_FIREJOLMJOLM_MANJBATJBAT_MANJMAGGOT_PURRINGJELEMENTMAN_FIREJELEMENTMAN_MAGMAJELEMENTMAN_IRONJELEMENTMAN_MUDJBIRD_SWALLOW_CAVEJCAVE_SWALLOW_MANJBIRD_SWALLOW_CAVE_GIANTJ AMPHIBIAN_MANJ REPTILE_MANJ SERPENT_MANJANT_MANJ -RODENT MANJ WILD_BOARJ WILD_BOAR_MANJGIANT_WILD_BOARJCOYOTEJ -COYOTE_MANJ GIANT_COYOTEJKANGAROOJ KANGAROO_MANJGIANT_KANGAROOJKOALAJ KOALA_MANJ GIANT_KOALAJADDERJ ADDER_MANJ GIANT_ADDERJECHIDNAJ ECHIDNA_MANJ GIANT_ECHIDNAJ PORCUPINEJ PORCUPINE_MANJGIANT_PORCUPINEJ KINGSNAKEJ KINGSNAKE_MANJGIANT_KINGSNAKEJ GRAY_LANGURJGRAY_LANGUR_MANJGIANT_GRAY_LANGURJBOBCATJ -BOBCAT_MANJ GIANT_BOBCATJSKUNKJ SKUNK_MANJ GIANT_SKUNKJGREEN_TREE_FROGJGREEN_TREE_FROG_MANJGIANT_GREEN_TREE_FROGJHAREJHARE_MANJ -GIANT_HAREJ RATTLESNAKEJRATTLESNAKE_MANJGIANT_RATTLESNAKEJWEASELJ -WEASEL_MANJ GIANT_WEASELJCOPPERHEAD_SNAKEJCOPPERHEAD_SNAKE_MANJGIANT_COPPERHEAD_SNAKEJIBEXJIBEX_MANJ -GIANT_IBEXJWOMBATJ -WOMBAT_MANJ GIANT_WOMBATJDINGOJ DINGO_MANJ GIANT_DINGOJCOATIJ COATI_MANJ GIANT_COATIJOPOSSUMJ OPOSSUM_MANJ GIANT_OPOSSUMJMONGOOSEJ MONGOOSE_MANJGIANT_MONGOOSEJHYENAJ HYENA_MANJ GIANT_HYENAJANACONDAJ ANACONDA_MANJGIANT_ANACONDAJMONITOR_LIZARDJMONITOR_LIZARD_MANJGIANT_MONITOR_LIZARDJ -KING_COBRAJKING_COBRA_MANJGIANT_KING_COBRAJOCELOTJ -OCELOT_MANJ GIANT_OCELOTJJACKALJ -JACKAL_MANJ GIANT_JACKALJCAPUCHINJ CAPUCHIN_MANJGIANT_CAPUCHINJSLOTHJ SLOTH_MANJ GIANT_SLOTHJ SPIDER_MONKEYJSPIDER_MONKEY_MANJGIANT_SPIDER_MONKEYJPANGOLINJ PANGOLIN_MANJGIANT_PANGOLINJ BLACK_MAMBAJBLACK_MAMBA_MANJGIANT_BLACK_MAMBAJ -BEAR_SLOTHJSLOTH_BEAR_MANJGIANT_SLOTH_BEARJAYE-AYEJ AYE-AYE_MANJ GIANT_AYE-AYEJ -BUSHMASTERJBUSHMASTER_MANJGIANT_BUSHMASTERJPYTHONJ -PYTHON_MANJ GIANT_PYTHONJTAPIRJ TAPIR_MANJ GIANT_TAPIRJIMPALAJ -IMPALA_MANJ GIANT_IMPALAJAARDVARKJ AARDVARK_MANJGIANT_AARDVARKJ LION_TAMARINJLION_TAMARIN_MANJGIANT_LION_TAMARINJSTOATJ STOAT_MANJ GIANT_STOATJLYNXJLYNX_MANJ -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/hair.dfstock b/data/stockpiles/hair.dfstock deleted file mode 100644 index 1aad78df1..000000000 --- a/data/stockpiles/hair.dfstock +++ /dev/null @@ -1,157 +0,0 @@ -*ÆW -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCH:TOAD:TOAD_MAN: -GIANT_TOAD:WORM:WORM_MAN: BIRD_BLUEJAY: BLUEJAY_MAN: GIANT_BLUEJAY: BIRD_CARDINAL: CARDINAL_MAN:GIANT_CARDINAL: BIRD_GRACKLE: GRACKLE_MAN: GIANT_GRACKLE: BIRD_ORIOLE: -ORIOLE_MAN: GIANT_ORIOLE:BIRD_RW_BLACKBIRD:RW_BLACKBIRD_MAN:GIANT_RW_BLACKBIRD: BIRD_PENGUIN:BIRD_PENGUIN_LITTLE:BIRD_PENGUIN_EMPEROR: PENGUIN MAN:BIRD_PENGUIN_GIANT:BIRD_FALCON_PEREGRINE:PEREGRINE FALCON MAN:GIANT PEREGRINE FALCON: BIRD_KIWI:KIWI MAN:BIRD_KIWI_GIANT: BIRD_OSTRICH: OSTRICH MAN:BIRD_OSTRICH_GIANT: BIRD_CROW:CROW_MAN: -GIANT_CROW: -BIRD_RAVEN: RAVEN_MAN: GIANT_RAVEN:BIRD_CASSOWARY: CASSOWARY_MAN:GIANT_CASSOWARY:BIRD_KEA:KEA_MAN: GIANT_KEA:BIRD_OWL_SNOWY: SNOWY_OWL_MAN:GIANT_SNOWY_OWL:SPARROW: SPARROW_MAN: GIANT_SPARROW:BIRD_STORK_WHITE:WHITE_STORK_MAN:GIANT_WHITE_STORK: BIRD_LOON:LOON_MAN: -GIANT_LOON: BIRD_OWL_BARN: BARN_OWL_MAN:GIANT_BARN_OWL: BIRD_PARAKEET: PARAKEET_MAN:GIANT_PARAKEET: BIRD_KAKAPO: -KAKAPO_MAN: GIANT_KAKAPO:BIRD_PARROT_GREY:GREY_PARROT_MAN:GIANT_GREY_PARROT: BIRD_PUFFIN: -PUFFIN_MAN: GIANT_PUFFIN: BIRD_SWAN:SWAN_MAN: -GIANT_SWAN: BIRD_LORIKEET: LORIKEET_MAN:GIANT_LORIKEET: BIRD_WREN:WREN_MAN: -GIANT_WREN: BIRD_OSPREY: -OSPREY_MAN: GIANT_OSPREY:BIRD_EMU:EMU_MAN: GIANT_EMU:BIRD_COCKATIEL: COCKATIEL_MAN:GIANT_COCKATIEL:BIRD_LOVEBIRD_PEACH-FACED:PEACH-FACED_LOVEBIRD_MAN:GIANT_PEACH-FACED_LOVEBIRD: BIRD_MAGPIE: -MAGPIE_MAN: GIANT_MAGPIE: BIRD_KESTREL: KESTREL_MAN: GIANT_KESTREL:BIRD_ALBATROSS: ALBATROSS_MAN:GIANT_ALBATROSS:BIRD_OWL_GREAT_HORNED:GREAT_HORNED_OWL_MAN:GIANT_GREAT_HORNED_OWL: -BIRD_EAGLE: EAGLE_MAN: GIANT_EAGLE: BIRD_HORNBILL: HORNBILL_MAN:GIANT_HORNBILL:BIRD_LOVEBIRD_MASKED:MASKED_LOVEBIRD_MAN:GIANT_MASKED_LOVEBIRD: BIRD_BUSHTIT: BUSHTIT_MAN: GIANT_BUSHTIT: DAMSELFLY: DAMSELFLY_MAN:GIANT_DAMSELFLY:MOTH:MOTH_MAN: -GIANT_MOTH: GRASSHOPPER:GRASSHOPPER_MAN:GIANT_GRASSHOPPER: BARK_SCORPION:BARK_SCORPION_MAN:GIANT_BARK_SCORPION:MANTIS: -MANTIS_MAN: GIANT_MANTIS:TICK:TICK_MAN: -GIANT_TICK:LOUSE: LOUSE_MAN: GIANT_LOUSE:THRIPS: -THRIPS_MAN: GIANT_THRIPS:SLUG:SLUG_MAN: -GIANT_SLUG:MOSQUITO: MOSQUITO_MAN:GIANT_MOSQUITO:SPIDER_JUMPING:JUMPING_SPIDER_MAN:GIANT_JUMPING_SPIDER:TERMITE: -MOON_SNAIL:MOON_SNAIL_MAN:GIANT_MOON_SNAIL:SPIDER_BROWN_RECLUSE:BROWN_RECLUSE_SPIDER_MAN:GIANT_BROWN_RECLUSE_SPIDER:SNAIL: SNAIL_MAN: GIANT_SNAIL: GECKO_LEOPARD:LEOPARD_GECKO_MAN:GIANT_LEOPARD_GECKO:DESERT TORTOISE:DESERT_TORTOISE_MAN:GIANT_DESERT_TORTOISE: GILA_MONSTER:GILA_MONSTER_MAN:GIANT_GILA_MONSTER:DOG:CAT:MULE:DONKEY:HORSE:COW:SHEEP:PIG:GOAT: BIRD_CHICKEN:CAVY: BIRD_DUCK: WATER_BUFFALO:REINDEER: -BIRD_GOOSE:YAK:LLAMA:ALPACA:BIRD_GUINEAFOWL:BIRD_PEAFOWL_BLUE: BIRD_TURKEY:RABBIT:CHIMERA:CENTAUR:GRIFFON:FLY:FLY_MAN: GIANT_FLY: ROACH_LARGE: ROACH_MAN: GIANT_ROACH:BEETLE: -BEETLE_MAN: GIANT_BEETLE:ANT:BUTTERFLY_MONARCH:BUTTERFLY_MONARCH_MAN:GIANT_BUTTERFLY_MONARCH:FIREFLY: FIREFLY_MAN: GIANT_FIREFLY: DRAGONFLY: DRAGONFLY_MAN:GIANT_DRAGONFLY: HONEY_BEE: BUMBLEBEE: GOAT_MOUNTAIN:GOAT_MOUNTAIN_MAN:GIANT_GOAT_MOUNTAIN: MARMOT_HOARY:MARMOT_HOARY_MAN:GIANT_MARMOT_HOARY:GNOME_MOUNTAIN: -GNOME_DARK:WALRUS: -WALRUS_MAN: GIANT_WALRUS:FISH_LAMPREY_SEA:SHARK_GREAT_WHITE: SHARK_FRILL:SHARK_SPINY_DOGFISH:SHARK_WOBBEGONG_SPOTTED: SHARK_WHALE: SHARK_BASKING: SHARK_NURSE:SHARK_MAKO_SHORTFIN:SHARK_MAKO_LONGFIN: SHARK_TIGER: -SHARK_BULL:SHARK_REEF_BLACKTIP:SHARK_REEF_WHITETIP: -SHARK_BLUE:SHARK_HAMMERHEAD: SHARK_ANGEL:FISH_SKATE_COMMON:FISH_RAY_MANTA: FISH_STINGRAY:FISH_COELACANTH: FISH_STURGEON:FISH_CONGER_EEL: FISH_MILKFISH:FISH_COD: FISH_OPAH:FISH_GROUPER_GIANT: FISH_BLUEFISH:FISH_SUNFISH_OCEAN:FISH_SWORDFISH: FISH_MARLIN: FISH_HALIBUT:FISH_BARRACUDA_GREAT:FISH_TUNA_BLUEFIN:NARWHAL: NARWHAL MAN:NARWHAL, GIANT:HIPPO: HIPPO_MAN: GIANT_HIPPO:FISH_GAR_LONGNOSE: FISH_CARP:FISH_TIGERFISH: FISH_PIKE:PLATYPUS: PLATYPUS MAN:PLATYPUS, GIANT: BEAR_GRIZZLY:BEAR_GRIZZLY_MAN:GIANT_BEAR_GRIZZLY: -BEAR_BLACK:BEAR_BLACK_MAN:GIANT_BEAR_BLACK:DEER:DEER_MAN: -GIANT_DEER:FOX:FOX_MAN: GIANT_FOX:RACCOON: RACCOON_MAN: GIANT_RACCOON:MACAQUE_RHESUS:MACAQUE_RHESUS_MAN:GIANT_MACAQUE_RHESUS:COUGAR: -COUGAR_MAN: GIANT_COUGAR:WOLF:WOLF_MAN: -GIANT_WOLF: GROUNDHOG: GROUNDHOG_MAN:GIANT_GROUNDHOG: ALLIGATOR: ALLIGATOR_MAN:GIANT_ALLIGATOR: BIRD_BUZZARD: BUZZARD_MAN: GIANT_BUZZARD:PANDA:PANDA, GIGANTIC: PANDA MAN:CAPYBARA:CAPYBARA, GIANT: CAPYBARA MAN:BADGER: -BADGER MAN: BADGER, GIANT:MOOSE: MOOSE MAN: MOOSE, GIANT: RED PANDA: RED PANDA MAN:RED PANDA, GIANT:ELEPHANT: ELEPHANT_MAN:GIANT_ELEPHANT:WARTHOG: WARTHOG_MAN: GIANT_WARTHOG:LION:LION_MAN: -GIANT_LION:LEOPARD: LEOPARD_MAN: GIANT_LEOPARD:JAGUAR: -JAGUAR_MAN: GIANT_JAGUAR:TIGER: TIGER_MAN: GIANT_TIGER:CHEETAH: CHEETAH_MAN: GIANT_CHEETAH:GAZELLE: GAZELLE_MAN: GIANT_GAZELLE:MANDRILL: MANDRILL_MAN:GIANT_MANDRILL: -CHIMPANZEE:BONOBO:GORILLA: ORANGUTAN:GIBBON_SIAMANG:GIBBON_WHITE_HANDED:GIBBON_BLACK_HANDED: GIBBON_GRAY:GIBBON_SILVERY:GIBBON_PILEATED: GIBBON_BILOU:GIBBON_WHITE_BROWED:GIBBON_BLACK_CRESTED: CAMEL_1_HUMP:CAMEL_1_HUMP_MAN:GIANT_CAMEL_1_HUMP: CAMEL_2_HUMP:CAMEL_2_HUMP_MAN:GIANT_CAMEL_2_HUMP:CROCODILE_SALTWATER:CROCODILE_SALTWATER_MAN:GIANT_CROCODILE_SALTWATER: BIRD_VULTURE: VULTURE_MAN: GIANT_VULTURE: -RHINOCEROS:RHINOCEROS_MAN:GIANT_RHINOCEROS:GIRAFFE: GIRAFFE_MAN: GIANT_GIRAFFE: HONEY BADGER:HONEY BADGER MAN:HONEY BADGER, GIANT:GIANT TORTOISE:GIANT TORTOISE MAN:GIGANTIC TORTOISE: ARMADILLO: ARMADILLO MAN:ARMADILLO, GIANT:MUSKOX: -MUSKOX_MAN: GIANT_MUSKOX:ELK:ELK_MAN: GIANT_ELK: -BEAR_POLAR:BEAR_POLAR_MAN:GIANT_BEAR_POLAR: WOLVERINE: WOLVERINE_MAN:GIANT_WOLVERINE: -CHINCHILLA:CHINCHILLA_MAN:GIANT_CHINCHILLA: FLOATING_GUTS:DRUNIAN: CREEPING_EYE:VORACIOUS_CAVE_CRAWLER:BLIND_CAVE_OGRE: -CAP_HOPPER: -MAGMA_CRAB:CRUNDLE: HUNGRY_HEAD: -FLESH_BALL:ELK_BIRD: HELMET_SNAKE:GREEN_DEVOURER:RUTHERER:CREEPY_CRAWLER:DRALTHA:GIANT_EARTHWORM: BLOOD_MAN:BUGBAT:MANERA: -MOLEMARIAN:JABBERER: POND_GRABBER:BLIND_CAVE_BEAR: CAVE_DRAGON:REACHER:ELEMENTMAN_GABBRO:GORLAK: CAVE_FLOATER:PLUMP_HELMET_MAN: CAVE_BLOB:ELEMENTMAN_AMETHYST:OCTOPUS: OCTOPUS_MAN: GIANT_OCTOPUS:CRAB:CRAB_MAN: -GIANT_CRAB: LEOPARD_SEAL:LEOPARD_SEAL_MAN:GIANT_LEOPARD_SEAL: -CUTTLEFISH:CUTTLEFISH_MAN:GIANT_CUTTLEFISH:ORCA:ORCA_MAN: -GIANT_ORCA:SPONGE: -SPONGE_MAN: GIANT_SPONGE:HORSESHOE_CRAB:HORSESHOE_CRAB_MAN:GIANT_HORSESHOE_CRAB: SPERM_WHALE:SPERM_WHALE_MAN:GIANT_SPERM_WHALE: ELEPHANT_SEAL:ELEPHANT_SEAL_MAN:GIANT_ELEPHANT_SEAL: HARP_SEAL: HARP_SEAL_MAN:GIANT_HARP_SEAL:NAUTILUS: NAUTILUS_MAN:GIANT_NAUTILUS: FOXSQUIRREL: MOGHOPPER: RAT_DEMON:WAMBLER_FLUFFY:LIZARD_RHINO_TWO_LEGGED: WORM_KNUCKLE:SPIDER_PHANTOM: FLY_ACORN: -GNAT_BLOOD:LIZARD: -LIZARD_MAN: GIANT_LIZARD:SKINK: SKINK_MAN: GIANT_SKINK: CHAMELEON: CHAMELEON_MAN:GIANT_CHAMELEON:ANOLE: ANOLE_MAN: GIANT_ANOLE:IGUANA: -IGUANA_MAN: GIANT_IGUANA: RIVER OTTER: SEA OTTER: OTTER_MAN: GIANT_OTTER:SNAPPING TURTLE:ALLIGATOR SNAPPING TURTLE:SNAPPING_TURTLE_MAN:GIANT_SNAPPING_TURTLE:BEAVER: -BEAVER_MAN: GIANT_BEAVER:LEECH: LEECH_MAN: GIANT_LEECH:AXOLOTL: AXOLOTL_MAN: GIANT_AXOLOTL:MINK:MINK_MAN: -GIANT_MINK: POND_TURTLE:POND_TURTLE_MAN:GIANT_POND_TURTLE:RAT:RAT_MAN: SQUIRREL_GRAY:SQUIRREL_GRAY_MAN:GIANT_SQUIRREL_GRAY: SQUIRREL_RED:SQUIRREL_RED_MAN:GIANT_SQUIRREL_RED:CHIPMUNK: CHIPMUNK_MAN:GIANT_CHIPMUNK:HAMSTER: HAMSTER_MAN: GIANT_HAMSTER:HEDGEHOG: HEDGEHOG_MAN:GIANT_HEDGEHOG:SQUIRREL_FLYING:FLYING_SQUIRREL_MAN:GIANT_FLYING_SQUIRREL:MUSSEL:OYSTER: FISH_SALMON:FISH_CLOWNFISH: FISH_HAGFISH:FISH_LAMPREY_BROOK: FISH_RAY_BAT:FISH_RAY_THORNBACK:FISH_RATFISH_SPOTTED: FISH_HERRING: FISH_SHAD: FISH_ANCHOVY:FISH_TROUT_STEELHEAD: FISH_HAKE: FISH_SEAHORSE: FISH_GLASSEYE:FISH_PUFFER_WHITE_SPOTTED: FISH_SOLE: FISH_FLOUNDER: FISH_MACKEREL:JELLYFISH_SEA_NETTLE:SQUID: SQUID MAN:GIGANTIC SQUID: FISH_LUNGFISH:FISH_LOACH_CLOWN:FISH_BULLHEAD_BROWN:FISH_BULLHEAD_YELLOW:FISH_BULLHEAD_BLACK:FISH_KNIFEFISH_BANDED: FISH_CHAR:FISH_TROUT_RAINBOW:FISH_MOLLY_SAILFIN: -FISH_GUPPY: -FISH_PERCH:DWARF:HUMAN:ELF:GOBLIN:KOBOLD:GREMLIN:TROLL:OGRE:UNICORN:DRAGON:SATYR:COLOSSUS_BRONZE:GIANT:CYCLOPS:ETTIN:MINOTAUR:YETI: SASQUATCH: BLIZZARD_MAN:WOLF_ICE:FAIRY:PIXIE:BEAK_DOG: GRIMELING: BLENDEC_FOUL: STRANGLER: NIGHTWING:HARPY:HYDRA: MERPERSON: SEA_SERPENT: SEA_MONSTER:BIRD_ROC:CROCODILE_CAVE:TOAD_GIANT_CAVE: OLM_GIANT: BAT_GIANT: RAT_GIANT: RAT_LARGE:MOLE_DOG_NAKED: -TROGLODYTE: -MOLE_GIANT:IMP_FIRE:SPIDER_CAVE_GIANT: SPIDER_CAVE: FISH_CAVE: CAVE_FISH_MAN: LOBSTER_CAVE: -SNAKE_FIRE:OLM:OLM_MAN:BAT:BAT_MAN:MAGGOT_PURRING:ELEMENTMAN_FIRE:ELEMENTMAN_MAGMA:ELEMENTMAN_IRON:ELEMENTMAN_MUD:BIRD_SWALLOW_CAVE:CAVE_SWALLOW_MAN:BIRD_SWALLOW_CAVE_GIANT: AMPHIBIAN_MAN: REPTILE_MAN: SERPENT_MAN:ANT_MAN: -RODENT MAN: WILD_BOAR: WILD_BOAR_MAN:GIANT_WILD_BOAR:COYOTE: -COYOTE_MAN: GIANT_COYOTE:KANGAROO: KANGAROO_MAN:GIANT_KANGAROO:KOALA: KOALA_MAN: GIANT_KOALA:ADDER: ADDER_MAN: GIANT_ADDER:ECHIDNA: ECHIDNA_MAN: GIANT_ECHIDNA: PORCUPINE: PORCUPINE_MAN:GIANT_PORCUPINE: KINGSNAKE: KINGSNAKE_MAN:GIANT_KINGSNAKE: GRAY_LANGUR:GRAY_LANGUR_MAN:GIANT_GRAY_LANGUR:BOBCAT: -BOBCAT_MAN: GIANT_BOBCAT:SKUNK: SKUNK_MAN: GIANT_SKUNK:GREEN_TREE_FROG:GREEN_TREE_FROG_MAN:GIANT_GREEN_TREE_FROG:HARE:HARE_MAN: -GIANT_HARE: RATTLESNAKE:RATTLESNAKE_MAN:GIANT_RATTLESNAKE:WEASEL: -WEASEL_MAN: GIANT_WEASEL:COPPERHEAD_SNAKE:COPPERHEAD_SNAKE_MAN:GIANT_COPPERHEAD_SNAKE:IBEX:IBEX_MAN: -GIANT_IBEX:WOMBAT: -WOMBAT_MAN: GIANT_WOMBAT:DINGO: DINGO_MAN: GIANT_DINGO:COATI: COATI_MAN: GIANT_COATI:OPOSSUM: OPOSSUM_MAN: GIANT_OPOSSUM:MONGOOSE: MONGOOSE_MAN:GIANT_MONGOOSE:HYENA: HYENA_MAN: GIANT_HYENA:ANACONDA: ANACONDA_MAN:GIANT_ANACONDA:MONITOR_LIZARD:MONITOR_LIZARD_MAN:GIANT_MONITOR_LIZARD: -KING_COBRA:KING_COBRA_MAN:GIANT_KING_COBRA:OCELOT: -OCELOT_MAN: GIANT_OCELOT:JACKAL: -JACKAL_MAN: GIANT_JACKAL:CAPUCHIN: CAPUCHIN_MAN:GIANT_CAPUCHIN:SLOTH: SLOTH_MAN: GIANT_SLOTH: SPIDER_MONKEY:SPIDER_MONKEY_MAN:GIANT_SPIDER_MONKEY:PANGOLIN: PANGOLIN_MAN:GIANT_PANGOLIN: BLACK_MAMBA:BLACK_MAMBA_MAN:GIANT_BLACK_MAMBA: -BEAR_SLOTH:SLOTH_BEAR_MAN:GIANT_SLOTH_BEAR:AYE-AYE: AYE-AYE_MAN: GIANT_AYE-AYE: -BUSHMASTER:BUSHMASTER_MAN:GIANT_BUSHMASTER:PYTHON: -PYTHON_MAN: GIANT_PYTHON:TAPIR: TAPIR_MAN: GIANT_TAPIR:IMPALA: -IMPALA_MAN: GIANT_IMPALA:AARDVARK: AARDVARK_MAN:GIANT_AARDVARK: LION_TAMARIN:LION_TAMARIN_MAN:GIANT_LION_TAMARIN:STOAT: STOAT_MAN: GIANT_STOAT:LYNX:LYNX_MAN: -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/horns.dfstock b/data/stockpiles/horns.dfstock deleted file mode 100644 index 44726daa047e31f19d1fe00209f5029460d8e174..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 730 zcmXw1F_POb43x%_Y{R+C6 z{o7Nwt4`ESs)??%L$GiWI*l?>Z&tSHxdrNH$N5gfqN9^SWJ4}#^AZZ3ihiYGw_(zq zI>U?5Fmg)buy5vG(>Ykrt|X4s7e)&jjLx$SG~~i%L%a(|1rR3>Hho@IGG13J4ir~$ z9_UWraeEr|0}C%$(1IQ%=o2QQXPd<4PG`e-2K;5V%a(AYf&EtbpRbq^6%1@n9C-t)p`i&yY`@7cSJzHm?;SuSepYhIdIGq)riaeHNKA z_0cf~b<1}f4$ur3@2AJzu>iQNG?X<7*P%N_*cUAoF`_Jk2h_x>Xn5N!`qPWy!f|sY zazet($j(^|;2B=Cl|>%HzWjfIB;D5vajh;q6EF2>)Wehsitlubx=)f0Z?l0zt4J0o zte@oOEWoGdQ)d(sJJ>Noh>db&F~!2Z+sehPhk;KpaY^+%^>3S`Kv!>_%SEdVIXa7p z3?^+zr_Uhrs6R#th`XZdsLPH=I?1w7H>%$2rw~kafG^03%}4XCZNInekGA>QHow~D Jcia4Fn}2%@sj2_~ diff --git a/data/stockpiles/shells.dfstock b/data/stockpiles/shells.dfstock deleted file mode 100644 index 548152764..000000000 --- a/data/stockpiles/shells.dfstock +++ /dev/null @@ -1,157 +0,0 @@ -*ÆW -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCHBTOADBTOAD_MANB -GIANT_TOADBWORMBWORM_MANB BIRD_BLUEJAYB BLUEJAY_MANB GIANT_BLUEJAYB BIRD_CARDINALB CARDINAL_MANBGIANT_CARDINALB BIRD_GRACKLEB GRACKLE_MANB GIANT_GRACKLEB BIRD_ORIOLEB -ORIOLE_MANB GIANT_ORIOLEBBIRD_RW_BLACKBIRDBRW_BLACKBIRD_MANBGIANT_RW_BLACKBIRDB BIRD_PENGUINBBIRD_PENGUIN_LITTLEBBIRD_PENGUIN_EMPERORB PENGUIN MANBBIRD_PENGUIN_GIANTBBIRD_FALCON_PEREGRINEBPEREGRINE FALCON MANBGIANT PEREGRINE FALCONB BIRD_KIWIBKIWI MANBBIRD_KIWI_GIANTB BIRD_OSTRICHB OSTRICH MANBBIRD_OSTRICH_GIANTB BIRD_CROWBCROW_MANB -GIANT_CROWB -BIRD_RAVENB RAVEN_MANB GIANT_RAVENBBIRD_CASSOWARYB CASSOWARY_MANBGIANT_CASSOWARYBBIRD_KEABKEA_MANB GIANT_KEABBIRD_OWL_SNOWYB SNOWY_OWL_MANBGIANT_SNOWY_OWLBSPARROWB SPARROW_MANB GIANT_SPARROWBBIRD_STORK_WHITEBWHITE_STORK_MANBGIANT_WHITE_STORKB BIRD_LOONBLOON_MANB -GIANT_LOONB BIRD_OWL_BARNB BARN_OWL_MANBGIANT_BARN_OWLB BIRD_PARAKEETB PARAKEET_MANBGIANT_PARAKEETB BIRD_KAKAPOB -KAKAPO_MANB GIANT_KAKAPOBBIRD_PARROT_GREYBGREY_PARROT_MANBGIANT_GREY_PARROTB BIRD_PUFFINB -PUFFIN_MANB GIANT_PUFFINB BIRD_SWANBSWAN_MANB -GIANT_SWANB BIRD_LORIKEETB LORIKEET_MANBGIANT_LORIKEETB BIRD_WRENBWREN_MANB -GIANT_WRENB BIRD_OSPREYB -OSPREY_MANB GIANT_OSPREYBBIRD_EMUBEMU_MANB GIANT_EMUBBIRD_COCKATIELB COCKATIEL_MANBGIANT_COCKATIELBBIRD_LOVEBIRD_PEACH-FACEDBPEACH-FACED_LOVEBIRD_MANBGIANT_PEACH-FACED_LOVEBIRDB BIRD_MAGPIEB -MAGPIE_MANB GIANT_MAGPIEB BIRD_KESTRELB KESTREL_MANB GIANT_KESTRELBBIRD_ALBATROSSB ALBATROSS_MANBGIANT_ALBATROSSBBIRD_OWL_GREAT_HORNEDBGREAT_HORNED_OWL_MANBGIANT_GREAT_HORNED_OWLB -BIRD_EAGLEB EAGLE_MANB GIANT_EAGLEB BIRD_HORNBILLB HORNBILL_MANBGIANT_HORNBILLBBIRD_LOVEBIRD_MASKEDBMASKED_LOVEBIRD_MANBGIANT_MASKED_LOVEBIRDB BIRD_BUSHTITB BUSHTIT_MANB GIANT_BUSHTITB DAMSELFLYB DAMSELFLY_MANBGIANT_DAMSELFLYBMOTHBMOTH_MANB -GIANT_MOTHB GRASSHOPPERBGRASSHOPPER_MANBGIANT_GRASSHOPPERB BARK_SCORPIONBBARK_SCORPION_MANBGIANT_BARK_SCORPIONBMANTISB -MANTIS_MANB GIANT_MANTISBTICKBTICK_MANB -GIANT_TICKBLOUSEB LOUSE_MANB GIANT_LOUSEBTHRIPSB -THRIPS_MANB GIANT_THRIPSBSLUGBSLUG_MANB -GIANT_SLUGBMOSQUITOB MOSQUITO_MANBGIANT_MOSQUITOBSPIDER_JUMPINGBJUMPING_SPIDER_MANBGIANT_JUMPING_SPIDERBTERMITEB -MOON_SNAILBMOON_SNAIL_MANBGIANT_MOON_SNAILBSPIDER_BROWN_RECLUSEBBROWN_RECLUSE_SPIDER_MANBGIANT_BROWN_RECLUSE_SPIDERBSNAILB SNAIL_MANB GIANT_SNAILB GECKO_LEOPARDBLEOPARD_GECKO_MANBGIANT_LEOPARD_GECKOBDESERT TORTOISEBDESERT_TORTOISE_MANBGIANT_DESERT_TORTOISEB GILA_MONSTERBGILA_MONSTER_MANBGIANT_GILA_MONSTERBDOGBCATBMULEBDONKEYBHORSEBCOWBSHEEPBPIGBGOATB BIRD_CHICKENBCAVYB BIRD_DUCKB WATER_BUFFALOBREINDEERB -BIRD_GOOSEBYAKBLLAMABALPACABBIRD_GUINEAFOWLBBIRD_PEAFOWL_BLUEB BIRD_TURKEYBRABBITBCHIMERABCENTAURBGRIFFONBFLYBFLY_MANB GIANT_FLYB ROACH_LARGEB ROACH_MANB GIANT_ROACHBBEETLEB -BEETLE_MANB GIANT_BEETLEBANTBBUTTERFLY_MONARCHBBUTTERFLY_MONARCH_MANBGIANT_BUTTERFLY_MONARCHBFIREFLYB FIREFLY_MANB GIANT_FIREFLYB DRAGONFLYB DRAGONFLY_MANBGIANT_DRAGONFLYB HONEY_BEEB BUMBLEBEEB GOAT_MOUNTAINBGOAT_MOUNTAIN_MANBGIANT_GOAT_MOUNTAINB MARMOT_HOARYBMARMOT_HOARY_MANBGIANT_MARMOT_HOARYBGNOME_MOUNTAINB -GNOME_DARKBWALRUSB -WALRUS_MANB GIANT_WALRUSBFISH_LAMPREY_SEABSHARK_GREAT_WHITEB SHARK_FRILLBSHARK_SPINY_DOGFISHBSHARK_WOBBEGONG_SPOTTEDB SHARK_WHALEB SHARK_BASKINGB SHARK_NURSEBSHARK_MAKO_SHORTFINBSHARK_MAKO_LONGFINB SHARK_TIGERB -SHARK_BULLBSHARK_REEF_BLACKTIPBSHARK_REEF_WHITETIPB -SHARK_BLUEBSHARK_HAMMERHEADB SHARK_ANGELBFISH_SKATE_COMMONBFISH_RAY_MANTAB FISH_STINGRAYBFISH_COELACANTHB FISH_STURGEONBFISH_CONGER_EELB FISH_MILKFISHBFISH_CODB FISH_OPAHBFISH_GROUPER_GIANTB FISH_BLUEFISHBFISH_SUNFISH_OCEANBFISH_SWORDFISHB FISH_MARLINB FISH_HALIBUTBFISH_BARRACUDA_GREATBFISH_TUNA_BLUEFINBNARWHALB NARWHAL MANBNARWHAL, GIANTBHIPPOB HIPPO_MANB GIANT_HIPPOBFISH_GAR_LONGNOSEB FISH_CARPBFISH_TIGERFISHB FISH_PIKEBPLATYPUSB PLATYPUS MANBPLATYPUS, GIANTB BEAR_GRIZZLYBBEAR_GRIZZLY_MANBGIANT_BEAR_GRIZZLYB -BEAR_BLACKBBEAR_BLACK_MANBGIANT_BEAR_BLACKBDEERBDEER_MANB -GIANT_DEERBFOXBFOX_MANB GIANT_FOXBRACCOONB RACCOON_MANB GIANT_RACCOONBMACAQUE_RHESUSBMACAQUE_RHESUS_MANBGIANT_MACAQUE_RHESUSBCOUGARB -COUGAR_MANB GIANT_COUGARBWOLFBWOLF_MANB -GIANT_WOLFB GROUNDHOGB GROUNDHOG_MANBGIANT_GROUNDHOGB ALLIGATORB ALLIGATOR_MANBGIANT_ALLIGATORB BIRD_BUZZARDB BUZZARD_MANB GIANT_BUZZARDBPANDABPANDA, GIGANTICB PANDA MANBCAPYBARABCAPYBARA, GIANTB CAPYBARA MANBBADGERB -BADGER MANB BADGER, GIANTBMOOSEB MOOSE MANB MOOSE, GIANTB RED PANDAB RED PANDA MANBRED PANDA, GIANTBELEPHANTB ELEPHANT_MANBGIANT_ELEPHANTBWARTHOGB WARTHOG_MANB GIANT_WARTHOGBLIONBLION_MANB -GIANT_LIONBLEOPARDB LEOPARD_MANB GIANT_LEOPARDBJAGUARB -JAGUAR_MANB GIANT_JAGUARBTIGERB TIGER_MANB GIANT_TIGERBCHEETAHB CHEETAH_MANB GIANT_CHEETAHBGAZELLEB GAZELLE_MANB GIANT_GAZELLEBMANDRILLB MANDRILL_MANBGIANT_MANDRILLB -CHIMPANZEEBBONOBOBGORILLAB ORANGUTANBGIBBON_SIAMANGBGIBBON_WHITE_HANDEDBGIBBON_BLACK_HANDEDB GIBBON_GRAYBGIBBON_SILVERYBGIBBON_PILEATEDB GIBBON_BILOUBGIBBON_WHITE_BROWEDBGIBBON_BLACK_CRESTEDB CAMEL_1_HUMPBCAMEL_1_HUMP_MANBGIANT_CAMEL_1_HUMPB CAMEL_2_HUMPBCAMEL_2_HUMP_MANBGIANT_CAMEL_2_HUMPBCROCODILE_SALTWATERBCROCODILE_SALTWATER_MANBGIANT_CROCODILE_SALTWATERB BIRD_VULTUREB VULTURE_MANB GIANT_VULTUREB -RHINOCEROSBRHINOCEROS_MANBGIANT_RHINOCEROSBGIRAFFEB GIRAFFE_MANB GIANT_GIRAFFEB HONEY BADGERBHONEY BADGER MANBHONEY BADGER, GIANTBGIANT TORTOISEBGIANT TORTOISE MANBGIGANTIC TORTOISEB ARMADILLOB ARMADILLO MANBARMADILLO, GIANTBMUSKOXB -MUSKOX_MANB GIANT_MUSKOXBELKBELK_MANB GIANT_ELKB -BEAR_POLARBBEAR_POLAR_MANBGIANT_BEAR_POLARB WOLVERINEB WOLVERINE_MANBGIANT_WOLVERINEB -CHINCHILLABCHINCHILLA_MANBGIANT_CHINCHILLAB FLOATING_GUTSBDRUNIANB CREEPING_EYEBVORACIOUS_CAVE_CRAWLERBBLIND_CAVE_OGREB -CAP_HOPPERB -MAGMA_CRABBCRUNDLEB HUNGRY_HEADB -FLESH_BALLBELK_BIRDB HELMET_SNAKEBGREEN_DEVOURERBRUTHERERBCREEPY_CRAWLERBDRALTHABGIANT_EARTHWORMB BLOOD_MANBBUGBATBMANERAB -MOLEMARIANBJABBERERB POND_GRABBERBBLIND_CAVE_BEARB CAVE_DRAGONBREACHERBELEMENTMAN_GABBROBGORLAKB CAVE_FLOATERBPLUMP_HELMET_MANB CAVE_BLOBBELEMENTMAN_AMETHYSTBOCTOPUSB OCTOPUS_MANB GIANT_OCTOPUSBCRABBCRAB_MANB -GIANT_CRABB LEOPARD_SEALBLEOPARD_SEAL_MANBGIANT_LEOPARD_SEALB -CUTTLEFISHBCUTTLEFISH_MANBGIANT_CUTTLEFISHBORCABORCA_MANB -GIANT_ORCABSPONGEB -SPONGE_MANB GIANT_SPONGEBHORSESHOE_CRABBHORSESHOE_CRAB_MANBGIANT_HORSESHOE_CRABB SPERM_WHALEBSPERM_WHALE_MANBGIANT_SPERM_WHALEB ELEPHANT_SEALBELEPHANT_SEAL_MANBGIANT_ELEPHANT_SEALB HARP_SEALB HARP_SEAL_MANBGIANT_HARP_SEALBNAUTILUSB NAUTILUS_MANBGIANT_NAUTILUSB FOXSQUIRRELB MOGHOPPERB RAT_DEMONBWAMBLER_FLUFFYBLIZARD_RHINO_TWO_LEGGEDB WORM_KNUCKLEBSPIDER_PHANTOMB FLY_ACORNB -GNAT_BLOODBLIZARDB -LIZARD_MANB GIANT_LIZARDBSKINKB SKINK_MANB GIANT_SKINKB CHAMELEONB CHAMELEON_MANBGIANT_CHAMELEONBANOLEB ANOLE_MANB GIANT_ANOLEBIGUANAB -IGUANA_MANB GIANT_IGUANAB RIVER OTTERB SEA OTTERB OTTER_MANB GIANT_OTTERBSNAPPING TURTLEBALLIGATOR SNAPPING TURTLEBSNAPPING_TURTLE_MANBGIANT_SNAPPING_TURTLEBBEAVERB -BEAVER_MANB GIANT_BEAVERBLEECHB LEECH_MANB GIANT_LEECHBAXOLOTLB AXOLOTL_MANB GIANT_AXOLOTLBMINKBMINK_MANB -GIANT_MINKB POND_TURTLEBPOND_TURTLE_MANBGIANT_POND_TURTLEBRATBRAT_MANB SQUIRREL_GRAYBSQUIRREL_GRAY_MANBGIANT_SQUIRREL_GRAYB SQUIRREL_REDBSQUIRREL_RED_MANBGIANT_SQUIRREL_REDBCHIPMUNKB CHIPMUNK_MANBGIANT_CHIPMUNKBHAMSTERB HAMSTER_MANB GIANT_HAMSTERBHEDGEHOGB HEDGEHOG_MANBGIANT_HEDGEHOGBSQUIRREL_FLYINGBFLYING_SQUIRREL_MANBGIANT_FLYING_SQUIRRELBMUSSELBOYSTERB FISH_SALMONBFISH_CLOWNFISHB FISH_HAGFISHBFISH_LAMPREY_BROOKB FISH_RAY_BATBFISH_RAY_THORNBACKBFISH_RATFISH_SPOTTEDB FISH_HERRINGB FISH_SHADB FISH_ANCHOVYBFISH_TROUT_STEELHEADB FISH_HAKEB FISH_SEAHORSEB FISH_GLASSEYEBFISH_PUFFER_WHITE_SPOTTEDB FISH_SOLEB FISH_FLOUNDERB FISH_MACKERELBJELLYFISH_SEA_NETTLEBSQUIDB SQUID MANBGIGANTIC SQUIDB FISH_LUNGFISHBFISH_LOACH_CLOWNBFISH_BULLHEAD_BROWNBFISH_BULLHEAD_YELLOWBFISH_BULLHEAD_BLACKBFISH_KNIFEFISH_BANDEDB FISH_CHARBFISH_TROUT_RAINBOWBFISH_MOLLY_SAILFINB -FISH_GUPPYB -FISH_PERCHBDWARFBHUMANBELFBGOBLINBKOBOLDBGREMLINBTROLLBOGREBUNICORNBDRAGONBSATYRBCOLOSSUS_BRONZEBGIANTBCYCLOPSBETTINBMINOTAURBYETIB SASQUATCHB BLIZZARD_MANBWOLF_ICEBFAIRYBPIXIEBBEAK_DOGB GRIMELINGB BLENDEC_FOULB STRANGLERB NIGHTWINGBHARPYBHYDRAB MERPERSONB SEA_SERPENTB SEA_MONSTERBBIRD_ROCBCROCODILE_CAVEBTOAD_GIANT_CAVEB OLM_GIANTB BAT_GIANTB RAT_GIANTB RAT_LARGEBMOLE_DOG_NAKEDB -TROGLODYTEB -MOLE_GIANTBIMP_FIREBSPIDER_CAVE_GIANTB SPIDER_CAVEB FISH_CAVEB CAVE_FISH_MANB LOBSTER_CAVEB -SNAKE_FIREBOLMBOLM_MANBBATBBAT_MANBMAGGOT_PURRINGBELEMENTMAN_FIREBELEMENTMAN_MAGMABELEMENTMAN_IRONBELEMENTMAN_MUDBBIRD_SWALLOW_CAVEBCAVE_SWALLOW_MANBBIRD_SWALLOW_CAVE_GIANTB AMPHIBIAN_MANB REPTILE_MANB SERPENT_MANBANT_MANB -RODENT MANB WILD_BOARB WILD_BOAR_MANBGIANT_WILD_BOARBCOYOTEB -COYOTE_MANB GIANT_COYOTEBKANGAROOB KANGAROO_MANBGIANT_KANGAROOBKOALAB KOALA_MANB GIANT_KOALABADDERB ADDER_MANB GIANT_ADDERBECHIDNAB ECHIDNA_MANB GIANT_ECHIDNAB PORCUPINEB PORCUPINE_MANBGIANT_PORCUPINEB KINGSNAKEB KINGSNAKE_MANBGIANT_KINGSNAKEB GRAY_LANGURBGRAY_LANGUR_MANBGIANT_GRAY_LANGURBBOBCATB -BOBCAT_MANB GIANT_BOBCATBSKUNKB SKUNK_MANB GIANT_SKUNKBGREEN_TREE_FROGBGREEN_TREE_FROG_MANBGIANT_GREEN_TREE_FROGBHAREBHARE_MANB -GIANT_HAREB RATTLESNAKEBRATTLESNAKE_MANBGIANT_RATTLESNAKEBWEASELB -WEASEL_MANB GIANT_WEASELBCOPPERHEAD_SNAKEBCOPPERHEAD_SNAKE_MANBGIANT_COPPERHEAD_SNAKEBIBEXBIBEX_MANB -GIANT_IBEXBWOMBATB -WOMBAT_MANB GIANT_WOMBATBDINGOB DINGO_MANB GIANT_DINGOBCOATIB COATI_MANB GIANT_COATIBOPOSSUMB OPOSSUM_MANB GIANT_OPOSSUMBMONGOOSEB MONGOOSE_MANBGIANT_MONGOOSEBHYENAB HYENA_MANB GIANT_HYENABANACONDAB ANACONDA_MANBGIANT_ANACONDABMONITOR_LIZARDBMONITOR_LIZARD_MANBGIANT_MONITOR_LIZARDB -KING_COBRABKING_COBRA_MANBGIANT_KING_COBRABOCELOTB -OCELOT_MANB GIANT_OCELOTBJACKALB -JACKAL_MANB GIANT_JACKALBCAPUCHINB CAPUCHIN_MANBGIANT_CAPUCHINBSLOTHB SLOTH_MANB GIANT_SLOTHB SPIDER_MONKEYBSPIDER_MONKEY_MANBGIANT_SPIDER_MONKEYBPANGOLINB PANGOLIN_MANBGIANT_PANGOLINB BLACK_MAMBABBLACK_MAMBA_MANBGIANT_BLACK_MAMBAB -BEAR_SLOTHBSLOTH_BEAR_MANBGIANT_SLOTH_BEARBAYE-AYEB AYE-AYE_MANB GIANT_AYE-AYEB -BUSHMASTERBBUSHMASTER_MANBGIANT_BUSHMASTERBPYTHONB -PYTHON_MANB GIANT_PYTHONBTAPIRB TAPIR_MANB GIANT_TAPIRBIMPALAB -IMPALA_MANB GIANT_IMPALABAARDVARKB AARDVARK_MANBGIANT_AARDVARKB LION_TAMARINBLION_TAMARIN_MANBGIANT_LION_TAMARINBSTOATB STOAT_MANB GIANT_STOATBLYNXBLYNX_MANB -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/skulls.dfstock b/data/stockpiles/skulls.dfstock deleted file mode 100644 index ae52e4537..000000000 --- a/data/stockpiles/skulls.dfstock +++ /dev/null @@ -1,157 +0,0 @@ -*ÆW -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCH*TOAD*TOAD_MAN* -GIANT_TOAD*WORM*WORM_MAN* BIRD_BLUEJAY* BLUEJAY_MAN* GIANT_BLUEJAY* BIRD_CARDINAL* CARDINAL_MAN*GIANT_CARDINAL* BIRD_GRACKLE* GRACKLE_MAN* GIANT_GRACKLE* BIRD_ORIOLE* -ORIOLE_MAN* GIANT_ORIOLE*BIRD_RW_BLACKBIRD*RW_BLACKBIRD_MAN*GIANT_RW_BLACKBIRD* BIRD_PENGUIN*BIRD_PENGUIN_LITTLE*BIRD_PENGUIN_EMPEROR* PENGUIN MAN*BIRD_PENGUIN_GIANT*BIRD_FALCON_PEREGRINE*PEREGRINE FALCON MAN*GIANT PEREGRINE FALCON* BIRD_KIWI*KIWI MAN*BIRD_KIWI_GIANT* BIRD_OSTRICH* OSTRICH MAN*BIRD_OSTRICH_GIANT* BIRD_CROW*CROW_MAN* -GIANT_CROW* -BIRD_RAVEN* RAVEN_MAN* GIANT_RAVEN*BIRD_CASSOWARY* CASSOWARY_MAN*GIANT_CASSOWARY*BIRD_KEA*KEA_MAN* GIANT_KEA*BIRD_OWL_SNOWY* SNOWY_OWL_MAN*GIANT_SNOWY_OWL*SPARROW* SPARROW_MAN* GIANT_SPARROW*BIRD_STORK_WHITE*WHITE_STORK_MAN*GIANT_WHITE_STORK* BIRD_LOON*LOON_MAN* -GIANT_LOON* BIRD_OWL_BARN* BARN_OWL_MAN*GIANT_BARN_OWL* BIRD_PARAKEET* PARAKEET_MAN*GIANT_PARAKEET* BIRD_KAKAPO* -KAKAPO_MAN* GIANT_KAKAPO*BIRD_PARROT_GREY*GREY_PARROT_MAN*GIANT_GREY_PARROT* BIRD_PUFFIN* -PUFFIN_MAN* GIANT_PUFFIN* BIRD_SWAN*SWAN_MAN* -GIANT_SWAN* BIRD_LORIKEET* LORIKEET_MAN*GIANT_LORIKEET* BIRD_WREN*WREN_MAN* -GIANT_WREN* BIRD_OSPREY* -OSPREY_MAN* GIANT_OSPREY*BIRD_EMU*EMU_MAN* GIANT_EMU*BIRD_COCKATIEL* COCKATIEL_MAN*GIANT_COCKATIEL*BIRD_LOVEBIRD_PEACH-FACED*PEACH-FACED_LOVEBIRD_MAN*GIANT_PEACH-FACED_LOVEBIRD* BIRD_MAGPIE* -MAGPIE_MAN* GIANT_MAGPIE* BIRD_KESTREL* KESTREL_MAN* GIANT_KESTREL*BIRD_ALBATROSS* ALBATROSS_MAN*GIANT_ALBATROSS*BIRD_OWL_GREAT_HORNED*GREAT_HORNED_OWL_MAN*GIANT_GREAT_HORNED_OWL* -BIRD_EAGLE* EAGLE_MAN* GIANT_EAGLE* BIRD_HORNBILL* HORNBILL_MAN*GIANT_HORNBILL*BIRD_LOVEBIRD_MASKED*MASKED_LOVEBIRD_MAN*GIANT_MASKED_LOVEBIRD* BIRD_BUSHTIT* BUSHTIT_MAN* GIANT_BUSHTIT* DAMSELFLY* DAMSELFLY_MAN*GIANT_DAMSELFLY*MOTH*MOTH_MAN* -GIANT_MOTH* GRASSHOPPER*GRASSHOPPER_MAN*GIANT_GRASSHOPPER* BARK_SCORPION*BARK_SCORPION_MAN*GIANT_BARK_SCORPION*MANTIS* -MANTIS_MAN* GIANT_MANTIS*TICK*TICK_MAN* -GIANT_TICK*LOUSE* LOUSE_MAN* GIANT_LOUSE*THRIPS* -THRIPS_MAN* GIANT_THRIPS*SLUG*SLUG_MAN* -GIANT_SLUG*MOSQUITO* MOSQUITO_MAN*GIANT_MOSQUITO*SPIDER_JUMPING*JUMPING_SPIDER_MAN*GIANT_JUMPING_SPIDER*TERMITE* -MOON_SNAIL*MOON_SNAIL_MAN*GIANT_MOON_SNAIL*SPIDER_BROWN_RECLUSE*BROWN_RECLUSE_SPIDER_MAN*GIANT_BROWN_RECLUSE_SPIDER*SNAIL* SNAIL_MAN* GIANT_SNAIL* GECKO_LEOPARD*LEOPARD_GECKO_MAN*GIANT_LEOPARD_GECKO*DESERT TORTOISE*DESERT_TORTOISE_MAN*GIANT_DESERT_TORTOISE* GILA_MONSTER*GILA_MONSTER_MAN*GIANT_GILA_MONSTER*DOG*CAT*MULE*DONKEY*HORSE*COW*SHEEP*PIG*GOAT* BIRD_CHICKEN*CAVY* BIRD_DUCK* WATER_BUFFALO*REINDEER* -BIRD_GOOSE*YAK*LLAMA*ALPACA*BIRD_GUINEAFOWL*BIRD_PEAFOWL_BLUE* BIRD_TURKEY*RABBIT*CHIMERA*CENTAUR*GRIFFON*FLY*FLY_MAN* GIANT_FLY* ROACH_LARGE* ROACH_MAN* GIANT_ROACH*BEETLE* -BEETLE_MAN* GIANT_BEETLE*ANT*BUTTERFLY_MONARCH*BUTTERFLY_MONARCH_MAN*GIANT_BUTTERFLY_MONARCH*FIREFLY* FIREFLY_MAN* GIANT_FIREFLY* DRAGONFLY* DRAGONFLY_MAN*GIANT_DRAGONFLY* HONEY_BEE* BUMBLEBEE* GOAT_MOUNTAIN*GOAT_MOUNTAIN_MAN*GIANT_GOAT_MOUNTAIN* MARMOT_HOARY*MARMOT_HOARY_MAN*GIANT_MARMOT_HOARY*GNOME_MOUNTAIN* -GNOME_DARK*WALRUS* -WALRUS_MAN* GIANT_WALRUS*FISH_LAMPREY_SEA*SHARK_GREAT_WHITE* SHARK_FRILL*SHARK_SPINY_DOGFISH*SHARK_WOBBEGONG_SPOTTED* SHARK_WHALE* SHARK_BASKING* SHARK_NURSE*SHARK_MAKO_SHORTFIN*SHARK_MAKO_LONGFIN* SHARK_TIGER* -SHARK_BULL*SHARK_REEF_BLACKTIP*SHARK_REEF_WHITETIP* -SHARK_BLUE*SHARK_HAMMERHEAD* SHARK_ANGEL*FISH_SKATE_COMMON*FISH_RAY_MANTA* FISH_STINGRAY*FISH_COELACANTH* FISH_STURGEON*FISH_CONGER_EEL* FISH_MILKFISH*FISH_COD* FISH_OPAH*FISH_GROUPER_GIANT* FISH_BLUEFISH*FISH_SUNFISH_OCEAN*FISH_SWORDFISH* FISH_MARLIN* FISH_HALIBUT*FISH_BARRACUDA_GREAT*FISH_TUNA_BLUEFIN*NARWHAL* NARWHAL MAN*NARWHAL, GIANT*HIPPO* HIPPO_MAN* GIANT_HIPPO*FISH_GAR_LONGNOSE* FISH_CARP*FISH_TIGERFISH* FISH_PIKE*PLATYPUS* PLATYPUS MAN*PLATYPUS, GIANT* BEAR_GRIZZLY*BEAR_GRIZZLY_MAN*GIANT_BEAR_GRIZZLY* -BEAR_BLACK*BEAR_BLACK_MAN*GIANT_BEAR_BLACK*DEER*DEER_MAN* -GIANT_DEER*FOX*FOX_MAN* GIANT_FOX*RACCOON* RACCOON_MAN* GIANT_RACCOON*MACAQUE_RHESUS*MACAQUE_RHESUS_MAN*GIANT_MACAQUE_RHESUS*COUGAR* -COUGAR_MAN* GIANT_COUGAR*WOLF*WOLF_MAN* -GIANT_WOLF* GROUNDHOG* GROUNDHOG_MAN*GIANT_GROUNDHOG* ALLIGATOR* ALLIGATOR_MAN*GIANT_ALLIGATOR* BIRD_BUZZARD* BUZZARD_MAN* GIANT_BUZZARD*PANDA*PANDA, GIGANTIC* PANDA MAN*CAPYBARA*CAPYBARA, GIANT* CAPYBARA MAN*BADGER* -BADGER MAN* BADGER, GIANT*MOOSE* MOOSE MAN* MOOSE, GIANT* RED PANDA* RED PANDA MAN*RED PANDA, GIANT*ELEPHANT* ELEPHANT_MAN*GIANT_ELEPHANT*WARTHOG* WARTHOG_MAN* GIANT_WARTHOG*LION*LION_MAN* -GIANT_LION*LEOPARD* LEOPARD_MAN* GIANT_LEOPARD*JAGUAR* -JAGUAR_MAN* GIANT_JAGUAR*TIGER* TIGER_MAN* GIANT_TIGER*CHEETAH* CHEETAH_MAN* GIANT_CHEETAH*GAZELLE* GAZELLE_MAN* GIANT_GAZELLE*MANDRILL* MANDRILL_MAN*GIANT_MANDRILL* -CHIMPANZEE*BONOBO*GORILLA* ORANGUTAN*GIBBON_SIAMANG*GIBBON_WHITE_HANDED*GIBBON_BLACK_HANDED* GIBBON_GRAY*GIBBON_SILVERY*GIBBON_PILEATED* GIBBON_BILOU*GIBBON_WHITE_BROWED*GIBBON_BLACK_CRESTED* CAMEL_1_HUMP*CAMEL_1_HUMP_MAN*GIANT_CAMEL_1_HUMP* CAMEL_2_HUMP*CAMEL_2_HUMP_MAN*GIANT_CAMEL_2_HUMP*CROCODILE_SALTWATER*CROCODILE_SALTWATER_MAN*GIANT_CROCODILE_SALTWATER* BIRD_VULTURE* VULTURE_MAN* GIANT_VULTURE* -RHINOCEROS*RHINOCEROS_MAN*GIANT_RHINOCEROS*GIRAFFE* GIRAFFE_MAN* GIANT_GIRAFFE* HONEY BADGER*HONEY BADGER MAN*HONEY BADGER, GIANT*GIANT TORTOISE*GIANT TORTOISE MAN*GIGANTIC TORTOISE* ARMADILLO* ARMADILLO MAN*ARMADILLO, GIANT*MUSKOX* -MUSKOX_MAN* GIANT_MUSKOX*ELK*ELK_MAN* GIANT_ELK* -BEAR_POLAR*BEAR_POLAR_MAN*GIANT_BEAR_POLAR* WOLVERINE* WOLVERINE_MAN*GIANT_WOLVERINE* -CHINCHILLA*CHINCHILLA_MAN*GIANT_CHINCHILLA* FLOATING_GUTS*DRUNIAN* CREEPING_EYE*VORACIOUS_CAVE_CRAWLER*BLIND_CAVE_OGRE* -CAP_HOPPER* -MAGMA_CRAB*CRUNDLE* HUNGRY_HEAD* -FLESH_BALL*ELK_BIRD* HELMET_SNAKE*GREEN_DEVOURER*RUTHERER*CREEPY_CRAWLER*DRALTHA*GIANT_EARTHWORM* BLOOD_MAN*BUGBAT*MANERA* -MOLEMARIAN*JABBERER* POND_GRABBER*BLIND_CAVE_BEAR* CAVE_DRAGON*REACHER*ELEMENTMAN_GABBRO*GORLAK* CAVE_FLOATER*PLUMP_HELMET_MAN* CAVE_BLOB*ELEMENTMAN_AMETHYST*OCTOPUS* OCTOPUS_MAN* GIANT_OCTOPUS*CRAB*CRAB_MAN* -GIANT_CRAB* LEOPARD_SEAL*LEOPARD_SEAL_MAN*GIANT_LEOPARD_SEAL* -CUTTLEFISH*CUTTLEFISH_MAN*GIANT_CUTTLEFISH*ORCA*ORCA_MAN* -GIANT_ORCA*SPONGE* -SPONGE_MAN* GIANT_SPONGE*HORSESHOE_CRAB*HORSESHOE_CRAB_MAN*GIANT_HORSESHOE_CRAB* SPERM_WHALE*SPERM_WHALE_MAN*GIANT_SPERM_WHALE* ELEPHANT_SEAL*ELEPHANT_SEAL_MAN*GIANT_ELEPHANT_SEAL* HARP_SEAL* HARP_SEAL_MAN*GIANT_HARP_SEAL*NAUTILUS* NAUTILUS_MAN*GIANT_NAUTILUS* FOXSQUIRREL* MOGHOPPER* RAT_DEMON*WAMBLER_FLUFFY*LIZARD_RHINO_TWO_LEGGED* WORM_KNUCKLE*SPIDER_PHANTOM* FLY_ACORN* -GNAT_BLOOD*LIZARD* -LIZARD_MAN* GIANT_LIZARD*SKINK* SKINK_MAN* GIANT_SKINK* CHAMELEON* CHAMELEON_MAN*GIANT_CHAMELEON*ANOLE* ANOLE_MAN* GIANT_ANOLE*IGUANA* -IGUANA_MAN* GIANT_IGUANA* RIVER OTTER* SEA OTTER* OTTER_MAN* GIANT_OTTER*SNAPPING TURTLE*ALLIGATOR SNAPPING TURTLE*SNAPPING_TURTLE_MAN*GIANT_SNAPPING_TURTLE*BEAVER* -BEAVER_MAN* GIANT_BEAVER*LEECH* LEECH_MAN* GIANT_LEECH*AXOLOTL* AXOLOTL_MAN* GIANT_AXOLOTL*MINK*MINK_MAN* -GIANT_MINK* POND_TURTLE*POND_TURTLE_MAN*GIANT_POND_TURTLE*RAT*RAT_MAN* SQUIRREL_GRAY*SQUIRREL_GRAY_MAN*GIANT_SQUIRREL_GRAY* SQUIRREL_RED*SQUIRREL_RED_MAN*GIANT_SQUIRREL_RED*CHIPMUNK* CHIPMUNK_MAN*GIANT_CHIPMUNK*HAMSTER* HAMSTER_MAN* GIANT_HAMSTER*HEDGEHOG* HEDGEHOG_MAN*GIANT_HEDGEHOG*SQUIRREL_FLYING*FLYING_SQUIRREL_MAN*GIANT_FLYING_SQUIRREL*MUSSEL*OYSTER* FISH_SALMON*FISH_CLOWNFISH* FISH_HAGFISH*FISH_LAMPREY_BROOK* FISH_RAY_BAT*FISH_RAY_THORNBACK*FISH_RATFISH_SPOTTED* FISH_HERRING* FISH_SHAD* FISH_ANCHOVY*FISH_TROUT_STEELHEAD* FISH_HAKE* FISH_SEAHORSE* FISH_GLASSEYE*FISH_PUFFER_WHITE_SPOTTED* FISH_SOLE* FISH_FLOUNDER* FISH_MACKEREL*JELLYFISH_SEA_NETTLE*SQUID* SQUID MAN*GIGANTIC SQUID* FISH_LUNGFISH*FISH_LOACH_CLOWN*FISH_BULLHEAD_BROWN*FISH_BULLHEAD_YELLOW*FISH_BULLHEAD_BLACK*FISH_KNIFEFISH_BANDED* FISH_CHAR*FISH_TROUT_RAINBOW*FISH_MOLLY_SAILFIN* -FISH_GUPPY* -FISH_PERCH*DWARF*HUMAN*ELF*GOBLIN*KOBOLD*GREMLIN*TROLL*OGRE*UNICORN*DRAGON*SATYR*COLOSSUS_BRONZE*GIANT*CYCLOPS*ETTIN*MINOTAUR*YETI* SASQUATCH* BLIZZARD_MAN*WOLF_ICE*FAIRY*PIXIE*BEAK_DOG* GRIMELING* BLENDEC_FOUL* STRANGLER* NIGHTWING*HARPY*HYDRA* MERPERSON* SEA_SERPENT* SEA_MONSTER*BIRD_ROC*CROCODILE_CAVE*TOAD_GIANT_CAVE* OLM_GIANT* BAT_GIANT* RAT_GIANT* RAT_LARGE*MOLE_DOG_NAKED* -TROGLODYTE* -MOLE_GIANT*IMP_FIRE*SPIDER_CAVE_GIANT* SPIDER_CAVE* FISH_CAVE* CAVE_FISH_MAN* LOBSTER_CAVE* -SNAKE_FIRE*OLM*OLM_MAN*BAT*BAT_MAN*MAGGOT_PURRING*ELEMENTMAN_FIRE*ELEMENTMAN_MAGMA*ELEMENTMAN_IRON*ELEMENTMAN_MUD*BIRD_SWALLOW_CAVE*CAVE_SWALLOW_MAN*BIRD_SWALLOW_CAVE_GIANT* AMPHIBIAN_MAN* REPTILE_MAN* SERPENT_MAN*ANT_MAN* -RODENT MAN* WILD_BOAR* WILD_BOAR_MAN*GIANT_WILD_BOAR*COYOTE* -COYOTE_MAN* GIANT_COYOTE*KANGAROO* KANGAROO_MAN*GIANT_KANGAROO*KOALA* KOALA_MAN* GIANT_KOALA*ADDER* ADDER_MAN* GIANT_ADDER*ECHIDNA* ECHIDNA_MAN* GIANT_ECHIDNA* PORCUPINE* PORCUPINE_MAN*GIANT_PORCUPINE* KINGSNAKE* KINGSNAKE_MAN*GIANT_KINGSNAKE* GRAY_LANGUR*GRAY_LANGUR_MAN*GIANT_GRAY_LANGUR*BOBCAT* -BOBCAT_MAN* GIANT_BOBCAT*SKUNK* SKUNK_MAN* GIANT_SKUNK*GREEN_TREE_FROG*GREEN_TREE_FROG_MAN*GIANT_GREEN_TREE_FROG*HARE*HARE_MAN* -GIANT_HARE* RATTLESNAKE*RATTLESNAKE_MAN*GIANT_RATTLESNAKE*WEASEL* -WEASEL_MAN* GIANT_WEASEL*COPPERHEAD_SNAKE*COPPERHEAD_SNAKE_MAN*GIANT_COPPERHEAD_SNAKE*IBEX*IBEX_MAN* -GIANT_IBEX*WOMBAT* -WOMBAT_MAN* GIANT_WOMBAT*DINGO* DINGO_MAN* GIANT_DINGO*COATI* COATI_MAN* GIANT_COATI*OPOSSUM* OPOSSUM_MAN* GIANT_OPOSSUM*MONGOOSE* MONGOOSE_MAN*GIANT_MONGOOSE*HYENA* HYENA_MAN* GIANT_HYENA*ANACONDA* ANACONDA_MAN*GIANT_ANACONDA*MONITOR_LIZARD*MONITOR_LIZARD_MAN*GIANT_MONITOR_LIZARD* -KING_COBRA*KING_COBRA_MAN*GIANT_KING_COBRA*OCELOT* -OCELOT_MAN* GIANT_OCELOT*JACKAL* -JACKAL_MAN* GIANT_JACKAL*CAPUCHIN* CAPUCHIN_MAN*GIANT_CAPUCHIN*SLOTH* SLOTH_MAN* GIANT_SLOTH* SPIDER_MONKEY*SPIDER_MONKEY_MAN*GIANT_SPIDER_MONKEY*PANGOLIN* PANGOLIN_MAN*GIANT_PANGOLIN* BLACK_MAMBA*BLACK_MAMBA_MAN*GIANT_BLACK_MAMBA* -BEAR_SLOTH*SLOTH_BEAR_MAN*GIANT_SLOTH_BEAR*AYE-AYE* AYE-AYE_MAN* GIANT_AYE-AYE* -BUSHMASTER*BUSHMASTER_MAN*GIANT_BUSHMASTER*PYTHON* -PYTHON_MAN* GIANT_PYTHON*TAPIR* TAPIR_MAN* GIANT_TAPIR*IMPALA* -IMPALA_MAN* GIANT_IMPALA*AARDVARK* AARDVARK_MAN*GIANT_AARDVARK* LION_TAMARIN*LION_TAMARIN_MAN*GIANT_LION_TAMARIN*STOAT* STOAT_MAN* GIANT_STOAT*LYNX*LYNX_MAN* -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file diff --git a/data/stockpiles/teeth.dfstock b/data/stockpiles/teeth.dfstock deleted file mode 100644 index 06875f8b5..000000000 --- a/data/stockpiles/teeth.dfstock +++ /dev/null @@ -1,157 +0,0 @@ -*ÆW -WOOD -DOOR - FLOODGATE -BED -CHAIR -CHAIN -FLASK -GOBLET - -INSTRUMENT -TOY -WINDOW -CAGE -BARREL -BUCKET - -ANIMALTRAP -TABLE -COFFIN -STATUE -WEAPON -ARMOR -SHOES -SHIELD -HELM -GLOVES -BOX -BAG -BIN - -ARMORSTAND - -WEAPONRACK -CABINET -FIGURINE -AMULET -SCEPTER -AMMO -CROWN -RING -EARRING -BRACELET -GEM -ANVIL -REMAINS -MEAT -FISH -FISH_RAW -VERMIN -PET -SEEDS -PLANT - SKIN_TANNED - PLANT_GROWTH -THREAD -CLOTH -TOTEM -PANTS -BACKPACK -QUIVER - CATAPULTPARTS - BALLISTAPARTS - SIEGEAMMO -BALLISTAARROWHEAD - TRAPPARTS -TRAPCOMP -DRINK - POWDER_MISC -CHEESE -FOOD - LIQUID_MISC -COIN -GLOB - PIPE_SECTION - HATCH_COVER -GRATE -QUERN - MILLSTONE -SPLINT -CRUTCH -TRACTION_BENCH -TOOL -SLAB -EGG -BOOK -SHEET -BRANCHJTOADJTOAD_MANJ -GIANT_TOADJWORMJWORM_MANJ BIRD_BLUEJAYJ BLUEJAY_MANJ GIANT_BLUEJAYJ BIRD_CARDINALJ CARDINAL_MANJGIANT_CARDINALJ BIRD_GRACKLEJ GRACKLE_MANJ GIANT_GRACKLEJ BIRD_ORIOLEJ -ORIOLE_MANJ GIANT_ORIOLEJBIRD_RW_BLACKBIRDJRW_BLACKBIRD_MANJGIANT_RW_BLACKBIRDJ BIRD_PENGUINJBIRD_PENGUIN_LITTLEJBIRD_PENGUIN_EMPERORJ PENGUIN MANJBIRD_PENGUIN_GIANTJBIRD_FALCON_PEREGRINEJPEREGRINE FALCON MANJGIANT PEREGRINE FALCONJ BIRD_KIWIJKIWI MANJBIRD_KIWI_GIANTJ BIRD_OSTRICHJ OSTRICH MANJBIRD_OSTRICH_GIANTJ BIRD_CROWJCROW_MANJ -GIANT_CROWJ -BIRD_RAVENJ RAVEN_MANJ GIANT_RAVENJBIRD_CASSOWARYJ CASSOWARY_MANJGIANT_CASSOWARYJBIRD_KEAJKEA_MANJ GIANT_KEAJBIRD_OWL_SNOWYJ SNOWY_OWL_MANJGIANT_SNOWY_OWLJSPARROWJ SPARROW_MANJ GIANT_SPARROWJBIRD_STORK_WHITEJWHITE_STORK_MANJGIANT_WHITE_STORKJ BIRD_LOONJLOON_MANJ -GIANT_LOONJ BIRD_OWL_BARNJ BARN_OWL_MANJGIANT_BARN_OWLJ BIRD_PARAKEETJ PARAKEET_MANJGIANT_PARAKEETJ BIRD_KAKAPOJ -KAKAPO_MANJ GIANT_KAKAPOJBIRD_PARROT_GREYJGREY_PARROT_MANJGIANT_GREY_PARROTJ BIRD_PUFFINJ -PUFFIN_MANJ GIANT_PUFFINJ BIRD_SWANJSWAN_MANJ -GIANT_SWANJ BIRD_LORIKEETJ LORIKEET_MANJGIANT_LORIKEETJ BIRD_WRENJWREN_MANJ -GIANT_WRENJ BIRD_OSPREYJ -OSPREY_MANJ GIANT_OSPREYJBIRD_EMUJEMU_MANJ GIANT_EMUJBIRD_COCKATIELJ COCKATIEL_MANJGIANT_COCKATIELJBIRD_LOVEBIRD_PEACH-FACEDJPEACH-FACED_LOVEBIRD_MANJGIANT_PEACH-FACED_LOVEBIRDJ BIRD_MAGPIEJ -MAGPIE_MANJ GIANT_MAGPIEJ BIRD_KESTRELJ KESTREL_MANJ GIANT_KESTRELJBIRD_ALBATROSSJ ALBATROSS_MANJGIANT_ALBATROSSJBIRD_OWL_GREAT_HORNEDJGREAT_HORNED_OWL_MANJGIANT_GREAT_HORNED_OWLJ -BIRD_EAGLEJ EAGLE_MANJ GIANT_EAGLEJ BIRD_HORNBILLJ HORNBILL_MANJGIANT_HORNBILLJBIRD_LOVEBIRD_MASKEDJMASKED_LOVEBIRD_MANJGIANT_MASKED_LOVEBIRDJ BIRD_BUSHTITJ BUSHTIT_MANJ GIANT_BUSHTITJ DAMSELFLYJ DAMSELFLY_MANJGIANT_DAMSELFLYJMOTHJMOTH_MANJ -GIANT_MOTHJ GRASSHOPPERJGRASSHOPPER_MANJGIANT_GRASSHOPPERJ BARK_SCORPIONJBARK_SCORPION_MANJGIANT_BARK_SCORPIONJMANTISJ -MANTIS_MANJ GIANT_MANTISJTICKJTICK_MANJ -GIANT_TICKJLOUSEJ LOUSE_MANJ GIANT_LOUSEJTHRIPSJ -THRIPS_MANJ GIANT_THRIPSJSLUGJSLUG_MANJ -GIANT_SLUGJMOSQUITOJ MOSQUITO_MANJGIANT_MOSQUITOJSPIDER_JUMPINGJJUMPING_SPIDER_MANJGIANT_JUMPING_SPIDERJTERMITEJ -MOON_SNAILJMOON_SNAIL_MANJGIANT_MOON_SNAILJSPIDER_BROWN_RECLUSEJBROWN_RECLUSE_SPIDER_MANJGIANT_BROWN_RECLUSE_SPIDERJSNAILJ SNAIL_MANJ GIANT_SNAILJ GECKO_LEOPARDJLEOPARD_GECKO_MANJGIANT_LEOPARD_GECKOJDESERT TORTOISEJDESERT_TORTOISE_MANJGIANT_DESERT_TORTOISEJ GILA_MONSTERJGILA_MONSTER_MANJGIANT_GILA_MONSTERJDOGJCATJMULEJDONKEYJHORSEJCOWJSHEEPJPIGJGOATJ BIRD_CHICKENJCAVYJ BIRD_DUCKJ WATER_BUFFALOJREINDEERJ -BIRD_GOOSEJYAKJLLAMAJALPACAJBIRD_GUINEAFOWLJBIRD_PEAFOWL_BLUEJ BIRD_TURKEYJRABBITJCHIMERAJCENTAURJGRIFFONJFLYJFLY_MANJ GIANT_FLYJ ROACH_LARGEJ ROACH_MANJ GIANT_ROACHJBEETLEJ -BEETLE_MANJ GIANT_BEETLEJANTJBUTTERFLY_MONARCHJBUTTERFLY_MONARCH_MANJGIANT_BUTTERFLY_MONARCHJFIREFLYJ FIREFLY_MANJ GIANT_FIREFLYJ DRAGONFLYJ DRAGONFLY_MANJGIANT_DRAGONFLYJ HONEY_BEEJ BUMBLEBEEJ GOAT_MOUNTAINJGOAT_MOUNTAIN_MANJGIANT_GOAT_MOUNTAINJ MARMOT_HOARYJMARMOT_HOARY_MANJGIANT_MARMOT_HOARYJGNOME_MOUNTAINJ -GNOME_DARKJWALRUSJ -WALRUS_MANJ GIANT_WALRUSJFISH_LAMPREY_SEAJSHARK_GREAT_WHITEJ SHARK_FRILLJSHARK_SPINY_DOGFISHJSHARK_WOBBEGONG_SPOTTEDJ SHARK_WHALEJ SHARK_BASKINGJ SHARK_NURSEJSHARK_MAKO_SHORTFINJSHARK_MAKO_LONGFINJ SHARK_TIGERJ -SHARK_BULLJSHARK_REEF_BLACKTIPJSHARK_REEF_WHITETIPJ -SHARK_BLUEJSHARK_HAMMERHEADJ SHARK_ANGELJFISH_SKATE_COMMONJFISH_RAY_MANTAJ FISH_STINGRAYJFISH_COELACANTHJ FISH_STURGEONJFISH_CONGER_EELJ FISH_MILKFISHJFISH_CODJ FISH_OPAHJFISH_GROUPER_GIANTJ FISH_BLUEFISHJFISH_SUNFISH_OCEANJFISH_SWORDFISHJ FISH_MARLINJ FISH_HALIBUTJFISH_BARRACUDA_GREATJFISH_TUNA_BLUEFINJNARWHALJ NARWHAL MANJNARWHAL, GIANTJHIPPOJ HIPPO_MANJ GIANT_HIPPOJFISH_GAR_LONGNOSEJ FISH_CARPJFISH_TIGERFISHJ FISH_PIKEJPLATYPUSJ PLATYPUS MANJPLATYPUS, GIANTJ BEAR_GRIZZLYJBEAR_GRIZZLY_MANJGIANT_BEAR_GRIZZLYJ -BEAR_BLACKJBEAR_BLACK_MANJGIANT_BEAR_BLACKJDEERJDEER_MANJ -GIANT_DEERJFOXJFOX_MANJ GIANT_FOXJRACCOONJ RACCOON_MANJ GIANT_RACCOONJMACAQUE_RHESUSJMACAQUE_RHESUS_MANJGIANT_MACAQUE_RHESUSJCOUGARJ -COUGAR_MANJ GIANT_COUGARJWOLFJWOLF_MANJ -GIANT_WOLFJ GROUNDHOGJ GROUNDHOG_MANJGIANT_GROUNDHOGJ ALLIGATORJ ALLIGATOR_MANJGIANT_ALLIGATORJ BIRD_BUZZARDJ BUZZARD_MANJ GIANT_BUZZARDJPANDAJPANDA, GIGANTICJ PANDA MANJCAPYBARAJCAPYBARA, GIANTJ CAPYBARA MANJBADGERJ -BADGER MANJ BADGER, GIANTJMOOSEJ MOOSE MANJ MOOSE, GIANTJ RED PANDAJ RED PANDA MANJRED PANDA, GIANTJELEPHANTJ ELEPHANT_MANJGIANT_ELEPHANTJWARTHOGJ WARTHOG_MANJ GIANT_WARTHOGJLIONJLION_MANJ -GIANT_LIONJLEOPARDJ LEOPARD_MANJ GIANT_LEOPARDJJAGUARJ -JAGUAR_MANJ GIANT_JAGUARJTIGERJ TIGER_MANJ GIANT_TIGERJCHEETAHJ CHEETAH_MANJ GIANT_CHEETAHJGAZELLEJ GAZELLE_MANJ GIANT_GAZELLEJMANDRILLJ MANDRILL_MANJGIANT_MANDRILLJ -CHIMPANZEEJBONOBOJGORILLAJ ORANGUTANJGIBBON_SIAMANGJGIBBON_WHITE_HANDEDJGIBBON_BLACK_HANDEDJ GIBBON_GRAYJGIBBON_SILVERYJGIBBON_PILEATEDJ GIBBON_BILOUJGIBBON_WHITE_BROWEDJGIBBON_BLACK_CRESTEDJ CAMEL_1_HUMPJCAMEL_1_HUMP_MANJGIANT_CAMEL_1_HUMPJ CAMEL_2_HUMPJCAMEL_2_HUMP_MANJGIANT_CAMEL_2_HUMPJCROCODILE_SALTWATERJCROCODILE_SALTWATER_MANJGIANT_CROCODILE_SALTWATERJ BIRD_VULTUREJ VULTURE_MANJ GIANT_VULTUREJ -RHINOCEROSJRHINOCEROS_MANJGIANT_RHINOCEROSJGIRAFFEJ GIRAFFE_MANJ GIANT_GIRAFFEJ HONEY BADGERJHONEY BADGER MANJHONEY BADGER, GIANTJGIANT TORTOISEJGIANT TORTOISE MANJGIGANTIC TORTOISEJ ARMADILLOJ ARMADILLO MANJARMADILLO, GIANTJMUSKOXJ -MUSKOX_MANJ GIANT_MUSKOXJELKJELK_MANJ GIANT_ELKJ -BEAR_POLARJBEAR_POLAR_MANJGIANT_BEAR_POLARJ WOLVERINEJ WOLVERINE_MANJGIANT_WOLVERINEJ -CHINCHILLAJCHINCHILLA_MANJGIANT_CHINCHILLAJ FLOATING_GUTSJDRUNIANJ CREEPING_EYEJVORACIOUS_CAVE_CRAWLERJBLIND_CAVE_OGREJ -CAP_HOPPERJ -MAGMA_CRABJCRUNDLEJ HUNGRY_HEADJ -FLESH_BALLJELK_BIRDJ HELMET_SNAKEJGREEN_DEVOURERJRUTHERERJCREEPY_CRAWLERJDRALTHAJGIANT_EARTHWORMJ BLOOD_MANJBUGBATJMANERAJ -MOLEMARIANJJABBERERJ POND_GRABBERJBLIND_CAVE_BEARJ CAVE_DRAGONJREACHERJELEMENTMAN_GABBROJGORLAKJ CAVE_FLOATERJPLUMP_HELMET_MANJ CAVE_BLOBJELEMENTMAN_AMETHYSTJOCTOPUSJ OCTOPUS_MANJ GIANT_OCTOPUSJCRABJCRAB_MANJ -GIANT_CRABJ LEOPARD_SEALJLEOPARD_SEAL_MANJGIANT_LEOPARD_SEALJ -CUTTLEFISHJCUTTLEFISH_MANJGIANT_CUTTLEFISHJORCAJORCA_MANJ -GIANT_ORCAJSPONGEJ -SPONGE_MANJ GIANT_SPONGEJHORSESHOE_CRABJHORSESHOE_CRAB_MANJGIANT_HORSESHOE_CRABJ SPERM_WHALEJSPERM_WHALE_MANJGIANT_SPERM_WHALEJ ELEPHANT_SEALJELEPHANT_SEAL_MANJGIANT_ELEPHANT_SEALJ HARP_SEALJ HARP_SEAL_MANJGIANT_HARP_SEALJNAUTILUSJ NAUTILUS_MANJGIANT_NAUTILUSJ FOXSQUIRRELJ MOGHOPPERJ RAT_DEMONJWAMBLER_FLUFFYJLIZARD_RHINO_TWO_LEGGEDJ WORM_KNUCKLEJSPIDER_PHANTOMJ FLY_ACORNJ -GNAT_BLOODJLIZARDJ -LIZARD_MANJ GIANT_LIZARDJSKINKJ SKINK_MANJ GIANT_SKINKJ CHAMELEONJ CHAMELEON_MANJGIANT_CHAMELEONJANOLEJ ANOLE_MANJ GIANT_ANOLEJIGUANAJ -IGUANA_MANJ GIANT_IGUANAJ RIVER OTTERJ SEA OTTERJ OTTER_MANJ GIANT_OTTERJSNAPPING TURTLEJALLIGATOR SNAPPING TURTLEJSNAPPING_TURTLE_MANJGIANT_SNAPPING_TURTLEJBEAVERJ -BEAVER_MANJ GIANT_BEAVERJLEECHJ LEECH_MANJ GIANT_LEECHJAXOLOTLJ AXOLOTL_MANJ GIANT_AXOLOTLJMINKJMINK_MANJ -GIANT_MINKJ POND_TURTLEJPOND_TURTLE_MANJGIANT_POND_TURTLEJRATJRAT_MANJ SQUIRREL_GRAYJSQUIRREL_GRAY_MANJGIANT_SQUIRREL_GRAYJ SQUIRREL_REDJSQUIRREL_RED_MANJGIANT_SQUIRREL_REDJCHIPMUNKJ CHIPMUNK_MANJGIANT_CHIPMUNKJHAMSTERJ HAMSTER_MANJ GIANT_HAMSTERJHEDGEHOGJ HEDGEHOG_MANJGIANT_HEDGEHOGJSQUIRREL_FLYINGJFLYING_SQUIRREL_MANJGIANT_FLYING_SQUIRRELJMUSSELJOYSTERJ FISH_SALMONJFISH_CLOWNFISHJ FISH_HAGFISHJFISH_LAMPREY_BROOKJ FISH_RAY_BATJFISH_RAY_THORNBACKJFISH_RATFISH_SPOTTEDJ FISH_HERRINGJ FISH_SHADJ FISH_ANCHOVYJFISH_TROUT_STEELHEADJ FISH_HAKEJ FISH_SEAHORSEJ FISH_GLASSEYEJFISH_PUFFER_WHITE_SPOTTEDJ FISH_SOLEJ FISH_FLOUNDERJ FISH_MACKERELJJELLYFISH_SEA_NETTLEJSQUIDJ SQUID MANJGIGANTIC SQUIDJ FISH_LUNGFISHJFISH_LOACH_CLOWNJFISH_BULLHEAD_BROWNJFISH_BULLHEAD_YELLOWJFISH_BULLHEAD_BLACKJFISH_KNIFEFISH_BANDEDJ FISH_CHARJFISH_TROUT_RAINBOWJFISH_MOLLY_SAILFINJ -FISH_GUPPYJ -FISH_PERCHJDWARFJHUMANJELFJGOBLINJKOBOLDJGREMLINJTROLLJOGREJUNICORNJDRAGONJSATYRJCOLOSSUS_BRONZEJGIANTJCYCLOPSJETTINJMINOTAURJYETIJ SASQUATCHJ BLIZZARD_MANJWOLF_ICEJFAIRYJPIXIEJBEAK_DOGJ GRIMELINGJ BLENDEC_FOULJ STRANGLERJ NIGHTWINGJHARPYJHYDRAJ MERPERSONJ SEA_SERPENTJ SEA_MONSTERJBIRD_ROCJCROCODILE_CAVEJTOAD_GIANT_CAVEJ OLM_GIANTJ BAT_GIANTJ RAT_GIANTJ RAT_LARGEJMOLE_DOG_NAKEDJ -TROGLODYTEJ -MOLE_GIANTJIMP_FIREJSPIDER_CAVE_GIANTJ SPIDER_CAVEJ FISH_CAVEJ CAVE_FISH_MANJ LOBSTER_CAVEJ -SNAKE_FIREJOLMJOLM_MANJBATJBAT_MANJMAGGOT_PURRINGJELEMENTMAN_FIREJELEMENTMAN_MAGMAJELEMENTMAN_IRONJELEMENTMAN_MUDJBIRD_SWALLOW_CAVEJCAVE_SWALLOW_MANJBIRD_SWALLOW_CAVE_GIANTJ AMPHIBIAN_MANJ REPTILE_MANJ SERPENT_MANJANT_MANJ -RODENT MANJ WILD_BOARJ WILD_BOAR_MANJGIANT_WILD_BOARJCOYOTEJ -COYOTE_MANJ GIANT_COYOTEJKANGAROOJ KANGAROO_MANJGIANT_KANGAROOJKOALAJ KOALA_MANJ GIANT_KOALAJADDERJ ADDER_MANJ GIANT_ADDERJECHIDNAJ ECHIDNA_MANJ GIANT_ECHIDNAJ PORCUPINEJ PORCUPINE_MANJGIANT_PORCUPINEJ KINGSNAKEJ KINGSNAKE_MANJGIANT_KINGSNAKEJ GRAY_LANGURJGRAY_LANGUR_MANJGIANT_GRAY_LANGURJBOBCATJ -BOBCAT_MANJ GIANT_BOBCATJSKUNKJ SKUNK_MANJ GIANT_SKUNKJGREEN_TREE_FROGJGREEN_TREE_FROG_MANJGIANT_GREEN_TREE_FROGJHAREJHARE_MANJ -GIANT_HAREJ RATTLESNAKEJRATTLESNAKE_MANJGIANT_RATTLESNAKEJWEASELJ -WEASEL_MANJ GIANT_WEASELJCOPPERHEAD_SNAKEJCOPPERHEAD_SNAKE_MANJGIANT_COPPERHEAD_SNAKEJIBEXJIBEX_MANJ -GIANT_IBEXJWOMBATJ -WOMBAT_MANJ GIANT_WOMBATJDINGOJ DINGO_MANJ GIANT_DINGOJCOATIJ COATI_MANJ GIANT_COATIJOPOSSUMJ OPOSSUM_MANJ GIANT_OPOSSUMJMONGOOSEJ MONGOOSE_MANJGIANT_MONGOOSEJHYENAJ HYENA_MANJ GIANT_HYENAJANACONDAJ ANACONDA_MANJGIANT_ANACONDAJMONITOR_LIZARDJMONITOR_LIZARD_MANJGIANT_MONITOR_LIZARDJ -KING_COBRAJKING_COBRA_MANJGIANT_KING_COBRAJOCELOTJ -OCELOT_MANJ GIANT_OCELOTJJACKALJ -JACKAL_MANJ GIANT_JACKALJCAPUCHINJ CAPUCHIN_MANJGIANT_CAPUCHINJSLOTHJ SLOTH_MANJ GIANT_SLOTHJ SPIDER_MONKEYJSPIDER_MONKEY_MANJGIANT_SPIDER_MONKEYJPANGOLINJ PANGOLIN_MANJGIANT_PANGOLINJ BLACK_MAMBAJBLACK_MAMBA_MANJGIANT_BLACK_MAMBAJ -BEAR_SLOTHJSLOTH_BEAR_MANJGIANT_SLOTH_BEARJAYE-AYEJ AYE-AYE_MANJ GIANT_AYE-AYEJ -BUSHMASTERJBUSHMASTER_MANJGIANT_BUSHMASTERJPYTHONJ -PYTHON_MANJ GIANT_PYTHONJTAPIRJ TAPIR_MANJ GIANT_TAPIRJIMPALAJ -IMPALA_MANJ GIANT_IMPALAJAARDVARKJ AARDVARK_MANJGIANT_AARDVARKJ LION_TAMARINJLION_TAMARIN_MANJGIANT_LION_TAMARINJSTOATJ STOAT_MANJ GIANT_STOATJLYNXJLYNX_MANJ -GIANT_LYNXPX˜ ¨°¸À \ No newline at end of file From 0d1caafeb36ab0ee7a98ad8cff6ef29a419434df Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:08:37 -0700 Subject: [PATCH 0876/2222] update stone stockpile defs --- data/stockpiles/bauxite.dfstock | Bin 42 -> 21 bytes data/stockpiles/clay.dfstock | Bin 125 -> 104 bytes data/stockpiles/coalproducing.dfstock | Bin 69 -> 48 bytes data/stockpiles/economic.dfstock | Bin 266 -> 245 bytes data/stockpiles/flux.dfstock | Bin 118 -> 97 bytes .../{iron.dfstock => ironore.dfstock} | Bin 84 -> 63 bytes .../{metal.dfstock => metalore.dfstock} | Bin 389 -> 368 bytes data/stockpiles/otherstone.dfstock | Bin 1123 -> 1102 bytes ...aster.dfstock => plasterproducing.dfstock} | Bin 103 -> 82 bytes 9 files changed, 0 insertions(+), 0 deletions(-) rename data/stockpiles/{iron.dfstock => ironore.dfstock} (51%) rename data/stockpiles/{metal.dfstock => metalore.dfstock} (87%) rename data/stockpiles/{plaster.dfstock => plasterproducing.dfstock} (60%) diff --git a/data/stockpiles/bauxite.dfstock b/data/stockpiles/bauxite.dfstock index 25524dcb9032e269b96a0f3e161a091056a1c451..83a49cb5b46a80a7e75a73eafe9b9711f6455162 100644 GIT binary patch delta 4 LcmdNAogfMT0w4h8 delta 26 icmWgDnjk7Vfst_rBjW-_h82tq8yFdPFftrqWB>q6eg&KW diff --git a/data/stockpiles/clay.dfstock b/data/stockpiles/clay.dfstock index 7e661688d746282ef78dd723b8c54c97b24b3695..e01c7abaf7d58c77aec38c6b4f65b5523f40baca 100644 GIT binary patch delta 4 Lcmb=;n2-Sg1h@g- delta 26 icmd0(osc0qfst_rBjW-_h82tq8yFdPFftrqWB>qUdqEOa<)# diff --git a/data/stockpiles/economic.dfstock b/data/stockpiles/economic.dfstock index b69a8034c0f694c53da4e9d61fbdc15695d09241..976f4cc11f7a5094000d3be86817cb054b515097 100644 GIT binary patch delta 6 NcmeBT`pP)rD*y>N0|)>B delta 28 kcmey$*u^yAtLOwq#uqSb_Rd| diff --git a/data/stockpiles/iron.dfstock b/data/stockpiles/ironore.dfstock similarity index 51% rename from data/stockpiles/iron.dfstock rename to data/stockpiles/ironore.dfstock index 90182276c7dd93c5de4a4fff79c828085788a190..2108b60ac3ecba9a4671642eef6870b0c9e86842 100644 GIT binary patch delta 4 LcmWHspI{FF18@Oj delta 26 icmcBynP4wEfst_rBjW-_h82tq8yFdPFftrqWB>qIq6QiO diff --git a/data/stockpiles/metal.dfstock b/data/stockpiles/metalore.dfstock similarity index 87% rename from data/stockpiles/metal.dfstock rename to data/stockpiles/metalore.dfstock index a8b58b0adb9a986c64b6293b6a09bcf4d81026b8..8cbb60959edb7679dcb68f4d7cf8c7504bb03643 100644 GIT binary patch delta 7 OcmZo={=l@MfDr%-WdfW4 delta 29 lcmeys)XKb}fKhY;BjXH4#s!QFD;OCzFf#67WH`Xc004Yu2U7q5 diff --git a/data/stockpiles/otherstone.dfstock b/data/stockpiles/otherstone.dfstock index 79c865f4e5bc598b68f1d683d2ce5976c7070be7..1f9f867edf1c8544c656445f60f1bc88abe1d681 100644 GIT binary patch delta 7 OcmaFNagJky9}55tLIT79 delta 29 lcmX@d@t9+SAB*S&M#dS8j0+eURxmPbU}W6E$Z&v>0RVut2YUbj diff --git a/data/stockpiles/plaster.dfstock b/data/stockpiles/plasterproducing.dfstock similarity index 60% rename from data/stockpiles/plaster.dfstock rename to data/stockpiles/plasterproducing.dfstock index 6ac4d4607b4b2df25fa02c132bc910b5046cee8b..2764150a3f55b7791ba460ef3c692992856ca887 100644 GIT binary patch delta 4 LcmYcfnh*p41PB3~ delta 26 icmWGapAaNEfst_rBjW-_h82tq8yFdPFftrqWB>qOAO=$a From fecebe5d711f7d4a33064b59e2b94be17e261e53 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:12:54 -0700 Subject: [PATCH 0877/2222] update ammo stockpile defs --- data/stockpiles/artifactammo.dfstock | Bin 33 -> 12 bytes data/stockpiles/bolts.dfstock | Bin 45 -> 923 bytes data/stockpiles/boneammo.dfstock | Bin 29 -> 8 bytes data/stockpiles/masterworkammo.dfstock | Bin 34 -> 13 bytes data/stockpiles/metalammo.dfstock | Bin 768 -> 725 bytes data/stockpiles/woodammo.dfstock | Bin 29 -> 8 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/artifactammo.dfstock b/data/stockpiles/artifactammo.dfstock index 874ec21064edbf5f45e96dcb2a57a5a51462099c..16cd6f57e91f5567da74cd624dcbaa24cf529bec 100644 GIT binary patch literal 12 TcmZ?bQsQtdD#=VsOfCTc6AlBV literal 33 pcmZ?bQsQtdD#=VsOfH$g$T)+MaRDR43PyMG*AN!H`Vp@iB(-KZ>ZDnu7^Z$-e6$ zYGFYz^ULNJedG}6PNGw2PnruZQRH*tfkzD;Ea^1-0B;pk8jQ`L2PtGKo{OJ;R2ngR zc&0j{uDR`r^=eqZh7D@i5E#3ZGrD!KR|orba8L(_R+HzebQ9+Xt2Ixu%+2FTEewXCwHn&>ndztKtbi0W&>zc-2MUt=gnbm}Ok*?!qVV{k*X|ykm_StBg KMq3;0v+)1$lq_H@-s$$Br$h?AD78;OlJX8Lk<%F delta 92 zcmcc0+Q6pl^nz82&(qI8$lcM;)7i>1$lni{@9yv8vQhXMqo@c%O|XZnk56!jtE=@nv diff --git a/data/stockpiles/woodammo.dfstock b/data/stockpiles/woodammo.dfstock index 623ca25f239a3faf33bc1e79710a50a0356cb6c8..6bec8b1e589154621fba15a68f13d3a73a472e2e 100644 GIT binary patch literal 8 PcmZ=_6JiPX_jdsR1?>Tt literal 29 lcmZ=_6JiPX_jj4V$T)+MaRDR43Py Date: Mon, 20 Mar 2023 17:17:25 -0700 Subject: [PATCH 0878/2222] update bars/blocks stockpile defs --- data/stockpiles/ash.dfstock | Bin 28 -> 7 bytes data/stockpiles/bars.dfstock | Bin 803 -> 760 bytes data/stockpiles/blocks.dfstock | Bin 2671 -> 2662 bytes data/stockpiles/coal.dfstock | Bin 31 -> 8 bytes data/stockpiles/ironbars.dfstock | Bin 39 -> 18 bytes data/stockpiles/metalbars.dfstock | Bin 768 -> 725 bytes data/stockpiles/otherbars.dfstock | Bin 715 -> 37 bytes data/stockpiles/pearlash.dfstock | Bin 33 -> 12 bytes data/stockpiles/pigironbars.dfstock | Bin 43 -> 22 bytes data/stockpiles/potash.dfstock | Bin 31 -> 10 bytes data/stockpiles/soap.dfstock | Bin 29 -> 8 bytes data/stockpiles/steelbars.dfstock | Bin 40 -> 19 bytes 12 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/ash.dfstock b/data/stockpiles/ash.dfstock index 70948081e2e7a2f858a236093a08acc37cdeac68..d313e0096d9bd3df718a9b7dfe60dfa50de46d89 100644 GIT binary patch literal 7 OcmWGy delta 79 zcmeytx|oeQsGDsfvk5<+r=Ne2yQ80{vz5EQkIP20XN;mE2(e%fS0A6?5LZ{9$=Xb3 bMJF&a&R}F*z{s$IQDOrl!wyD<1B?s+g8CJQ diff --git a/data/stockpiles/blocks.dfstock b/data/stockpiles/blocks.dfstock index 293dc52510e59dea7cdc58504a883a7cece347eb..cd08d93338e7f7e73da8d65b8c904069047f53f8 100644 GIT binary patch delta 72 zcmaDa@=Sy&=1$lq_H@-s$$Br$h?AD78;OlJXA^9~~b delta 92 zcmcc0+Q6n9^nz82&(qI8$lcM;)7i>1$lni{@9yv8vQhXMqo@c%O|XZnk56!jtEE diff --git a/data/stockpiles/otherbars.dfstock b/data/stockpiles/otherbars.dfstock index 8eb0d432c9397b1bf9cda39e40417fd28fb6e48f..4cbcc1c8f9edaee5a76d306803c3fcc9d1e07b46 100644 GIT binary patch literal 37 ncmWGi=3;U7cl6<63-Awd4EEq+1`!+qu8u)IAQnrozheLZk%9;g literal 715 zcmZvZyG{c!5JlGoWq5^0;=hQZC<>kJooq&#wO6(`h&28sN=iyfBpOOS4J)v+qjfpQ zGk5M>+pi0LiQ3pkXjD&)p_*T(+94gl=0usUJ122&50tAMQLB7LTBDD^uad5`!a07M zKu5x(?%SLJf$}!8N$QJD{63W(JbSQViyHXj2z!fL0)97oAn}s>B@lJKSWw@d%w&>( zSB7yQ5{ZoF^R&c8ON6=^njg}WB%$`i&p3u z&)L0h2Sm|N{GTlB8V|8AEDCH{V5q7u?4mO diff --git a/data/stockpiles/potash.dfstock b/data/stockpiles/potash.dfstock index 0db45dd7f02fc03b156fcdeec39002e5355f8711..8a09e446fe97cd24fe93d9840f65a46a3256c0df 100644 GIT binary patch literal 10 RcmWII;9?8#4{;3k000VX0y+Qy literal 31 ncmWII;9?8#4{;3kn83(5gOPCoBf|>>6 literal 40 wcmWF%l;ZdF^AB=&^z(GK3J!5~^_jrPID?UK0VBf-Mu`oK3_BPZ4lptR0LbGBmjD0& From 93b289042cbbda5dd860d2d1ed447f21d3320000 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:18:57 -0700 Subject: [PATCH 0879/2222] update gem stockpile defs --- data/stockpiles/cutgems.dfstock | Bin 2907 -> 2886 bytes data/stockpiles/cutglass.dfstock | Bin 64 -> 43 bytes data/stockpiles/cutstone.dfstock | Bin 1880 -> 1893 bytes data/stockpiles/roughgems.dfstock | Bin 2907 -> 2886 bytes data/stockpiles/roughglass.dfstock | Bin 64 -> 43 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/cutgems.dfstock b/data/stockpiles/cutgems.dfstock index 42949b8dcc2b9070e7c83af389a31a7f3ec5e305..1049acd6735e43165f17811674c93e0f7ceb57f1 100644 GIT binary patch delta 7 OcmcaDc1&!88#e$AWdhFt delta 29 lcmX>mc3W(N8@K2LM#dS8j0+eURxnCzU}V_A$Z&v>0RV!t2b}-_ diff --git a/data/stockpiles/cutglass.dfstock b/data/stockpiles/cutglass.dfstock index 7523381b419e564539214d990c8119cf6b4ab40a..6997d42acd16a5e7a44368c673e96614cb0570cd 100644 GIT binary patch delta 4 LcmZ?Jo}di?0?+{` delta 26 icmdOPn4m2>fst_rBjW-_h82tw8yFdOFftrqWB>qD!v*yK diff --git a/data/stockpiles/cutstone.dfstock b/data/stockpiles/cutstone.dfstock index ccbcaf42e9f2a154a1c3fb63aca66d0595102aa5..5e0f3f881ba08e522bf372252d1aa083988f934d 100644 GIT binary patch delta 46 zcmcb?_mqz*>d{6fM|LG81y4W!Aa_SUPiHHqAb;QBcxNA1N56Py|Db?iSC`P>5CBrx B4f_B9 delta 33 pcmaFLcY}{9>cB=OM|RN(jEplF85b}ztYDPbz{s$Jk>LO%0|2TQ2)_UT diff --git a/data/stockpiles/roughgems.dfstock b/data/stockpiles/roughgems.dfstock index 20ea6b98ace404d22c71d7a728116978e90db471..fdad4be1added15df89853ffa6f8849acfc412c5 100644 GIT binary patch delta 7 OcmcaDc1&!88#e$AWdhFt delta 29 lcmX>mc3W(N8@K2LM#dS8j0+eURxnCzU}V_A$Z&v>0RV!t2b}-_ diff --git a/data/stockpiles/roughglass.dfstock b/data/stockpiles/roughglass.dfstock index 9571b3f80ba1a33f3bacad1036beb0e4788c6eba..23b95b81ac8ac31bb49bd82b1fddf850cb8226ef 100644 GIT binary patch delta 4 LcmZ?Jo}di?0?+{` delta 26 icmdOPn4m2>fst_rBjW-_h82tw8yFdOFftrqWB>qD!v*yK From e9594a0c3beaeb39c288c5dcc1e6adcc9ad5f1ca Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:23:22 -0700 Subject: [PATCH 0880/2222] update finished goods stockpile defs --- data/stockpiles/artifactfinishedgoods.dfstock | Bin 33 -> 12 bytes data/stockpiles/crafts.dfstock | Bin 82 -> 5866 bytes data/stockpiles/goblets.dfstock | Bin 31 -> 5803 bytes .../masterworkfinishedgoods.dfstock | Bin 34 -> 13 bytes data/stockpiles/stonetools.dfstock | Bin 1886 -> 2063 bytes data/stockpiles/toys.dfstock | 2 ++ data/stockpiles/woodtools.dfstock | 2 ++ 7 files changed, 4 insertions(+) create mode 100644 data/stockpiles/toys.dfstock create mode 100644 data/stockpiles/woodtools.dfstock diff --git a/data/stockpiles/artifactfinishedgoods.dfstock b/data/stockpiles/artifactfinishedgoods.dfstock index 760b0867d663d7d8f5c4242ebf4d0b5dd8bb4546..08e8e0706ba17660eb19c85f176b0644dead52f3 100644 GIT binary patch literal 12 TcmYe|QsQtdD#=VsOfCTc6pjPM literal 33 pcmYe|QsQtdD#=VsOfH$g$T)+MaRDR43PyY{bJR+qmLRclnZeB-5Rf^wJV(%F4| zd4|>E@{JNM{kX`SkScEctWeeEd9XoMtFyhe`Rb}Mq7J<@Hk7Ng%+_j!_4KE-u3nut z&|+5SzEsAn-WVl9sod(!YqMLOiz>sPb7`G0tE<8(RriGv-mk7Cwz~1pTj@FujX`Graxj8`odH6o&3`;BWk$$dKPK(>2f4Fe z^@TP0`gaFmrhV0ha{cFpXwpEDiCk1bT1*6~0Q#ag>%SjVNaaMOWe@lOYV}??wf^gJ z*#}_t|1CDw0q_E~pcsTNBLIO062hTTNKmwOcgG;AtkXx}l;{q_l|$mxMie5<)Mutu zn|C5tYf7>ajrJRO(i+V`Y=F!;8lqrh8!@w#88mJRpY2!H`=$LPO$sRHTj8{q8lDnT zWr-fOg9{6>w8jWVq!%vdG)i=|GFsFeXC~@g<)c&7OY!5B;+Y-gTsOo@s5atOL5Ul3 zNHH|J-VH*+SZK4U1|;2TH%ZB0gC3g1R2x8lUqi8vOhdqKvkbejGPwqnl;h$4&qQV~$BFB=1|R!5ie-0LDI&HuvLn3Og%zEKj7rxz|pD zh7Or>ap`V2sf=w{CU_DW`KAm`xpcYg9?KjZ%U-8H+1pqR3xi;BS7^uyJdF z()iX0$32lKjntE(_zfnl&T#q?uN_0jm59#7oDz$As8*(LS_fP*P=rx;@G#YY%mq>3 z4;3aM4#7C6LG#}Z?SHE+=E@D&(u37Q43+AhA{y&XEVgUw`y)M4*R`n7sE5pul@iV0 zZAPXW@<6$N2L>EZ4hC-`^09`Li&4uG?U8U*J*JtAj~{S|G))E+;GU6_1VaUgsYWG- z=qRzS%`~PJREFG7B<&r^Yq49Ri|j&b)GdZT zYTcxx#8pPUZ?QsoN-IF>N!Q~DlNKl~xsFK9d(*MmNxJQrT2zH;7E?0~mne6~Z0d1Z zlt*cslqD2!@~4GZ69V-lPMpWVO$(8MIUSRHz~w1bLv6^$%B0uL&fMy4LhmEIM@nfV=X{mWE)gz zt4Kv;TjG8j<^o}MTvW_8sRN19ay=YmpC72P5E+I^w&u!iAO_l%i4*|&gv4R?f={$7 z7inClT{iO?MVwu5hGYLI9FL3ZEas-{OgbP)zn7&3Bao!1mBuxhB;bb(MIj$b9LFJ= zBN@_fQ*x+bB8zF9vDit6Lyd25$4e!_Y!r|8*yY0b2tcU9;3?q$JYxj_R10oU-$YOy zdm-@{fKQW{uE^4Y%`zS0J=?aPt8a9X0;Jue&$!Hn<2QnIux0axkv0Qpqr(Kzpn?;n z8qQ)$Q+$P)F6vZ`qH8=}9*9@)z9RW0;vI%9(%-Z>CXr%j4I;({b}=WbypNX?E+t?N zVuF{H>&)qDP(k(G>G%wkFhvH(*8zRpDozOlM6f068gy+PSmuMy(x+q{5JbJAOVoP4 zGq+JhAjDZQG#Y)2`V)j;6S8kk$iC$?O*XzfCH(4?@at2;Z%zrny}qz7-#>kPc=_e}YXA8BEMFcz zygq(^{dW8G@$vbW>F4%RG&)2`M9)5a#eEIP6^Yw*z hd42lu@ZIapCyh6(@n){^=99)7)_8N!c=Ji){{U{5F$Dks delta 31 ncmaE*8^n}kJ(1a3bOIye3`WKUj0`InB{ncJ>|kU#z{mgqhW!Vo diff --git a/data/stockpiles/goblets.dfstock b/data/stockpiles/goblets.dfstock index 6bf3c6ccb777ce4474a7bceb763e545de3a8e77a..b117634ba5c45e349563210aa9569cdc8a25a741 100644 GIT binary patch literal 5803 zcmZ`-U2o*J6@BQs+1A)Joqi?L78v-YfPp>)K@g;G6h%wip+qf7%~*aJIEfc9U~B@{ z0h)i*pV>oF+Bq6cA9Y3E&vOqi`Q3m2`P*MtwlFHJUhS`nAK(Flxh!^5#FyZC6>DJ z(7qgu&`5(CDL}?T!MXDWmwCdZYA$2 zp!H6ze|OYdIQYi`9~5+~jw?D{9g;UJGa~4=Z+M+DN(Kkno8w+ATL0H>4Z!~(VO+(k1C{cqS5jI z_yB6{UO2V>>+-h`!0P||*;yx*5vT>lAbcGG2sDro4vj*BqHTv;22o?3J^`mhcNnf5 z5~ntz5@DvkFumHm6{T8Jl8xxJ-@udJXa-^jWX{nL1smImnWe&@aZ~tgzp>sg?I&qc zKr!!x(_U(LN=TJ8demkY7Gi0Q5sXMLT*+ya=xAlMXgSV8w52LXr>K|W=9J=@9p&7R zVy>UEeok#P&9W!B?EZb?kB$}%RyJlsbL1F zB-@;FoA28NoSgEy&Gw0tyTL`+tf?I%&2NJ3T_eCLig~7TV%W%97^z~t`B#amLY0;y zI;dRfq*kbew;RFr@Lj_$4&qQV~$BFB=35w!5idS2IC=;HuvLn3Og&e zEKj7rxzkR9Xd9&=9%7PzL6-Wa}?jL>=xqhKxjFUP*G%C~>$JuEC z#%M4VpnR0r5jFrH7c~zGiz-AQ9=eE0MqZLa!42lTnsuiiiC`AeM7JDCT04^0VzopU z*@e`o9fm(@-K3+&RYtw*@rUx1{s5_GU5_J7`ax+abwp};Faw*Nq&EXoi)t{BoiCRUSjb?3hE90a?(%%rMTegBNCYs2JU-ypn2+gRL?) z)&lfJwn3G)iBv?kCGNLjE)ZtRMa5i`I*=%>)cGL$>`0B3C@@U2HCGRr7-&@?QUK%= z5{KCfKGCXDq;Z{A*}`iSadyGU$Not;9v9bH%w0X0bU=`PFKZ1(AW79KjcYPVzz=vg z;CN~r$03>{zmR^Ll0yv>Sxn=M#Y#FHTI}78mr8=!C?4;z%OB$-0HF$lr-1+Sj8y@7eRIEg~Ve3wk9!Mk);Kj6*|Oww(UJv-{>j@NUKMmahVOLZv^RJE9MO&Z2{0m zhY6xV1t)5i&tghbe1(}V>Qs$t=saE?h*$8wBKalaEru=9-}NOXkt(+a5n~6tn3Gj` zh?f&CC14I>f|r!*!s#Zfp!#lgdvX5(hctm}iGlTRbi4W~fIF_@`?5)mgr z(W z^4+|3L|YHJMF;`NSK3Udc4B{^7bi+2_z-p29C;cf-FQ5JD$ySBklA@v_TWMaHNAV0 zw`I!w;Y*HSlznwZ_VpRrH)mwua+)R^U!D_wbx!#8IpH_wgx_Ahw$DF2ez<@BX{BZT==JCVB(=XHKYyb0456_Q3K3`p`|9t=O)63(JAMT&7e$(9l`{m*J n{m)NVuf_ArrDyk?EpbB)&@HD0sE>!Zf&j~f36AIc(V literal 31 ncmYe|;9_(4ck*!!nZU?6gOPCoBf|C*wrO8I7ErV zzbGX$FR`dni90+sC&#%cF|8yuMTyrfGcPr#62|2SE-gqc%FHiP;&!b_PAw?O%+E{A xQR4JXEG|hcN-ND#;&3b~$xKU3F42OUsO64qq!y}?T5uz^kd4$r7^wv}5&*AKM}z Date: Mon, 20 Mar 2023 17:25:47 -0700 Subject: [PATCH 0881/2222] update cloth stockpile defs --- data/stockpiles/adamantinecloth.dfstock | Bin 45 -> 24 bytes data/stockpiles/adamantinethread.dfstock | Bin 45 -> 24 bytes data/stockpiles/cloth.dfstock | Bin 4919 -> 5009 bytes data/stockpiles/thread.dfstock | Bin 4919 -> 5009 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/stockpiles/adamantinecloth.dfstock b/data/stockpiles/adamantinecloth.dfstock index e63bd31d021f0d0dce2373a5c6e31b96906b9962..38781d313086daffb7f7f89cc18b323bf439af80 100644 GIT binary patch delta 4 LcmdOum>>ZF0yqHf delta 26 icmb2pogg7Pfst_rBjW-_h82tw8yFdOFftrqWB>q8MFqS7 diff --git a/data/stockpiles/adamantinethread.dfstock b/data/stockpiles/adamantinethread.dfstock index 0b50c6c3935b0a4b210398ef7ff255fac2984266..2231b389f544c9daf78536e8ff6e0e7737c80874 100644 GIT binary patch delta 4 LcmdOum>>ZF0yqHf delta 26 icmb2pogg7Pfst_rBjW-_h82tw8yFdOFftrqWB>q8MFqS7 diff --git a/data/stockpiles/cloth.dfstock b/data/stockpiles/cloth.dfstock index fbe98f3728f2c53ed198b3883c8b24cd95670c47..26c4c52f300b14ad85bf66ff1df64ad35dbba7f6 100644 GIT binary patch literal 5009 zcma)ANsiPo6g>l7&=LZPVF9RCiRWbJPPr~aSV!kLNMc8bpQdi(tR=ONCY zzu%8vv)1VT+M2X$?8&slLtFoK>Wik+rm zf^BD-a;!+eKBDp!6znu^HL764(lF|FoKE~y-lj0zY7CD<7Q?DzxYZcJ=Hhx`6t)_} z-N+cWg=0k=&KD-IB8e4ASl&!wDG8+T2+lelV<|B-ms1!zjwb`>up2qf5y$J3OW3d+ z_cT|q7k3E`7p9AlM<6T^FQzJ$?*7|mdMwLGzx#B(5<1H~LjbKu-TEMC1E1HCy$Rd=1O`fF?UU7y55ya5J- z422jBp2AXOgC~d~pr=wI8-y7e*-+3^C6JBO*?1qQ+!H7+s{tzsrZ9XEb7>14#9ZD2 z6z&Qbp!gbxC^RHDKq2gl;?RnQ2eH@{`n)eJfd?^$t#DWX#<9B`HUkqdbxdJjl#`4= zUNr1Frm?%JQ=Y&N86<9n3$3bIh77~_G5bRyyXY~1g5rp%i zZ~<`F2|NR23{xis>|Kc?l6a~~EX`8G0!R(pTWTCMR&R=-FBHRJLow_+!ncvIG*bfg zg|IXezOaPnG-357iL)zVrHSAqAv^;JUp2ywNm#uVZU*IFE9dn^F_;&HE3NSOR0LZ} zVL4P-8I*sL1cJtI5{b=HHaX;p#xEyqVJ!{J2KbY*M zG+(lAd;BZ3_Ryz8XD86s?ivp*hmHC1_e5QJ@L~X4vn$5E4!gCLx$(T$18>us{l4!^ zQT9!-@r#$vh_&m!ts8GBTaG2zZ>?>ztts07z_K(uGl0)WrYRefJM>|H*Vbh@-M^_k Y_$`XQN72t{@hf`yJ6imS7Js9~J!7g}d;kCd diff --git a/data/stockpiles/thread.dfstock b/data/stockpiles/thread.dfstock index eee0653acbf1edd4577813336a0bc5f45e5c2ba9..2774f50e8a5aa2e2a55c51b13760eadd8daf8f9a 100644 GIT binary patch literal 5009 zcma)A$#R=87{2>fyUk3OnJ$O6N6?OM3dpt^wgH^>#yj*u`pjh_2`6k{uAe^r`-1rU z@5k<2+8Etk8*^-Ho0+D+Z>ryA-hRT9ibL#LM`)8q+xP?ru>Mr)PsgyeS(L>Lb5FVLp9Z6b@|>Sc=5vD(rOPbGTt{ z0u_-6=7>ZvbR_Z>w{8oGu`L98L@1^(Yzu=`M>vj)d<6xEPGMUTEG2mM^wIof}O^#Mip#W8b;lQ(}|zT>lB7tjp1>~Vpw$yw;ChZTwE`V!d7Fr z8yUm4aIA>K`N9NNB(Wk1%bO`IC4m$k!CA*+EG35KatcGo@nql}b|c3*;&^>>2^*H< zp5_V`15aS+1YUe1f`d#%FaaWlJ3z$HBSEkf{4FIYRzz{=@Fo#>lL)+o1>Ud%ccVyf z8u90rz$GPdF~}HpfFzg_o-6AsOES1@B!eX-87yzfVM$2==Zh4u>ZHWx;%AG*-`md!Lsn?Adoywy&2d9-a_0e2wpY zn)0bH{2A`eyy2NCs=<2SZZmvlzvG9u=9C{YHN$6iIyoWWQ@;OT+P|su;XlyvlLBRDVN7v*F{VCVv~8Y&d9DlXz_%k)=e15fdb{5>Rh~al{{se^W1;{6 literal 4919 zcma)AyOPr|6ph<-B(TG4hDWhOY3p&Nq|-V|V%D)ej(f*ySsAA6F6vv3$TLj@)-KU6BrT4&A=HfBj+$^F0d~G=7md~7lL=1d%e6|Bd&1J z3Ku}bd&D16=@q;IB8I6G3CxSYl@<&ZKwwc7SUd#BL1Sqa5|&XA>|Nn!^yPjLg!7_s z0dUs|JOgA5Qzr%NU5O)-c&bS(%~HbxNDbRtY8*6HZ;GKW6vJUdG3+|Rw~??kQv&sc zurw3Cu!QF{Vf7}7vnyexiQpt5JOc<{HNuTaSiKc)2IXHXm-R(4m=}dBt?>9%1Y1gB zIaF8~lz);0g2rzWjlY64z9bqyI5Zx7+CM2TN2Kv3(f-L8utKgGo(*rcCTmR5y3&nd zr}bAOw$IBM?gZ`(cd9nVee%w5H}B4H=lwmtv4^r~y~d1jKjz1{>~&!Aq{_R0Fzan@ zKChd7_g84+p^vA=j-b6gR32LP8`JLhL|J%n(Sx-)WW!#E-P*!zJ?~Y|+tucHY#Wp1 zZI$i(;*~RE?WV2E${TvQ97=H9TU)L7CaeDg^V}Rv4?Z23Ca+BD(EI&OUFP|C|E}=h Y*C_fHML(k1&*=HDX!bjr{fTCO0Y-01TmS$7 From 02c57f944a5d2c558c73a95e216699f5c12e80c2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:39:40 -0700 Subject: [PATCH 0882/2222] update weapon stockpile defs --- data/stockpiles/adamantineweapons.dfstock | Bin 0 -> 29 bytes data/stockpiles/artifactweapons.dfstock | Bin 38 -> 17 bytes data/stockpiles/bronzeweapons.dfstock | Bin 46 -> 25 bytes data/stockpiles/copperweapons.dfstock | Bin 46 -> 25 bytes data/stockpiles/ironweapons.dfstock | Bin 44 -> 23 bytes data/stockpiles/masterworkweapons.dfstock | Bin 39 -> 18 bytes data/stockpiles/metalweapons.dfstock | Bin 773 -> 730 bytes data/stockpiles/platinumweapons.dfstock | Bin 0 -> 27 bytes data/stockpiles/silverweapons.dfstock | Bin 0 -> 25 bytes data/stockpiles/steelweapons.dfstock | Bin 45 -> 24 bytes data/stockpiles/stoneweapons.dfstock | Bin 1885 -> 1898 bytes data/stockpiles/trapcomponents.dfstock | Bin 224 -> 203 bytes data/stockpiles/unusableweapons.dfstock | Bin 0 -> 7 bytes data/stockpiles/usableweapons.dfstock | Bin 0 -> 7 bytes data/stockpiles/woodentools.dfstock | Bin 35 -> 0 bytes 15 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/stockpiles/adamantineweapons.dfstock create mode 100644 data/stockpiles/platinumweapons.dfstock create mode 100644 data/stockpiles/silverweapons.dfstock create mode 100644 data/stockpiles/unusableweapons.dfstock create mode 100644 data/stockpiles/usableweapons.dfstock delete mode 100644 data/stockpiles/woodentools.dfstock diff --git a/data/stockpiles/adamantineweapons.dfstock b/data/stockpiles/adamantineweapons.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..6236196f0e9703f3b256c8587c99be8f028b9d62 GIT binary patch literal 29 kcmZo-lu{D$^z#pLcl7ggwsLfF^mX(L@$_@GU~pgn0BVv3@&Et; literal 0 HcmV?d00001 diff --git a/data/stockpiles/artifactweapons.dfstock b/data/stockpiles/artifactweapons.dfstock index ba91931e0e069823cbeac65cb2dec05c24991ead..0299f41d2e2478893dba784749fe9fb1bbdb440a 100644 GIT binary patch literal 17 YcmZo-q8j|IN~ diff --git a/data/stockpiles/copperweapons.dfstock b/data/stockpiles/copperweapons.dfstock index d3a2aac2fcfca2deb38ce1ed0cee9b2c0f4705b1..a30c53107a94b0364d319b0623a231d090d7afa1 100644 GIT binary patch delta 4 LcmdOsoFEAR0zd%q delta 26 icmb2tn;q8j|IN~ diff --git a/data/stockpiles/ironweapons.dfstock b/data/stockpiles/ironweapons.dfstock index 2128596fe949bfe1720f9d1be04bb0944fa24676..8c0afecd16e397bccd998712a9d78c4bbf3904ac 100644 GIT binary patch delta 4 LcmdN9pCAqZ0x$sU delta 26 icmWgFnIJAYfst_rBjW-_h82tw8yFdOFftrqWB>q7`vtiG diff --git a/data/stockpiles/masterworkweapons.dfstock b/data/stockpiles/masterworkweapons.dfstock index 29d2ec1b843a14908024805b9421815e739478fa..61bbfd17b1d585c72706429bb4cba09ac37fcc64 100644 GIT binary patch literal 18 ZcmZo- literal 39 vcmZo-qslWzek3t>e;=30@=O;2WH%0R delta 97 zcmcb`+RCQV#Q26)iO>ZF0yqHf delta 26 icmb2pogg7Pfst_rBjW-_h82tw8yFdOFftrqWB>q8MFqS7 diff --git a/data/stockpiles/stoneweapons.dfstock b/data/stockpiles/stoneweapons.dfstock index 8a6ee77aa472f31e712f4dc22ec701667beb9a5b..acba2374c111028ff8d0379cf57a00da29249a2c 100644 GIT binary patch delta 51 zcmcc1_ll3XiSgM+W=D24B?V7E{~&iqKTl^Xryzgd;CN>rS4Y2iXaAspU{{yW;1CN2 G2L=F{P!6yF delta 38 ucmaFGcbAX3iSfurW=D2O3kC;<35<+07#SBZGOS>f*ucoJgOT9?BLe`>dI@v@ diff --git a/data/stockpiles/trapcomponents.dfstock b/data/stockpiles/trapcomponents.dfstock index 9f7c4194b5c2a24dcf43d037cd3d6b2fc67c1592..475b530d4c5f17a7313a2a0660eb059dee4dfd50 100644 GIT binary patch delta 6 NcmaFBc$#s-X#fgk0|@{C delta 28 kcmX@j_<(W3Y0(Lcj58P+7cercV3gRv$gqQv;Q%880EDjx*8l(j diff --git a/data/stockpiles/unusableweapons.dfstock b/data/stockpiles/unusableweapons.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..6af8d1b5a2a48242cd3fb5bee39b763d76ced295 GIT binary patch literal 7 OcmZo-WU*jyU<3dK5CH)I literal 0 HcmV?d00001 diff --git a/data/stockpiles/usableweapons.dfstock b/data/stockpiles/usableweapons.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..248610ae941c9286c6d5750425297d6e8248c16e GIT binary patch literal 7 OcmZo-WU*j$U;qFG5&;1K literal 0 HcmV?d00001 diff --git a/data/stockpiles/woodentools.dfstock b/data/stockpiles/woodentools.dfstock deleted file mode 100644 index de3acb7945ccceee4f6fa92d626bd2fc0211ac87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35 rcmYe|;bIB#_xBNE3HSGRnZU?6gOPCoBf| Date: Mon, 20 Mar 2023 17:42:21 -0700 Subject: [PATCH 0883/2222] update armor stockpile defs --- data/stockpiles/artifactarmor.dfstock | Bin 38 -> 17 bytes data/stockpiles/bronzearmor.dfstock | Bin 46 -> 25 bytes data/stockpiles/copperarmor.dfstock | Bin 46 -> 25 bytes data/stockpiles/ironarmor.dfstock | Bin 44 -> 23 bytes data/stockpiles/masterworkarmor.dfstock | Bin 39 -> 18 bytes data/stockpiles/metalarmor.dfstock | Bin 773 -> 730 bytes data/stockpiles/otherarmor.dfstock | Bin 122 -> 101 bytes data/stockpiles/steelarmor.dfstock | Bin 45 -> 24 bytes data/stockpiles/unusablearmor.dfstock | Bin 0 -> 7 bytes data/stockpiles/usablearmor.dfstock | Bin 0 -> 7 bytes 10 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/stockpiles/unusablearmor.dfstock create mode 100644 data/stockpiles/usablearmor.dfstock diff --git a/data/stockpiles/artifactarmor.dfstock b/data/stockpiles/artifactarmor.dfstock index 8d81ee35585d3995b7ae9bebca153b18d871bf9c..af207f4fd5cd112a3d676a9f8d81ca155e8fd70c 100644 GIT binary patch literal 17 YcmeBTq8j|IN~ diff --git a/data/stockpiles/copperarmor.dfstock b/data/stockpiles/copperarmor.dfstock index 695eeb321abfd6b97a795283860c10bf6d14655e..bc7b8ec58e5a07cb0a80cd813037b7c34e1a1bda 100644 GIT binary patch delta 4 LcmdOsoFEAR0zd%q delta 26 icmb2tn;q8j|IN~ diff --git a/data/stockpiles/ironarmor.dfstock b/data/stockpiles/ironarmor.dfstock index a1cdc6fd1133e9ca5d3978179972cfee9d28cfc3..9474bd69f36ca53d6a47ebd963b46b7d7fdcb39b 100644 GIT binary patch delta 4 LcmdN9pCAqZ0x$sU delta 26 icmWgFnIJAYfst_rBjW-_h82tw8yFdOFftrqWB>q7`vtiG diff --git a/data/stockpiles/masterworkarmor.dfstock b/data/stockpiles/masterworkarmor.dfstock index e3d457dc88d36da0dfd598376e1b424500ccfe9b..09330aa79153d2d16996ffb6a2cd168abe42cae3 100644 GIT binary patch literal 18 ZcmeBTqslWzek3t>e;=30@=O;2YL^a~ delta 97 zcmcb`+RCQV#rTHRiOqUhX$hn diff --git a/data/stockpiles/steelarmor.dfstock b/data/stockpiles/steelarmor.dfstock index aff9f56d2184b6ebf962722b3cff3868e3f4b02d..c2ba86fcbbe767fce19ad31f97eca698842a63a8 100644 GIT binary patch delta 4 LcmdOum>>ZF0yqHf delta 26 icmb2pogg7Pfst_rBjW-_h82tw8yFdOFftrqWB>q8MFqS7 diff --git a/data/stockpiles/unusablearmor.dfstock b/data/stockpiles/unusablearmor.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..d5b2e9c64925daefe29d8247bb6b02c69bdec9b8 GIT binary patch literal 7 OcmeBTWQkx%U<3dL2mwg| literal 0 HcmV?d00001 diff --git a/data/stockpiles/usablearmor.dfstock b/data/stockpiles/usablearmor.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..58c9296de954a2967b0d70677fde9aab32707c97 GIT binary patch literal 7 OcmeBTWQkx*U;qFH3IRy~ literal 0 HcmV?d00001 From d1db7b4ff39341014fcfae0ad2f40b11439ff3ef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 17:44:05 -0700 Subject: [PATCH 0884/2222] round out the stockpile defs --- data/stockpiles/otherweapons.dfstock | Bin 122 -> 1992 bytes data/stockpiles/tools.dfstock | Bin 29 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/stockpiles/tools.dfstock diff --git a/data/stockpiles/otherweapons.dfstock b/data/stockpiles/otherweapons.dfstock index 3c59675f00eb52be2680bd170d2882ea23d568dc..f1127a78419b9876b5ec72273bbd1a410bf8b588 100644 GIT binary patch literal 1992 zcmZ`)+m6&Q3|*u_Jgi{3Uy6`WzXI`uo0=v}nv^)5-R?{Og>U1-IO&X3w<8|w_;P%F z?EZQ3_pUnJ$Jkc4gQt*Z<72kfp^1U3r()Ur>X~QCmQ!^Q=a1EGmzcxcc@n8^4Ib0w zR?l~4Th4p2J%PEc$Yvtg47UN7gI64Tx)jJG%4z8 z$xhMlyc${>P?$g*%g(DMQ!L6WHsV@Go9X->G4IFXsM-;~>v^z~-=h#?o#YMrz)nP4 zdP)t3qA*zQDerKB{K(#_4LlM;;g~7fdbTNNgQJ9E@Yg`_C*hbN#_O4qRRNj|Xu!-1(vDX|K7Xmf3 z0c>Ie(H6i;qM36d%k3VZjVa6*Hnh5?K~U+%3K=KD)X&%NUz~ORVQgC$y7Ov$T-^rL zbw3@qb2(TVI%Ga9v?{KfdMPr1Ifno)=BULNpFnL^h7jqihLQ%%(BB?eXcU3%nP)vFUs| z6cNLeq8}H4O6Y^j#$t7_*BW<2<3lNZtt0Y9c9^bO<0dA0(BWwC$Sj!Do(s&HySK3jP%VmSSw%dwj#my^5H85e& za}^DEGHh3ef}PGQq7(qWmSCmoME5hb)Z^z{a4RBl4>5jL0N_kuq(Q Date: Mon, 20 Mar 2023 19:09:29 -0700 Subject: [PATCH 0885/2222] add weapons toggle stockpile setting --- data/stockpiles/weapons.dfstock | Bin 0 -> 709 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/stockpiles/weapons.dfstock diff --git a/data/stockpiles/weapons.dfstock b/data/stockpiles/weapons.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..e4968f56d4a16f14d4fc2edfca5a24651d83c615 GIT binary patch literal 709 zcmZ{iJx&BM42741#s>hz@*^sGDnzxOj7g%+&&bIrbh!&R<7P}W5>Pz4D9_LNoafJr zuS>da!b?tXzDn%-xi{w*x(5P(NT0aYAW(K3qiAoe^Z?wYWu1F*Su5QjeUZ(HfFc{A zBd{3FrA)a&*W%5Yu7GP8Zqa?n&93R6W@mvW$6*qkA>Kami-W(liyz Date: Mon, 20 Mar 2023 19:21:44 -0700 Subject: [PATCH 0886/2222] in-progress docs --- docs/plugins/stockpiles.rst | 218 +++++++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 2 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 83cfada02..3d262b1e8 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -46,9 +46,18 @@ Examples Imports a player-exported settings file named ``plants``, or the library ``plants`` settings file if a player-exported file by that name doesn't exist. +``stockpiles import -m enable plants`` + Enables plants in the selected stockpile. +``stockpiles import -m disable category_food -f tallow`` + Disables all tallow in the selected food stockpile. ``stockpiles export mysettings`` Export the settings for the currently selected stockpile to a file named ``dfhack-config/stockpiles/mysettings.dfstock``. +``stockpiles export mysettings -i categories,types`` + Export the stockpile category and item settings, but ignore the container + and general settings. This allows you to import the configuration later + without touching the container and general settings of the target + stockpile. Options ------- @@ -92,6 +101,211 @@ The stockpiles settings library ------------------------------- DFHack comes with a library of useful stockpile settings files that are ready -for import: +for import. If the stockpile configuration that you need isn't directly +represented, you can often use the ``enable`` and ``disable`` modes and/or +the ``filter`` option to transform an existing saved stockpile setting. Some +stockpile configurations can only be achieved with filters since the contents +of the stockpile lists are different for each world. For example, to disable +all tallow in your main food stockpile, you'd run this command:: + + stockpiles import category_food -m disable -f tallow + +Top-level categories +~~~~~~~~~~~~~~~~~~~~ + +Each stockpile category has a file that allows you to enable or disable the +entire category, or with a filter, any matchable subset thereof:: + + category_ammo + category_animals + category_armor + category_bars_blocks + category_cloth + category_coins + category_corpses + category_finished_goods + category_food + category_furniture + category_gems + category_leather + category_refuse + category_sheets + category_stone + category_weapons + category_wood + +For many of the categories, there are also settings files that manipulate interesting +subsets of that category. + +Ammo stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +bolts +metalammo +boneammo +woodammo +masterworkammo +artifactammo + +Animal stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +traps +cages + +Armor stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +metalarmor +otherarmor +ironarmor +bronzearmor +copperarmor +steelarmor +masterworkarmor +artifactarmor +usablearmor +unusablearmor + + +Bar stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~ + +bars +metalbars +ironbars +pigironbars +steelbars +otherbars +coal +potash +ash +pearlash +soap +blocks + +Cloth stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +thread +adamantinethread +cloth +adamantinecloth + +Notes: + +* ``thread`` and ``cloth`` refers to all materials that are not adamantine. + +Finished goods stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +stonetools +woodtools +crafts +goblets +toys +masterworkfinishedgoods +artifactfinishedgoods + +Food stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +preparedmeals +unpreparedfish +plants +booze +seeds +dye +miscliquid +wax + +Furniture stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +pots +bags +buckets +sand +masterworkfurniture +artifactfurniture + +* Because of the limitations of Dwarf Fortress, ``bags`` cannot distinguish + between empty bags and bags filled with gypsum powder. + +Gem stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~ + +roughgems +roughglass +cutgems +cutglass +cutstone + +Refuse stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +=========== ================== ================== +Exclusive Forbid Permit +=========== ================== ================== +corpses forbidcorpses permitcorpses +skulls forbidskulls permitskulls +bones forbidbones permitbones +shells forbidshells permitshells +teeth forbidteeth permitteeth +horns forbidhorns permithorns +hair forbidhair permithair +craftrefuse forbidcraftrefuse permitcraftrefuse +=========== ================== ================== + +Notes: + +* ``usablehair`` Only hair and wool that can make usable clothing is included, + i.e. from sheep, llamas, alpacas, and trolls. +* ``craftrefuse`` includes everything a craftsdwarf or tailor can use: skulls, + bones, shells, teeth, horns, and "usable" hair/wool (defined above). + +rawhides +tannedhides +usablehair + +You can get a stockpile of usable refuse with the following set of commands:: + + stockpiles import category_refuse -m enable -f skulls + stockpiles import category_refuse -m enable -f bones + stockpiles import category_refuse -m enable -f shells + stockpiles import category_refuse -m enable -f teeth + stockpiles import category_refuse -m enable -f horns + stockpiles import usablehair -m enable + +Stone stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +metalore +ironore +economic +flux +plasterproducing +coalproducing +otherstone +bauxite +clay + +Weapon stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -TODO: port alias library here +weapons +metalweapons +stoneweapons +otherweapons +trapcomponents +ironweapons +silverweapons +bronzeweapons +copperweapons +steelweapons +platinumweapons +adamantineweapons +masterworkweapons +artifactweapons +usableweapons +unusableweapons From 581c7370ca24a670e86eb41e64417e74249c6cfc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 22:29:26 -0700 Subject: [PATCH 0887/2222] prepend searchable prefix --- plugins/stockpiles/StockpileSerializer.cpp | 315 ++++++++++----------- plugins/stockpiles/StockpileSerializer.h | 6 +- 2 files changed, 156 insertions(+), 165 deletions(-) diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 32546053d..203d466b0 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -222,29 +222,34 @@ static typename df::enum_traits::base_type linear_index(df::enum_traits tr return -1; } -static void set_elem(bool all, char val, bool enabled, bool& elem) { - if (all || enabled) - elem = val; -} - static bool matches_filter(const string& filter, const string& name) { if (!filter.size()) return true; + DEBUG(log).print("searching for '%s' in '%s'\n", filter.c_str(), name.c_str()); return std::search(name.begin(), name.end(), filter.begin(), filter.end(), [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } ) != name.end(); } -static void set_filter_elem(const string& filter, char val, const string& name, const string& id, char& elem) { - if (matches_filter(filter, name)) { +static void set_flag(const char* name, const string& filter, bool all, char val, bool enabled, bool& elem) { + if ((all || enabled) && matches_filter(filter, name)) { + DEBUG(log).print("setting %s to %d\n", name, val); + elem = val; + } +} + +static void set_filter_elem(const char* subcat, const string& filter, char val, + const string& name, const string& id, char& elem) { + if (matches_filter(filter, subcat + ((*subcat ? "/" : "") + name))) { DEBUG(log).print("setting %s (%s) to %d\n", name.c_str(), id.c_str(), val); elem = val; } } template -static void set_filter_elem(const string& filter, T_val val, const string& name, T_id id, T_val& elem) { - if (matches_filter(filter, name)) { +static void set_filter_elem(const char* subcat, const string& filter, T_val val, + const string& name, T_id id, T_val& elem) { + if (matches_filter(filter, subcat + ((*subcat ? "/" : "") + name))) { DEBUG(log).print("setting %s (%d) to %d\n", name.c_str(), (int32_t)id, val); elem = val; } @@ -286,15 +291,15 @@ static bool serialize_list_itemdef(FuncWriteExport add_value, return all; } -static void unserialize_list_itemdef(bool all, char val, const string& filter, FuncReadImport read_value, - int32_t list_size, std::vector& pile_list, item_type::item_type type) { +static void unserialize_list_itemdef(const char* subcat, bool all, char val, const string& filter, + FuncReadImport read_value, int32_t list_size, std::vector& pile_list, item_type::item_type type) { int num_elems = Items::getSubtypeCount(type); pile_list.resize(num_elems, '\0'); if (all) { for (auto idx = 0; idx < num_elems; ++idx) { ItemTypeInfo ii; ii.decode(type, idx); - set_filter_elem(filter, val, ii.toString(), idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, ii.toString(), idx, pile_list.at(idx)); } return; } @@ -308,7 +313,7 @@ static void unserialize_list_itemdef(bool all, char val, const string& filter, F WARN(log).print("item type index invalid: %d\n", ii.subtype); continue; } - set_filter_elem(filter, val, id, ii.subtype, pile_list.at(ii.subtype)); + set_filter_elem(subcat, filter, val, id, ii.subtype, pile_list.at(ii.subtype)); } } @@ -334,12 +339,12 @@ static void quality_clear(bool(&pile_list)[7]) { std::fill(pile_list, pile_list + 7, false); } -static void unserialize_list_quality(bool all, bool val, const string& filter, +static void unserialize_list_quality(const char* subcat, bool all, bool val, const string& filter, FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { if (all) { for (auto idx = 0; idx < 7; ++idx) { string id = ENUM_KEY_STR(item_quality, (df::item_quality)idx); - set_filter_elem(filter, val, id, idx, pile_list[idx]); + set_filter_elem(subcat, filter, val, id, idx, pile_list[idx]); } return; } @@ -353,7 +358,7 @@ static void unserialize_list_quality(bool all, bool val, const string& filter, WARN(log).print("invalid quality token: %s\n", quality.c_str()); continue; } - set_filter_elem(filter, val, quality, idx, pile_list[idx]); + set_filter_elem(subcat, filter, val, quality, idx, pile_list[idx]); } } @@ -395,14 +400,14 @@ static bool serialize_list_other_mats( return all; } -static void unserialize_list_other_mats(bool all, char val, const string& filter, +static void unserialize_list_other_mats(const char* subcat, bool all, char val, const string& filter, const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { size_t num_elems = other_mats.size(); pile_list.resize(num_elems, '\0'); if (all) { for (auto & entry : other_mats) - set_filter_elem(filter, val, entry.second, entry.first, pile_list.at(entry.first)); + set_filter_elem(subcat, filter, val, entry.second, entry.first, pile_list.at(entry.first)); return; } @@ -417,7 +422,7 @@ static void unserialize_list_other_mats(bool all, char val, const string& filter WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); } } @@ -445,14 +450,15 @@ static bool serialize_list_organic_mat(FuncWriteExport add_value, return all; } -static void unserialize_list_organic_mat(bool all, char val, const string& filter, FuncReadImport read_value, - size_t list_size, std::vector& pile_list, organic_mat_category::organic_mat_category cat) { +static void unserialize_list_organic_mat(const char* subcat, bool all, char val, const string& filter, + FuncReadImport read_value, size_t list_size, std::vector& pile_list, + organic_mat_category::organic_mat_category cat) { size_t num_elems = OrganicMatLookup::food_max_size(cat); pile_list.resize(num_elems, '\0'); if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string token = OrganicMatLookup::food_token_by_idx(cat, idx); - set_filter_elem(filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); } return; } @@ -464,7 +470,7 @@ static void unserialize_list_organic_mat(bool all, char val, const string& filte WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); } } @@ -493,8 +499,8 @@ static bool serialize_list_item_type(FuncItemAllowed is_allowed, return all; } -static void unserialize_list_item_type(bool all, char val, const string& filter, FuncItemAllowed is_allowed, - FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { +static void unserialize_list_item_type(const char* subcat, bool all, char val, const string& filter, + FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { // TODO can we remove the hardcoded list size? size_t num_elems = 112; pile_list.resize(num_elems, '\0'); @@ -506,7 +512,7 @@ static void unserialize_list_item_type(bool all, char val, const string& filter, if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = ENUM_KEY_STR(item_type, (df::item_type)idx); - set_filter_elem(filter, val, id, idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, id, idx, pile_list.at(idx)); } return; } @@ -523,7 +529,7 @@ static void unserialize_list_item_type(bool all, char val, const string& filter, WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); } } @@ -545,7 +551,7 @@ static bool serialize_list_material(FuncMaterialAllowed is_allowed, return all; } -static void unserialize_list_material(bool all, char val, const string& filter, +static void unserialize_list_material(const char* subcat, bool all, char val, const string& filter, FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { // we initialize all disallowed values to 1 @@ -562,7 +568,7 @@ static void unserialize_list_material(bool all, char val, const string& filter, for (size_t idx = 0; idx < num_elems; ++idx) { MaterialInfo mi; mi.decode(0, idx); - set_filter_elem(filter, val, mi.toString(), idx, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, mi.toString(), idx, pile_list.at(idx)); } return; } @@ -576,7 +582,7 @@ static void unserialize_list_material(bool all, char val, const string& filter, WARN(log).print("material type index invalid: %d\n", mi.index); continue; } - set_filter_elem(filter, val, id, mi.index, pile_list.at(mi.index)); + set_filter_elem(subcat, filter, val, id, mi.index, pile_list.at(mi.index)); } } @@ -598,14 +604,14 @@ static bool serialize_list_creature(FuncWriteExport add_value, const std::vector return all; } -static void unserialize_list_creature(bool all, char val, const string& filter, +static void unserialize_list_creature(const char* subcat, bool all, char val, const string& filter, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { size_t num_elems = world->raws.creatures.all.size(); pile_list.resize(num_elems, '\0'); if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { auto r = find_creature(idx); - set_filter_elem(filter, val, r->name[0], r->creature_id, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, r->name[0], r->creature_id, pile_list.at(idx)); } return; } @@ -618,7 +624,7 @@ static void unserialize_list_creature(bool all, char val, const string& filter, continue; } auto r = find_creature(idx); - set_filter_elem(filter, val, r->name[0], r->creature_id, pile_list.at(idx)); + set_filter_elem(subcat, filter, val, r->name[0], r->creature_id, pile_list.at(idx)); } } @@ -796,7 +802,7 @@ static void read_elem(const char *name, DeserializeMode mode, bool is_set = elem_fn() != 0; if (mode == DESERIALIZE_MODE_SET || is_set) { T_elem val = (mode == DESERIALIZE_MODE_DISABLE) ? 0 : elem_fn(); - DEBUG(log).print("setting %s=%d\n", name, val); + DEBUG(log).print("setting %s to %d\n", name, val); setting = val; } } @@ -809,17 +815,13 @@ static void read_category(const char *name, DeserializeMode mode, enum df::stockpile_group_set::Mask cat_mask, std::function clear_fn, std::function set_fn) { - bool has_cat = has_cat_fn(); - bool all = has_cat && cat_fn().has_all() && cat_fn().all(); - bool just_disable = all && mode == DESERIALIZE_MODE_DISABLE; - - if (mode == DESERIALIZE_MODE_SET || just_disable) { + if (mode == DESERIALIZE_MODE_SET) { DEBUG(log).print("clearing %s\n", name); cat_flags &= ~cat_mask; clear_fn(); } - if (!has_cat || just_disable) + if (!has_cat_fn()) return; if (mode == DESERIALIZE_MODE_DISABLE && !(cat_flags & cat_mask)) @@ -828,6 +830,7 @@ static void read_category(const char *name, DeserializeMode mode, if (mode == DESERIALIZE_MODE_SET || mode == DESERIALIZE_MODE_ENABLE) cat_flags |= cat_mask; + bool all = cat_fn().all(); char val = (mode == DESERIALIZE_MODE_DISABLE) ? (char)0 : (char)1; DEBUG(log).print("setting %s %s elements to %d\n", all ? "all" : "marked", name, val); @@ -932,32 +935,18 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil [&](bool all, char val) { auto & bammo = mBuffer.ammo(); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("type", all, val, filter, [&](const size_t& idx) -> const std::string& { return bammo.type(idx); }, bammo.type_size(), pammo.type, item_type::AMMO); - unserialize_list_material(all, val, filter, ammo_mat_is_allowed, + unserialize_list_material("mats", all, val, filter, ammo_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, bammo.mats_size(), pammo.mats); - pammo.other_mats.resize(2, '\0'); - if (bammo.other_mats_size() > 0) { - // TODO remove hardcoded value - for (int i = 0; i < bammo.other_mats_size(); ++i) { - const std::string token = bammo.other_mats(i); - const int32_t idx = token == "WOOD" ? 0 : token == "BONE" ? 1 - : -1; - DEBUG(log).print("other mats %d is %s\n", idx, token.c_str()); - if (idx == -1) - continue; - pammo.other_mats.at(idx) = 1; - } - } - pammo.other_mats.resize(2, '\0'); if (all) { - set_filter_elem(filter, val, "WOOD", 0, pammo.other_mats.at(0)); - set_filter_elem(filter, val, "BONE", 1, pammo.other_mats.at(1)); + set_filter_elem("other", filter, val, "WOOD", 0, pammo.other_mats.at(0)); + set_filter_elem("other", filter, val, "BONE", 1, pammo.other_mats.at(1)); } else { // TODO can we un-hardcode the values? for (int i = 0; i < bammo.other_mats_size(); ++i) { @@ -965,15 +954,15 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil const int32_t idx = id == "WOOD" ? 0 : id == "BONE" ? 1 : -1; if (idx == -1) continue; - set_filter_elem(filter, val, id, idx, pammo.other_mats.at(idx)); + set_filter_elem("other", filter, val, id, idx, pammo.other_mats.at(idx)); } } - unserialize_list_quality(all, val, filter, + unserialize_list_quality("core", all, val, filter, [&](const size_t& idx) -> const std::string& { return bammo.quality_core(idx); }, bammo.quality_core_size(), pammo.quality_core); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("total", all, val, filter, [&](const size_t& idx) -> const std::string& { return bammo.quality_total(idx); }, bammo.quality_total_size(), pammo.quality_total); }); @@ -1006,10 +995,10 @@ void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& [&](bool all, char val) { auto & banimals = mBuffer.animals(); - set_elem(all, val, banimals.empty_cages(), panimals.empty_cages); - set_elem(all, val, banimals.empty_traps(), panimals.empty_traps); + set_flag("cages", filter, all, val, banimals.empty_cages(), panimals.empty_cages); + set_flag("traps", filter, all, val, banimals.empty_traps(), panimals.empty_traps); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("", all, val, filter, [&](const size_t& idx) -> const std::string& { return banimals.enabled(idx); }, banimals.enabled_size(), panimals.enabled); }); @@ -1113,47 +1102,47 @@ void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & barmor = mBuffer.armor(); - set_elem(all, val, barmor.unusable(), parmor.unusable); - set_elem(all, val, barmor.usable(), parmor.usable); + set_flag("nouse", filter, all, val, barmor.unusable(), parmor.unusable); + set_flag("canuse", filter, all, val, barmor.usable(), parmor.usable); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("body", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.body(idx); }, barmor.body_size(), parmor.body, item_type::ARMOR); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("head", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.head(idx); }, barmor.head_size(), parmor.head, item_type::HELM); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("feet", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.feet(idx); }, barmor.feet_size(), parmor.feet, item_type::SHOES); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("hands", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.hands(idx); }, barmor.hands_size(), parmor.hands, item_type::GLOVES); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("legs", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.legs(idx); }, barmor.legs_size(), parmor.legs, item_type::PANTS); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("shield", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.shield(idx); }, barmor.shield_size(), parmor.shield, item_type::SHIELD); - unserialize_list_material(all, val, filter, + unserialize_list_material("mats", all, val, filter, armor_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return barmor.mats(idx); }, barmor.mats_size(), parmor.mats); - unserialize_list_other_mats(all, val, filter, + unserialize_list_other_mats("other", all, val, filter, mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const std::string& { return barmor.other_mats(idx); }, barmor.other_mats_size(), parmor.other_mats); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("core", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.quality_core(idx); }, barmor.quality_core_size(), parmor.quality_core); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("total", all, val, filter, [&](const size_t& idx) -> const std::string& { return barmor.quality_total(idx); }, barmor.quality_total_size(), parmor.quality_total); }); @@ -1197,29 +1186,29 @@ void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::stri mPile->settings.flags.whole, mPile->settings.flags.mask_bars_blocks, [&]() { - pbarsblocks.bars_other_mats.clear(); pbarsblocks.bars_mats.clear(); - pbarsblocks.blocks_other_mats.clear(); + pbarsblocks.bars_other_mats.clear(); pbarsblocks.blocks_mats.clear(); + pbarsblocks.blocks_other_mats.clear(); }, [&](bool all, char val) { auto & bbarsblocks = mBuffer.barsblocks(); - unserialize_list_material(all, val, filter, bars_mat_is_allowed, + unserialize_list_material("mats/bars", all, val, filter, bars_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bbarsblocks.bars_mats(idx); }, bbarsblocks.bars_mats_size(), pbarsblocks.bars_mats); - unserialize_list_material(all, val, filter, - blocks_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_mats(idx); }, - bbarsblocks.blocks_mats_size(), pbarsblocks.blocks_mats); - - unserialize_list_other_mats(all, val, filter, + unserialize_list_other_mats("other/bars", all, val, filter, mOtherMatsBars.mats, [&](const size_t& idx) -> const std::string& { return bbarsblocks.bars_other_mats(idx); }, bbarsblocks.bars_other_mats_size(), pbarsblocks.bars_other_mats); - unserialize_list_other_mats(all, val, filter, + unserialize_list_material("mats/blocks", all, val, filter, + blocks_mat_is_allowed, + [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_mats(idx); }, + bbarsblocks.blocks_mats_size(), pbarsblocks.blocks_mats); + + unserialize_list_other_mats("other/blocks", all, val, filter, mOtherMatsBlocks.mats, [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_other_mats(idx); }, bbarsblocks.blocks_other_mats_size(), pbarsblocks.blocks_other_mats); @@ -1272,47 +1261,47 @@ void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& fi mPile->settings.flags.whole, mPile->settings.flags.mask_cloth, [&]() { - pcloth.thread_metal.clear(); - pcloth.thread_plant.clear(); pcloth.thread_silk.clear(); pcloth.thread_yarn.clear(); - pcloth.cloth_metal.clear(); - pcloth.cloth_plant.clear(); + pcloth.thread_plant.clear(); + pcloth.thread_metal.clear(); pcloth.cloth_silk.clear(); + pcloth.cloth_plant.clear(); pcloth.cloth_yarn.clear(); + pcloth.cloth_metal.clear(); }, [&](bool all, char val) { auto & bcloth = mBuffer.cloth(); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("thread/silk", all, val, filter, [&](size_t idx) -> std::string { return bcloth.thread_silk(idx); }, bcloth.thread_silk_size(), pcloth.thread_silk, organic_mat_category::Silk); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("thread/plant", all, val, filter, [&](size_t idx) -> std::string { return bcloth.thread_plant(idx); }, bcloth.thread_plant_size(), pcloth.thread_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("thread/yarn", all, val, filter, [&](size_t idx) -> std::string { return bcloth.thread_yarn(idx); }, bcloth.thread_yarn_size(), pcloth.thread_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("thread/metal", all, val, filter, [&](size_t idx) -> std::string { return bcloth.thread_metal(idx); }, bcloth.thread_metal_size(), pcloth.thread_metal, organic_mat_category::MetalThread); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("cloth/silk", all, val, filter, [&](size_t idx) -> std::string { return bcloth.cloth_silk(idx); }, bcloth.cloth_silk_size(), pcloth.cloth_silk, organic_mat_category::Silk); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("cloth/plant", all, val, filter, [&](size_t idx) -> std::string { return bcloth.cloth_plant(idx); }, bcloth.cloth_plant_size(), pcloth.cloth_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("cloth/yarn", all, val, filter, [&](size_t idx) -> std::string { return bcloth.cloth_yarn(idx); }, bcloth.cloth_yarn_size(), pcloth.cloth_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("cloth/metal", all, val, filter, [&](size_t idx) -> std::string { return bcloth.cloth_metal(idx); }, bcloth.cloth_metal_size(), pcloth.cloth_metal, organic_mat_category::MetalThread); }); @@ -1342,7 +1331,7 @@ void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bcoin = mBuffer.coin(); - unserialize_list_material(all, val, filter, coins_mat_is_allowed, + unserialize_list_material("", all, val, filter, coins_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bcoin.mats(idx); }, bcoin.mats_size(), pcoins.mats); }); @@ -1426,23 +1415,23 @@ void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::s [&](bool all, char val) { auto & bfinished_goods = mBuffer.finished_goods(); - unserialize_list_item_type(all, val, filter, finished_goods_type_is_allowed, + unserialize_list_item_type("type", all, val, filter, finished_goods_type_is_allowed, [&](const size_t& idx) -> const std::string& { return bfinished_goods.type(idx); }, bfinished_goods.type_size(), pfinished_goods.type); - unserialize_list_material(all, val, filter, finished_goods_mat_is_allowed, + unserialize_list_material("mats", all, val, filter, finished_goods_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bfinished_goods.mats(idx); }, bfinished_goods.mats_size(), pfinished_goods.mats); - unserialize_list_other_mats(all, val, filter, mOtherMatsFinishedGoods.mats, + unserialize_list_other_mats("other", all, val, filter, mOtherMatsFinishedGoods.mats, [&](const size_t& idx) -> const std::string& { return bfinished_goods.other_mats(idx); }, bfinished_goods.other_mats_size(), pfinished_goods.other_mats); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("core", all, val, filter, [&](const size_t& idx) -> const std::string& { return bfinished_goods.quality_core(idx); }, bfinished_goods.quality_core_size(), pfinished_goods.quality_core); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("total", all, val, filter, [&](const size_t& idx) -> const std::string& { return bfinished_goods.quality_total(idx); }, bfinished_goods.quality_total_size(), pfinished_goods.quality_total); }); @@ -1458,7 +1447,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_meat(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; - return food_pair(setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); + return food_pair("meat", setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); } case organic_mat_category::Fish: { @@ -1466,7 +1455,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_fish(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; - return food_pair(setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); + return food_pair("fish", setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { @@ -1474,7 +1463,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_unprepared_fish(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; - return food_pair(setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); + return food_pair("unpreparedfish", setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { @@ -1482,7 +1471,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_egg(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; - return food_pair(setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); + return food_pair("egg", setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); } case organic_mat_category::Plants: { @@ -1490,7 +1479,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_plants(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; - return food_pair(setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); + return food_pair("plants", setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); } case organic_mat_category::PlantDrink: { @@ -1498,7 +1487,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_drink_plant(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; - return food_pair(setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); + return food_pair("drink/plant", setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); } case organic_mat_category::CreatureDrink: { @@ -1506,7 +1495,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_drink_animal(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; - return food_pair(setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); + return food_pair("drink/animal", setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); } case organic_mat_category::PlantCheese: { @@ -1514,7 +1503,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_cheese_plant(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; - return food_pair(setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); + return food_pair("cheese/plant", setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); } case organic_mat_category::CreatureCheese: { @@ -1522,7 +1511,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_cheese_animal(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; - return food_pair(setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); + return food_pair("cheese/animal", setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); } case organic_mat_category::Seed: { @@ -1530,7 +1519,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_seeds(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; - return food_pair(setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); + return food_pair("seeds", setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); } case organic_mat_category::Leaf: { @@ -1538,7 +1527,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_leaves(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; - return food_pair(setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); + return food_pair("leaves", setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); } case organic_mat_category::PlantPowder: { @@ -1546,7 +1535,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_powder_plant(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; - return food_pair(setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); + return food_pair("powder/plant", setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); } case organic_mat_category::CreaturePowder: { @@ -1554,7 +1543,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_powder_creature(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; - return food_pair(setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); + return food_pair("powder/animal", setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); } case organic_mat_category::Glob: { @@ -1562,7 +1551,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_glob(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; - return food_pair(setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); + return food_pair("glob", setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); } case organic_mat_category::PlantLiquid: { @@ -1570,7 +1559,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_liquid_plant(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; - return food_pair(setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); + return food_pair("liquid/plant", setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); } case organic_mat_category::CreatureLiquid: { @@ -1578,7 +1567,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_liquid_animal(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; - return food_pair(setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); + return food_pair("liquid/animal", setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); } case organic_mat_category::MiscLiquid: { @@ -1586,7 +1575,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_liquid_misc(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; - return food_pair(setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); + return food_pair("liquid/misc", setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); } case organic_mat_category::Paste: @@ -1595,7 +1584,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_glob_paste(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; - return food_pair(setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); + return food_pair("paste", setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); } case organic_mat_category::Pressed: { @@ -1603,7 +1592,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_glob_pressed(id); }; FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; - return food_pair(setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); + return food_pair("pressed", setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); } case organic_mat_category::Leather: case organic_mat_category::Silk: @@ -1671,13 +1660,13 @@ void StockpileSerializer::read_food(DeserializeMode mode, const std::string& fil [&](bool all, char val) { auto & bfood = mBuffer.food(); - set_elem(all, val, bfood.prepared_meals(), pfood.prepared_meals); + set_flag("preparedmeals", filter, all, val, bfood.prepared_meals(), pfood.prepared_meals); for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { food_pair p = food_map((organic_mat_category)mat_category); if (!p.valid) continue; - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat(p.name, all, val, filter, p.get_value, p.serialized_count, *p.stockpile_values, (organic_mat_category)mat_category); } @@ -1749,7 +1738,7 @@ void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = ENUM_KEY_STR(furniture_type, (df::furniture_type)idx); - set_filter_elem(filter, val, id, idx, pfurniture.type.at(idx)); + set_filter_elem("type", filter, val, id, idx, pfurniture.type.at(idx)); } } else { for (int i = 0; i < bfurniture.type_size(); ++i) { @@ -1759,23 +1748,23 @@ void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string WARN(log).print("furniture type index invalid %s, idx=%d\n", token.c_str(), idx); continue; } - set_filter_elem(filter, val, token, idx, pfurniture.type.at(idx)); + set_filter_elem("type", filter, val, token, idx, pfurniture.type.at(idx)); } } - unserialize_list_material(all, val, filter, furniture_mat_is_allowed, + unserialize_list_material("mats", all, val, filter, furniture_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bfurniture.mats(idx); }, bfurniture.mats_size(), pfurniture.mats); - unserialize_list_other_mats(all, val, filter, + unserialize_list_other_mats("other", all, val, filter, mOtherMatsFurniture.mats, [&](const size_t& idx) -> const std::string& { return bfurniture.other_mats(idx); }, bfurniture.other_mats_size(), pfurniture.other_mats); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("core", all, val, filter, [&](const size_t& idx) -> const std::string& { return bfurniture.quality_core(idx); }, bfurniture.quality_core_size(), pfurniture.quality_core); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("total", all, val, filter, [&](const size_t& idx) -> const std::string& { return bfurniture.quality_total(idx); }, bfurniture.quality_total_size(), pfurniture.quality_total); }); @@ -1853,11 +1842,11 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil [&](bool all, char val) { auto & bgems = mBuffer.gems(); - unserialize_list_material(all, val, filter, gem_mat_is_allowed, + unserialize_list_material("mats/rough", all, val, filter, gem_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bgems.rough_mats(idx); }, bgems.rough_mats_size(),pgems.rough_mats); - unserialize_list_material(all, val, filter, gem_cut_mat_is_allowed, + unserialize_list_material("mats/cut", all, val, filter, gem_cut_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bgems.cut_mats(idx); }, bgems.cut_mats_size(), pgems.cut_mats); @@ -1869,11 +1858,11 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil MaterialInfo mi; mi.decode(idx, -1); if (gem_other_mat_is_allowed(mi)) - set_filter_elem(filter, val, mi.getToken(), idx, pgems.rough_other_mats.at(idx)); + set_filter_elem("other/rough", filter, val, mi.getToken(), idx, pgems.rough_other_mats.at(idx)); if (!mi.isValid()) mi.decode(0, idx); if (gem_other_mat_is_allowed(mi)) - set_filter_elem(filter, val, mi.getToken(), idx, pgems.cut_other_mats.at(idx)); + set_filter_elem("other/cut", filter, val, mi.getToken(), idx, pgems.cut_other_mats.at(idx)); } return; } else { @@ -1881,10 +1870,10 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil for (size_t i = 0; i < builtin_size; ++i) { string id = bgems.rough_other_mats(i); if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) - set_filter_elem(filter, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); + set_filter_elem("other/rough", filter, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); id = bgems.cut_other_mats(i); if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) - set_filter_elem(filter, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); + set_filter_elem("other/cut", filter, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); } } }); @@ -1909,7 +1898,7 @@ void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& [&](bool all, char val) { auto & bleather = mBuffer.leather(); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("", all, val, filter, [&](size_t idx) -> std::string { return bleather.mats(idx); }, bleather.mats_size(), pleather.mats, organic_mat_category::Leather); }); @@ -1933,7 +1922,7 @@ void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& }, [&](bool all, char val) { auto & bcorpses = mBuffer.corpses_v50(); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("", all, val, filter, [&](const size_t& idx) -> const std::string& { return bcorpses.corpses(idx); }, bcorpses.corpses_size(), pcorpses.corpses); }); @@ -2010,35 +1999,35 @@ void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& f [&](bool all, char val) { auto & brefuse = mBuffer.refuse(); - set_elem(all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); - set_elem(all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); + set_flag("rawhide/fresh", filter, all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); + set_flag("rawhide/rotten", filter, all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); - unserialize_list_item_type(all, val, filter, refuse_type_is_allowed, + unserialize_list_item_type("type", all, val, filter, refuse_type_is_allowed, [&](const size_t& idx) -> const string& { return brefuse.type(idx); }, brefuse.type_size(), prefuse.type); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("corpses", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.corpses(idx); }, brefuse.corpses_size(), prefuse.corpses); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("bodyparts", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.body_parts(idx); }, brefuse.body_parts_size(), prefuse.body_parts); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("skulls", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.skulls(idx); }, brefuse.skulls_size(), prefuse.skulls); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("bones", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.bones(idx); }, brefuse.bones_size(), prefuse.bones); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("hair", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.hair(idx); }, brefuse.hair_size(), prefuse.hair); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("shells", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.shells(idx); }, brefuse.shells_size(), prefuse.shells); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("teeth", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.teeth(idx); }, brefuse.teeth_size(), prefuse.teeth); - unserialize_list_creature(all, val, filter, + unserialize_list_creature("horns", all, val, filter, [&](const size_t& idx) -> const string& { return brefuse.horns(idx); }, brefuse.horns_size(), prefuse.horns); }); @@ -2071,11 +2060,11 @@ void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bsheet = mBuffer.sheet(); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("paper", all, val, filter, [&](size_t idx) -> std::string { return bsheet.paper(idx); }, bsheet.paper_size(), psheet.paper, organic_mat_category::Paper); - unserialize_list_organic_mat(all, val, filter, + unserialize_list_organic_mat("parchment", all, val, filter, [&](size_t idx) -> std::string { return bsheet.parchment(idx); }, bsheet.parchment_size(), psheet.parchment, organic_mat_category::Parchment); }); @@ -2109,7 +2098,7 @@ void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bstone = mBuffer.stone(); - unserialize_list_material(all, val, filter, stone_is_allowed, + unserialize_list_material("", all, val, filter, stone_is_allowed, [&](const size_t& idx) -> const std::string& { return bstone.mats(idx); }, bstone.mats_size(), pstone.mats); }); @@ -2177,30 +2166,30 @@ void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& [&](bool all, char val) { auto & bweapons = mBuffer.weapons(); - set_elem(all, val, bweapons.unusable(), pweapons.unusable); - set_elem(all, val, bweapons.usable(), pweapons.usable); + set_flag("nouse", filter, all, val, bweapons.unusable(), pweapons.unusable); + set_flag("canuse", filter, all, val, bweapons.usable(), pweapons.usable); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("type/weapon", all, val, filter, [&](const size_t& idx) -> const std::string& { return bweapons.weapon_type(idx); }, bweapons.weapon_type_size(), pweapons.weapon_type, item_type::WEAPON); - unserialize_list_itemdef(all, val, filter, + unserialize_list_itemdef("type/trapcomp", all, val, filter, [&](const size_t& idx) -> const std::string& { return bweapons.trapcomp_type(idx); }, bweapons.trapcomp_type_size(), pweapons.trapcomp_type, item_type::TRAPCOMP); - unserialize_list_material(all, val, filter, weapons_mat_is_allowed, + unserialize_list_material("mats", all, val, filter, weapons_mat_is_allowed, [&](const size_t& idx) -> const std::string& { return bweapons.mats(idx); }, bweapons.mats_size(), pweapons.mats); - unserialize_list_other_mats(all, val, filter, mOtherMatsWeaponsArmor.mats, + unserialize_list_other_mats("other", all, val, filter, mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const std::string& { return bweapons.other_mats(idx); }, bweapons.other_mats_size(), pweapons.other_mats); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("core", all, val, filter, [&](const size_t& idx) -> const std::string& { return bweapons.quality_core(idx); }, bweapons.quality_core_size(), pweapons.quality_core); - unserialize_list_quality(all, val, filter, + unserialize_list_quality("total", all, val, filter, [&](const size_t& idx) -> const std::string& { return bweapons.quality_total(idx); }, bweapons.quality_total_size(), pweapons.quality_total); }); @@ -2245,7 +2234,7 @@ void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& fil if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = world->raws.plants.all[idx]->id; - set_filter_elem(filter, val, id, idx, pwood.mats.at(idx)); + set_filter_elem("", filter, val, id, idx, pwood.mats.at(idx)); } } else { for (int i = 0; i < bwood.mats_size(); ++i) { @@ -2255,7 +2244,7 @@ void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& fil WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); continue; } - set_filter_elem(filter, val, token, idx, pwood.mats.at(idx)); + set_filter_elem("", filter, val, token, idx, pwood.mats.at(idx)); } } }); diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 4085744d0..8573a453e 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -39,6 +39,7 @@ typedef std::function FuncMaterialAllowed; // convenient struct for parsing food stockpile items struct food_pair { + const char * name; // exporting FuncWriteExport set_value; std::vector* stockpile_values; @@ -47,8 +48,9 @@ struct food_pair { size_t serialized_count; bool valid; - food_pair(FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count) - : set_value(s), stockpile_values(sp_v), get_value(g), serialized_count(count), valid(true) { } + food_pair(const char * n, FuncWriteExport s, std::vector* sp_v, FuncReadImport g, size_t count) + : name(n), set_value(s), stockpile_values(sp_v), get_value(g), serialized_count(count), + valid(true) { } food_pair(): valid(false) { } }; From cabdfe67bf90bcbca65b15d22b32b61d7f017be8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 23:25:21 -0700 Subject: [PATCH 0888/2222] remove quality sp settings, update bolts --- data/stockpiles/artifactammo.dfstock | 2 -- data/stockpiles/artifactarmor.dfstock | Bin 17 -> 0 bytes data/stockpiles/artifactfinishedgoods.dfstock | 2 -- data/stockpiles/artifactfurniture.dfstock | 2 -- data/stockpiles/artifactweapons.dfstock | Bin 17 -> 0 bytes data/stockpiles/bolts.dfstock | 4 ++-- data/stockpiles/masterworkammo.dfstock | 1 - data/stockpiles/masterworkarmor.dfstock | Bin 18 -> 0 bytes data/stockpiles/masterworkfinishedgoods.dfstock | 1 - data/stockpiles/masterworkfurniture.dfstock | 1 - data/stockpiles/masterworkweapons.dfstock | Bin 18 -> 0 bytes 11 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 data/stockpiles/artifactammo.dfstock delete mode 100644 data/stockpiles/artifactarmor.dfstock delete mode 100644 data/stockpiles/artifactfinishedgoods.dfstock delete mode 100644 data/stockpiles/artifactfurniture.dfstock delete mode 100644 data/stockpiles/artifactweapons.dfstock delete mode 100644 data/stockpiles/masterworkammo.dfstock delete mode 100644 data/stockpiles/masterworkarmor.dfstock delete mode 100644 data/stockpiles/masterworkfinishedgoods.dfstock delete mode 100644 data/stockpiles/masterworkfurniture.dfstock delete mode 100644 data/stockpiles/masterworkweapons.dfstock diff --git a/data/stockpiles/artifactammo.dfstock b/data/stockpiles/artifactammo.dfstock deleted file mode 100644 index 16cd6f57e..000000000 --- a/data/stockpiles/artifactammo.dfstock +++ /dev/null @@ -1,2 +0,0 @@ -B -"Artifact \ No newline at end of file diff --git a/data/stockpiles/artifactarmor.dfstock b/data/stockpiles/artifactarmor.dfstock deleted file mode 100644 index af207f4fd5cd112a3d676a9f8d81ca155e8fd70c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 YcmeBT From 876425fbc8a5118e1331f152e4fedc3765b8e809 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 23:25:52 -0700 Subject: [PATCH 0889/2222] allow multiple filters --- docs/plugins/stockpiles.rst | 47 +- plugins/lua/stockpiles.lua | 6 +- plugins/stockpiles/StockpileSerializer.cpp | 686 +++++++++++---------- plugins/stockpiles/StockpileSerializer.h | 40 +- plugins/stockpiles/stockpiles.cpp | 5 +- 5 files changed, 403 insertions(+), 381 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 3d262b1e8..c36031ad7 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -77,14 +77,15 @@ Options to the stockpile, but no other settings are changed. In ``disable`` mode, enabled settings in the file are *removed* from the current stockpile configuration, and nothing else is changed. -``-f``, ``--filter `` - When importing, only modify the settings that contain the given substring. +``-f``, ``--filter [,...]`` + When importing, only modify the settings that contain at least one of the + given substrings. Configuration elements ---------------------- -The different configuration elements you can include in an exported settings file -are: +The different configuration elements you can include in an exported settings +file are: :containers: Max bins, max barrels, and num wheelbarrows. :general: Whether the stockpile takes from links only and whether organic @@ -104,9 +105,9 @@ DFHack comes with a library of useful stockpile settings files that are ready for import. If the stockpile configuration that you need isn't directly represented, you can often use the ``enable`` and ``disable`` modes and/or the ``filter`` option to transform an existing saved stockpile setting. Some -stockpile configurations can only be achieved with filters since the contents -of the stockpile lists are different for each world. For example, to disable -all tallow in your main food stockpile, you'd run this command:: +stockpile configurations can only be achieved with filters since the stockpile +lists are different for each world. For example, to disable all tallow in your +main food stockpile, you'd run this command:: stockpiles import category_food -m disable -f tallow @@ -134,18 +135,34 @@ entire category, or with a filter, any matchable subset thereof:: category_weapons category_wood -For many of the categories, there are also settings files that manipulate interesting -subsets of that category. +For many of the categories, there are also subcategory prefixes that you can +match with filters and convenient pre-made settings files that manipulate +interesting category subsets. Ammo stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~ -bolts -metalammo -boneammo -woodammo -masterworkammo -artifactammo +Subcategory prefixes:: + + type/ + mats/ + other/ + core/ + total/ + +Convenience settings files:: + + bolts + metalammo + boneammo + woodammo + +Example commands for a stockpile of metal bolts:: + + stockpiles import category_ammo + stockpiles import -m disable -f other/ category_ammo + stockpiles import -m disable -f type/ category_ammo + stockpiles import -m enable bolts Animal stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 1a92e2c0c..f9c7d7b4a 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -113,7 +113,7 @@ local function import_stockpile(name, opts) else name = STOCKPILES_LIBRARY_DIR .. '/' .. name end - stockpiles_import(name, get_sp_id(opts), opts.mode, opts.filter) + stockpiles_import(name, get_sp_id(opts), opts.mode, table.concat(opts.filters, ',')) end local valid_includes = {general=true, categories=true, types=true} @@ -145,11 +145,11 @@ local function process_args(opts, args) opts.includes = {} opts.mode = 'set' - opts.filter = '' + opts.filters = {} return argparse.processArgsGetopt(args, { {'f', 'filter', hasArg=true, - handler=function(arg) opts.filter = arg end}, + handler=function(arg) opts.filters = argparse.stringList(arg) end}, {'h', 'help', handler=function() opts.help = true end}, {'i', 'include', hasArg=true, handler=function(arg) opts.includes = parse_include(arg) end}, diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 203d466b0..ca51c0b3e 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -168,7 +168,7 @@ bool StockpileSerializer::serialize_to_ostream(std::ostream* output, uint32_t in return output->good(); } -bool StockpileSerializer::serialize_to_file(const std::string& file, uint32_t includedElements) { +bool StockpileSerializer::serialize_to_file(const string& file, uint32_t includedElements) { std::fstream output(file, std::ios::out | std::ios::binary | std::ios::trunc); if (output.fail()) { WARN(log).print("ERROR: failed to open file for writing: '%s'\n", @@ -178,7 +178,7 @@ bool StockpileSerializer::serialize_to_file(const std::string& file, uint32_t in return serialize_to_ostream(&output, includedElements); } -bool StockpileSerializer::parse_from_istream(std::istream* input, DeserializeMode mode, const std::string& filter) { +bool StockpileSerializer::parse_from_istream(std::istream* input, DeserializeMode mode, const vector& filters) { if (input->fail()) return false; mBuffer.Clear(); @@ -186,18 +186,18 @@ bool StockpileSerializer::parse_from_istream(std::istream* input, DeserializeMod const bool res = mBuffer.ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); if (res) - read(mode, filter); + read(mode, filters); return res; } -bool StockpileSerializer::unserialize_from_file(const std::string& file, DeserializeMode mode, const string& filter) { +bool StockpileSerializer::unserialize_from_file(const string& file, DeserializeMode mode, const vector& filters) { std::fstream input(file, std::ios::in | std::ios::binary); if (input.fail()) { WARN(log).print("failed to open file for reading: '%s'\n", file.c_str()); return false; } - return parse_from_istream(&input, mode, filter); + return parse_from_istream(&input, mode, filters); } /** @@ -207,7 +207,7 @@ bool StockpileSerializer::unserialize_from_file(const std::string& file, Deseria * @return the enum's value, -1 if not found */ template -static typename df::enum_traits::base_type linear_index(df::enum_traits traits, const std::string& token) { +static typename df::enum_traits::base_type linear_index(df::enum_traits traits, const string& token) { auto j = traits.first_item_value; auto limit = traits.last_item_value; // sometimes enums start at -1, which is bad news for array indexing @@ -222,34 +222,36 @@ static typename df::enum_traits::base_type linear_index(df::enum_traits tr return -1; } -static bool matches_filter(const string& filter, const string& name) { - if (!filter.size()) - return true; - DEBUG(log).print("searching for '%s' in '%s'\n", filter.c_str(), name.c_str()); - return std::search(name.begin(), name.end(), filter.begin(), filter.end(), - [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } - ) != name.end(); +static bool matches_filter(const vector& filters, const string& name) { + for (auto & filter : filters) { + DEBUG(log).print("searching for '%s' in '%s'\n", filter.c_str(), name.c_str()); + if (std::search(name.begin(), name.end(), filter.begin(), filter.end(), + [](unsigned char ch1, unsigned char ch2) { return std::toupper(ch1) == std::toupper(ch2); } + ) != name.end()) + return true; + } + return !filters.size(); } -static void set_flag(const char* name, const string& filter, bool all, char val, bool enabled, bool& elem) { - if ((all || enabled) && matches_filter(filter, name)) { +static void set_flag(const char* name, const vector& filters, bool all, char val, bool enabled, bool& elem) { + if ((all || enabled) && matches_filter(filters, name)) { DEBUG(log).print("setting %s to %d\n", name, val); elem = val; } } -static void set_filter_elem(const char* subcat, const string& filter, char val, +static void set_filter_elem(const char* subcat, const vector& filters, char val, const string& name, const string& id, char& elem) { - if (matches_filter(filter, subcat + ((*subcat ? "/" : "") + name))) { + if (matches_filter(filters, subcat + ((*subcat ? "/" : "") + name))) { DEBUG(log).print("setting %s (%s) to %d\n", name.c_str(), id.c_str(), val); elem = val; } } template -static void set_filter_elem(const char* subcat, const string& filter, T_val val, +static void set_filter_elem(const char* subcat, const vector& filters, T_val val, const string& name, T_id id, T_val& elem) { - if (matches_filter(filter, subcat + ((*subcat ? "/" : "") + name))) { + if (matches_filter(filters, subcat + ((*subcat ? "/" : "") + name))) { DEBUG(log).print("setting %s (%d) to %d\n", name.c_str(), (int32_t)id, val); elem = val; } @@ -269,8 +271,8 @@ static void set_filter_elem(const char* subcat, const string& filter, T_val val, * The unserialization process is the same in reverse. */ static bool serialize_list_itemdef(FuncWriteExport add_value, - std::vector list, - std::vector items, + vector list, + vector items, item_type::item_type type) { bool all = true; for (size_t i = 0; i < list.size(); ++i) { @@ -291,15 +293,15 @@ static bool serialize_list_itemdef(FuncWriteExport add_value, return all; } -static void unserialize_list_itemdef(const char* subcat, bool all, char val, const string& filter, - FuncReadImport read_value, int32_t list_size, std::vector& pile_list, item_type::item_type type) { +static void unserialize_list_itemdef(const char* subcat, bool all, char val, const vector& filters, + FuncReadImport read_value, int32_t list_size, vector& pile_list, item_type::item_type type) { int num_elems = Items::getSubtypeCount(type); pile_list.resize(num_elems, '\0'); if (all) { for (auto idx = 0; idx < num_elems; ++idx) { ItemTypeInfo ii; ii.decode(type, idx); - set_filter_elem(subcat, filter, val, ii.toString(), idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, ii.toString(), idx, pile_list.at(idx)); } return; } @@ -313,7 +315,7 @@ static void unserialize_list_itemdef(const char* subcat, bool all, char val, con WARN(log).print("item type index invalid: %d\n", ii.subtype); continue; } - set_filter_elem(subcat, filter, val, id, ii.subtype, pile_list.at(ii.subtype)); + set_filter_elem(subcat, filters, val, id, ii.subtype, pile_list.at(ii.subtype)); } } @@ -328,7 +330,7 @@ static bool serialize_list_quality(FuncWriteExport add_value, all = false; continue; } - const std::string f_type(quality_traits::key_table[i]); + const string f_type(quality_traits::key_table[i]); add_value(f_type); DEBUG(log).print("adding quality %s\n", f_type.c_str()); } @@ -339,12 +341,12 @@ static void quality_clear(bool(&pile_list)[7]) { std::fill(pile_list, pile_list + 7, false); } -static void unserialize_list_quality(const char* subcat, bool all, bool val, const string& filter, +static void unserialize_list_quality(const char* subcat, bool all, bool val, const vector& filters, FuncReadImport read_value, int32_t list_size, bool(&pile_list)[7]) { if (all) { for (auto idx = 0; idx < 7; ++idx) { string id = ENUM_KEY_STR(item_quality, (df::item_quality)idx); - set_filter_elem(subcat, filter, val, id, idx, pile_list[idx]); + set_filter_elem(subcat, filters, val, id, idx, pile_list[idx]); } return; } @@ -352,26 +354,26 @@ static void unserialize_list_quality(const char* subcat, bool all, bool val, con using df::enums::item_quality::item_quality; df::enum_traits quality_traits; for (int i = 0; i < list_size; ++i) { - const std::string quality = read_value(i); + const string quality = read_value(i); df::enum_traits::base_type idx = linear_index(quality_traits, quality); if (idx < 0) { WARN(log).print("invalid quality token: %s\n", quality.c_str()); continue; } - set_filter_elem(subcat, filter, val, quality, idx, pile_list[idx]); + set_filter_elem(subcat, filters, val, quality, idx, pile_list[idx]); } } -static string other_mats_index(const std::map other_mats, +static string other_mats_index(const std::map other_mats, int idx) { auto it = other_mats.find(idx); if (it == other_mats.end()) - return std::string(); + return string(); return it->second; } -static int other_mats_token(const std::map other_mats, - const std::string& token) { +static int other_mats_token(const std::map other_mats, + const string& token) { for (auto it = other_mats.begin(); it != other_mats.end(); ++it) { if (it->second == token) return it->first; @@ -380,16 +382,16 @@ static int other_mats_token(const std::map other_mats, } static bool serialize_list_other_mats( - const std::map other_mats, + const std::map other_mats, FuncWriteExport add_value, - std::vector list) { + vector list) { bool all = true; for (size_t i = 0; i < list.size(); ++i) { if (!list.at(i)) { all = false; continue; } - const std::string token = other_mats_index(other_mats, i); + const string token = other_mats_index(other_mats, i); if (token.empty()) { WARN(log).print("invalid other material with index %zd\n", i); continue; @@ -400,19 +402,19 @@ static bool serialize_list_other_mats( return all; } -static void unserialize_list_other_mats(const char* subcat, bool all, char val, const string& filter, - const std::map other_mats, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { +static void unserialize_list_other_mats(const char* subcat, bool all, char val, const vector& filters, + const std::map other_mats, FuncReadImport read_value, int32_t list_size, vector& pile_list) { size_t num_elems = other_mats.size(); pile_list.resize(num_elems, '\0'); if (all) { for (auto & entry : other_mats) - set_filter_elem(subcat, filter, val, entry.second, entry.first, pile_list.at(entry.first)); + set_filter_elem(subcat, filters, val, entry.second, entry.first, pile_list.at(entry.first)); return; } for (int i = 0; i < list_size; ++i) { - const std::string token = read_value(i); + const string token = read_value(i); size_t idx = other_mats_token(other_mats, token); if (idx < 0) { WARN(log).print("invalid other mat with token %s\n", token.c_str()); @@ -422,12 +424,12 @@ static void unserialize_list_other_mats(const char* subcat, bool all, char val, WARN(log).print("other_mats index too large! idx[%zd] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); } } static bool serialize_list_organic_mat(FuncWriteExport add_value, - const std::vector* list, + const vector* list, organic_mat_category::organic_mat_category cat) { bool all = true; if (!list) { @@ -439,7 +441,7 @@ static bool serialize_list_organic_mat(FuncWriteExport add_value, all = false; continue; } - std::string token = OrganicMatLookup::food_token_by_idx(cat, i); + string token = OrganicMatLookup::food_token_by_idx(cat, i); if (token.empty()) { DEBUG(log).print("food mat invalid :(\n"); continue; @@ -450,32 +452,32 @@ static bool serialize_list_organic_mat(FuncWriteExport add_value, return all; } -static void unserialize_list_organic_mat(const char* subcat, bool all, char val, const string& filter, - FuncReadImport read_value, size_t list_size, std::vector& pile_list, +static void unserialize_list_organic_mat(const char* subcat, bool all, char val, const vector& filters, + FuncReadImport read_value, size_t list_size, vector& pile_list, organic_mat_category::organic_mat_category cat) { size_t num_elems = OrganicMatLookup::food_max_size(cat); pile_list.resize(num_elems, '\0'); if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string token = OrganicMatLookup::food_token_by_idx(cat, idx); - set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); } return; } for (size_t i = 0; i < list_size; ++i) { - const std::string token = read_value(i); + const string token = read_value(i); int16_t idx = OrganicMatLookup::food_idx_by_token(cat, token); if (idx < 0 || size_t(idx) >= num_elems) { WARN(log).print("organic mat index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); } } static bool serialize_list_item_type(FuncItemAllowed is_allowed, - FuncWriteExport add_value, const std::vector& list) { + FuncWriteExport add_value, const vector& list) { using df::enums::item_type::item_type; using type_traits = df::enum_traits; @@ -490,7 +492,7 @@ static bool serialize_list_item_type(FuncItemAllowed is_allowed, continue; } const item_type type = (item_type)((df::enum_traits::base_type)i); - std::string r_type(type_traits::key_table[i + 1]); + string r_type(type_traits::key_table[i + 1]); if (!is_allowed(type)) continue; add_value(r_type); @@ -499,8 +501,8 @@ static bool serialize_list_item_type(FuncItemAllowed is_allowed, return all; } -static void unserialize_list_item_type(const char* subcat, bool all, char val, const string& filter, - FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { +static void unserialize_list_item_type(const char* subcat, bool all, char val, const vector& filters, + FuncItemAllowed is_allowed, FuncReadImport read_value, int32_t list_size, vector& pile_list) { // TODO can we remove the hardcoded list size? size_t num_elems = 112; pile_list.resize(num_elems, '\0'); @@ -512,7 +514,7 @@ static void unserialize_list_item_type(const char* subcat, bool all, char val, c if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = ENUM_KEY_STR(item_type, (df::item_type)idx); - set_filter_elem(subcat, filter, val, id, idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, id, idx, pile_list.at(idx)); } return; } @@ -520,7 +522,7 @@ static void unserialize_list_item_type(const char* subcat, bool all, char val, c using df::enums::item_type::item_type; df::enum_traits type_traits; for (int i = 0; i < list_size; ++i) { - const std::string token = read_value(i); + const string token = read_value(i); // subtract one because item_type starts at -1 const df::enum_traits::base_type idx = linear_index(type_traits, token) - 1; if (!is_allowed((item_type)idx)) @@ -529,12 +531,12 @@ static void unserialize_list_item_type(const char* subcat, bool all, char val, c WARN(log).print("error item_type index too large! idx[%d] max_size[%zd]\n", idx, num_elems); continue; } - set_filter_elem(subcat, filter, val, token, idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, token, idx, pile_list.at(idx)); } } static bool serialize_list_material(FuncMaterialAllowed is_allowed, - FuncWriteExport add_value, const std::vector& list) { + FuncWriteExport add_value, const vector& list) { bool all = true; MaterialInfo mi; for (size_t i = 0; i < list.size(); ++i) { @@ -551,9 +553,9 @@ static bool serialize_list_material(FuncMaterialAllowed is_allowed, return all; } -static void unserialize_list_material(const char* subcat, bool all, char val, const string& filter, +static void unserialize_list_material(const char* subcat, bool all, char val, const vector& filters, FuncMaterialAllowed is_allowed, FuncReadImport read_value, int32_t list_size, - std::vector& pile_list) { + vector& pile_list) { // we initialize all disallowed values to 1 // why? because that's how the memory is in DF before we muck with it. size_t num_elems = world->raws.inorganics.size(); @@ -568,7 +570,7 @@ static void unserialize_list_material(const char* subcat, bool all, char val, co for (size_t idx = 0; idx < num_elems; ++idx) { MaterialInfo mi; mi.decode(0, idx); - set_filter_elem(subcat, filter, val, mi.toString(), idx, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, mi.toString(), idx, pile_list.at(idx)); } return; } @@ -582,11 +584,11 @@ static void unserialize_list_material(const char* subcat, bool all, char val, co WARN(log).print("material type index invalid: %d\n", mi.index); continue; } - set_filter_elem(subcat, filter, val, id, mi.index, pile_list.at(mi.index)); + set_filter_elem(subcat, filters, val, id, mi.index, pile_list.at(mi.index)); } } -static bool serialize_list_creature(FuncWriteExport add_value, const std::vector& list) { +static bool serialize_list_creature(FuncWriteExport add_value, const vector& list) { bool all = true; for (size_t i = 0; i < list.size(); ++i) { @@ -604,14 +606,14 @@ static bool serialize_list_creature(FuncWriteExport add_value, const std::vector return all; } -static void unserialize_list_creature(const char* subcat, bool all, char val, const string& filter, - FuncReadImport read_value, int32_t list_size, std::vector& pile_list) { +static void unserialize_list_creature(const char* subcat, bool all, char val, const vector& filters, + FuncReadImport read_value, int32_t list_size, vector& pile_list) { size_t num_elems = world->raws.creatures.all.size(); pile_list.resize(num_elems, '\0'); if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { auto r = find_creature(idx); - set_filter_elem(subcat, filter, val, r->name[0], r->creature_id, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, r->name[0], r->creature_id, pile_list.at(idx)); } return; } @@ -624,7 +626,7 @@ static void unserialize_list_creature(const char* subcat, bool all, char val, co continue; } auto r = find_creature(idx); - set_filter_elem(subcat, filter, val, r->name[0], r->creature_id, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, r->name[0], r->creature_id, pile_list.at(idx)); } } @@ -754,34 +756,34 @@ void StockpileSerializer::write(uint32_t includedElements) { std::bind(&StockpileSerializer::write_wood, this, _1)); } -void StockpileSerializer::read(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read(DeserializeMode mode, const vector& filters) { DEBUG(log).print("==READ==\n"); read_containers(mode); read_general(mode); - read_ammo(mode, filter); - read_animals(mode, filter); - read_armor(mode, filter); - read_bars_blocks(mode, filter); - read_cloth(mode, filter); - read_coins(mode, filter); - read_finished_goods(mode, filter); - read_food(mode, filter); - read_furniture(mode, filter); - read_gems(mode, filter); - read_leather(mode, filter); + read_ammo(mode, filters); + read_animals(mode, filters); + read_armor(mode, filters); + read_bars_blocks(mode, filters); + read_cloth(mode, filters); + read_coins(mode, filters); + read_finished_goods(mode, filters); + read_food(mode, filters); + read_furniture(mode, filters); + read_gems(mode, filters); + read_leather(mode, filters); // support for old versions before corpses had a set if (mBuffer.has_corpses()) { StockpileSettings::CorpsesSet* corpses = mBuffer.mutable_corpses_v50(); corpses->set_all(true); } - read_corpses(mode, filter); + read_corpses(mode, filters); - read_refuse(mode, filter); - read_sheet(mode, filter); - read_stone(mode, filter); - read_weapons(mode, filter); - read_wood(mode, filter); + read_refuse(mode, filters); + read_sheet(mode, filters); + read_stone(mode, filters); + read_weapons(mode, filters); + read_wood(mode, filters); } void StockpileSerializer::write_containers() { @@ -880,14 +882,14 @@ static bool ammo_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { bool all = serialize_list_itemdef( - [&](const std::string& token) { ammo->add_type(token); }, + [&](const string& token) { ammo->add_type(token); }, mPile->settings.ammo.type, - std::vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), + vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), item_type::AMMO); all = serialize_list_material( ammo_mat_is_allowed, - [&](const std::string& token) { ammo->add_mats(token); }, + [&](const string& token) { ammo->add_mats(token); }, mPile->settings.ammo.mats) && all; if (mPile->settings.ammo.other_mats.size() > 2) { @@ -902,23 +904,23 @@ bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { all = false; continue; } - const std::string token = i == 0 ? "WOOD" : "BONE"; + const string token = i == 0 ? "WOOD" : "BONE"; ammo->add_other_mats(token); DEBUG(log).print("other mats %zd is %s\n", i, token.c_str()); } all = serialize_list_quality( - [&](const std::string& token) { ammo->add_quality_core(token); }, + [&](const string& token) { ammo->add_quality_core(token); }, mPile->settings.ammo.quality_core) && all; all = serialize_list_quality( - [&](const std::string& token) { ammo->add_quality_total(token); }, + [&](const string& token) { ammo->add_quality_total(token); }, mPile->settings.ammo.quality_total) && all; return all; } -void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_ammo(DeserializeMode mode, const vector& filters) { auto & pammo = mPile->settings.ammo; read_category("ammo", mode, std::bind(&StockpileSettings::has_ammo, mBuffer), @@ -935,35 +937,35 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const std::string& fil [&](bool all, char val) { auto & bammo = mBuffer.ammo(); - unserialize_list_itemdef("type", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bammo.type(idx); }, + unserialize_list_itemdef("type", all, val, filters, + [&](const size_t& idx) -> const string& { return bammo.type(idx); }, bammo.type_size(), pammo.type, item_type::AMMO); - unserialize_list_material("mats", all, val, filter, ammo_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bammo.mats(idx); }, + unserialize_list_material("mats", all, val, filters, ammo_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bammo.mats(idx); }, bammo.mats_size(), pammo.mats); pammo.other_mats.resize(2, '\0'); if (all) { - set_filter_elem("other", filter, val, "WOOD", 0, pammo.other_mats.at(0)); - set_filter_elem("other", filter, val, "BONE", 1, pammo.other_mats.at(1)); + set_filter_elem("other", filters, val, "WOOD", 0, pammo.other_mats.at(0)); + set_filter_elem("other", filters, val, "BONE", 1, pammo.other_mats.at(1)); } else { // TODO can we un-hardcode the values? for (int i = 0; i < bammo.other_mats_size(); ++i) { - const std::string id = bammo.other_mats(i); + const string id = bammo.other_mats(i); const int32_t idx = id == "WOOD" ? 0 : id == "BONE" ? 1 : -1; if (idx == -1) continue; - set_filter_elem("other", filter, val, id, idx, pammo.other_mats.at(idx)); + set_filter_elem("other", filters, val, id, idx, pammo.other_mats.at(idx)); } } - unserialize_list_quality("core", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bammo.quality_core(idx); }, + unserialize_list_quality("core", all, val, filters, + [&](const size_t& idx) -> const string& { return bammo.quality_core(idx); }, bammo.quality_core_size(), pammo.quality_core); - unserialize_list_quality("total", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bammo.quality_total(idx); }, + unserialize_list_quality("total", all, val, filters, + [&](const size_t& idx) -> const string& { return bammo.quality_total(idx); }, bammo.quality_total_size(), pammo.quality_total); }); } @@ -976,11 +978,11 @@ bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) animals->set_empty_traps(panimals.empty_traps); return serialize_list_creature( - [&](const std::string& token) { animals->add_enabled(token); }, + [&](const string& token) { animals->add_enabled(token); }, panimals.enabled) && all; } -void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_animals(DeserializeMode mode, const vector& filters) { auto & panimals = mPile->settings.animals; read_category("animals", mode, std::bind(&StockpileSettings::has_animals, mBuffer), @@ -995,11 +997,11 @@ void StockpileSerializer::read_animals(DeserializeMode mode, const std::string& [&](bool all, char val) { auto & banimals = mBuffer.animals(); - set_flag("cages", filter, all, val, banimals.empty_cages(), panimals.empty_cages); - set_flag("traps", filter, all, val, banimals.empty_traps(), panimals.empty_traps); + set_flag("cages", filters, all, val, banimals.empty_cages(), panimals.empty_cages); + set_flag("traps", filters, all, val, banimals.empty_traps(), panimals.empty_traps); - unserialize_list_creature("", all, val, filter, - [&](const size_t& idx) -> const std::string& { return banimals.enabled(idx); }, + unserialize_list_creature("", all, val, filters, + [&](const size_t& idx) -> const string& { return banimals.enabled(idx); }, banimals.enabled_size(), panimals.enabled); }); } @@ -1018,69 +1020,69 @@ bool StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { // armor type all = serialize_list_itemdef( - [&](const std::string& token) { armor->add_body(token); }, + [&](const string& token) { armor->add_body(token); }, parmor.body, - std::vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), + vector(world->raws.itemdefs.armor.begin(), world->raws.itemdefs.armor.end()), item_type::ARMOR) && all; // helm type all = serialize_list_itemdef( - [&](const std::string& token) { armor->add_head(token); }, + [&](const string& token) { armor->add_head(token); }, parmor.head, - std::vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), + vector(world->raws.itemdefs.helms.begin(), world->raws.itemdefs.helms.end()), item_type::HELM) && all; // shoes type all = serialize_list_itemdef( - [&](const std::string& token) { armor->add_feet(token); }, + [&](const string& token) { armor->add_feet(token); }, parmor.feet, - std::vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), + vector(world->raws.itemdefs.shoes.begin(), world->raws.itemdefs.shoes.end()), item_type::SHOES) && all; // gloves type all = serialize_list_itemdef( - [&](const std::string& token) { armor->add_hands(token); }, + [&](const string& token) { armor->add_hands(token); }, parmor.hands, - std::vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), + vector(world->raws.itemdefs.gloves.begin(), world->raws.itemdefs.gloves.end()), item_type::GLOVES) && all; // pant type all = serialize_list_itemdef( - [&](const std::string& token) { armor->add_legs(token); }, + [&](const string& token) { armor->add_legs(token); }, parmor.legs, - std::vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), + vector(world->raws.itemdefs.pants.begin(), world->raws.itemdefs.pants.end()), item_type::PANTS) && all; // shield type all = serialize_list_itemdef( - [&](const std::string& token) { armor->add_shield(token); }, + [&](const string& token) { armor->add_shield(token); }, parmor.shield, - std::vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), + vector(world->raws.itemdefs.shields.begin(), world->raws.itemdefs.shields.end()), item_type::SHIELD) && all; // materials all = serialize_list_material( armor_mat_is_allowed, - [&](const std::string& token) { armor->add_mats(token); }, + [&](const string& token) { armor->add_mats(token); }, parmor.mats) && all; // other mats all = serialize_list_other_mats( - mOtherMatsWeaponsArmor.mats, [&](const std::string& token) { armor->add_other_mats(token); }, + mOtherMatsWeaponsArmor.mats, [&](const string& token) { armor->add_other_mats(token); }, parmor.other_mats) && all; // quality core - all = serialize_list_quality([&](const std::string& token) { armor->add_quality_core(token); }, + all = serialize_list_quality([&](const string& token) { armor->add_quality_core(token); }, parmor.quality_core) && all; // quality total - all = serialize_list_quality([&](const std::string& token) { armor->add_quality_total(token); }, + all = serialize_list_quality([&](const string& token) { armor->add_quality_total(token); }, parmor.quality_total) && all; return all; } -void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_armor(DeserializeMode mode, const vector& filters) { auto & parmor = mPile->settings.armor; read_category("armor", mode, std::bind(&StockpileSettings::has_armor, mBuffer), @@ -1102,48 +1104,48 @@ void StockpileSerializer::read_armor(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & barmor = mBuffer.armor(); - set_flag("nouse", filter, all, val, barmor.unusable(), parmor.unusable); - set_flag("canuse", filter, all, val, barmor.usable(), parmor.usable); + set_flag("nouse", filters, all, val, barmor.unusable(), parmor.unusable); + set_flag("canuse", filters, all, val, barmor.usable(), parmor.usable); - unserialize_list_itemdef("body", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.body(idx); }, + unserialize_list_itemdef("body", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.body(idx); }, barmor.body_size(), parmor.body, item_type::ARMOR); - unserialize_list_itemdef("head", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.head(idx); }, + unserialize_list_itemdef("head", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.head(idx); }, barmor.head_size(), parmor.head, item_type::HELM); - unserialize_list_itemdef("feet", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.feet(idx); }, + unserialize_list_itemdef("feet", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.feet(idx); }, barmor.feet_size(), parmor.feet, item_type::SHOES); - unserialize_list_itemdef("hands", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.hands(idx); }, + unserialize_list_itemdef("hands", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.hands(idx); }, barmor.hands_size(), parmor.hands, item_type::GLOVES); - unserialize_list_itemdef("legs", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.legs(idx); }, + unserialize_list_itemdef("legs", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.legs(idx); }, barmor.legs_size(), parmor.legs, item_type::PANTS); - unserialize_list_itemdef("shield", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.shield(idx); }, + unserialize_list_itemdef("shield", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.shield(idx); }, barmor.shield_size(), parmor.shield, item_type::SHIELD); - unserialize_list_material("mats", all, val, filter, + unserialize_list_material("mats", all, val, filters, armor_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return barmor.mats(idx); }, + [&](const size_t& idx) -> const string& { return barmor.mats(idx); }, barmor.mats_size(), parmor.mats); - unserialize_list_other_mats("other", all, val, filter, - mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const std::string& { return barmor.other_mats(idx); }, + unserialize_list_other_mats("other", all, val, filters, + mOtherMatsWeaponsArmor.mats, [&](const size_t& idx) -> const string& { return barmor.other_mats(idx); }, barmor.other_mats_size(), parmor.other_mats); - unserialize_list_quality("core", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.quality_core(idx); }, + unserialize_list_quality("core", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.quality_core(idx); }, barmor.quality_core_size(), parmor.quality_core); - unserialize_list_quality("total", all, val, filter, - [&](const size_t& idx) -> const std::string& { return barmor.quality_total(idx); }, + unserialize_list_quality("total", all, val, filters, + [&](const size_t& idx) -> const string& { return barmor.quality_total(idx); }, barmor.quality_total_size(), parmor.quality_total); }); } @@ -1159,26 +1161,26 @@ static bool blocks_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { bool all = serialize_list_material( bars_mat_is_allowed, - [&](const std::string& token) { bars_blocks->add_bars_mats(token); }, + [&](const string& token) { bars_blocks->add_bars_mats(token); }, mPile->settings.bars_blocks.bars_mats); all = serialize_list_material( blocks_mat_is_allowed, - [&](const std::string& token) { bars_blocks->add_blocks_mats(token); }, + [&](const string& token) { bars_blocks->add_blocks_mats(token); }, mPile->settings.bars_blocks.blocks_mats) && all; all = serialize_list_other_mats( - mOtherMatsBars.mats, [&](const std::string& token) { bars_blocks->add_bars_other_mats(token); }, + mOtherMatsBars.mats, [&](const string& token) { bars_blocks->add_bars_other_mats(token); }, mPile->settings.bars_blocks.bars_other_mats) && all; all = serialize_list_other_mats( - mOtherMatsBlocks.mats, [&](const std::string& token) { bars_blocks->add_blocks_other_mats(token); }, + mOtherMatsBlocks.mats, [&](const string& token) { bars_blocks->add_blocks_other_mats(token); }, mPile->settings.bars_blocks.blocks_other_mats) && all; return all; } -void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const vector& filters) { auto & pbarsblocks = mPile->settings.bars_blocks; read_category("bars_blocks", mode, std::bind(&StockpileSettings::has_barsblocks, mBuffer), @@ -1194,23 +1196,23 @@ void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const std::stri [&](bool all, char val) { auto & bbarsblocks = mBuffer.barsblocks(); - unserialize_list_material("mats/bars", all, val, filter, bars_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bbarsblocks.bars_mats(idx); }, + unserialize_list_material("mats/bars", all, val, filters, bars_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bbarsblocks.bars_mats(idx); }, bbarsblocks.bars_mats_size(), pbarsblocks.bars_mats); - unserialize_list_other_mats("other/bars", all, val, filter, + unserialize_list_other_mats("other/bars", all, val, filters, mOtherMatsBars.mats, - [&](const size_t& idx) -> const std::string& { return bbarsblocks.bars_other_mats(idx); }, + [&](const size_t& idx) -> const string& { return bbarsblocks.bars_other_mats(idx); }, bbarsblocks.bars_other_mats_size(), pbarsblocks.bars_other_mats); - unserialize_list_material("mats/blocks", all, val, filter, + unserialize_list_material("mats/blocks", all, val, filters, blocks_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_mats(idx); }, + [&](const size_t& idx) -> const string& { return bbarsblocks.blocks_mats(idx); }, bbarsblocks.blocks_mats_size(), pbarsblocks.blocks_mats); - unserialize_list_other_mats("other/blocks", all, val, filter, + unserialize_list_other_mats("other/blocks", all, val, filters, mOtherMatsBlocks.mats, - [&](const size_t& idx) -> const std::string& { return bbarsblocks.blocks_other_mats(idx); }, + [&](const size_t& idx) -> const string& { return bbarsblocks.blocks_other_mats(idx); }, bbarsblocks.blocks_other_mats_size(), pbarsblocks.blocks_other_mats); }); } @@ -1219,41 +1221,41 @@ bool StockpileSerializer::write_cloth(StockpileSettings::ClothSet* cloth) { bool all = true; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_thread_silk(token); }, + [&](const string& token) { cloth->add_thread_silk(token); }, &mPile->settings.cloth.thread_silk, organic_mat_category::Silk) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_thread_plant(token); }, + [&](const string& token) { cloth->add_thread_plant(token); }, &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_thread_yarn(token); }, + [&](const string& token) { cloth->add_thread_yarn(token); }, &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_thread_metal(token); }, + [&](const string& token) { cloth->add_thread_metal(token); }, &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_cloth_silk(token); }, + [&](const string& token) { cloth->add_cloth_silk(token); }, &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_cloth_plant(token); }, + [&](const string& token) { cloth->add_cloth_plant(token); }, &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_cloth_yarn(token); }, + [&](const string& token) { cloth->add_cloth_yarn(token); }, &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn) && all; all = serialize_list_organic_mat( - [&](const std::string& token) { cloth->add_cloth_metal(token); }, + [&](const string& token) { cloth->add_cloth_metal(token); }, &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread) && all; return all; } -void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_cloth(DeserializeMode mode, const vector& filters) { auto & pcloth = mPile->settings.cloth; read_category("cloth", mode, std::bind(&StockpileSettings::has_cloth, mBuffer), @@ -1273,36 +1275,36 @@ void StockpileSerializer::read_cloth(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bcloth = mBuffer.cloth(); - unserialize_list_organic_mat("thread/silk", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.thread_silk(idx); }, + unserialize_list_organic_mat("thread/silk", all, val, filters, + [&](size_t idx) -> string { return bcloth.thread_silk(idx); }, bcloth.thread_silk_size(), pcloth.thread_silk, organic_mat_category::Silk); - unserialize_list_organic_mat("thread/plant", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.thread_plant(idx); }, + unserialize_list_organic_mat("thread/plant", all, val, filters, + [&](size_t idx) -> string { return bcloth.thread_plant(idx); }, bcloth.thread_plant_size(), pcloth.thread_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat("thread/yarn", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.thread_yarn(idx); }, + unserialize_list_organic_mat("thread/yarn", all, val, filters, + [&](size_t idx) -> string { return bcloth.thread_yarn(idx); }, bcloth.thread_yarn_size(), pcloth.thread_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat("thread/metal", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.thread_metal(idx); }, + unserialize_list_organic_mat("thread/metal", all, val, filters, + [&](size_t idx) -> string { return bcloth.thread_metal(idx); }, bcloth.thread_metal_size(), pcloth.thread_metal, organic_mat_category::MetalThread); - unserialize_list_organic_mat("cloth/silk", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.cloth_silk(idx); }, + unserialize_list_organic_mat("cloth/silk", all, val, filters, + [&](size_t idx) -> string { return bcloth.cloth_silk(idx); }, bcloth.cloth_silk_size(), pcloth.cloth_silk, organic_mat_category::Silk); - unserialize_list_organic_mat("cloth/plant", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.cloth_plant(idx); }, + unserialize_list_organic_mat("cloth/plant", all, val, filters, + [&](size_t idx) -> string { return bcloth.cloth_plant(idx); }, bcloth.cloth_plant_size(), pcloth.cloth_plant, organic_mat_category::PlantFiber); - unserialize_list_organic_mat("cloth/yarn", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.cloth_yarn(idx); }, + unserialize_list_organic_mat("cloth/yarn", all, val, filters, + [&](size_t idx) -> string { return bcloth.cloth_yarn(idx); }, bcloth.cloth_yarn_size(), pcloth.cloth_yarn, organic_mat_category::Yarn); - unserialize_list_organic_mat("cloth/metal", all, val, filter, - [&](size_t idx) -> std::string { return bcloth.cloth_metal(idx); }, + unserialize_list_organic_mat("cloth/metal", all, val, filters, + [&](size_t idx) -> string { return bcloth.cloth_metal(idx); }, bcloth.cloth_metal_size(), pcloth.cloth_metal, organic_mat_category::MetalThread); }); } @@ -1314,11 +1316,11 @@ static bool coins_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_coins(StockpileSettings::CoinSet* coins) { return serialize_list_material( coins_mat_is_allowed, - [&](const std::string& token) { coins->add_mats(token); }, + [&](const string& token) { coins->add_mats(token); }, mPile->settings.coins.mats); } -void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_coins(DeserializeMode mode, const vector& filters) { auto & pcoins = mPile->settings.coins; read_category("coin", mode, std::bind(&StockpileSettings::has_coin, mBuffer), @@ -1331,8 +1333,8 @@ void StockpileSerializer::read_coins(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bcoin = mBuffer.coin(); - unserialize_list_material("", all, val, filter, coins_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bcoin.mats(idx); }, + unserialize_list_material("", all, val, filters, coins_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bcoin.mats(idx); }, bcoin.mats_size(), pcoins.mats); }); } @@ -1377,28 +1379,28 @@ static bool finished_goods_mat_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { bool all = serialize_list_item_type( finished_goods_type_is_allowed, - [&](const std::string& token) { finished_goods->add_type(token); }, + [&](const string& token) { finished_goods->add_type(token); }, mPile->settings.finished_goods.type); all = serialize_list_material( finished_goods_mat_is_allowed, - [&](const std::string& token) { finished_goods->add_mats(token); }, + [&](const string& token) { finished_goods->add_mats(token); }, mPile->settings.finished_goods.mats) && all; all = serialize_list_other_mats( - mOtherMatsFinishedGoods.mats, [&](const std::string& token) { finished_goods->add_other_mats(token); }, + mOtherMatsFinishedGoods.mats, [&](const string& token) { finished_goods->add_other_mats(token); }, mPile->settings.finished_goods.other_mats) && all; - all = serialize_list_quality([&](const std::string& token) { finished_goods->add_quality_core(token); }, + all = serialize_list_quality([&](const string& token) { finished_goods->add_quality_core(token); }, mPile->settings.finished_goods.quality_core) && all; - all = serialize_list_quality([&](const std::string& token) { finished_goods->add_quality_total(token); }, + all = serialize_list_quality([&](const string& token) { finished_goods->add_quality_total(token); }, mPile->settings.finished_goods.quality_total) && all; return all; } -void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_finished_goods(DeserializeMode mode, const vector& filters) { auto & pfinished_goods = mPile->settings.finished_goods; read_category("finished_goods", mode, std::bind(&StockpileSettings::has_finished_goods, mBuffer), @@ -1415,24 +1417,24 @@ void StockpileSerializer::read_finished_goods(DeserializeMode mode, const std::s [&](bool all, char val) { auto & bfinished_goods = mBuffer.finished_goods(); - unserialize_list_item_type("type", all, val, filter, finished_goods_type_is_allowed, - [&](const size_t& idx) -> const std::string& { return bfinished_goods.type(idx); }, + unserialize_list_item_type("type", all, val, filters, finished_goods_type_is_allowed, + [&](const size_t& idx) -> const string& { return bfinished_goods.type(idx); }, bfinished_goods.type_size(), pfinished_goods.type); - unserialize_list_material("mats", all, val, filter, finished_goods_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bfinished_goods.mats(idx); }, + unserialize_list_material("mats", all, val, filters, finished_goods_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bfinished_goods.mats(idx); }, bfinished_goods.mats_size(), pfinished_goods.mats); - unserialize_list_other_mats("other", all, val, filter, mOtherMatsFinishedGoods.mats, - [&](const size_t& idx) -> const std::string& { return bfinished_goods.other_mats(idx); }, + unserialize_list_other_mats("other", all, val, filters, mOtherMatsFinishedGoods.mats, + [&](const size_t& idx) -> const string& { return bfinished_goods.other_mats(idx); }, bfinished_goods.other_mats_size(), pfinished_goods.other_mats); - unserialize_list_quality("core", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bfinished_goods.quality_core(idx); }, + unserialize_list_quality("core", all, val, filters, + [&](const size_t& idx) -> const string& { return bfinished_goods.quality_core(idx); }, bfinished_goods.quality_core_size(), pfinished_goods.quality_core); - unserialize_list_quality("total", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bfinished_goods.quality_total(idx); }, + unserialize_list_quality("total", all, val, filters, + [&](const size_t& idx) -> const string& { return bfinished_goods.quality_total(idx); }, bfinished_goods.quality_total_size(), pfinished_goods.quality_total); }); } @@ -1443,155 +1445,155 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego switch (cat) { case organic_mat_category::Meat: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_meat(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().meat(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().meat(idx); }; return food_pair("meat", setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); } case organic_mat_category::Fish: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_fish(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().fish(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().fish(idx); }; return food_pair("fish", setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_unprepared_fish(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().unprepared_fish(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().unprepared_fish(idx); }; return food_pair("unpreparedfish", setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_egg(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().egg(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().egg(idx); }; return food_pair("egg", setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); } case organic_mat_category::Plants: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_plants(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().plants(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().plants(idx); }; return food_pair("plants", setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); } case organic_mat_category::PlantDrink: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_drink_plant(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().drink_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().drink_plant(idx); }; return food_pair("drink/plant", setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); } case organic_mat_category::CreatureDrink: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_drink_animal(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().drink_animal(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().drink_animal(idx); }; return food_pair("drink/animal", setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); } case organic_mat_category::PlantCheese: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_cheese_plant(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().cheese_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().cheese_plant(idx); }; return food_pair("cheese/plant", setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); } case organic_mat_category::CreatureCheese: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_cheese_animal(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().cheese_animal(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().cheese_animal(idx); }; return food_pair("cheese/animal", setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); } case organic_mat_category::Seed: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_seeds(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().seeds(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().seeds(idx); }; return food_pair("seeds", setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); } case organic_mat_category::Leaf: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_leaves(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().leaves(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().leaves(idx); }; return food_pair("leaves", setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); } case organic_mat_category::PlantPowder: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_powder_plant(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().powder_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().powder_plant(idx); }; return food_pair("powder/plant", setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); } case organic_mat_category::CreaturePowder: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_powder_creature(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().powder_creature(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().powder_creature(idx); }; return food_pair("powder/animal", setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); } case organic_mat_category::Glob: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_glob(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().glob(idx); }; return food_pair("glob", setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); } case organic_mat_category::PlantLiquid: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_liquid_plant(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_plant(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().liquid_plant(idx); }; return food_pair("liquid/plant", setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); } case organic_mat_category::CreatureLiquid: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_liquid_animal(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_animal(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().liquid_animal(idx); }; return food_pair("liquid/animal", setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); } case organic_mat_category::MiscLiquid: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_liquid_misc(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().liquid_misc(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().liquid_misc(idx); }; return food_pair("liquid/misc", setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); } case organic_mat_category::Paste: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_glob_paste(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob_paste(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().glob_paste(idx); }; return food_pair("paste", setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); } case organic_mat_category::Pressed: { - FuncWriteExport setter = [&](const std::string& id) { + FuncWriteExport setter = [&](const string& id) { mBuffer.mutable_food()->add_glob_pressed(id); }; - FuncReadImport getter = [&](size_t idx) -> std::string { return mBuffer.food().glob_pressed(idx); }; + FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().glob_pressed(idx); }; return food_pair("pressed", setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); } case organic_mat_category::Leather: @@ -1638,7 +1640,7 @@ bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { return all; } -void StockpileSerializer::read_food(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_food(DeserializeMode mode, const vector& filters) { using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; @@ -1660,13 +1662,13 @@ void StockpileSerializer::read_food(DeserializeMode mode, const std::string& fil [&](bool all, char val) { auto & bfood = mBuffer.food(); - set_flag("preparedmeals", filter, all, val, bfood.prepared_meals(), pfood.prepared_meals); + set_flag("preparedmeals", filters, all, val, bfood.prepared_meals(), pfood.prepared_meals); for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { food_pair p = food_map((organic_mat_category)mat_category); if (!p.valid) continue; - unserialize_list_organic_mat(p.name, all, val, filter, + unserialize_list_organic_mat(p.name, all, val, filters, p.get_value, p.serialized_count, *p.stockpile_values, (organic_mat_category)mat_category); } @@ -1690,30 +1692,30 @@ bool StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furni all = false; continue; } - std::string f_type(type_traits::key_table[i]); + string f_type(type_traits::key_table[i]); furniture->add_type(f_type); DEBUG(log).print("furniture_type %zd is %s\n", i, f_type.c_str()); } all = serialize_list_material( furniture_mat_is_allowed, - [&](const std::string& token) { furniture->add_mats(token); }, + [&](const string& token) { furniture->add_mats(token); }, pfurniture.mats) && all; all = serialize_list_other_mats( mOtherMatsFurniture.mats, - [&](const std::string& token) { furniture->add_other_mats(token); }, + [&](const string& token) { furniture->add_other_mats(token); }, pfurniture.other_mats) && all; all = serialize_list_quality( - [&](const std::string& token) { furniture->add_quality_core(token); }, + [&](const string& token) { furniture->add_quality_core(token); }, pfurniture.quality_core) && all; all = serialize_list_quality( - [&](const std::string& token) { furniture->add_quality_total(token); }, + [&](const string& token) { furniture->add_quality_total(token); }, pfurniture.quality_total) && all; return all; } -void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_furniture(DeserializeMode mode, const vector& filters) { auto & pfurniture = mPile->settings.furniture; read_category("furniture", mode, std::bind(&StockpileSettings::has_furniture, mBuffer), @@ -1738,34 +1740,34 @@ void StockpileSerializer::read_furniture(DeserializeMode mode, const std::string if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = ENUM_KEY_STR(furniture_type, (df::furniture_type)idx); - set_filter_elem("type", filter, val, id, idx, pfurniture.type.at(idx)); + set_filter_elem("type", filters, val, id, idx, pfurniture.type.at(idx)); } } else { for (int i = 0; i < bfurniture.type_size(); ++i) { - const std::string token = bfurniture.type(i); + const string token = bfurniture.type(i); df::enum_traits::base_type idx = linear_index(type_traits, token); if (idx < 0 || size_t(idx) >= pfurniture.type.size()) { WARN(log).print("furniture type index invalid %s, idx=%d\n", token.c_str(), idx); continue; } - set_filter_elem("type", filter, val, token, idx, pfurniture.type.at(idx)); + set_filter_elem("type", filters, val, token, idx, pfurniture.type.at(idx)); } } - unserialize_list_material("mats", all, val, filter, furniture_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bfurniture.mats(idx); }, + unserialize_list_material("mats", all, val, filters, furniture_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bfurniture.mats(idx); }, bfurniture.mats_size(), pfurniture.mats); - unserialize_list_other_mats("other", all, val, filter, - mOtherMatsFurniture.mats, [&](const size_t& idx) -> const std::string& { return bfurniture.other_mats(idx); }, + unserialize_list_other_mats("other", all, val, filters, + mOtherMatsFurniture.mats, [&](const size_t& idx) -> const string& { return bfurniture.other_mats(idx); }, bfurniture.other_mats_size(), pfurniture.other_mats); - unserialize_list_quality("core", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bfurniture.quality_core(idx); }, + unserialize_list_quality("core", all, val, filters, + [&](const size_t& idx) -> const string& { return bfurniture.quality_core(idx); }, bfurniture.quality_core_size(), pfurniture.quality_core); - unserialize_list_quality("total", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bfurniture.quality_total(idx); }, + unserialize_list_quality("total", all, val, filters, + [&](const size_t& idx) -> const string& { return bfurniture.quality_total(idx); }, bfurniture.quality_total_size(), pfurniture.quality_total); }); } @@ -1789,12 +1791,12 @@ bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { bool all = serialize_list_material( gem_mat_is_allowed, - [&](const std::string& token) { gems->add_rough_mats(token); }, + [&](const string& token) { gems->add_rough_mats(token); }, pgems.rough_mats); all = serialize_list_material( gem_cut_mat_is_allowed, - [&](const std::string& token) { gems->add_cut_mats(token); }, + [&](const string& token) { gems->add_cut_mats(token); }, pgems.cut_mats) && all; for (size_t i = 0; i < pgems.rough_other_mats.size(); ++i) { @@ -1826,7 +1828,7 @@ bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { return all; } -void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_gems(DeserializeMode mode, const vector& filters) { auto & pgems = mPile->settings.gems; read_category("gems", mode, std::bind(&StockpileSettings::has_gems, mBuffer), @@ -1842,12 +1844,12 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil [&](bool all, char val) { auto & bgems = mBuffer.gems(); - unserialize_list_material("mats/rough", all, val, filter, gem_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bgems.rough_mats(idx); }, + unserialize_list_material("mats/rough", all, val, filters, gem_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bgems.rough_mats(idx); }, bgems.rough_mats_size(),pgems.rough_mats); - unserialize_list_material("mats/cut", all, val, filter, gem_cut_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bgems.cut_mats(idx); }, + unserialize_list_material("mats/cut", all, val, filters, gem_cut_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bgems.cut_mats(idx); }, bgems.cut_mats_size(), pgems.cut_mats); const size_t builtin_size = std::extentraws.mat_table.builtin)>::value; @@ -1858,11 +1860,11 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil MaterialInfo mi; mi.decode(idx, -1); if (gem_other_mat_is_allowed(mi)) - set_filter_elem("other/rough", filter, val, mi.getToken(), idx, pgems.rough_other_mats.at(idx)); + set_filter_elem("other/rough", filters, val, mi.getToken(), idx, pgems.rough_other_mats.at(idx)); if (!mi.isValid()) mi.decode(0, idx); if (gem_other_mat_is_allowed(mi)) - set_filter_elem("other/cut", filter, val, mi.getToken(), idx, pgems.cut_other_mats.at(idx)); + set_filter_elem("other/cut", filters, val, mi.getToken(), idx, pgems.cut_other_mats.at(idx)); } return; } else { @@ -1870,10 +1872,10 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil for (size_t i = 0; i < builtin_size; ++i) { string id = bgems.rough_other_mats(i); if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) - set_filter_elem("other/rough", filter, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); + set_filter_elem("other/rough", filters, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); id = bgems.cut_other_mats(i); if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) - set_filter_elem("other/cut", filter, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); + set_filter_elem("other/cut", filters, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); } } }); @@ -1881,11 +1883,11 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const std::string& fil bool StockpileSerializer::write_leather(StockpileSettings::LeatherSet* leather) { return serialize_list_organic_mat( - [&](const std::string& id) { leather->add_mats(id); }, + [&](const string& id) { leather->add_mats(id); }, &mPile->settings.leather.mats, organic_mat_category::Leather); } -void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_leather(DeserializeMode mode, const vector& filters) { auto & pleather = mPile->settings.leather; read_category("leather", mode, std::bind(&StockpileSettings::has_leather, mBuffer), @@ -1898,19 +1900,19 @@ void StockpileSerializer::read_leather(DeserializeMode mode, const std::string& [&](bool all, char val) { auto & bleather = mBuffer.leather(); - unserialize_list_organic_mat("", all, val, filter, - [&](size_t idx) -> std::string { return bleather.mats(idx); }, + unserialize_list_organic_mat("", all, val, filters, + [&](size_t idx) -> string { return bleather.mats(idx); }, bleather.mats_size(), pleather.mats, organic_mat_category::Leather); }); } bool StockpileSerializer::write_corpses(StockpileSettings::CorpsesSet* corpses) { return serialize_list_creature( - [&](const std::string& token) { corpses->add_corpses(token); }, + [&](const string& token) { corpses->add_corpses(token); }, mPile->settings.corpses.corpses); } -void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_corpses(DeserializeMode mode, const vector& filters) { auto & pcorpses = mPile->settings.corpses; read_category("corpses", mode, std::bind(&StockpileSettings::has_corpses_v50, mBuffer), @@ -1922,8 +1924,8 @@ void StockpileSerializer::read_corpses(DeserializeMode mode, const std::string& }, [&](bool all, char val) { auto & bcorpses = mBuffer.corpses_v50(); - unserialize_list_creature("", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bcorpses.corpses(idx); }, + unserialize_list_creature("", all, val, filters, + [&](const size_t& idx) -> const string& { return bcorpses.corpses(idx); }, bcorpses.corpses_size(), pcorpses.corpses); }); } @@ -1945,38 +1947,38 @@ bool StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { refuse->set_rotten_raw_hide(prefuse.rotten_raw_hide); all = serialize_list_item_type(refuse_type_is_allowed, - [&](const std::string& token) { refuse->add_type(token); }, + [&](const string& token) { refuse->add_type(token); }, prefuse.type) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_corpses(token); }, + [&](const string& token) { refuse->add_corpses(token); }, prefuse.corpses) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_body_parts(token); }, + [&](const string& token) { refuse->add_body_parts(token); }, prefuse.body_parts) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_skulls(token); }, + [&](const string& token) { refuse->add_skulls(token); }, prefuse.skulls) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_bones(token); }, + [&](const string& token) { refuse->add_bones(token); }, prefuse.bones) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_hair(token); }, + [&](const string& token) { refuse->add_hair(token); }, prefuse.hair) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_shells(token); }, + [&](const string& token) { refuse->add_shells(token); }, prefuse.shells) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_teeth(token); }, + [&](const string& token) { refuse->add_teeth(token); }, prefuse.teeth) && all; all = serialize_list_creature( - [&](const std::string& token) { refuse->add_horns(token); }, + [&](const string& token) { refuse->add_horns(token); }, prefuse.horns) && all; return all; } -void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_refuse(DeserializeMode mode, const vector& filters) { auto & prefuse = mPile->settings.refuse; read_category("refuse", mode, std::bind(&StockpileSettings::has_refuse, mBuffer), @@ -1999,35 +2001,35 @@ void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& f [&](bool all, char val) { auto & brefuse = mBuffer.refuse(); - set_flag("rawhide/fresh", filter, all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); - set_flag("rawhide/rotten", filter, all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); + set_flag("rawhide/fresh", filters, all, val, brefuse.fresh_raw_hide(), prefuse.fresh_raw_hide); + set_flag("rawhide/rotten", filters, all, val, brefuse.rotten_raw_hide(), prefuse.rotten_raw_hide); - unserialize_list_item_type("type", all, val, filter, refuse_type_is_allowed, + unserialize_list_item_type("type", all, val, filters, refuse_type_is_allowed, [&](const size_t& idx) -> const string& { return brefuse.type(idx); }, brefuse.type_size(), prefuse.type); - unserialize_list_creature("corpses", all, val, filter, + unserialize_list_creature("corpses", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.corpses(idx); }, brefuse.corpses_size(), prefuse.corpses); - unserialize_list_creature("bodyparts", all, val, filter, + unserialize_list_creature("bodyparts", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.body_parts(idx); }, brefuse.body_parts_size(), prefuse.body_parts); - unserialize_list_creature("skulls", all, val, filter, + unserialize_list_creature("skulls", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.skulls(idx); }, brefuse.skulls_size(), prefuse.skulls); - unserialize_list_creature("bones", all, val, filter, + unserialize_list_creature("bones", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.bones(idx); }, brefuse.bones_size(), prefuse.bones); - unserialize_list_creature("hair", all, val, filter, + unserialize_list_creature("hair", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.hair(idx); }, brefuse.hair_size(), prefuse.hair); - unserialize_list_creature("shells", all, val, filter, + unserialize_list_creature("shells", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.shells(idx); }, brefuse.shells_size(), prefuse.shells); - unserialize_list_creature("teeth", all, val, filter, + unserialize_list_creature("teeth", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.teeth(idx); }, brefuse.teeth_size(), prefuse.teeth); - unserialize_list_creature("horns", all, val, filter, + unserialize_list_creature("horns", all, val, filters, [&](const size_t& idx) -> const string& { return brefuse.horns(idx); }, brefuse.horns_size(), prefuse.horns); }); @@ -2036,17 +2038,17 @@ void StockpileSerializer::read_refuse(DeserializeMode mode, const std::string& f bool StockpileSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { bool all = serialize_list_organic_mat( - [&](const std::string& token) { sheet->add_paper(token); }, + [&](const string& token) { sheet->add_paper(token); }, &mPile->settings.sheet.paper, organic_mat_category::Paper); all = serialize_list_organic_mat( - [&](const std::string& token) { sheet->add_parchment(token); }, + [&](const string& token) { sheet->add_parchment(token); }, &mPile->settings.sheet.parchment, organic_mat_category::Parchment) && all; return all; } -void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_sheet(DeserializeMode mode, const vector& filters) { auto & psheet = mPile->settings.sheet; read_category("sheet", mode, std::bind(&StockpileSettings::has_sheet, mBuffer), @@ -2060,12 +2062,12 @@ void StockpileSerializer::read_sheet(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bsheet = mBuffer.sheet(); - unserialize_list_organic_mat("paper", all, val, filter, - [&](size_t idx) -> std::string { return bsheet.paper(idx); }, + unserialize_list_organic_mat("paper", all, val, filters, + [&](size_t idx) -> string { return bsheet.paper(idx); }, bsheet.paper_size(), psheet.paper, organic_mat_category::Paper); - unserialize_list_organic_mat("parchment", all, val, filter, - [&](size_t idx) -> std::string { return bsheet.parchment(idx); }, + unserialize_list_organic_mat("parchment", all, val, filters, + [&](size_t idx) -> string { return bsheet.parchment(idx); }, bsheet.parchment_size(), psheet.parchment, organic_mat_category::Parchment); }); } @@ -2081,11 +2083,11 @@ static bool stone_is_allowed(const MaterialInfo& mi) { bool StockpileSerializer::write_stone(StockpileSettings::StoneSet* stone) { return serialize_list_material( stone_is_allowed, - [&](const std::string& token) { stone->add_mats(token); }, + [&](const string& token) { stone->add_mats(token); }, mPile->settings.stone.mats); } -void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_stone(DeserializeMode mode, const vector& filters) { auto & pstone = mPile->settings.stone; read_category("stone", mode, std::bind(&StockpileSettings::has_stone, mBuffer), @@ -2098,8 +2100,8 @@ void StockpileSerializer::read_stone(DeserializeMode mode, const std::string& fi [&](bool all, char val) { auto & bstone = mBuffer.stone(); - unserialize_list_material("", all, val, filter, stone_is_allowed, - [&](const size_t& idx) -> const std::string& { return bstone.mats(idx); }, + unserialize_list_material("", all, val, filters, stone_is_allowed, + [&](const size_t& idx) -> const string& { return bstone.mats(idx); }, bstone.mats_size(), pstone.mats); }); } @@ -2116,39 +2118,39 @@ bool StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) weapons->set_usable(pweapons.usable); all = serialize_list_itemdef( - [&](const std::string& token) { weapons->add_weapon_type(token); }, + [&](const string& token) { weapons->add_weapon_type(token); }, pweapons.weapon_type, - std::vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), + vector(world->raws.itemdefs.weapons.begin(), world->raws.itemdefs.weapons.end()), item_type::WEAPON) && all; all = serialize_list_itemdef( - [&](const std::string& token) { weapons->add_trapcomp_type(token); }, + [&](const string& token) { weapons->add_trapcomp_type(token); }, pweapons.trapcomp_type, - std::vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), + vector(world->raws.itemdefs.trapcomps.begin(), world->raws.itemdefs.trapcomps.end()), item_type::TRAPCOMP) && all; all = serialize_list_material( weapons_mat_is_allowed, - [&](const std::string& token) { weapons->add_mats(token); }, + [&](const string& token) { weapons->add_mats(token); }, pweapons.mats) && all; all = serialize_list_other_mats( mOtherMatsWeaponsArmor.mats, - [&](const std::string& token) { weapons->add_other_mats(token); }, + [&](const string& token) { weapons->add_other_mats(token); }, pweapons.other_mats) && all; all = serialize_list_quality( - [&](const std::string& token) { weapons->add_quality_core(token); }, + [&](const string& token) { weapons->add_quality_core(token); }, pweapons.quality_core) && all; all = serialize_list_quality( - [&](const std::string& token) { weapons->add_quality_total(token); }, + [&](const string& token) { weapons->add_quality_total(token); }, pweapons.quality_total) && all; return all; } -void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_weapons(DeserializeMode mode, const vector& filters) { auto & pweapons = mPile->settings.weapons; read_category("weapons", mode, std::bind(&StockpileSettings::has_weapons, mBuffer), @@ -2166,31 +2168,31 @@ void StockpileSerializer::read_weapons(DeserializeMode mode, const std::string& [&](bool all, char val) { auto & bweapons = mBuffer.weapons(); - set_flag("nouse", filter, all, val, bweapons.unusable(), pweapons.unusable); - set_flag("canuse", filter, all, val, bweapons.usable(), pweapons.usable); + set_flag("nouse", filters, all, val, bweapons.unusable(), pweapons.unusable); + set_flag("canuse", filters, all, val, bweapons.usable(), pweapons.usable); - unserialize_list_itemdef("type/weapon", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bweapons.weapon_type(idx); }, + unserialize_list_itemdef("type/weapon", all, val, filters, + [&](const size_t& idx) -> const string& { return bweapons.weapon_type(idx); }, bweapons.weapon_type_size(), pweapons.weapon_type, item_type::WEAPON); - unserialize_list_itemdef("type/trapcomp", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bweapons.trapcomp_type(idx); }, + unserialize_list_itemdef("type/trapcomp", all, val, filters, + [&](const size_t& idx) -> const string& { return bweapons.trapcomp_type(idx); }, bweapons.trapcomp_type_size(), pweapons.trapcomp_type, item_type::TRAPCOMP); - unserialize_list_material("mats", all, val, filter, weapons_mat_is_allowed, - [&](const size_t& idx) -> const std::string& { return bweapons.mats(idx); }, + unserialize_list_material("mats", all, val, filters, weapons_mat_is_allowed, + [&](const size_t& idx) -> const string& { return bweapons.mats(idx); }, bweapons.mats_size(), pweapons.mats); - unserialize_list_other_mats("other", all, val, filter, mOtherMatsWeaponsArmor.mats, - [&](const size_t& idx) -> const std::string& { return bweapons.other_mats(idx); }, + unserialize_list_other_mats("other", all, val, filters, mOtherMatsWeaponsArmor.mats, + [&](const size_t& idx) -> const string& { return bweapons.other_mats(idx); }, bweapons.other_mats_size(), pweapons.other_mats); - unserialize_list_quality("core", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bweapons.quality_core(idx); }, + unserialize_list_quality("core", all, val, filters, + [&](const size_t& idx) -> const string& { return bweapons.quality_core(idx); }, bweapons.quality_core_size(), pweapons.quality_core); - unserialize_list_quality("total", all, val, filter, - [&](const size_t& idx) -> const std::string& { return bweapons.quality_total(idx); }, + unserialize_list_quality("total", all, val, filters, + [&](const size_t& idx) -> const string& { return bweapons.quality_total(idx); }, bweapons.quality_total_size(), pweapons.quality_total); }); } @@ -2215,7 +2217,7 @@ bool StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { return all; } -void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& filter) { +void StockpileSerializer::read_wood(DeserializeMode mode, const vector& filters) { auto & pwood = mPile->settings.wood; read_category("wood", mode, std::bind(&StockpileSettings::has_wood, mBuffer), @@ -2234,17 +2236,17 @@ void StockpileSerializer::read_wood(DeserializeMode mode, const std::string& fil if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { string id = world->raws.plants.all[idx]->id; - set_filter_elem("", filter, val, id, idx, pwood.mats.at(idx)); + set_filter_elem("", filters, val, id, idx, pwood.mats.at(idx)); } } else { for (int i = 0; i < bwood.mats_size(); ++i) { - const std::string token = bwood.mats(i); + const string token = bwood.mats(i); const size_t idx = find_plant(token); if (idx < 0 || (size_t)idx >= num_elems) { WARN(log).print("wood mat index invalid %s idx=%zd\n", token.c_str(), idx); continue; } - set_filter_elem("", filter, val, token, idx, pwood.mats.at(idx)); + set_filter_elem("", filters, val, token, idx, pwood.mats.at(idx)); } } }); diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index 8573a453e..f0e1a62e2 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -81,12 +81,12 @@ public: /** * Again, copied from message.cc */ - bool parse_from_istream(std::istream* input, DeserializeMode mode, const std::string& filter); + bool parse_from_istream(std::istream* input, DeserializeMode mode, const std::vector& filters); /** * Read stockpile settings from file */ - bool unserialize_from_file(const std::string& file, DeserializeMode mode, const std::string& filter); + bool unserialize_from_file(const std::string& file, DeserializeMode mode, const std::vector& filters); private: df::building_stockpilest* mPile; @@ -96,7 +96,7 @@ private: void write(uint32_t includedElements); // parse serialized data into ui indices - void read(DeserializeMode mode, const std::string& filter); + void read(DeserializeMode mode, const std::vector& filters); void write_containers(); void read_containers(DeserializeMode mode); @@ -104,38 +104,38 @@ private: void read_general(DeserializeMode mode); bool write_ammo(dfstockpiles::StockpileSettings::AmmoSet* ammo); - void read_ammo(DeserializeMode mode, const std::string& filter); + void read_ammo(DeserializeMode mode, const std::vector& filters); bool write_animals(dfstockpiles::StockpileSettings::AnimalsSet* animals); - void read_animals(DeserializeMode mode, const std::string& filter); + void read_animals(DeserializeMode mode, const std::vector& filters); bool write_armor(dfstockpiles::StockpileSettings::ArmorSet* armor); - void read_armor(DeserializeMode mode, const std::string& filter); + void read_armor(DeserializeMode mode, const std::vector& filters); bool write_bars_blocks(dfstockpiles::StockpileSettings::BarsBlocksSet* bars_blocks); - void read_bars_blocks(DeserializeMode mode, const std::string& filter); + void read_bars_blocks(DeserializeMode mode, const std::vector& filters); bool write_cloth(dfstockpiles::StockpileSettings::ClothSet* cloth); - void read_cloth(DeserializeMode mode, const std::string& filter); + void read_cloth(DeserializeMode mode, const std::vector& filters); bool write_coins(dfstockpiles::StockpileSettings::CoinSet* coins); - void read_coins(DeserializeMode mode, const std::string& filter); + void read_coins(DeserializeMode mode, const std::vector& filters); bool write_finished_goods(dfstockpiles::StockpileSettings::FinishedGoodsSet* finished_goods); - void read_finished_goods(DeserializeMode mode, const std::string& filter); + void read_finished_goods(DeserializeMode mode, const std::vector& filters); food_pair food_map(df::enums::organic_mat_category::organic_mat_category cat); bool write_food(dfstockpiles::StockpileSettings::FoodSet* food); - void read_food(DeserializeMode mode, const std::string& filter); + void read_food(DeserializeMode mode, const std::vector& filters); bool write_furniture(dfstockpiles::StockpileSettings::FurnitureSet* furniture); - void read_furniture(DeserializeMode mode, const std::string& filter); + void read_furniture(DeserializeMode mode, const std::vector& filters); bool write_gems(dfstockpiles::StockpileSettings::GemsSet* gems); - void read_gems(DeserializeMode mode, const std::string& filter); + void read_gems(DeserializeMode mode, const std::vector& filters); bool write_leather(dfstockpiles::StockpileSettings::LeatherSet* leather); - void read_leather(DeserializeMode mode, const std::string& filter); + void read_leather(DeserializeMode mode, const std::vector& filters); bool write_corpses(dfstockpiles::StockpileSettings::CorpsesSet* corpses); - void read_corpses(DeserializeMode mode, const std::string& filter); + void read_corpses(DeserializeMode mode, const std::vector& filters); bool write_refuse(dfstockpiles::StockpileSettings::RefuseSet* refuse); - void read_refuse(DeserializeMode mode, const std::string& filter); + void read_refuse(DeserializeMode mode, const std::vector& filters); bool write_sheet(dfstockpiles::StockpileSettings::SheetSet* sheet); - void read_sheet(DeserializeMode mode, const std::string& filter); + void read_sheet(DeserializeMode mode, const std::vector& filters); bool write_stone(dfstockpiles::StockpileSettings::StoneSet* stone); - void read_stone(DeserializeMode mode, const std::string& filter); + void read_stone(DeserializeMode mode, const std::vector& filters); bool write_weapons(dfstockpiles::StockpileSettings::WeaponsSet* weapons); - void read_weapons(DeserializeMode mode, const std::string& filter); + void read_weapons(DeserializeMode mode, const std::vector& filters); bool write_wood(dfstockpiles::StockpileSettings::WoodSet* wood); - void read_wood(DeserializeMode mode, const std::string& filter); + void read_wood(DeserializeMode mode, const std::vector& filters); }; diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index 99cc16607..d1ce46e9c 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -129,9 +129,12 @@ static bool stockpiles_import(color_ostream& out, string fname, int id, string m else if (mode_str == "disable") mode = DESERIALIZE_MODE_DISABLE; + vector filters; + split_string(&filters, filter, ",", true); + try { StockpileSerializer cereal(sp); - if (!cereal.unserialize_from_file(fname, mode, filter)) { + if (!cereal.unserialize_from_file(fname, mode, filters)) { out.printerr("deserialization failed: '%s'\n", fname.c_str()); return false; } From 5af26675b44a4ec60e532ddde771367cfd88b7af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 23:28:56 -0700 Subject: [PATCH 0890/2222] rename "category" files to just "cat" --- data/stockpiles/{category_ammo.dfstock => cat_ammo.dfstock} | 0 data/stockpiles/{category_animals.dfstock => cat_animals.dfstock} | 0 data/stockpiles/{category_armor.dfstock => cat_armor.dfstock} | 0 .../{category_bars_blocks.dfstock => cat_bars_blocks.dfstock} | 0 data/stockpiles/{category_cloth.dfstock => cat_cloth.dfstock} | 0 data/stockpiles/{category_coins.dfstock => cat_coins.dfstock} | 0 data/stockpiles/{category_corpses.dfstock => cat_corpses.dfstock} | 0 ...category_finished_goods.dfstock => cat_finished_goods.dfstock} | 0 data/stockpiles/{category_food.dfstock => cat_food.dfstock} | 0 .../{category_furniture.dfstock => cat_furniture.dfstock} | 0 data/stockpiles/{category_gems.dfstock => cat_gems.dfstock} | 0 data/stockpiles/{category_leather.dfstock => cat_leather.dfstock} | 0 data/stockpiles/{category_refuse.dfstock => cat_refuse.dfstock} | 0 data/stockpiles/{category_sheets.dfstock => cat_sheets.dfstock} | 0 data/stockpiles/{category_stone.dfstock => cat_stone.dfstock} | 0 data/stockpiles/{category_weapons.dfstock => cat_weapons.dfstock} | 0 data/stockpiles/{category_wood.dfstock => cat_wood.dfstock} | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename data/stockpiles/{category_ammo.dfstock => cat_ammo.dfstock} (100%) rename data/stockpiles/{category_animals.dfstock => cat_animals.dfstock} (100%) rename data/stockpiles/{category_armor.dfstock => cat_armor.dfstock} (100%) rename data/stockpiles/{category_bars_blocks.dfstock => cat_bars_blocks.dfstock} (100%) rename data/stockpiles/{category_cloth.dfstock => cat_cloth.dfstock} (100%) rename data/stockpiles/{category_coins.dfstock => cat_coins.dfstock} (100%) rename data/stockpiles/{category_corpses.dfstock => cat_corpses.dfstock} (100%) rename data/stockpiles/{category_finished_goods.dfstock => cat_finished_goods.dfstock} (100%) rename data/stockpiles/{category_food.dfstock => cat_food.dfstock} (100%) rename data/stockpiles/{category_furniture.dfstock => cat_furniture.dfstock} (100%) rename data/stockpiles/{category_gems.dfstock => cat_gems.dfstock} (100%) rename data/stockpiles/{category_leather.dfstock => cat_leather.dfstock} (100%) rename data/stockpiles/{category_refuse.dfstock => cat_refuse.dfstock} (100%) rename data/stockpiles/{category_sheets.dfstock => cat_sheets.dfstock} (100%) rename data/stockpiles/{category_stone.dfstock => cat_stone.dfstock} (100%) rename data/stockpiles/{category_weapons.dfstock => cat_weapons.dfstock} (100%) rename data/stockpiles/{category_wood.dfstock => cat_wood.dfstock} (100%) diff --git a/data/stockpiles/category_ammo.dfstock b/data/stockpiles/cat_ammo.dfstock similarity index 100% rename from data/stockpiles/category_ammo.dfstock rename to data/stockpiles/cat_ammo.dfstock diff --git a/data/stockpiles/category_animals.dfstock b/data/stockpiles/cat_animals.dfstock similarity index 100% rename from data/stockpiles/category_animals.dfstock rename to data/stockpiles/cat_animals.dfstock diff --git a/data/stockpiles/category_armor.dfstock b/data/stockpiles/cat_armor.dfstock similarity index 100% rename from data/stockpiles/category_armor.dfstock rename to data/stockpiles/cat_armor.dfstock diff --git a/data/stockpiles/category_bars_blocks.dfstock b/data/stockpiles/cat_bars_blocks.dfstock similarity index 100% rename from data/stockpiles/category_bars_blocks.dfstock rename to data/stockpiles/cat_bars_blocks.dfstock diff --git a/data/stockpiles/category_cloth.dfstock b/data/stockpiles/cat_cloth.dfstock similarity index 100% rename from data/stockpiles/category_cloth.dfstock rename to data/stockpiles/cat_cloth.dfstock diff --git a/data/stockpiles/category_coins.dfstock b/data/stockpiles/cat_coins.dfstock similarity index 100% rename from data/stockpiles/category_coins.dfstock rename to data/stockpiles/cat_coins.dfstock diff --git a/data/stockpiles/category_corpses.dfstock b/data/stockpiles/cat_corpses.dfstock similarity index 100% rename from data/stockpiles/category_corpses.dfstock rename to data/stockpiles/cat_corpses.dfstock diff --git a/data/stockpiles/category_finished_goods.dfstock b/data/stockpiles/cat_finished_goods.dfstock similarity index 100% rename from data/stockpiles/category_finished_goods.dfstock rename to data/stockpiles/cat_finished_goods.dfstock diff --git a/data/stockpiles/category_food.dfstock b/data/stockpiles/cat_food.dfstock similarity index 100% rename from data/stockpiles/category_food.dfstock rename to data/stockpiles/cat_food.dfstock diff --git a/data/stockpiles/category_furniture.dfstock b/data/stockpiles/cat_furniture.dfstock similarity index 100% rename from data/stockpiles/category_furniture.dfstock rename to data/stockpiles/cat_furniture.dfstock diff --git a/data/stockpiles/category_gems.dfstock b/data/stockpiles/cat_gems.dfstock similarity index 100% rename from data/stockpiles/category_gems.dfstock rename to data/stockpiles/cat_gems.dfstock diff --git a/data/stockpiles/category_leather.dfstock b/data/stockpiles/cat_leather.dfstock similarity index 100% rename from data/stockpiles/category_leather.dfstock rename to data/stockpiles/cat_leather.dfstock diff --git a/data/stockpiles/category_refuse.dfstock b/data/stockpiles/cat_refuse.dfstock similarity index 100% rename from data/stockpiles/category_refuse.dfstock rename to data/stockpiles/cat_refuse.dfstock diff --git a/data/stockpiles/category_sheets.dfstock b/data/stockpiles/cat_sheets.dfstock similarity index 100% rename from data/stockpiles/category_sheets.dfstock rename to data/stockpiles/cat_sheets.dfstock diff --git a/data/stockpiles/category_stone.dfstock b/data/stockpiles/cat_stone.dfstock similarity index 100% rename from data/stockpiles/category_stone.dfstock rename to data/stockpiles/cat_stone.dfstock diff --git a/data/stockpiles/category_weapons.dfstock b/data/stockpiles/cat_weapons.dfstock similarity index 100% rename from data/stockpiles/category_weapons.dfstock rename to data/stockpiles/cat_weapons.dfstock diff --git a/data/stockpiles/category_wood.dfstock b/data/stockpiles/cat_wood.dfstock similarity index 100% rename from data/stockpiles/category_wood.dfstock rename to data/stockpiles/cat_wood.dfstock From cdf2c407bcfd5a9bd45a394ef38db38f933a3749 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 20 Mar 2023 23:53:16 -0700 Subject: [PATCH 0891/2222] simplify finished goods stockpile settings --- data/stockpiles/crafts.dfstock | 4 ++-- data/stockpiles/goblets.dfstock | 4 ++-- data/stockpiles/stonetools.dfstock | 4 ++-- data/stockpiles/toys.dfstock | 4 ++-- data/stockpiles/woodtools.dfstock | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/stockpiles/crafts.dfstock b/data/stockpiles/crafts.dfstock index 9367855ee..633dd9f1f 100644 --- a/data/stockpiles/crafts.dfstock +++ b/data/stockpiles/crafts.dfstock @@ -1,4 +1,4 @@ -bç- +bG FIGURINE AMULET SCEPTER @@ -7,4 +7,4 @@ b EARRING BRACELET GEM -TOTEMWOOD PLANT_CLOTHBONETOOTHHORNPEARLSHELLLEATHERSILKAMBERCORAL GREEN_GLASS CLEAR_GLASS CRYSTAL_GLASSYARNWAXINORGANIC:IRONINORGANIC:SILVERINORGANIC:COPPERINORGANIC:NICKELINORGANIC:ZINCINORGANIC:BRONZEINORGANIC:BRASSINORGANIC:STEELINORGANIC:PIG_IRONINORGANIC:PLATINUMINORGANIC:ELECTRUM INORGANIC:TININORGANIC:PEWTER_FINEINORGANIC:PEWTER_TRIFLEINORGANIC:PEWTER_LAYINORGANIC:LEADINORGANIC:ALUMINUMINORGANIC:NICKEL_SILVERINORGANIC:BILLONINORGANIC:STERLING_SILVERINORGANIC:BLACK_BRONZEINORGANIC:ROSE_GOLDINORGANIC:BISMUTHINORGANIC:BISMUTH_BRONZEINORGANIC:ADAMANTINEINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:ONYXINORGANIC:MORIONINORGANIC:SCHORLINORGANIC:LACE AGATEINORGANIC:BLUE JADEINORGANIC:LAPIS LAZULIINORGANIC:PRASEINORGANIC:PRASE OPALINORGANIC:BLOODSTONEINORGANIC:MOSS AGATEINORGANIC:MOSS OPALINORGANIC:VARISCITEINORGANIC:CHRYSOPRASEINORGANIC:CHRYSOCOLLAINORGANIC:SARDINORGANIC:CARNELIANINORGANIC:BANDED AGATEINORGANIC:SARDONYXINORGANIC:CHERRY OPALINORGANIC:LAVENDER JADEINORGANIC:PINK JADEINORGANIC:TUBE AGATEINORGANIC:FIRE AGATEINORGANIC:PLUME AGATEINORGANIC:BROWN JASPERINORGANIC:PICTURE JASPERINORGANIC:SMOKY QUARTZINORGANIC:WAX OPALINORGANIC:WOOD OPALINORGANIC:AMBER OPALINORGANIC:GOLD OPALINORGANIC:CITRINEINORGANIC:YELLOW JASPERINORGANIC:TIGEREYEINORGANIC:TIGER IRONINORGANIC:SUNSTONEINORGANIC:RESIN OPALINORGANIC:PYRITEINORGANIC:CLEAR TOURMALINEINORGANIC:GRAY CHALCEDONYINORGANIC:DENDRITIC AGATEINORGANIC:SHELL OPALINORGANIC:BONE OPALINORGANIC:WHITE CHALCEDONYINORGANIC:FORTIFICATION AGATEINORGANIC:MILK QUARTZINORGANIC:MOONSTONEINORGANIC:WHITE JADEINORGANIC:JASPER OPALINORGANIC:PINEAPPLE OPALINORGANIC:ONYX OPALINORGANIC:MILK OPALINORGANIC:PIPE OPALINORGANIC:AVENTURINEINORGANIC:TURQUOISEINORGANIC:QUARTZ_ROSEINORGANIC:CRYSTAL_ROCKINORGANIC:BLACK ZIRCONINORGANIC:BLACK PYROPEINORGANIC:MELANITEINORGANIC:INDIGO TOURMALINEINORGANIC:BLUE GARNETINORGANIC:TSAVORITEINORGANIC:GREEN TOURMALINEINORGANIC:DEMANTOIDINORGANIC:GREEN ZIRCONINORGANIC:GREEN JADEINORGANIC:HELIODORINORGANIC:PERIDOTINORGANIC:RED ZIRCONINORGANIC:RED TOURMALINEINORGANIC:RED PYROPEINORGANIC:ALMANDINEINORGANIC:RED GROSSULARINORGANIC:PINK TOURMALINEINORGANIC:RED BERYLINORGANIC:FIRE OPALINORGANIC:RHODOLITEINORGANIC:SPINEL_PURPLEINORGANIC:ALEXANDRITEINORGANIC:TANZANITEINORGANIC:MORGANITEINORGANIC:VIOLET SPESSARTINEINORGANIC:PINK GARNETINORGANIC:KUNZITEINORGANIC:CINNAMON GROSSULARINORGANIC:HONEY YELLOW BERYLINORGANIC:JELLY OPALINORGANIC:BROWN ZIRCONINORGANIC:YELLOW ZIRCONINORGANIC:GOLDEN BERYLINORGANIC:YELLOW SPESSARTINEINORGANIC:TOPAZINORGANIC:TOPAZOLITEINORGANIC:YELLOW GROSSULARINORGANIC:RUBICELLEINORGANIC:CLEAR GARNETINORGANIC:GOSHENITEINORGANIC:CAT'S EYEINORGANIC:CLEAR ZIRCONINORGANIC:AMETHYSTINORGANIC:AQUAMARINEINORGANIC:SPINEL_REDINORGANIC:CHRYSOBERYLINORGANIC:OPAL_PFIREINORGANIC:OPAL_REDFLASHINORGANIC:OPAL_BLACKINORGANIC:OPAL_WHITEINORGANIC:OPAL_CRYSTALINORGANIC:OPAL_CLAROINORGANIC:OPAL_LEVININORGANIC:OPAL_HARLEQUININORGANIC:OPAL_PINFIREINORGANIC:OPAL_BANDFIREINORGANIC:DIAMOND_LYINORGANIC:DIAMOND_FYINORGANIC:EMERALDINORGANIC:RUBYINORGANIC:SAPPHIREINORGANIC:DIAMOND_CLEARINORGANIC:DIAMOND_REDINORGANIC:DIAMOND_GREENINORGANIC:DIAMOND_BLUEINORGANIC:DIAMOND_YELLOWINORGANIC:DIAMOND_BLACKINORGANIC:SAPPHIRE_STARINORGANIC:RUBY_STARINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE INORGANIC:BROMS_CLEAN_CORPSEDUSTINORGANIC:GOLDINORGANIC:DIVINE_1INORGANIC:DIVINE_3INORGANIC:DIVINE_5INORGANIC:DIVINE_7INORGANIC:DIVINE_9INORGANIC:DIVINE_11INORGANIC:DIVINE_13INORGANIC:DIVINE_15INORGANIC:DIVINE_17INORGANIC:DIVINE_19"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file +TOTEM \ No newline at end of file diff --git a/data/stockpiles/goblets.dfstock b/data/stockpiles/goblets.dfstock index b117634ba..bff4f8a6d 100644 --- a/data/stockpiles/goblets.dfstock +++ b/data/stockpiles/goblets.dfstock @@ -1,2 +1,2 @@ -b¨- -GOBLETWOOD PLANT_CLOTHBONETOOTHHORNPEARLSHELLLEATHERSILKAMBERCORAL GREEN_GLASS CLEAR_GLASS CRYSTAL_GLASSYARNWAXINORGANIC:IRONINORGANIC:SILVERINORGANIC:COPPERINORGANIC:NICKELINORGANIC:ZINCINORGANIC:BRONZEINORGANIC:BRASSINORGANIC:STEELINORGANIC:PIG_IRONINORGANIC:PLATINUMINORGANIC:ELECTRUM INORGANIC:TININORGANIC:PEWTER_FINEINORGANIC:PEWTER_TRIFLEINORGANIC:PEWTER_LAYINORGANIC:LEADINORGANIC:ALUMINUMINORGANIC:NICKEL_SILVERINORGANIC:BILLONINORGANIC:STERLING_SILVERINORGANIC:BLACK_BRONZEINORGANIC:ROSE_GOLDINORGANIC:BISMUTHINORGANIC:BISMUTH_BRONZEINORGANIC:ADAMANTINEINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:ONYXINORGANIC:MORIONINORGANIC:SCHORLINORGANIC:LACE AGATEINORGANIC:BLUE JADEINORGANIC:LAPIS LAZULIINORGANIC:PRASEINORGANIC:PRASE OPALINORGANIC:BLOODSTONEINORGANIC:MOSS AGATEINORGANIC:MOSS OPALINORGANIC:VARISCITEINORGANIC:CHRYSOPRASEINORGANIC:CHRYSOCOLLAINORGANIC:SARDINORGANIC:CARNELIANINORGANIC:BANDED AGATEINORGANIC:SARDONYXINORGANIC:CHERRY OPALINORGANIC:LAVENDER JADEINORGANIC:PINK JADEINORGANIC:TUBE AGATEINORGANIC:FIRE AGATEINORGANIC:PLUME AGATEINORGANIC:BROWN JASPERINORGANIC:PICTURE JASPERINORGANIC:SMOKY QUARTZINORGANIC:WAX OPALINORGANIC:WOOD OPALINORGANIC:AMBER OPALINORGANIC:GOLD OPALINORGANIC:CITRINEINORGANIC:YELLOW JASPERINORGANIC:TIGEREYEINORGANIC:TIGER IRONINORGANIC:SUNSTONEINORGANIC:RESIN OPALINORGANIC:PYRITEINORGANIC:CLEAR TOURMALINEINORGANIC:GRAY CHALCEDONYINORGANIC:DENDRITIC AGATEINORGANIC:SHELL OPALINORGANIC:BONE OPALINORGANIC:WHITE CHALCEDONYINORGANIC:FORTIFICATION AGATEINORGANIC:MILK QUARTZINORGANIC:MOONSTONEINORGANIC:WHITE JADEINORGANIC:JASPER OPALINORGANIC:PINEAPPLE OPALINORGANIC:ONYX OPALINORGANIC:MILK OPALINORGANIC:PIPE OPALINORGANIC:AVENTURINEINORGANIC:TURQUOISEINORGANIC:QUARTZ_ROSEINORGANIC:CRYSTAL_ROCKINORGANIC:BLACK ZIRCONINORGANIC:BLACK PYROPEINORGANIC:MELANITEINORGANIC:INDIGO TOURMALINEINORGANIC:BLUE GARNETINORGANIC:TSAVORITEINORGANIC:GREEN TOURMALINEINORGANIC:DEMANTOIDINORGANIC:GREEN ZIRCONINORGANIC:GREEN JADEINORGANIC:HELIODORINORGANIC:PERIDOTINORGANIC:RED ZIRCONINORGANIC:RED TOURMALINEINORGANIC:RED PYROPEINORGANIC:ALMANDINEINORGANIC:RED GROSSULARINORGANIC:PINK TOURMALINEINORGANIC:RED BERYLINORGANIC:FIRE OPALINORGANIC:RHODOLITEINORGANIC:SPINEL_PURPLEINORGANIC:ALEXANDRITEINORGANIC:TANZANITEINORGANIC:MORGANITEINORGANIC:VIOLET SPESSARTINEINORGANIC:PINK GARNETINORGANIC:KUNZITEINORGANIC:CINNAMON GROSSULARINORGANIC:HONEY YELLOW BERYLINORGANIC:JELLY OPALINORGANIC:BROWN ZIRCONINORGANIC:YELLOW ZIRCONINORGANIC:GOLDEN BERYLINORGANIC:YELLOW SPESSARTINEINORGANIC:TOPAZINORGANIC:TOPAZOLITEINORGANIC:YELLOW GROSSULARINORGANIC:RUBICELLEINORGANIC:CLEAR GARNETINORGANIC:GOSHENITEINORGANIC:CAT'S EYEINORGANIC:CLEAR ZIRCONINORGANIC:AMETHYSTINORGANIC:AQUAMARINEINORGANIC:SPINEL_REDINORGANIC:CHRYSOBERYLINORGANIC:OPAL_PFIREINORGANIC:OPAL_REDFLASHINORGANIC:OPAL_BLACKINORGANIC:OPAL_WHITEINORGANIC:OPAL_CRYSTALINORGANIC:OPAL_CLAROINORGANIC:OPAL_LEVININORGANIC:OPAL_HARLEQUININORGANIC:OPAL_PINFIREINORGANIC:OPAL_BANDFIREINORGANIC:DIAMOND_LYINORGANIC:DIAMOND_FYINORGANIC:EMERALDINORGANIC:RUBYINORGANIC:SAPPHIREINORGANIC:DIAMOND_CLEARINORGANIC:DIAMOND_REDINORGANIC:DIAMOND_GREENINORGANIC:DIAMOND_BLUEINORGANIC:DIAMOND_YELLOWINORGANIC:DIAMOND_BLACKINORGANIC:SAPPHIRE_STARINORGANIC:RUBY_STARINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE INORGANIC:BROMS_CLEAN_CORPSEDUSTINORGANIC:GOLDINORGANIC:DIVINE_1INORGANIC:DIVINE_3INORGANIC:DIVINE_5INORGANIC:DIVINE_7INORGANIC:DIVINE_9INORGANIC:DIVINE_11INORGANIC:DIVINE_13INORGANIC:DIVINE_15INORGANIC:DIVINE_17INORGANIC:DIVINE_19"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file +b +GOBLET \ No newline at end of file diff --git a/data/stockpiles/stonetools.dfstock b/data/stockpiles/stonetools.dfstock index 2ac7807ad..dee961a74 100644 --- a/data/stockpiles/stonetools.dfstock +++ b/data/stockpiles/stonetools.dfstock @@ -1,2 +1,2 @@ -bŒ -TOOLINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE INORGANIC:BROMS_CLEAN_CORPSEDUST"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file +bÆ +TOOLINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE \ No newline at end of file diff --git a/data/stockpiles/toys.dfstock b/data/stockpiles/toys.dfstock index 7edca2019..a90eab591 100644 --- a/data/stockpiles/toys.dfstock +++ b/data/stockpiles/toys.dfstock @@ -1,2 +1,2 @@ -b¥- -TOYWOOD PLANT_CLOTHBONETOOTHHORNPEARLSHELLLEATHERSILKAMBERCORAL GREEN_GLASS CLEAR_GLASS CRYSTAL_GLASSYARNWAXINORGANIC:IRONINORGANIC:SILVERINORGANIC:COPPERINORGANIC:NICKELINORGANIC:ZINCINORGANIC:BRONZEINORGANIC:BRASSINORGANIC:STEELINORGANIC:PIG_IRONINORGANIC:PLATINUMINORGANIC:ELECTRUM INORGANIC:TININORGANIC:PEWTER_FINEINORGANIC:PEWTER_TRIFLEINORGANIC:PEWTER_LAYINORGANIC:LEADINORGANIC:ALUMINUMINORGANIC:NICKEL_SILVERINORGANIC:BILLONINORGANIC:STERLING_SILVERINORGANIC:BLACK_BRONZEINORGANIC:ROSE_GOLDINORGANIC:BISMUTHINORGANIC:BISMUTH_BRONZEINORGANIC:ADAMANTINEINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:ONYXINORGANIC:MORIONINORGANIC:SCHORLINORGANIC:LACE AGATEINORGANIC:BLUE JADEINORGANIC:LAPIS LAZULIINORGANIC:PRASEINORGANIC:PRASE OPALINORGANIC:BLOODSTONEINORGANIC:MOSS AGATEINORGANIC:MOSS OPALINORGANIC:VARISCITEINORGANIC:CHRYSOPRASEINORGANIC:CHRYSOCOLLAINORGANIC:SARDINORGANIC:CARNELIANINORGANIC:BANDED AGATEINORGANIC:SARDONYXINORGANIC:CHERRY OPALINORGANIC:LAVENDER JADEINORGANIC:PINK JADEINORGANIC:TUBE AGATEINORGANIC:FIRE AGATEINORGANIC:PLUME AGATEINORGANIC:BROWN JASPERINORGANIC:PICTURE JASPERINORGANIC:SMOKY QUARTZINORGANIC:WAX OPALINORGANIC:WOOD OPALINORGANIC:AMBER OPALINORGANIC:GOLD OPALINORGANIC:CITRINEINORGANIC:YELLOW JASPERINORGANIC:TIGEREYEINORGANIC:TIGER IRONINORGANIC:SUNSTONEINORGANIC:RESIN OPALINORGANIC:PYRITEINORGANIC:CLEAR TOURMALINEINORGANIC:GRAY CHALCEDONYINORGANIC:DENDRITIC AGATEINORGANIC:SHELL OPALINORGANIC:BONE OPALINORGANIC:WHITE CHALCEDONYINORGANIC:FORTIFICATION AGATEINORGANIC:MILK QUARTZINORGANIC:MOONSTONEINORGANIC:WHITE JADEINORGANIC:JASPER OPALINORGANIC:PINEAPPLE OPALINORGANIC:ONYX OPALINORGANIC:MILK OPALINORGANIC:PIPE OPALINORGANIC:AVENTURINEINORGANIC:TURQUOISEINORGANIC:QUARTZ_ROSEINORGANIC:CRYSTAL_ROCKINORGANIC:BLACK ZIRCONINORGANIC:BLACK PYROPEINORGANIC:MELANITEINORGANIC:INDIGO TOURMALINEINORGANIC:BLUE GARNETINORGANIC:TSAVORITEINORGANIC:GREEN TOURMALINEINORGANIC:DEMANTOIDINORGANIC:GREEN ZIRCONINORGANIC:GREEN JADEINORGANIC:HELIODORINORGANIC:PERIDOTINORGANIC:RED ZIRCONINORGANIC:RED TOURMALINEINORGANIC:RED PYROPEINORGANIC:ALMANDINEINORGANIC:RED GROSSULARINORGANIC:PINK TOURMALINEINORGANIC:RED BERYLINORGANIC:FIRE OPALINORGANIC:RHODOLITEINORGANIC:SPINEL_PURPLEINORGANIC:ALEXANDRITEINORGANIC:TANZANITEINORGANIC:MORGANITEINORGANIC:VIOLET SPESSARTINEINORGANIC:PINK GARNETINORGANIC:KUNZITEINORGANIC:CINNAMON GROSSULARINORGANIC:HONEY YELLOW BERYLINORGANIC:JELLY OPALINORGANIC:BROWN ZIRCONINORGANIC:YELLOW ZIRCONINORGANIC:GOLDEN BERYLINORGANIC:YELLOW SPESSARTINEINORGANIC:TOPAZINORGANIC:TOPAZOLITEINORGANIC:YELLOW GROSSULARINORGANIC:RUBICELLEINORGANIC:CLEAR GARNETINORGANIC:GOSHENITEINORGANIC:CAT'S EYEINORGANIC:CLEAR ZIRCONINORGANIC:AMETHYSTINORGANIC:AQUAMARINEINORGANIC:SPINEL_REDINORGANIC:CHRYSOBERYLINORGANIC:OPAL_PFIREINORGANIC:OPAL_REDFLASHINORGANIC:OPAL_BLACKINORGANIC:OPAL_WHITEINORGANIC:OPAL_CRYSTALINORGANIC:OPAL_CLAROINORGANIC:OPAL_LEVININORGANIC:OPAL_HARLEQUININORGANIC:OPAL_PINFIREINORGANIC:OPAL_BANDFIREINORGANIC:DIAMOND_LYINORGANIC:DIAMOND_FYINORGANIC:EMERALDINORGANIC:RUBYINORGANIC:SAPPHIREINORGANIC:DIAMOND_CLEARINORGANIC:DIAMOND_REDINORGANIC:DIAMOND_GREENINORGANIC:DIAMOND_BLUEINORGANIC:DIAMOND_YELLOWINORGANIC:DIAMOND_BLACKINORGANIC:SAPPHIRE_STARINORGANIC:RUBY_STARINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE INORGANIC:BROMS_CLEAN_CORPSEDUSTINORGANIC:GOLDINORGANIC:DIVINE_1INORGANIC:DIVINE_3INORGANIC:DIVINE_5INORGANIC:DIVINE_7INORGANIC:DIVINE_9INORGANIC:DIVINE_11INORGANIC:DIVINE_13INORGANIC:DIVINE_15INORGANIC:DIVINE_17INORGANIC:DIVINE_19"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file +b +TOY \ No newline at end of file diff --git a/data/stockpiles/woodtools.dfstock b/data/stockpiles/woodtools.dfstock index 12c100005..86eb0d0c8 100644 --- a/data/stockpiles/woodtools.dfstock +++ b/data/stockpiles/woodtools.dfstock @@ -1,2 +1,2 @@ -b° -TOOLWOOD"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file +b +TOOLWOOD \ No newline at end of file From b5d4f04d2ff176836cf8bf3ec0a2edeeca8bf556 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Mar 2023 00:19:49 -0700 Subject: [PATCH 0892/2222] differentiate fish --- docs/plugins/stockpiles.rst | 181 ++++++++++++++------- plugins/stockpiles/StockpileSerializer.cpp | 4 +- 2 files changed, 122 insertions(+), 63 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index c36031ad7..987be3299 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -48,7 +48,7 @@ Examples exist. ``stockpiles import -m enable plants`` Enables plants in the selected stockpile. -``stockpiles import -m disable category_food -f tallow`` +``stockpiles import -m disable cat_food -f tallow`` Disables all tallow in the selected food stockpile. ``stockpiles export mysettings`` Export the settings for the currently selected stockpile to a file named @@ -109,7 +109,7 @@ stockpile configurations can only be achieved with filters since the stockpile lists are different for each world. For example, to disable all tallow in your main food stockpile, you'd run this command:: - stockpiles import category_food -m disable -f tallow + stockpiles import cat_food -m disable -f tallow Top-level categories ~~~~~~~~~~~~~~~~~~~~ @@ -117,27 +117,27 @@ Top-level categories Each stockpile category has a file that allows you to enable or disable the entire category, or with a filter, any matchable subset thereof:: - category_ammo - category_animals - category_armor - category_bars_blocks - category_cloth - category_coins - category_corpses - category_finished_goods - category_food - category_furniture - category_gems - category_leather - category_refuse - category_sheets - category_stone - category_weapons - category_wood - -For many of the categories, there are also subcategory prefixes that you can -match with filters and convenient pre-made settings files that manipulate -interesting category subsets. + cat_ammo + cat_animals + cat_armor + cat_bars_blocks + cat_cloth + cat_coins + cat_corpses + cat_finished_goods + cat_food + cat_furniture + cat_gems + cat_leather + cat_refuse + cat_sheets + cat_stone + cat_weapons + cat_wood + +For many of the categories, there are also flags and subcategory prefixes that +you can match with filters and convenient pre-made settings files that +manipulate interesting category subsets. Ammo stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -150,7 +150,7 @@ Subcategory prefixes:: core/ total/ -Convenience settings files:: +Settings files:: bolts metalammo @@ -159,59 +159,118 @@ Convenience settings files:: Example commands for a stockpile of metal bolts:: - stockpiles import category_ammo - stockpiles import -m disable -f other/ category_ammo - stockpiles import -m disable -f type/ category_ammo + stockpiles import cat_ammo -f mats/,core/,total/ stockpiles import -m enable bolts Animal stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -traps -cages +Flags:: + + cages + traps + +Settings files:: + + cages + traps + +Example commands for a stockpile of empty cages:: + + stockpiles import cages + +Or, using the flag for the same effect:: + + stockpiles import cat_animals -f cages Armor stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -metalarmor -otherarmor -ironarmor -bronzearmor -copperarmor -steelarmor -masterworkarmor -artifactarmor -usablearmor -unusablearmor +Flags and subcategory prefixes:: + + nouse + canuse + body/ + head/ + feet/ + hands/ + legs/ + shield/ + mats/ + other/ + core/ + total/ + +Settings files:: + metalarmor + otherarmor + ironarmor + bronzearmor + copperarmor + steelarmor + usablearmor + unusablearmor + +Example commands for a stockpile of sub-masterwork meltable armor:: + + stockpiles import cat_armor + stockpiles import -m disable -f other/,core/mas,core/art cat_armor Bar stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~ -bars -metalbars -ironbars -pigironbars -steelbars -otherbars -coal -potash -ash -pearlash -soap -blocks +Subcategory prefixes:: + + mats/bars/ + other/bars/ + mats/blocks/ + other/blocks/ + +Settings files:: + + bars + metalbars + ironbars + pigironbars + steelbars + otherbars + coal + potash + ash + pearlash + soap + blocks + +Example commands for a stockpile of blocks:: + + stockpiles import blocks Cloth stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -thread -adamantinethread -cloth -adamantinecloth +Subcategory prefixes:: + + thread/silk/ + thread/plant/ + thread/yarn/ + thread/metal/ + cloth/silk/ + cloth/plant/ + cloth/yarn/ + cloth/metal/ + +Settings files:: + + thread + adamantinethread + cloth + adamantinecloth Notes: -* ``thread`` and ``cloth`` refers to all materials that are not adamantine. +* ``thread`` and ``cloth`` settings files set all materials that are not +adamantine. Finished goods stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -287,11 +346,11 @@ usablehair You can get a stockpile of usable refuse with the following set of commands:: - stockpiles import category_refuse -m enable -f skulls - stockpiles import category_refuse -m enable -f bones - stockpiles import category_refuse -m enable -f shells - stockpiles import category_refuse -m enable -f teeth - stockpiles import category_refuse -m enable -f horns + stockpiles import cat_refuse -m enable -f skulls + stockpiles import cat_refuse -m enable -f bones + stockpiles import cat_refuse -m enable -f shells + stockpiles import cat_refuse -m enable -f teeth + stockpiles import cat_refuse -m enable -f horns stockpiles import usablehair -m enable Stone stockpile adjustments diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index ca51c0b3e..dd418c840 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1457,7 +1457,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_fish(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().fish(idx); }; - return food_pair("fish", setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); + return food_pair("fish/prepared", setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { @@ -1465,7 +1465,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_unprepared_fish(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().unprepared_fish(idx); }; - return food_pair("unpreparedfish", setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); + return food_pair("fish/unprepared", setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { From f6ebe36012a3ceb364d0105f98c2f90421e3ada5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Mar 2023 00:46:06 -0700 Subject: [PATCH 0893/2222] final stockpile library update --- data/stockpiles/bags.dfstock | 4 ++-- data/stockpiles/buckets.dfstock | 4 ++-- data/stockpiles/pots.dfstock | 4 ++-- data/stockpiles/sand.dfstock | 5 +++-- data/stockpiles/weapons.dfstock | Bin 709 -> 0 bytes 5 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 data/stockpiles/weapons.dfstock diff --git a/data/stockpiles/bags.dfstock b/data/stockpiles/bags.dfstock index 6f95e7638..3bfa1a1a1 100644 --- a/data/stockpiles/bags.dfstock +++ b/data/stockpiles/bags.dfstock @@ -1,2 +1,2 @@ -Ë -BAG PLANT_CLOTHLEATHERSILKYARN"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file + +BAG \ No newline at end of file diff --git a/data/stockpiles/buckets.dfstock b/data/stockpiles/buckets.dfstock index 656e21309..cb6b8c4bb 100644 --- a/data/stockpiles/buckets.dfstock +++ b/data/stockpiles/buckets.dfstock @@ -1,2 +1,2 @@ -à -BUCKETWOOD PLANT_CLOTHBONETOOTHHORNPEARLSHELLLEATHERSILKAMBERCORAL GREEN_GLASS CLEAR_GLASS CRYSTAL_GLASSYARNINORGANIC:IRONINORGANIC:SILVERINORGANIC:COPPERINORGANIC:NICKELINORGANIC:ZINCINORGANIC:BRONZEINORGANIC:BRASSINORGANIC:STEELINORGANIC:PIG_IRONINORGANIC:PLATINUMINORGANIC:ELECTRUM INORGANIC:TININORGANIC:PEWTER_FINEINORGANIC:PEWTER_TRIFLEINORGANIC:PEWTER_LAYINORGANIC:LEADINORGANIC:ALUMINUMINORGANIC:NICKEL_SILVERINORGANIC:BILLONINORGANIC:STERLING_SILVERINORGANIC:BLACK_BRONZEINORGANIC:ROSE_GOLDINORGANIC:BISMUTHINORGANIC:BISMUTH_BRONZEINORGANIC:ADAMANTINEINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE INORGANIC:BROMS_CLEAN_CORPSEDUSTINORGANIC:GOLDINORGANIC:DIVINE_1INORGANIC:DIVINE_3INORGANIC:DIVINE_5INORGANIC:DIVINE_7INORGANIC:DIVINE_9INORGANIC:DIVINE_11INORGANIC:DIVINE_13INORGANIC:DIVINE_15INORGANIC:DIVINE_17INORGANIC:DIVINE_19"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file + +BUCKET \ No newline at end of file diff --git a/data/stockpiles/pots.dfstock b/data/stockpiles/pots.dfstock index 016b086b3..2c1fe0e5c 100644 --- a/data/stockpiles/pots.dfstock +++ b/data/stockpiles/pots.dfstock @@ -1,2 +1,2 @@ -æ - FOOD_STORAGEWOOD PLANT_CLOTHBONETOOTHHORNPEARLSHELLLEATHERSILKAMBERCORAL GREEN_GLASS CLEAR_GLASS CRYSTAL_GLASSYARNINORGANIC:IRONINORGANIC:SILVERINORGANIC:COPPERINORGANIC:NICKELINORGANIC:ZINCINORGANIC:BRONZEINORGANIC:BRASSINORGANIC:STEELINORGANIC:PIG_IRONINORGANIC:PLATINUMINORGANIC:ELECTRUM INORGANIC:TININORGANIC:PEWTER_FINEINORGANIC:PEWTER_TRIFLEINORGANIC:PEWTER_LAYINORGANIC:LEADINORGANIC:ALUMINUMINORGANIC:NICKEL_SILVERINORGANIC:BILLONINORGANIC:STERLING_SILVERINORGANIC:BLACK_BRONZEINORGANIC:ROSE_GOLDINORGANIC:BISMUTHINORGANIC:BISMUTH_BRONZEINORGANIC:ADAMANTINEINORGANIC:PLASTERINORGANIC:CERAMIC_EARTHENWAREINORGANIC:CERAMIC_STONEWAREINORGANIC:CERAMIC_PORCELAININORGANIC:ASH_GLAZEINORGANIC:TIN_GLAZEINORGANIC:SANDSTONEINORGANIC:SILTSTONEINORGANIC:MUDSTONEINORGANIC:SHALEINORGANIC:CLAYSTONEINORGANIC:ROCK_SALTINORGANIC:LIMESTONEINORGANIC:CONGLOMERATEINORGANIC:DOLOMITEINORGANIC:CHERTINORGANIC:CHALKINORGANIC:GRANITEINORGANIC:DIORITEINORGANIC:GABBROINORGANIC:RHYOLITEINORGANIC:BASALTINORGANIC:ANDESITEINORGANIC:DACITEINORGANIC:OBSIDIANINORGANIC:QUARTZITEINORGANIC:SLATEINORGANIC:PHYLLITEINORGANIC:SCHISTINORGANIC:GNEISSINORGANIC:MARBLEINORGANIC:HEMATITEINORGANIC:LIMONITEINORGANIC:GARNIERITEINORGANIC:NATIVE_GOLDINORGANIC:NATIVE_SILVERINORGANIC:NATIVE_COPPERINORGANIC:MALACHITEINORGANIC:GALENAINORGANIC:SPHALERITEINORGANIC:CASSITERITEINORGANIC:COAL_BITUMINOUSINORGANIC:LIGNITEINORGANIC:NATIVE_PLATINUMINORGANIC:CINNABARINORGANIC:COBALTITEINORGANIC:TETRAHEDRITEINORGANIC:HORN_SILVERINORGANIC:GYPSUMINORGANIC:TALC INORGANIC:JETINORGANIC:PUDDINGSTONEINORGANIC:PETRIFIED_WOODINORGANIC:GRAPHITEINORGANIC:BRIMSTONEINORGANIC:KIMBERLITEINORGANIC:BISMUTHINITEINORGANIC:REALGARINORGANIC:ORPIMENTINORGANIC:STIBNITEINORGANIC:MARCASITEINORGANIC:SYLVITEINORGANIC:CRYOLITEINORGANIC:PERICLASEINORGANIC:ILMENITEINORGANIC:RUTILEINORGANIC:MAGNETITEINORGANIC:CHROMITEINORGANIC:PYROLUSITEINORGANIC:PITCHBLENDEINORGANIC:BAUXITEINORGANIC:NATIVE_ALUMINUMINORGANIC:BORAXINORGANIC:OLIVINEINORGANIC:HORNBLENDEINORGANIC:KAOLINITEINORGANIC:SERPENTINEINORGANIC:ORTHOCLASEINORGANIC:MICROCLINEINORGANIC:MICAINORGANIC:CALCITEINORGANIC:SALTPETERINORGANIC:ALABASTERINORGANIC:SELENITEINORGANIC:SATINSPARINORGANIC:ANHYDRITEINORGANIC:ALUNITEINORGANIC:RAW_ADAMANTINEINORGANIC:SLADE INORGANIC:BROMS_CLEAN_CORPSEDUSTINORGANIC:GOLDINORGANIC:DIVINE_1INORGANIC:DIVINE_3INORGANIC:DIVINE_5INORGANIC:DIVINE_7INORGANIC:DIVINE_9INORGANIC:DIVINE_11INORGANIC:DIVINE_13INORGANIC:DIVINE_15INORGANIC:DIVINE_17INORGANIC:DIVINE_19"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file + + FOOD_STORAGE \ No newline at end of file diff --git a/data/stockpiles/sand.dfstock b/data/stockpiles/sand.dfstock index 87b2fe9ce..b97921e81 100644 --- a/data/stockpiles/sand.dfstock +++ b/data/stockpiles/sand.dfstock @@ -1,2 +1,3 @@ -Ð -SAND_BAG PLANT_CLOTHLEATHERSILKYARN"Ordinary" WellCrafted" FinelyCrafted"Superior" Exceptional" Masterful"Artifact*Ordinary* WellCrafted* FinelyCrafted*Superior* Exceptional* Masterful*Artifact \ No newline at end of file + + +SAND_BAG \ No newline at end of file diff --git a/data/stockpiles/weapons.dfstock b/data/stockpiles/weapons.dfstock deleted file mode 100644 index e4968f56d4a16f14d4fc2edfca5a24651d83c615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 709 zcmZ{iJx&BM42741#s>hz@*^sGDnzxOj7g%+&&bIrbh!&R<7P}W5>Pz4D9_LNoafJr zuS>da!b?tXzDn%-xi{w*x(5P(NT0aYAW(K3qiAoe^Z?wYWu1F*Su5QjeUZ(HfFc{A zBd{3FrA)a&*W%5Yu7GP8Zqa?n&93R6W@mvW$6*qkA>Kami-W(liyz Date: Tue, 21 Mar 2023 00:46:21 -0700 Subject: [PATCH 0894/2222] finalize docs, clear flags --- docs/plugins/stockpiles.rst | 241 ++++++++++++++------- plugins/stockpiles/StockpileSerializer.cpp | 4 + 2 files changed, 170 insertions(+), 75 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 987be3299..6b87d7a96 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -275,113 +275,204 @@ adamantine. Finished goods stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -stonetools -woodtools -crafts -goblets -toys -masterworkfinishedgoods -artifactfinishedgoods +Subcategory prefixes:: + + type/ + mats/ + other/ + core/ + total/ + +Settings files:: + + stonetools + woodtools + crafts + goblets + toys + +Example commands for a toy stockpile:: + + stockpiles import cat_furniture -f mats/,other/,core/,total/ + stockpiles import -m enable toys Food stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~ -preparedmeals -unpreparedfish -plants -booze -seeds -dye -miscliquid -wax +Flags and subcategory prefixes:: + + preparedmeals + meat/ + fish/prepared/ + fish/unprepared/ + egg/ + plants/ + drink/plant/ + drink/animal/ + cheese/plant/ + cheese/animal/ + seeds/ + leaves/ + powder/plant/ + powder/animal/ + glob/ + liquid/plant/ + liquid/animal/ + liquid/misc/ + paste/ + pressed/ + +Settings files:: + + preparedmeals + unpreparedfish + plants + booze + seeds + dye + miscliquid + wax + +Example commands for a kitchen ingredients stockpile:: + + stockpiles import cat_food -f meat/,fish/prepared/,egg/,cheese/,leaves/,powder/,glob/,liquid/plant/,paste/,pressed/ + stockpiles import cat_food -m enable -f milk,royal_jelly + stockpiles import dye -m disable + stockpiles import cat_food -m disable -f tallow,thread,liquid/misc/ Furniture stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -pots -bags -buckets -sand -masterworkfurniture -artifactfurniture +Subcategory prefixes:: + + type/ + mats/ + other/ + core/ + total/ + +Settings files:: + + pots + bags + buckets + sand * Because of the limitations of Dwarf Fortress, ``bags`` cannot distinguish between empty bags and bags filled with gypsum powder. +Example commands for a sand bag stockpile:: + + stockpiles import cat_furniture + stockpiles import cat_furniture -m disable -f type/ + stockpiles import sand -m enable + Gem stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~ -roughgems -roughglass -cutgems -cutglass -cutstone +Subcategory prefixes:: + + mats/rough/ + mats/cut/ + other/rough/ + other/cut/ + +Settings files:: + + roughgems + roughglass + cutgems + cutglass + cutstone Refuse stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -=========== ================== ================== -Exclusive Forbid Permit -=========== ================== ================== -corpses forbidcorpses permitcorpses -skulls forbidskulls permitskulls -bones forbidbones permitbones -shells forbidshells permitshells -teeth forbidteeth permitteeth -horns forbidhorns permithorns -hair forbidhair permithair -craftrefuse forbidcraftrefuse permitcraftrefuse -=========== ================== ================== +Flags and subcategory prefixes:: + + rawhide/fresh + rawhide/rotten + type/ + corpses/ + bodyparts/ + skulls/ + bones/ + hair/ + shells/ + teeth/ + horns/ + +Settings files:: + + rawhides + tannedhides + usablehair Notes: * ``usablehair`` Only hair and wool that can make usable clothing is included, i.e. from sheep, llamas, alpacas, and trolls. -* ``craftrefuse`` includes everything a craftsdwarf or tailor can use: skulls, - bones, shells, teeth, horns, and "usable" hair/wool (defined above). - -rawhides -tannedhides -usablehair -You can get a stockpile of usable refuse with the following set of commands:: +Example commands for a craftable refuse stockpile:: - stockpiles import cat_refuse -m enable -f skulls - stockpiles import cat_refuse -m enable -f bones - stockpiles import cat_refuse -m enable -f shells - stockpiles import cat_refuse -m enable -f teeth - stockpiles import cat_refuse -m enable -f horns + stockpiles import cat_refuse -f skulls/,bones/,shells',teeth/,horns/ stockpiles import usablehair -m enable +Sheet stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Subcategory prefixes:: + + paper/ + parchment/ + Stone stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -metalore -ironore -economic -flux -plasterproducing -coalproducing -otherstone -bauxite -clay +Settings files:: + + metalore + ironore + economic + flux + plasterproducing + coalproducing + otherstone + bauxite + clay Weapon stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -weapons -metalweapons -stoneweapons -otherweapons -trapcomponents -ironweapons -silverweapons -bronzeweapons -copperweapons -steelweapons -platinumweapons -adamantineweapons -masterworkweapons -artifactweapons -usableweapons -unusableweapons +Flags and subcategory prefixes:: + + nouse + canuse + type/weapon/ + type/trapcomp/ + mats/ + other/ + core/ + total/ + +Settings files:: + + metalweapons + stoneweapons + otherweapons + trapcomponents + ironweapons + silverweapons + bronzeweapons + copperweapons + steelweapons + platinumweapons + adamantineweapons + usableweapons + unusableweapons + +Example commands for a non-metallic trap components stockpile:: + + stockpiles import cat_weapons + stockpiles import cat_weapons -m disable -f type/weapon/ + stockpiles metalweapons -m disable diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index dd418c840..f6a63ff4f 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1090,6 +1090,8 @@ void StockpileSerializer::read_armor(DeserializeMode mode, const vector& mPile->settings.flags.whole, mPile->settings.flags.mask_armor, [&]() { + parmor.unusable = false; + parmor.usable = false; parmor.body.clear(); parmor.head.clear(); parmor.feet.clear(); @@ -2158,6 +2160,8 @@ void StockpileSerializer::read_weapons(DeserializeMode mode, const vectorsettings.flags.whole, mPile->settings.flags.mask_weapons, [&]() { + pweapons.unusable = false; + pweapons.usable = false; pweapons.weapon_type.clear(); pweapons.trapcomp_type.clear(); pweapons.other_mats.clear(); From 22872f61c408d0763b98fb09e4ded0c35bafbb52 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 21 Mar 2023 02:13:23 -0700 Subject: [PATCH 0895/2222] fix indent --- docs/plugins/stockpiles.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 6b87d7a96..8093b6c1d 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -270,7 +270,7 @@ Settings files:: Notes: * ``thread`` and ``cloth`` settings files set all materials that are not -adamantine. + adamantine. Finished goods stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 9154a52d0a869a065cf2c7ec3183b56d5b7a5962 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 22 Mar 2023 23:58:02 -0700 Subject: [PATCH 0896/2222] remove spaces and dashes from allowed filename chars since this will likely make it more difficult to represent stockpile filenames in quickfort's stockpile configuration language --- docs/plugins/stockpiles.rst | 3 +-- plugins/lua/stockpiles.lua | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 8093b6c1d..560ae2497 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -20,8 +20,7 @@ Usage Exported stockpile settings are saved in the ``dfhack-config/stockpiles`` folder, where you can view and delete them, if desired. Names can only -contain numbers, letters, periods, underscores, dashes, and spaces. If -the name has spaces, be sure to surround it with double quotes (:kbd:`"`). +contain numbers, letters, periods, and underscores. The names of library settings files are all prefixed by the string ``library/``. You can specify library files explicitly by including the prefix, or you can diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index f9c7d7b4a..6134f0a1b 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -62,8 +62,8 @@ local function assert_safe_name(name) if not name or #name == 0 then qerror('name missing or empty') end - if name:find('[^%a ._-]') then - qerror('name can only contain numbers, letters, periods, underscores, dashes, and spaces') + if name:find('[^%a._]') then + qerror('name can only contain numbers, letters, periods, and underscores') end end From 05f407e10bedde8102eac52f235d55eecb9d70d7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 02:36:20 -0700 Subject: [PATCH 0897/2222] update changelog --- docs/changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2b88261f8..c7baf4df8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: upright spike traps are now placed extended rather than retracted ## Misc Improvements +- `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from +- `stockpiles`: support partial application of a saved config based on dynamic filtering +- `stockpiles`: additive and subtractive modes when applying a second stockpile configuration on top of a first +- `stockpiles`: write player-exported stockpile configurations to the ``dfhack-config/stockpiles`` folder. If you have any stockpile configs in other directories, please move them to that folder. +- `stockpiles`: now includes a library of useful stockpile configs (see docs for details) - `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) ## Documentation From 873e94ea5df894780f67e64b5f7f161ba3d7e5af Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 10:48:33 -0700 Subject: [PATCH 0898/2222] actually record the deserialized material mask val --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 4 ++-- plugins/buildingplan/itemfilter.cpp | 14 ++++++-------- plugins/buildingplan/itemfilter.h | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 142b10e83..976490f97 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `tailor`: now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine - `buildingplan`: upright spike traps are now placed extended rather than retracted +- `buildingplan`: fixed material filter getting lost for planning buildings on save/reload ## Misc Improvements - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index b472c7390..1e2d2c80e 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -130,7 +130,7 @@ static const vector & get_job_items(color_ostream &out, Bu }, [&](lua_State *L) { df::job_item *jitem = Lua::GetDFObject(L, -1); - DEBUG(status,out).print("retrieving job_item for (%d, %d, %d) index=%d: %p\n", + DEBUG(status,out).print("retrieving job_item for (%d, %d, %d) index=%d: 0x%p\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), index, jitem); if (!jitem) failed = true; @@ -311,7 +311,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { PlannedBuilding pb(out, building_configs[idx]); df::building *bld = df::building::find(pb.id); if (!bld) { - INFO(status,out).print("building %d no longer exists; skipping\n", pb.id); + DEBUG(status,out).print("building %d no longer exists; skipping\n", pb.id); pb.remove(out); continue; } diff --git a/plugins/buildingplan/itemfilter.cpp b/plugins/buildingplan/itemfilter.cpp index 8e66c3ed7..dac5f98d6 100644 --- a/plugins/buildingplan/itemfilter.cpp +++ b/plugins/buildingplan/itemfilter.cpp @@ -35,7 +35,7 @@ bool ItemFilter::isEmpty() const { && materials.empty(); } -static bool deserializeMaterialMask(string ser, df::dfhack_material_category mat_mask) { +static bool deserializeMaterialMask(const string& ser, df::dfhack_material_category& mat_mask) { if (ser.empty()) return true; @@ -46,7 +46,7 @@ static bool deserializeMaterialMask(string ser, df::dfhack_material_category mat return true; } -static bool deserializeMaterials(string ser, set &materials) { +static bool deserializeMaterials(const string& ser, set &materials) { if (ser.empty()) return true; @@ -63,7 +63,7 @@ static bool deserializeMaterials(string ser, set &material return true; } -ItemFilter::ItemFilter(color_ostream &out, string serialized) : ItemFilter() { +ItemFilter::ItemFilter(color_ostream &out, const string& serialized) : ItemFilter() { vector tokens; split_string(&tokens, serialized, "/"); if (tokens.size() < 5) { @@ -87,11 +87,9 @@ string ItemFilter::serialize() const { std::ostringstream ser; ser << bitfield_to_string(mat_mask, ",") << "/"; vector matstrs; - if (!materials.empty()) { - for (auto &mat : materials) - matstrs.emplace_back(mat.getToken()); - ser << join_strings(",", matstrs); - } + for (auto &mat : materials) + matstrs.emplace_back(mat.getToken()); + ser << join_strings(",", matstrs); ser << "/" << static_cast(min_quality); ser << "/" << static_cast(max_quality); ser << "/" << static_cast(decorated_only); diff --git a/plugins/buildingplan/itemfilter.h b/plugins/buildingplan/itemfilter.h index 5ae59dd4a..c74150434 100644 --- a/plugins/buildingplan/itemfilter.h +++ b/plugins/buildingplan/itemfilter.h @@ -8,7 +8,7 @@ class ItemFilter { public: ItemFilter(); - ItemFilter(DFHack::color_ostream &out, std::string serialized); + ItemFilter(DFHack::color_ostream &out, const std::string& serialized); void clear(); bool isEmpty() const; From 95f6e6e5beab4ad2367b561dbd080dbafc22e8d4 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Thu, 23 Mar 2023 16:21:14 -0500 Subject: [PATCH 0899/2222] tailor: fix material overrun issue properly track materials required for already-queued jobs fixes #3056 --- docs/changelog.txt | 2 +- library/xml | 2 +- plugins/tailor.cpp | 31 ++++++++++++++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 142b10e83..ca5b87255 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,8 +37,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. ## Fixes -- `tailor`: now properly discriminates between dyed and undyed cloth and no longer defaults to using adamantine - `buildingplan`: upright spike traps are now placed extended rather than retracted +- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, and properly tracks material requirements for already queued orders ## Misc Improvements - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from diff --git a/library/xml b/library/xml index 8b8ac2de0..0dc8ae877 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8b8ac2de03ab5f8e5506864a751933059abfd03e +Subproject commit 0dc8ae87746e287538be01f01dc628662e756794 diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 66e54dd60..2c5a1a027 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -383,6 +383,17 @@ public: auto sub = o->item_subtype; int race = o->hist_figure_id; + + for (auto& m : all_materials) + { + if (o->material_category.whole == m.job_material.whole) + { + supply[m] -= o->amount_left; + TRACE(cycle).print("tailor: supply of %s reduced by %d due to being required for an existing order\n", + m.name.c_str(), o->amount_left); + } + } + if (race == -1) continue; // -1 means that the race of the worker will determine the size made; we must ignore these jobs @@ -525,6 +536,18 @@ public: } return ordered; } + + int do_cycle() + { + reset(); + scan_clothing(); + scan_materials(); + scan_replacements(); + create_orders(); + scan_existing_orders(); + return place_orders(); + } + }; static std::unique_ptr tailor_instance; @@ -684,13 +707,7 @@ static int do_cycle(color_ostream &out) { DEBUG(cycle,out).print("running %s cycle\n", plugin_name); - tailor_instance->reset(); - tailor_instance->scan_clothing(); - tailor_instance->scan_materials(); - tailor_instance->scan_replacements(); - tailor_instance->create_orders(); - tailor_instance->scan_existing_orders(); - return tailor_instance->place_orders(); + return tailor_instance->do_cycle(); } ///////////////////////////////////////////////////// From 013c6fe02a376fa1a2b4d6823879a8171a2f5ee1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 23 Mar 2023 11:07:10 -0700 Subject: [PATCH 0900/2222] don't render footprint if we're minmized --- plugins/lua/buildingplan/planneroverlay.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index a60b2ebcd..ec05e90f6 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -694,6 +694,8 @@ function PlannerOverlay:onRenderFrame(dc, rect) uibs.building_type, uibs.building_subtype, uibs.custom_type)) end + if self.minimized then return end + local selection_pos = self.saved_selection_pos or uibs.selection_pos if not selection_pos or selection_pos.x < 0 then return end From 0f8478df17596ffb07589c0500592827e509f8be Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 24 Mar 2023 07:13:34 +0000 Subject: [PATCH 0901/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 0dc8ae877..8b8ac2de0 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0dc8ae87746e287538be01f01dc628662e756794 +Subproject commit 8b8ac2de03ab5f8e5506864a751933059abfd03e diff --git a/scripts b/scripts index 7f388c0c1..0b37dcf99 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7f388c0c1bf9ed5fa3f7abf1153da54921a93e15 +Subproject commit 0b37dcf996bf4b64423d2acb3e3ca224ad76c6bc From 99d050d0a85f6120f8bed2fa1775dc479b7f852c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 14:06:39 -0700 Subject: [PATCH 0902/2222] respect building size limits --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 135 +++++++++++++------- 2 files changed, 92 insertions(+), 44 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2a5b2cf15..88c6eac3b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `buildingplan`: upright spike traps are now placed extended rather than retracted - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload +- `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) - `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, and properly tracks material requirements for already queued orders ## Misc Improvements diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index ec05e90f6..e92824f5f 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -23,13 +23,68 @@ local function is_choosing_area() return uibs.selection_pos.x >= 0 end -local function get_cur_area_dims(placement_data) - if not placement_data and not is_choosing_area() then return 1, 1, 1 end - local selection_pos = placement_data and placement_data.p1 or uibs.selection_pos - local pos = placement_data and placement_data.p2 or uibs.pos - return math.abs(selection_pos.x - pos.x) + 1, - math.abs(selection_pos.y - pos.y) + 1, - math.abs(selection_pos.z - pos.z) + 1 +-- TODO: reuse data in quickfort database +local function get_selection_size_limits() + local btype = uibs.building_type + if btype == df.building_type.Bridge + or btype == df.building_type.FarmPlot + or btype == df.building_type.RoadPaved + or btype == df.building_type.RoadDirt then + return {w=31, h=31} + elseif btype == df.building_type.AxleHorizontal + or btype == df.building_type.Rollers then + return uibs.direction == 1 and {w=1, h=31} or {w=31, h=1} + end +end + +local function get_selected_bounds(selection_pos, pos) + selection_pos = selection_pos or uibs.selection_pos + if not is_choosing_area() then return end + + pos = pos or uibs.pos + + local bounds = { + x1=math.min(selection_pos.x, pos.x), + x2=math.max(selection_pos.x, pos.x), + y1=math.min(selection_pos.y, pos.y), + y2=math.max(selection_pos.y, pos.y), + z1=math.min(selection_pos.z, pos.z), + z2=math.max(selection_pos.z, pos.z), + } + + -- clamp to map edges + bounds = { + x1=math.max(0, bounds.x1), + x2=math.min(df.global.world.map.x_count-1, bounds.x2), + y1=math.max(0, bounds.y1), + y2=math.min(df.global.world.map.y_count-1, bounds.y2), + z1=math.max(0, bounds.z1), + z2=math.min(df.global.world.map.z_count-1, bounds.z2), + } + + local limits = get_selection_size_limits() + if limits then + -- clamp to building type area limit + bounds = { + x1=math.max(selection_pos.x - (limits.w-1), bounds.x1), + x2=math.min(selection_pos.x + (limits.w-1), bounds.x2), + y1=math.max(selection_pos.y - (limits.h-1), bounds.y1), + y2=math.min(selection_pos.y + (limits.h-1), bounds.y2), + z1=bounds.z1, + z2=bounds.z2, + } + end + + return bounds +end + +local function get_cur_area_dims(bounds) + if not bounds and not is_choosing_area() then return 1, 1, 1 end + bounds = bounds or get_selected_bounds() + if not bounds then return 1, 1, 1 end + return bounds.x2 - bounds.x1 + 1, + bounds.y2 - bounds.y1 + 1, + bounds.z2 - bounds.z1 + 1 end local function is_pressure_plate() @@ -53,7 +108,7 @@ end -- adjusted from CycleHotkeyLabel on the planner panel local weapon_quantity = 1 -local function get_quantity(filter, hollow, placement_data) +local function get_quantity(filter, hollow, bounds) if is_pressure_plate() then local flags = uibs.plate_info.flags return (flags.units and 1 or 0) + (flags.water and 1 or 0) + @@ -62,7 +117,7 @@ local function get_quantity(filter, hollow, placement_data) return weapon_quantity end local quantity = filter.quantity or 1 - local dimx, dimy, dimz = get_cur_area_dims(placement_data) + local dimx, dimy, dimz = get_cur_area_dims(bounds) if quantity < 1 then return (((dimx * dimy) // 4) + 1) * dimz end @@ -519,19 +574,18 @@ function PlannerOverlay:clear_filter(idx) end local function get_placement_data() - local pos = uibs.pos local direction = uibs.direction - local width, height, depth = get_cur_area_dims() + local bounds = get_selected_bounds() + local width, height, depth = get_cur_area_dims(bounds) local _, adjusted_width, adjusted_height = dfhack.buildings.getCorrectSize( width, height, uibs.building_type, uibs.building_subtype, uibs.custom_type, direction) -- get the upper-left corner of the building/area at min z-level - local has_selection = is_choosing_area() - local start_pos = xyz2pos( - has_selection and math.min(uibs.selection_pos.x, pos.x) or pos.x - adjusted_width//2, - has_selection and math.min(uibs.selection_pos.y, pos.y) or pos.y - adjusted_height//2, - has_selection and math.min(uibs.selection_pos.z, pos.z) or pos.z - ) + local start_pos = bounds and xyz2pos(bounds.x1, bounds.y1, bounds.z1) or + xyz2pos( + uibs.pos.x - adjusted_width//2, + uibs.pos.y - adjusted_height//2, + uibs.pos.z) if uibs.building_type == df.building_type.ScrewPump then if direction == df.screw_pump_direction.FromSouth then start_pos.y = start_pos.y + 1 @@ -546,11 +600,11 @@ local function get_placement_data() and (width > 1 or height > 1 or depth > 1) then max_x = min_x + width - 1 max_y = min_y + height - 1 - max_z = math.max(uibs.selection_pos.z, pos.z) + max_z = math.max(uibs.selection_pos.z, uibs.pos.z) end return { - p1=xyz2pos(min_x, min_y, min_z), - p2=xyz2pos(max_x, max_y, max_z), + x1=min_x, y1=min_y, z1=min_z, + x2=max_x, y2=max_y, z2=max_z, width=adjusted_width, height=adjusted_height } @@ -564,10 +618,11 @@ function PlannerOverlay:save_placement() self.saved_pos = copyall(uibs.pos) uibs.selection_pos:clear() else - self.saved_selection_pos = copyall(self.saved_placement.p1) - self.saved_pos = copyall(self.saved_placement.p2) - self.saved_pos.x = self.saved_pos.x + self.saved_placement.width - 1 - self.saved_pos.y = self.saved_pos.y + self.saved_placement.height - 1 + local sp = self.saved_placement + self.saved_selection_pos = xyz2pos(sp.x1, sp.y1, sp.z1) + self.saved_pos = xyz2pos(sp.x2, sp.y2, sp.z2) + self.saved_pos.x = self.saved_pos.x + sp.width - 1 + self.saved_pos.y = self.saved_pos.y + sp.height - 1 end end @@ -624,6 +679,7 @@ function PlannerOverlay:onInput(keys) local num_filters = #filters local choose = self.subviews.choose if choose.enabled() and choose:getOptionValue() then + local bounds = get_selected_bounds() self:save_placement() local is_hollow = self.subviews.hollow:getOptionValue() local chosen_items, active_screens = {}, {} @@ -636,8 +692,7 @@ function PlannerOverlay:onInput(keys) active_screens[idx] = itemselection.ItemSelectionScreen{ index=idx, desc=require('plugins.buildingplan').get_desc(filter), - quantity=get_quantity(filter, is_hollow, - self.saved_placement), + quantity=get_quantity(filter, is_hollow, bounds), on_submit=function(items) chosen_items[idx] = items active_screens[idx]:dismiss() @@ -696,16 +751,8 @@ function PlannerOverlay:onRenderFrame(dc, rect) if self.minimized then return end - local selection_pos = self.saved_selection_pos or uibs.selection_pos - if not selection_pos or selection_pos.x < 0 then return end - - local pos = self.saved_pos or uibs.pos - local bounds = { - x1 = math.max(0, math.min(selection_pos.x, pos.x)), - x2 = math.min(df.global.world.map.x_count-1, math.max(selection_pos.x, pos.x)), - y1 = math.max(0, math.min(selection_pos.y, pos.y)), - y2 = math.min(df.global.world.map.y_count-1, math.max(selection_pos.y, pos.y)), - } + local bounds = get_selected_bounds(self.saved_selection_pos, self.saved_pos) + if not bounds then return end local hollow = self.subviews.hollow:getOptionValue() local default_pen = (self.saved_selection_pos or #uibs.errors == 0) and pens.GOOD_TILE_PEN or pens.BAD_TILE_PEN @@ -728,9 +775,9 @@ function PlannerOverlay:onRenderFrame(dc, rect) guidm.renderMapOverlay(get_overlay_pen, bounds) end -function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) +function PlannerOverlay:get_stairs_subtype(pos, bounds) local subtype = uibs.building_subtype - if pos.z == corner1.z then + if pos.z == bounds.z1 then local opt = self.subviews.stairs_bottom_subtype:getOptionValue() if opt == 'auto' then local tt = dfhack.maps.getTileType(pos) @@ -741,7 +788,7 @@ function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) else subtype = opt end - elseif pos.z == corner2.z then + elseif pos.z == bounds.z2 then local opt = self.subviews.stairs_top_subtype:getOptionValue() if opt == 'auto' then local tt = dfhack.maps.getTileType(pos) @@ -757,7 +804,7 @@ function PlannerOverlay:get_stairs_subtype(pos, corner1, corner2) end function PlannerOverlay:place_building(placement_data, chosen_items) - local p1, p2 = placement_data.p1, placement_data.p2 + local pd = placement_data local blds = {} local hollow = self.subviews.hollow:getOptionValue() local subtype = uibs.building_subtype @@ -767,17 +814,17 @@ function PlannerOverlay:place_building(placement_data, chosen_items) elseif is_weapon_trap() then filters[2].quantity = get_quantity(filters[2]) end - for z=p1.z,p2.z do for y=p1.y,p2.y do for x=p1.x,p2.x do - if hollow and x ~= p1.x and x ~= p2.x and y ~= p1.y and y ~= p2.y then + for z=pd.z1,pd.z2 do for y=pd.y1,pd.y2 do for x=pd.x1,pd.x2 do + if hollow and x ~= pd.x1 and x ~= pd.x2 and y ~= pd.y1 and y ~= pd.y2 then goto continue end local pos = xyz2pos(x, y, z) if is_stairs() then - subtype = self:get_stairs_subtype(pos, p1, p2) + subtype = self:get_stairs_subtype(pos, pd) end local bld, err = dfhack.buildings.constructBuilding{pos=pos, type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, - width=placement_data.width, height=placement_data.height, + width=pd.width, height=pd.height, direction=uibs.direction, filters=filters} if err then -- it's ok if some buildings fail to build From c8f590cbacefad8121216041ec215a597effc082 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 14:36:06 -0700 Subject: [PATCH 0903/2222] allow player to choose any item when choosing items manually that is, ignore the global and building-specific filters --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 23 +++++---- plugins/buildingplan/buildingplan.h | 2 +- plugins/buildingplan/plannedbuilding.cpp | 2 +- plugins/lua/buildingplan/planneroverlay.lua | 57 +++++++++------------ 5 files changed, 38 insertions(+), 47 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 88c6eac3b..11d849e9b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, and properly tracks material requirements for already queued orders ## Misc Improvements +- `buildingplan`: filters and global settings are now ignored when manually choosing items for a building - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from - `stockpiles`: support partial application of a saved config based on dynamic filtering - `stockpiles`: additive and subtractive modes when applying a second stockpile configuration on top of a first diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 1e2d2c80e..7f43f4a3c 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -424,7 +424,7 @@ static string getBucket(const df::job_item & ji, const PlannedBuilding & pb, int } // get a list of item vectors that we should search for matches -vector getVectorIds(color_ostream &out, const df::job_item *job_item) { +vector getVectorIds(color_ostream &out, const df::job_item *job_item, bool ignore_filters) { std::vector ret; // if the filter already has the vector_id set to something specific, use it @@ -440,13 +440,13 @@ vector getVectorIds(color_ostream &out, const df::job_it // which vectors to search if (job_item->flags2.bits.building_material) { - if (get_config_bool(config, CONFIG_BLOCKS)) + if (ignore_filters || get_config_bool(config, CONFIG_BLOCKS)) ret.push_back(df::job_item_vector_id::BLOCKS); - if (get_config_bool(config, CONFIG_BOULDERS)) + if (ignore_filters || get_config_bool(config, CONFIG_BOULDERS)) ret.push_back(df::job_item_vector_id::BOULDER); - if (get_config_bool(config, CONFIG_LOGS)) + if (ignore_filters || get_config_bool(config, CONFIG_LOGS)) ret.push_back(df::job_item_vector_id::WOOD); - if (get_config_bool(config, CONFIG_BARS)) + if (ignore_filters || get_config_bool(config, CONFIG_BARS)) ret.push_back(df::job_item_vector_id::BAR); } @@ -624,7 +624,7 @@ static void scheduleCycle(color_ostream &out) { } static int scanAvailableItems(color_ostream &out, df::building_type type, int16_t subtype, - int32_t custom, int index, vector *item_ids = NULL, + int32_t custom, int index, bool ignore_filters, vector *item_ids = NULL, map *counts = NULL) { DEBUG(status,out).print( "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", @@ -639,7 +639,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ auto &specials = item_filters.getSpecials(); auto &jitem = job_items[index]; - auto vector_ids = getVectorIds(out, jitem); + auto vector_ids = getVectorIds(out, jitem, ignore_filters); int count = 0; for (auto vector_id : vector_ids) { @@ -651,7 +651,8 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ filter.setMaterialMask(0); filter.setMaterials(set()); } - if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter, specials)) { + if (itemPassesScreen(item) && + (ignore_filters || matchesFilters(item, jitem, heat, filter, specials))) { if (item_ids) item_ids->emplace_back(item->id); if (counts) { @@ -680,7 +681,7 @@ static int getAvailableItems(lua_State *L) { "entering getAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); vector item_ids; - scanAvailableItems(*out, type, subtype, custom, index, &item_ids); + scanAvailableItems(*out, type, subtype, custom, index, true, &item_ids); Lua::PushVector(L, item_ids); return 1; } @@ -703,7 +704,7 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 DEBUG(status,out).print( "entering countAvailableItems building_type=%d subtype=%d custom=%d index=%d\n", type, subtype, custom, index); - return scanAvailableItems(out, type, subtype, custom, index); + return scanAvailableItems(out, type, subtype, custom, index, false); } static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { @@ -871,7 +872,7 @@ static int getMaterialFilter(lua_State *L) { return 0; const auto &mat_filter = filters[index].getMaterials(); map counts; - scanAvailableItems(*out, type, subtype, custom, index, NULL, &counts); + scanAvailableItems(*out, type, subtype, custom, index, false, NULL, &counts); HeatSafety heat = get_heat_safety_filter(key); df::job_item jitem_cur_heat = getJobItemWithHeatSafety( get_job_items(*out, key)[index], heat); diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 495602b0b..756a81f83 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -47,7 +47,7 @@ bool get_config_bool(DFHack::PersistentDataItem &c, int index); void set_config_val(DFHack::PersistentDataItem &c, int index, int value); void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); -std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item); +std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item, bool ignore_filters); bool itemPassesScreen(df::item * item); df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat); bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter, const std::set &special); diff --git a/plugins/buildingplan/plannedbuilding.cpp b/plugins/buildingplan/plannedbuilding.cpp index 2be2cf26f..a20d7b29a 100644 --- a/plugins/buildingplan/plannedbuilding.cpp +++ b/plugins/buildingplan/plannedbuilding.cpp @@ -29,7 +29,7 @@ static vector> get_vector_ids(color_ostream &out, auto &jitems = bld->jobs[0]->job_items; int num_job_items = (int)jitems.size(); for (int jitem_idx = num_job_items - 1; jitem_idx >= 0; --jitem_idx) { - ret.emplace_back(getVectorIds(out, jitems[jitem_idx])); + ret.emplace_back(getVectorIds(out, jitems[jitem_idx], false)); } return ret; } diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index e92824f5f..7db3dc3a3 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -496,13 +496,6 @@ function PlannerOverlay:init() options={{label='Yes', value=true}, {label='No', value=false}}, initial_option=false, - enabled=function() - for idx = 1,4 do - if (self.subviews['item'..idx].available or 0) > 0 then - return true - end - end - end, on_change=function(choose) buildingplan.setChooseItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, choose) end, @@ -678,7 +671,7 @@ function PlannerOverlay:onInput(keys) local filters = get_cur_filters() local num_filters = #filters local choose = self.subviews.choose - if choose.enabled() and choose:getOptionValue() then + if choose:getOptionValue() then local bounds = get_selected_bounds() self:save_placement() local is_hollow = self.subviews.hollow:getOptionValue() @@ -687,33 +680,29 @@ function PlannerOverlay:onInput(keys) df.global.game.main_interface.bottom_mode_selected = -1 for idx = num_filters,1,-1 do chosen_items[idx] = {} - if (self.subviews['item'..idx].available or 0) > 0 then - local filter = filters[idx] - active_screens[idx] = itemselection.ItemSelectionScreen{ - index=idx, - desc=require('plugins.buildingplan').get_desc(filter), - quantity=get_quantity(filter, is_hollow, bounds), - on_submit=function(items) - chosen_items[idx] = items - active_screens[idx]:dismiss() - active_screens[idx] = nil - pending = pending - 1 - if pending == 0 then - df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT - self:place_building(self:restore_placement(), chosen_items) - end - end, - on_cancel=function() - for i,scr in pairs(active_screens) do - scr:dismiss() - end + local filter = filters[idx] + active_screens[idx] = itemselection.ItemSelectionScreen{ + index=idx, + desc=require('plugins.buildingplan').get_desc(filter), + quantity=get_quantity(filter, is_hollow, bounds), + on_submit=function(items) + chosen_items[idx] = items + active_screens[idx]:dismiss() + active_screens[idx] = nil + pending = pending - 1 + if pending == 0 then df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT - self:restore_placement() - end, - }:show() - else - pending = pending - 1 - end + self:place_building(self:restore_placement(), chosen_items) + end + end, + on_cancel=function() + for i,scr in pairs(active_screens) do + scr:dismiss() + end + df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT + self:restore_placement() + end, + }:show() end else self:place_building(get_placement_data()) From fe0590503fa680426afe0d1e66f845e71a2de5b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 15:05:17 -0700 Subject: [PATCH 0904/2222] mark tiles with magma or deep water as "not free" --- docs/changelog.txt | 1 + library/modules/Buildings.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 88c6eac3b..11e552abe 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `buildingplan`: upright spike traps are now placed extended rather than retracted +- `buildingplan`: you can no longer designate constructions on tiles with magma or deep water - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) - `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, and properly tracks material requirements for already queued orders diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 1d1ba9318..de87d3fb8 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -799,10 +799,13 @@ bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, if (!allow_occupied && block->occupancy[btile.x][btile.y].bits.building) allowed = false; - else + else if (!allow_wall) { - auto tile = block->tiletype[btile.x][btile.y]; - if (!allow_wall && !HighPassable(tile)) + auto &tt = block->tiletype[btile.x][btile.y]; + auto &des = block->designation[btile.x][btile.y]; + if (!HighPassable(tt) || + des.bits.flow_size > 1 || + (des.bits.flow_size >= 1 && des.bits.liquid_type == df::tile_liquid::Magma)) allowed = false; } From 58eaf33b085f11bcc32dc940f061e9debb014178 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 15:26:17 -0700 Subject: [PATCH 0905/2222] normalize stairs so they all use the same filter --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 88c6eac3b..c550fbc8a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. ## Fixes +- `buildingplan`: filters are now properly applied to planned stairs - `buildingplan`: upright spike traps are now placed extended rather than retracted - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 1e2d2c80e..fd77194e5 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -9,6 +9,7 @@ #include "modules/World.h" +#include "df/construction_type.h" #include "df/item.h" #include "df/job_item.h" #include "df/world.h" @@ -282,6 +283,18 @@ static void clear_state(color_ostream &out) { call_buildingplan_lua(&out, "reload_pens"); } +static int16_t get_subtype(df::building *bld) { + if (!bld) + return -1; + + int16_t subtype = bld->getSubtype(); + if (bld->getType() == df::building_type::Construction && + subtype >= df::construction_type::UpStair && + subtype <= df::construction_type::UpDownStair) + subtype = df::construction_type::UpDownStair; + return subtype; +} + DFhackCExport command_result plugin_load_data (color_ostream &out) { cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); @@ -315,7 +328,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { pb.remove(out); continue; } - BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); + BuildingTypeKey key(bld->getType(), get_subtype(bld), bld->getCustomType()); if (pb.item_filters.size() != get_item_filters(out, key).getItemFilters().size()) { WARN(status).print("loaded state for building %d doesn't match world\n", pb.id); pb.remove(out); @@ -604,11 +617,15 @@ static bool isPlannedBuilding(color_ostream &out, df::building *bld) { static bool addPlannedBuilding(color_ostream &out, df::building *bld) { DEBUG(status,out).print("entering addPlannedBuilding\n"); - if (!bld || planned_buildings.count(bld->id) - || !isPlannableBuilding(out, bld->getType(), bld->getSubtype(), - bld->getCustomType())) + if (!bld || planned_buildings.count(bld->id)) return false; - BuildingTypeKey key(bld->getType(), bld->getSubtype(), bld->getCustomType()); + + int16_t subtype = get_subtype(bld); + + if (!isPlannableBuilding(out, bld->getType(), subtype, bld->getCustomType())) + return false; + + BuildingTypeKey key(bld->getType(), subtype, bld->getCustomType()); PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key)); return registerPlannedBuilding(out, pb); } From a8822bdefa2268c55affe0e0d5e8fe5fc429e48d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 15:43:02 -0700 Subject: [PATCH 0906/2222] take carved stairs into account when auto building stairs --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c550fbc8a..3e340cf94 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `buildingplan`: filters are now properly applied to planned stairs +- `buildingplan`: existing carved up/down stairs are now taken into account when determining which stair shape to construct - `buildingplan`: upright spike traps are now placed extended rather than retracted - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index e92824f5f..8231a8f46 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -782,7 +782,7 @@ function PlannerOverlay:get_stairs_subtype(pos, bounds) if opt == 'auto' then local tt = dfhack.maps.getTileType(pos) local shape = df.tiletype.attrs[tt].shape - if shape ~= df.tiletype_shape.STAIR_DOWN then + if shape ~= df.tiletype_shape.STAIR_DOWN and shape ~= df.tiletype_shape.STAIR_UPDOWN then subtype = df.construction_type.UpStair end else @@ -793,7 +793,7 @@ function PlannerOverlay:get_stairs_subtype(pos, bounds) if opt == 'auto' then local tt = dfhack.maps.getTileType(pos) local shape = df.tiletype.attrs[tt].shape - if shape ~= df.tiletype_shape.STAIR_UP then + if shape ~= df.tiletype_shape.STAIR_UP and shape ~= df.tiletype_shape.STAIR_UPDOWN then subtype = df.construction_type.DownStair end else From d67d57af3dbac5c71f0e273bfa5827e359091933 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 23:25:14 -0700 Subject: [PATCH 0907/2222] tombstone autounsuspend and add alias --- data/init/dfhack.tools.init | 1 + docs/about/Removed.rst | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 0ca764e8e..aaf0cf277 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -142,4 +142,5 @@ enable \ # Default Aliases # ################### +alias add autounsuspend suspendmanager alias add gui/dig gui/design diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index 4a8cdbccf..a0365e621 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -26,6 +26,12 @@ Moved frequently used materials to the top of the materials list when building buildings. Also offered extended options when building constructions. All functionality has been merged into `buildingplan`. +.. _autounsuspend: + +autounsuspend +============= +Replaced by `suspendmanager`. + .. _combine-drinks: combine-drinks From 2bc92042a5b17266a4c425a14d8c73862949334b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 23:25:24 -0700 Subject: [PATCH 0908/2222] allow enable to interpret aliases --- docs/changelog.txt | 1 + library/Core.cpp | 13 ++++++++----- library/include/Core.h | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 11e552abe..7cb1fafcf 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -51,6 +51,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `stockpiles`: now includes a library of useful stockpile configs (see docs for details) - `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) - `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen +- `enable`: can now interpret aliases defined with the `alias` command ## Documentation diff --git a/library/Core.cpp b/library/Core.cpp index 01bc89480..d55959ceb 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -763,6 +763,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, s } } + part = GetAliasCommand(part, true); + Plugin * plug = (*plug_mgr)[part]; if(!plug) @@ -2657,13 +2659,14 @@ std::map> Core::ListAliases() return aliases; } -std::string Core::GetAliasCommand(const std::string &name, const std::string &default_) +std::string Core::GetAliasCommand(const std::string &name, bool ignore_params) { std::lock_guard lock(alias_mutex); - if (IsAlias(name)) - return join_strings(" ", aliases[name]); - else - return default_; + if (!IsAlias(name) || aliases[name].empty()) + return name; + if (ignore_params) + return aliases[name][0]; + return join_strings(" ", aliases[name]); } ///////////////// diff --git a/library/include/Core.h b/library/include/Core.h index 6c99e62be..8232708b5 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -173,7 +173,7 @@ namespace DFHack bool RunAlias(color_ostream &out, const std::string &name, const std::vector ¶meters, command_result &result); std::map> ListAliases(); - std::string GetAliasCommand(const std::string &name, const std::string &default_ = ""); + std::string GetAliasCommand(const std::string &name, bool ignore_params = false); std::string getHackPath(); From a1c2df23c52e7784536fa31e65a222e0c33be33c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 23:55:54 -0700 Subject: [PATCH 0909/2222] fix vscode lint warning --- library/lua/dfhack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 2ee03dfed..51d6a0dfb 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -770,7 +770,7 @@ function dfhack.run_script_with_env(envVars, name, flags, ...) elseif ((type(v.required) == 'boolean' and v.required) or (type(v.required) == 'function' and v.required(flags))) then if not script_flags[flag] then - local msg = v.error or 'Flag "' .. flag .. '" not recognized' + local msg = v.error or ('Flag "' .. flag .. '" not recognized') error(name .. ': ' .. msg) end end From df286ab71e92f81ad54636b991045bfbaafeea5c Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sat, 25 Mar 2023 10:18:06 -0500 Subject: [PATCH 0910/2222] reenable getplants --- docs/changelog.txt | 1 + plugins/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 11e552abe..bf833d19b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `getplants`: getplants is now available - `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. ## Fixes diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8fac3d467..fde94b711 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -119,7 +119,7 @@ dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(follow follow.cpp) #dfhack_plugin(forceequip forceequip.cpp) #dfhack_plugin(generated-creature-renamer generated-creature-renamer.cpp) -#dfhack_plugin(getplants getplants.cpp) +dfhack_plugin(getplants getplants.cpp) dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) #dfhack_plugin(infiniteSky infiniteSky.cpp) #dfhack_plugin(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) From 9f5e4eeda17630f8597f952f7c38a177621e21f3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 23:55:54 -0700 Subject: [PATCH 0911/2222] fix vscode lint warning --- library/lua/dfhack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 2ee03dfed..51d6a0dfb 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -770,7 +770,7 @@ function dfhack.run_script_with_env(envVars, name, flags, ...) elseif ((type(v.required) == 'boolean' and v.required) or (type(v.required) == 'function' and v.required(flags))) then if not script_flags[flag] then - local msg = v.error or 'Flag "' .. flag .. '" not recognized' + local msg = v.error or ('Flag "' .. flag .. '" not recognized') error(name .. ': ' .. msg) end end From 1c25c9b80cd732c5f562f6029da7124d1e47affa Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sat, 25 Mar 2023 12:25:45 -0500 Subject: [PATCH 0912/2222] autoclothing: support disable this also includes some minor refactorings: * some debug messages have been "tagged" so it's obvious they're from autoclothing * a serialized list of flag checks has been replaced with a maskcheck * partial lua api added, currently only supports enable/disable --- plugins/CMakeLists.txt | 2 +- plugins/autoclothing.cpp | 114 +++++++++++++++++++++++++++++---------- 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 8fac3d467..047bd2269 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -77,7 +77,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) #dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) -dfhack_plugin(autoclothing autoclothing.cpp) +dfhack_plugin(autoclothing autoclothing.cpp LINK_LIBRARIES lua) dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index fc8b1e43b..aa114cb84 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -61,7 +61,25 @@ namespace DFHack { } static const string CONFIG_KEY = string(plugin_name) + "/config"; +enum ConfigValues { + CONFIG_IS_ENABLED = 0, +}; +//static int get_config_val(PersistentDataItem& c, int index) { +// if (!c.isValid()) +// return -1; +// return c.ival(index); +//} +//static bool get_config_bool(PersistentDataItem& c, int index) { +// return get_config_val(c, index) == 1; +//} +static void set_config_val(PersistentDataItem& c, int index, int value) { + if (c.isValid()) + c.ival(index) = value; +} +static void set_config_bool(PersistentDataItem& c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} // Here go all the command declarations... // mostly to allow having the mandatory stuff on top of the file and commands on the bottom @@ -250,6 +268,29 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan } +DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + return CR_FAILURE; + } + + if (enable != autoclothing_enabled) { + auto enabled = World::GetPersistentData("autoclothing/enabled"); + autoclothing_enabled = enable; + DEBUG(report, out).print("%s from the API; persisting\n", + autoclothing_enabled ? "enabled" : "disabled"); + set_config_bool(enabled, CONFIG_IS_ENABLED, autoclothing_enabled); + if (enable) + do_autoclothing(); + } + else { + DEBUG(report, out).print("%s from the API, but already %s; no action\n", + autoclothing_enabled ? "enabled" : "disabled", + autoclothing_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} + // Whatever you put here will be done in each game step. Don't abuse it. // It's optional, so you can just comment it out like this if you don't need it. @@ -389,6 +430,7 @@ command_result autoclothing(color_ostream &out, vector & parameters) if (parameters.size() == 0) { CoreSuspender suspend; + out << "Automatic clothing management is currently " << (autoclothing_enabled ? "enabled" : "disabled") << "." << endl; out << "Currently set " << clothingOrders.size() << " automatic clothing orders" << endl; for (size_t i = 0; i < clothingOrders.size(); i++) { @@ -523,7 +565,7 @@ static void find_needed_clothing_items() if (!item) { - WARN(cycle).print("Invalid inventory item ID: %d\n", ownedItem); + WARN(cycle).print("autoclothing: Invalid inventory item ID: %d\n", ownedItem); continue; } @@ -728,34 +770,28 @@ static void list_unit_counts(color_ostream& out, map& unitList) static bool isAvailableItem(df::item* item) { - if (item->flags.bits.in_job) - return false; - if (item->flags.bits.hostile) - return false; - if (item->flags.bits.in_building) - return false; - if (item->flags.bits.in_building) - return false; - if (item->flags.bits.encased) - return false; - if (item->flags.bits.foreign) - return false; - if (item->flags.bits.trader) - return false; - if (item->flags.bits.owned) - return false; - if (item->flags.bits.artifact) - return false; - if (item->flags.bits.forbid) - return false; - if (item->flags.bits.dump) - return false; - if (item->flags.bits.on_fire) - return false; - if (item->flags.bits.melt) - return false; - if (item->flags.bits.hidden) + static struct BadFlags { + uint32_t whole; + + BadFlags() { + df::item_flags flags; +#define F(x) flags.bits.x = true; + F(in_job); F(hostile); F(in_building); F(encased); + F(foreign); F(trader); F(owned); F(forbid); + F(dump); F(on_fire); F(melt); F(hidden); + + F(garbage_collect); F(rotten); F(construction); + F(in_chest); F(removed); F(spider_web); + + // F(artifact); -- TODO: should this be included? +#undef F + whole = flags.whole; + } + } badFlags; + + if ((item->flags.whole & badFlags.whole) != 0) return false; + if (item->getWear() > 1) return false; if (!item->isClothing()) @@ -782,7 +818,7 @@ static void generate_report(color_ostream& out) auto item = Items::findItemByID(itemId); if (!item) { - WARN(cycle,out).print("Invalid inventory item ID: %d\n", itemId); + WARN(cycle,out).print("autoclothing: Invalid inventory item ID: %d\n", itemId); continue; } if (item->getWear() >= 1) @@ -915,3 +951,23 @@ static void generate_report(color_ostream& out) } } + +///////////////////////////////////////////////////// +// Lua API +// TODO: implement Lua hooks to manipulate the persistent order configuration +// + +static void autoclothing_doCycle(color_ostream& out) { + DEBUG(report, out).print("entering autoclothing_doCycle\n"); + do_autoclothing(); +} + + +DFHACK_PLUGIN_LUA_FUNCTIONS{ + DFHACK_LUA_FUNCTION(autoclothing_doCycle), + DFHACK_LUA_END +}; + +DFHACK_PLUGIN_LUA_COMMANDS{ + DFHACK_LUA_END +}; From 2627820bfa3df3841d78e79f9895f166346d0041 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 22:34:09 -0700 Subject: [PATCH 0913/2222] untested -> unavailable --- docs/Tags.rst | 2 +- docs/changelog.txt | 1 + docs/plugins/3dveins.rst | 2 +- docs/plugins/add-spatter.rst | 2 +- docs/plugins/autogems.rst | 2 +- docs/plugins/autotrade.rst | 2 +- docs/plugins/building-hacks.rst | 2 +- docs/plugins/burrows.rst | 2 +- docs/plugins/changeitem.rst | 2 +- docs/plugins/channel-safely.rst | 2 +- docs/plugins/createitem.rst | 2 +- docs/plugins/deramp.rst | 2 +- docs/plugins/digFlood.rst | 2 +- docs/plugins/diggingInvaders.rst | 2 +- docs/plugins/dwarfmonitor.rst | 2 +- docs/plugins/dwarfvet.rst | 2 +- docs/plugins/embark-assistant.rst | 2 +- docs/plugins/embark-tools.rst | 2 +- docs/plugins/fix-unit-occupancy.rst | 2 +- docs/plugins/fixveins.rst | 2 +- docs/plugins/flows.rst | 2 +- docs/plugins/follow.rst | 2 +- docs/plugins/forceequip.rst | 2 +- docs/plugins/generated-creature-renamer.rst | 2 +- docs/plugins/getplants.rst | 2 +- docs/plugins/infiniteSky.rst | 2 +- docs/plugins/isoworldremote.rst | 2 +- docs/plugins/jobutils.rst | 2 +- docs/plugins/labormanager.rst | 2 +- docs/plugins/lair.rst | 2 +- docs/plugins/luasocket.rst | 2 +- docs/plugins/manipulator.rst | 2 +- docs/plugins/map-render.rst | 2 +- docs/plugins/mode.rst | 2 +- docs/plugins/mousequery.rst | 2 +- docs/plugins/petcapRemover.rst | 2 +- docs/plugins/plants.rst | 2 +- docs/plugins/power-meter.rst | 2 +- docs/plugins/prospector.rst | 2 +- docs/plugins/rename.rst | 2 +- docs/plugins/rendermax.rst | 2 +- docs/plugins/search.rst | 2 +- docs/plugins/siege-engine.rst | 2 +- docs/plugins/sort.rst | 2 +- docs/plugins/spectate.rst | 2 +- docs/plugins/steam-engine.rst | 2 +- docs/plugins/stockflow.rst | 2 +- docs/plugins/stocks.rst | 2 +- docs/plugins/title-folder.rst | 2 +- docs/plugins/title-version.rst | 2 +- docs/plugins/trackstop.rst | 2 +- docs/plugins/tubefill.rst | 2 +- docs/plugins/tweak.rst | 2 +- docs/plugins/workNow.rst | 2 +- docs/plugins/workflow.rst | 2 +- docs/plugins/zone.rst | 2 +- library/lua/dfhack.lua | 2 +- 57 files changed, 57 insertions(+), 56 deletions(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index 60a6a6813..5ff13d632 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -49,4 +49,4 @@ for the tag assignment spreadsheet. "misc" tags ----------- -- `untested `: Tools that are not yet tested with the current release. +- `unavailable `: Tools that are not yet available for the current release. diff --git a/docs/changelog.txt b/docs/changelog.txt index 7cb1fafcf..032ca52ad 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,6 +54,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `enable`: can now interpret aliases defined with the `alias` command ## Documentation +- ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. all of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again. ## API diff --git a/docs/plugins/3dveins.rst b/docs/plugins/3dveins.rst index 1d7acd94f..3112934ab 100644 --- a/docs/plugins/3dveins.rst +++ b/docs/plugins/3dveins.rst @@ -3,7 +3,7 @@ .. dfhack-tool:: :summary: Rewrite layer veins to expand in 3D space. - :tags: untested fort gameplay map + :tags: unavailable fort gameplay map Existing, flat veins are removed and new 3D veins that naturally span z-levels are generated in their place. The transformation preserves the mineral counts diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index fc3c507c2..6914690ea 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -3,7 +3,7 @@ add-spatter .. dfhack-tool:: :summary: Make tagged reactions produce contaminants. - :tags: untested adventure fort gameplay items + :tags: unavailable adventure fort gameplay items :no-command: Give some use to all those poisons that can be bought from caravans! The plugin diff --git a/docs/plugins/autogems.rst b/docs/plugins/autogems.rst index f18b671a1..6135b39d2 100644 --- a/docs/plugins/autogems.rst +++ b/docs/plugins/autogems.rst @@ -3,7 +3,7 @@ autogems .. dfhack-tool:: :summary: Automatically cut rough gems. - :tags: untested fort auto workorders + :tags: unavailable fort auto workorders :no-command: .. dfhack-command:: autogems-reload diff --git a/docs/plugins/autotrade.rst b/docs/plugins/autotrade.rst index 439eead40..24c1b42fa 100644 --- a/docs/plugins/autotrade.rst +++ b/docs/plugins/autotrade.rst @@ -3,7 +3,7 @@ autotrade .. dfhack-tool:: :summary: Quickly designate items to be traded. - :tags: untested fort productivity items stockpiles + :tags: unavailable fort productivity items stockpiles :no-command: When `enabled `, this plugin adds an option to the :kbd:`q` menu for diff --git a/docs/plugins/building-hacks.rst b/docs/plugins/building-hacks.rst index cce3479d8..b45b23be4 100644 --- a/docs/plugins/building-hacks.rst +++ b/docs/plugins/building-hacks.rst @@ -3,7 +3,7 @@ building-hacks .. dfhack-tool:: :summary: Provides a Lua API for creating powered workshops. - :tags: untested fort gameplay buildings + :tags: unavailable fort gameplay buildings :no-command: See `building-hacks-api` for more details. diff --git a/docs/plugins/burrows.rst b/docs/plugins/burrows.rst index 54812ece5..299656cf5 100644 --- a/docs/plugins/burrows.rst +++ b/docs/plugins/burrows.rst @@ -3,7 +3,7 @@ burrows .. dfhack-tool:: :summary: Auto-expand burrows as you dig. - :tags: untested fort auto design productivity map units + :tags: unavailable fort auto design productivity map units :no-command: .. dfhack-command:: burrow diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index fe9229a09..788b49247 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -3,7 +3,7 @@ changeitem .. dfhack-tool:: :summary: Change item material or base quality. - :tags: untested adventure fort armok items + :tags: unavailable adventure fort armok items By default, a change is only allowed if the existing and desired item materials are of the same subtype (for example wood -> wood, stone -> stone, etc). But diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index 3acbe66cd..c24f352b0 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -3,7 +3,7 @@ channel-safely .. dfhack-tool:: :summary: Auto-manage channel designations to keep dwarves safe. - :tags: untested fort auto + :tags: unavailable fort auto Multi-level channel projects can be dangerous, and managing the safety of your dwarves throughout the completion of such projects can be difficult and time diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index 7abe06cff..b287042c7 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -3,7 +3,7 @@ createitem .. dfhack-tool:: :summary: Create arbitrary items. - :tags: untested adventure fort armok items + :tags: unavailable adventure fort armok items You can create new items of any type and made of any material. A unit must be selected in-game to use this command. By default, items created are spawned at diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index 641f370a6..4a1f34548 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -3,7 +3,7 @@ deramp .. dfhack-tool:: :summary: Removes all ramps designated for removal from the map. - :tags: untested fort armok map + :tags: unavailable fort armok map It also removes any "floating" down ramps that can remain after a cave-in. diff --git a/docs/plugins/digFlood.rst b/docs/plugins/digFlood.rst index 70dd9b388..ee502371a 100644 --- a/docs/plugins/digFlood.rst +++ b/docs/plugins/digFlood.rst @@ -3,7 +3,7 @@ digFlood .. dfhack-tool:: :summary: Digs out veins as they are discovered. - :tags: untested fort auto map + :tags: unavailable fort auto map Once you register specific vein types, this tool will automatically designate tiles of those types of veins for digging as your miners complete adjacent diff --git a/docs/plugins/diggingInvaders.rst b/docs/plugins/diggingInvaders.rst index 4ea52013e..001636709 100644 --- a/docs/plugins/diggingInvaders.rst +++ b/docs/plugins/diggingInvaders.rst @@ -3,7 +3,7 @@ diggingInvaders .. dfhack-tool:: :summary: Invaders dig and destroy to get to your dwarves. - :tags: untested fort gameplay military units + :tags: unavailable fort gameplay military units Usage ----- diff --git a/docs/plugins/dwarfmonitor.rst b/docs/plugins/dwarfmonitor.rst index 47d49d1e0..45a5d8f85 100644 --- a/docs/plugins/dwarfmonitor.rst +++ b/docs/plugins/dwarfmonitor.rst @@ -3,7 +3,7 @@ dwarfmonitor .. dfhack-tool:: :summary: Report on dwarf preferences and efficiency. - :tags: untested fort inspection jobs units + :tags: unavailable fort inspection jobs units It can also show heads-up display widgets with live fort statistics. diff --git a/docs/plugins/dwarfvet.rst b/docs/plugins/dwarfvet.rst index 7480c10a6..b4dfe0ada 100644 --- a/docs/plugins/dwarfvet.rst +++ b/docs/plugins/dwarfvet.rst @@ -3,7 +3,7 @@ dwarfvet .. dfhack-tool:: :summary: Allows animals to be treated at animal hospitals. - :tags: untested fort gameplay animals + :tags: unavailable fort gameplay animals Annoyed that your dragons become useless after a minor injury? Well, with dwarfvet, injured animals will be treated at an animal hospital, which is simply diff --git a/docs/plugins/embark-assistant.rst b/docs/plugins/embark-assistant.rst index bb74f221d..282d4b122 100644 --- a/docs/plugins/embark-assistant.rst +++ b/docs/plugins/embark-assistant.rst @@ -3,7 +3,7 @@ embark-assistant .. dfhack-tool:: :summary: Embark site selection support. - :tags: untested embark fort interface + :tags: unavailable embark fort interface Run this command while the pre-embark screen is displayed to show extended (and reasonably correct) resource information for the embark rectangle as well as diff --git a/docs/plugins/embark-tools.rst b/docs/plugins/embark-tools.rst index 6a600019b..f320706ff 100644 --- a/docs/plugins/embark-tools.rst +++ b/docs/plugins/embark-tools.rst @@ -3,7 +3,7 @@ embark-tools .. dfhack-tool:: :summary: Extend the embark screen functionality. - :tags: untested embark fort interface + Usage ----- diff --git a/docs/plugins/fix-unit-occupancy.rst b/docs/plugins/fix-unit-occupancy.rst index 8589f765a..6ba01b712 100644 --- a/docs/plugins/fix-unit-occupancy.rst +++ b/docs/plugins/fix-unit-occupancy.rst @@ -3,7 +3,7 @@ fix-unit-occupancy .. dfhack-tool:: :summary: Fix phantom unit occupancy issues. - :tags: untested fort bugfix map + :tags: unavailable fort bugfix map If you see "unit blocking tile" messages that you can't account for (:bug:`3499`), this tool can help. diff --git a/docs/plugins/fixveins.rst b/docs/plugins/fixveins.rst index c13bbe9a1..552c5f8c3 100644 --- a/docs/plugins/fixveins.rst +++ b/docs/plugins/fixveins.rst @@ -3,7 +3,7 @@ fixveins .. dfhack-tool:: :summary: Restore missing mineral inclusions. - :tags: untested fort bugfix map + :tags: unavailable fort bugfix map This tool can also remove invalid references to mineral inclusions if you broke your embark with tools like `tiletypes`. diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst index 209bb577c..56840d999 100644 --- a/docs/plugins/flows.rst +++ b/docs/plugins/flows.rst @@ -3,7 +3,7 @@ flows .. dfhack-tool:: :summary: Counts map blocks with flowing liquids. - :tags: untested fort inspection map + :tags: unavailable fort inspection map If you suspect that your magma sea leaks into HFS, you can use this tool to be sure without revealing the map. diff --git a/docs/plugins/follow.rst b/docs/plugins/follow.rst index 47db40bd8..e72f79ce0 100644 --- a/docs/plugins/follow.rst +++ b/docs/plugins/follow.rst @@ -3,7 +3,7 @@ follow .. dfhack-tool:: :summary: Make the screen follow the selected unit. - :tags: untested fort interface units + :tags: unavailable fort interface units Once you exit from the current menu or cursor mode, the screen will stay centered on the unit. Handy for watching dwarves running around. Deactivated by diff --git a/docs/plugins/forceequip.rst b/docs/plugins/forceequip.rst index d4c9a6c4a..2565d12c3 100644 --- a/docs/plugins/forceequip.rst +++ b/docs/plugins/forceequip.rst @@ -3,7 +3,7 @@ forceequip .. dfhack-tool:: :summary: Move items into a unit's inventory. - :tags: untested adventure fort animals items military units + :tags: unavailable adventure fort animals items military units This tool is typically used to equip specific clothing/armor items onto a dwarf, but can also be used to put armor onto a war animal or to add unusual items diff --git a/docs/plugins/generated-creature-renamer.rst b/docs/plugins/generated-creature-renamer.rst index ba1761a1b..ea386eacf 100644 --- a/docs/plugins/generated-creature-renamer.rst +++ b/docs/plugins/generated-creature-renamer.rst @@ -3,7 +3,7 @@ generated-creature-renamer .. dfhack-tool:: :summary: Automatically renames generated creatures. - :tags: untested adventure fort legends units + :tags: unavailable adventure fort legends units :no-command: .. dfhack-command:: list-generated diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 756fa1aaa..0a59f2492 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -3,7 +3,7 @@ getplants .. dfhack-tool:: :summary: Designate trees for chopping and shrubs for gathering. - :tags: untested fort productivity plants + :tags: unavailable fort productivity plants Specify the types of trees to cut down and/or shrubs to gather by their plant names. diff --git a/docs/plugins/infiniteSky.rst b/docs/plugins/infiniteSky.rst index 7e8ec2f31..789709c9f 100644 --- a/docs/plugins/infiniteSky.rst +++ b/docs/plugins/infiniteSky.rst @@ -3,7 +3,7 @@ infiniteSky .. dfhack-tool:: :summary: Automatically allocate new z-levels of sky - :tags: untested fort auto design map + :tags: unavailable fort auto design map If enabled, this plugin will automatically allocate new z-levels of sky at the top of the map as you build up. Or it can allocate one or many additional levels diff --git a/docs/plugins/isoworldremote.rst b/docs/plugins/isoworldremote.rst index 2442f70ef..b792cb649 100644 --- a/docs/plugins/isoworldremote.rst +++ b/docs/plugins/isoworldremote.rst @@ -3,7 +3,7 @@ isoworldremote .. dfhack-tool:: :summary: Provides a remote API used by Isoworld. - :tags: untested dev graphics + :tags: unavailable dev graphics :no-command: See `remote` for related remote APIs. diff --git a/docs/plugins/jobutils.rst b/docs/plugins/jobutils.rst index f68c200b2..674c6897a 100644 --- a/docs/plugins/jobutils.rst +++ b/docs/plugins/jobutils.rst @@ -5,7 +5,7 @@ jobutils .. dfhack-tool:: :summary: Provides commands for interacting with jobs. - :tags: untested fort inspection jobs + :tags: unavailable fort inspection jobs :no-command: .. dfhack-command:: job diff --git a/docs/plugins/labormanager.rst b/docs/plugins/labormanager.rst index f808fcb59..731f706b8 100644 --- a/docs/plugins/labormanager.rst +++ b/docs/plugins/labormanager.rst @@ -3,7 +3,7 @@ labormanager .. dfhack-tool:: :summary: Automatically manage dwarf labors. - :tags: untested fort auto labors + :tags: unavailable fort auto labors Labormanager is derived from `autolabor` but uses a completely different approach to assigning jobs to dwarves. While autolabor tries to keep as many diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index 77ad0a045..82c5e211c 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -3,7 +3,7 @@ lair .. dfhack-tool:: :summary: Mark the map as a monster lair. - :tags: untested fort armok map + :tags: unavailable fort armok map This avoids item scatter when the fortress is abandoned. diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst index 69f566e24..1aa320ed7 100644 --- a/docs/plugins/luasocket.rst +++ b/docs/plugins/luasocket.rst @@ -3,7 +3,7 @@ luasocket .. dfhack-tool:: :summary: Provides a Lua API for accessing network sockets. - :tags: untested dev + :tags: unavailable dev :no-command: See `luasocket-api` for details. diff --git a/docs/plugins/manipulator.rst b/docs/plugins/manipulator.rst index b4c0d6160..734800e77 100644 --- a/docs/plugins/manipulator.rst +++ b/docs/plugins/manipulator.rst @@ -3,7 +3,7 @@ manipulator .. dfhack-tool:: :summary: An in-game labor management interface. - :tags: untested fort productivity labors + :tags: unavailable fort productivity labors :no-command: It is equivalent to the popular Dwarf Therapist utility. diff --git a/docs/plugins/map-render.rst b/docs/plugins/map-render.rst index bbd01aa97..4f3c6ba72 100644 --- a/docs/plugins/map-render.rst +++ b/docs/plugins/map-render.rst @@ -3,7 +3,7 @@ map-render .. dfhack-tool:: :summary: Provides a Lua API for re-rendering portions of the map. - :tags: untested dev graphics + :tags: unavailable dev graphics :no-command: See `map-render-api` for details. diff --git a/docs/plugins/mode.rst b/docs/plugins/mode.rst index 42c7f6fcc..061e3a548 100644 --- a/docs/plugins/mode.rst +++ b/docs/plugins/mode.rst @@ -3,7 +3,7 @@ mode .. dfhack-tool:: :summary: See and change the game mode. - :tags: untested armok dev gameplay + :tags: unavailable armok dev gameplay .. warning:: diff --git a/docs/plugins/mousequery.rst b/docs/plugins/mousequery.rst index 0e49d3924..bff110f0e 100644 --- a/docs/plugins/mousequery.rst +++ b/docs/plugins/mousequery.rst @@ -3,7 +3,7 @@ mousequery .. dfhack-tool:: :summary: Adds mouse controls to the DF interface. - :tags: untested fort productivity interface + :tags: unavailable fort productivity interface Adds mouse controls to the DF interface. For example, with ``mousequery`` you can click on buildings to configure them, hold the mouse button to draw dig diff --git a/docs/plugins/petcapRemover.rst b/docs/plugins/petcapRemover.rst index 798e39f32..4f6ea4160 100644 --- a/docs/plugins/petcapRemover.rst +++ b/docs/plugins/petcapRemover.rst @@ -3,7 +3,7 @@ petcapRemover .. dfhack-tool:: :summary: Modify the pet population cap. - :tags: untested fort auto animals + :tags: unavailable fort auto animals In vanilla DF, pets will not reproduce unless the population is below 50 and the number of children of that species is below a certain percentage. This plugin diff --git a/docs/plugins/plants.rst b/docs/plugins/plants.rst index cdcc0daac..281b295cf 100644 --- a/docs/plugins/plants.rst +++ b/docs/plugins/plants.rst @@ -5,7 +5,7 @@ plants .. dfhack-tool:: :summary: Provides commands that interact with plants. - :tags: untested adventure fort armok map plants + :tags: unavailable adventure fort armok map plants :no-command: .. dfhack-command:: plant diff --git a/docs/plugins/power-meter.rst b/docs/plugins/power-meter.rst index 8ffc79e3e..f3a76c60a 100644 --- a/docs/plugins/power-meter.rst +++ b/docs/plugins/power-meter.rst @@ -3,7 +3,7 @@ power-meter .. dfhack-tool:: :summary: Allow pressure plates to measure power. - :tags: untested fort gameplay buildings + :tags: unavailable fort gameplay buildings :no-command: If you run `gui/power-meter` while building a pressure plate, the pressure diff --git a/docs/plugins/prospector.rst b/docs/plugins/prospector.rst index 3fb47cc57..4628d11c8 100644 --- a/docs/plugins/prospector.rst +++ b/docs/plugins/prospector.rst @@ -5,7 +5,7 @@ prospector .. dfhack-tool:: :summary: Provides commands that help you analyze natural resources. - :tags: untested embark fort armok inspection map + :tags: embark fort armok inspection map :no-command: .. dfhack-command:: prospect diff --git a/docs/plugins/rename.rst b/docs/plugins/rename.rst index 3c6ed4a77..903a1a1d4 100644 --- a/docs/plugins/rename.rst +++ b/docs/plugins/rename.rst @@ -3,7 +3,7 @@ rename .. dfhack-tool:: :summary: Easily rename things. - :tags: untested adventure fort productivity buildings stockpiles units + :tags: unavailable adventure fort productivity buildings stockpiles units Use `gui/rename` for an in-game interface. diff --git a/docs/plugins/rendermax.rst b/docs/plugins/rendermax.rst index 370771d6b..91faa6f5c 100644 --- a/docs/plugins/rendermax.rst +++ b/docs/plugins/rendermax.rst @@ -3,7 +3,7 @@ rendermax .. dfhack-tool:: :summary: Modify the map lighting. - :tags: untested adventure fort gameplay graphics + :tags: unavailable adventure fort gameplay graphics This plugin provides a collection of OpenGL lighting filters that affect how the map is drawn to the screen. diff --git a/docs/plugins/search.rst b/docs/plugins/search.rst index 8e1a93143..c1493f992 100644 --- a/docs/plugins/search.rst +++ b/docs/plugins/search.rst @@ -5,7 +5,7 @@ search .. dfhack-tool:: :summary: Adds search capabilities to the UI. - :tags: untested fort productivity interface + :tags: unavailable fort productivity interface :no-command: Search options are added to the Stocks, Animals, Trading, Stockpile, Noble diff --git a/docs/plugins/siege-engine.rst b/docs/plugins/siege-engine.rst index fa18dcd48..57693fa11 100644 --- a/docs/plugins/siege-engine.rst +++ b/docs/plugins/siege-engine.rst @@ -3,7 +3,7 @@ siege-engine .. dfhack-tool:: :summary: Extend the functionality and usability of siege engines. - :tags: untested fort gameplay buildings + :tags: unavailable fort gameplay buildings :no-command: Siege engines in DF haven't been updated since the game was 2D, and can only aim diff --git a/docs/plugins/sort.rst b/docs/plugins/sort.rst index 3a8c7c0f5..067e188fd 100644 --- a/docs/plugins/sort.rst +++ b/docs/plugins/sort.rst @@ -3,7 +3,7 @@ sort .. dfhack-tool:: :summary: Sort lists shown in the DF interface. - :tags: untested fort productivity interface + :tags: unavailable fort productivity interface :no-command: .. dfhack-command:: sort-items diff --git a/docs/plugins/spectate.rst b/docs/plugins/spectate.rst index 448369cd8..95ce852ae 100644 --- a/docs/plugins/spectate.rst +++ b/docs/plugins/spectate.rst @@ -3,7 +3,7 @@ spectate .. dfhack-tool:: :summary: Automatically follow productive dwarves. - :tags: untested fort interface + :tags: unavailable fort interface Usage ----- diff --git a/docs/plugins/steam-engine.rst b/docs/plugins/steam-engine.rst index 11d0145f3..532b311d0 100644 --- a/docs/plugins/steam-engine.rst +++ b/docs/plugins/steam-engine.rst @@ -3,7 +3,7 @@ steam-engine .. dfhack-tool:: :summary: Allow modded steam engine buildings to function. - :tags: untested fort gameplay buildings + :tags: unavailable fort gameplay buildings :no-command: The steam-engine plugin detects custom workshops with the string diff --git a/docs/plugins/stockflow.rst b/docs/plugins/stockflow.rst index f501fc904..29b7838fc 100644 --- a/docs/plugins/stockflow.rst +++ b/docs/plugins/stockflow.rst @@ -3,7 +3,7 @@ stockflow .. dfhack-tool:: :summary: Queue manager jobs based on free space in stockpiles. - :tags: untested fort auto stockpiles workorders + :tags: unavailable fort auto stockpiles workorders With this plugin, the fortress bookkeeper can tally up free space in specific stockpiles and queue jobs through the manager to produce items to fill the free diff --git a/docs/plugins/stocks.rst b/docs/plugins/stocks.rst index c924b748c..f8db4ee67 100644 --- a/docs/plugins/stocks.rst +++ b/docs/plugins/stocks.rst @@ -3,7 +3,7 @@ stocks .. dfhack-tool:: :summary: Enhanced fortress stock management interface. - :tags: untested fort productivity items + :tags: unavailable fort productivity items When the plugin is enabled, two new hotkeys become available: diff --git a/docs/plugins/title-folder.rst b/docs/plugins/title-folder.rst index 32818071b..ee4068547 100644 --- a/docs/plugins/title-folder.rst +++ b/docs/plugins/title-folder.rst @@ -3,7 +3,7 @@ title-folder .. dfhack-tool:: :summary: Displays the DF folder name in the window title bar. - :tags: untested interface + :tags: unavailable interface :no-command: Usage diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst index 18c7c3ba8..4d0ef0c0b 100644 --- a/docs/plugins/title-version.rst +++ b/docs/plugins/title-version.rst @@ -3,7 +3,7 @@ title-version .. dfhack-tool:: :summary: Displays the DFHack version on DF's title screen. - :tags: untested interface + :tags: unavailable interface :no-command: Usage diff --git a/docs/plugins/trackstop.rst b/docs/plugins/trackstop.rst index 6ae23dcd0..041bcac2c 100644 --- a/docs/plugins/trackstop.rst +++ b/docs/plugins/trackstop.rst @@ -3,7 +3,7 @@ trackstop .. dfhack-tool:: :summary: Add dynamic configuration options for track stops. - :tags: untested fort gameplay buildings + :tags: unavailable fort gameplay buildings :no-command: When enabled, this plugin adds a :kbd:`q` menu for track stops, which is diff --git a/docs/plugins/tubefill.rst b/docs/plugins/tubefill.rst index eb568ef03..a8684c765 100644 --- a/docs/plugins/tubefill.rst +++ b/docs/plugins/tubefill.rst @@ -3,7 +3,7 @@ tubefill .. dfhack-tool:: :summary: Replenishes mined-out adamantine. - :tags: untested fort armok map + :tags: unavailable fort armok map Veins that were originally hollow will be left alone. diff --git a/docs/plugins/tweak.rst b/docs/plugins/tweak.rst index dc5ed19a8..f9467e333 100644 --- a/docs/plugins/tweak.rst +++ b/docs/plugins/tweak.rst @@ -3,7 +3,7 @@ tweak .. dfhack-tool:: :summary: A collection of tweaks and bugfixes. - :tags: untested adventure fort armok bugfix fps interface + :tags: unavailable adventure fort armok bugfix fps interface Usage ----- diff --git a/docs/plugins/workNow.rst b/docs/plugins/workNow.rst index 66bc822ae..cec39c667 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/workNow.rst @@ -3,7 +3,7 @@ workNow .. dfhack-tool:: :summary: Reduce the time that dwarves idle after completing a job. - :tags: untested fort auto labors + :tags: unavailable fort auto labors After finishing a job, dwarves will wander away for a while before picking up a new job. This plugin will automatically poke the game to assign dwarves to new diff --git a/docs/plugins/workflow.rst b/docs/plugins/workflow.rst index 72b7a8e2b..c95054c0e 100644 --- a/docs/plugins/workflow.rst +++ b/docs/plugins/workflow.rst @@ -3,7 +3,7 @@ workflow .. dfhack-tool:: :summary: Manage automated item production rules. - :tags: untested fort auto jobs + :tags: unavailable fort auto jobs Manage repeat jobs according to stock levels. `gui/workflow` provides a simple front-end integrated in the game UI. diff --git a/docs/plugins/zone.rst b/docs/plugins/zone.rst index 9d9991dde..af6ee2b5f 100644 --- a/docs/plugins/zone.rst +++ b/docs/plugins/zone.rst @@ -3,7 +3,7 @@ zone .. dfhack-tool:: :summary: Manage activity zones, cages, and the animals therein. - :tags: untested fort productivity animals buildings + :tags: unavailable fort productivity animals buildings Usage ----- diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 51d6a0dfb..aa33b62d6 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -702,7 +702,7 @@ local warned_scripts = {} function dfhack.run_script(name,...) if not warned_scripts[name] then local helpdb = require('helpdb') - if helpdb.is_entry(name) and helpdb.get_entry_tags(name).untested then + if helpdb.is_entry(name) and helpdb.get_entry_tags(name).unavailable then warned_scripts[name] = true dfhack.printerr(('UNTESTED WARNING: the "%s" script has not been validated to work well with this version of DF.'):format(name)) dfhack.printerr('It may not work as expected, or it may corrupt your game.') From 5cfa968b38f9f0834a7b7a127ef70f183fdbadf3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 25 Mar 2023 12:35:16 -0700 Subject: [PATCH 0914/2222] update scripts ref --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 0b37dcf99..f637dc4bf 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 0b37dcf996bf4b64423d2acb3e3ca224ad76c6bc +Subproject commit f637dc4bf18a7ca4968f45e79790893c6d22c3f5 From c8c004067803e74f9508e58d0e3c51eeb93bdd6e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sat, 25 Mar 2023 14:56:04 -0500 Subject: [PATCH 0915/2222] tailor: add inventory sanity check debug mode enable with "tailor debugging on" disable with "tailor debugging off" this setting is not persisted --- plugins/lua/tailor.lua | 7 +++++ plugins/tailor.cpp | 58 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/plugins/lua/tailor.lua b/plugins/lua/tailor.lua index e9a88bfe1..46b3526d9 100644 --- a/plugins/lua/tailor.lua +++ b/plugins/lua/tailor.lua @@ -32,6 +32,11 @@ function setMaterials(names) idxs.adamantine or -1) end +function setDebugMode(opt) + local fl = (opt[1] == "true" or opt[1] == "on") + tailor_setDebugFlag(fl) +end + function parse_commandline(...) local args, opts = {...}, {} local positionals = process_args(opts, args) @@ -47,6 +52,8 @@ function parse_commandline(...) tailor_doCycle() elseif command == 'materials' then setMaterials(positionals) + elseif command == 'debugging' then + setDebugMode(positionals) else return false end diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 2c5a1a027..3ba6744bb 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -157,7 +157,14 @@ private: int default_reserve = 10; + bool inventory_sanity_checking = false; + public: + void set_debug_flag(bool f) + { + inventory_sanity_checking = f; + } + void reset() { available.clear(); @@ -170,16 +177,42 @@ public: void scan_clothing() { - for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" + if (!inventory_sanity_checking) { - if (i->flags.whole & badFlags.whole) - continue; - if (i->getWear() >= 1) - continue; - df::item_type t = i->getType(); - int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; + for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" + { + if (i->flags.whole & badFlags.whole) + continue; + if (i->getWear() >= 1) + continue; + df::item_type t = i->getType(); + int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; + + available[std::make_pair(t, size)] += 1; + } + } + else + { + auto& l = world->items.other[df::items_other_id::ANY_GENERIC37]; + + for (auto i : world->items.other[df::items_other_id::IN_PLAY]) + { + if (i->flags.whole & badFlags.whole) + continue; + if (!i->isClothing()) + continue; + if (std::find(std::begin(l), std::end(l), i) == std::end(l)) + { + DEBUG(cycle).print("tailor: clothing item %d missing from GENERIC37 list\n", i->id); + } + if (i->getWear() >= 1) + continue; + df::item_type t = i->getType(); + int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; + + available[std::make_pair(t, size)] += 1; + } - available[std::make_pair(t, size)] += 1; } } @@ -748,9 +781,18 @@ static int tailor_getMaterialPreferences(lua_State *L) { return 1; } +static void tailor_setDebugFlag(color_ostream& out, bool enable) +{ + DEBUG(config, out).print("entering tailor_setDebugFlag\n"); + + tailor_instance->set_debug_flag(enable); + +} + DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(tailor_doCycle), DFHACK_LUA_FUNCTION(tailor_setMaterialPreferences), + DFHACK_LUA_FUNCTION(tailor_setDebugFlag), DFHACK_LUA_END }; From 76bacee2382298c857afc3c3cca093900dceef7f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 02:07:59 -0800 Subject: [PATCH 0916/2222] dynamically add mod scripts to the script path --- docs/changelog.txt | 1 + library/Core.cpp | 101 +++++++++++++++++++++++++++++++++++------ library/include/Core.h | 3 +- 3 files changed, 91 insertions(+), 14 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d70b7882c..f968d79ac 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) - `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen - `enable`: can now interpret aliases defined with the `alias` command +- scripts in installed mods are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world. ## Documentation - ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. all of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again. diff --git a/library/Core.cpp b/library/Core.cpp index d55959ceb..1bca0f5ed 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -105,6 +105,7 @@ DBG_DECLARE(core,script,DebugCategory::LINFO); static const std::string CONFIG_PATH = "dfhack-config/"; static const std::string CONFIG_DEFAULTS_PATH = "hack/data/dfhack-config-defaults/"; +static const std::string MOD_PATH = "data/installed_mods/"; class MainThread { public: @@ -440,6 +441,12 @@ bool Core::addScriptPath(std::string path, bool search_before) return true; } +bool Core::setModScriptPaths(const std::vector &mod_script_paths) { + std::lock_guard lock(script_path_mutex); + script_paths[2] = mod_script_paths; + return true; +} + bool Core::removeScriptPath(std::string path) { std::lock_guard lock(script_path_mutex); @@ -464,20 +471,21 @@ void Core::getScriptPaths(std::vector *dest) std::lock_guard lock(script_path_mutex); dest->clear(); std::string df_path = this->p->getPath() + "/"; - for (auto it = script_paths[0].begin(); it != script_paths[0].end(); ++it) - dest->push_back(*it); + for (auto & path : script_paths[0]) + dest->emplace_back(path); dest->push_back(df_path + CONFIG_PATH + "scripts"); if (df::global::world && isWorldLoaded()) { std::string save = World::ReadWorldFolder(); if (save.size()) - dest->push_back(df_path + "/save/" + save + "/scripts"); + dest->emplace_back(df_path + "save/" + save + "/scripts"); } - dest->push_back(df_path + "/hack/scripts"); - for (auto it = script_paths[1].begin(); it != script_paths[1].end(); ++it) - dest->push_back(*it); + dest->emplace_back(df_path + "hack/scripts"); + for (auto & path : script_paths[2]) + dest->emplace_back(path); + for (auto & path : script_paths[1]) + dest->emplace_back(path); } - std::string Core::findScript(std::string name) { std::vector paths; @@ -526,6 +534,61 @@ bool loadScriptPaths(color_ostream &out, bool silent = false) return true; } +bool loadModScriptPaths(color_ostream &out) { + std::map files; + Filesystem::listdir_recursive(MOD_PATH, files, 0); + + DEBUG(script,out).print("found %zd installed mods\n", files.size()); + if (!files.size()) + return true; + + for (auto & entry : files) { + DEBUG(script,out).print(" %s\n", entry.first.c_str()); + } + + std::vector mod_paths; + if (Core::getInstance().isWorldLoaded()) { + DEBUG(script,out).print("active load order:\n"); + for (auto & path : df::global::world->object_loader.object_load_order_src_dir) { + DEBUG(script,out).print(" %s\n", path->c_str()); + if (0 == path->find(MOD_PATH)) + mod_paths.emplace_back(*path); + } + } + + std::vector mod_script_paths; + for (auto pathit = mod_paths.rbegin(); pathit != mod_paths.rend(); ++pathit) { + std::string active_path = *pathit + "scripts_modactive"; + std::string installed_path = *pathit + "scripts_modinstalled"; + DEBUG(script,out).print("checking active path: %s\n", pathit->c_str()); + if (Filesystem::isdir(active_path)) + mod_script_paths.emplace_back(active_path); + if (Filesystem::isdir(installed_path)) + mod_script_paths.emplace_back(installed_path); + std::string slashless = *pathit; + slashless.resize(slashless.size()-1); + if (0 == files.erase(slashless)) + WARN(script,out).print("script path not found: '%s'\n", pathit->c_str()); + } + + for (auto & entry : files) { + if (!entry.second) + continue; + DEBUG(script,out).print("checking inactive path: %s\n", entry.first.c_str()); + std::string installed_path = entry.first + "/scripts_modinstalled"; + if (Filesystem::isdir(installed_path)) + mod_script_paths.emplace_back(installed_path); + } + + DEBUG(script,out).print("final mod script paths:\n"); + for (auto & path : mod_script_paths) + DEBUG(script,out).print(" %s\n", path.c_str()); + + Core::getInstance().setModScriptPaths(mod_script_paths); + + return true; +} + static std::map state_change_event_map; static void sc_event_map_init() { if (!state_change_event_map.size()) @@ -2113,14 +2176,22 @@ void Core::onStateChange(color_ostream &out, state_change_event event) switch (event) { case SC_CORE_INITIALIZED: - { - auto L = Lua::Core::State; - Lua::StackUnwinder top(L); - Lua::CallLuaModuleFunction(con, L, "helpdb", "refresh"); - Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); - } + { + loadModScriptPaths(out); + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(con, L, "helpdb", "refresh"); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); break; + } case SC_WORLD_LOADED: + { + loadModScriptPaths(out); + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); + // fallthrough + } case SC_WORLD_UNLOADED: case SC_MAP_LOADED: case SC_MAP_UNLOADED: @@ -2185,6 +2256,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event) if (event == SC_WORLD_UNLOADED) { Persistence::Internal::clear(); + loadModScriptPaths(out); + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); } } diff --git a/library/include/Core.h b/library/include/Core.h index 8232708b5..2e022d6ca 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -158,6 +158,7 @@ namespace DFHack bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false); bool addScriptPath(std::string path, bool search_before = false); + bool setModScriptPaths(const std::vector &mod_script_paths); bool removeScriptPath(std::string path); std::string findScript(std::string name); void getScriptPaths(std::vector *dest); @@ -239,7 +240,7 @@ namespace DFHack std::vector> allModules; DFHack::PluginManager * plug_mgr; - std::vector script_paths[2]; + std::vector script_paths[3]; std::mutex script_path_mutex; // hotkey-related stuff From e7f8fbb235eefdd7806befabaafefb9cd4fea991 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 08:53:29 -0800 Subject: [PATCH 0917/2222] apparaently the macro expansion has an ambiguous else --- library/Core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 1bca0f5ed..b5337b827 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -567,8 +567,9 @@ bool loadModScriptPaths(color_ostream &out) { mod_script_paths.emplace_back(installed_path); std::string slashless = *pathit; slashless.resize(slashless.size()-1); - if (0 == files.erase(slashless)) + if (0 == files.erase(slashless)) { WARN(script,out).print("script path not found: '%s'\n", pathit->c_str()); + } } for (auto & entry : files) { From 42b18d001bca119b44328fe70c52e2c60135307d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 11 Mar 2023 12:51:46 -0800 Subject: [PATCH 0918/2222] refresh overlay and helpdb on new world to pick up mod content --- library/lua/helpdb.lua | 10 ++++++++++ plugins/lua/overlay.lua | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index df5482aec..a7ac6226f 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -12,6 +12,8 @@ local TAG_DEFINITIONS = 'hack/docs/docs/Tags.txt' local SCRIPT_DOC_BEGIN = '[====[' local SCRIPT_DOC_END = ']====]' +local GLOBAL_KEY = 'HELPDB' + -- enums local ENTRY_TYPES = { BUILTIN='builtin', @@ -423,6 +425,14 @@ function refresh() ensure_db() end +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc ~= SC_WORLD_LOADED then + return + end + -- pick up widgets from active mods + refresh() +end + local function parse_blocks(text) local blocks = {} for line in text:gmatch('[^\n]*') do diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 3d476bf0d..ff8bf7c98 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -8,6 +8,7 @@ local widgets = require('gui.widgets') local OVERLAY_CONFIG_FILE = 'dfhack-config/overlay.json' local OVERLAY_WIDGETS_VAR = 'OVERLAY_WIDGETS' +local GLOBAL_KEY = 'OVERLAY' local DEFAULT_X_POS, DEFAULT_Y_POS = -2, -2 @@ -311,6 +312,14 @@ function reload() reposition_widgets() end +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc ~= SC_WORLD_LOADED then + return + end + -- pick up widgets from active mods + reload() +end + local function dump_widget_config(name, widget) local pos = overlay_config[name].pos print(('widget %s is positioned at x=%d, y=%d'):format(name, pos.x, pos.y)) From c3946247d689d9c13e1001f45798bd5a21ab074f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Mar 2023 17:05:54 -0700 Subject: [PATCH 0919/2222] update docs for Core --- docs/Core.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/Core.rst b/docs/Core.rst index 86e357b27..56597e9f2 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -235,10 +235,41 @@ root DF folder): #. :file:`dfhack-config/scripts` #. :file:`save/{world}/scripts` (only if a save is loaded) #. :file:`hack/scripts` +#. :file:`data/installed_mods/...` (see below) For example, if ``teleport`` is run, these folders are searched in order for ``teleport.lua``, and the first matching file is run. +Scripts in installed mods +......................... + +Script directories in installed mods are automatically added to the script path +according to the following rules: + +**If a world is not loaded**, then directories matching the pattern +``data/installed_mods/*/scripts_modinstalled/`` are added to the script path +in alphabetical order of the mod name. + +**If a world is loaded**, then the ``scripts_modactive`` directories of active +mods are also added to the script path according to the active mod load order, +and scripts in active mods take precedence over scripts in +``scripts_modinstalled`` in non-active mods. For example, the search paths for +mods might look like this:: + + activemod1/scripts_modactive + activemod1/scripts_modinstalled + activemod2/scripts_modactive + activemod2/scripts_modinstalled + inactivemod1/scripts_modinstalled + inactivemod2/scripts_modinstalled + +Not all mods will have script directories, of course, and those mods will not be +added to the script search path. Mods are re-scanned whenever a world is loaded +or unloaded. + +Custom script paths +................... + Script paths can be added by modifying :file:`dfhack-config/script-paths.txt`. Each line should start with one of these characters: From 57d6cab10fef346c2faeeac7a1fac5c57ad13fb9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 12 Mar 2023 17:14:02 -0700 Subject: [PATCH 0920/2222] wording --- docs/Core.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index 56597e9f2..b6cdb2066 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -248,7 +248,7 @@ according to the following rules: **If a world is not loaded**, then directories matching the pattern ``data/installed_mods/*/scripts_modinstalled/`` are added to the script path -in alphabetical order of the mod name. +in alphabetical order. **If a world is loaded**, then the ``scripts_modactive`` directories of active mods are also added to the script path according to the active mod load order, @@ -256,12 +256,14 @@ and scripts in active mods take precedence over scripts in ``scripts_modinstalled`` in non-active mods. For example, the search paths for mods might look like this:: - activemod1/scripts_modactive - activemod1/scripts_modinstalled - activemod2/scripts_modactive - activemod2/scripts_modinstalled + activemod_last_in_load_order/scripts_modactive + activemod_last_in_load_order/scripts_modinstalled + activemod_second_to_last_in_load_order/scripts_modactive + activemod_second_to_last_in_load_order/scripts_modinstalled + ... inactivemod1/scripts_modinstalled inactivemod2/scripts_modinstalled + ... Not all mods will have script directories, of course, and those mods will not be added to the script search path. Mods are re-scanned whenever a world is loaded From de576b558615a87d00245ce20dc4f87804273551 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 17 Mar 2023 21:11:18 -0700 Subject: [PATCH 0921/2222] start updating modding guide --- docs/guides/modding-guide.rst | 70 ++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 73d9407d6..43e066105 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -8,29 +8,73 @@ DFHack modding guide What is the difference between a script and a mod? -------------------------------------------------- -A script is a single file that can be run as a command in DFHack, like something -that modifies or displays game data on request. A mod is something you install -to get persistent behavioural changes in the game and/or add new content. Mods -can contain and use scripts in addition to (or instead of) modifications to the -DF game raws. - -DFHack scripts are written in Lua. If you don't already know Lua, there's a -great primer at `lua.org `__. +Well, sometimes there is no difference. A mod is anything you add to the game, +which can be graphics overrides, content in the raws, DFHack scripts, or both. +There are already resources out there for +`raws modding `__, so this +guide will focus more on scripts, both standalone and as an extension to +raws-based mods. A DFHack script is a Lua file that can be run as a command in +DFHack. Scripts can do pretty much anything, from displaying information to +enforcing new game mechanics. + +If you don't already know Lua, there's a great primer at +`lua.org `__. Why not just mod the raws? -------------------------- It depends on what you want to do. Some mods *are* better to do in just the -raws. You don't need DFHack to add a new race or modify attributes, for example. -However, DFHack scripts can do many things that you just can't do in the raws, -like make a creature that trails smoke. Some things *could* be done in the raws, -but writing a script is less hacky, easier to maintain, easier to extend, and is +raws. You don't need DFHack to add a new race or modify attributes. However, +DFHack scripts can do many things that you just can't do in the raws, like make +a creature that trails smoke. Some things *could* be done in the raws, but +writing a script is less hacky, easier to maintain, easier to extend, and is not prone to side-effects. A great example is adding a syndrome when a reaction is performed. If done in the raws, you have to create an exploding boulder to apply the syndrome. DFHack scripts can add the syndrome directly and with much more flexibility. In the end, complex mods will likely require a mix of raw modding and DFHack scripting. +The structure of a mod +---------------------- + +For reference, `Tachy Guns `__ is a +full mod that conforms to this guide. + +Create a folder for mod projects somewhere outside your Dwarf Fortress +installation directory (e.g. ``/path/to/mymods/``) and use your mod IDs as the +names for the mod folders within it. In the example below, we'll use a mod ID of +``example-mod``. I'm sure your mods will have more creative names! The +``example-mod`` mod will be developed in the ``/path/to/mymods/example-mod/`` +directory and has a basic structure that looks like this:: + + init.d/example-mod.lua + raw/objects/... + raw/scripts/example-mod.lua + raw/scripts/example-mod/... + README.md + +Let's go through that line by line. + +* A short (one-line) script in ``init.d/`` to initialise your + mod when a save is loaded. +* Modifications to the game raws (potentially with custom raw tokens) go in + ``raw/objects/``. +* A control script in ``scripts/`` that handles enabling and disabling your + mod. +* A subfolder for your mod under ``scripts/`` will contain all the internal + scripts and/or modules used by your mod. + +It is a good idea to use a version control system to organize changes to your +mod code. You can create a separate Git repository for each of your mods. The +``README.md`` file will be your mod help text when people browse to your online +repository. + +Unless you want to install your ``raw/`` folder into your DF game folder every +time you make a change to your scripts, you should add your development scripts +directory to your script paths in ``dfhack-config/script-paths.txt``:: + + +/path/to/mymods/example-mod/scripts/ + A mod-maker's development environment ------------------------------------- @@ -54,7 +98,7 @@ Then that directory will be searched when you run DFHack commands from inside the game. The ``+`` at the front of the path means to search that directory first, before any other script directory (like :file:`hack/scripts` or :file:`raw/scripts`). That way, your latest changes will always be used instead -of older copies that you may have installed in a DF directory. +of older copies that you may have in mods installed in the DF directory. For scripts with the same name, the `order of precedence ` will be: From e4579a4aa13f6da015b4e5eb7d1a04ee0f4996d4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 18:42:42 -0700 Subject: [PATCH 0922/2222] update modding guide --- docs/Core.rst | 3 +- docs/guides/modding-guide.rst | 309 ++++++++++++++++++---------------- 2 files changed, 162 insertions(+), 150 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index b6cdb2066..c06a413da 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -267,7 +267,8 @@ mods might look like this:: Not all mods will have script directories, of course, and those mods will not be added to the script search path. Mods are re-scanned whenever a world is loaded -or unloaded. +or unloaded. For more information on scripts and mods, check out the +`modding-guide`. Custom script paths ................... diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 43e066105..73758db6f 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -9,16 +9,16 @@ What is the difference between a script and a mod? -------------------------------------------------- Well, sometimes there is no difference. A mod is anything you add to the game, -which can be graphics overrides, content in the raws, DFHack scripts, or both. -There are already resources out there for +which can be graphics overrides, content in the raws, DFHack scripts, any, or +all. There are already resources out there for `raws modding `__, so this guide will focus more on scripts, both standalone and as an extension to -raws-based mods. A DFHack script is a Lua file that can be run as a command in -DFHack. Scripts can do pretty much anything, from displaying information to -enforcing new game mechanics. +raws-based mods. -If you don't already know Lua, there's a great primer at -`lua.org `__. +A DFHack script is a Lua file that can be run as a command in +DFHack. Scripts can do pretty much anything, from displaying information to +enforcing new game mechanics. If you don't already know Lua, there's a great +primer at `lua.org `__. Why not just mod the raws? -------------------------- @@ -26,13 +26,14 @@ Why not just mod the raws? It depends on what you want to do. Some mods *are* better to do in just the raws. You don't need DFHack to add a new race or modify attributes. However, DFHack scripts can do many things that you just can't do in the raws, like make -a creature that trails smoke. Some things *could* be done in the raws, but -writing a script is less hacky, easier to maintain, easier to extend, and is -not prone to side-effects. A great example is adding a syndrome when a reaction -is performed. If done in the raws, you have to create an exploding boulder to -apply the syndrome. DFHack scripts can add the syndrome directly and with much -more flexibility. In the end, complex mods will likely require a mix of raw -modding and DFHack scripting. +a creature that trails smoke or launch a unit into the air when they are hit +with a certain type of projectile. Some things *could* be done in the raws, but +a script is better (e.g. easier to maintain, easier to extend, and/or not prone +to side-effects). A great example is adding a syndrome when a reaction +is performed. If done in the raws, you have to create an exploding boulder as +an intermediary to apply the syndrome. DFHack scripts can add the syndrome +directly and with much more flexibility. In the end, complex mods will likely +require a mix of raw modding and DFHack scripting. The structure of a mod ---------------------- @@ -40,82 +41,102 @@ The structure of a mod For reference, `Tachy Guns `__ is a full mod that conforms to this guide. -Create a folder for mod projects somewhere outside your Dwarf Fortress -installation directory (e.g. ``/path/to/mymods/``) and use your mod IDs as the -names for the mod folders within it. In the example below, we'll use a mod ID of -``example-mod``. I'm sure your mods will have more creative names! The -``example-mod`` mod will be developed in the ``/path/to/mymods/example-mod/`` -directory and has a basic structure that looks like this:: +In the example below, we'll use a mod name of ``example-mod``. I'm sure your +mods will have more creative names! Mods have a basic structure that looks like +this:: - init.d/example-mod.lua - raw/objects/... - raw/scripts/example-mod.lua - raw/scripts/example-mod/... - README.md + info.txt + graphics/... + objects/... + scripts_modactive/example-mod.lua + scripts_modactive/internal/example-mod/... + scripts_modinstalled/... + README.md (optional) Let's go through that line by line. -* A short (one-line) script in ``init.d/`` to initialise your - mod when a save is loaded. -* Modifications to the game raws (potentially with custom raw tokens) go in - ``raw/objects/``. -* A control script in ``scripts/`` that handles enabling and disabling your - mod. -* A subfolder for your mod under ``scripts/`` will contain all the internal - scripts and/or modules used by your mod. +- The :file:`info.txt` file contains metadata about your mod that DF will + display in-game. You can read more about this file in the + `Official DF Modding Guide `__. +- Modifications to the game raws (potentially with custom raw tokens) go in + the :file:`graphics/` and :file:`objects/` folders. You can read more about + the files that go in these directories on the :wiki:`Modding` wiki page. +- A control script in :file:`scripts_modactive/` directory that handles + system-level event hooks (e.g. reloading state when a world is loaded), + registering `overlays `, and + `enabling/disabling ` your mod. You can put other + scripts in this directory as well if you want them to appear as runnable + DFHack commands when your mod is active for the current world. Lua modules + that your main scripts use, but which don't need to be directly runnable by + the player, should go in a subdirectory under + :file:`scripts_modactive/internal/` so they don't show up in the DFHack + `launcher ` command autocomplete lists. +- Scripts that you want to be available before a world is loaded (i.e. on the + DF title screen) or that you want to be runnable in any world, regardless + of whether your mod is active, should go in the + :file:`scripts_modinstalled/` folder. You can also have an :file:`internal/` + subfolder in here for private modules if you like. +- Finally, a :file:`README.md` file that has more information about your mod. + If you develop your mod using version control (recommended!), that + :file:`README.md` file can also serve as your git repository documentation. + +These files end up in a subdirectory under :file:`data/installed_mods/` when +the mod is selected as "active" for the first time. + +What if I just want to distribute a simple script? +-------------------------------------------------- + +If your mod is just a script with no raws modifications, things get a bit +simpler. All you need is:: -It is a good idea to use a version control system to organize changes to your -mod code. You can create a separate Git repository for each of your mods. The -``README.md`` file will be your mod help text when people browse to your online -repository. + info.txt + scripts_modinstalled/yourscript.lua + README.md (optional) -Unless you want to install your ``raw/`` folder into your DF game folder every -time you make a change to your scripts, you should add your development scripts -directory to your script paths in ``dfhack-config/script-paths.txt``:: +Adding your script to the :file:`scripts_modinstalled/` folder will allow +DFHack to find it and add your mod to the `script-paths`. Your script will be +runnable from the title screen and in any loaded world, regardless of whether +your mod is explicitly "active". - +/path/to/mymods/example-mod/scripts/ +Be sure to remind players to mark your mod as "active" at least once so it gets +installed to the :file:`data/installed_mods/` folder. They may have to create a +new world just so they can mark the mod as "active". This is true both for +players who copied the mod into the :file:`mods/` folder manually and for +players who subscribed via +`Steam Workshop `__. A mod-maker's development environment ------------------------------------- -While you're writing your mod, you need a place to store your in-development -scripts that will: +Create a folder for development somewhere outside your Dwarf Fortress +installation directory (e.g. ``/path/to/mymods/``). If you work on multiple +mods, you might want to make a subdirectory for each mod. -- be directly runnable by DFHack -- not get lost when you upgrade DFHack +If you have changes to the raws, you'll have to copy them into DF's ``data/ +installed_mods/`` folder to have them take effect, but you can set things up so +that scripts are run directly from your dev directory. This way, you can edit +your scripts and have the changes available in the game immediately: no +copying, no restarting. -The recommended approach is to create a directory somewhere outside of your DF -installation (let's call it "/path/to/own-scripts") and do all your script -development in there. +How does this magic work? Just add a line like this to your +``dfhack-config/script-paths.txt`` file:: -Inside your DF installation folder, there is a file named -:file:`dfhack-config/script-paths.txt`. If you add a line like this to that -file:: - - +/path/to/own-scripts + +/path/to/mymods/example-mod/scripts_modinstalled Then that directory will be searched when you run DFHack commands from inside the game. The ``+`` at the front of the path means to search that directory -first, before any other script directory (like :file:`hack/scripts` or -:file:`raw/scripts`). That way, your latest changes will always be used instead -of older copies that you may have in mods installed in the DF directory. - -For scripts with the same name, the `order of precedence ` will -be: - -1. ``own-scripts/`` -2. ``dfhack-config/scripts/`` -3. ``save/*/scripts/`` -4. ``hack/scripts/`` +first, before any other script directory (like :file:`hack/scripts` or other +versions of your mod in ``data/installed_mods/``). The structure of the game ------------------------- -"The game" is in the global variable `df `. The game's memory can be -found in ``df.global``, containing things like the list of all items, whether to -reindex pathfinding, et cetera. Also relevant to us in ``df`` are the various -types found in the game, e.g. ``df.pronoun_type`` which we will be using in this -guide. We'll explore more of the game structures below. +"The game" is in the global variable `df `. Most of the information +relevant to a script is found in ``df.global.world``, which contains things +like the list of all items, whether to reindex pathfinding, et cetera. Also +relevant to us are the various data types found in the game, e.g. +``df.pronoun_type`` which we will be using in this guide. We'll explore more of +the game structures below. Your first script ----------------- @@ -127,8 +148,8 @@ First line, we get the unit:: local unit = dfhack.gui.getSelectedUnit() -If no unit is selected, an error message will be printed (which can be silenced -by passing ``true`` to ``getSelectedUnit``) and ``unit`` will be ``nil``. +If no unit is selected, ``unit`` will be ``nil`` and an error message will be +printed (which can be silenced by passing ``true`` to ``getSelectedUnit``). If ``unit`` is ``nil``, we don't want the script to run anymore:: @@ -138,33 +159,32 @@ If ``unit`` is ``nil``, we don't want the script to run anymore:: Now, the field ``sex`` in a unit is an integer, but each integer corresponds to a string value ("it", "she", or "he"). We get this value by indexing the -bidirectional map ``df.pronoun_type``. Indexing the other way, incidentally, -with one of the strings, will yield its corresponding number. So:: +bidirectional map ``df.pronoun_type``. Indexing the other way, with one of the +strings, will yield its corresponding number. So:: local pronounTypeString = df.pronoun_type[unit.sex] print(pronounTypeString) -Simple. Save this as a Lua file in your own scripts directory and run it as -shown before when a unit is selected in the Dwarf Fortress UI. +Simple. Save this as a Lua file in your own scripts directory and run it from +`gui/launcher` when a unit is selected in the Dwarf Fortress UI. -Exploring DF structures ------------------------ +Exploring DF state +------------------ So how could you have known about the field and type we just used? Well, there are two main tools for discovering the various fields in the game's data structures. The first is the ``df-structures`` `repository `__ that contains XML files -describing the contents of the game's structures. These are complete, but +describing the layouts of the game's structures. These are complete, but difficult to read (for a human). The second option is the `gui/gm-editor` -script, an interactive data explorer. You can run the script while objects like -units are selected to view the data within them. You can also run -``gui/gm-editor scr`` to view the data for the current screen. Press :kbd:`?` -while the script is active to view help. +interface, an interactive data explorer. You can run the script while objects +like units are selected to view the data within them. Press :kbd:`?` while the +script is active to view help. Familiarising yourself with the many structs of the game will help with ideas immensely, and you can always ask for help in the `right places `. -Detecting triggers +Reacting to events ------------------ The common method for injecting new behaviour into the game is to define a @@ -174,7 +194,7 @@ provides two libraries for this, ``repeat-util`` and `eventful `. frames (paused or unpaused), ticks (unpaused), in-game days, months, or years. If you need to be aware the instant something happens, you'll need to run a check once a tick. Be careful not to do this gratuitously, though, since -running that often can slow down the game! +running callbacks too often can slow down the game! ``eventful``, on the other hand, is much more performance-friendly since it will only call your callback when a relevant event happens, like a reaction or job @@ -371,7 +391,8 @@ Then, let's make a ``repeat-util`` callback for once a tick:: repeatUtil.scheduleEvery(modId, 1, "ticks", function() Let's iterate over every active unit, and for every unit, iterate over their -worn items to calculate how much we are going to take from their on-foot movement timers:: +worn items to calculate how much we are going to take from their on-foot +movement timers:: for _, unit in ipairs(df.global.world.units.active) do local amount = 0 @@ -385,82 +406,78 @@ worn items to calculate how much we are going to take from their on-foot movemen end -- Subtract amount from on-foot movement timers if not on ground if not unit.flags1.on_ground then - dfhack.units.subtractActionTimers(unit, amount, df.unit_action_type_group.MovementFeet) + dfhack.units.subtractActionTimers(unit, amount, + df.unit_action_type_group.MovementFeet) end end -The structure of a full mod ---------------------------- - -For reference, `Tachy Guns `__ is a -full mod that conforms to this guide. - -Create a folder for mod projects somewhere outside your Dwarf Fortress -installation directory (e.g. ``/path/to/mymods/``) and use your mod IDs as the -names for the mod folders within it. In the example below, we'll use a mod ID of -``example-mod``. I'm sure your mods will have more creative names! The -``example-mod`` mod will be developed in the ``/path/to/mymods/example-mod/`` -directory and has a basic structure that looks like this:: - - init.d/example-mod.lua - raw/objects/... - raw/scripts/example-mod.lua - raw/scripts/example-mod/... - README.md +Putting it all together +----------------------- -Let's go through that line by line. +Ok, you're all set up! Now, let's take a look at an example +``scripts_modinstalled/example-mod.lua`` file:: -* A short (one-line) script in ``init.d/`` to initialise your - mod when a save is loaded. -* Modifications to the game raws (potentially with custom raw tokens) go in - ``raw/objects/``. -* A control script in ``scripts/`` that handles enabling and disabling your - mod. -* A subfolder for your mod under ``scripts/`` will contain all the internal - scripts and/or modules used by your mod. + -- main file for example-mod -It is a good idea to use a version control system to organize changes to your -mod code. You can create a separate Git repository for each of your mods. The -``README.md`` file will be your mod help text when people browse to your online -repository. + -- these lines indicate that the script supports the "enable" API so you + -- can start it by running "enable example-mod" and stop it by running + -- "disable example-mod" + --@module = true + --@enable = true -Unless you want to install your ``raw/`` folder into your DF game folder every -time you make a change to your scripts, you should add your development scripts -directory to your script paths in ``dfhack-config/script-paths.txt``:: + -- this is the help text that will appear in `help` and `gui/launcher` + -- Documentation on how to format docs here: + -- see possible tags here: https://docs.dfhack.org/en/latest/docs/Tags.html + --[====[ + example-mod + =========== - +/path/to/mymods/example-mod/scripts/ + Tags: fort | gameplay -Ok, you're all set up! Now, let's take a look at an example -``scripts/example-mod.lua`` file:: + Short one-sentence description ... - -- main setup and teardown for example-mod - -- this next line indicates that the script supports the "enable" - -- API so you can start it by running "enable example-mod" and stop - -- it by running "disable example-mod" - --@ enable = true + Longer description ... - local usage = [[ Usage ----- enable example-mod disable example-mod - ]] + ]====] + local repeatUtil = require('repeat-util') local eventful = require('plugins.eventful') -- you can reference global values or functions declared in any of -- your internal scripts - local moduleA = reqscript('example-mod/module-a') - local moduleB = reqscript('example-mod/module-b') - local moduleC = reqscript('example-mod/module-c') - local moduleD = reqscript('example-mod/module-d') + local moduleA = reqscript('internal/example-mod/module-a') + local moduleB = reqscript('internal/example-mod/module-b') + local moduleC = reqscript('internal/example-mod/module-c') + local moduleD = reqscript('internal/example-mod/module-d') + + local GLOBAL_KEY = 'example-mod' enabled = enabled or false - local modId = 'example-mod' + + function isEnabled() + return enabled + end + + dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc == SC_MAP_UNLOADED then + dfhack.run_command('disable', 'example-mod') + return + end + + if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then + return + end + + dfhack.run_command('enable', 'example-mod') + end if not dfhack_flags.enable then - print(usage) + print(dfhack.script_help()) print() print(('Example mod is currently '):format( enabled and 'enabled' or 'disabled')) @@ -516,23 +533,17 @@ Ok, you're all set up! Now, let's take a look at an example enabled = false end -You can call ``enable example-mod`` and ``disable example-mod`` yourself while -developing, but for end users you can start your mod automatically from -``init.d/example-mod.lua``:: - - dfhack.run_command('enable example-mod') - -Inside ``raw/scripts/example-mod/module-a.lua`` you could have code like this:: +Inside ``scripts_modinstalled/internal/example-mod/module-a.lua`` you could +have code like this:: --@ module = true - -- The above line is required for reqscript to work function onLoad() -- global variables are exported -- do initialization here end - -- this is an internal function: local functions/variables - -- are not exported + -- this is a local function: local functions/variables + -- are not accessible to other scripts. local function usedByOnTick(unit) -- ... end @@ -543,6 +554,6 @@ Inside ``raw/scripts/example-mod/module-a.lua`` you could have code like this:: end end -The `reqscript ` function reloads scripts that have changed, so you can modify -your scripts while DF is running and just disable/enable your mod to load the -changes into your ongoing game! +The `reqscript ` function reloads scripts that have changed, so you +can modify your scripts while DF is running and just disable/enable your mod to +load the changes into your ongoing game! From 7cb9d3fe8cf4e1ca209e094ba7317aaba9b297d0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 24 Mar 2023 18:47:18 -0700 Subject: [PATCH 0923/2222] formatting --- docs/guides/modding-guide.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 73758db6f..cf61c801c 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -419,15 +419,15 @@ Ok, you're all set up! Now, let's take a look at an example -- main file for example-mod - -- these lines indicate that the script supports the "enable" API so you - -- can start it by running "enable example-mod" and stop it by running - -- "disable example-mod" + -- these lines indicate that the script supports the "enable" + -- API so you can start it by running "enable example-mod" and + -- stop it by running "disable example-mod" --@module = true --@enable = true - -- this is the help text that will appear in `help` and `gui/launcher` - -- Documentation on how to format docs here: - -- see possible tags here: https://docs.dfhack.org/en/latest/docs/Tags.html + -- this is the help text that will appear in `help` and + -- `gui/launcher`. see possible tags here: + -- https://docs.dfhack.org/en/latest/docs/Tags.html --[====[ example-mod =========== From 6a3e61519a1fd4af6a1802177e8090e50223787c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 25 Mar 2023 01:55:35 -0700 Subject: [PATCH 0924/2222] remove reference to Tachy Guns --- docs/guides/modding-guide.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index cf61c801c..5cabf0856 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -38,9 +38,6 @@ require a mix of raw modding and DFHack scripting. The structure of a mod ---------------------- -For reference, `Tachy Guns `__ is a -full mod that conforms to this guide. - In the example below, we'll use a mod name of ``example-mod``. I'm sure your mods will have more creative names! Mods have a basic structure that looks like this:: From 0ba4225d85924a86c97cad7938fdbfe95827f590 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 25 Mar 2023 15:19:24 -0700 Subject: [PATCH 0925/2222] support showing a different tile on hover for Labels --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 12 +++++++----- library/lua/gui/widgets.lua | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b9c31f146..ced0f961e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -64,6 +64,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- ``widget.Label``: tokens can now specify a ``htile`` property to indicate the tile that should be shown when the Label is hovered over with the mouse ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9f2386660..862c5bccc 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4731,10 +4731,12 @@ containing newlines, or a table with the following possible fields: Specifies the number of character positions to advance on the line before rendering the token. -* ``token.tile = pen`` +* ``token.tile``, ``token.htile`` Specifies a pen or texture index (or a function that returns a pen or texture - index) to paint as one tile before the main part of the token. + index) to paint as one tile before the main part of the token. If ``htile`` + is specified, that is used instead of ``tile`` when the Label is hovered over + with the mouse. * ``token.width = ...`` @@ -4762,10 +4764,10 @@ containing newlines, or a table with the following possible fields: Same as the attributes of the label itself, but applies only to the token. -* ``token.pen``, ``token.dpen`` +* ``token.pen``, ``token.dpen``, ``token.hpen`` - Specify the pen and disabled pen to be used for the token's text. - The field may be either the pen itself, or a callback that returns it. + Specify the pen, disabled pen, and hover pen to be used for the token's text. + The fields may be either the pen itself, or a callback that returns it. * ``token.on_activate`` diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index b86e31710..b5497f0d0 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1121,10 +1121,10 @@ function render_text(obj,dc,x0,y0,pen,dpen,disabled,hpen,hovered) end end - if token.tile then + if token.tile or (hovered and token.htile) then x = x + 1 if dc then - local tile = getval(token.tile) + local tile = hovered and getval(token.htile or token.tile) or getval(token.tile) local tile_pen = tonumber(tile) and to_pen{tile=tile} or tile dc:char(nil, tile_pen) if token.width then From 28eeb95eedca903efeb35024ffd8fa4d5ebb8254 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 25 Mar 2023 22:28:21 +0000 Subject: [PATCH 0926/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f637dc4bf..2568b79c3 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f637dc4bf18a7ca4968f45e79790893c6d22c3f5 +Subproject commit 2568b79c3a74128b649bab8fdd20ce67cd88790f From b3a20e771b8b32d7015f7286673514329fa1b6d5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 25 Mar 2023 15:53:42 -0700 Subject: [PATCH 0927/2222] remove lua's ability to load binary chunks to protect against malware attacks --- depends/lua/src/ldo.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/depends/lua/src/ldo.c b/depends/lua/src/ldo.c index 316e45c8f..65158df0b 100644 --- a/depends/lua/src/ldo.c +++ b/depends/lua/src/ldo.c @@ -767,14 +767,14 @@ static void f_parser (lua_State *L, void *ud) { LClosure *cl; struct SParser *p = cast(struct SParser *, ud); int c = zgetc(p->z); /* read first character */ - if (c == LUA_SIGNATURE[0]) { - checkmode(L, p->mode, "binary"); - cl = luaU_undump(L, p->z, p->name); - } - else { + // if (c == LUA_SIGNATURE[0]) { + // checkmode(L, p->mode, "binary"); + // cl = luaU_undump(L, p->z, p->name); + // } + // else { checkmode(L, p->mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); - } + // } lua_assert(cl->nupvalues == cl->p->sizeupvalues); luaF_initupvals(L, cl); } From e2d4d45c023438af92e2abbaa00bb2c206e9ac4c Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 27 Mar 2023 04:44:38 +0000 Subject: [PATCH 0928/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 2568b79c3..bb3dfc0d3 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 2568b79c3a74128b649bab8fdd20ce67cd88790f +Subproject commit bb3dfc0d3e2bdebc4ba6fd814371662457b18917 From 4a173aac997c05b7921ccd96ee0446250c82b318 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:07:40 -0500 Subject: [PATCH 0929/2222] tailor: fix #3093, #3103 I significantly restructured the assignment algorithm to eliminate the overproduction issues in #3093 #3103 is addressed by excluding units that don't have the caste-level EQUIPS flag closes #3093 closes #3103 --- docs/changelog.txt | 2 +- plugins/tailor.cpp | 151 ++++++++++++++++++++++----------------------- 2 files changed, 75 insertions(+), 78 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ced0f961e..619a538e3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,7 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: you can no longer designate constructions on tiles with magma or deep water - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) -- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, and properly tracks material requirements for already queued orders +- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, properly tracks material requirements for already queued orders, skips units who can't wear clothes, and hopefully won't over-order items anymore ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 3ba6744bb..bb84c438c 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -144,13 +144,15 @@ static struct BadFlags { } badFlags; class Tailor { -private: - std::map, int> available; // key is item type & size - std::map, int> needed; // same - std::map, int> queued; // same +private: std::map sizes; // this maps body size to races - std::map, int> orders; // key is item type, item subtype, size + + std::map, int> available; + + std::map, int> needed; + + std::map, int> orders; std::map supply; std::map reserves; @@ -169,50 +171,34 @@ public: { available.clear(); needed.clear(); - queued.clear(); sizes.clear(); - orders.clear(); supply.clear(); + orders.clear(); } void scan_clothing() { - if (!inventory_sanity_checking) + for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "nontattered clothing" { - for (auto i : world->items.other[df::items_other_id::ANY_GENERIC37]) // GENERIC37 is "clothing" + if (i->flags.whole & badFlags.whole) { - if (i->flags.whole & badFlags.whole) - continue; - if (i->getWear() >= 1) - continue; - df::item_type t = i->getType(); - int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - - available[std::make_pair(t, size)] += 1; + continue; } - } - else - { - auto& l = world->items.other[df::items_other_id::ANY_GENERIC37]; - - for (auto i : world->items.other[df::items_other_id::IN_PLAY]) - { - if (i->flags.whole & badFlags.whole) - continue; - if (!i->isClothing()) - continue; - if (std::find(std::begin(l), std::end(l), i) == std::end(l)) - { - DEBUG(cycle).print("tailor: clothing item %d missing from GENERIC37 list\n", i->id); - } - if (i->getWear() >= 1) - continue; - df::item_type t = i->getType(); - int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; + if (i->getWear() >= 1) + continue; + df::item_type t = i->getType(); + int size = world->raws.creatures.all[i->getMakerRace()]->adultsize; - available[std::make_pair(t, size)] += 1; - } + available[std::make_pair(t, size)] += 1; + } + for (auto& i : available) + { + df::item_type t; + int size; + std::tie(t, size) = i.first; + DEBUG(cycle).print("tailor: %d %s of size %d found\n", + i.second, ENUM_KEY_STR(item_type, t).c_str(), size); } } @@ -273,14 +259,13 @@ public: if (!Units::isOwnCiv(u) || !Units::isOwnGroup(u) || !Units::isActive(u) || - Units::isBaby(u)) - continue; // skip units we don't control + Units::isBaby(u) || + !Units::casteFlagSet(u->race, u->caste, df::enums::caste_raw_flags::EQUIPS)) + continue; // skip units we don't control or that can't wear clothes std::set wearing; - wearing.clear(); - + std::set ordered; std::deque worn; - worn.clear(); for (auto inv : u->inventory) { @@ -295,33 +280,37 @@ public: int usize = world->raws.creatures.all[u->race]->adultsize; sizes[usize] = u->race; - for (auto ty : std::set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) - { - if (wearing.count(ty) == 0) - { - TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n", - ENUM_KEY_STR(item_type, ty).c_str(), - usize, - Translation::TranslateName(&u->name, false).c_str()); - needed[std::make_pair(ty, usize)] += 1; - } - } - for (auto w : worn) { auto ty = w->getType(); - auto oo = itemTypeMap.find(ty); - if (oo == itemTypeMap.end()) - { - continue; - } - const df::job_type o = oo->second; int isize = world->raws.creatures.all[w->getMakerRace()]->adultsize; std::string description; w->getItemDescription(&description, 0); - if (available[std::make_pair(ty, usize)] > 0) + bool allocated = false; + + if (wearing.count(ty) == 0) + { + if (available[std::make_pair(ty, usize)] > 0) + { + available[std::make_pair(ty, usize)] -= 1; + DEBUG(cycle).print("tailor: allocating a %s (size %d) to %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), usize, + Translation::TranslateName(&u->name, false).c_str()); + wearing.insert(ty); + } + else if (ordered.count(ty) == 0) + { + DEBUG(cycle).print ("tailor: %s (size %d) worn by %s (size %d) needs replacement, but none available\n", + description.c_str(), isize, + Translation::TranslateName(&u->name, false).c_str(), usize); + needed[std::make_pair(ty, usize)] += 1; + ordered.insert(ty); + } + } + + if (wearing.count(ty) > 0) { if (w->flags.bits.owned) { @@ -335,23 +324,21 @@ public: ); } - if (wearing.count(ty) == 0) - { - DEBUG(cycle).print("tailor: allocating a %s (size %d) to %s\n", - ENUM_KEY_STR(item_type, ty).c_str(), usize, - Translation::TranslateName(&u->name, false).c_str()); - available[std::make_pair(ty, usize)] -= 1; - } - if (w->getWear() > 1) w->flags.bits.dump = true; } - else + + } + + for (auto ty : std::set{ df::item_type::ARMOR, df::item_type::PANTS, df::item_type::SHOES }) + { + if (wearing.count(ty) == 0 && ordered.count(ty) == 0) { - DEBUG(cycle).print ("tailor: %s (size %d) worn by %s (size %d) needs replacement, but none available\n", - description.c_str(), isize, - Translation::TranslateName(&u->name, false).c_str(), usize); - orders[std::make_tuple(o, w->getSubtype(), usize)] += 1; + TRACE(cycle).print("tailor: one %s of size %d needed to cover %s\n", + ENUM_KEY_STR(item_type, ty).c_str(), + usize, + Translation::TranslateName(&u->name, false).c_str()); + needed[std::make_pair(ty, usize)] += 1; } } } @@ -367,6 +354,9 @@ public: int size = a.first.second; int count = a.second; + if (count <= 0) + continue; + int sub = 0; std::vector v; @@ -432,7 +422,14 @@ public: int size = world->raws.creatures.all[race]->adultsize; - orders[std::make_tuple(o->job_type, sub, size)] -= o->amount_left; + + auto tt = jobTypeMap.find(o->job_type); + if (tt == jobTypeMap.end()) + { + continue; + } + + needed[std::make_pair(tt->second, size)] -= o->amount_left; TRACE(cycle).print("tailor: existing order for %d %s of size %d detected\n", o->amount_left, ENUM_KEY_STR(job_type, o->job_type).c_str(), @@ -576,8 +573,8 @@ public: scan_clothing(); scan_materials(); scan_replacements(); - create_orders(); scan_existing_orders(); + create_orders(); return place_orders(); } From 0f5456c45b3324e9eb8135908cf425e83fe5481e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:31:47 -0500 Subject: [PATCH 0930/2222] tidy up after self remove a couple of no-longer-used variables that i missed last time --- plugins/tailor.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index bb84c438c..286bd9c0d 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -288,8 +288,6 @@ public: std::string description; w->getItemDescription(&description, 0); - bool allocated = false; - if (wearing.count(ty) == 0) { if (available[std::make_pair(ty, usize)] > 0) @@ -404,7 +402,6 @@ public: if (f == jobTypeMap.end()) continue; - auto sub = o->item_subtype; int race = o->hist_figure_id; for (auto& m : all_materials) From f3862e3eecacd5af65d0799c49dcee6c3a964980 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 27 Mar 2023 00:44:03 -0500 Subject: [PATCH 0931/2222] tailor: gatekeep debugging code --- docs/changelog.txt | 6 +++++- plugins/tailor.cpp | 15 +++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 619a538e3..65194dcfa 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,7 +44,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: you can no longer designate constructions on tiles with magma or deep water - `buildingplan`: fixed material filter getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) -- `tailor`: now properly discriminates between dyed and undyed cloth, no longer defaults to using adamantine, properly tracks material requirements for already queued orders, skips units who can't wear clothes, and hopefully won't over-order items anymore +- `tailor`: properly discriminates between dyed and undyed cloth +- `tailor`: no longer defaults to using adamantine +- `tailor`: properly tracks material requirements for already queued orders +- `tailor`: skips units who can't wear clothes +- `tailor`: hopefully won't over-order items any more ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building diff --git a/plugins/tailor.cpp b/plugins/tailor.cpp index 286bd9c0d..2b44d11ae 100644 --- a/plugins/tailor.cpp +++ b/plugins/tailor.cpp @@ -192,13 +192,16 @@ public: available[std::make_pair(t, size)] += 1; } - for (auto& i : available) + if (DBG_NAME(cycle).isEnabled(DebugCategory::LDEBUG)) { - df::item_type t; - int size; - std::tie(t, size) = i.first; - DEBUG(cycle).print("tailor: %d %s of size %d found\n", - i.second, ENUM_KEY_STR(item_type, t).c_str(), size); + for (auto& i : available) + { + df::item_type t; + int size; + std::tie(t, size) = i.first; + DEBUG(cycle).print("tailor: %d %s of size %d found\n", + i.second, ENUM_KEY_STR(item_type, t).c_str(), size); + } } } From 8da7e216a4a2dac1576ff68045c3fd79e34fcc9f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Mar 2023 02:24:56 -0700 Subject: [PATCH 0932/2222] buildingplan - suspendmanager integration --- docs/changelog.txt | 1 + docs/plugins/buildingplan.rst | 5 ++- plugins/buildingplan/buildingplan.cpp | 39 ++++++++++++++------- plugins/buildingplan/buildingplan.h | 2 +- plugins/buildingplan/buildingplan_cycle.cpp | 19 +++++----- plugins/lua/buildingplan.lua | 5 +++ 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 65194dcfa..df153d43e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,6 +52,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building +- `buildingplan`: if `suspendmanager` is running, then planned buildings will be left suspended when their items are all attached. `suspendmanager` will unsuspsend them for construction when it is safe to do so. - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from - `stockpiles`: support partial application of a saved config based on dynamic filtering - `stockpiles`: additive and subtractive modes when applying a second stockpile configuration on top of a first diff --git a/docs/plugins/buildingplan.rst b/docs/plugins/buildingplan.rst index 71951f386..9cc7e68a2 100644 --- a/docs/plugins/buildingplan.rst +++ b/docs/plugins/buildingplan.rst @@ -20,7 +20,10 @@ building. Once all items are attached, the construction job will be unsuspended and a dwarf will come and build the building. If you have the `unsuspend` overlay enabled (it is enabled by default), then buildingplan-suspended buildings will appear with a ``P`` marker on the main map, as opposed to the -usual ``x`` marker for "regular" suspended buildings. +usual ``x`` marker for "regular" suspended buildings. If you have +`suspendmanager` running, then buildings will be left suspended when their +items are all attached and ``suspendmanager`` will unsuspend them for +construction when it is safe to do so. If you want to impose restrictions on which items are chosen for the buildings, buildingplan has full support for quality and material filters (see `below diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 101349fde..11e08c2a0 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -72,7 +72,7 @@ static Tasks tasks; void PlannedBuilding::remove(color_ostream &out) { DEBUG(status,out).print("removing persistent data for building %d\n", id); World::DeletePersistentData(bld_config); - if (planned_buildings.count(id) > 0) + if (planned_buildings.count(id)) planned_buildings.erase(id); } @@ -212,9 +212,9 @@ static DefaultItemFilters & get_item_filters(color_ostream &out, const BuildingT static command_result do_command(color_ostream &out, vector ¶meters); void buildingplan_cycle(color_ostream &out, Tasks &tasks, - unordered_map &planned_buildings); + unordered_map &planned_buildings, bool unsuspend_on_finalize); -static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb); +static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb, bool unsuspend_on_finalize); DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { DEBUG(status,out).print("initializing %s\n", plugin_name); @@ -295,6 +295,16 @@ static int16_t get_subtype(df::building *bld) { return subtype; } +static bool is_suspendmanager_enabled(color_ostream &out) { + bool suspendmanager_enabled = false; + call_buildingplan_lua(&out, "is_suspendmanager_enabled", 0, 1, + Lua::DEFAULT_LUA_LAMBDA, + [&](lua_State *L){ + suspendmanager_enabled = lua_toboolean(L, -1); + }); + return suspendmanager_enabled; +} + DFhackCExport command_result plugin_load_data (color_ostream &out) { cycle_timestamp = 0; config = World::GetPersistentData(CONFIG_KEY); @@ -320,6 +330,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { vector building_configs; World::GetPersistentData(&building_configs, BLD_CONFIG_KEY); const size_t num_building_configs = building_configs.size(); + bool unsuspend_on_finalize = !is_suspendmanager_enabled(out); for (size_t idx = 0; idx < num_building_configs; ++idx) { PlannedBuilding pb(out, building_configs[idx]); df::building *bld = df::building::find(pb.id); @@ -334,7 +345,7 @@ DFhackCExport command_result plugin_load_data (color_ostream &out) { pb.remove(out); continue; } - registerPlannedBuilding(out, pb); + registerPlannedBuilding(out, pb, unsuspend_on_finalize); } return CR_OK; @@ -347,7 +358,8 @@ static void do_cycle(color_ostream &out) { cycle_timestamp = world->frame_counter; cycle_requested = false; - buildingplan_cycle(out, tasks, planned_buildings); + bool unsuspend_on_finalize = !is_suspendmanager_enabled(out); + buildingplan_cycle(out, tasks, planned_buildings, unsuspend_on_finalize); call_buildingplan_lua(&out, "signal_reset"); } @@ -469,7 +481,7 @@ vector getVectorIds(color_ostream &out, const df::job_it return ret; } -static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { +static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb, bool unsuspend_on_finalize) { df::building * bld = pb.getBuildingIfValidOrRemoveIfNot(out); if (!bld) return false; @@ -479,10 +491,15 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { return false; } + // suspend jobs + for (auto job : bld->jobs) + job->flags.bits.suspend = true; + auto job_items = bld->jobs[0]->job_items; if (isJobReady(out, job_items)) { // all items are already attached - finalizeBuilding(out, bld); + finalizeBuilding(out, bld, unsuspend_on_finalize); + pb.remove(out); return true; } @@ -508,10 +525,6 @@ static bool registerPlannedBuilding(color_ostream &out, PlannedBuilding & pb) { } } - // suspend jobs - for (auto job : bld->jobs) - job->flags.bits.suspend = true; - // add the planned buildings to our register planned_buildings.emplace(bld->id, pb); @@ -627,7 +640,9 @@ static bool addPlannedBuilding(color_ostream &out, df::building *bld) { BuildingTypeKey key(bld->getType(), subtype, bld->getCustomType()); PlannedBuilding pb(out, bld, get_heat_safety_filter(key), get_item_filters(out, key)); - return registerPlannedBuilding(out, pb); + + bool unsuspend_on_finalize = !is_suspendmanager_enabled(out); + return registerPlannedBuilding(out, pb, unsuspend_on_finalize); } static void doCycle(color_ostream &out) { diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 756a81f83..26aa77fbc 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -52,4 +52,4 @@ bool itemPassesScreen(df::item * item); df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat); bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter, const std::set &special); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); -void finalizeBuilding(DFHack::color_ostream &out, df::building *bld); +void finalizeBuilding(DFHack::color_ostream &out, df::building *bld, bool unsuspend_on_finalize); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 803f1f130..3213e741d 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -111,7 +111,7 @@ static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b) { // now all at 0, so there is no risk of having extra items attached. we don't // remove them to keep the "finalize with buildingplan active" path as similar // as possible to the "finalize with buildingplan disabled" path. -void finalizeBuilding(color_ostream &out, df::building *bld) { +void finalizeBuilding(color_ostream &out, df::building *bld, bool unsuspend_on_finalize) { DEBUG(cycle,out).print("finalizing building %d\n", bld->id); auto job = bld->jobs[0]; @@ -143,8 +143,10 @@ void finalizeBuilding(color_ostream &out, df::building *bld) { } // we're good to go! - job->flags.bits.suspend = false; - Job::checkBuildingsNow(); + if (unsuspend_on_finalize) { + job->flags.bits.suspend = false; + Job::checkBuildingsNow(); + } } static df::building * popInvalidTasks(color_ostream &out, Bucket &task_queue, @@ -181,7 +183,8 @@ static bool isAccessibleFrom(color_ostream &out, df::item *item, df::job *job) { static void doVector(color_ostream &out, df::job_item_vector_id vector_id, map &buckets, - unordered_map &planned_buildings) { + unordered_map &planned_buildings, + bool unsuspend_on_finalize) { auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); auto item_vector = df::global::world->items.other[other_id]; DEBUG(cycle,out).print("matching %zu item(s) in vector %s against %zu filter bucket(s)\n", @@ -239,7 +242,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, --jitems[filter_idx]->quantity; task_queue.pop_front(); if (isJobReady(out, jitems)) { - finalizeBuilding(out, bld); + finalizeBuilding(out, bld, unsuspend_on_finalize); planned_buildings.at(id).remove(out); } if (task_queue.empty()) { @@ -274,7 +277,7 @@ struct VectorsToScanLast { }; void buildingplan_cycle(color_ostream &out, Tasks &tasks, - unordered_map &planned_buildings) { + unordered_map &planned_buildings, bool unsuspend_on_finalize) { static const VectorsToScanLast vectors_to_scan_last; DEBUG(cycle,out).print( @@ -292,7 +295,7 @@ void buildingplan_cycle(color_ostream &out, Tasks &tasks, } auto & buckets = it->second; - doVector(out, vector_id, buckets, planned_buildings); + doVector(out, vector_id, buckets, planned_buildings, unsuspend_on_finalize); if (buckets.empty()) { DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), @@ -306,7 +309,7 @@ void buildingplan_cycle(color_ostream &out, Tasks &tasks, if (tasks.count(vector_id) == 0) continue; auto & buckets = tasks[vector_id]; - doVector(out, vector_id, buckets, planned_buildings); + doVector(out, vector_id, buckets, planned_buildings, unsuspend_on_finalize); if (buckets.empty()) { DEBUG(cycle,out).print("removing empty vector: %s; %zu vector(s) left\n", ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(), diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 077470409..a33c1684f 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -51,6 +51,11 @@ function parse_commandline(...) return true end +function is_suspendmanager_enabled() + local ok, sm = pcall(reqscript, 'suspendmanager') + return ok and sm.isEnabled() +end + function get_num_filters(btype, subtype, custom) local filters = dfhack.buildings.getFiltersByType({}, btype, subtype, custom) return filters and #filters or 0 From 63772fd808107c37627ae5d5946a25baa7173d64 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Mar 2023 03:51:45 -0700 Subject: [PATCH 0933/2222] ensure rclicks don't bleed through for dialogs --- library/lua/gui/dialogs.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 3b2470114..1b7858416 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -4,9 +4,6 @@ local _ENV = mkmodule('gui.dialogs') local gui = require('gui') local widgets = require('gui.widgets') -local utils = require('utils') - -local dscreen = dfhack.screen MessageBox = defclass(MessageBox, gui.FramedScreen) @@ -66,6 +63,7 @@ function MessageBox:onInput(keys) elseif (keys.LEAVESCREEN or keys._MOUSE_R_DOWN) and self.on_cancel then self.on_cancel() end + gui.markMouseClicksHandled(keys) return true end return self:inputToSubviews(keys) @@ -135,6 +133,7 @@ function InputBox:onInput(keys) if self.on_cancel then self.on_cancel() end + gui.markMouseClicksHandled(keys) return true end return self:inputToSubviews(keys) @@ -218,9 +217,9 @@ end function ListBox:onRenderFrame(dc,rect) ListBox.super.onRenderFrame(self,dc,rect) - --if self.select2_hint then - -- dc:seek(rect.x1+2,rect.y2):key('SEC_SELECT'):string(': '..self.select2_hint,COLOR_GREY) - --end + if self.select2_hint then + dc:seek(rect.x1+2,rect.y2):string('Shift-Click', COLOR_LIGHTGREEN):string(': '..self.select2_hint, COLOR_GREY) + end end function ListBox:getWantedFrameSize() @@ -236,6 +235,7 @@ function ListBox:onInput(keys) if self.on_cancel then self.on_cancel() end + gui.markMouseClicksHandled(keys) return true end return self:inputToSubviews(keys) From 0ed311c108ec68bcc0ddcd9063f08417130479b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Mar 2023 03:52:12 -0700 Subject: [PATCH 0934/2222] allow exported orders files to be deleted from the import dialog --- plugins/lua/orders.lua | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index 6f5bc677a..50197fce4 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -19,15 +19,37 @@ local function do_clear() function() dfhack.run_command('orders', 'clear') end) end +local function get_import_choices() + return dfhack.run_command_silent('orders', 'list'):split('\n') +end + local function do_import() - local output = dfhack.run_command_silent('orders', 'list') - dialogs.ListBox{ - frame_title='Import Manager Orders', + local dlg + local function get_dlg() return dlg end + dlg = dialogs.ListBox{ + frame_title='Import/Delete Manager Orders', with_filter=true, - choices=output:split('\n'), - on_select=function(idx, choice) + choices=get_import_choices(), + on_select=function(_, choice) dfhack.run_command('orders', 'import', choice.text) end, + dismiss_on_select2=false, + on_select2=function(_, choice) + if choice.text:startswith('library/') then return end + local fname = 'dfhack-config/orders/'..choice.text..'.json' + if not dfhack.filesystem.isfile(fname) then return end + dialogs.showYesNoPrompt('Delete orders file?', + 'Are you sure you want to delete "' .. fname .. '"?', nil, + function() + print('deleting ' .. fname) + os.remove(fname) + local list = get_dlg().subviews.list + local filter = list:getFilter() + list:setChoices(get_import_choices(), list:getSelected()) + list:setFilter(filter) + end) + end, + select2_hint='Delete file', }:show() end From d5c8237c913b655d9dc3060aa98810740c129ffa Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Mar 2023 03:53:10 -0700 Subject: [PATCH 0935/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 65194dcfa..d55ab6585 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -59,6 +59,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `stockpiles`: now includes a library of useful stockpile configs (see docs for details) - `automelt`: now allows metal chests to be melted (workaround for DF bug 2493 is no longer needed) - `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen +- `orders`: add option to delete exported files from the import dialog - `enable`: can now interpret aliases defined with the `alias` command - scripts in installed mods are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world. From b515b337ea55ed06c5223f81f861a77920014d5b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Mar 2023 08:38:18 -0700 Subject: [PATCH 0936/2222] silence noisy buildingplan warning about itemless buildings not having items --- plugins/buildingplan/defaultitemfilters.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index fc7dd9f56..ac0a4699e 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -60,6 +60,8 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &f auto &serialized = filter_config.val(); DEBUG(status,out).print("deserializing default item filters for key %d,%d,%d: %s\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); + if (!jitems.size()) + return; std::vector elems; split_string(&elems, serialized, "|"); std::vector filters = deserialize_item_filters(out, elems[0]); From df51835b1c5d076f3e7440b37e307100bc8b25be Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Mon, 27 Mar 2023 22:05:06 +0100 Subject: [PATCH 0937/2222] Amend main module code in modding guide --- docs/guides/modding-guide.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 5cabf0856..b386b48c3 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -457,6 +457,8 @@ Ok, you're all set up! Now, let's take a look at an example enabled = enabled or false function isEnabled() + -- this function is for the enabled API, the script won't show up on the + -- control panel without it return enabled end @@ -473,6 +475,10 @@ Ok, you're all set up! Now, let's take a look at an example dfhack.run_command('enable', 'example-mod') end + if dfhack_flags.module then + return + end + if not dfhack_flags.enable then print(dfhack.script_help()) print() From 02b52fcb3decced37bad3dfd6226f35e7ddf6457 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 27 Mar 2023 16:11:21 -0700 Subject: [PATCH 0938/2222] confirm for removing burrows via the repaint menu --- docs/changelog.txt | 1 + docs/plugins/confirm.rst | 5 +++-- plugins/lua/confirm.lua | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d55ab6585..62aa4a037 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,6 +52,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building +- `confirm`: adds confirmation for removing burrows via the repaint menu - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from - `stockpiles`: support partial application of a saved config based on dynamic filtering - `stockpiles`: additive and subtractive modes when applying a second stockpile configuration on top of a first diff --git a/docs/plugins/confirm.rst b/docs/plugins/confirm.rst index f0a34b909..1e6270187 100644 --- a/docs/plugins/confirm.rst +++ b/docs/plugins/confirm.rst @@ -5,8 +5,9 @@ confirm :summary: Adds confirmation dialogs for destructive actions. :tags: fort interface -Now you can get the chance to avoid accidentally disbanding a squad or deleting a -hauling route in case you hit the key accidentally. +In the base game, it is frightenly easy to destroy hours of work with a single +misclick. Now you can avoid the consequences of accidentally disbanding a squad +(for example), or deleting a hauling route. Usage ----- diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 13964645b..1ddf190b4 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -12,10 +12,11 @@ setmetatable(keys, { -- Mouse keys will be sent as a string instead of interface_key local MOUSE_LEFT = "MOUSE_LEFT" local MOUSE_RIGHT = "MOUSE_RIGHT" + --[[ The screen where a confirmation has been triggered Note that this is *not* necessarily the topmost viewscreen, so do not use gui.getCurViewscreen() or related functions. ]] -screen = nil +--screen = nil function if_nil(obj, default) if obj == nil then @@ -118,7 +119,9 @@ zone_remove.message = "Are you sure you want to remove this zone?" burrow_remove = defconf('burrow-remove') function burrow_remove.intercept_key(key) - return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 171 + return key == MOUSE_LEFT and + (df.global.game.main_interface.current_hover == 171 or + df.global.game.main_interface.current_hover == 168) end burrow_remove.title = "Remove burrow" burrow_remove.message = "Are you sure you want to remove this burrow?" From db45d20c237904d63a13a752bdce7c917374d3c1 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 28 Mar 2023 07:14:31 +0000 Subject: [PATCH 0939/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index bb3dfc0d3..27089b81b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit bb3dfc0d3e2bdebc4ba6fd814371662457b18917 +Subproject commit 27089b81be6f9de9486118a97a15be4dde09b1b3 From 147b0ba84ab39cd4b180cd5c8241656bdd11f78f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 28 Mar 2023 23:36:49 -0700 Subject: [PATCH 0940/2222] fix and extend Labels/CycleHotkeyLabels --- docs/changelog.txt | 3 +++ docs/dev/Lua API.rst | 4 ++-- library/lua/gui/widgets.lua | 12 ++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index f09fa8cc1..86ed0f148 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -72,6 +72,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``widget.Label``: tokens can now specify a ``htile`` property to indicate the tile that should be shown when the Label is hovered over with the mouse +- ``widget.Label``: click handlers no longer get the label itself as the first param to the click handler +- ``widget.CycleHotkeyLabel``: options that are bare integers will no longer be interpreted as the pen color in addition to being the label and value +- ``widget.CycleHotkeyLabel``: option labels and pens can now be functions that return a label or pen ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 862c5bccc..2af9e39f1 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4901,8 +4901,8 @@ It has the following attributes: :label_below: If ``true``, then the option value will apear below the label instead of to the right of it. Defaults to ``false``. :options: A list of strings or tables of - ``{label=string, value=string[, pen=pen]}``. String options use the same - string for the label and value and the default pen. The optional ``pen`` + ``{label=string or fn, value=val[, pen=pen]}``. String options use the same + string for the label and value and use the default pen. The optional ``pen`` element could be a color like ``COLOR_RED``. :initial_option: The value or numeric index of the initial option. :on_change: The callback to call when the selected option changes. It is called diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index b5497f0d0..b33076cdc 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1361,11 +1361,11 @@ function Label:onInput(keys) return true end if keys._MOUSE_L_DOWN and self:getMousePos() and self.on_click then - self:on_click() + self.on_click() return true end if keys._MOUSE_R_DOWN and self:getMousePos() and self.on_rclick then - self:on_rclick() + self.on_rclick() return true end for k,v in pairs(self.scroll_keys) do @@ -1560,17 +1560,17 @@ function CycleHotkeyLabel:setOption(value_or_index, call_on_change) end end -local function cyclehotkeylabel_getOptionElem(self, option_idx, key) +local function cyclehotkeylabel_getOptionElem(self, option_idx, key, require_key) option_idx = option_idx or self.option_idx local option = self.options[option_idx] if type(option) == 'table' then return option[key] end - return option + return not require_key and option or nil end function CycleHotkeyLabel:getOptionLabel(option_idx) - return cyclehotkeylabel_getOptionElem(self, option_idx, 'label') + return getval(cyclehotkeylabel_getOptionElem(self, option_idx, 'label')) end function CycleHotkeyLabel:getOptionValue(option_idx) @@ -1578,7 +1578,7 @@ function CycleHotkeyLabel:getOptionValue(option_idx) end function CycleHotkeyLabel:getOptionPen(option_idx) - local pen = cyclehotkeylabel_getOptionElem(self, option_idx, 'pen') + local pen = getval(cyclehotkeylabel_getOptionElem(self, option_idx, 'pen', true)) if type(pen) == 'string' then return nil end return pen end From f2958a552901c5c04505af8b28f1638bd535868b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 28 Mar 2023 23:51:52 -0700 Subject: [PATCH 0941/2222] implement automaterial selection for buildingplan --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 6 +- plugins/buildingplan/buildingplan.h | 6 ++ plugins/buildingplan/defaultitemfilters.cpp | 10 ++-- plugins/buildingplan/defaultitemfilters.h | 6 +- plugins/lua/buildingplan/itemselection.lua | 61 +++++++++++++++++---- plugins/lua/buildingplan/planneroverlay.lua | 43 +++++++++++---- 7 files changed, 100 insertions(+), 33 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index f09fa8cc1..49c5bc0b5 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -53,6 +53,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: filters and global settings are now ignored when manually choosing items for a building - `buildingplan`: if `suspendmanager` is running, then planned buildings will be left suspended when their items are all attached. `suspendmanager` will unsuspsend them for construction when it is safe to do so. +- `buildingplan`: add option for autoselecting the last manually chosen item (like `automaterial` used to do) - `confirm`: adds confirmation for removing burrows via the repaint menu - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from - `stockpiles`: support partial application of a saved config based on dynamic filtering diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 11e08c2a0..7debbbf79 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -938,8 +938,10 @@ static int getMaterialFilter(lua_State *L) { return 1; } -static void setChooseItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, bool choose) { - DEBUG(status,out).print("entering setChooseItems\n"); +static void setChooseItems(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int choose) { + DEBUG(status,out).print( + "entering setChooseItems building_type=%d subtype=%d custom=%d choose=%d\n", + type, subtype, custom, choose); BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(out, key); filters.setChooseItems(choose); diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 26aa77fbc..5c4f25aa4 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -42,6 +42,12 @@ enum HeatSafety { HEAT_SAFETY_MAGMA = 2, }; +enum ItemSelectionChoice { + ITEM_SELECTION_CHOICE_FILTER = 0, + ITEM_SELECTION_CHOICE_MANUAL = 1, + ITEM_SELECTION_CHOICE_AUTOMATERIAL = 2, +}; + int get_config_val(DFHack::PersistentDataItem &c, int index); bool get_config_bool(DFHack::PersistentDataItem &c, int index); void set_config_val(DFHack::PersistentDataItem &c, int index, int value); diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index ac0a4699e..35147579c 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -39,14 +39,14 @@ static string serialize(const std::vector &item_filters, const std:: } DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, const std::vector &jitems) - : key(key), choose_items(false) { + : key(key), choose_items(ItemSelectionChoice::ITEM_SELECTION_CHOICE_FILTER) { DEBUG(status,out).print("creating persistent data for filter key %d,%d,%d\n", std::get<0>(key), std::get<1>(key), std::get<2>(key)); filter_config = World::AddPersistentData(FILTER_CONFIG_KEY); set_config_val(filter_config, FILTER_CONFIG_TYPE, std::get<0>(key)); set_config_val(filter_config, FILTER_CONFIG_SUBTYPE, std::get<1>(key)); set_config_val(filter_config, FILTER_CONFIG_CUSTOM, std::get<2>(key)); - set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose_items); + set_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose_items); item_filters.resize(jitems.size()); for (size_t idx = 0; idx < jitems.size(); ++idx) { item_filters[idx].setMaxQuality(get_max_quality(jitems[idx]), true); @@ -56,7 +56,7 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &filter_config, const std::vector &jitems) : key(getKey(filter_config)), filter_config(filter_config) { - choose_items = get_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS); + choose_items = get_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS); auto &serialized = filter_config.val(); DEBUG(status,out).print("deserializing default item filters for key %d,%d,%d: %s\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); @@ -81,9 +81,9 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &f } } -void DefaultItemFilters::setChooseItems(bool choose) { +void DefaultItemFilters::setChooseItems(int choose) { choose_items = choose; - set_config_bool(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose); + set_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS, choose); } void DefaultItemFilters::setSpecial(const std::string &special, bool val) { diff --git a/plugins/buildingplan/defaultitemfilters.h b/plugins/buildingplan/defaultitemfilters.h index d7ed12a7b..7d285ce4c 100644 --- a/plugins/buildingplan/defaultitemfilters.h +++ b/plugins/buildingplan/defaultitemfilters.h @@ -14,17 +14,17 @@ public: DefaultItemFilters(DFHack::color_ostream &out, BuildingTypeKey key, const std::vector &jitems); DefaultItemFilters(DFHack::color_ostream &out, DFHack::PersistentDataItem &filter_config, const std::vector &jitems); - void setChooseItems(bool choose); + void setChooseItems(int choose); void setItemFilter(DFHack::color_ostream &out, const ItemFilter &filter, int index); void setSpecial(const std::string &special, bool val); - bool getChooseItems() const { return choose_items; } + int getChooseItems() const { return choose_items; } const std::vector & getItemFilters() const { return item_filters; } const std::set & getSpecials() const { return specials; } private: DFHack::PersistentDataItem filter_config; - bool choose_items; + int choose_items; std::vector item_filters; std::set specials; }; diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index 8134b9455..9cfe0f843 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -15,6 +15,12 @@ local BUILD_TEXT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_GREEN, keep_lower=true} -- most recent entries are at the *end* of the list local recently_used = {} +function get_automaterial_selection(building_type) + local tracker = recently_used[building_type] + if not tracker or not tracker.list then return end + return tracker.list[#tracker.list] +end + local function sort_by_type(a, b) local ad, bd = a.data, b.data return ad.item_type < bd.item_type or @@ -54,6 +60,7 @@ ItemSelection.ATTRS{ index=DEFAULT_NIL, desc=DEFAULT_NIL, quantity=DEFAULT_NIL, + autoselect=DEFAULT_NIL, on_submit=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } @@ -63,34 +70,55 @@ function ItemSelection:init() self.selected_set = {} local plural = self.quantity == 1 and '' or 's' + local choices = self:get_choices(sort_by_recency) + + if self.autoselect then + self:do_autoselect(choices) + if self.num_selected >= self.quantity then + self:submit(choices) + return + end + end + self:addviews{ widgets.Label{ - frame={t=0, l=0, r=10}, + frame={t=0, l=0, r=16}, text={ - self.desc, - plural, - NEWLINE, + self.desc, plural, NEWLINE, ('Select up to %d item%s ('):format(self.quantity, plural), {text=function() return self.num_selected end}, ' selected)', }, }, widgets.Label{ - frame={r=0, w=11, t=0, h=3}, + frame={r=0, w=15, t=0, h=3}, text_pen=BUILD_TEXT_PEN, text_hpen=BUILD_TEXT_HPEN, text={ - ' ', NEWLINE, - ' Confirm ', NEWLINE, - ' ', + ' Use filter ', NEWLINE, + ' for remaining ', NEWLINE, + ' items ', }, on_click=self:callback('submit'), + visible=function() return self.num_selected < self.quantity end, + }, + widgets.Label{ + frame={r=0, w=15, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' ', NEWLINE, + ' Continue ', NEWLINE, + ' ', + }, + on_click=self:callback('submit'), + visible=function() return self.num_selected >= self.quantity end, }, widgets.FilteredList{ view_id='flist', frame={t=3, l=0, r=0, b=4}, case_sensitive=false, - choices=self:get_choices(sort_by_recency), + choices=choices, icon_width=2, on_submit=self:callback('toggle_group'), edit_on_char=function(ch) return ch:match('[%l -]') end, @@ -116,7 +144,7 @@ function ItemSelection:init() widgets.HotkeyLabel{ frame={l=22, b=1}, key='CUSTOM_SHIFT_C', - label='Confirm', + label='Continue', auto_width=true, on_activate=self:callback('submit'), }, @@ -215,6 +243,13 @@ function ItemSelection:get_choices(sort_fn) return choices end +function ItemSelection:do_autoselect(choices) + if #choices == 0 then return end + local desired = get_automaterial_selection(uibs.building_type) + if choices[1].search_key ~= desired then return end + self:toggle_group(1, choices[1]) +end + function ItemSelection:increment_group(idx, choice) local data = choice.data if self.quantity <= self.num_selected then return false end @@ -282,13 +317,13 @@ local function track_recently_used(choices) end end -function ItemSelection:submit() +function ItemSelection:submit(choices) local selected_items = {} for item_id in pairs(self.selected_set) do table.insert(selected_items, item_id) end if #selected_items > 0 then - track_recently_used(self.subviews.flist:getChoices()) + track_recently_used(choices or self.subviews.flist:getChoices()) end self.on_submit(selected_items) end @@ -328,6 +363,7 @@ ItemSelectionScreen.ATTRS { index=DEFAULT_NIL, desc=DEFAULT_NIL, quantity=DEFAULT_NIL, + autoselect=DEFAULT_NIL, on_submit=DEFAULT_NIL, on_cancel=DEFAULT_NIL, } @@ -338,6 +374,7 @@ function ItemSelectionScreen:init() index=self.index, desc=self.desc, quantity=self.quantity, + autoselect=self.autoselect, on_submit=self.on_submit, on_cancel=self.on_cancel, } diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 1ae7885cb..3c08597d3 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -490,12 +490,21 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel{ view_id='choose', - frame={b=0, l=0, w=25}, + frame={b=0, l=0}, key='CUSTOM_I', - label='Choose from items:', - options={{label='Yes', value=true}, - {label='No', value=false}}, - initial_option=false, + label='Item selection:', + options={ + {label='Use filters', value=0}, + { + label=function() + local automaterial = itemselection.get_automaterial_selection(uibs.building_type) + return ('Last choice (%s)'):format(automaterial or 'Will ask') + end, + value=2, + }, + {label='Manual choice', value=1}, + }, + initial_option=0, on_change=function(choose) buildingplan.setChooseItems(uibs.building_type, uibs.building_subtype, uibs.custom_type, choose) end, @@ -670,10 +679,11 @@ function PlannerOverlay:onInput(keys) if is_choosing_area() or cur_building_has_no_area() then local filters = get_cur_filters() local num_filters = #filters - local choose = self.subviews.choose - if choose:getOptionValue() then + local choose = self.subviews.choose:getOptionValue() + if choose > 0 then local bounds = get_selected_bounds() self:save_placement() + local autoselect = choose == 2 local is_hollow = self.subviews.hollow:getOptionValue() local chosen_items, active_screens = {}, {} local pending = num_filters @@ -681,14 +691,19 @@ function PlannerOverlay:onInput(keys) for idx = num_filters,1,-1 do chosen_items[idx] = {} local filter = filters[idx] - active_screens[idx] = itemselection.ItemSelectionScreen{ + local selection_screen = itemselection.ItemSelectionScreen{ index=idx, desc=require('plugins.buildingplan').get_desc(filter), quantity=get_quantity(filter, is_hollow, bounds), + autoselect=autoselect, on_submit=function(items) chosen_items[idx] = items - active_screens[idx]:dismiss() - active_screens[idx] = nil + if active_screens[idx] then + active_screens[idx]:dismiss() + active_screens[idx] = nil + else + active_screens[idx] = true + end pending = pending - 1 if pending == 0 then df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT @@ -702,7 +717,13 @@ function PlannerOverlay:onInput(keys) df.global.game.main_interface.bottom_mode_selected = df.main_bottom_mode_type.BUILDING_PLACEMENT self:restore_placement() end, - }:show() + } + if active_screens[idx] then + -- we've already returned via autoselect + active_screens[idx] = nil + else + active_screens[idx] = selection_screen:show() + end end else self:place_building(get_placement_data()) From 94728489524d682e26499e2cdecfbfaefb76be64 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 29 Mar 2023 07:03:34 +0000 Subject: [PATCH 0942/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 27089b81b..71c58248e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 27089b81be6f9de9486118a97a15be4dde09b1b3 +Subproject commit 71c58248e915b0ccf8ad090fd9a27bc5b9641883 From 12e7ae1207fc3792a266ccc4d8b306013dc14692 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 29 Mar 2023 00:29:44 -0700 Subject: [PATCH 0943/2222] sync tags from spreadsheet --- docs/plugins/getplants.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/getplants.rst b/docs/plugins/getplants.rst index 0a59f2492..05bac71c9 100644 --- a/docs/plugins/getplants.rst +++ b/docs/plugins/getplants.rst @@ -3,7 +3,7 @@ getplants .. dfhack-tool:: :summary: Designate trees for chopping and shrubs for gathering. - :tags: unavailable fort productivity plants + :tags: fort productivity plants Specify the types of trees to cut down and/or shrubs to gather by their plant names. From 0db93762cf495b589658e8d07ee9aa6ca608aef6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 29 Mar 2023 15:09:18 -0700 Subject: [PATCH 0944/2222] bounds check the choice data old forts that persisted data before this attribute existed will have an invalid value --- plugins/buildingplan/defaultitemfilters.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/buildingplan/defaultitemfilters.cpp b/plugins/buildingplan/defaultitemfilters.cpp index 35147579c..069459841 100644 --- a/plugins/buildingplan/defaultitemfilters.cpp +++ b/plugins/buildingplan/defaultitemfilters.cpp @@ -57,6 +57,9 @@ DefaultItemFilters::DefaultItemFilters(color_ostream &out, BuildingTypeKey key, DefaultItemFilters::DefaultItemFilters(color_ostream &out, PersistentDataItem &filter_config, const std::vector &jitems) : key(getKey(filter_config)), filter_config(filter_config) { choose_items = get_config_val(filter_config, FILTER_CONFIG_CHOOSE_ITEMS); + if (choose_items < ItemSelectionChoice::ITEM_SELECTION_CHOICE_FILTER || + choose_items > ItemSelectionChoice::ITEM_SELECTION_CHOICE_AUTOMATERIAL) + choose_items = ItemSelectionChoice::ITEM_SELECTION_CHOICE_FILTER; auto &serialized = filter_config.val(); DEBUG(status,out).print("deserializing default item filters for key %d,%d,%d: %s\n", std::get<0>(key), std::get<1>(key), std::get<2>(key), serialized.c_str()); From 58cbed877b572bee210d0b3ebe98362b8aec25e1 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 30 Mar 2023 07:14:18 +0000 Subject: [PATCH 0945/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 71c58248e..f6c4bc97e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 71c58248e915b0ccf8ad090fd9a27bc5b9641883 +Subproject commit f6c4bc97ec50646b4dc813b0130025e0e0012382 From 7ff9d73a4cafa6fdd63dc208cbd988240fafa04a Mon Sep 17 00:00:00 2001 From: John Cosker Date: Thu, 30 Mar 2023 15:17:08 -0400 Subject: [PATCH 0946/2222] Technically drawing works --- plugins/CMakeLists.txt | 1 + plugins/design.cpp | 452 +++++++++++++++++++++++++++++++++++++++++ plugins/lua/design.lua | 9 + 3 files changed, 462 insertions(+) create mode 100644 plugins/design.cpp create mode 100644 plugins/lua/design.lua diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 477e83436..c880a89ff 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -77,6 +77,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) #dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) +dfhack_plugin(design design.cpp LINK_LIBRARIES lua) dfhack_plugin(autoclothing autoclothing.cpp) dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) diff --git a/plugins/design.cpp b/plugins/design.cpp new file mode 100644 index 000000000..25f876d3e --- /dev/null +++ b/plugins/design.cpp @@ -0,0 +1,452 @@ +#include +#include +#include +#include +#include +#include + +#include "ColorText.h" +#include "Debug.h" +#include "LuaTools.h" +#include "PluginManager.h" +#include "df/graphic_viewportst.h" +#include "df/world.h" +#include "modules/Persistence.h" +#include "modules/Screen.h" +#include "modules/World.h" + +DFHACK_PLUGIN("design"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); +using DFHack::color_value; + +REQUIRE_GLOBAL(window_x); +REQUIRE_GLOBAL(window_y); +REQUIRE_GLOBAL(world); +REQUIRE_GLOBAL(plotinfo); +using namespace DFHack; +using namespace df::enums; + +enum ConfigValues { + CONFIG_IS_ENABLED = 0, +}; +namespace DFHack { +// // for configuration-related logging +DBG_DECLARE(design, status, DebugCategory::LDEBUG); +// for logging during the periodic scan +DBG_DECLARE(design, cycle, DebugCategory::LDEBUG); +} // namespace DFHack +static const std::string CONFIG_KEY = std::string(plugin_name) + "/config"; +static PersistentDataItem config; +static const int32_t CYCLE_TICKS = 1200; +static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle + +static command_result do_command(color_ostream &out, + std::vector ¶meters); +static int32_t do_cycle(color_ostream &out, bool force_designate = false); + +DFhackCExport command_result plugin_init(color_ostream &out, + std::vector &commands) { + DEBUG(status, out).print("initializing %s\n", plugin_name); + + // provide a configuration interface for the plugin + commands.push_back( + PluginCommand(plugin_name, "Designs stuff TBD", do_command)); + + return CR_OK; +} + +static int get_config_val(PersistentDataItem &c, int index) { + if (!c.isValid()) return -1; + return c.ival(index); +} + +static bool get_config_bool(PersistentDataItem &c, int index) { + return get_config_val(c, index) == 1; +} + +static void set_config_val(PersistentDataItem &c, int index, int value) { + if (c.isValid()) c.ival(index) = value; +} + +static void set_config_bool(PersistentDataItem &c, int index, bool value) { + set_config_val(c, index, value ? 1 : 0); +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); + + return CR_FAILURE; + } + + if (enable != is_enabled) { + is_enabled = enable; + DEBUG(status, out) + .print("%s from the API; persisting\n", + is_enabled ? "enabled" : "disabled"); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + if (enable) do_cycle(out, true); + } else { + DEBUG(status, out) + .print("%s from the API, but already %s; no action\n", + is_enabled ? "enabled" : "disabled", + is_enabled ? "enabled" : "disabled"); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream &out) { + DEBUG(status, out).print("shutting down %s\n", plugin_name); + + return CR_OK; +} + +DFhackCExport command_result plugin_load_data(color_ostream &out) { + cycle_timestamp = 0; + config = World::GetPersistentData(CONFIG_KEY); + + if (!config.isValid()) { + DEBUG(status, out).print("no config found in this save; initializing\n"); + config = World::AddPersistentData(CONFIG_KEY); + set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); + } + + // we have to copy our enabled flag into the global plugin variable, but + // all the other state we can directly read/modify from the persistent + // data structure. + is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); + DEBUG(status, out) + .print("loading persisted enabled state: %s\n", + is_enabled ? "true" : "false"); + + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, + state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + if (is_enabled) { + DEBUG(status, out).print("world unloaded; disabling %s\n", plugin_name); + is_enabled = false; + } + } + + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate(color_ostream &out) { + if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { + int32_t ret = do_cycle(out); + } + + return CR_OK; +} +int selected_tile_texpos = 0; +const static bool hi = + Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos); + +static command_result do_command(color_ostream &out, + std::vector ¶meters) { + return CR_OK; +} + +static int32_t do_cycle(color_ostream &out, bool force_designate) { return 0; } + +// Assuming the existence of a class named Point, similar to the one in Lua +class Point { + public: + int x; + int y; + + Point(int x, int y) : x(x), y(y) {} + + bool operator==(const Point &other) const { + return x == other.x && y == other.y; + } +}; + +// Assuming the existence of a class named Color, similar to the one in Lua +class Color { + public: + // Define your color values here +}; + +// Assuming the existence of a class named Pen, similar to the one in Lua +class Pen { + public: + std::string ch; + int tile; + color_value fg; + // Define your pen properties and methods here +}; + +class Design { + public: + std::map PENS; + + enum PEN_MASK { + NORTH = 0, + SOUTH, + EAST, + WEST, + DRAG_POINT, + MOUSEOVER, + INSHAPE, + EXTRA_POINT, + NUM_FLAGS + }; + + // Define the function similar to the Lua version + + uint32_t gen_pen_key(bool n, bool s, bool e, bool w, bool is_corner, + bool is_mouse_over, bool inshape, bool extra_point) { + std::bitset ret; + ret[NORTH] = n; + ret[SOUTH] = s; + ret[EAST] = e; + ret[WEST] = w; + ret[DRAG_POINT] = is_corner; + ret[MOUSEOVER] = is_mouse_over; + ret[INSHAPE] = inshape; + ret[EXTRA_POINT] = extra_point; + + return static_cast(ret.to_ulong()); + } + + Pen Design::get_pen(int x, int y, + const std::map> &arr); + // Define the function similar to the Lua version +}; + +Design design; + +// Add other methods and member variables needed for the class + +static int design_getPen(lua_State *L) { + std::map> arr; + if (lua_istable(L, -1)) { + // Iterate over the outer table + lua_pushnil(L); // First key + while (lua_next(L, -2) != 0) { + int x = lua_tointeger(L, -2); // Convert key to an integer + + if (lua_istable(L, -1)) { + // Iterate over the inner table + lua_pushnil(L); // First key + while (lua_next(L, -2) != 0) { + int y = lua_tointeger(L, -2); // Convert key to an integer + bool value = lua_toboolean(L, -1); + + if (value) { + if (arr.count(x) == 0) arr[x] = {}; + arr[x][y] = value; + } + lua_pop(L, 1); // Remove value, keep the key for the next iteration + } + } + lua_pop(L, 1); // Remove inner table, keep the key for the next iteration + } + } + + for (auto x : arr) { + for (auto y : x.second) { + Screen::Pen cur_tile = Screen::readTile(x.first, y.first, true); + // cur_tile.tile = selected_tile_texpos; + Pen pen = design.get_pen(x.first, y.first, arr); + cur_tile.tile = pen.tile; + Screen::paintTile(cur_tile, x.first - *window_x, y.first - *window_y, + true); + } + } + return 0; +} +enum class CURSORS { + INSIDE, + NORTH, + N_NUB, + S_NUB, + W_NUB, + E_NUB, + NE, + NW, + WEST, + EAST, + SW, + SOUTH, + SE, + VERT_NS, + VERT_EW, + POINT +}; + +std::map> CURSORS_MAP = { + {CURSORS::INSIDE, {1, 2}}, {CURSORS::NORTH, {1, 1}}, + {CURSORS::N_NUB, {3, 2}}, {CURSORS::S_NUB, {4, 2}}, + {CURSORS::W_NUB, {3, 1}}, {CURSORS::E_NUB, {5, 1}}, + {CURSORS::NE, {2, 1}}, {CURSORS::NW, {0, 1}}, + {CURSORS::WEST, {0, 2}}, {CURSORS::EAST, {2, 2}}, + {CURSORS::SW, {0, 3}}, {CURSORS::SOUTH, {1, 3}}, + {CURSORS::SE, {2, 3}}, {CURSORS::VERT_NS, {3, 3}}, + {CURSORS::VERT_EW, {4, 1}}, {CURSORS::POINT, {4, 3}}, +}; +Pen make_pen(const std::pair &direction, bool is_corner, + bool is_mouse_over, bool inshape, bool extra_point) { + color_value color = COLOR_GREEN; + int ycursor_mod = 0; + + if (!extra_point) { + if (is_corner) { + color = COLOR_CYAN; + ycursor_mod += 6; + if (is_mouse_over) { + color = COLOR_MAGENTA; + ycursor_mod += 3; + } + } + } else { + ycursor_mod += 15; + color = COLOR_LIGHTRED; + + if (is_mouse_over) { + color = COLOR_RED; + ycursor_mod += 3; + } + } + + Pen pen; + pen.ch = inshape ? "X" : "o"; + pen.fg = color; + int selected_tile_texpos = 0; + Screen::findGraphicsTile("CURSORS", direction.first, direction.second, &selected_tile_texpos); + pen.tile = selected_tile_texpos; + + // Assuming dfhack.screen.findGraphicsTile is replaced with a custom function + // findGraphicsTile pen.tile = findGraphicsTile("CURSORS", direction.first, + // direction.second + ycursor_mod); + + return pen; +} +Pen Design::get_pen(int x, int y, + const std::map> &arr) { + auto has_point = [&arr](int _x, int _y) { + return arr.count(_x) != 0 && arr.at(_x).count(_y) != 0 && arr.at(_x).at(_y); + }; + bool get_point = has_point(x, y); + + // Basic shapes are bounded by rectangles and therefore can have corner drag + // points even if they're not real points in the shape if (marks.size() >= + // shape.min_points && shape.basic_shape) { + // Point shape_top_left, shape_bot_right; + // shape.get_point_dims(shape_top_left, shape_bot_right); + + // if (x == shape_top_left.x && y == shape_top_left.y && + // shape.drag_corners.nw) { + // drag_point = true; + // } else if (x == shape_bot_right.x && y == shape_top_left.y && + // shape.drag_corners.ne) { + // drag_point = true; + // } else if (x == shape_top_left.x && y == shape_bot_right.y && + // shape.drag_corners.sw) { + // drag_point = true; + // } else if (x == shape_bot_right.x && y == shape_bot_right.y && + // shape.drag_corners.se) { + // drag_point = true; + // } + // } + + // for (const auto& mark : marks) { + // if (mark == Point(x, y)) { + // drag_point = true; + // } + // } + + // if (mirror_point && *mirror_point == Point(x, y)) { + // drag_point = true; + // } + + // // Check for an extra point + // bool extra_point = false; + // for (const auto& point : extra_points) { + // if (x == point.x && y == point.y) { + // extra_point = true; + // break; + // } + // } + + // // Show center point if both marks are set + // if ((shape.basic_shape && marks.size() == shape.max_points) || + // (!shape.basic_shape && !placing_mark.active && !marks.empty())) { + // int center_x, center_y; + // shape.get_center(center_x, center_y); + + // if (x == center_x && y == center_y) { + // extra_point = true; + // } + // } + + bool n = false, w = false, e = false, s = false; + if (get_point) { + if (y == 0 || !has_point(x, y - 1)) n = true; + if (x == 0 || !has_point(x - 1, y)) w = true; + if (!has_point(x + 1, y)) e = true; + if (!has_point(x, y + 1)) s = true; + } + // DEBUG(status).print("jcosker %d %d %d %d\n", n, s, e, w); + + // Get the bit field to use as a key for the PENS map + uint32_t pen_key = gen_pen_key(n, s, e, w, false, false, get_point, false); + // DEBUG(status).print("jcosker %zu\n", pen_key); + + if (PENS.find(pen_key) == PENS.end()) { + std::pair cursor{-1, -1}; + // int cursor = -1; // Assuming -1 is an invalid cursor value + + // Determine the cursor to use based on the input parameters + // The CURSORS enum or equivalent should be defined in your code + if (get_point && !n && !w && !e && !s) + cursor = CURSORS_MAP.at(CURSORS::INSIDE); + else if (get_point && n && w && !e && !s) + cursor = CURSORS_MAP.at(CURSORS::NW); + else if (get_point && n && !w && !e && !s) + cursor = CURSORS_MAP.at(CURSORS::NORTH); + else if (get_point && n && e && !w && !s) + cursor = CURSORS_MAP.at(CURSORS::NE); + else if (get_point && !n && w && !e && !s) + cursor = CURSORS_MAP.at(CURSORS::WEST); + else if (get_point && !n && !w && e && !s) + cursor = CURSORS_MAP.at(CURSORS::EAST); + else if (get_point && !n && w && !e && s) + cursor = CURSORS_MAP.at(CURSORS::SW); + else if (get_point && !n && !w && !e && s) + cursor = CURSORS_MAP.at(CURSORS::SOUTH); + else if (get_point && !n && !w && e && s) + cursor = CURSORS_MAP.at(CURSORS::SE); + else if (get_point && n && w && e && !s) + cursor = CURSORS_MAP.at(CURSORS::N_NUB); + else if (get_point && n && !w && e && s) + cursor = CURSORS_MAP.at(CURSORS::E_NUB); + else if (get_point && n && w && !e && s) + cursor = CURSORS_MAP.at(CURSORS::W_NUB); + else if (get_point && !n && w && e && s) + cursor = CURSORS_MAP.at(CURSORS::S_NUB); + else if (get_point && !n && w && e && !s) + cursor = CURSORS_MAP.at(CURSORS::VERT_NS); + else if (get_point && n && !w && !e && s) + cursor = CURSORS_MAP.at(CURSORS::VERT_EW); + else if (get_point && n && w && e && s) + cursor = CURSORS_MAP.at(CURSORS::POINT); + // else if (drag_point && !get_point) cursor = CURSORS::INSIDE; + // else if (extra_point) cursor = CURSORS::INSIDE; + // Create the pen if the cursor is set + + DEBUG(status).print("jcosker %d, %d\n", cursor.first, cursor.second); + if (cursor.first != -1) { + PENS[pen_key] = make_pen(cursor, false, false, get_point, false); + } + } + + // Return the pen for the caller + return PENS.at(pen_key); +} + +DFHACK_PLUGIN_LUA_COMMANDS{DFHACK_LUA_COMMAND(design_getPen), DFHACK_LUA_END}; diff --git a/plugins/lua/design.lua b/plugins/lua/design.lua new file mode 100644 index 000000000..fe916aa41 --- /dev/null +++ b/plugins/lua/design.lua @@ -0,0 +1,9 @@ +local _ENV = mkmodule('plugins.design') + +view2 = {design_window = { name = "hello"}} + +function getPen(hi) + design_getPen(hi) +end + +return _ENV From 0771fddcd3ff1fdc3474ba5571fa25df93bd9135 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 31 Mar 2023 07:13:59 +0000 Subject: [PATCH 0947/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index f6c4bc97e..2cb58469e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit f6c4bc97ec50646b4dc813b0130025e0e0012382 +Subproject commit 2cb58469e0696111d8a29a2d3ce0ca585d29b3f5 From 44adae24abaea4400962eb693401017f0769934c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 31 Mar 2023 04:45:59 -0700 Subject: [PATCH 0948/2222] fix items not being filtered correctly in item selection screen --- plugins/buildingplan/buildingplan.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 7debbbf79..81b5feaae 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -678,13 +678,14 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ auto other_id = ENUM_ATTR(job_item_vector_id, other, vector_id); for (auto &item : df::global::world->items.other[other_id]) { ItemFilter filter = filters[index]; - if (counts) { + set special = specials; + if (ignore_filters || counts) { // don't filter by material; we want counts for all materials filter.setMaterialMask(0); filter.setMaterials(set()); + special.clear(); } - if (itemPassesScreen(item) && - (ignore_filters || matchesFilters(item, jitem, heat, filter, specials))) { + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter, specials)) { if (item_ids) item_ids->emplace_back(item->id); if (counts) { From 99e2e596c08635ad83d6b4997552622abdbdce9b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 31 Mar 2023 04:47:54 -0700 Subject: [PATCH 0949/2222] use normalized specials vector --- plugins/buildingplan/buildingplan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 81b5feaae..eb765c1c4 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -685,7 +685,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ filter.setMaterials(set()); special.clear(); } - if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter, specials)) { + if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter, special)) { if (item_ids) item_ids->emplace_back(item->id); if (counts) { From 38d4eea7acc54dc7f6aa540bd9833c21a3a5363d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 31 Mar 2023 05:02:02 -0700 Subject: [PATCH 0950/2222] bump to 50.07-beta2 --- CMakeLists.txt | 2 +- docs/changelog.txt | 47 +++++++++++++++++++++++++++++----------------- library/xml | 2 +- scripts | 2 +- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 790e8f59b..dfb6dfb21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "beta1") +set(DFHACK_RELEASE "beta2") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 790e0a2ea..3fe5fdbbb 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,29 +34,45 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins -- `getplants`: getplants is now available -- `prospector`: prospector tool in fort mode is now available. embark prospect is not yet available and is disabled at this time. + +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.07-beta2 + +## New Plugins +- `getplants`: designate trees for chopping and shrubs for gathering according to type +- `prospector`: get stone, ore, gem, and other tile property counts in fort mode. embark site estimates are not yet available. ## Fixes - `buildingplan`: filters are now properly applied to planned stairs - `buildingplan`: existing carved up/down stairs are now taken into account when determining which stair shape to construct - `buildingplan`: upright spike traps are now placed extended rather than retracted -- `buildingplan`: you can no longer designate constructions on tiles with magma or deep water -- `buildingplan`: fixed material filter getting lost for planning buildings on save/reload +- `buildingplan`: you can no longer designate constructions on tiles with magma or deep water, mirroring the vanilla restrictions +- `buildingplan`: fixed material filters getting lost for planning buildings on save/reload - `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) -- `tailor`: properly discriminates between dyed and undyed cloth -- `tailor`: no longer defaults to using adamantine -- `tailor`: properly tracks material requirements for already queued orders -- `tailor`: skips units who can't wear clothes -- `tailor`: hopefully won't over-order items any more +- `tailor`: properly discriminate between dyed and undyed cloth +- `tailor`: no longer default to using adamantine cloth for producing clothes +- `tailor`: take queued orders into account when calculating available materials +- `tailor`: skip units who can't wear clothes +- `tailor`: identify more available items as available, solving issues with over-production ## Misc Improvements -- `buildingplan`: filters and global settings are now ignored when manually choosing items for a building +- `buildingplan`: filters and global settings are now ignored when manually choosing items for a building, allowing you to make custom choices independently of the filters that would otherwise be used - `buildingplan`: if `suspendmanager` is running, then planned buildings will be left suspended when their items are all attached. `suspendmanager` will unsuspsend them for construction when it is safe to do so. - `buildingplan`: add option for autoselecting the last manually chosen item (like `automaterial` used to do) - `confirm`: adds confirmation for removing burrows via the repaint menu - `stockpiles`: support applying stockpile configurations with fully enabled categories to stockpiles in worlds other than the one where the configuration was exported from -- `stockpiles`: support partial application of a saved config based on dynamic filtering +- `stockpiles`: support partial application of a saved config based on dynamic filtering (e.g. disable all tallow in a food stockpile, even tallow from world-specific generated creatures) - `stockpiles`: additive and subtractive modes when applying a second stockpile configuration on top of a first - `stockpiles`: write player-exported stockpile configurations to the ``dfhack-config/stockpiles`` folder. If you have any stockpile configs in other directories, please move them to that folder. - `stockpiles`: now includes a library of useful stockpile configs (see docs for details) @@ -64,12 +80,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen - `orders`: add option to delete exported files from the import dialog - `enable`: can now interpret aliases defined with the `alias` command -- scripts in installed mods are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world. +- scripts in ``data/installed mods/`` subfolders are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world. ## Documentation -- ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. all of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again. - -## API +- `modding-guide`: guide updated to include information for 3rd party script developers +- the ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. most of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again. ## Lua - ``widget.Label``: tokens can now specify a ``htile`` property to indicate the tile that should be shown when the Label is hovered over with the mouse @@ -77,8 +92,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``widget.CycleHotkeyLabel``: options that are bare integers will no longer be interpreted as the pen color in addition to being the label and value - ``widget.CycleHotkeyLabel``: option labels and pens can now be functions that return a label or pen -## Removed - # 50.07-beta1 ## Fixes diff --git a/library/xml b/library/xml index 8b8ac2de0..739d17867 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8b8ac2de03ab5f8e5506864a751933059abfd03e +Subproject commit 739d1786723bbe912f448064c5705092a7936cc6 diff --git a/scripts b/scripts index 2cb58469e..0b55c5418 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 2cb58469e0696111d8a29a2d3ce0ca585d29b3f5 +Subproject commit 0b55c5418970917310bfd437499adfac0be80e0e From 18f1b5c675b99ec067f6423f326e49300908491a Mon Sep 17 00:00:00 2001 From: TaxiService Date: Fri, 31 Mar 2023 17:33:23 +0200 Subject: [PATCH 0951/2222] attempt#2 to fix masterwork/exceptional/decorated symbols this time we're using a couple of `static_cast(...)` thingies --- library/modules/Items.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index bfdd525be..9c1217c93 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -720,13 +720,13 @@ df::coord Items::getPosition(df::item *item) return item->pos; } -static char quality_table[] = { 0, '-', '+', '*', '=', '@' }; +static int quality_table[] = { 0, 45, 43, 42, 240, 15 }; static void addQuality(std::string &tmp, int quality) { if (quality > 0 && quality <= 5) { - char c = quality_table[quality]; - tmp = c + tmp + c; + int c = quality_table[quality]; + tmp = static_cast(c) + tmp + static_cast(c); } } @@ -825,7 +825,7 @@ std::string Items::getDescription(df::item *item, int type, bool decorate) addQuality(tmp, item->getQuality()); if (item->isImproved()) { - tmp = "<" + tmp + ">"; + tmp = static_cast(174) + tmp + static_cast(175); addQuality(tmp, item->getImprovementQuality()); } } From 51236f90fa79a1ef810bdea4574f86ffda5cbc0a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 31 Mar 2023 21:05:19 -0500 Subject: [PATCH 0952/2222] update steam launcher this version launches dwarf fortress via the steam client the existing one doesn't set up the steam app context which means access to DF's steam workshop is broken. launching through the steam client avoids this issue. --- docs/changelog.txt | 1 + package/windows/launchdf.c | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 3fe5fdbbb..279acdef7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- Steam launcher now launches Dwarf Fortress via the Steam client ## Misc Improvements diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c index 9f4b01fcb..588624511 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.c @@ -2,15 +2,28 @@ int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) { - STARTUPINFOA si; + STARTUPINFOW si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - if (CreateProcessA("Dwarf Fortress.exe", - NULL, + WCHAR steamPath[1024]; + DWORD datasize = 1024; + + LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", L"SteamExe", RRF_RT_REG_SZ, NULL, &steamPath, &datasize); + + if (retCode != ERROR_SUCCESS) + { + MessageBoxW(NULL, L"Could not find Steam client executable", NULL, 0); + exit(1); + } + + WCHAR commandLine[1024] = L"steam.exe -applaunch 975370"; + + if (CreateProcessW(steamPath, + commandLine, NULL, NULL, FALSE, @@ -20,7 +33,7 @@ int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) &si, &pi) == 0) { - MessageBoxA(NULL, "could not launch 'Dwarf Fortress.exe'", NULL, 0); + MessageBoxW(NULL, L"could not launch Dwarf Fortress", NULL, 0); exit(1); } From 92438ed98c39fe18510bbd2c78d12de87fc29aa3 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 1 Apr 2023 02:26:16 +0000 Subject: [PATCH 0953/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 0b55c5418..298d9c00c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 0b55c5418970917310bfd437499adfac0be80e0e +Subproject commit 298d9c00c7df81fc4ca363366dbaebf7f47be489 From d13dfd7d496cf45a5a56e5951457efb4ed9a5e70 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 31 Mar 2023 19:29:01 -0700 Subject: [PATCH 0954/2222] interpret shrubbery as floor instead of wall --- docs/changelog.txt | 3 ++- plugins/blueprint.cpp | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 279acdef7..b34e66aaa 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,7 +36,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- Steam launcher now launches Dwarf Fortress via the Steam client +- ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional +- `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls ## Misc Improvements diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index b09c4d497..39847935d 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -229,6 +229,9 @@ static const char * get_tile_dig(const df::coord &pos, const tile_context &) { case tiletype_shape::BOULDER: case tiletype_shape::PEBBLES: case tiletype_shape::BROOK_TOP: + case tiletype_shape::SAPLING: + case tiletype_shape::SHRUB: + case tiletype_shape::TWIG: return "d"; case tiletype_shape::STAIR_UP: return "u"; From 3a6893de53ee3994aa68641dd8b49a423ff617ab Mon Sep 17 00:00:00 2001 From: TaxiService Date: Sat, 1 Apr 2023 16:43:05 +0200 Subject: [PATCH 0955/2222] attempt to increase code readability following lethosor and myk's suggestions, each symbol now is its own constant with a descriptive name. ...will it work though? --- library/modules/Items.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 9c1217c93..75951aaa5 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -720,13 +720,18 @@ df::coord Items::getPosition(df::item *item) return item->pos; } -static int quality_table[] = { 0, 45, 43, 42, 240, 15 }; +static const char MARKER_EXCEPTIONAL = static_cast(240); +static const char MARKER_MASTERWORK = static_cast(15); +static const char MARKER_IMPROVED_LEFT = static_cast(174); +static const char MARKER_IMPROVED_RIGHT = static_cast(175); + +static char quality_table[] = { 0, '-', '+', '*', MARKER_EXCEPTIONAL, MARKER_MASTERWORK }; static void addQuality(std::string &tmp, int quality) { if (quality > 0 && quality <= 5) { - int c = quality_table[quality]; - tmp = static_cast(c) + tmp + static_cast(c); + char c = quality_table[quality]; + tmp = c + tmp + c; } } @@ -825,7 +830,7 @@ std::string Items::getDescription(df::item *item, int type, bool decorate) addQuality(tmp, item->getQuality()); if (item->isImproved()) { - tmp = static_cast(174) + tmp + static_cast(175); + tmp = MARKER_IMPROVED_LEFT + tmp + MARKER_IMPROVED_RIGHT; addQuality(tmp, item->getImprovementQuality()); } } From e3bab1eb6b0c464d33b2a0f981d1e0b645eb1759 Mon Sep 17 00:00:00 2001 From: TaxiService Date: Sat, 1 Apr 2023 23:40:45 +0200 Subject: [PATCH 0956/2222] simplifying character definition even more removed those pesky superfluous consts and casts... --- library/modules/Items.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 75951aaa5..5a34d7385 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -720,12 +720,7 @@ df::coord Items::getPosition(df::item *item) return item->pos; } -static const char MARKER_EXCEPTIONAL = static_cast(240); -static const char MARKER_MASTERWORK = static_cast(15); -static const char MARKER_IMPROVED_LEFT = static_cast(174); -static const char MARKER_IMPROVED_RIGHT = static_cast(175); - -static char quality_table[] = { 0, '-', '+', '*', MARKER_EXCEPTIONAL, MARKER_MASTERWORK }; +static char quality_table[] = { 0, '-', '+', '*', '\xF0', '\x0F' }; static void addQuality(std::string &tmp, int quality) { @@ -830,7 +825,7 @@ std::string Items::getDescription(df::item *item, int type, bool decorate) addQuality(tmp, item->getQuality()); if (item->isImproved()) { - tmp = MARKER_IMPROVED_LEFT + tmp + MARKER_IMPROVED_RIGHT; + tmp = '\xAE' + tmp + '\xAF'; addQuality(tmp, item->getImprovementQuality()); } } From 8d8a4abd13e96c17214dfb84fcbb94ab77f87b9e Mon Sep 17 00:00:00 2001 From: TaxiService Date: Sun, 2 Apr 2023 02:33:09 +0200 Subject: [PATCH 0957/2222] reverted loss of readability --- library/modules/Items.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 5a34d7385..303f9771e 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -720,7 +720,13 @@ df::coord Items::getPosition(df::item *item) return item->pos; } -static char quality_table[] = { 0, '-', '+', '*', '\xF0', '\x0F' }; +// These '\xFF' chars refer to quality markers from curses.png, namely: 250 (≡), 15 (☼), 174 («) and 175 (»). +static const char MARKER_EXCEPTIONAL = '\xF0'; +static const char MARKER_MASTERWORK = '\x0F'; +static const char MARKER_IMPROVED_LEFT = '\xAE'; +static const char MARKER_IMPROVED_RIGHT = '\xAF'; + +static char quality_table[] = { 0, '-', '+', '*', MARKER_EXCEPTIONAL, MARKER_MASTERWORK }; static void addQuality(std::string &tmp, int quality) { @@ -825,7 +831,7 @@ std::string Items::getDescription(df::item *item, int type, bool decorate) addQuality(tmp, item->getQuality()); if (item->isImproved()) { - tmp = '\xAE' + tmp + '\xAF'; + tmp = MARKER_IMPROVED_LEFT + tmp + MARKER_IMPROVED_RIGHT; addQuality(tmp, item->getImprovementQuality()); } } From 5846c3fc12d2021509e501d4d0da10abaa55d0cf Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 1 Apr 2023 22:11:38 -0700 Subject: [PATCH 0958/2222] Update changelog.txt --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b34e66aaa..6dde716b3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls ## Misc Improvements +- `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game ## Documentation From 9461e609a0a9ea23ecd9b4088c14c7aface63d33 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 1 Apr 2023 22:53:35 -0700 Subject: [PATCH 0959/2222] adjust text margin in hotkey menu for wide scrollbar --- docs/changelog.txt | 1 + plugins/lua/hotkeys.lua | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b34e66aaa..6ad97f8fa 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar - ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 0eb09d244..e8121fded 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -107,7 +107,7 @@ local function get_bindings_to_hotkeys(hotkeys, bindings) end -- number of non-text tiles: icon, space, space between cmd and hk, scrollbar -local LIST_BUFFER = 2 + 1 + 1 +local LIST_BUFFER = 2 + 1 + 1 + 2 local function get_choices(hotkeys, bindings, is_inverted) local choices, max_width, seen = {}, 0, {} @@ -143,7 +143,7 @@ local function get_choices(hotkeys, bindings, is_inverted) -- adjust width of command fields so the hotkey tokens are right justified for _,choice in ipairs(choices) do local command_token = choice.text[1] - command_token.width = max_width - choice.hk_width - 3 + command_token.width = max_width - choice.hk_width - 5 end return choices, max_width From 55f7643381f973ddb19e3be93f08c4bcfb26160a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 1 Apr 2023 23:50:14 -0700 Subject: [PATCH 0960/2222] use the defined constant instead of a magic number --- plugins/lua/hotkeys.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index e8121fded..8edb70073 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -106,8 +106,8 @@ local function get_bindings_to_hotkeys(hotkeys, bindings) return bindings_to_hotkeys end --- number of non-text tiles: icon, space, space between cmd and hk, scrollbar -local LIST_BUFFER = 2 + 1 + 1 + 2 +-- number of non-text tiles: icon, space between cmd and hk, scrollbar+margin +local LIST_BUFFER = 2 + 1 + 3 local function get_choices(hotkeys, bindings, is_inverted) local choices, max_width, seen = {}, 0, {} @@ -143,7 +143,7 @@ local function get_choices(hotkeys, bindings, is_inverted) -- adjust width of command fields so the hotkey tokens are right justified for _,choice in ipairs(choices) do local command_token = choice.text[1] - command_token.width = max_width - choice.hk_width - 5 + command_token.width = max_width - choice.hk_width - (LIST_BUFFER - 1) end return choices, max_width From 40a6d968bf4425eb8b1ee355aa2c9e784186372e Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 2 Apr 2023 07:12:31 +0000 Subject: [PATCH 0961/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 298d9c00c..d2dad272e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 298d9c00c7df81fc4ca363366dbaebf7f47be489 +Subproject commit d2dad272e4b24c043ca62f843511c763fb1f67b5 From f8de51aba3bd8710c59203e16bce50cb1f6e95ec Mon Sep 17 00:00:00 2001 From: silverflyone Date: Tue, 4 Apr 2023 03:08:43 +1000 Subject: [PATCH 0962/2222] Update Buildings.cpp Fixes #3159. Valid map coordinates from (0, 0, 0) to (world->map.x_count - 1, world->map.y_count - 1, world->map.z_count - 1). Stockpile coords (x1, y1, z) to (x2, y2, z) may lie outside of this region. Use min of (0, 0) and max of (world->map.x_count - 1, world->map.y_count - 1) when iterating the block. --- library/modules/Buildings.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 2b6fc8ec8..8a71485b2 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1637,17 +1637,17 @@ StockpileIterator& StockpileIterator::operator++() { ++current; } else { // Start with the top-left block covering the stockpile. - block = Maps::getTileBlock(stockpile->x1, stockpile->y1, stockpile->z); + block = Maps::getTileBlock(std::min(std::max(stockpile->x1, 0), world->map.x_count-1), std::min(std::max(stockpile->y1, 0), world->map.y_count-1), stockpile->z); current = 0; } while (current >= block->items.size()) { // Out of items in this block; find the next block to search. - if (block->map_pos.x + 16 <= stockpile->x2) { - block = Maps::getTileBlock(block->map_pos.x + 16, block->map_pos.y, stockpile->z); + if (std::max(block->map_pos.x + 16, 0) <= std::min(std::max(stockpile->x2, 0), world->map.x_count-1)) { + block = Maps::getTileBlock(std::min(std::max(block->map_pos.x + 16, 0), world->map.x_count-1), block->map_pos.y, stockpile->z); current = 0; - } else if (block->map_pos.y + 16 <= stockpile->y2) { - block = Maps::getTileBlock(stockpile->x1, block->map_pos.y + 16, stockpile->z); + } else if (std::max(block->map_pos.y + 16, 0) <= std::min(std::max(stockpile->y2, 0), world->map.y_count-1)) { + block = Maps::getTileBlock(std::min(std::max(stockpile->x1, 0), world->map.x_count-1), std::min(std::max(block->map_pos.y + 16, 0), world->map.y_count-1), stockpile->z); current = 0; } else { // All items in all blocks have been checked. From 181f0bdf720f85f29c3c159b7f77d70b00dc22cc Mon Sep 17 00:00:00 2001 From: silverflyone Date: Tue, 4 Apr 2023 03:27:32 +1000 Subject: [PATCH 0963/2222] Update Buildings.cpp Boundary checks added. --- library/modules/Buildings.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 8a71485b2..172975e21 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1635,6 +1635,12 @@ StockpileIterator& StockpileIterator::operator++() { if (block) { // Check the next item in the current block. ++current; + } + else if (stockpile->x2 < 0 || stockpile->y2 < 0 || stockpile->z < 0 || stockpile->x1 > world->map.x_count - 1 || stockpile->y1 > world->map.y_count - 1 || stockpile->z > world->map.z_count - 1) { + // if the stockpile bounds exist outside of valid map plane then no items can be in the stockpile + block = NULL; + item = NULL; + return *this; } else { // Start with the top-left block covering the stockpile. block = Maps::getTileBlock(std::min(std::max(stockpile->x1, 0), world->map.x_count-1), std::min(std::max(stockpile->y1, 0), world->map.y_count-1), stockpile->z); From 7c63ea9394e4222cd03dde1b3d30f8069e752689 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 20:13:57 +0200 Subject: [PATCH 0964/2222] added BOLD_FRAME and INTERIOR_MEDIUM_FRAME --- data/art/border-bold.png | Bin 0 -> 836 bytes docs/dev/Lua API.rst | 17 ++++++++++++++--- library/LuaApi.cpp | 1 + library/include/modules/Textures.h | 1 + library/lua/gui.lua | 3 +++ library/modules/Textures.cpp | 7 +++++++ 6 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 data/art/border-bold.png diff --git a/data/art/border-bold.png b/data/art/border-bold.png new file mode 100644 index 0000000000000000000000000000000000000000..16423a3a7477fd94d1e27a45f0f91b2f6f28af84 GIT binary patch literal 836 zcmV-K1H1f*P)3>q_IhpTDzpXQV0Ya?T#%Xxr4<(f~1J)^xK_v z@@{6{+>ARL0>0vIZ+Q3ioA>jZnai?lapBheUis|74G}Nf!?XRecX0F*&y&gI>$|+a zx`5|zpO5f)Z(|Jm#(FWj{^Ub}b*I7cWb9m*3gz3)r_I9gUN}m;wc6gW6r#Zj5dBrr zgpNR&M&70tTt0+>W*viH}Jt$jww56!b)+K_TRg8Rz#8txYu7Rov6doV85+(tm zzsi!#qr0!>gvj-j*Uj?bz#Yu@kLFc(qpScAd6wI>0ok7nDBBmAJ~~qj+HfU8&$h+C zv>|2aWBaJh3TPiNr%6Jf$}zOkDM~;6Q>p8*Ujyu=WbD!Z(<9-18i9MR?h-};GAoOHFC$XbICkwsN*rO)9eZ;l(Wt-^G z@ohKun8|L4V?fAO8Aisp^IRWIUYS7;eHj%L4-HFpu4nAX$e^$^%;2M((8*5q@!sv> znTNv4z43|In!5A~ia3c55(Y-_0)vloqM_LO9-=L|ORvD_7&lfkqhU;Tst=T&tdsj? zPlysq;a7Qvz@0FXW%E%ti5!3B&ik^yDXy^o!zE0WHbUsD@lNHB00RKGR=gVn;~Fyn O0000 Date: Mon, 3 Apr 2023 20:37:45 +0200 Subject: [PATCH 0965/2222] fixed vertical divider characters in ascii mode --- plugins/lua/buildingplan/pens.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index 973bb7bc6..ed8f393d6 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -19,8 +19,8 @@ function reload_pens() local tb_texpos = dfhack.textures.getThinBordersTexposStart() VERT_TOP_PEN = to_pen{tile=tp(tb_texpos, 10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} - VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} - VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} + VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} + VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=193, fg=COLOR_GREY, bg=COLOR_BLACK} local cp_texpos = dfhack.textures.getControlPanelTexposStart() BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} From d04780ee86d6038b6a9f52abe2a17efa25cea5d9 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 20:38:57 +0200 Subject: [PATCH 0966/2222] tweaked some item names shown on buildingplan uis --- plugins/lua/buildingplan.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index a33c1684f..2de467f7c 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -103,6 +103,14 @@ function get_desc(filter) desc = 'Mechanism' elseif desc == 'Wood' then desc = 'Log' + elseif desc == 'Any weapon' then + desc = 'Weapon' + elseif desc == 'Any spike' then + desc = 'Spike' + elseif desc == 'Ballistapart' then + desc = 'Ballista part' + elseif desc == 'Catapultpart' then + desc = 'Catapult part' end return desc From 57146c527a04be2cf8ceb2cbbbfb4f2539a9ac69 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 21:20:45 +0200 Subject: [PATCH 0967/2222] added changelog entry --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b0b86c4cd..522ed00c0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,6 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- `gui`: added two new window borders, "BOLD_FRAME" and "INTERIOR_MEDIUM_FRAME" ## Removed From 4dc7b3cc43fc7b2bbf97781f2d6f081b7c3ab827 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 21:25:59 +0200 Subject: [PATCH 0968/2222] attempt to make changelog entry work --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 522ed00c0..d85231266 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,7 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua -- `gui`: added two new window borders, "BOLD_FRAME" and "INTERIOR_MEDIUM_FRAME" +- `widget`: added two new window borders, ``BOLD_FRAME`` and ``INTERIOR_MEDIUM_FRAME`` ## Removed From 3628f8c5545ad190620705311c33f84fa772c969 Mon Sep 17 00:00:00 2001 From: TaxiService Date: Mon, 3 Apr 2023 21:29:24 +0200 Subject: [PATCH 0969/2222] Update docs/changelog.txt YES PLS Co-authored-by: Myk --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d85231266..ff9d05b0f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,7 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua -- `widget`: added two new window borders, ``BOLD_FRAME`` and ``INTERIOR_MEDIUM_FRAME`` +- added two new window borders: ``gui.BOLD_FRAME`` for accented elements and ``gui.INTERIOR_MEDIUM_FRAME`` for a signature-less frame that's thicker than the existing ``gui.INTERIOR_FRAME`` ## Removed From 07a4da65736df10b07b36d7f8c70eec7a7b42969 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 22:23:32 +0200 Subject: [PATCH 0970/2222] reworked itemselection ui & added vertical divider pens --- docs/changelog.txt | 1 + plugins/lua/buildingplan/itemselection.lua | 81 ++++++++++++---------- plugins/lua/buildingplan/pens.lua | 6 ++ 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b0b86c4cd..dd1a29e28 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game +- `buildingplan`: rearranged elements of ``itemselection`` interface ## Documentation diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index 9cfe0f843..dfb77d1fd 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -55,7 +55,7 @@ end ItemSelection = defclass(ItemSelection, widgets.Window) ItemSelection.ATTRS{ frame_title='Choose items', - frame={w=56, h=20, l=4, t=8}, + frame={w=56, h=24, l=4, t=7}, resizable=true, index=DEFAULT_NIL, desc=DEFAULT_NIL, @@ -114,17 +114,8 @@ function ItemSelection:init() on_click=self:callback('submit'), visible=function() return self.num_selected >= self.quantity end, }, - widgets.FilteredList{ - view_id='flist', - frame={t=3, l=0, r=0, b=4}, - case_sensitive=false, - choices=choices, - icon_width=2, - on_submit=self:callback('toggle_group'), - edit_on_char=function(ch) return ch:match('[%l -]') end, - }, widgets.CycleHotkeyLabel{ - frame={l=0, b=2}, + frame={l=1, t=3}, key='CUSTOM_SHIFT_R', label='Sort by:', options={ @@ -134,53 +125,71 @@ function ItemSelection:init() }, on_change=self:callback('on_sort'), }, - widgets.HotkeyLabel{ - frame={l=0, b=1}, - key='SELECT', - label='Use all/none', - auto_width=true, - on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + widgets.FilteredList{ + view_id='flist', + frame={t=5, l=1, r=1, b=5}, + case_sensitive=false, + choices=choices, + icon_width=2, + on_submit=self:callback('toggle_group'), + edit_on_char=function(ch) return ch:match('[%l -]') end, }, - widgets.HotkeyLabel{ - frame={l=22, b=1}, - key='CUSTOM_SHIFT_C', - label='Continue', - auto_width=true, - on_activate=self:callback('submit'), + widgets.Label{ + frame={l=1, t=5}, + text_pen=COLOR_LIGHTCYAN, + text={">"}, }, - widgets.HotkeyLabel{ - frame={l=38, b=1}, - key='LEAVESCREEN', - label='Go back', - auto_width=true, - on_activate=self:callback('on_cancel'), + widgets.Panel{ + frame={l=0, t=6, r=0, b=4}, + frame_style=gui.INTERIOR_FRAME, }, widgets.HotkeyLabel{ - frame={l=0, b=0}, + frame={l=0, b=2}, key='KEYBOARD_CURSOR_RIGHT_FAST', - key_sep=' : ', + key_sep='ight: ', label='Use one', auto_width=true, on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, }, widgets.Label{ - frame={l=6, b=0, w=5}, + frame={l=6, b=2, w=5}, text_pen=COLOR_LIGHTGREEN, text='Right', }, widgets.HotkeyLabel{ - frame={l=23, b=0}, + frame={l=1, b=1}, key='KEYBOARD_CURSOR_LEFT_FAST', - key_sep=' : ', + key_sep='eft: ', label='Use one fewer', auto_width=true, on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, }, widgets.Label{ - frame={l=29, b=0, w=4}, + frame={l=7, b=1, w=4}, text_pen=COLOR_LIGHTGREEN, text='Left', }, + widgets.HotkeyLabel{ + frame={l=6, b=0}, + key='SELECT', + label='Use all/none', + auto_width=true, + on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.HotkeyLabel{ + frame={r=5, b=2}, + key='LEAVESCREEN', + label='Go back', + auto_width=true, + on_activate=self:callback('on_cancel'), + }, + widgets.HotkeyLabel{ + frame={r=4, b=0}, + key='CUSTOM_SHIFT_C', + label='Continue', + auto_width=true, + on_activate=self:callback('submit'), + }, } end @@ -234,7 +243,7 @@ function ItemSelection:get_choices(sort_fn) for desc,choice in pairs(buckets) do local data = choice.data choice.text = { - {width=10, text=function() return ('[%d/%d]'):format(data.selected, data.quantity) end}, + {width=10, text=function() return ('%d/%d'):format(data.selected, data.quantity) end}, {gap=2, text=desc}, } table.insert(choices, choice) diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index 973bb7bc6..288aea0db 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -2,6 +2,7 @@ local _ENV = mkmodule('plugins.buildingplan.pens') GOOD_TILE_PEN, BAD_TILE_PEN = nil, nil VERT_TOP_PEN, VERT_MID_PEN, VERT_BOT_PEN = nil, nil, nil +HORI_LEFT_PEN, HORI_MID_PEN, HORI_RIGHT_PEN = nil, nil, nil BUTTON_START_PEN, BUTTON_END_PEN = nil, nil SELECTED_ITEM_PEN = nil MINIMIZED_LEFT_PEN, MINIMIZED_RIGHT_PEN = nil, nil @@ -22,6 +23,11 @@ function reload_pens() VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} + local mb_texpos = dfhack.textures.getMediumBordersTexposStart() + HORI_LEFT_PEN = to_pen{tile=tp(mb_texpos, 12), ch=195, fg=COLOR_GREY, bg=COLOR_BLACK} + HORI_MID_PEN = to_pen{tile=tp(mb_texpos, 5), ch=196, fg=COLOR_GREY, bg=COLOR_BLACK} + HORI_RIGHT_PEN = to_pen{tile=tp(mb_texpos, 13), ch=180, fg=COLOR_GREY, bg=COLOR_BLACK} + local cp_texpos = dfhack.textures.getControlPanelTexposStart() BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} BUTTON_END_PEN = to_pen{tile=tp(cp_texpos, 15), ch=']', fg=COLOR_YELLOW} From ea9f3ef9d1a743945b29c4d70fcbb628cb579cfb Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 22:27:50 +0200 Subject: [PATCH 0971/2222] restored pens.lua (has nothing to do with itemselection) --- plugins/lua/buildingplan/pens.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index 288aea0db..973bb7bc6 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -2,7 +2,6 @@ local _ENV = mkmodule('plugins.buildingplan.pens') GOOD_TILE_PEN, BAD_TILE_PEN = nil, nil VERT_TOP_PEN, VERT_MID_PEN, VERT_BOT_PEN = nil, nil, nil -HORI_LEFT_PEN, HORI_MID_PEN, HORI_RIGHT_PEN = nil, nil, nil BUTTON_START_PEN, BUTTON_END_PEN = nil, nil SELECTED_ITEM_PEN = nil MINIMIZED_LEFT_PEN, MINIMIZED_RIGHT_PEN = nil, nil @@ -23,11 +22,6 @@ function reload_pens() VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=192, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} - local mb_texpos = dfhack.textures.getMediumBordersTexposStart() - HORI_LEFT_PEN = to_pen{tile=tp(mb_texpos, 12), ch=195, fg=COLOR_GREY, bg=COLOR_BLACK} - HORI_MID_PEN = to_pen{tile=tp(mb_texpos, 5), ch=196, fg=COLOR_GREY, bg=COLOR_BLACK} - HORI_RIGHT_PEN = to_pen{tile=tp(mb_texpos, 13), ch=180, fg=COLOR_GREY, bg=COLOR_BLACK} - local cp_texpos = dfhack.textures.getControlPanelTexposStart() BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} BUTTON_END_PEN = to_pen{tile=tp(cp_texpos, 15), ch=']', fg=COLOR_YELLOW} From 8860ddf9828a6a9d4aa67fcc0ddcb08f52fa5137 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Mon, 3 Apr 2023 22:46:31 +0200 Subject: [PATCH 0972/2222] planneroverlay: initial rework commit --- docs/changelog.txt | 1 + plugins/lua/buildingplan/pens.lua | 15 +- plugins/lua/buildingplan/planneroverlay.lua | 167 +++++++++++++------- 3 files changed, 119 insertions(+), 64 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ff9d05b0f..b20e103c2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game +- `buildingplan`: rearranged elements of ``planneroverlay`` interface ## Documentation diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index ed8f393d6..e69a4c210 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -2,9 +2,10 @@ local _ENV = mkmodule('plugins.buildingplan.pens') GOOD_TILE_PEN, BAD_TILE_PEN = nil, nil VERT_TOP_PEN, VERT_MID_PEN, VERT_BOT_PEN = nil, nil, nil +HORI_LEFT_PEN, HORI_MID_PEN, HORI_RIGHT_PEN = nil, nil, nil BUTTON_START_PEN, BUTTON_END_PEN = nil, nil SELECTED_ITEM_PEN = nil -MINIMIZED_LEFT_PEN, MINIMIZED_RIGHT_PEN = nil, nil +MINI_TEXT_PEN, MINI_TEXT_HPEN, MINI_BUTT_PEN, MINI_BUTT_HPEN = nil, nil, nil, nil local to_pen = dfhack.pen.parse @@ -22,14 +23,20 @@ function reload_pens() VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=193, fg=COLOR_GREY, bg=COLOR_BLACK} + local mb_texpos = dfhack.textures.getMediumBordersTexposStart() + HORI_LEFT_PEN = to_pen{tile=tp(mb_texpos, 12), ch=195, fg=COLOR_GREY, bg=COLOR_BLACK} + HORI_MID_PEN = to_pen{tile=tp(mb_texpos, 5), ch=196, fg=COLOR_GREY, bg=COLOR_BLACK} + HORI_RIGHT_PEN = to_pen{tile=tp(mb_texpos, 13), ch=180, fg=COLOR_GREY, bg=COLOR_BLACK} + local cp_texpos = dfhack.textures.getControlPanelTexposStart() BUTTON_START_PEN = to_pen{tile=tp(cp_texpos, 13), ch='[', fg=COLOR_YELLOW} BUTTON_END_PEN = to_pen{tile=tp(cp_texpos, 15), ch=']', fg=COLOR_YELLOW} SELECTED_ITEM_PEN = to_pen{tile=tp(cp_texpos, 9), ch=string.char(251), fg=COLOR_YELLOW} - local wb_texpos = dfhack.textures.getWindowBordersTexposStart() - MINIMIZED_LEFT_PEN = to_pen{tile=tp(wb_texpos, 0), ch=199, fg=COLOR_WHITE} - MINIMIZED_RIGHT_PEN = to_pen{tile=tp(wb_texpos, 2), ch=182, fg=COLOR_WHITE} + MINI_TEXT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_GREY} + MINI_TEXT_HPEN = to_pen{fg=COLOR_BLACK, bg=COLOR_WHITE} + MINI_BUTT_PEN = to_pen{fg=COLOR_BLACK, bg=COLOR_LIGHTRED} + MINI_BUTT_HPEN = to_pen{fg=COLOR_WHITE, bg=COLOR_RED} end reload_pens() diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 3c08597d3..8c67b59f5 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -207,20 +207,29 @@ ItemLine.ATTRS{ } function ItemLine:init() - self.frame.h = 1 + --self.frame.h = 2 self.visible = function() return #get_cur_filters() >= self.idx end self:addviews{ widgets.Label{ - frame={t=0, l=0}, - text='*', - auto_width=true, + frame={t=0, l=1}, + text=string.char(26), + --auto_width=true, visible=self.is_selected_fn, }, + widgets.Label{ + frame={t=0, l=2}, + text={ + {text=self:callback('get_item_line_text')}, + --{text='[filter]', pen=self:callback('get_f_pen')}, + --{text='[x]', pen=self:callback('get_x_pen')}, + }, + }, widgets.Label{ frame={t=0, l=25}, text={ - {tile=pens.BUTTON_START_PEN}, - {gap=6, tile=pens.BUTTON_END_PEN}, + --{tile=pens.BUTTON_START_PEN}, + --{gap=6, tile=pens.BUTTON_END_PEN}, + {text='[filter]', pen=self:callback('get_f_pen')}, }, auto_width=true, on_click=function() self.on_filter(self.idx) end, @@ -228,19 +237,17 @@ function ItemLine:init() widgets.Label{ frame={t=0, l=33}, text={ - {tile=pens.BUTTON_START_PEN}, - {gap=1, tile=pens.BUTTON_END_PEN}, + --{tile=pens.BUTTON_START_PEN}, + --{gap=1, tile=pens.BUTTON_END_PEN}, + {text='[x]', pen=self:callback('get_x_pen')}, }, auto_width=true, on_click=function() self.on_clear_filter(self.idx) end, }, widgets.Label{ - frame={t=0, l=2}, + frame={t=1, l=2}, text={ - {width=21, text=self:callback('get_item_line_text')}, - {gap=3, text='filter', pen=COLOR_GREEN}, - {gap=2, text='x', pen=self:callback('get_x_pen')}, - {gap=3, text=function() return self.note end, + {gap=2, text=function() return self.note end, pen=function() return self.note_pen end}, }, }, @@ -259,9 +266,13 @@ function ItemLine:onInput(keys) return ItemLine.super.onInput(self, keys) end +function ItemLine:get_f_pen() + return self.is_selected_fn and COLOR_LIGHTCYAN or COLOR_CYAN +end + function ItemLine:get_x_pen() return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) and - COLOR_GREEN or COLOR_GREY + COLOR_LIGHTRED or COLOR_BLACK end function ItemLine:get_item_line_text() @@ -276,13 +287,13 @@ function ItemLine:get_item_line_text() uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) if self.available >= quantity then self.note_pen = COLOR_GREEN - self.note = 'Available now' + self.note = string.char(192)..' Available now' else - self.note_pen = COLOR_YELLOW - self.note = 'Will link later' + self.note_pen = COLOR_BROWN + self.note = string.char(192)..' Will link later' end - return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') + return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and ' of ' or 's of ') end function ItemLine:reduce_quantity(used_quantity) @@ -307,10 +318,10 @@ end PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) PlannerOverlay.ATTRS{ - default_pos={x=5,y=9}, + default_pos={x=5,y=8}, default_enabled=true, viewscreens='dwarfmode/Building/Placement', - frame={w=56, h=20}, + frame={w=56, h=22}, } function PlannerOverlay:init() @@ -319,31 +330,32 @@ function PlannerOverlay:init() local main_panel = widgets.Panel{ view_id='main', - frame={t=0, l=0, r=0, h=14}, - frame_style=gui.MEDIUM_FRAME, + frame={t=1, l=0, r=0, h=14}, + frame_style=gui.INTERIOR_MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, visible=function() return not self.minimized end, } local minimized_panel = widgets.Panel{ - frame={t=0, r=0, w=4, h=1}, + frame={t=0, r=1, w=17, h=1}, subviews={ widgets.Label{ - frame={t=0, l=0, w=1, h=1}, - text={{tile=pens.MINIMIZED_LEFT_PEN}}, + frame={t=0, r=0, h=1}, + text={ + {text=' show Planner ', pen=pens.MINI_TEXT_PEN, hpen=pens.MINI_TEXT_HPEN}, + {text='['..string.char(31)..']', pen=pens.MINI_BUTT_PEN, hpen=pens.MINI_BUTT_HPEN}, + }, visible=function() return self.minimized end, - }, - widgets.Label{ - frame={t=0, l=1, w=2, h=1}, - text=string.char(31)..string.char(30), - text_pen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_GREY}, - text_hpen=dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE}, on_click=function() self.minimized = not self.minimized end, }, widgets.Label{ - frame={t=0, r=0, w=1, h=1}, - text={{tile=pens.MINIMIZED_RIGHT_PEN}}, - visible=function() return self.minimized end, + frame={t=0, r=0, h=1}, + text={ + {text=' hide Planner ', pen=pens.MINI_TEXT_PEN, hpen=pens.MINI_TEXT_HPEN}, + {text='['..string.char(30)..']', pen=pens.MINI_BUTT_PEN, hpen=pens.MINI_BUTT_HPEN}, + }, + visible=function() return not self.minimized end, + on_click=function() self.minimized = not self.minimized end, }, }, } @@ -387,7 +399,7 @@ function PlannerOverlay:init() on_clear_filter=self:callback('clear_filter')}, widgets.CycleHotkeyLabel{ view_id='hollow', - frame={t=3, l=4}, + frame={b=4, l=1, w=19}, key='CUSTOM_H', label='Hollow area:', visible=is_construction, @@ -398,7 +410,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel{ view_id='stairs_top_subtype', - frame={t=4, l=4}, + frame={b=5, l=23, w=28}, key='CUSTOM_R', label='Top Stair Type: ', visible=is_stairs, @@ -410,7 +422,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel { view_id='stairs_bottom_subtype', - frame={t=5, l=4}, + frame={b=4, l=23, w=28}, key='CUSTOM_B', label='Bottom Stair Type:', visible=is_stairs, @@ -422,17 +434,28 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel { view_id='weapons', - frame={t=5, l=4}, + frame={b=4, l=1, w=22}, key='CUSTOM_T', key_back='CUSTOM_SHIFT_T', - label='Num weapons:', + label='# of weapons:', visible=is_weapon_or_spike_trap, - options={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + options={ + {label='(1)', value=1, pen=COLOR_YELLOW}, + {label='(2)', value=2, pen=COLOR_YELLOW}, + {label='(3)', value=3, pen=COLOR_YELLOW}, + {label='(4)', value=4, pen=COLOR_YELLOW}, + {label='(5)', value=5, pen=COLOR_YELLOW}, + {label='(6)', value=6, pen=COLOR_YELLOW}, + {label='(7)', value=7, pen=COLOR_YELLOW}, + {label='(8)', value=8, pen=COLOR_YELLOW}, + {label='(9)', value=9, pen=COLOR_YELLOW}, + {label='(10)', value=10, pen=COLOR_YELLOW}, + }, on_change=function(val) weapon_quantity = val end, }, widgets.ToggleHotkeyLabel { view_id='engraved', - frame={t=5, l=4}, + frame={b=4, l=1, w=22}, key='CUSTOM_T', label='Engraved only:', visible=is_slab, @@ -441,7 +464,8 @@ function PlannerOverlay:init() end, }, widgets.Label{ - frame={b=3, l=17}, + frame={b=2, l=25}, + text_pen=dfhack.pen.parse{fg=COLOR_DARKGREY}, text={ 'Selected area: ', {text=function() @@ -457,29 +481,29 @@ function PlannerOverlay:init() visible=function() return #get_cur_filters() > 0 end, subviews={ widgets.HotkeyLabel{ - frame={b=1, l=0}, - key='STRING_A042', + frame={b=2, l=1}, + key='CUSTOM_SHIFT_Q', auto_width=true, enabled=function() return #get_cur_filters() > 1 end, on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, }, widgets.HotkeyLabel{ - frame={b=1, l=1}, - key='STRING_A047', - label='Prev/next item', + frame={b=2, l=2}, + key='CUSTOM_Q', + label='Prev/next', auto_width=true, enabled=function() return #get_cur_filters() > 1 end, on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, }, widgets.HotkeyLabel{ - frame={b=1, l=21}, + frame={b=1, l=1}, key='CUSTOM_F', label='Set filter', auto_width=true, on_activate=function() self:set_filter(self.selected) end, }, widgets.HotkeyLabel{ - frame={b=1, l=37}, + frame={b=0, l=1}, key='CUSTOM_X', label='Clear filter', auto_width=true, @@ -490,19 +514,20 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel{ view_id='choose', - frame={b=0, l=0}, - key='CUSTOM_I', - label='Item selection:', + frame={b=0, l=23}, + key='CUSTOM_Z', + label='Choose items:', + label_below=true, options={ - {label='Use filters', value=0}, + {label='with Filters', value=0}, { label=function() local automaterial = itemselection.get_automaterial_selection(uibs.building_type) - return ('Last choice (%s)'):format(automaterial or 'Will ask') + return ('Last used (%s)'):format(automaterial or 'n/a') end, value=2, }, - {label='Manual choice', value=1}, + {label='Manually', value=1}, }, initial_option=0, on_change=function(choose) @@ -511,7 +536,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel{ view_id='safety', - frame={b=0, l=29, w=25}, + frame={b=2, l=23, w=25}, key='CUSTOM_G', label='Building safety:', options={ @@ -529,23 +554,30 @@ function PlannerOverlay:init() }, } + local divider_widget = widgets.Panel{ + view_id='divider', + frame={t=10, l=0, r=0, h=1}, + on_render=self:callback('draw_divider_h'), + visible=function() return not self.minimized end, + } + local error_panel = widgets.ResizingPanel{ view_id='errors', - frame={t=14, l=0, r=0}, - frame_style=gui.MEDIUM_FRAME, + frame={t=15, l=0, r=0}, + frame_style=gui.BOLD_FRAME, frame_background=gui.CLEAR_PEN, visible=function() return not self.minimized end, } error_panel:addviews{ widgets.WrappedLabel{ - frame={t=0, l=0, r=0}, + frame={t=0, l=1, r=0}, text_pen=COLOR_LIGHTRED, text_to_wrap=get_placement_errors, visible=function() return #uibs.errors > 0 end, }, widgets.Label{ - frame={t=0, l=0, r=0}, + frame={t=0, l=1, r=0}, text_pen=COLOR_GREEN, text='OK to build', visible=function() return #uibs.errors == 0 end, @@ -556,9 +588,24 @@ function PlannerOverlay:init() main_panel, minimized_panel, error_panel, + divider_widget, } end +function PlannerOverlay:draw_divider_h(dc) + local x2 = dc.width -1 + for x=0,x2 do + dc:seek(x, 0) + if x == 0 then + dc:char(nil, pens.HORI_LEFT_PEN) + elseif x == x2 then + dc:char(nil, pens.HORI_RIGHT_PEN) + else + dc:char(nil, pens.HORI_MID_PEN) + end + end +end + function PlannerOverlay:reset() self.subviews.item1:reset() self.subviews.item2:reset() From 824f7ed038066ea5a3c2666eb489e8518cdbc830 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Tue, 4 Apr 2023 00:08:01 +0200 Subject: [PATCH 0973/2222] clean up Items.cpp quality symbols definitions --- library/modules/Items.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 303f9771e..03d6cda4a 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -720,13 +720,14 @@ df::coord Items::getPosition(df::item *item) return item->pos; } -// These '\xFF' chars refer to quality markers from curses.png, namely: 250 (≡), 15 (☼), 174 («) and 175 (»). -static const char MARKER_EXCEPTIONAL = '\xF0'; -static const char MARKER_MASTERWORK = '\x0F'; -static const char MARKER_IMPROVED_LEFT = '\xAE'; -static const char MARKER_IMPROVED_RIGHT = '\xAF'; - -static char quality_table[] = { 0, '-', '+', '*', MARKER_EXCEPTIONAL, MARKER_MASTERWORK }; +static const char quality_table[] = { + '\0', // (base) + '-', // well-crafted + '+', // finely-crafted + '*', // superior quality + '\xF0', // (≡) exceptional + '\x0F' // (☼) masterful +}; static void addQuality(std::string &tmp, int quality) { @@ -831,7 +832,7 @@ std::string Items::getDescription(df::item *item, int type, bool decorate) addQuality(tmp, item->getQuality()); if (item->isImproved()) { - tmp = MARKER_IMPROVED_LEFT + tmp + MARKER_IMPROVED_RIGHT; + tmp = '\xAE' + tmp + '\xAF'; // («) + tmp + (») addQuality(tmp, item->getImprovementQuality()); } } From cf847109ce84f87047dd1c8620b01edacd57b930 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 3 Apr 2023 15:59:42 -0700 Subject: [PATCH 0974/2222] read mods from all mod dirs and only use most recent versions --- docs/Core.rst | 37 ++++++++---- docs/changelog.txt | 2 + docs/guides/modding-guide.rst | 26 ++++---- library/Core.cpp | 58 +++--------------- library/LuaApi.cpp | 8 +-- library/lua/script-manager.lua | 106 +++++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 83 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index c06a413da..5decd668d 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -243,18 +243,31 @@ For example, if ``teleport`` is run, these folders are searched in order for Scripts in installed mods ......................... -Script directories in installed mods are automatically added to the script path -according to the following rules: - -**If a world is not loaded**, then directories matching the pattern -``data/installed_mods/*/scripts_modinstalled/`` are added to the script path -in alphabetical order. - -**If a world is loaded**, then the ``scripts_modactive`` directories of active -mods are also added to the script path according to the active mod load order, -and scripts in active mods take precedence over scripts in -``scripts_modinstalled`` in non-active mods. For example, the search paths for -mods might look like this:: +Scripts in mods are automatically added to the script path. The following +directories are searched for mods:: + + ../../workshop/content/975370/ (the DF Steam workshop directory) + mods/ + data/installed_mods/ + +Each mod can have two directories that contain scripts: + +- ``scripts_modactive/`` is added to the script path if and only if the mod is + active in the loaded world. +- ``scripts_modinstalled/`` is added to the script path as long as the mod is + installed in one of the searched mod directories. + +Multiple versions of a mod may be installed at the same time. If a mod is +active in a loaded world, then the scripts for the version of the mod that is +active will be added to the script path. Otherwise, the latest version of each +mod is added to the script path. + +Scripts for active mods take precedence according to their load order when you +generated the current world. + +Scripts for non-active mods are ordered by their containing mod's ID. + +For example, the search paths for mods might look like this:: activemod_last_in_load_order/scripts_modactive activemod_last_in_load_order/scripts_modinstalled diff --git a/docs/changelog.txt b/docs/changelog.txt index b0b86c4cd..c63168dca 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game +- Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game +- Mods: scripts from only the most recent version of an installed mod are added to the script path ## Documentation diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index b386b48c3..0c9513fef 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -77,8 +77,11 @@ Let's go through that line by line. If you develop your mod using version control (recommended!), that :file:`README.md` file can also serve as your git repository documentation. -These files end up in a subdirectory under :file:`data/installed_mods/` when -the mod is selected as "active" for the first time. +These files end up in a subdirectory under :file:`mods/` when players copy them +in or install them from the +`Steam Workshop `__, and in +:file:`data/installed_mods/` when the mod is selected as "active" for the first +time. What if I just want to distribute a simple script? -------------------------------------------------- @@ -95,13 +98,6 @@ DFHack to find it and add your mod to the `script-paths`. Your script will be runnable from the title screen and in any loaded world, regardless of whether your mod is explicitly "active". -Be sure to remind players to mark your mod as "active" at least once so it gets -installed to the :file:`data/installed_mods/` folder. They may have to create a -new world just so they can mark the mod as "active". This is true both for -players who copied the mod into the :file:`mods/` folder manually and for -players who subscribed via -`Steam Workshop `__. - A mod-maker's development environment ------------------------------------- @@ -109,11 +105,11 @@ Create a folder for development somewhere outside your Dwarf Fortress installation directory (e.g. ``/path/to/mymods/``). If you work on multiple mods, you might want to make a subdirectory for each mod. -If you have changes to the raws, you'll have to copy them into DF's ``data/ -installed_mods/`` folder to have them take effect, but you can set things up so -that scripts are run directly from your dev directory. This way, you can edit -your scripts and have the changes available in the game immediately: no -copying, no restarting. +If you have changes to the raws, you'll have to copy them into DF's +``data/installed_mods/`` folder to have them take effect, but you can set +things up so that scripts are run directly from your dev directory. This way, +you can edit your scripts and have the changes available in the game +immediately: no copying, no restarting. How does this magic work? Just add a line like this to your ``dfhack-config/script-paths.txt`` file:: @@ -123,7 +119,7 @@ How does this magic work? Just add a line like this to your Then that directory will be searched when you run DFHack commands from inside the game. The ``+`` at the front of the path means to search that directory first, before any other script directory (like :file:`hack/scripts` or other -versions of your mod in ``data/installed_mods/``). +versions of your mod in the DF mod folders). The structure of the game ------------------------- diff --git a/library/Core.cpp b/library/Core.cpp index b5337b827..d13c82fa5 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -105,7 +105,6 @@ DBG_DECLARE(core,script,DebugCategory::LINFO); static const std::string CONFIG_PATH = "dfhack-config/"; static const std::string CONFIG_DEFAULTS_PATH = "hack/data/dfhack-config-defaults/"; -static const std::string MOD_PATH = "data/installed_mods/"; class MainThread { public: @@ -534,60 +533,19 @@ bool loadScriptPaths(color_ostream &out, bool silent = false) return true; } -bool loadModScriptPaths(color_ostream &out) { - std::map files; - Filesystem::listdir_recursive(MOD_PATH, files, 0); - - DEBUG(script,out).print("found %zd installed mods\n", files.size()); - if (!files.size()) - return true; - - for (auto & entry : files) { - DEBUG(script,out).print(" %s\n", entry.first.c_str()); - } - - std::vector mod_paths; - if (Core::getInstance().isWorldLoaded()) { - DEBUG(script,out).print("active load order:\n"); - for (auto & path : df::global::world->object_loader.object_load_order_src_dir) { - DEBUG(script,out).print(" %s\n", path->c_str()); - if (0 == path->find(MOD_PATH)) - mod_paths.emplace_back(*path); - } - } - +static void loadModScriptPaths(color_ostream &out) { + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); std::vector mod_script_paths; - for (auto pathit = mod_paths.rbegin(); pathit != mod_paths.rend(); ++pathit) { - std::string active_path = *pathit + "scripts_modactive"; - std::string installed_path = *pathit + "scripts_modinstalled"; - DEBUG(script,out).print("checking active path: %s\n", pathit->c_str()); - if (Filesystem::isdir(active_path)) - mod_script_paths.emplace_back(active_path); - if (Filesystem::isdir(installed_path)) - mod_script_paths.emplace_back(installed_path); - std::string slashless = *pathit; - slashless.resize(slashless.size()-1); - if (0 == files.erase(slashless)) { - WARN(script,out).print("script path not found: '%s'\n", pathit->c_str()); - } - } - - for (auto & entry : files) { - if (!entry.second) - continue; - DEBUG(script,out).print("checking inactive path: %s\n", entry.first.c_str()); - std::string installed_path = entry.first + "/scripts_modinstalled"; - if (Filesystem::isdir(installed_path)) - mod_script_paths.emplace_back(installed_path); - } - + Lua::CallLuaModuleFunction(out, L, "script-manager", "get_mod_script_paths", 0, 1, + Lua::DEFAULT_LUA_LAMBDA, + [&](lua_State *L) { + Lua::GetVector(L, mod_script_paths); + }); DEBUG(script,out).print("final mod script paths:\n"); for (auto & path : mod_script_paths) DEBUG(script,out).print(" %s\n", path.c_str()); - Core::getInstance().setModScriptPaths(mod_script_paths); - - return true; } static std::map state_change_event_map; diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index c9bdc3021..60d533891 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2728,13 +2728,9 @@ static int filesystem_listdir_recursive(lua_State *L) include_prefix = lua_toboolean(L, 3); std::map files; int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix); - if (err) - { + if (err != -1) { lua_pushnil(L); - if (err == -1) - lua_pushfstring(L, "max depth exceeded: %d", depth); - else - lua_pushstring(L, strerror(err)); + lua_pushstring(L, strerror(err)); lua_pushinteger(L, err); return 3; } diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index cc5dd9fe3..1a7161a10 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -2,6 +2,9 @@ local _ENV = mkmodule('script-manager') local utils = require('utils') +--------------------- +-- enabled API + -- for each script that can be loaded as a module, calls cb(script_name, env) function foreach_module_script(cb) for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do @@ -57,4 +60,107 @@ function list() end end +--------------------- +-- mod script paths + +-- this perhaps could/should be queried from the Steam API +-- are there any installation configurations where this will be wrong, though? +local WORKSHOP_MODS_PATH = '../../workshop/content/975370/' +local MODS_PATH = 'mods/' +local INSTALLED_MODS_PATH = 'data/installed_mods/' + +-- last instance of the same version of the same mod wins, so read them in this +-- order (in increasing order of liklihood that players may have made custom +-- changes to the files) +local MOD_PATH_ROOTS = {WORKSHOP_MODS_PATH, MODS_PATH, INSTALLED_MODS_PATH} + +local function get_mod_id_and_version(path) + local idfile = path .. '/info.txt' + local ok, lines = pcall(io.lines, idfile) + if not ok then return end + local id, version + for line in lines do + if not id then + _,_,id = line:find('^%[ID:([^%]]+)%]') + end + if not version then + -- note this doesn't include the closing brace since some people put + -- non-number characters in here, and DF only reads the digits as the + -- numeric version + _,_,version = line:find('^%[NUMERIC_VERSION:(%d+)') + end + -- note that we do *not* want to break out of this loop early since + -- lines has to hit EOF to close the file + end + return id, version +end + +local function add_script_path(mod_script_paths, path) + if dfhack.filesystem.isdir(path) then + print('indexing scripts from mod script path: ' .. path) + table.insert(mod_script_paths, path) + end +end + +local function add_script_paths(mod_script_paths, base_path, include_modactive) + if not base_path:endswith('/') then + base_path = base_path .. '/' + end + if include_modactive then + add_script_path(mod_script_paths, base_path..'scripts_modactive') + end + add_script_path(mod_script_paths, base_path..'scripts_modinstalled') +end + +function get_mod_script_paths() + -- ordered map of mod id -> {handled=bool, versions=map of version -> path} + local mods = utils.OrderedTable() + local mod_script_paths = {} + + -- if a world is loaded, process active mods first, and lock to active version + if dfhack.isWorldLoaded() then + for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do + path = tostring(path) + if not path:startswith(INSTALLED_MODS_PATH) then goto continue end + local id = get_mod_id_and_version(path) + if not id then goto continue end + mods[id] = {handled=true} + add_script_paths(mod_script_paths, path, true) + ::continue:: + end + end + + -- assemble version -> path maps for all (non-handled) mod source dirs + for _,mod_path_root in ipairs(MOD_PATH_ROOTS) do + local files = dfhack.filesystem.listdir_recursive(mod_path_root, 0) + if not files then goto skip_path_root end + for _,f in ipairs(files) do + if not f.isdir then goto continue end + local id, version = get_mod_id_and_version(f.path) + if not id or not version then goto continue end + local mod = ensure_key(mods, id) + if mod.handled then goto continue end + ensure_key(mod, 'versions')[version] = f.path + ::continue:: + end + ::skip_path_root:: + end + + -- add script paths from most recent version of all not-yet-handled mods + for _,v in pairs(mods) do + if v.handled then goto continue end + local max_version, path + for version,mod_path in pairs(v.versions) do + if not max_version or max_version < version then + path = mod_path + max_version = version + end + end + add_script_paths(mod_script_paths, path) + ::continue:: + end + + return mod_script_paths +end + return _ENV From e8fb2c5a4671e613dcb3c6b58a4c76f786e0c11c Mon Sep 17 00:00:00 2001 From: TaxiService Date: Tue, 4 Apr 2023 01:37:03 +0200 Subject: [PATCH 0975/2222] Update docs/changelog.txt Co-authored-by: Myk --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index dd1a29e28..cbc7b284d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,7 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game -- `buildingplan`: rearranged elements of ``itemselection`` interface +-@ `buildingplan`: rearranged elements of ``itemselection`` interface ## Documentation From 0e3df4e3a5dbc8fc30a7831c0530946588a996f6 Mon Sep 17 00:00:00 2001 From: TaxiService Date: Tue, 4 Apr 2023 03:28:23 +0200 Subject: [PATCH 0976/2222] Update docs/changelog.txt Co-authored-by: Myk --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b20e103c2..15a30b9e2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,7 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game -- `buildingplan`: rearranged elements of ``planneroverlay`` interface +-@ `buildingplan`: rearranged elements of ``planneroverlay`` interface ## Documentation From 0cf4497328807fe9d2b3531e15fe498040d013e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 02:55:13 +0000 Subject: [PATCH 0977/2222] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.21.0 → 0.22.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.21.0...0.22.0) - [github.com/Lucas-C/pre-commit-hooks: v1.4.2 → v1.5.1](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.4.2...v1.5.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 94bfad310..c305de56c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: args: ['--fix=lf'] - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.21.0 + rev: 0.22.0 hooks: - id: check-github-workflows - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.4.2 + rev: v1.5.1 hooks: - id: forbid-tabs exclude_types: From d12938fb238f24e2dcfbe4e8703f7ea16eb7a71e Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:13:18 +0000 Subject: [PATCH 0978/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 739d17867..d7f07e54b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 739d1786723bbe912f448064c5705092a7936cc6 +Subproject commit d7f07e54b4ea7832b226f8d8678906c9c366918a diff --git a/scripts b/scripts index d2dad272e..53f0aedf9 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d2dad272e4b24c043ca62f843511c763fb1f67b5 +Subproject commit 53f0aedf9f7df33a4f79246ba46de02794619d09 From aa8870fcad5dc3811ffe21157508ca616fba79bd Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Tue, 4 Apr 2023 17:38:38 +0200 Subject: [PATCH 0979/2222] custom EditField & reorganized panels --- plugins/lua/buildingplan/itemselection.lua | 237 ++++++++++++--------- 1 file changed, 134 insertions(+), 103 deletions(-) diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index dfb77d1fd..e8687c01d 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -81,116 +81,147 @@ function ItemSelection:init() end self:addviews{ - widgets.Label{ - frame={t=0, l=0, r=16}, - text={ - self.desc, plural, NEWLINE, - ('Select up to %d item%s ('):format(self.quantity, plural), - {text=function() return self.num_selected end}, - ' selected)', - }, - }, - widgets.Label{ - frame={r=0, w=15, t=0, h=3}, - text_pen=BUILD_TEXT_PEN, - text_hpen=BUILD_TEXT_HPEN, - text={ - ' Use filter ', NEWLINE, - ' for remaining ', NEWLINE, - ' items ', - }, - on_click=self:callback('submit'), - visible=function() return self.num_selected < self.quantity end, - }, - widgets.Label{ - frame={r=0, w=15, t=0, h=3}, - text_pen=BUILD_TEXT_PEN, - text_hpen=BUILD_TEXT_HPEN, - text={ - ' ', NEWLINE, - ' Continue ', NEWLINE, - ' ', + widgets.Panel{ + view_id='header', + frame={t=0, h=3}, + subviews={ + widgets.Label{ + frame={t=0, l=0, r=16}, + text={ + self.desc, plural, NEWLINE, + ('Select up to %d item%s ('):format(self.quantity, plural), + {text=function() return self.num_selected end}, + ' selected)', + }, + }, + widgets.Label{ + frame={r=0, w=15, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' Use filter ', NEWLINE, + ' for remaining ', NEWLINE, + ' items ', + }, + on_click=self:callback('submit'), + visible=function() return self.num_selected < self.quantity end, + }, + widgets.Label{ + frame={r=0, w=15, t=0, h=3}, + text_pen=BUILD_TEXT_PEN, + text_hpen=BUILD_TEXT_HPEN, + text={ + ' ', NEWLINE, + ' Continue ', NEWLINE, + ' ', + }, + on_click=self:callback('submit'), + visible=function() return self.num_selected >= self.quantity end, + }, }, - on_click=self:callback('submit'), - visible=function() return self.num_selected >= self.quantity end, }, - widgets.CycleHotkeyLabel{ - frame={l=1, t=3}, - key='CUSTOM_SHIFT_R', - label='Sort by:', - options={ - {label='Recently used', value=sort_by_recency}, - {label='Name', value=sort_by_name}, - {label='Amount', value=sort_by_quantity}, + } + + self:addviews{ + widgets.Panel{ + view_id='body', + frame={t=self.subviews.header.frame.h, b=4}, + subviews={ + widgets.EditField{ + view_id='search', + frame={l=1, t=0}, + label_text='Search: ', + on_char=function(ch) return ch:match('[%l -]') end, + }, + widgets.CycleHotkeyLabel{ + frame={l=1, t=2}, + key='CUSTOM_SHIFT_R', + label='Sort by:', + options={ + {label='Recently used', value=sort_by_recency}, + {label='Name', value=sort_by_name}, + {label='Amount', value=sort_by_quantity}, + }, + on_change=self:callback('on_sort'), + }, + widgets.Panel{ + frame={l=0, t=3, r=0, b=0}, + frame_style=gui.INTERIOR_FRAME, + subviews={ + widgets.FilteredList{ + view_id='flist', + frame={t=0, b=0}, + case_sensitive=false, + choices=choices, + icon_width=2, + on_submit=self:callback('toggle_group'), + }, + }, + }, }, - on_change=self:callback('on_sort'), - }, - widgets.FilteredList{ - view_id='flist', - frame={t=5, l=1, r=1, b=5}, - case_sensitive=false, - choices=choices, - icon_width=2, - on_submit=self:callback('toggle_group'), - edit_on_char=function(ch) return ch:match('[%l -]') end, - }, - widgets.Label{ - frame={l=1, t=5}, - text_pen=COLOR_LIGHTCYAN, - text={">"}, }, widgets.Panel{ - frame={l=0, t=6, r=0, b=4}, - frame_style=gui.INTERIOR_FRAME, - }, - widgets.HotkeyLabel{ - frame={l=0, b=2}, - key='KEYBOARD_CURSOR_RIGHT_FAST', - key_sep='ight: ', - label='Use one', - auto_width=true, - on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=6, b=2, w=5}, - text_pen=COLOR_LIGHTGREEN, - text='Right', - }, - widgets.HotkeyLabel{ - frame={l=1, b=1}, - key='KEYBOARD_CURSOR_LEFT_FAST', - key_sep='eft: ', - label='Use one fewer', - auto_width=true, - on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.Label{ - frame={l=7, b=1, w=4}, - text_pen=COLOR_LIGHTGREEN, - text='Left', - }, - widgets.HotkeyLabel{ - frame={l=6, b=0}, - key='SELECT', - label='Use all/none', - auto_width=true, - on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, - }, - widgets.HotkeyLabel{ - frame={r=5, b=2}, - key='LEAVESCREEN', - label='Go back', - auto_width=true, - on_activate=self:callback('on_cancel'), - }, - widgets.HotkeyLabel{ - frame={r=4, b=0}, - key='CUSTOM_SHIFT_C', - label='Continue', - auto_width=true, - on_activate=self:callback('submit'), + view_id='footer', + frame={l=1, r=1, b=0, h=3}, + subviews={ + --[[ + add an explanation for this terribleness + --]] + widgets.HotkeyLabel{ + frame={l=0, h=1, t=0}, + key='KEYBOARD_CURSOR_RIGHT_FAST', + key_sep='----: ', --these hypens are overwritten by the next Label + label='Use one', + auto_width=true, + on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=6, w=5, t=0}, + text_pen=COLOR_LIGHTGREEN, + text='Right', + }, + widgets.HotkeyLabel{ + frame={l=1, h=1, t=1}, + key='KEYBOARD_CURSOR_LEFT_FAST', + key_sep='---: ', --these hypens are overwritten by the next Label + label='Use one fewer', + auto_width=true, + on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.Label{ + frame={l=7, w=4, t=1}, + text_pen=COLOR_LIGHTGREEN, + text='Left', + }, + widgets.HotkeyLabel{ + frame={l=6, t=2, h=2}, + key='SELECT', + label='Use all/none', + auto_width=true, + on_activate=function() self:toggle_group(self.subviews.flist.list:getSelected()) end, + }, + widgets.HotkeyLabel{ + frame={r=5, t=0}, + key='LEAVESCREEN', + label='Go back', + auto_width=true, + on_activate=self:callback('on_cancel'), + }, + widgets.HotkeyLabel{ + frame={r=4, t=2}, + key='CUSTOM_SHIFT_C', + label='Continue', + auto_width=true, + on_activate=self:callback('submit'), + }, + }, }, } + + self.subviews.flist.list.frame.t = 0 + self.subviews.flist.edit.visible = false + self.subviews.flist.edit = self.subviews.search + self.subviews.search.on_change = self.subviews.flist:callback('onFilterChange') end -- resort and restore selection From 59b79472301f88ba94775608afb3684478cce31e Mon Sep 17 00:00:00 2001 From: TaxiService Date: Tue, 4 Apr 2023 18:31:04 +0200 Subject: [PATCH 0980/2222] removed trailing whitespace... --- plugins/lua/buildingplan/itemselection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index e8687c01d..782ac915b 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -164,7 +164,7 @@ function ItemSelection:init() view_id='footer', frame={l=1, r=1, b=0, h=3}, subviews={ - --[[ + --[[ add an explanation for this terribleness --]] widgets.HotkeyLabel{ From 0c91644c5e1c20b8dcccc6b3de798a2437e4ca6e Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Tue, 4 Apr 2023 22:34:47 +0200 Subject: [PATCH 0981/2222] various tweaks and fixes --- plugins/lua/buildingplan/planneroverlay.lua | 42 ++++++++++++--------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 8c67b59f5..977fb154c 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -207,38 +207,38 @@ ItemLine.ATTRS{ } function ItemLine:init() - --self.frame.h = 2 + self.frame.h = 2 self.visible = function() return #get_cur_filters() >= self.idx end self:addviews{ widgets.Label{ + view_id='item_symbol', frame={t=0, l=1}, text=string.char(26), - --auto_width=true, + auto_width=true, visible=self.is_selected_fn, }, + } + self:addviews{ widgets.Label{ + view_id='item_info', frame={t=0, l=2}, text={ {text=self:callback('get_item_line_text')}, - --{text='[filter]', pen=self:callback('get_f_pen')}, - --{text='[x]', pen=self:callback('get_x_pen')}, }, }, + } + self:addviews{ widgets.Label{ - frame={t=0, l=25}, + frame={t=0, l=28}, text={ - --{tile=pens.BUTTON_START_PEN}, - --{gap=6, tile=pens.BUTTON_END_PEN}, {text='[filter]', pen=self:callback('get_f_pen')}, }, auto_width=true, on_click=function() self.on_filter(self.idx) end, }, widgets.Label{ - frame={t=0, l=33}, + frame={t=0, l=36}, text={ - --{tile=pens.BUTTON_START_PEN}, - --{gap=1, tile=pens.BUTTON_END_PEN}, {text='[x]', pen=self:callback('get_x_pen')}, }, auto_width=true, @@ -266,8 +266,8 @@ function ItemLine:onInput(keys) return ItemLine.super.onInput(self, keys) end -function ItemLine:get_f_pen() - return self.is_selected_fn and COLOR_LIGHTCYAN or COLOR_CYAN +function ItemLine:get_f_pen() -- TODO: make this thing work. I've tried many things to no avail. -taxi + return self.selected and COLOR_LIGHTCYAN or COLOR_CYAN end function ItemLine:get_x_pen() @@ -293,7 +293,7 @@ function ItemLine:get_item_line_text() self.note = string.char(192)..' Will link later' end - return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and ' of ' or 's of ') + return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') end function ItemLine:reduce_quantity(used_quantity) @@ -318,7 +318,7 @@ end PlannerOverlay = defclass(PlannerOverlay, overlay.OverlayWidget) PlannerOverlay.ATTRS{ - default_pos={x=5,y=8}, + default_pos={x=5,y=9}, default_enabled=true, viewscreens='dwarfmode/Building/Placement', frame={w=56, h=22}, @@ -432,7 +432,7 @@ function PlannerOverlay:init() {label='Up', value=df.construction_type.UpStair}, }, }, - widgets.CycleHotkeyLabel { + widgets.CycleHotkeyLabel { -- TODO: this thing also needs a slider view_id='weapons', frame={b=4, l=1, w=22}, key='CUSTOM_T', @@ -464,7 +464,7 @@ function PlannerOverlay:init() end, }, widgets.Label{ - frame={b=2, l=25}, + frame={b=2, l=23}, text_pen=dfhack.pen.parse{fg=COLOR_DARKGREY}, text={ 'Selected area: ', @@ -519,9 +519,9 @@ function PlannerOverlay:init() label='Choose items:', label_below=true, options={ - {label='with Filters', value=0}, + {label='with filters', value=0}, { - label=function() + label=function() -- TODO: hide this option if last used mat does not exist yet local automaterial = itemselection.get_automaterial_selection(uibs.building_type) return ('Last used (%s)'):format(automaterial or 'n/a') end, @@ -589,6 +589,12 @@ function PlannerOverlay:init() minimized_panel, error_panel, divider_widget, + widgets.Panel{ + frame={t=0, l=1, w=37, h=1}, + frame_inset=0, + frame_background=gui.CLEAR_PEN, + visible=function() return not self.minimized end, + }, } end From 0cebad10785423cd5f6f3b854eb8594738eaed02 Mon Sep 17 00:00:00 2001 From: silverflyone Date: Wed, 5 Apr 2023 11:49:14 +1000 Subject: [PATCH 0982/2222] Update Buildings.cpp Removed extra min/max checks. --- library/modules/Buildings.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 172975e21..23b3263f3 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1649,11 +1649,11 @@ StockpileIterator& StockpileIterator::operator++() { while (current >= block->items.size()) { // Out of items in this block; find the next block to search. - if (std::max(block->map_pos.x + 16, 0) <= std::min(std::max(stockpile->x2, 0), world->map.x_count-1)) { - block = Maps::getTileBlock(std::min(std::max(block->map_pos.x + 16, 0), world->map.x_count-1), block->map_pos.y, stockpile->z); + if (block->map_pos.x + 16 <= std::min(stockpile->x2, world->map.x_count-1)) { + block = Maps::getTileBlock(block->map_pos.x + 16, block->map_pos.y, stockpile->z); current = 0; - } else if (std::max(block->map_pos.y + 16, 0) <= std::min(std::max(stockpile->y2, 0), world->map.y_count-1)) { - block = Maps::getTileBlock(std::min(std::max(stockpile->x1, 0), world->map.x_count-1), std::min(std::max(block->map_pos.y + 16, 0), world->map.y_count-1), stockpile->z); + } else if (block->map_pos.y + 16 <= std::min(stockpile->y2, world->map.y_count-1)) { + block = Maps::getTileBlock(std::max(stockpile->x1, 0), block->map_pos.y + 16, stockpile->z); current = 0; } else { // All items in all blocks have been checked. From d42d9ec6268ef7654c386a67b1ef9da9cd91b699 Mon Sep 17 00:00:00 2001 From: silverflyone Date: Wed, 5 Apr 2023 12:53:39 +1000 Subject: [PATCH 0983/2222] Update changelog.txt Fix log entry --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ff9d05b0f..7cc0ed0c0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar - ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls +- ``Buildings::StockpileIterator:``: fix to use world map boundaries when stockpile coordinates exist outside of the world map. Fixes unhandled exception for `combine`. ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game From 7294678e2757f671ff44ffd88ef3655ca90c7920 Mon Sep 17 00:00:00 2001 From: Myk Date: Tue, 4 Apr 2023 22:42:58 -0700 Subject: [PATCH 0984/2222] Update docs/changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7cc0ed0c0..7f3262099 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,7 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar - ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls -- ``Buildings::StockpileIterator:``: fix to use world map boundaries when stockpile coordinates exist outside of the world map. Fixes unhandled exception for `combine`. +- `combine`: fix error processing stockpiles with boundaries that extend outside of the map ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game From c490a9ebc2fb92ebcd018b9c0d5192ba01152d81 Mon Sep 17 00:00:00 2001 From: TaxiService Date: Wed, 5 Apr 2023 15:55:47 +0200 Subject: [PATCH 0985/2222] updated comments in the footer area --- plugins/lua/buildingplan/itemselection.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index 782ac915b..84e866502 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -164,13 +164,10 @@ function ItemSelection:init() view_id='footer', frame={l=1, r=1, b=0, h=3}, subviews={ - --[[ - add an explanation for this terribleness - --]] widgets.HotkeyLabel{ frame={l=0, h=1, t=0}, key='KEYBOARD_CURSOR_RIGHT_FAST', - key_sep='----: ', --these hypens are overwritten by the next Label + key_sep='----: ', -- these hypens function as "padding" to be overwritten by the next Label label='Use one', auto_width=true, on_activate=function() self:increment_group(self.subviews.flist.list:getSelected()) end, @@ -178,12 +175,12 @@ function ItemSelection:init() widgets.Label{ frame={l=6, w=5, t=0}, text_pen=COLOR_LIGHTGREEN, - text='Right', + text='Right', -- this overrides the "6----" characters from the previous HotkeyLabel }, widgets.HotkeyLabel{ frame={l=1, h=1, t=1}, key='KEYBOARD_CURSOR_LEFT_FAST', - key_sep='---: ', --these hypens are overwritten by the next Label + key_sep='---: ', -- these hypens function as "padding" to be overwritten by the next Label label='Use one fewer', auto_width=true, on_activate=function() self:decrement_group(self.subviews.flist.list:getSelected()) end, @@ -191,7 +188,7 @@ function ItemSelection:init() widgets.Label{ frame={l=7, w=4, t=1}, text_pen=COLOR_LIGHTGREEN, - text='Left', + text='Left', -- this overrides the "4---" characters from the previous HotkeyLabel }, widgets.HotkeyLabel{ frame={l=6, t=2, h=2}, From 1ed25cdd72c9e966f17b8d3d74ea97f5fe8e36a1 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 6 Apr 2023 07:12:58 +0000 Subject: [PATCH 0986/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 53f0aedf9..e6216cc28 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 53f0aedf9f7df33a4f79246ba46de02794619d09 +Subproject commit e6216cc28e4315df5fb128411d0ca57fe78ccb2b From 70b8c831788ccedde4ca429d1ed39b0d950c68dd Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 6 Apr 2023 16:08:09 +0200 Subject: [PATCH 0987/2222] highlight selected item, + various tweaks --- plugins/lua/buildingplan/planneroverlay.lua | 48 ++++++++++++--------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 977fb154c..d4e4acc08 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -212,36 +212,42 @@ function ItemLine:init() self:addviews{ widgets.Label{ view_id='item_symbol', - frame={t=0, l=1}, - text=string.char(26), + frame={t=0, l=0}, + text=string.char(16), -- this is the "â–º" character + text_pen=COLOR_YELLOW, auto_width=true, visible=self.is_selected_fn, }, } self:addviews{ widgets.Label{ - view_id='item_info', + view_id='item_desc', frame={t=0, l=2}, text={ - {text=self:callback('get_item_line_text')}, + {text=self:callback('get_item_line_text'), + pen=function() return gui.invert_color(COLOR_WHITE, self.is_selected_fn()) end}, }, }, } self:addviews{ widgets.Label{ + view_id='item_filter', frame={t=0, l=28}, text={ - {text='[filter]', pen=self:callback('get_f_pen')}, + {text=self:callback('get_filter_text'), + pen=function() return gui.invert_color(COLOR_LIGHTCYAN, self.is_selected_fn()) end}, }, auto_width=true, on_click=function() self.on_filter(self.idx) end, }, widgets.Label{ - frame={t=0, l=36}, + frame={t=0, l=42}, text={ - {text='[x]', pen=self:callback('get_x_pen')}, + {text='[clear]', + pen=COLOR_LIGHTRED}, }, auto_width=true, + visible=self:callback('has_filter'), on_click=function() self.on_clear_filter(self.idx) end, }, widgets.Label{ @@ -266,15 +272,6 @@ function ItemLine:onInput(keys) return ItemLine.super.onInput(self, keys) end -function ItemLine:get_f_pen() -- TODO: make this thing work. I've tried many things to no avail. -taxi - return self.selected and COLOR_LIGHTCYAN or COLOR_CYAN -end - -function ItemLine:get_x_pen() - return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) and - COLOR_LIGHTRED or COLOR_BLACK -end - function ItemLine:get_item_line_text() local idx = self.idx local filter = get_cur_filters()[idx] @@ -287,15 +284,24 @@ function ItemLine:get_item_line_text() uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) if self.available >= quantity then self.note_pen = COLOR_GREEN - self.note = string.char(192)..' Available now' + self.note = string.char(192)..' Available now' -- character 192 is "â””" else self.note_pen = COLOR_BROWN - self.note = string.char(192)..' Will link later' + self.note = string.char(192)..' Will link later' -- character 192 is "â””" end return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') end +function ItemLine:has_filter() + return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) +end + +function ItemLine:get_filter_text() -- TODO: reuse "has_filter()" instead of copying this whole string? (i couldnt make it work -taxi) + return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) + and '[edit filters]' or '[any material]' -- TODO: make this show the filter's materials instead of "edit filters" +end + function ItemLine:reduce_quantity(used_quantity) if not self.available then return end local filter = get_cur_filters()[self.idx] @@ -399,7 +405,7 @@ function PlannerOverlay:init() on_clear_filter=self:callback('clear_filter')}, widgets.CycleHotkeyLabel{ view_id='hollow', - frame={b=4, l=1, w=19}, + frame={b=4, l=1, w=21}, key='CUSTOM_H', label='Hollow area:', visible=is_construction, @@ -490,7 +496,7 @@ function PlannerOverlay:init() widgets.HotkeyLabel{ frame={b=2, l=2}, key='CUSTOM_Q', - label='Prev/next', + label='Prev/next item', auto_width=true, enabled=function() return #get_cur_filters() > 1 end, on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, @@ -523,7 +529,7 @@ function PlannerOverlay:init() { label=function() -- TODO: hide this option if last used mat does not exist yet local automaterial = itemselection.get_automaterial_selection(uibs.building_type) - return ('Last used (%s)'):format(automaterial or 'n/a') + return ('Last used (%s)'):format(automaterial or 'pick manually') end, value=2, }, From f66c60ee65b39886fc9c64bc66ff6d093bb43fb1 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 6 Apr 2023 16:22:07 +0200 Subject: [PATCH 0988/2222] removed some trailing whitespace... --- plugins/lua/buildingplan/planneroverlay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index d4e4acc08..174097c1d 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -234,7 +234,7 @@ function ItemLine:init() view_id='item_filter', frame={t=0, l=28}, text={ - {text=self:callback('get_filter_text'), + {text=self:callback('get_filter_text'), pen=function() return gui.invert_color(COLOR_LIGHTCYAN, self.is_selected_fn()) end}, }, auto_width=true, @@ -243,7 +243,7 @@ function ItemLine:init() widgets.Label{ frame={t=0, l=42}, text={ - {text='[clear]', + {text='[clear]', pen=COLOR_LIGHTRED}, }, auto_width=true, From 6347e117512b659224bf551748bda20b08bf16fb Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Fri, 7 Apr 2023 00:46:32 +0200 Subject: [PATCH 0989/2222] moved prev/next on top, made set/edit filter button, more tweaks --- plugins/lua/buildingplan/planneroverlay.lua | 82 ++++++++++++--------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 174097c1d..a4173c5c6 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -108,7 +108,7 @@ end -- adjusted from CycleHotkeyLabel on the planner panel local weapon_quantity = 1 -local function get_quantity(filter, hollow, bounds) +local function get_quantity(filter, hollow, bounds) -- TODO: this should account for erroring constructions if is_pressure_plate() then local flags = uibs.plate_info.flags return (flags.units and 1 or 0) + (flags.water and 1 or 0) + @@ -411,12 +411,12 @@ function PlannerOverlay:init() visible=is_construction, options={ {label='No', value=false}, - {label='Yes', value=true}, + {label='Yes', value=true, pen=COLOR_GREEN}, }, }, widgets.CycleHotkeyLabel{ view_id='stairs_top_subtype', - frame={b=5, l=23, w=28}, + frame={b=5, l=23, w=30}, key='CUSTOM_R', label='Top Stair Type: ', visible=is_stairs, @@ -428,7 +428,7 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel { view_id='stairs_bottom_subtype', - frame={b=4, l=23, w=28}, + frame={b=4, l=23, w=30}, key='CUSTOM_B', label='Bottom Stair Type:', visible=is_stairs, @@ -471,7 +471,7 @@ function PlannerOverlay:init() }, widgets.Label{ frame={b=2, l=23}, - text_pen=dfhack.pen.parse{fg=COLOR_DARKGREY}, + text_pen=COLOR_DARKGREY, text={ 'Selected area: ', {text=function() @@ -487,32 +487,18 @@ function PlannerOverlay:init() visible=function() return #get_cur_filters() > 0 end, subviews={ widgets.HotkeyLabel{ - frame={b=2, l=1}, - key='CUSTOM_SHIFT_Q', - auto_width=true, - enabled=function() return #get_cur_filters() > 1 end, - on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, - }, - widgets.HotkeyLabel{ - frame={b=2, l=2}, - key='CUSTOM_Q', - label='Prev/next item', - auto_width=true, - enabled=function() return #get_cur_filters() > 1 end, - on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, - }, - widgets.HotkeyLabel{ - frame={b=1, l=1}, + frame={b=1, l=1, w=22}, key='CUSTOM_F', - label='Set filter', - auto_width=true, + label=function() + return buildingplan.hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) + and 'Edit filter' or 'Set filter' + end, on_activate=function() self:set_filter(self.selected) end, }, widgets.HotkeyLabel{ - frame={b=0, l=1}, + frame={b=0, l=1, w=22}, key='CUSTOM_X', label='Clear filter', - auto_width=true, on_activate=function() self:clear_filter(self.selected) end, enabled=function() return buildingplan.hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.selected - 1) @@ -590,17 +576,47 @@ function PlannerOverlay:init() }, } + local prev_next_selector = widgets.Panel{ + frame={h=1}, + auto_width=true, + subviews={ + widgets.HotkeyLabel{ + frame={t=0, l=1, w=9}, + key='CUSTOM_SHIFT_Q', + key_sep='\0', + label=': Prev/', + on_activate=function() self.selected = ((self.selected - 2) % #get_cur_filters()) + 1 end, + }, + widgets.HotkeyLabel{ + frame={t=0, l=2, w=1}, + key='CUSTOM_Q', + on_activate=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, + }, + widgets.Label{ + frame={t=0,l=10}, + text='next item', + on_click=function() self.selected = (self.selected % #get_cur_filters()) + 1 end, + }, + }, + visible=function() return #get_cur_filters() > 1 end, + } + + local black_bar = widgets.Panel{ + frame={t=0, l=1, w=37, h=1}, + frame_inset=0, + frame_background=gui.CLEAR_PEN, + visible=function() return not self.minimized end, + subviews={ + prev_next_selector, + }, + } + self:addviews{ - main_panel, + black_bar, minimized_panel, - error_panel, + main_panel, divider_widget, - widgets.Panel{ - frame={t=0, l=1, w=37, h=1}, - frame_inset=0, - frame_background=gui.CLEAR_PEN, - visible=function() return not self.minimized end, - }, + error_panel, } end From 8d40ca8be68725c11dcaadbc95cb20084bd93649 Mon Sep 17 00:00:00 2001 From: Quietust Date: Sat, 1 Apr 2023 12:37:36 -0600 Subject: [PATCH 0990/2222] Add "faststart" plugin to make DF start faster In particular, it makes the game's "Loading..." screen animate as quickly as possible, shortening it from around 10 seconds to slightly more than 1 second. A conditional build setting makes it skip the animation as well, making it slightly faster yet. Ideally, this should become part of the Tweak plugin, but we're not building that right now. --- docs/changelog.txt | 1 + docs/plugins/faststart.rst | 18 ++++++++++ plugins/CMakeLists.txt | 1 + plugins/faststart.cpp | 69 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 docs/plugins/faststart.rst create mode 100644 plugins/faststart.cpp diff --git a/docs/changelog.txt b/docs/changelog.txt index 4be3e6a64..59c0dc2b1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `faststart`: speeds up the "Loading..." screen so the Main Menu appears sooner ## Fixes - `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar diff --git a/docs/plugins/faststart.rst b/docs/plugins/faststart.rst new file mode 100644 index 000000000..3dc734183 --- /dev/null +++ b/docs/plugins/faststart.rst @@ -0,0 +1,18 @@ +faststart +========= + +.. dfhack-tool:: + :summary: Makes the main menu appear sooner. + :tags: interface + :no-command: + +This plugin accelerates the initial "Loading..." screen that appears when the +game first starts, so you don't have to wait as long before the Main Menu +appears and you can start playing. + +Usage +----- + +:: + + enable faststart diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index dc7fa9276..53d0296c7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -112,6 +112,7 @@ dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) #dfhack_plugin(embark-tools embark-tools.cpp) dfhack_plugin(eventful eventful.cpp LINK_LIBRARIES lua) dfhack_plugin(fastdwarf fastdwarf.cpp) +dfhack_plugin(faststart faststart.cpp) dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fixveins fixveins.cpp) diff --git a/plugins/faststart.cpp b/plugins/faststart.cpp new file mode 100644 index 000000000..d885726c0 --- /dev/null +++ b/plugins/faststart.cpp @@ -0,0 +1,69 @@ +// Fast Startup tweak + +#include "Core.h" +#include +#include +#include +#include +#include + +#include "df/viewscreen_initial_prepst.h" +#include + +using namespace DFHack; +using namespace df::enums; +using std::vector; + +// Uncomment this to make the Loading screen as fast as possible +// This has the side effect of removing the dwarf face animation +// (and briefly making the game become unresponsive) + +//#define REALLY_FAST + +DFHACK_PLUGIN("faststart"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +struct prep_hook : df::viewscreen_initial_prepst +{ + typedef df::viewscreen_initial_prepst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, logic, ()) + { +#ifdef REALLY_FAST + while (breakdown_level != interface_breakdown_types::STOPSCREEN) + { + render_count++; + INTERPOSE_NEXT(logic)(); + } +#else + render_count = 4; + INTERPOSE_NEXT(logic)(); +#endif + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(prep_hook, logic); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(prep_hook, logic).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + INTERPOSE_HOOK(prep_hook, logic).remove(); + return CR_OK; +} From 93962df3de15db1193438b520c52ef2af00aa93d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 01:57:11 +0000 Subject: [PATCH 0991/2222] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- plugins/faststart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/faststart.cpp b/plugins/faststart.cpp index d885726c0..de014801c 100644 --- a/plugins/faststart.cpp +++ b/plugins/faststart.cpp @@ -18,7 +18,7 @@ using std::vector; // This has the side effect of removing the dwarf face animation // (and briefly making the game become unresponsive) -//#define REALLY_FAST +//#define REALLY_FAST DFHACK_PLUGIN("faststart"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); From 75bdc8904c920a5542c0ee2d28a67339f9b44c19 Mon Sep 17 00:00:00 2001 From: Myk Date: Thu, 6 Apr 2023 18:59:57 -0700 Subject: [PATCH 0992/2222] add dfhack tag to faststart --- docs/plugins/faststart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/faststart.rst b/docs/plugins/faststart.rst index 3dc734183..b39269e01 100644 --- a/docs/plugins/faststart.rst +++ b/docs/plugins/faststart.rst @@ -3,7 +3,7 @@ faststart .. dfhack-tool:: :summary: Makes the main menu appear sooner. - :tags: interface + :tags: dfhack interface :no-command: This plugin accelerates the initial "Loading..." screen that appears when the From 2ac2817987ae2e8b9d426550c50406c2e1576b3f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 6 Apr 2023 23:19:04 -0700 Subject: [PATCH 0993/2222] enable bugfix services by default --- data/dfhack-config/init/dfhack.control-panel-system.init | 4 ++++ .../dfhack-config/init/onMapLoad.control-panel-new-fort.init | 4 ++++ data/dfhack-config/init/onMapLoad.control-panel-repeats.init | 5 +++++ docs/changelog.txt | 1 + 4 files changed, 14 insertions(+) create mode 100644 data/dfhack-config/init/dfhack.control-panel-system.init create mode 100644 data/dfhack-config/init/onMapLoad.control-panel-new-fort.init create mode 100644 data/dfhack-config/init/onMapLoad.control-panel-repeats.init diff --git a/data/dfhack-config/init/dfhack.control-panel-system.init b/data/dfhack-config/init/dfhack.control-panel-system.init new file mode 100644 index 000000000..c13565c28 --- /dev/null +++ b/data/dfhack-config/init/dfhack.control-panel-system.init @@ -0,0 +1,4 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +enable faststart diff --git a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init new file mode 100644 index 000000000..8a159b0de --- /dev/null +++ b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init @@ -0,0 +1,4 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +on-new-fortress enable fix/protect-nicks diff --git a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init new file mode 100644 index 000000000..1463230aa --- /dev/null +++ b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init @@ -0,0 +1,5 @@ +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] +repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] diff --git a/docs/changelog.txt b/docs/changelog.txt index 59c0dc2b1..2ae96aa86 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game - Mods: scripts from only the most recent version of an installed mod are added to the script path +- `gui/control-panel`: bugfix services are now enabled by default ## Documentation From 2923cf7d2114df2578227202a36746df7d9bff3a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 06:22:02 +0000 Subject: [PATCH 0994/2222] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../init/dfhack.control-panel-system.init | 8 ++++---- .../init/onMapLoad.control-panel-new-fort.init | 8 ++++---- .../init/onMapLoad.control-panel-repeats.init | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/data/dfhack-config/init/dfhack.control-panel-system.init b/data/dfhack-config/init/dfhack.control-panel-system.init index c13565c28..8b1431373 100644 --- a/data/dfhack-config/init/dfhack.control-panel-system.init +++ b/data/dfhack-config/init/dfhack.control-panel-system.init @@ -1,4 +1,4 @@ -# DO NOT EDIT THIS FILE -# Please use gui/control-panel to edit this file - -enable faststart +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +enable faststart diff --git a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init index 8a159b0de..66a07d14f 100644 --- a/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init +++ b/data/dfhack-config/init/onMapLoad.control-panel-new-fort.init @@ -1,4 +1,4 @@ -# DO NOT EDIT THIS FILE -# Please use gui/control-panel to edit this file - -on-new-fortress enable fix/protect-nicks +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +on-new-fortress enable fix/protect-nicks diff --git a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init index 1463230aa..cbed00b67 100644 --- a/data/dfhack-config/init/onMapLoad.control-panel-repeats.init +++ b/data/dfhack-config/init/onMapLoad.control-panel-repeats.init @@ -1,5 +1,5 @@ -# DO NOT EDIT THIS FILE -# Please use gui/control-panel to edit this file - -repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] -repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] +# DO NOT EDIT THIS FILE +# Please use gui/control-panel to edit this file + +repeat --name general-strike --time 1 --timeUnits days --command [ fix/general-strike -q ] +repeat --name warn-starving --time 10 --timeUnits days --command [ warn-starving ] From 00445767c74eb21600971ffd0699921e2b43300d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 7 Apr 2023 06:27:56 +0000 Subject: [PATCH 0995/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index e6216cc28..a4e5d4514 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit e6216cc28e4315df5fb128411d0ca57fe78ccb2b +Subproject commit a4e5d4514ec33462ee0c0bd25e02d4a9c3e2ce01 From 83017e8b8f7fffb935fd72bee5e356f972c1e6dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 00:48:04 -0700 Subject: [PATCH 0996/2222] give active mods a chance to reattach their hooks --- docs/changelog.txt | 1 + docs/guides/modding-guide.rst | 5 +++++ library/Core.cpp | 5 ++++- library/LuaApi.cpp | 2 +- library/lua/script-manager.lua | 41 ++++++++++++++++++++++------------ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2ae96aa86..a0919c602 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game - Mods: scripts from only the most recent version of an installed mod are added to the script path +- Mods: give active mods a chance to reattach their load hooks when a world is reloaded - `gui/control-panel`: bugfix services are now enabled by default ## Documentation diff --git a/docs/guides/modding-guide.rst b/docs/guides/modding-guide.rst index 0c9513fef..38117503c 100644 --- a/docs/guides/modding-guide.rst +++ b/docs/guides/modding-guide.rst @@ -461,6 +461,11 @@ Ok, you're all set up! Now, let's take a look at an example dfhack.onStateChange[GLOBAL_KEY] = function(sc) if sc == SC_MAP_UNLOADED then dfhack.run_command('disable', 'example-mod') + + -- ensure our mod doesn't try to enable itself when a different + -- world is loaded where we are *not* active + dfhack.onStateChange[GLOBAL_KEY] = nil + return end diff --git a/library/Core.cpp b/library/Core.cpp index d13c82fa5..97c69946e 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2148,7 +2148,10 @@ void Core::onStateChange(color_ostream &out, state_change_event event) loadModScriptPaths(out); auto L = Lua::Core::State; Lua::StackUnwinder top(L); - Lua::CallLuaModuleFunction(con, L, "script-manager", "reload"); + Lua::CallLuaModuleFunction(con, L, "script-manager", "reload", 1, 0, + [](lua_State* L) { + Lua::Push(L, true); + }); // fallthrough } case SC_WORLD_UNLOADED: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d30dc7f77..08903c7bd 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2729,7 +2729,7 @@ static int filesystem_listdir_recursive(lua_State *L) include_prefix = lua_toboolean(L, 3); std::map files; int err = DFHack::Filesystem::listdir_recursive(dir, files, depth, include_prefix); - if (err != -1) { + if (err != 0 && err != -1) { lua_pushnil(L); lua_pushstring(L, strerror(err)); lua_pushinteger(L, err); diff --git a/library/lua/script-manager.lua b/library/lua/script-manager.lua index 1a7161a10..450012357 100644 --- a/library/lua/script-manager.lua +++ b/library/lua/script-manager.lua @@ -6,22 +6,27 @@ local utils = require('utils') -- enabled API -- for each script that can be loaded as a module, calls cb(script_name, env) -function foreach_module_script(cb) +function foreach_module_script(cb, preprocess_script_file_fn) for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do local files = dfhack.filesystem.listdir_recursive( script_path, nil, false) if not files then goto skip_path end for _,f in ipairs(files) do - if not f.isdir and - f.path:endswith('.lua') and - not f.path:startswith('test/') and - not f.path:startswith('internal/') then - local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' - local ok, script_env = pcall(reqscript, script_name) - if ok then - cb(script_name, script_env) - end + if f.isdir or not f.path:endswith('.lua') or + f.path:startswith('.git') or + f.path:startswith('test/') or + f.path:startswith('internal/') then + goto continue end + if preprocess_script_file_fn then + preprocess_script_file_fn(script_path, f.path) + end + local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua' + local ok, script_env = pcall(reqscript, script_name) + if ok then + cb(script_name, script_env) + end + ::continue:: end ::skip_path:: end @@ -42,9 +47,17 @@ local function process_script(env_name, env) enabled_map[env_name] = fn end -function reload() +function reload(refresh_active_mod_scripts) enabled_map = utils.OrderedTable() - foreach_module_script(process_script) + local force_refresh_fn = refresh_active_mod_scripts and function(script_path, script_name) + if script_path:find('scripts_modactive') then + internal_script = dfhack.internal.scripts[script_path..'/'..script_name] + if internal_script then + internal_script.env = nil + end + end + end or nil + foreach_module_script(process_script, force_refresh_fn) end local function ensure_loaded() @@ -97,7 +110,7 @@ end local function add_script_path(mod_script_paths, path) if dfhack.filesystem.isdir(path) then - print('indexing scripts from mod script path: ' .. path) + print('indexing mod scripts: ' .. path) table.insert(mod_script_paths, path) end end @@ -120,7 +133,7 @@ function get_mod_script_paths() -- if a world is loaded, process active mods first, and lock to active version if dfhack.isWorldLoaded() then for _,path in ipairs(df.global.world.object_loader.object_load_order_src_dir) do - path = tostring(path) + path = tostring(path.value) if not path:startswith(INSTALLED_MODS_PATH) then goto continue end local id = get_mod_id_and_version(path) if not id then goto continue end From 9e5728244ea136df1541e0c216f27032b9e75c8a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 02:07:45 -0700 Subject: [PATCH 0997/2222] don't error out when buildingplan is loaded at DF start --- plugins/buildingplan/buildingplan.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index eb765c1c4..2193b380b 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -742,6 +742,8 @@ static int countAvailableItems(color_ostream &out, df::building_type type, int16 static bool hasFilter(color_ostream &out, df::building_type type, int16_t subtype, int32_t custom, int index) { TRACE(status,out).print("entering hasFilter\n"); + if (!Core::getInstance().isWorldLoaded()) + return false; BuildingTypeKey key(type, subtype, custom); auto &filters = get_item_filters(out, key); if (index < 0 || filters.getItemFilters().size() <= (size_t)index) From 54c62a7307edfa0b38f1ea79fee923466ed9dabc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 02:08:47 -0700 Subject: [PATCH 0998/2222] tidy up --- plugins/lua/buildingplan/planneroverlay.lua | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index a4173c5c6..185b1e28c 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -108,7 +108,8 @@ end -- adjusted from CycleHotkeyLabel on the planner panel local weapon_quantity = 1 -local function get_quantity(filter, hollow, bounds) -- TODO: this should account for erroring constructions +-- TODO: this should account for erroring constructions +local function get_quantity(filter, hollow, bounds) if is_pressure_plate() then local flags = uibs.plate_info.flags return (flags.units and 1 or 0) + (flags.water and 1 or 0) + @@ -218,8 +219,6 @@ function ItemLine:init() auto_width=true, visible=self.is_selected_fn, }, - } - self:addviews{ widgets.Label{ view_id='item_desc', frame={t=0, l=2}, @@ -228,8 +227,6 @@ function ItemLine:init() pen=function() return gui.invert_color(COLOR_WHITE, self.is_selected_fn()) end}, }, }, - } - self:addviews{ widgets.Label{ view_id='item_filter', frame={t=0, l=28}, @@ -242,10 +239,8 @@ function ItemLine:init() }, widgets.Label{ frame={t=0, l=42}, - text={ - {text='[clear]', - pen=COLOR_LIGHTRED}, - }, + text='[clear]', + text_pen=COLOR_LIGHTRED, auto_width=true, visible=self:callback('has_filter'), on_click=function() self.on_clear_filter(self.idx) end, @@ -284,22 +279,24 @@ function ItemLine:get_item_line_text() uibs.building_type, uibs.building_subtype, uibs.custom_type, idx - 1) if self.available >= quantity then self.note_pen = COLOR_GREEN - self.note = string.char(192)..' Available now' -- character 192 is "â””" + self.note = ' Available now' else self.note_pen = COLOR_BROWN - self.note = string.char(192)..' Will link later' -- character 192 is "â””" + self.note = ' Will link later' end + self.note = string.char(192) .. self.note -- character 192 is "â””" return ('%d %s%s'):format(quantity, self.desc, quantity == 1 and '' or 's') end function ItemLine:has_filter() - return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) + return require('plugins.buildingplan').hasFilter( + uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) end -function ItemLine:get_filter_text() -- TODO: reuse "has_filter()" instead of copying this whole string? (i couldnt make it work -taxi) - return require('plugins.buildingplan').hasFilter(uibs.building_type, uibs.building_subtype, uibs.custom_type, self.idx-1) - and '[edit filters]' or '[any material]' -- TODO: make this show the filter's materials instead of "edit filters" +function ItemLine:get_filter_text() + -- TODO: make this show the filter's materials instead of "edit filters" + return self:has_filter() and '[edit filters]' or '[any material]' end function ItemLine:reduce_quantity(used_quantity) From 36db68deeda0f9485251b55ddc3013840364466d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 02:17:22 -0700 Subject: [PATCH 0999/2222] user longer text for weapon label --- plugins/lua/buildingplan/planneroverlay.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 185b1e28c..29e6daca5 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -437,10 +437,10 @@ function PlannerOverlay:init() }, widgets.CycleHotkeyLabel { -- TODO: this thing also needs a slider view_id='weapons', - frame={b=4, l=1, w=22}, + frame={b=4, l=1, w=28}, key='CUSTOM_T', key_back='CUSTOM_SHIFT_T', - label='# of weapons:', + label='Number of weapons:', visible=is_weapon_or_spike_trap, options={ {label='(1)', value=1, pen=COLOR_YELLOW}, @@ -508,9 +508,9 @@ function PlannerOverlay:init() label='Choose items:', label_below=true, options={ - {label='with filters', value=0}, + {label='With filters', value=0}, { - label=function() -- TODO: hide this option if last used mat does not exist yet + label=function() local automaterial = itemselection.get_automaterial_selection(uibs.building_type) return ('Last used (%s)'):format(automaterial or 'pick manually') end, From 91b35475fa941bd4d08107161fc023510412c92e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 12:41:46 -0700 Subject: [PATCH 1000/2222] allow launching DF to work under wine --- package/windows/launchdf.c | 71 ++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c index 588624511..e903d8a84 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.c @@ -1,7 +1,26 @@ +#include #include -int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) -{ +static BOOL is_running_on_wine() { + static const char *(CDECL *pwine_get_version)(void); + HMODULE hntdll = GetModuleHandle("ntdll.dll"); + if(!hntdll) + return FALSE; + + pwine_get_version = (void *)GetProcAddress(hntdll, "wine_get_version"); + return !!pwine_get_version; +} + +static LPCWSTR launch_via_steam_posix() { + const char* argv[] = { "/bin/sh", "-c", "\"steam -applaunch 975370\"", NULL }; + + // does not return on success + _execv(argv[0], argv); + + return L"Could not launch Dwarf Fortress"; +} + +static LPCWSTR launch_via_steam_windows() { STARTUPINFOW si; PROCESS_INFORMATION pi; @@ -12,28 +31,42 @@ int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) WCHAR steamPath[1024]; DWORD datasize = 1024; - LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", L"SteamExe", RRF_RT_REG_SZ, NULL, &steamPath, &datasize); + LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", + L"SteamExe", RRF_RT_REG_SZ, NULL, &steamPath, &datasize); + + MessageBoxW(NULL, steamPath, NULL, 0); if (retCode != ERROR_SUCCESS) - { - MessageBoxW(NULL, L"Could not find Steam client executable", NULL, 0); - exit(1); - } + return L"Could not find Steam client executable"; WCHAR commandLine[1024] = L"steam.exe -applaunch 975370"; - if (CreateProcessW(steamPath, - commandLine, - NULL, - NULL, - FALSE, - 0, - NULL, - NULL, - &si, - &pi) == 0) - { - MessageBoxW(NULL, L"could not launch Dwarf Fortress", NULL, 0); + if (CreateProcessW(steamPath, commandLine, + NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0) + return L"Could not launch Dwarf Fortress"; + + return NULL; +} + +// this method doesn't properly attribute Steam playtime metrics to DF, +// but that's better than not having DF start at all. +static BOOL launch_direct() { + STARTUPINFOA si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + return CreateProcessA("Dwarf Fortress.exe", + NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); +} + +int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) { + LPCWSTR err = is_running_on_wine() ? launch_via_steam_posix() : launch_via_steam_windows(); + + if (err && !launch_direct()) { + MessageBoxW(NULL, err, NULL, 0); exit(1); } From 4d758589cba10fbca8453cfc27636112c0184f85 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 13:17:17 -0700 Subject: [PATCH 1001/2222] remove debug statement --- package/windows/launchdf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c index e903d8a84..6aea4e7d2 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.c @@ -34,8 +34,6 @@ static LPCWSTR launch_via_steam_windows() { LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", L"SteamExe", RRF_RT_REG_SZ, NULL, &steamPath, &datasize); - MessageBoxW(NULL, steamPath, NULL, 0); - if (retCode != ERROR_SUCCESS) return L"Could not find Steam client executable"; From 14bc22ff316b7efdac33f640ec2fa5283c958d27 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 7 Apr 2023 14:40:38 -0700 Subject: [PATCH 1002/2222] A -> W to align with existing codepaths --- package/windows/launchdf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/windows/launchdf.c b/package/windows/launchdf.c index 6aea4e7d2..992bf6636 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.c @@ -49,14 +49,14 @@ static LPCWSTR launch_via_steam_windows() { // this method doesn't properly attribute Steam playtime metrics to DF, // but that's better than not having DF start at all. static BOOL launch_direct() { - STARTUPINFOA si; + STARTUPINFOW si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - return CreateProcessA("Dwarf Fortress.exe", + return CreateProcessW(L"Dwarf Fortress.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); } From 882573bc4f085ae44c8501f2f986062f095f5cdc Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 8 Apr 2023 07:12:15 +0000 Subject: [PATCH 1003/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index a4e5d4514..d8557ba6f 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a4e5d4514ec33462ee0c0bd25e02d4a9c3e2ce01 +Subproject commit d8557ba6f1777c1a6d1b92a364e4ee22ec829873 From 5aa246f6b0fb62b472386b9830c3734c6f27c9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vuchener?= Date: Sat, 8 Apr 2023 12:33:24 +0200 Subject: [PATCH 1004/2222] plugins: include proto directory from current source directory Updated remotefortressreader for the new behavior. --- plugins/Plugins.cmake | 10 +++++----- plugins/remotefortressreader/CMakeLists.txt | 16 +--------------- plugins/remotefortressreader/proto/.gitignore | 3 +++ .../proto/AdventureControl.proto | 0 .../proto/DwarfControl.proto | 0 .../proto/ItemdefInstrument.proto | 0 .../proto/RemoteFortressReader.proto | 0 .../proto/ui_sidebar_mode.proto | 0 8 files changed, 9 insertions(+), 20 deletions(-) create mode 100644 plugins/remotefortressreader/proto/.gitignore rename plugins/{ => remotefortressreader}/proto/AdventureControl.proto (100%) rename plugins/{ => remotefortressreader}/proto/DwarfControl.proto (100%) rename plugins/{ => remotefortressreader}/proto/ItemdefInstrument.proto (100%) rename plugins/{ => remotefortressreader}/proto/RemoteFortressReader.proto (100%) rename plugins/{ => remotefortressreader}/proto/ui_sidebar_mode.proto (100%) diff --git a/plugins/Plugins.cmake b/plugins/Plugins.cmake index db7582c09..a0cea765f 100644 --- a/plugins/Plugins.cmake +++ b/plugins/Plugins.cmake @@ -6,11 +6,6 @@ if(UNIX) endif() endif() -include_directories("${dfhack_SOURCE_DIR}/library/include") -include_directories("${dfhack_SOURCE_DIR}/library/proto") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/proto") -include_directories("${dfhack_SOURCE_DIR}/library/depends/xgetopt") - macro(car var) set(${var} ${ARGV1}) endmacro() @@ -123,6 +118,11 @@ macro(dfhack_plugin) add_library(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES}) ide_folder(${PLUGIN_NAME} "Plugins") + target_include_directories(${PLUGIN_NAME} PRIVATE "${dfhack_SOURCE_DIR}/library/include") + target_include_directories(${PLUGIN_NAME} PRIVATE "${dfhack_SOURCE_DIR}/library/proto") + target_include_directories(${PLUGIN_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/proto") + target_include_directories(${PLUGIN_NAME} PRIVATE "${dfhack_SOURCE_DIR}/library/depends/xgetopt") + if(NUM_PROTO) add_dependencies(${PLUGIN_NAME} generate_proto_${PLUGIN_NAME}) target_link_libraries(${PLUGIN_NAME} dfhack protobuf-lite dfhack-version ${PLUGIN_LINK_LIBRARIES}) diff --git a/plugins/remotefortressreader/CMakeLists.txt b/plugins/remotefortressreader/CMakeLists.txt index 55525bc21..262d163f1 100644 --- a/plugins/remotefortressreader/CMakeLists.txt +++ b/plugins/remotefortressreader/CMakeLists.txt @@ -24,23 +24,9 @@ set(PROJECT_PROTO ui_sidebar_mode ) -set(PLUGIN_PROTOS) -foreach(pbuf ${PROJECT_PROTO}) - list(APPEND PLUGIN_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/../proto/${pbuf}.proto) -endforeach() - -string(REPLACE ".proto" ".pb.cc" PLUGIN_PROTO_SRCS "${PLUGIN_PROTOS}") -string(REPLACE ".proto" ".pb.h" PLUGIN_PROTO_HDRS "${PLUGIN_PROTOS}") -set_source_files_properties(${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS} PROPERTIES GENERATED TRUE) - -set_source_files_properties( ${PROJECT_HDRS} ${PLUGIN_PROTO_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) - -# mash them together (headers are marked as headers and nothing will try to compile them) -list(APPEND PROJECT_SRCS ${PROJECT_HDRS} ${PLUGIN_PROTOS} ${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS}) - if(UNIX AND NOT APPLE) set(PROJECT_LIBS ${PROJECT_LIBS} SDL) endif() # this makes sure all the stuff is put in proper places and linked to dfhack -dfhack_plugin(RemoteFortressReader ${PROJECT_SRCS} LINK_LIBRARIES protobuf-lite ${PROJECT_LIBS} COMPILE_FLAGS_MSVC "/FI\"Export.h\"" COMPILE_FLAGS_GCC "-include Export.h -Wno-misleading-indentation" ) +dfhack_plugin(RemoteFortressReader ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS} PROTOBUFS ${PROJECT_PROTO}) diff --git a/plugins/remotefortressreader/proto/.gitignore b/plugins/remotefortressreader/proto/.gitignore new file mode 100644 index 000000000..befabf79d --- /dev/null +++ b/plugins/remotefortressreader/proto/.gitignore @@ -0,0 +1,3 @@ +*.pb.cc +*.pb.cc.rule +*.pb.h diff --git a/plugins/proto/AdventureControl.proto b/plugins/remotefortressreader/proto/AdventureControl.proto similarity index 100% rename from plugins/proto/AdventureControl.proto rename to plugins/remotefortressreader/proto/AdventureControl.proto diff --git a/plugins/proto/DwarfControl.proto b/plugins/remotefortressreader/proto/DwarfControl.proto similarity index 100% rename from plugins/proto/DwarfControl.proto rename to plugins/remotefortressreader/proto/DwarfControl.proto diff --git a/plugins/proto/ItemdefInstrument.proto b/plugins/remotefortressreader/proto/ItemdefInstrument.proto similarity index 100% rename from plugins/proto/ItemdefInstrument.proto rename to plugins/remotefortressreader/proto/ItemdefInstrument.proto diff --git a/plugins/proto/RemoteFortressReader.proto b/plugins/remotefortressreader/proto/RemoteFortressReader.proto similarity index 100% rename from plugins/proto/RemoteFortressReader.proto rename to plugins/remotefortressreader/proto/RemoteFortressReader.proto diff --git a/plugins/proto/ui_sidebar_mode.proto b/plugins/remotefortressreader/proto/ui_sidebar_mode.proto similarity index 100% rename from plugins/proto/ui_sidebar_mode.proto rename to plugins/remotefortressreader/proto/ui_sidebar_mode.proto From 95fb4e7eaa70e3939ec6b0e0cd6acb2324f29233 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sun, 9 Apr 2023 12:57:11 -0500 Subject: [PATCH 1005/2222] add raw/cooked z level output to prospector toady shifted displayed z levels by 100 for v50 --- docs/changelog.txt | 1 + plugins/prospector.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 259a7ce7f..7b71f5253 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls - `combine`: fix error processing stockpiles with boundaries that extend outside of the map +- `prospector`: display both "raw" Z levels and "cooked" elevations ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index 86d77e643..148883830 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -168,9 +168,13 @@ static void printMatdata(color_ostream &con, const matdata &data, bool only_z = con << std::setw(9) << int(data.count); if(data.lower_z != data.upper_z) - con <<" Z:" << std::setw(4) << data.lower_z << ".." << data.upper_z << std::endl; + con <<" Z:" << std::setw(4) << data.lower_z << " .." << std::setw(4) << data.upper_z + <<" Elev:" << std::setw(4) << (data.lower_z - 100) << " .." << std::setw(4) << (data.upper_z - 100) + << std::endl; else - con <<" Z:" << std::setw(4) << data.lower_z << std::endl; + con <<" Z:" << std::setw(4) << data.lower_z << " " + <<" Elev:" << std::setw(4) << (data.lower_z - 100) + << std::endl; } static int getValue(const df::inorganic_raw &info) From ef53a243e64bc332a7d0e298b15cf1de84b843c6 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Sun, 9 Apr 2023 13:47:45 -0500 Subject: [PATCH 1006/2222] prospector redux this is arguably a better approach for dealing with the change in elevation display in v50 --- plugins/prospector.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index 148883830..d660c98b5 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -168,12 +168,10 @@ static void printMatdata(color_ostream &con, const matdata &data, bool only_z = con << std::setw(9) << int(data.count); if(data.lower_z != data.upper_z) - con <<" Z:" << std::setw(4) << data.lower_z << " .." << std::setw(4) << data.upper_z - <<" Elev:" << std::setw(4) << (data.lower_z - 100) << " .." << std::setw(4) << (data.upper_z - 100) + con <<" Elev:" << std::setw(4) << (data.lower_z) << ".." << (data.upper_z) << std::endl; else - con <<" Z:" << std::setw(4) << data.lower_z << " " - <<" Elev:" << std::setw(4) << (data.lower_z - 100) + con <<" Elev:" << std::setw(4) << (data.lower_z) << std::endl; } @@ -680,7 +678,8 @@ static command_result map_prospector(color_ostream &con, b->GetGlobalFeature(&blockFeatureGlobal); b->GetLocalFeature(&blockFeatureLocal); - int global_z = world->map.region_z + z; + // the '- 100' is because DF v50 and later have a 100 offset in reported elevation + int global_z = world->map.region_z + z - 100; // Iterate over all the tiles in the block for(uint32_t y = 0; y < 16; y++) From bd00bb8faf07914e82ac4fabd77fbc8f0f1dbc09 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 10 Apr 2023 07:13:21 +0000 Subject: [PATCH 1007/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index d7f07e54b..78a2008e4 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d7f07e54b4ea7832b226f8d8678906c9c366918a +Subproject commit 78a2008e4d20a47f0533f285c3457a278dba5556 From 95e796abb40a9c00320b18eb0b424820bdd06ef2 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 00:14:30 -0700 Subject: [PATCH 1008/2222] update python build action to non-deprecated version --- .github/workflows/build.yml | 6 +++--- .github/workflows/buildmaster-rebuild.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index abee9a86a..f03d8b3b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: plugins: all steps: - name: Set up Python 3 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3 - name: Install dependencies @@ -161,7 +161,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Set up Python 3 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3 - name: Install dependencies @@ -184,7 +184,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Set up Python 3 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3 - name: Set up Ruby 2.7 diff --git a/.github/workflows/buildmaster-rebuild.yml b/.github/workflows/buildmaster-rebuild.yml index 5a1fe9504..d4c7a70e6 100644 --- a/.github/workflows/buildmaster-rebuild.yml +++ b/.github/workflows/buildmaster-rebuild.yml @@ -14,7 +14,7 @@ jobs: name: Trigger Buildmaster steps: - name: Set up Python 3 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3 - name: Install dependencies From 07c83d3bf32a67adade558bd4b6fa6bfe2e1b909 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 03:37:10 -0700 Subject: [PATCH 1009/2222] allow getval to take and pass params --- library/lua/utils.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 2e03a8616..3883439f1 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -2,9 +2,9 @@ local _ENV = mkmodule('utils') local df = df -function getval(obj) +function getval(obj, ...) if type(obj) == 'function' then - return obj() + return obj(...) else return obj end From 661ddbd2562deac2e9ea8e99c398985c4831dc09 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 03:37:22 -0700 Subject: [PATCH 1010/2222] automelt doesn't need to disable itself --- plugins/automelt.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/plugins/automelt.cpp b/plugins/automelt.cpp index beb4cba35..dc0bc0f79 100644 --- a/plugins/automelt.cpp +++ b/plugins/automelt.cpp @@ -172,20 +172,6 @@ DFhackCExport command_result plugin_load_data(color_ostream &out) return CR_OK; } -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - if (event == DFHack::SC_WORLD_UNLOADED) - { - if (is_enabled) - { - DEBUG(status, out).print("world unloaded; disabling %s\n", plugin_name); - is_enabled = false; - } - } - - return CR_OK; -} - DFhackCExport command_result plugin_onupdate(color_ostream &out) { if (!Core::getInstance().isWorldLoaded()) From 04c16186de711e85a149dbb5dd99b9ec4c11a4c5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 04:24:00 -0700 Subject: [PATCH 1011/2222] hide planner overlay during the tutorial --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7b71f5253..adc4ca786 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,6 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game +- `buildingplan`: hide planner overlay while the DF tutorial is active so that it can detect when you have placed the carpenter's workshop and bed and allow you to finish the tutorial -@ `buildingplan`: rearranged elements of ``planneroverlay`` interface -@ `buildingplan`: rearranged elements of ``itemselection`` interface - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 29e6daca5..3e06ac79d 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -141,8 +141,15 @@ local function is_construction() return uibs.building_type == df.building_type.Construction end +local function is_tutorial_open() + local help = df.global.game.main_interface.help + return help.open and + help.context == df.help_context_type.START_TUTORIAL_WORKSHOPS_AND_TASKS +end + local function is_plannable() - return get_cur_filters() and + return not is_tutorial_open() and + get_cur_filters() and not (is_construction() and uibs.building_subtype == df.construction_type.TrackNSEW) end From 5cb3e6215141284720728587de07bc8f6e2c6539 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 10 Apr 2023 11:34:16 +0000 Subject: [PATCH 1012/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index d8557ba6f..973b8ef19 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d8557ba6f1777c1a6d1b92a364e4ee22ec829873 +Subproject commit 973b8ef191cc92cff62d96aa591c7631818d19b6 From 6a7446780b401c721a64fb28ce42094243448ff4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Apr 2023 22:59:55 -0700 Subject: [PATCH 1013/2222] hide terminal console when running on steam deck --- docs/changelog.txt | 1 + library/CMakeLists.txt | 2 + library/Core.cpp | 7 ++++ library/include/modules/DFSteam.h | 32 ++++++++++++++++ library/modules/DFSteam.cpp | 64 +++++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 library/include/modules/DFSteam.h create mode 100644 library/modules/DFSteam.cpp diff --git a/docs/changelog.txt b/docs/changelog.txt index adc4ca786..e63b80bd7 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,6 +52,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Mods: scripts from only the most recent version of an installed mod are added to the script path - Mods: give active mods a chance to reattach their load hooks when a world is reloaded - `gui/control-panel`: bugfix services are now enabled by default +- Core: hide DFHack terminal console by default when running on Steam Deck ## Documentation diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 92db43563..a3fcb8b6f 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -125,6 +125,7 @@ set(MODULE_HEADERS include/modules/Burrows.h include/modules/Constructions.h include/modules/DFSDL.h + include/modules/DFSteam.h include/modules/Designations.h include/modules/EventManager.h include/modules/Filesystem.h @@ -154,6 +155,7 @@ set(MODULE_SOURCES modules/Burrows.cpp modules/Constructions.cpp modules/DFSDL.cpp + modules/DFSteam.cpp modules/Designations.cpp modules/EventManager.cpp modules/Filesystem.cpp diff --git a/library/Core.cpp b/library/Core.cpp index 97c69946e..d03e8c632 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -49,6 +49,7 @@ distribution. #include "PluginManager.h" #include "ModuleFactory.h" #include "modules/DFSDL.h" +#include "modules/DFSteam.h" #include "modules/EventManager.h" #include "modules/Filesystem.h" #include "modules/Gui.h" @@ -1303,6 +1304,10 @@ static void run_dfhack_init(color_ostream &out, Core *core) return; } + // if we're running on Steam Deck, hide the terminal by default + if (DFSteam::DFIsSteamRunningOnSteamDeck()) + core->getConsole().hide(); + // load baseline defaults core->loadScriptFile(out, CONFIG_PATH + "init/default.dfhack.init", false); @@ -1668,6 +1673,8 @@ bool Core::Init() fatal("cannot bind SDL libraries"); return false; } + if (DFSteam::init(con)) + std::cerr << "Found Steam.\n"; std::cerr << "Initializing textures.\n"; Textures::init(con); // create mutex for syncing with interactive tasks diff --git a/library/include/modules/DFSteam.h b/library/include/modules/DFSteam.h new file mode 100644 index 000000000..3144830da --- /dev/null +++ b/library/include/modules/DFSteam.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ColorText.h" +#include "Export.h" + +namespace DFHack +{ + +/** + * The DFSteam module - provides access to Steam functions without actually + * requiring build-time linkage to Steam + * \ingroup grp_modules + * \ingroup grp_dfsdl + */ +namespace DFSteam +{ + +/** + * Call this on DFHack init so we can load the function pointers. Returns false on + * failure. + */ +bool init(DFHack::color_ostream& out); + +/** + * Call this when DFHack is being unloaded. + */ +void cleanup(); + +DFHACK_EXPORT bool DFIsSteamRunningOnSteamDeck(); + +} +} diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp new file mode 100644 index 000000000..d32eec961 --- /dev/null +++ b/library/modules/DFSteam.cpp @@ -0,0 +1,64 @@ +#include "Internal.h" + +#include "modules/DFSteam.h" + +#include "Debug.h" +#include "PluginManager.h" + +namespace DFHack +{ +DBG_DECLARE(core, dfsteam, DebugCategory::LINFO); +} + +using namespace DFHack; + +static DFLibrary* g_steam_handle = nullptr; +static const std::vector STEAM_LIBS { + "steam_api64.dll", + "steam_api", // TODO: validate this on OSX + "libsteam_api.so" // TODO: validate this on Linux +}; + +bool (*g_SteamAPI_Init)() = nullptr; +bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)() = nullptr; + +bool DFSteam::init(color_ostream& out) { + for (auto& lib_str : STEAM_LIBS) { + if ((g_steam_handle = OpenPlugin(lib_str.c_str()))) + break; + } + if (!g_steam_handle) { + DEBUG(dfsteam, out).print("steam library not found; stubbing calls\n"); + return false; + } + +#define bind(handle, name) \ + g_##name = (decltype(g_##name))LookupPlugin(handle, #name); \ + if (!g_##name) { \ + WARN(dfsteam, out).print("steam library function not found: " #name "\n"); \ + } + + bind(g_steam_handle, SteamAPI_Init); + if (!g_SteamAPI_Init || !g_SteamAPI_Init()) + return false; + + bind(g_steam_handle, SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck); +#undef bind + + DEBUG(dfsteam, out).print("steam library linked\n"); + return true; +} + +void DFSteam::cleanup() { + if (!g_steam_handle) + return; + + ClosePlugin(g_steam_handle); + g_steam_handle = nullptr; +} + +bool DFSteam::DFIsSteamRunningOnSteamDeck() { + if (!g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck) + return false; + return g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck(); +} From f0d19c93631b32ea96d078985502a3244897b104 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 9 Apr 2023 23:30:23 -0700 Subject: [PATCH 1014/2222] add note about dfhooks --- library/modules/DFSteam.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index d32eec961..8cc921608 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -39,6 +39,8 @@ bool DFSteam::init(color_ostream& out) { } bind(g_steam_handle, SteamAPI_Init); + + // TODO: can we remove this initialization of the Steam API once we move to dfhooks? if (!g_SteamAPI_Init || !g_SteamAPI_Init()) return false; From e4777d268836d34f1a11ff376dd26cb4a9130c0a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 00:00:47 -0700 Subject: [PATCH 1015/2222] add shutdown and cleanup logic --- library/Core.cpp | 1 + library/modules/DFSteam.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index d03e8c632..b1fe2d389 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2281,6 +2281,7 @@ int Core::Shutdown ( void ) allModules.clear(); Textures::cleanup(); DFSDL::cleanup(); + DFSteam::cleanup(); memset(&(s_mods), 0, sizeof(s_mods)); d.reset(); return -1; diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index 8cc921608..f58bc7cb4 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -20,6 +20,7 @@ static const std::vector STEAM_LIBS { }; bool (*g_SteamAPI_Init)() = nullptr; +void (*g_SteamAPI_Shutdown)() = nullptr; bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)() = nullptr; bool DFSteam::init(color_ostream& out) { @@ -39,9 +40,10 @@ bool DFSteam::init(color_ostream& out) { } bind(g_steam_handle, SteamAPI_Init); + bind(g_steam_handle, SteamAPI_Shutdown); // TODO: can we remove this initialization of the Steam API once we move to dfhooks? - if (!g_SteamAPI_Init || !g_SteamAPI_Init()) + if (!g_SteamAPI_Init || !g_SteamAPI_Shutdown || !g_SteamAPI_Init()) return false; bind(g_steam_handle, SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck); @@ -55,6 +57,9 @@ void DFSteam::cleanup() { if (!g_steam_handle) return; + if (g_SteamAPI_Shutdown) + g_SteamAPI_Shutdown(); + ClosePlugin(g_steam_handle); g_steam_handle = nullptr; } From ce017ee4a88648809d789c57ff3504f00e91870e Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Mon, 10 Apr 2023 03:01:36 -0500 Subject: [PATCH 1016/2222] properly callIs SteamRunningOnSteamDeck --- library/modules/DFSteam.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index f58bc7cb4..1226f2a27 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -21,7 +21,8 @@ static const std::vector STEAM_LIBS { bool (*g_SteamAPI_Init)() = nullptr; void (*g_SteamAPI_Shutdown)() = nullptr; -bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)() = nullptr; +void* (*g_SteamInternal_FindOrCreateUserInterface)(int, char *) = nullptr; +bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)(void*) = nullptr; bool DFSteam::init(color_ostream& out) { for (auto& lib_str : STEAM_LIBS) { @@ -46,6 +47,7 @@ bool DFSteam::init(color_ostream& out) { if (!g_SteamAPI_Init || !g_SteamAPI_Shutdown || !g_SteamAPI_Init()) return false; + bind(g_steam_handle, SteamInternal_FindOrCreateUserInterface); bind(g_steam_handle, SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck); #undef bind @@ -67,5 +69,11 @@ void DFSteam::cleanup() { bool DFSteam::DFIsSteamRunningOnSteamDeck() { if (!g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck) return false; - return g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck(); + + if (!g_SteamInternal_FindOrCreateUserInterface) + return false; + + void* SteamUtils = g_SteamInternal_FindOrCreateUserInterface(0, "SteamUtils010"); + + return g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck(SteamUtils); } From 836a3edcb958959ba9a4157b7419dacfb431e617 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 01:12:43 -0700 Subject: [PATCH 1017/2222] add some more logging --- library/modules/DFSteam.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index 1226f2a27..4c45f1114 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -21,7 +21,7 @@ static const std::vector STEAM_LIBS { bool (*g_SteamAPI_Init)() = nullptr; void (*g_SteamAPI_Shutdown)() = nullptr; -void* (*g_SteamInternal_FindOrCreateUserInterface)(int, char *) = nullptr; +void* (*g_SteamInternal_FindOrCreateUserInterface)(int, char*) = nullptr; bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)(void*) = nullptr; bool DFSteam::init(color_ostream& out) { @@ -44,8 +44,10 @@ bool DFSteam::init(color_ostream& out) { bind(g_steam_handle, SteamAPI_Shutdown); // TODO: can we remove this initialization of the Steam API once we move to dfhooks? - if (!g_SteamAPI_Init || !g_SteamAPI_Shutdown || !g_SteamAPI_Init()) + if (!g_SteamAPI_Init || !g_SteamAPI_Shutdown || !g_SteamAPI_Init()) { + DEBUG(dfsteam, out).print("steam detected but cannot be initialized\n"); return false; + } bind(g_steam_handle, SteamInternal_FindOrCreateUserInterface); bind(g_steam_handle, SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck); From 17373dcffd6ec40cc1c8bdb105e1e92adff7f1ec Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 01:16:22 -0700 Subject: [PATCH 1018/2222] constify! --- library/modules/DFSteam.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index 4c45f1114..b27cc1744 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -21,7 +21,7 @@ static const std::vector STEAM_LIBS { bool (*g_SteamAPI_Init)() = nullptr; void (*g_SteamAPI_Shutdown)() = nullptr; -void* (*g_SteamInternal_FindOrCreateUserInterface)(int, char*) = nullptr; +void* (*g_SteamInternal_FindOrCreateUserInterface)(int, const char*) = nullptr; bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)(void*) = nullptr; bool DFSteam::init(color_ostream& out) { From c0fe5776fb760d3cdfc3b8966ffde2e5416455dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 18:29:41 -0700 Subject: [PATCH 1019/2222] update Installing docs for Steam --- docs/Installing.rst | 163 ++++++++++++++++---------------------------- docs/changelog.txt | 1 + 2 files changed, 58 insertions(+), 106 deletions(-) diff --git a/docs/Installing.rst b/docs/Installing.rst index bc58a39b7..a80133e88 100644 --- a/docs/Installing.rst +++ b/docs/Installing.rst @@ -7,78 +7,50 @@ Installing DFHack .. contents:: :local: - Requirements ============ -DFHack supports Windows, Linux, and macOS, and both 64-bit and 32-bit builds -of Dwarf Fortress. +DFHack supports all operating systems and platforms that Dwarf Fortress itself +supports, which at the moment is just 64-bit Windows. However, the Windows +build of DFHack works well under ``wine`` (or ``Proton``, Steam's fork of +``wine``) on other operating systems. .. _installing-df-version: DFHack releases generally only support the version of Dwarf Fortress that they -are named after. For example, DFHack 0.40.24-r5 only supported DF 0.40.24. -DFHack releases *never* support newer versions of DF, because DFHack requires -data about DF that is only possible to obtain after DF has been released. -Occasionally, DFHack releases will be able to maintain support for older -versions of DF - for example, DFHack 0.34.11-r5 supported both DF 0.34.11 and -0.34.10. For maximum stability, you should usually use the latest versions of -both DF and DFHack. - -Windows -------- - -* DFHack only supports the SDL version of Dwarf Fortress. The "legacy" version - will *not* work with DFHack (the "small" SDL version is acceptable, however). -* Windows XP and older are *not* supported, due in part to a - `Visual C++ 2015 bug `_ - -The Windows build of DFHack should work under Wine on other operating systems, -although this is not tested very often. It is recommended to use the native -build for your operating system instead. - -.. _installing-reqs-linux: - -Linux ------ - -Generally, DFHack should work on any modern Linux distribution. There are -multiple release binaries provided - as of DFHack 0.47.04-r1, there are built -with GCC 7 and GCC 4.8 (as indicated by the ``gcc`` component of their -filenames). Using the newest build that works on your system is recommended. -The GCC 4.8 build is built on Ubuntu 14.04 and targets an older glibc, so it -should work on older distributions. - -In the event that none of the provided binaries work on your distribution, -you may need to `compile DFHack from source `. - -macOS ------ - -OS X 10.6.8 or later is required. - +are named after. For example, DFHack 50.05 only supported DF 50.05. DFHack +releases *never* support newer versions of DF -- DFHack requires data about DF +that is only possible to obtain after DF has been released. Occasionally, +DFHack releases will be able to maintain support for older versions of DF - for +example, DFHack 0.34.11-r5 supported both DF 0.34.11 and 0.34.10. For maximum +stability, you should usually use the latest versions of both DF and DFHack. .. _downloading: Downloading DFHack ================== -Stable builds of DFHack are available on `GitHub `_. -GitHub has been known to change their layout periodically, but as of July 2020, -downloads are available at the bottom of the release notes for each release, under a section -named "Assets" (which you may have to expand). The name of the file indicates -which DF version, platform, and architecture the build supports - the platform -and architecture (64-bit or 32-bit) **must** match your build of DF. The DF -version should also match your DF version - see `above ` -for details. For example: +Stable builds of DFHack are available on +`Steam `__ +or from our `GitHub `__. Either +location will give you exactly the same package. + +On Steam, note that DFHack is a separate app, not a DF Steam Workshop mod. You +can run DF with DFHack by launching either the DFHack app or the original Dwarf +Fortress app. + +If you download from GitHub, downloads are available at the bottom of the +release notes for each release, under a section named "Assets" (which you may +have to expand). The name of the file indicates which DF version, platform, and +architecture the build supports - the platform and architecture (64-bit or +32-bit) **must** match your build of DF. The DF version should also match your +DF version - see `above ` for details. For example: -* ``dfhack-0.47.04-r1-Windows-64bit.zip`` supports 64-bit DF on Windows -* ``dfhack-0.47.04-r1-Linux-32bit-gcc-7.tar.bz2`` supports 32-bit DF on Linux - (see `installing-reqs-linux` for details on the GCC version indicator) +* ``dfhack-50.07-r1-Windows-64bit.zip`` supports 64-bit DF on Windows -The `DFHack website `_ also provides links to -unstable builds. These files have a different naming scheme, but the same -restrictions apply (e.g. a file named ``Windows64`` is for 64-bit Windows DF). +In between stable releases, we may create beta releases to test new features. +These are available via the beta release channel on Steam or from our regular +Github page as a pre-release tagged with a "beta" suffix. .. warning:: @@ -91,19 +63,22 @@ restrictions apply (e.g. a file named ``Windows64`` is for 64-bit Windows DF). Installing DFHack ================= +If you are installing from Steam, this is handled for you automatically. The +instructions here are for manual installs. + When you `download DFHack `, you will end up with a release archive (a ``.zip`` file on Windows, or a ``.tar.bz2`` file on other platforms). Your operating system should have built-in utilities capable of extracting files from these archives. -The release archives contain several folders, including a ``hack`` folder where -DFHack binary and system data is stored, a ``dfhack-config`` folder where user -data and configuration is stored, and a ``blueprints`` folder where `quickfort` -blueprints are stored. To install DFHack, copy all of the files from the DFHack -archive into the root DF folder, which should already include a ``data`` folder -and a ``raw`` folder, among other things. Some packs and other redistributions -of Dwarf Fortress may place DF in another folder, so ensure that the ``hack`` -folder ends up next to the ``data`` folder. +The release archives contain a ``hack`` folder where DFHack binary and system +data is stored, a ``stonesense`` folder that contains data specific to the +`stonesense` 3d renderer, and various libraries and executable files. To +install DFHack, copy all of the files from the DFHack archive into the root DF +folder, which should already include a ``data`` folder and a ``mods`` folder, +among other things. Some redistributions of Dwarf Fortress may place DF in +another folder, so ensure that the ``hack`` folder ends up next to the ``data`` +folder, and you'll be fine. .. note:: @@ -112,58 +87,34 @@ folder ends up next to the ``data`` folder. overwrite ``SDL.dll`` if prompted. (If you are not prompted, you may be installing DFHack in the wrong place.) - Uninstalling DFHack =================== -Uninstalling DFHack essentially involves reversing what you did to install -DFHack. On Windows, replace ``SDL.dll`` with ``SDLreal.dll`` first. Then, you +Manually uninstalling DFHack essentially involves reversing what you did to +install. On Windows, replace ``SDL.dll`` with ``SDLreal.dll`` first. Then, you can remove any files that were part of the DFHack archive. DFHack does not currently maintain a list of these files, so if you want to completely remove them, you should consult the DFHack archive that you installed for a full list. Generally, any files left behind should not negatively affect DF. +On Steam, uninstalling DFHack will cleanly remove everything that was installed +with DFHack, **including** the ``SDL.dll`` file, which will render Dwarf +Fortress inoperative. In your Steam client, open the properties window for +Dwarf Fortress, select "Local Files", and click on "Verify integrity of game +files...". This will get Dwarf Fortress working properly again. + +Note that Steam will leave behind the ``dfhack-config`` folder, which contains +all your personal DFHack-related settings and data. If you keep this folder, +all your settings will be restored when you reinstall DFHack later. Upgrading DFHack ================ -The recommended approach to upgrade DFHack is to uninstall DFHack first, then -install the new version. This will ensure that any files that are only part -of the older DFHack installation do not affect the new DFHack installation -(although this is unlikely to occur). - -It is also possible to overwrite an existing DFHack installation in-place. -To do this, follow the installation instructions above, but overwrite all files -that exist in the new DFHack archive (on Windows, this includes ``SDL.dll`` again). - -.. note:: - - You may wish to make a backup of your ``dfhack-config`` folder first if you - have made changes to it. Some archive managers (e.g. Archive Utility on macOS) - will overwrite the entire folder, removing any files that you have added. - - -Pre-packaged DFHack installations -================================= - -There are :wiki:`several packs available ` that include -DF, DFHack, and other utilities. If you are new to Dwarf Fortress and DFHack, -these may be easier to set up. Note that these packs are not maintained by the -DFHack team and vary in their release schedules and contents. Some may make -significant configuration changes, and some may not include DFHack at all. - -Linux packages -============== - -Third-party DFHack packages are available for some Linux distributions, -including in: +Again, if you have installed from Steam, your copy of DFHack will automatically be kept up to date. This section is for manual installers. -* `AUR `__, for Arch and related - distributions -* `RPM Fusion `__, - for Fedora and related distributions +First, remove the ``hack`` and ``stonesense`` folders in their entirety. This +ensures that files that don't exist in the latest version are properly removed +and don't affect your new installation. -Note that these may lag behind DFHack releases. If you want to use a newer -version of DFHack, we generally recommended installing it in a clean copy of DF -in your home folder. Attempting to upgrade an installation of DFHack from a -package manager may break it. +Then, extract the DFHack release archive into your Dwarf Fortress folder, +overwriting any remaining top-level files (including SDL.dll). diff --git a/docs/changelog.txt b/docs/changelog.txt index e63b80bd7..8beb3dc6e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Core: hide DFHack terminal console by default when running on Steam Deck ## Documentation +- `installing`: updated to include Steam installation instructions ## API From e16b01898b7f8788dcaf7ec97caef36f86847e7d Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 10 Apr 2023 20:35:05 -0700 Subject: [PATCH 1020/2222] Mention save instead of mods --- docs/Installing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Installing.rst b/docs/Installing.rst index a80133e88..15a6276bf 100644 --- a/docs/Installing.rst +++ b/docs/Installing.rst @@ -75,7 +75,7 @@ The release archives contain a ``hack`` folder where DFHack binary and system data is stored, a ``stonesense`` folder that contains data specific to the `stonesense` 3d renderer, and various libraries and executable files. To install DFHack, copy all of the files from the DFHack archive into the root DF -folder, which should already include a ``data`` folder and a ``mods`` folder, +folder, which should already include a ``data`` folder and a ``save`` folder, among other things. Some redistributions of Dwarf Fortress may place DF in another folder, so ensure that the ``hack`` folder ends up next to the ``data`` folder, and you'll be fine. From 51047367f4e29694f0a5b78f5c418826d6b5a09e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 10 Apr 2023 23:07:18 -0700 Subject: [PATCH 1021/2222] fix index out of bounds error when reading gems --- docs/changelog.txt | 2 ++ plugins/lua/stockpiles.lua | 2 +- plugins/stockpiles/StockpileSerializer.cpp | 20 ++++++++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8beb3dc6e..7c52315ea 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls - `combine`: fix error processing stockpiles with boundaries that extend outside of the map - `prospector`: display both "raw" Z levels and "cooked" elevations +- `stockpiles`: fix crash when importing settings for gems from other worlds +- `stockpiles`: allow numbers in saved stockpile filenames ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 6134f0a1b..9e17cbe2b 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -62,7 +62,7 @@ local function assert_safe_name(name) if not name or #name == 0 then qerror('name missing or empty') end - if name:find('[^%a._]') then + if name:find('[^%w._]') then qerror('name can only contain numbers, letters, periods, and underscores') end end diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index f6a63ff4f..529e402bd 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -1848,7 +1848,7 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const vector& unserialize_list_material("mats/rough", all, val, filters, gem_mat_is_allowed, [&](const size_t& idx) -> const string& { return bgems.rough_mats(idx); }, - bgems.rough_mats_size(),pgems.rough_mats); + bgems.rough_mats_size(), pgems.rough_mats); unserialize_list_material("mats/cut", all, val, filters, gem_cut_mat_is_allowed, [&](const size_t& idx) -> const string& { return bgems.cut_mats(idx); }, @@ -1871,13 +1871,17 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const vector& return; } else { MaterialInfo mi; - for (size_t i = 0; i < builtin_size; ++i) { - string id = bgems.rough_other_mats(i); - if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) - set_filter_elem("other/rough", filters, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); - id = bgems.cut_other_mats(i); - if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) - set_filter_elem("other/cut", filters, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); + for (int i = 0; i < (int)builtin_size; ++i) { + if (i < bgems.rough_other_mats_size()) { + string id = bgems.rough_other_mats(i); + if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) + set_filter_elem("other/rough", filters, val, id, mi.type, pgems.rough_other_mats.at(mi.type)); + } + if (i < bgems.cut_other_mats_size()) { + string id = bgems.cut_other_mats(i); + if (mi.find(id) && mi.isValid() && size_t(mi.type) < builtin_size) + set_filter_elem("other/cut", filters, val, id, mi.type, pgems.cut_other_mats.at(mi.type)); + } } } }); From c5ff1622cd82113aabee9c2d55184c67ed95dca9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 00:39:22 -0700 Subject: [PATCH 1022/2222] reformat code and clean up headers --- plugins/getplants.cpp | 319 ++++++++++++++++-------------------------- 1 file changed, 119 insertions(+), 200 deletions(-) diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 565316fa6..c9db92f5a 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -1,13 +1,5 @@ -// (un)designate matching plants for gathering/cutting -#include - -#include "Core.h" -#include "Console.h" -#include "Export.h" #include "PluginManager.h" -#include "DataDefs.h" #include "TileTypes.h" -#include "MiscUtils.h" #include "df/map_block.h" #include "df/map_block_column.h" @@ -76,48 +68,38 @@ enum class selectability { // result in the plants not being usable for farming or even collectable at all). //selectability selectablePlant(color_ostream &out, const df::plant_raw *plant, bool farming) -selectability selectablePlant(const df::plant_raw *plant, bool farming) -{ +selectability selectablePlant(const df::plant_raw* plant, bool farming) { const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant->material_defs.type[plant_material_def::basic_mat], plant->material_defs.idx[plant_material_def::basic_mat]); bool outOfSeason = false; selectability result = selectability::Nonselectable; - if (plant->flags.is_set(plant_raw_flags::TREE)) - { -// out.print("%s is a selectable tree\n", plant->id.c_str()); - if (farming) - { + if (plant->flags.is_set(plant_raw_flags::TREE)) { + // out.print("%s is a selectable tree\n", plant->id.c_str()); + if (farming) { return selectability::Nonselectable; } - else - { + else { return selectability::Selectable; } } - else if (plant->flags.is_set(plant_raw_flags::GRASS)) - { -// out.print("%s is a non selectable Grass\n", plant->id.c_str()); + else if (plant->flags.is_set(plant_raw_flags::GRASS)) { + // out.print("%s is a non selectable Grass\n", plant->id.c_str()); return selectability::Grass; } - if (farming && plant->material_defs.type[plant_material_def::seed] == -1) - { + if (farming && plant->material_defs.type[plant_material_def::seed] == -1) { return selectability::Nonselectable; } if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW) || - basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) - { -// out.print("%s is edible\n", plant->id.c_str()); - if (farming) - { - if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW)) - { + basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) { + // out.print("%s is edible\n", plant->id.c_str()); + if (farming) { + if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW)) { result = selectability::Selectable; } } - else - { + else { return selectability::Selectable; } } @@ -126,54 +108,43 @@ selectability selectablePlant(const df::plant_raw *plant, bool farming) plant->flags.is_set(plant_raw_flags::MILL) || plant->flags.is_set(plant_raw_flags::EXTRACT_VIAL) || plant->flags.is_set(plant_raw_flags::EXTRACT_BARREL) || - plant->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL)) - { -// out.print("%s is thread/mill/extract\n", plant->id.c_str()); - if (farming) - { + plant->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL)) { + // out.print("%s is thread/mill/extract\n", plant->id.c_str()); + if (farming) { result = selectability::Selectable; } - else - { + else { return selectability::Selectable; } } if (basic_mat.material->reaction_product.id.size() > 0 || - basic_mat.material->reaction_class.size() > 0) - { -// out.print("%s has a reaction\n", plant->id.c_str()); - if (farming) - { + basic_mat.material->reaction_class.size() > 0) { + // out.print("%s has a reaction\n", plant->id.c_str()); + if (farming) { result = selectability::Selectable; } - else - { + else { return selectability::Selectable; } } - for (size_t i = 0; i < plant->growths.size(); i++) - { + for (size_t i = 0; i < plant->growths.size(); i++) { if (plant->growths[i]->item_type == df::item_type::SEEDS || // Only trees have seed growths in vanilla, but raws can be modded... - plant->growths[i]->item_type == df::item_type::PLANT_GROWTH) - { + plant->growths[i]->item_type == df::item_type::PLANT_GROWTH) { const DFHack::MaterialInfo growth_mat = DFHack::MaterialInfo(plant->growths[i]->mat_type, plant->growths[i]->mat_index); if ((plant->growths[i]->item_type == df::item_type::SEEDS && - (growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED) || - growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW))) || + (growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED) || + growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW))) || (plant->growths[i]->item_type == df::item_type::PLANT_GROWTH && - growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... + growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... { bool seedSource = plant->growths[i]->item_type == df::item_type::SEEDS; - if (plant->growths[i]->item_type == df::item_type::PLANT_GROWTH) - { - for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++) - { + if (plant->growths[i]->item_type == df::item_type::PLANT_GROWTH) { + for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++) { if (growth_mat.material->reaction_product.material.mat_type[k] == plant->material_defs.type[plant_material_def::seed] && - growth_mat.material->reaction_product.material.mat_index[k] == plant->material_defs.idx[plant_material_def::seed]) - { + growth_mat.material->reaction_product.material.mat_index[k] == plant->material_defs.idx[plant_material_def::seed]) { seedSource = true; break; } @@ -182,52 +153,46 @@ selectability selectablePlant(const df::plant_raw *plant, bool farming) if (*cur_year_tick >= plant->growths[i]->timing_1 && (plant->growths[i]->timing_2 == -1 || - *cur_year_tick <= plant->growths[i]->timing_2)) - { -// out.print("%s has an edible seed or a stockpile growth\n", plant->id.c_str()); - if (!farming || seedSource) - { + *cur_year_tick <= plant->growths[i]->timing_2)) { + // out.print("%s has an edible seed or a stockpile growth\n", plant->id.c_str()); + if (!farming || seedSource) { return selectability::Selectable; } } - else - { - if (!farming || seedSource) - { + else { + if (!farming || seedSource) { outOfSeason = true; } } } } -/* else if (plant->growths[i]->behavior.bits.has_seed) // This code designates beans, etc. when DF doesn't, but plant gatherers still fail to collect anything, so it's useless: bug #0006940. - { - const DFHack::MaterialInfo seed_mat = DFHack::MaterialInfo(plant->material_defs.type[plant_material_def::seed], plant->material_defs.idx[plant_material_def::seed]); - - if (seed_mat.material->flags.is_set(material_flags::EDIBLE_RAW) || - seed_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) - { - if (*cur_year_tick >= plant->growths[i]->timing_1 && - (plant->growths[i]->timing_2 == -1 || - *cur_year_tick <= plant->growths[i]->timing_2)) + /* else if (plant->growths[i]->behavior.bits.has_seed) // This code designates beans, etc. when DF doesn't, but plant gatherers still fail to collect anything, so it's useless: bug #0006940. { - return selectability::Selectable; - } - else - { - outOfSeason = true; - } - } - } */ + const DFHack::MaterialInfo seed_mat = DFHack::MaterialInfo(plant->material_defs.type[plant_material_def::seed], plant->material_defs.idx[plant_material_def::seed]); + + if (seed_mat.material->flags.is_set(material_flags::EDIBLE_RAW) || + seed_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) + { + if (*cur_year_tick >= plant->growths[i]->timing_1 && + (plant->growths[i]->timing_2 == -1 || + *cur_year_tick <= plant->growths[i]->timing_2)) + { + return selectability::Selectable; + } + else + { + outOfSeason = true; + } + } + } */ } - if (outOfSeason) - { -// out.print("%s has an out of season growth\n", plant->id.c_str()); + if (outOfSeason) { + // out.print("%s has an out of season growth\n", plant->id.c_str()); return selectability::OutOfSeason; } - else - { -// out.printerr("%s cannot be gathered\n", plant->id.c_str()); + else { + // out.printerr("%s cannot be gathered\n", plant->id.c_str()); return result; } } @@ -241,17 +206,17 @@ bool ripe(int32_t x, int32_t y, int32_t start, int32_t end) { } // Looks in the picked growths vector to see if a matching growth has been marked as picked. -bool picked(const df::plant *plant, int32_t growth_subtype) { - df::world_data *world_data = world->world_data; - df::world_site *site = df::world_site::find(plotinfo->site_id); +bool picked(const df::plant* plant, int32_t growth_subtype) { + df::world_data* world_data = world->world_data; + df::world_site* site = df::world_site::find(plotinfo->site_id); int32_t pos_x = site->global_min_x + plant->pos.x / 48; int32_t pos_y = site->global_min_y + plant->pos.y / 48; size_t id = pos_x + pos_y * 16 * world_data->world_width; - df::world_object_data *object_data = df::world_object_data::find(id); + df::world_object_data* object_data = df::world_object_data::find(id); if (!object_data) { return false; } - df::map_block_column *column = world->map.map_block_columns[(plant->pos.x / 16) * world->map.x_count_block + (plant->pos.y / 16)]; + df::map_block_column* column = world->map.map_block_columns[(plant->pos.x / 16) * world->map.x_count_block + (plant->pos.y / 16)]; for (size_t i = 0; i < object_data->picked_growths.x.size(); i++) { if (object_data->picked_growths.x[i] == plant->pos.x && @@ -266,13 +231,12 @@ bool picked(const df::plant *plant, int32_t growth_subtype) { return false; } -bool designate(const df::plant *plant, bool farming) { - df::plant_raw *plant_raw = world->raws.plants.all[plant->material]; +bool designate(const df::plant* plant, bool farming) { + df::plant_raw* plant_raw = world->raws.plants.all[plant->material]; const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant_raw->material_defs.type[plant_material_def::basic_mat], plant_raw->material_defs.idx[plant_material_def::basic_mat]); if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW) || - basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) - { + basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) { return Designations::markPlant(plant); } @@ -280,42 +244,35 @@ bool designate(const df::plant *plant, bool farming) { plant_raw->flags.is_set(plant_raw_flags::MILL) || plant_raw->flags.is_set(plant_raw_flags::EXTRACT_VIAL) || plant_raw->flags.is_set(plant_raw_flags::EXTRACT_BARREL) || - plant_raw->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL)) - { + plant_raw->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL)) { if (!farming) { return Designations::markPlant(plant); } } if (basic_mat.material->reaction_product.id.size() > 0 || - basic_mat.material->reaction_class.size() > 0) - { + basic_mat.material->reaction_class.size() > 0) { if (!farming) { return Designations::markPlant(plant); } } - for (size_t i = 0; i < plant_raw->growths.size(); i++) - { + for (size_t i = 0; i < plant_raw->growths.size(); i++) { if (plant_raw->growths[i]->item_type == df::item_type::SEEDS || // Only trees have seed growths in vanilla, but raws can be modded... - plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) - { + plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) { const DFHack::MaterialInfo growth_mat = DFHack::MaterialInfo(plant_raw->growths[i]->mat_type, plant_raw->growths[i]->mat_index); if ((plant_raw->growths[i]->item_type == df::item_type::SEEDS && (growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED) || growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW))) || - (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH && - growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... + (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH && + growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... { bool seedSource = plant_raw->growths[i]->item_type == df::item_type::SEEDS; - if (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) - { - for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++) - { + if (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) { + for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++) { if (growth_mat.material->reaction_product.material.mat_type[k] == plant_raw->material_defs.type[plant_material_def::seed] && - growth_mat.material->reaction_product.material.mat_index[k] == plant_raw->material_defs.idx[plant_material_def::seed]) - { + growth_mat.material->reaction_product.material.mat_index[k] == plant_raw->material_defs.idx[plant_material_def::seed]) { seedSource = true; break; } @@ -324,8 +281,7 @@ bool designate(const df::plant *plant, bool farming) { bool istree = (tileMaterial(Maps::getTileBlock(plant->pos)->tiletype[plant->pos.x % 16][plant->pos.y % 16]) == tiletype_material::TREE); bool isripe = ripe(plant->pos.x, plant->pos.y, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2); - if ((!farming || seedSource) && (istree || isripe) && !picked(plant, i)) - { + if ((!farming || seedSource) && (istree || isripe) && !picked(plant, i)) { return Designations::markPlant(plant); } } @@ -335,8 +291,7 @@ bool designate(const df::plant *plant, bool farming) { return false; } -command_result df_getplants (color_ostream &out, vector & parameters) -{ +command_result df_getplants(color_ostream& out, vector & parameters) { string plantMatStr = ""; std::vector plantSelections; std::vector collectionCount; @@ -348,16 +303,14 @@ command_result df_getplants (color_ostream &out, vector & parameters) plantSelections.resize(world->raws.plants.all.size()); collectionCount.resize(world->raws.plants.all.size()); - for (size_t i = 0; i < plantSelections.size(); i++) - { + for (size_t i = 0; i < plantSelections.size(); i++) { plantSelections[i] = selectability::Unselected; collectionCount[i] = 0; } bool anyPlantsSelected = false; - for (size_t i = 0; i < parameters.size(); i++) - { + for (size_t i = 0; i < parameters.size(); i++) { if (parameters[i] == "help" || parameters[i] == "?") return CR_WRONG_USAGE; else if (parameters[i] == "-t") @@ -374,23 +327,18 @@ command_result df_getplants (color_ostream &out, vector & parameters) verbose = true; else if (parameters[i] == "-f") farming = true; - else if (parameters[i] == "-n") - { - if (parameters.size() > i + 1) - { + else if (parameters[i] == "-n") { + if (parameters.size() > i + 1) { maxCount = atoi(parameters[i + 1].c_str()); - if (maxCount >= 1) - { + if (maxCount >= 1) { i++; // We've consumed the next parameter, so we need to progress the iterator. } - else - { + else { out.printerr("-n requires a positive integer parameter!\n"); return CR_WRONG_USAGE; } } - else - { + else { out.printerr("-n requires a positive integer parameter!\n"); return CR_WRONG_USAGE; } @@ -398,55 +346,45 @@ command_result df_getplants (color_ostream &out, vector & parameters) else plantNames.insert(toUpper(parameters[i])); } - if (treesonly && shrubsonly) - { + if (treesonly && shrubsonly) { out.printerr("Cannot specify both -t and -s at the same time!\n"); return CR_WRONG_USAGE; } - if (treesonly && farming) - { + if (treesonly && farming) { out.printerr("Cannot specify both -t and -f at the same time!\n"); return CR_WRONG_USAGE; } - if (all && exclude) - { + if (all && exclude) { out.printerr("Cannot specify both -a and -x at the same time!\n"); return CR_WRONG_USAGE; } - if (all && plantNames.size()) - { + if (all && plantNames.size()) { out.printerr("Cannot specify -a along with plant IDs!\n"); return CR_WRONG_USAGE; } CoreSuspender suspend; - for (size_t i = 0; i < world->raws.plants.all.size(); i++) - { - df::plant_raw *plant = world->raws.plants.all[i]; - if (all) - { -// plantSelections[i] = selectablePlant(out, plant, farming); + for (size_t i = 0; i < world->raws.plants.all.size(); i++) { + df::plant_raw* plant = world->raws.plants.all[i]; + if (all) { + // plantSelections[i] = selectablePlant(out, plant, farming); plantSelections[i] = selectablePlant(plant, farming); } - else if (plantNames.find(plant->id) != plantNames.end()) - { + else if (plantNames.find(plant->id) != plantNames.end()) { plantNames.erase(plant->id); -// plantSelections[i] = selectablePlant(out, plant, farming); + // plantSelections[i] = selectablePlant(out, plant, farming); plantSelections[i] = selectablePlant(plant, farming); - switch (plantSelections[i]) - { + switch (plantSelections[i]) { case selectability::Grass: out.printerr("%s is a grass and cannot be gathered\n", plant->id.c_str()); break; case selectability::Nonselectable: - if (farming) - { + if (farming) { out.printerr("%s does not have any parts that can be gathered for seeds for farming\n", plant->id.c_str()); } - else - { + else { out.printerr("%s does not have any parts that can be gathered\n", plant->id.c_str()); } break; @@ -463,8 +401,7 @@ command_result df_getplants (color_ostream &out, vector & parameters) } } } - if (plantNames.size() > 0) - { + if (plantNames.size() > 0) { out.printerr("Invalid plant ID(s):"); for (set::const_iterator it = plantNames.begin(); it != plantNames.end(); it++) out.printerr(" %s", it->c_str()); @@ -472,33 +409,27 @@ command_result df_getplants (color_ostream &out, vector & parameters) return CR_FAILURE; } - for (size_t i = 0; i < plantSelections.size(); i++) - { + for (size_t i = 0; i < plantSelections.size(); i++) { if (plantSelections[i] == selectability::OutOfSeason || - plantSelections[i] == selectability::Selectable) - { + plantSelections[i] == selectability::Selectable) { anyPlantsSelected = true; break; } } - if (!anyPlantsSelected) - { + if (!anyPlantsSelected) { out.print("Valid plant IDs:\n"); - for (size_t i = 0; i < world->raws.plants.all.size(); i++) - { - df::plant_raw *plant = world->raws.plants.all[i]; -// switch (selectablePlant(out, plant, farming)) - switch (selectablePlant(plant, farming)) - { + for (size_t i = 0; i < world->raws.plants.all.size(); i++) { + df::plant_raw* plant = world->raws.plants.all[i]; + // switch (selectablePlant(out, plant, farming)) + switch (selectablePlant(plant, farming)) { case selectability::Grass: case selectability::Nonselectable: continue; case selectability::OutOfSeason: { - if (!treesonly) - { + if (!treesonly) { out.print("* (shrub) %s - %s is out of season\n", plant->id.c_str(), plant->name.c_str()); } break; @@ -523,22 +454,19 @@ command_result df_getplants (color_ostream &out, vector & parameters) } count = 0; - for (size_t i = 0; i < world->plants.all.size(); i++) - { - const df::plant *plant = world->plants.all[i]; - df::map_block *cur = Maps::getTileBlock(plant->pos); + for (size_t i = 0; i < world->plants.all.size(); i++) { + const df::plant* plant = world->plants.all[i]; + df::map_block* cur = Maps::getTileBlock(plant->pos); int x = plant->pos.x % 16; int y = plant->pos.y % 16; if (plantSelections[plant->material] == selectability::OutOfSeason || - plantSelections[plant->material] == selectability::Selectable) - { + plantSelections[plant->material] == selectability::Selectable) { if (exclude || plantSelections[plant->material] == selectability::OutOfSeason) continue; } - else - { + else { if (!exclude) continue; } @@ -553,37 +481,29 @@ command_result df_getplants (color_ostream &out, vector & parameters) continue; if (collectionCount[plant->material] >= maxCount) continue; - if (deselect && Designations::unmarkPlant(plant)) - { + if (deselect && Designations::unmarkPlant(plant)) { collectionCount[plant->material]++; ++count; } - if (!deselect && designate(plant, farming)) - { -// out.print("Designated %s at (%i, %i, %i), %d\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i); + if (!deselect && designate(plant, farming)) { + // out.print("Designated %s at (%i, %i, %i), %d\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i); collectionCount[plant->material]++; ++count; } } - if (count) - { - if (verbose) - { - for (size_t i = 0; i < plantSelections.size(); i++) - { - if (collectionCount[i] > 0) - out.print("Updated %d %s designations.\n", (int)collectionCount[i], world->raws.plants.all[i]->id.c_str()); - } - out.print("\n"); + if (count && verbose) { + for (size_t i = 0; i < plantSelections.size(); i++) { + if (collectionCount[i] > 0) + out.print("Updated %d %s designations.\n", (int)collectionCount[i], world->raws.plants.all[i]->id.c_str()); } + out.print("\n"); } out.print("Updated %d plant designations.\n", (int)count); return CR_OK; } -DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) -{ +DFhackCExport command_result plugin_init(color_ostream& out, vector & commands) { commands.push_back(PluginCommand( "getplants", "Designate trees for chopping and shrubs for gathering.", @@ -591,7 +511,6 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector Date: Tue, 11 Apr 2023 00:58:40 -0700 Subject: [PATCH 1023/2222] add debug logging --- plugins/getplants.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index c9db92f5a..821dfa0b7 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -1,3 +1,4 @@ +#include "Debug.h" #include "PluginManager.h" #include "TileTypes.h" @@ -24,6 +25,11 @@ using std::set; using namespace DFHack; using namespace df::enums; +namespace DFHack +{ +DBG_DECLARE(getplants, log, DebugCategory::LINFO); +} + DFHACK_PLUGIN("getplants"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); @@ -67,14 +73,13 @@ enum class selectability { // is one of the issues in bug 6940 on the bug tracker (the others cases are detected and // result in the plants not being usable for farming or even collectable at all). -//selectability selectablePlant(color_ostream &out, const df::plant_raw *plant, bool farming) -selectability selectablePlant(const df::plant_raw* plant, bool farming) { +selectability selectablePlant(color_ostream& out, const df::plant_raw* plant, bool farming) { const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant->material_defs.type[plant_material_def::basic_mat], plant->material_defs.idx[plant_material_def::basic_mat]); bool outOfSeason = false; selectability result = selectability::Nonselectable; if (plant->flags.is_set(plant_raw_flags::TREE)) { - // out.print("%s is a selectable tree\n", plant->id.c_str()); + DEBUG(log, out).print("%s is a selectable tree\n", plant->id.c_str()); if (farming) { return selectability::Nonselectable; } @@ -83,7 +88,7 @@ selectability selectablePlant(const df::plant_raw* plant, bool farming) { } } else if (plant->flags.is_set(plant_raw_flags::GRASS)) { - // out.print("%s is a non selectable Grass\n", plant->id.c_str()); + DEBUG(log, out).print("%s is a non selectable Grass\n", plant->id.c_str()); return selectability::Grass; } @@ -93,7 +98,7 @@ selectability selectablePlant(const df::plant_raw* plant, bool farming) { if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW) || basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED)) { - // out.print("%s is edible\n", plant->id.c_str()); + DEBUG(log, out).print("%s is edible\n", plant->id.c_str()); if (farming) { if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW)) { result = selectability::Selectable; @@ -109,7 +114,7 @@ selectability selectablePlant(const df::plant_raw* plant, bool farming) { plant->flags.is_set(plant_raw_flags::EXTRACT_VIAL) || plant->flags.is_set(plant_raw_flags::EXTRACT_BARREL) || plant->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL)) { - // out.print("%s is thread/mill/extract\n", plant->id.c_str()); + DEBUG(log, out).print("%s is thread/mill/extract\n", plant->id.c_str()); if (farming) { result = selectability::Selectable; } @@ -120,7 +125,7 @@ selectability selectablePlant(const df::plant_raw* plant, bool farming) { if (basic_mat.material->reaction_product.id.size() > 0 || basic_mat.material->reaction_class.size() > 0) { - // out.print("%s has a reaction\n", plant->id.c_str()); + DEBUG(log, out).print("%s has a reaction\n", plant->id.c_str()); if (farming) { result = selectability::Selectable; } @@ -154,7 +159,7 @@ selectability selectablePlant(const df::plant_raw* plant, bool farming) { if (*cur_year_tick >= plant->growths[i]->timing_1 && (plant->growths[i]->timing_2 == -1 || *cur_year_tick <= plant->growths[i]->timing_2)) { - // out.print("%s has an edible seed or a stockpile growth\n", plant->id.c_str()); + DEBUG(log, out).print("%s has an edible seed or a stockpile growth\n", plant->id.c_str()); if (!farming || seedSource) { return selectability::Selectable; } @@ -188,11 +193,11 @@ selectability selectablePlant(const df::plant_raw* plant, bool farming) { } if (outOfSeason) { - // out.print("%s has an out of season growth\n", plant->id.c_str()); + DEBUG(log, out).print("%s has an out of season growth\n", plant->id.c_str()); return selectability::OutOfSeason; } else { - // out.printerr("%s cannot be gathered\n", plant->id.c_str()); + DEBUG(log, out).print("%s cannot be gathered\n", plant->id.c_str()); return result; } } @@ -368,13 +373,11 @@ command_result df_getplants(color_ostream& out, vector & parameters) { for (size_t i = 0; i < world->raws.plants.all.size(); i++) { df::plant_raw* plant = world->raws.plants.all[i]; if (all) { - // plantSelections[i] = selectablePlant(out, plant, farming); - plantSelections[i] = selectablePlant(plant, farming); + plantSelections[i] = selectablePlant(out, plant, farming); } else if (plantNames.find(plant->id) != plantNames.end()) { plantNames.erase(plant->id); - // plantSelections[i] = selectablePlant(out, plant, farming); - plantSelections[i] = selectablePlant(plant, farming); + plantSelections[i] = selectablePlant(out, plant, farming); switch (plantSelections[i]) { case selectability::Grass: out.printerr("%s is a grass and cannot be gathered\n", plant->id.c_str()); @@ -421,8 +424,7 @@ command_result df_getplants(color_ostream& out, vector & parameters) { out.print("Valid plant IDs:\n"); for (size_t i = 0; i < world->raws.plants.all.size(); i++) { df::plant_raw* plant = world->raws.plants.all[i]; - // switch (selectablePlant(out, plant, farming)) - switch (selectablePlant(plant, farming)) { + switch (selectablePlant(out, plant, farming)) { case selectability::Grass: case selectability::Nonselectable: continue; @@ -486,7 +488,7 @@ command_result df_getplants(color_ostream& out, vector & parameters) { ++count; } if (!deselect && designate(plant, farming)) { - // out.print("Designated %s at (%i, %i, %i), %d\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i); + DEBUG(log, out).print("Designated %s at (%i, %i, %i), %d\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i); collectionCount[plant->material]++; ++count; } From cbf1e236721ee79f212d504ccfb6428d5c763208 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 02:34:45 -0700 Subject: [PATCH 1024/2222] fix designation of non-fruit trees --- plugins/getplants.cpp | 66 +++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/plugins/getplants.cpp b/plugins/getplants.cpp index 821dfa0b7..87f1c0e76 100644 --- a/plugins/getplants.cpp +++ b/plugins/getplants.cpp @@ -236,7 +236,15 @@ bool picked(const df::plant* plant, int32_t growth_subtype) { return false; } -bool designate(const df::plant* plant, bool farming) { +bool designate(color_ostream& out, const df::plant* plant, bool farming) { + TRACE(log, out).print("Attempting to designate %s at (%i, %i, %i)\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z); + + if (!farming) { + bool istree = (tileMaterial(Maps::getTileBlock(plant->pos)->tiletype[plant->pos.x % 16][plant->pos.y % 16]) == tiletype_material::TREE); + if (istree) + return Designations::markPlant(plant); + } + df::plant_raw* plant_raw = world->raws.plants.all[plant->material]; const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant_raw->material_defs.type[plant_material_def::basic_mat], plant_raw->material_defs.idx[plant_material_def::basic_mat]); @@ -263,34 +271,40 @@ bool designate(const df::plant* plant, bool farming) { } for (size_t i = 0; i < plant_raw->growths.size(); i++) { - if (plant_raw->growths[i]->item_type == df::item_type::SEEDS || // Only trees have seed growths in vanilla, but raws can be modded... - plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) { - const DFHack::MaterialInfo growth_mat = DFHack::MaterialInfo(plant_raw->growths[i]->mat_type, plant_raw->growths[i]->mat_index); - if ((plant_raw->growths[i]->item_type == df::item_type::SEEDS && - (growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED) || - growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW))) || - (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH && - growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... - { - bool seedSource = plant_raw->growths[i]->item_type == df::item_type::SEEDS; + TRACE(log, out).print("growth item type=%d\n", plant_raw->growths[i]->item_type); + // Only trees have seed growths in vanilla, but raws can be modded... + if (plant_raw->growths[i]->item_type != df::item_type::SEEDS && + plant_raw->growths[i]->item_type != df::item_type::PLANT_GROWTH) + continue; - if (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) { - for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++) { - if (growth_mat.material->reaction_product.material.mat_type[k] == plant_raw->material_defs.type[plant_material_def::seed] && - growth_mat.material->reaction_product.material.mat_index[k] == plant_raw->material_defs.idx[plant_material_def::seed]) { - seedSource = true; - break; - } - } - } + const DFHack::MaterialInfo growth_mat = DFHack::MaterialInfo(plant_raw->growths[i]->mat_type, plant_raw->growths[i]->mat_index); + TRACE(log, out).print("edible_cooked=%d edible_raw=%d leaf_mat=%d\n", + growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED), + growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW), + growth_mat.material->flags.is_set(material_flags::LEAF_MAT)); + if (!(plant_raw->growths[i]->item_type == df::item_type::SEEDS && + (growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED) || + growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW))) && + !(plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH && + growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now... + continue; + + bool seedSource = plant_raw->growths[i]->item_type == df::item_type::SEEDS; - bool istree = (tileMaterial(Maps::getTileBlock(plant->pos)->tiletype[plant->pos.x % 16][plant->pos.y % 16]) == tiletype_material::TREE); - bool isripe = ripe(plant->pos.x, plant->pos.y, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2); - if ((!farming || seedSource) && (istree || isripe) && !picked(plant, i)) { - return Designations::markPlant(plant); + if (plant_raw->growths[i]->item_type == df::item_type::PLANT_GROWTH) { + for (size_t k = 0; growth_mat.material->reaction_product.material.mat_type.size(); k++) { + if (growth_mat.material->reaction_product.material.mat_type[k] == plant_raw->material_defs.type[plant_material_def::seed] && + growth_mat.material->reaction_product.material.mat_index[k] == plant_raw->material_defs.idx[plant_material_def::seed]) { + seedSource = true; + break; } } } + + if ((!farming || seedSource) && + ripe(plant->pos.x, plant->pos.y, plant_raw->growths[i]->timing_1, plant_raw->growths[i]->timing_2) && + !picked(plant, i)) + return Designations::markPlant(plant); } return false; @@ -460,6 +474,8 @@ command_result df_getplants(color_ostream& out, vector & parameters) { const df::plant* plant = world->plants.all[i]; df::map_block* cur = Maps::getTileBlock(plant->pos); + TRACE(log, out).print("Examining %s at (%i, %i, %i) [index=%d]\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i); + int x = plant->pos.x % 16; int y = plant->pos.y % 16; if (plantSelections[plant->material] == selectability::OutOfSeason || @@ -487,7 +503,7 @@ command_result df_getplants(color_ostream& out, vector & parameters) { collectionCount[plant->material]++; ++count; } - if (!deselect && designate(plant, farming)) { + if (!deselect && designate(out, plant, farming)) { DEBUG(log, out).print("Designated %s at (%i, %i, %i), %d\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i); collectionCount[plant->material]++; ++count; From 20bea5fa217353fb73235cc2d58b2a55bde597e6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 03:49:28 -0700 Subject: [PATCH 1025/2222] add ability to filter by cloth and silk --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 42 +++++++++++++++----- plugins/lua/buildingplan/filterselection.lua | 2 + 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 7c52315ea..2104158f5 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,6 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game - `buildingplan`: hide planner overlay while the DF tutorial is active so that it can detect when you have placed the carpenter's workshop and bed and allow you to finish the tutorial +- `buildingplan`: can now filter by cloth and silk materials (for ropes) -@ `buildingplan`: rearranged elements of ``planneroverlay`` interface -@ `buildingplan`: rearranged elements of ``itemselection`` interface - Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 2193b380b..1d9f58416 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -12,6 +12,7 @@ #include "df/construction_type.h" #include "df/item.h" #include "df/job_item.h" +#include "df/organic_mat_category.h" #include "df/world.h" using std::map; @@ -150,6 +151,8 @@ static const df::dfhack_material_category wood_cat(df::dfhack_material_category: static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); static const df::dfhack_material_category glass_cat(df::dfhack_material_category::mask_glass); static const df::dfhack_material_category clay_cat(df::dfhack_material_category::mask_clay); +static const df::dfhack_material_category cloth_cat(df::dfhack_material_category::mask_cloth); +static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); static void cache_matched(int16_t type, int32_t index) { MaterialInfo mi; @@ -169,11 +172,26 @@ static void cache_matched(int16_t type, int32_t index) { } else if (mi.matches(clay_cat)) { DEBUG(status).print("cached clay material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "clay")); + } else if (mi.matches(cloth_cat)) { + DEBUG(status).print("cached cloth material: %s (%d, %d)\n", mi.toString().c_str(), type, index); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "cloth")); + } else if (mi.matches(silk_cat)) { + DEBUG(status).print("cached silk material: %s (%d, %d)\n", mi.toString().c_str(), type, index); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "silk")); } else TRACE(status).print("not matched: %s\n", mi.toString().c_str()); } +static void load_organic_material_cache(df::organic_mat_category cat) { + auto& mat_tab = world->raws.mat_table; + auto& cat_vec = mat_tab.organic_types[cat]; + auto& cat_indices = mat_tab.organic_indexes[cat]; + for (size_t i = 0; i < cat_vec.size(); i++) { + cache_matched(cat_vec[i], cat_indices[i]); + } +} + static void load_material_cache() { df::world_raws &raws = world->raws; for (int i = 1; i < DFHack::MaterialInfo::NUM_BUILTIN; ++i) @@ -183,17 +201,9 @@ static void load_material_cache() { for (size_t i = 0; i < raws.inorganics.size(); i++) cache_matched(0, i); - for (size_t i = 0; i < raws.plants.all.size(); i++) { - df::plant_raw *p = raws.plants.all[i]; - if (p->material.size() <= 1) - continue; - for (size_t j = 0; j < p->material.size(); j++) { - if (p->material[j]->id == "WOOD") { - cache_matched(DFHack::MaterialInfo::PLANT_BASE+j, i); - break; - } - } - } + load_organic_material_cache(df::organic_mat_category::Wood); + load_organic_material_cache(df::organic_mat_category::PlantFiber); + load_organic_material_cache(df::organic_mat_category::Silk); } static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { @@ -792,6 +802,10 @@ static int setMaterialMaskFilter(lua_State *L) { mask |= glass_cat.whole; else if (cat == "clay") mask |= clay_cat.whole; + else if (cat == "cloth") + mask |= cloth_cat.whole; + else if (cat == "silk") + mask |= silk_cat.whole; } DEBUG(status,*out).print( "setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n", @@ -837,6 +851,8 @@ static int getMaterialMaskFilter(lua_State *L) { ret.emplace("metal", !bits || bits & metal_cat.whole); ret.emplace("glass", !bits || bits & glass_cat.whole); ret.emplace("clay", !bits || bits & clay_cat.whole); + ret.emplace("cloth", !bits || bits & cloth_cat.whole); + ret.emplace("silk", !bits || bits & silk_cat.whole); Lua::Push(L, ret); return 1; } @@ -883,6 +899,10 @@ static int setMaterialFilter(lua_State *L) { mask.whole |= glass_cat.whole; else if (mat.matches(clay_cat)) mask.whole |= clay_cat.whole; + else if (mat.matches(cloth_cat)) + mask.whole |= cloth_cat.whole; + else if (mat.matches(silk_cat)) + mask.whole |= silk_cat.whole; } filter.setMaterialMask(mask.whole); get_item_filters(*out, key).setItemFilter(*out, filter, index); diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index 84b5c46ea..498c89c1e 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -453,6 +453,8 @@ function QualityAndMaterialsPage:refresh() make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), make_cat_choice('Clay', 'clay', 'CUSTOM_SHIFT_C', cats), + make_cat_choice('Cloth', 'cloth', 'CUSTOM_SHIFT_L', cats), + make_cat_choice('Silk', 'silk', 'CUSTOM_SHIFT_K', cats), } self.subviews.materials_categories:setChoices(category_choices) From 460b1e8eaf05f5dafad745ccc5776a66f47de431 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 10:14:28 -0700 Subject: [PATCH 1026/2222] adjust code to new tree root type --- library/modules/MapCache.cpp | 17 ++++++++++------- library/modules/Maps.cpp | 1 + .../remotefortressreader.cpp | 13 ++++++++----- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/library/modules/MapCache.cpp b/library/modules/MapCache.cpp index b88a078b1..c329220c9 100644 --- a/library/modules/MapCache.cpp +++ b/library/modules/MapCache.cpp @@ -60,6 +60,7 @@ using namespace std; #include "df/flow_info.h" #include "df/job.h" #include "df/plant.h" +#include "df/plant_root_tile.h" #include "df/plant_tree_info.h" #include "df/plant_tree_tile.h" #include "df/region_map_entry.h" @@ -805,14 +806,16 @@ void MapExtras::BlockInfo::prepare(Block *mblock) // If the block is at or above the plant's base level, we use the body array // otherwise we use the roots. // TODO: verify that the tree bounds intersect the block. - df::plant_tree_tile tile; + bool has_tree_tile = false; int z_diff = block->map_pos.z - pp->pos.z; - if (z_diff >= 0) - tile = info->body[z_diff][xx + (yy*info->dim_x)]; - else - tile = info->roots[-1 - z_diff][xx + (yy*info->dim_x)]; - if (tile.whole && !(tile.bits.blocked)) - { + if (z_diff >= 0) { + df::plant_tree_tile tile = info->body[z_diff][xx + (yy * info->dim_x)]; + has_tree_tile = tile.whole && !(tile.bits.blocked); + } else { + df::plant_root_tile tile = info->roots[-1 - z_diff][xx + (yy * info->dim_x)]; + has_tree_tile = tile.whole && !(tile.bits.blocked); + } + if (has_tree_tile) { df::coord pos = pp->pos; pos.x = pos.x - (info->dim_x / 2) + xx; pos.y = pos.y - (info->dim_y / 2) + yy; diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index ddcd6078f..2e831075d 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -57,6 +57,7 @@ using namespace std; #include "df/flow_info.h" #include "df/map_block_column.h" #include "df/plant.h" +#include "df/plant_root_tile.h" #include "df/plant_tree_info.h" #include "df/plant_tree_tile.h" #include "df/region_map_entry.h" diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index f33044877..061cf2698 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -76,7 +76,9 @@ #include "df/ocean_wave.h" #include "df/physical_attribute_type.h" #include "df/plant.h" +#include "df/plant_tree_tile.h" #include "df/plant_raw_flags.h" +#include "df/plant_root_tile.h" #include "df/projectile.h" #include "df/proj_itemst.h" #include "df/proj_unitst.h" @@ -971,17 +973,18 @@ void CopyBlock(df::map_block * DfBlock, RemoteFortressReader::MapBlock * NetBloc || yyy >= 16 ) continue; - df::plant_tree_tile tile; if (-localPos.z < 0) { - tile = tree_info->roots[-1 + localPos.z][xx + (yy*tree_info->dim_x)]; + df::plant_root_tile tile = tree_info->roots[-1 + localPos.z][xx + (yy * tree_info->dim_x)]; + if (!tile.whole || tile.bits.blocked) + continue; } else { - tile = tree_info->body[-localPos.z][xx + (yy*tree_info->dim_x)]; + df::plant_tree_tile tile = tree_info->body[-localPos.z][xx + (yy * tree_info->dim_x)]; + if (!tile.whole || tile.bits.blocked) + continue; } - if (!tile.whole || tile.bits.blocked) - continue; if (tree_info->body_height <= 1) trunk_percent[xxx][yyy] = 0; else From 63b95994051615261ebfab22460a212697d6cd9e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 10:15:24 -0700 Subject: [PATCH 1027/2222] update xml head --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 78a2008e4..eea005e8c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 78a2008e4d20a47f0533f285c3457a278dba5556 +Subproject commit eea005e8cec4448ab865862c744d6da16106f641 From f813e6fa939306b48af66e926724074630d2cffd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 10:23:29 -0700 Subject: [PATCH 1028/2222] update stonesense head --- plugins/stonesense | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stonesense b/plugins/stonesense index 3e494d9d9..9046525c9 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 3e494d9d968add443ebd63cc167933cc813f0eee +Subproject commit 9046525c91fd91e90685dfc071aa952bfe906155 From 766ec620b5cfff4a9c2a2bbd3b494fd81c722f93 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 11 Apr 2023 19:06:51 +0000 Subject: [PATCH 1029/2222] Auto-update submodules library/xml: master plugins/stonesense: master --- library/xml | 2 +- plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index eea005e8c..4453c0dcd 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit eea005e8cec4448ab865862c744d6da16106f641 +Subproject commit 4453c0dcda1255845c358f34f57c0471cf85ab2c diff --git a/plugins/stonesense b/plugins/stonesense index 9046525c9..b7073b664 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 9046525c91fd91e90685dfc071aa952bfe906155 +Subproject commit b7073b664310b909989ebe68de36a164e452825a From 49d55b2f07263fbb1179f6f8ec16a9f8f72026dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 12:31:27 -0700 Subject: [PATCH 1030/2222] bump version for beta --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dfb6dfb21..9168ebd85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "beta2") +set(DFHACK_RELEASE "r1b4") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From 6c1740e2ca2556dd1deca0e2d6115b0368adcaef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 11 Apr 2023 17:13:56 -0700 Subject: [PATCH 1031/2222] re-version to rc1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9168ebd85..3077bf139 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "r1b4") +set(DFHACK_RELEASE "rc1") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From f7e3973da8e6fc07574412c98de6f146f030ff6a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 12 Apr 2023 01:45:42 -0700 Subject: [PATCH 1032/2222] bump to 50.07-r1, scrub changelog --- CMakeLists.txt | 4 +-- docs/changelog.txt | 78 ++++++++++++++++++++++++++-------------------- library/xml | 2 +- scripts | 2 +- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3077bf139..b9f323640 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,8 +192,8 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "rc1") -set(DFHACK_PRERELEASE TRUE) +set(DFHACK_RELEASE "r1") +set(DFHACK_PRERELEASE FALSE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 2104158f5..351e13d76 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -26,7 +26,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ]]] ================================================================================ -======== IMPORTANT: rename this, and add a new "future" section, BEFORE ======== +======== IMPORTANT: rename this, and add a new "Future" section, BEFORE ======== ======== making a new DFHack release, even if the only changes made ======== ======== were in submodules with their own changelogs! ======== ================================================================================ @@ -34,54 +34,66 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins -- `faststart`: speeds up the "Loading..." screen so the Main Menu appears sooner ## Fixes -- `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar -- ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.07-r1 + +## New Plugins +- `faststart`: speeds up the "Loading..." screen so the Main Menu appears faster + +## Fixes +-@ `hotkeys`: hotkey hints on menu popup will no longer get their last character cut off by the scrollbar +-@ ``launchdf``: launch Dwarf Fortress via the Steam client so Steam Workshop is functional - `blueprint`: interpret saplings, shrubs, and twigs as floors instead of walls - `combine`: fix error processing stockpiles with boundaries that extend outside of the map -- `prospector`: display both "raw" Z levels and "cooked" elevations +-@ `prospector`: display both "raw" Z levels and "cooked" elevations - `stockpiles`: fix crash when importing settings for gems from other worlds -- `stockpiles`: allow numbers in saved stockpile filenames +-@ `stockpiles`: allow numbers in saved stockpile filenames ## Misc Improvements -- `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game -- `buildingplan`: hide planner overlay while the DF tutorial is active so that it can detect when you have placed the carpenter's workshop and bed and allow you to finish the tutorial +-@ `buildingplan`: items in the item selection dialog should now use the same item quality symbols as the base game +-@ `buildingplan`: hide planner overlay while the DF tutorial is active so that it can detect when you have placed the carpenter's workshop and bed and allow you to finish the tutorial - `buildingplan`: can now filter by cloth and silk materials (for ropes) -@ `buildingplan`: rearranged elements of ``planneroverlay`` interface -@ `buildingplan`: rearranged elements of ``itemselection`` interface -- Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game -- Mods: scripts from only the most recent version of an installed mod are added to the script path -- Mods: give active mods a chance to reattach their load hooks when a world is reloaded +-@ Mods: scripts in mods that are only in the steam workshop directory are now accessible. this means that a script-only mod that you never mark as "active" when generating a world will still receive automatic updates and be usable from in-game +-@ Mods: scripts from only the most recent version of an installed mod are added to the script path +-@ Mods: give active mods a chance to reattach their load hooks when a world is reloaded - `gui/control-panel`: bugfix services are now enabled by default - Core: hide DFHack terminal console by default when running on Steam Deck ## Documentation - `installing`: updated to include Steam installation instructions -## API - ## Lua - added two new window borders: ``gui.BOLD_FRAME`` for accented elements and ``gui.INTERIOR_MEDIUM_FRAME`` for a signature-less frame that's thicker than the existing ``gui.INTERIOR_FRAME`` -## Removed - # 50.07-beta2 ## New Plugins - `getplants`: designate trees for chopping and shrubs for gathering according to type -- `prospector`: get stone, ore, gem, and other tile property counts in fort mode. embark site estimates are not yet available. +- `prospector`: get stone, ore, gem, and other tile property counts in fort mode. ## Fixes -- `buildingplan`: filters are now properly applied to planned stairs -- `buildingplan`: existing carved up/down stairs are now taken into account when determining which stair shape to construct +-@ `buildingplan`: filters are now properly applied to planned stairs +-@ `buildingplan`: existing carved up/down stairs are now taken into account when determining which stair shape to construct - `buildingplan`: upright spike traps are now placed extended rather than retracted - `buildingplan`: you can no longer designate constructions on tiles with magma or deep water, mirroring the vanilla restrictions -- `buildingplan`: fixed material filters getting lost for planning buildings on save/reload -- `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) +-@ `buildingplan`: fixed material filters getting lost for planning buildings on save/reload +-@ `buildingplan`: respect building size limits (e.g. roads and bridges cannot be more than 31 tiles in any dimension) - `tailor`: properly discriminate between dyed and undyed cloth -- `tailor`: no longer default to using adamantine cloth for producing clothes +-@ `tailor`: no longer default to using adamantine cloth for producing clothes - `tailor`: take queued orders into account when calculating available materials - `tailor`: skip units who can't wear clothes - `tailor`: identify more available items as available, solving issues with over-production @@ -100,17 +112,17 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `orders`: add minimize button to overlay panel so you can get it out of the way to read long statue descriptions when choosing a subject in the details screen - `orders`: add option to delete exported files from the import dialog - `enable`: can now interpret aliases defined with the `alias` command -- scripts in ``data/installed mods/`` subfolders are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world. +- Mods: scripts in mods are now automatically added to the DFHack script path. DFHack recognizes two directories in a mod's folder: ``scripts_modinstalled/`` and ``scripts_modactive/``. ``scripts_modinstalled/`` folders will always be added the script path, regardless of whether the mod is active in a world. ``scripts_modactive/`` folders will only be added to the script path when the mod is active in the current loaded world. ## Documentation - `modding-guide`: guide updated to include information for 3rd party script developers - the ``untested`` tag has been renamed to ``unavailable`` to better reflect the status of the remaining unavaialable tools. most of the simply "untested" tools have now been tested and marked as working. the remaining tools are known to need development work before they are available again. ## Lua -- ``widget.Label``: tokens can now specify a ``htile`` property to indicate the tile that should be shown when the Label is hovered over with the mouse -- ``widget.Label``: click handlers no longer get the label itself as the first param to the click handler -- ``widget.CycleHotkeyLabel``: options that are bare integers will no longer be interpreted as the pen color in addition to being the label and value -- ``widget.CycleHotkeyLabel``: option labels and pens can now be functions that return a label or pen +- ``widgets.Label``: tokens can now specify a ``htile`` property to indicate the tile that should be shown when the Label is hovered over with the mouse +- ``widgets.Label``: click handlers no longer get the label itself as the first param to the click handler +- ``widgets.CycleHotkeyLabel``: options that are bare integers will no longer be interpreted as the pen color in addition to being the label and value +- ``widgets.CycleHotkeyLabel``: option labels and pens can now be functions that return a label or pen # 50.07-beta1 @@ -172,14 +184,14 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. -- `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. +-@ `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. -@ `seedwatch`: fix saving and loading of seed stock targets - `autodump`: changed behaviour to only change ``dump`` and ``forbid`` flags if an item is successfully dumped. -@ `autochop`: generate default names for burrows with no assigned names - ``Buildings::StockpileIterator``: fix check for stockpile items on block boundary. - `tailor`: block making clothing sized for toads; make replacement clothing orders use the size of the wearer, not the size of the garment -@ `confirm`: fix fps drop when enabled -- `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's +-@ `channel-safely`: fix an out of bounds error regarding the REPORT event listener receiving (presumably) stale id's ## Misc Improvements - `autobutcher`: logs activity to the console terminal instead of making disruptive in-game announcements @@ -203,13 +215,13 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - ``Units::isFortControlled``: Account for agitated wildlife -@ Fix right click sometimes closing both a DFHack window and a vanilla panel -- Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible -- `channel-safely`: fixed bug resulting in marker mode never being set for any designation +-@ Fixed issue with scrollable lists having some data off-screen if they were scrolled before being made visible +-@ `channel-safely`: fixed bug resulting in marker mode never being set for any designation -@ `automelt`: fixed bug related to lua stack smashing behavior in returned stockpile configs -@ `autochop`: fixed bug related to lua stack smashing behavior in returned stockpile configs -- `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg +-@ `nestboxes`: now cancels any in-progress hauling jobs when it protects a fertile egg -@ Fix persisted data not being written on manual save -- `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg +-@ `nestboxes`: now scans for eggs more frequently and cancels any in-progress hauling jobs when it protects a fertile egg ## Misc Improvements -@ `automelt`: is now more resistent to vanilla savegame corruption @@ -301,7 +313,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # 50.05-alpha2 ## Fixes -- `autofarm`: don't duplicate status line entries for crops with no current supply +-@ `autofarm`: don't duplicate status line entries for crops with no current supply -@ `orders`: allow the orders library to be listed and imported properly (if you previously copied the orders library into your ``dfhack-config/orders`` directory to work around this bug, you can remove those files now) - `tailor`: now respects the setting of the "used dyed clothing" standing order toggle diff --git a/library/xml b/library/xml index 4453c0dcd..43a89a268 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 4453c0dcda1255845c358f34f57c0471cf85ab2c +Subproject commit 43a89a268b825fc05457678b19e551bf632dcd19 diff --git a/scripts b/scripts index 973b8ef19..ec1a69788 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 973b8ef191cc92cff62d96aa591c7631818d19b6 +Subproject commit ec1a69788fd6329008672523b622fd8b390fea73 From 21784568bd144d3a70444f9cbc35705619297a1c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 09:57:06 -0800 Subject: [PATCH 1033/2222] migrate from SDL interposing to the hooks API --- CMakeLists.txt | 13 - library/CMakeLists.txt | 10 +- library/Core.cpp | 26 +- library/Hooks-darwin.cpp | 318 --------------- library/Hooks-linux.cpp | 139 ------- library/Hooks-windows.cpp | 838 -------------------------------------- library/Hooks.cpp | 3 + library/include/Core.h | 15 +- plugins/title-folder.cpp | 2 +- 9 files changed, 18 insertions(+), 1346 deletions(-) delete mode 100644 library/Hooks-darwin.cpp delete mode 100644 library/Hooks-linux.cpp delete mode 100644 library/Hooks-windows.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b9f323640..02857f91d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,19 +299,6 @@ if(WIN32) DESTINATION ${CMAKE_BINARY_DIR}/depends/) file(COPY ${ZLIB_DOWNLOAD_DIR}/zlib.lib DESTINATION ${CMAKE_BINARY_DIR}/depends/zlib/lib/) - - # Do the same for SDLreal.dll - # (DFHack doesn't require this at build time, so no need to move it to the build folder) - set(SDLREAL_DOWNLOAD_DIR ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH}) - if(${DFHACK_BUILD_ARCH} STREQUAL "64") - download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-SDL.dll" - ${SDLREAL_DOWNLOAD_DIR}/SDLreal.dll - "1ae242c4b94cb03756a1288122a66faf") - else() - download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-SDL.dll" - ${SDLREAL_DOWNLOAD_DIR}/SDLreal.dll - "5a09604daca6b2b5ce049d79af935d6a") - endif() endif() if(APPLE) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index a3fcb8b6f..418c9b371 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -95,7 +95,6 @@ endif() set(MAIN_SOURCES_WINDOWS ${CONSOLE_SOURCES} - Hooks-windows.cpp Hooks.cpp PlugLoad-windows.cpp Process-windows.cpp @@ -108,14 +107,12 @@ endif() set(MAIN_SOURCES_LINUX ${CONSOLE_SOURCES} - Hooks-linux.cpp PlugLoad-posix.cpp Process-linux.cpp ) set(MAIN_SOURCES_DARWIN ${CONSOLE_SOURCES} - Hooks-darwin.cpp PlugLoad-posix.cpp Process-darwin.cpp ) @@ -376,8 +373,7 @@ add_executable(binpatch binpatch.cpp) target_link_libraries(binpatch dfhack-md5) if(WIN32) - # name the resulting library SDL.dll on Windows - set_target_properties(dfhack PROPERTIES OUTPUT_NAME "SDL" ) + set_target_properties(dfhack PROPERTIES OUTPUT_NAME "dfhooks" ) set_target_properties(dfhack PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"" ) set_target_properties(dfhack-client PROPERTIES COMPILE_FLAGS "/FI\"Export.h\"" ) else() @@ -436,10 +432,6 @@ if(UNIX) install(TARGETS dfhooks LIBRARY DESTINATION . RUNTIME DESTINATION .) -else() - # On windows, copy the renamed SDL so DF can still run. - install(PROGRAMS ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH}/SDLreal.dll - DESTINATION ${DFHACK_LIBRARY_DESTINATION}) endif() # install the main lib diff --git a/library/Core.cpp b/library/Core.cpp index b1fe2d389..7bdaed80c 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2292,12 +2292,13 @@ int Core::Shutdown ( void ) #define KEY_F0 0410 /* Function keys. Space for 64 */ #define KEY_F(n) (KEY_F0+(n)) /* Value of function key n */ +// returns true if the event has been handled bool Core::ncurses_wgetch(int in, int & out) { if(!started) { out = in; - return true; + return false; } if(in >= KEY_F(1) && in <= KEY_F(8)) { @@ -2312,18 +2313,18 @@ bool Core::ncurses_wgetch(int in, int & out) df::global::plotinfo->main.hotkeys[idx].cmd == df::ui_hotkey::T_cmd::None) { setHotkeyCmd(df::global::plotinfo->main.hotkeys[idx].name); - return false; + return true; } else { out = in; - return true; + return false; } } */ } out = in; - return true; + return false; } bool Core::DFH_ncurses_key(int key) @@ -2331,7 +2332,7 @@ bool Core::DFH_ncurses_key(int key) if (getenv("DFHACK_HEADLESS")) return true; int dummy; - return !ncurses_wgetch(key, dummy); + return ncurses_wgetch(key, dummy); } int UnicodeAwareSym(const SDL::KeyboardEvent& ke) @@ -2382,21 +2383,19 @@ int UnicodeAwareSym(const SDL::KeyboardEvent& ke) return unicode; } - -//MEMO: return false if event is consumed -int Core::DFH_SDL_Event(SDL::Event* ev) +// returns true if the event is handled +bool Core::DFH_SDL_Event(SDL::Event* ev) { // do NOT process events before we are ready. - if(!started) return true; - if(!ev) - return true; + if(!started || !ev) + return false; if(ev->type == SDL::ET_ACTIVEEVENT && ev->active.gain) { // clear modstate when gaining focus in case alt-tab was used when // losing focus and modstate is now incorrectly set modstate = 0; - return true; + return false; } if(ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP) @@ -2431,8 +2430,7 @@ int Core::DFH_SDL_Event(SDL::Event* ev) hotkey_states[ke->ksym.sym] = false; } } - return true; - // do stuff with the events... + return false; } bool Core::SelectHotkey(int sym, int modifiers) diff --git a/library/Hooks-darwin.cpp b/library/Hooks-darwin.cpp deleted file mode 100644 index 418cf5472..000000000 --- a/library/Hooks-darwin.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* -https://github.com/peterix/dfhack -Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct interpose_s -{ - void *new_func; - void *orig_func; -} interpose_t; - -#include "DFHack.h" -#include "Core.h" -#include "Hooks.h" -#include "SDL_events.h" -#include - -/*static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) = -{ - { (void *)DFH_SDL_Init, (void *)SDL_Init }, - { (void *)DFH_SDL_PollEvent, (void *)SDL_PollEvent }, - { (void *)DFH_SDL_Quit, (void *)SDL_Quit }, - { (void *)DFH_SDL_NumJoysticks, (void *)SDL_NumJoysticks }, - -};*/ - -#define DYLD_INTERPOSE(_replacement,_replacee) \ - __attribute__((used)) static struct{ const void* replacment; const void* replacee; } \ - _interpose_##_replacee __attribute__ ((section ("__DATA,__interpose"))) = \ - { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee }; - -DYLD_INTERPOSE(DFH_SDL_Init,SDL_Init); -DYLD_INTERPOSE(DFH_SDL_PollEvent,SDL_PollEvent); -DYLD_INTERPOSE(DFH_SDL_Quit,SDL_Quit); -DYLD_INTERPOSE(DFH_SDL_NumJoysticks,SDL_NumJoysticks); -DYLD_INTERPOSE(DFH_wgetch,wgetch); - -/******************************************************************************* -* SDL part starts here * -*******************************************************************************/ - -#define SDL_APPMOUSEFOCUS 0x01 /**< The app has mouse coverage */ -#define SDL_APPINPUTFOCUS 0x02 /**< The app has input focus */ -#define SDL_APPACTIVE 0x04 /**< The application is active */ -static uint8_t (*_SDL_GetAppState)(void) = 0; -DFhackCExport uint8_t SDL_GetAppState(void) -{ - return _SDL_GetAppState(); -} - -// hook - called for each game tick (or more often) -DFhackCExport int DFH_SDL_NumJoysticks(void) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - return c.Update(); -} - -// hook - called at program exit -static void (*_SDL_Quit)(void) = 0; -DFhackCExport void DFH_SDL_Quit(void) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - c.Shutdown(); - - SDL_Quit(); -} - -// called by DF to check input events -static int (*_SDL_PollEvent)(SDL::Event* event) = 0; -DFhackCExport int DFH_SDL_PollEvent(SDL::Event* event) -{ - pollevent_again: - // if SDL returns 0 here, it means there are no more events. return 0 - int orig_return = SDL_PollEvent(event); - if(!orig_return || (!(SDL_GetAppState() & SDL_APPINPUTFOCUS) && - (event->type == SDL::ET_KEYDOWN || event->type == SDL::ET_KEYUP))) - return 0; - // otherwise we have an event to filter - else if( event != 0 ) - { - DFHack::Core & c = DFHack::Core::getInstance(); - // if we consume the event, ask SDL for more. - if(!c.DFH_SDL_Event(event)) - goto pollevent_again; - } - return orig_return; -} - -static int (*_SDL_PushEvent)(SDL::Event* event) = 0; -DFhackCExport int SDL_PushEvent(SDL::Event* event) -{ - return _SDL_PushEvent(event); -} - -struct WINDOW; -DFhackCExport int DFH_wgetch(WINDOW *win) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - wgetch_again: - int in = wgetch(win); - int out; - if(c.ncurses_wgetch(in, out)) - { - // not consumed, give to DF - return out; - } - else - { - // consumed, repeat - goto wgetch_again; - } -} - -void dlsym_bind_or_exit(void **target, const char *name) -{ - void *sym = dlsym(RTLD_NEXT, name); - if (sym) - { - if (*target && *target != sym) - { - fprintf(stderr, "warning: rebinding symbol %s from %p to %p\n", - name, *target, sym); - } - *target = sym; - } - else - { - fprintf(stderr, "Fatal: Could not find symbol: %s\n", name); - fprintf(stdout, "dfhack: something went horribly wrong\n" - "Check stderr.log for details\n"); - exit(1); - } -} - - -// New SDL functions starting in r5 -static vPtr (*_SDL_CreateRGBSurface)(uint32_t flags, int width, int height, int depth, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = 0; -DFhackCExport vPtr SDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) -{ - return _SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask); -} - -static vPtr (*_SDL_CreateRGBSurfaceFrom)(vPtr pixels, int width, int height, int depth, int pitch, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = 0; -DFhackCExport vPtr SDL_CreateRGBSurfaceFrom(vPtr pixels, int width, int height, int depth, int pitch, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) -{ - return _SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask); -} - -static void (*_SDL_FreeSurface)(vPtr surface) = 0; -DFhackCExport void SDL_FreeSurface(vPtr surface) -{ - _SDL_FreeSurface(surface); -} - -static vPtr (*_SDL_ConvertSurface)(vPtr surface, vPtr format, uint32_t flags) = 0; -DFhackCExport vPtr SDL_ConvertSurface(vPtr surface, vPtr format, uint32_t flags) -{ - return _SDL_ConvertSurface(surface, format, flags); -} - -static int (*_SDL_LockSurface)(vPtr surface) = 0; -DFhackCExport int SDL_LockSurface(vPtr surface) -{ - return _SDL_LockSurface(surface); -} - -static void (*_SDL_UnlockSurface)(vPtr surface) = 0; -DFhackCExport void SDL_UnlockSurface(vPtr surface) -{ - _SDL_UnlockSurface(surface); -} - -static uint8_t (*_SDL_GetMouseState)(int *, int *) = 0; -DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y) -{ - return _SDL_GetMouseState(x,y); -} - -static void * (*_SDL_GetVideoSurface)( void ) = 0; -DFhackCExport void * SDL_GetVideoSurface(void) -{ - return _SDL_GetVideoSurface(); -} - -static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0; -DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - if ( c.isValid() && dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 ) - { - DFHack::Graphic* g = c.getGraphic(); - DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h); - - if ( ov != NULL ) - { - if ( ov->paintOver ) - { - _SDL_UpperBlit(src, srcrect, dst, dstrect); - } - - DFHack::DFSDL_Rect* dstrect2 = new DFHack::DFSDL_Rect; - dstrect2->x = dstrect->x; - dstrect2->y = dstrect->y; - dstrect2->w = dstrect->w; - dstrect2->h = dstrect->h; - - if ( ov->dstResize != NULL ) - { - DFHack::DFSDL_Rect* r = (DFHack::DFSDL_Rect*)ov->dstResize; - dstrect2->x += r->x; - dstrect2->y += r->y; - dstrect2->w += r->w; - dstrect2->h += r->h; - } - - int result = _SDL_UpperBlit(ov->surface, ov->rect, dst, dstrect2); - delete dstrect2; - return result; - } - } - - return _SDL_UpperBlit(src, srcrect, dst, dstrect); -} - -static int (*_SDL_SemWait)(vPtr) = 0; -DFhackCExport int SDL_SemWait(vPtr sem) -{ - return _SDL_SemWait(sem); -} - -static int (*_SDL_SemPost)(vPtr) = 0; -DFhackCExport int SDL_SemPost(vPtr sem) -{ - return _SDL_SemPost(sem); -} - -// hook - called at program start, initialize some stuffs we'll use later -static int (*_SDL_Init)(uint32_t flags) = 0; -DFhackCExport int DFH_SDL_Init(uint32_t flags) -{ - // reroute stderr - fprintf(stderr,"dfhack: attempting to hook in\n"); - // we don't reroute stdout until we figure out if this should be done at all - // See: Console-posix.cpp - - // find real functions - fprintf(stderr,"dfhack: saving real SDL functions\n"); - - #define bind(sym) dlsym_bind_or_exit((void**)&_##sym, #sym) - bind(SDL_Init); - bind(SDL_Quit); - bind(SDL_PollEvent); - bind(SDL_PushEvent); - - bind(SDL_UpperBlit); - bind(SDL_CreateRGBSurface); - bind(SDL_CreateRGBSurfaceFrom); - bind(SDL_FreeSurface); - bind(SDL_ConvertSurface); - bind(SDL_LockSurface); - bind(SDL_UnlockSurface); - bind(SDL_GetMouseState); - bind(SDL_GetVideoSurface); - - bind(SDL_SemWait); - bind(SDL_SemPost); - bind(SDL_GetAppState); - #undef bind - - fprintf(stderr, "dfhack: saved real SDL functions\n"); - assert(_SDL_Init && _SDL_Quit && _SDL_PollEvent); - fprintf(stderr, "dfhack: hooking successful\n"); - - // prevent any subprocesses from trying to load libdfhack.dylib - setenv("DYLD_INSERT_LIBRARIES", "", 1); - - int ret = SDL_Init(flags); - return ret; -} diff --git a/library/Hooks-linux.cpp b/library/Hooks-linux.cpp deleted file mode 100644 index 7a0cdf947..000000000 --- a/library/Hooks-linux.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* -https://github.com/peterix/dfhack -Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DFHack.h" -#include "Core.h" -#include "Hooks.h" -#include - -/******************************************************************************* -* SDL part starts here * -*******************************************************************************/ -// hook - called for each game tick (or more often) -DFhackCExport int SDL_NumJoysticks(void) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - return c.Update(); -} - -// hook - called at program exit -static void (*_SDL_Quit)(void) = 0; -DFhackCExport void SDL_Quit(void) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - c.Shutdown(); - if(_SDL_Quit) - { - _SDL_Quit(); - } -} - -// called by DF to check input events -static int (*_SDL_PollEvent)(SDL::Event* event) = 0; -DFhackCExport int SDL_PollEvent(SDL::Event* event) -{ - pollevent_again: - // if SDL returns 0 here, it means there are no more events. return 0 - int orig_return = _SDL_PollEvent(event); - if(!orig_return) - return 0; - // otherwise we have an event to filter - else if( event != 0 ) - { - DFHack::Core & c = DFHack::Core::getInstance(); - // if we consume the event, ask SDL for more. - if(!c.DFH_SDL_Event(event)) - goto pollevent_again; - } - return orig_return; -} - -struct WINDOW; -DFhackCExport int wgetch(WINDOW *win) -{ - if (getenv("DFHACK_HEADLESS")) - { - return 0; - } - static int (*_wgetch)(WINDOW * win) = (int (*)( WINDOW * )) dlsym(RTLD_NEXT, "wgetch"); - if(!_wgetch) - { - exit(EXIT_FAILURE); - } - DFHack::Core & c = DFHack::Core::getInstance(); - wgetch_again: - int in = _wgetch(win); - int out; - if(c.ncurses_wgetch(in, out)) - { - // not consumed, give to DF - return out; - } - else - { - // consumed, repeat - goto wgetch_again; - } -} - -// hook - called at program start, initialize some stuffs we'll use later -static int (*_SDL_Init)(uint32_t flags) = 0; -DFhackCExport int SDL_Init(uint32_t flags) -{ - // find real functions - _SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init"); - _SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit"); - _SDL_PollEvent = (int (*)(SDL::Event*))dlsym(RTLD_NEXT,"SDL_PollEvent"); - - // check if we got them - if(_SDL_Init && _SDL_Quit && _SDL_PollEvent) - { - fprintf(stderr,"dfhack: hooking successful\n"); - } - else - { - // bail, this would be a disaster otherwise - fprintf(stderr,"dfhack: something went horribly wrong\n"); - exit(1); - } - - int ret = _SDL_Init(flags); - return ret; -} diff --git a/library/Hooks-windows.cpp b/library/Hooks-windows.cpp deleted file mode 100644 index d3f39b969..000000000 --- a/library/Hooks-windows.cpp +++ /dev/null @@ -1,838 +0,0 @@ -/* -https://github.com/peterix/dfhack -Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#define DFhackCExport extern "C" __declspec(dllexport) - -#include -#include -#include -#include -#include -#include "Core.h" -#include "Hooks.h" -#include - -#include "tinythread.h" -#include "modules/Graphic.h" - -/*************************************************************************/ -// extremely boring wrappers beyond this point. Only fix when broken - -// we don't know which of the SDL functions will be called first... so we -// just catch the first one and init all our function pointers at that time -static void InitSDLPointers(void); -static std::once_flag inited; - -/// wrappers for SDL 1.2 functions used in 40d16 -/***** Condition variables - -SDL_CreateCond - SDL_cond * SDLCALL SDL_CreateCond(void); -SDL_CondSignal - int SDLCALL SDL_CondSignal(SDL_cond *cond); -SDL_CondWait - int SDLCALL SDL_CondWait(SDL_cond *cond, SDL_mutex *mut); -SDL_DestroyCond - void SDLCALL SDL_DestroyCond(SDL_cond *cond); -*/ -static vPtr (*_SDL_CreateCond)() = 0; -DFhackCExport vPtr SDL_CreateCond() -{ - return _SDL_CreateCond(); -} - -static int (*_SDL_CondSignal)( vPtr ) = 0; -DFhackCExport int SDL_CondSignal( vPtr cond ) -{ - return _SDL_CondSignal(cond); -} - -static int (*_SDL_CondWait)( vPtr,vPtr ) = 0; -DFhackCExport int SDL_CondWait( vPtr cond, vPtr mutex ) -{ - return _SDL_CondWait(cond, mutex); -} - -static void (*_SDL_DestroyCond)( vPtr ) = 0; -DFhackCExport void SDL_DestroyCond( vPtr cond ) -{ - _SDL_DestroyCond(cond); -} - -/***** mutexes - -SDL_CreateMutex - SDL_mutex * SDLCALL SDL_CreateMutex(void); -SDL_mutexP - int SDLCALL SDL_mutexP(SDL_mutex *mutex); -SDL_DestroyMutex - void SDLCALL SDL_DestroyMutex(SDL_mutex *mutex); -*/ -static vPtr (*_SDL_CreateMutex)(void) = 0; -DFhackCExport vPtr SDL_CreateMutex(void) -{ - return _SDL_CreateMutex(); -} - -static int (*_SDL_mutexP)(vPtr mutex) = 0; -DFhackCExport int SDL_mutexP(vPtr mutex) -{ - return _SDL_mutexP(mutex); -} - -static int (*_SDL_mutexV)(vPtr mutex) = 0; -DFhackCExport int SDL_mutexV(vPtr mutex) -{ - return _SDL_mutexV(mutex); -} - -static void (*_SDL_DestroyMutex)(vPtr mutex) = 0; -DFhackCExport void SDL_DestroyMutex(vPtr mutex) -{ - _SDL_DestroyMutex(mutex); -} - - -/***** timers - -SDL_AddTimer - SDL_TimerID SDLCALL SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param); -SDL_RemoveTimer - SDL_bool SDLCALL SDL_RemoveTimer(SDL_TimerID t); -SDL_GetTicks - Uint32 SDLCALL SDL_GetTicks(void); -*/ -static vPtr (*_SDL_AddTimer)(uint32_t interval, fPtr callback, vPtr param) = 0; -DFhackCExport vPtr SDL_AddTimer(uint32_t interval, fPtr callback, vPtr param) -{ - return _SDL_AddTimer(interval, callback, param); -} - -static bool (*_SDL_RemoveTimer)(vPtr timer) = 0; -DFhackCExport bool SDL_RemoveTimer(vPtr timer) -{ - return _SDL_RemoveTimer(timer); -} - -static uint32_t (*_SDL_GetTicks)(void) = 0; -DFhackCExport uint32_t SDL_GetTicks(void) -{ - return _SDL_GetTicks(); -} - -/***** Surfaces -SDL_CreateRGBSurface - SDL_Surface * SDLCALL SDL_CreateRGBSurface - (Uint32 flags, int width, int height, int depth, - Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); - -SDL_CreateRGBSurfaceFrom - SDL_Surface * SDLCALL SDL_CreateRGBSurfaceFrom - (void *pixels, int width, int height, int depth, int pitch, - Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); - -SDL_FreeSurface - void SDLCALL SDL_FreeSurface(SDL_Surface *surface); - -SDL_ConvertSurface - SDL_Surface * SDLCALL SDL_ConvertSurface - (SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags); - -SDL_LockSurface - int SDLCALL SDL_LockSurface(SDL_Surface *surface); - -SDL_UnlockSurface - void SDLCALL SDL_UnlockSurface(SDL_Surface *surface); -*/ - -static vPtr (*_SDL_CreateRGBSurface)(uint32_t flags, int width, int height, int depth, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = 0; -DFhackCExport vPtr SDL_CreateRGBSurface(uint32_t flags, int width, int height, int depth, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) -{ - return _SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask); -} - -static vPtr (*_SDL_CreateRGBSurfaceFrom)(vPtr pixels, int width, int height, int depth, int pitch, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) = 0; -DFhackCExport vPtr SDL_CreateRGBSurfaceFrom(vPtr pixels, int width, int height, int depth, int pitch, - uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask) -{ - return _SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, Rmask, Gmask, Bmask, Amask); -} - -static void (*_SDL_FreeSurface)(vPtr surface) = 0; -DFhackCExport void SDL_FreeSurface(vPtr surface) -{ - _SDL_FreeSurface(surface); -} - -static vPtr (*_SDL_ConvertSurface)(vPtr surface, vPtr format, uint32_t flags) = 0; -DFhackCExport vPtr SDL_ConvertSurface(vPtr surface, vPtr format, uint32_t flags) -{ - return _SDL_ConvertSurface(surface, format, flags); -} - -static int (*_SDL_LockSurface)(vPtr surface) = 0; -DFhackCExport int SDL_LockSurface(vPtr surface) -{ - return _SDL_LockSurface(surface); -} - -static void (*_SDL_UnlockSurface)(vPtr surface) = 0; -DFhackCExport void SDL_UnlockSurface(vPtr surface) -{ - _SDL_UnlockSurface(surface); -} - -/***** More surface stuff -SDL_MapRGB - Uint32 SDLCALL SDL_MapRGB - (const SDL_PixelFormat * const format, const Uint8 r, const Uint8 g, const Uint8 b); - -SDL_SaveBMP_RW - int SDLCALL SDL_SaveBMP_RW - (SDL_Surface *surface, SDL_RWops *dst, int freedst); - -SDL_SetAlpha - int SDLCALL SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha); - -SDL_SetColorKey - int SDLCALL SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key); - -SDL_GetVideoInfo - const SDL_VideoInfo * SDLCALL SDL_GetVideoInfo(void); - -SDL_SetVideoMode - SDL_Surface * SDLCALL SDL_SetVideoMode - (int width, int height, int bpp, Uint32 flags); - -SDL_UpperBlit - int SDLCALL SDL_UpperBlit - (SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect); -*/ - -static uint32_t (*_SDL_MapRGB)(vPtr pixelformat, uint8_t r, uint8_t g, uint8_t b) = 0; -DFhackCExport uint32_t SDL_MapRGB(vPtr pixelformat, uint8_t r, uint8_t g, uint8_t b) -{ - return _SDL_MapRGB(pixelformat,r,g,b); -} - -static int (*_SDL_SaveBMP_RW)(vPtr surface, vPtr dst, int freedst) = 0; -DFhackCExport int SDL_SaveBMP_RW(vPtr surface, vPtr dst, int freedst) -{ - return _SDL_SaveBMP_RW(surface,dst,freedst); -} - -static int (*_SDL_SetAlpha)(vPtr surface, uint32_t flag, uint8_t alpha) = 0; -DFhackCExport int SDL_SetAlpha(vPtr surface, uint32_t flag, uint8_t alpha) -{ - return _SDL_SetAlpha(surface,flag,alpha); -} - -static int (*_SDL_SetColorKey)(vPtr surface, uint32_t flag, uint32_t key) = 0; -DFhackCExport int SDL_SetColorKey(vPtr surface, uint32_t flag, uint32_t key) -{ - return _SDL_SetColorKey(surface,flag,key); -} - -static vPtr (*_SDL_GetVideoInfo)(void) = 0; -DFhackCExport vPtr SDL_GetVideoInfo(void) -{ - return _SDL_GetVideoInfo(); -} - -static vPtr (*_SDL_SetVideoMode)(int width, int height, int bpp, uint32_t flags) = 0; -DFhackCExport vPtr SDL_SetVideoMode(int width, int height, int bpp, uint32_t flags) -{ - return _SDL_SetVideoMode(width, height, bpp, flags); -} - -static int (*_SDL_UpperBlit)(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) = 0; -DFhackCExport int SDL_UpperBlit(DFHack::DFSDL_Surface* src, DFHack::DFSDL_Rect* srcrect, DFHack::DFSDL_Surface* dst, DFHack::DFSDL_Rect* dstrect) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - if ( c.isValid() && dstrect != NULL && dstrect->h != 0 && dstrect->w != 0 ) - { - DFHack::Graphic* g = c.getGraphic(); - DFHack::DFTileSurface* ov = g->Call(dstrect->x/dstrect->w, dstrect->y/dstrect->h); - - if ( ov != NULL ) - { - if ( ov->paintOver ) - { - _SDL_UpperBlit(src, srcrect, dst, dstrect); - } - - DFHack::DFSDL_Rect* dstrect2 = new DFHack::DFSDL_Rect; - dstrect2->x = dstrect->x; - dstrect2->y = dstrect->y; - dstrect2->w = dstrect->w; - dstrect2->h = dstrect->h; - - if ( ov->dstResize != NULL ) - { - DFHack::DFSDL_Rect* r = (DFHack::DFSDL_Rect*)ov->dstResize; - dstrect2->x += r->x; - dstrect2->y += r->y; - dstrect2->w += r->w; - dstrect2->h += r->h; - } - - int result = _SDL_UpperBlit(ov->surface, ov->rect, dst, dstrect2); - delete dstrect2; - return result; - } - } - - return _SDL_UpperBlit(src, srcrect, dst, dstrect); -} - -/***** Even more surface -SDL_GL_GetAttribute - int SDLCALL SDL_GL_GetAttribute(SDL_GLattr attr, int* value); - -SDL_GL_SetAttribute - int SDLCALL SDL_GL_SetAttribute(SDL_GLattr attr, int value); - -SDL_WM_SetCaption - void SDLCALL SDL_WM_SetCaption(const char *title, const char *icon); - -SDL_WM_SetIcon - void SDLCALL SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask); - -SDL_FillRect - int SDLCALL SDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color); -*/ - - -static void * (*_SDL_GetVideoSurface)( void ) = 0; -DFhackCExport void * SDL_GetVideoSurface(void) -{ - return _SDL_GetVideoSurface(); -} - -static void * (*_SDL_DisplayFormat)( void * surface ) = 0; -DFhackCExport void * SDL_DisplayFormat(void *surface) -{ - return _SDL_DisplayFormat(surface); -} - -// SDL_Surface *SDL_DisplayFormatAlpha(SDL_Surface *surface); -static void * (*_SDL_DisplayFormatAlpha)( void * surface ) = 0; -DFhackCExport void * SDL_DisplayFormatAlpha(void *surface) -{ - return _SDL_DisplayFormatAlpha(surface); -} - -//void SDL_GetRGBA(Uint32 pixel, SDL_PixelFormat *fmt, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a); - -static void (*_SDL_GetRGBA)(uint32_t pixel, void * fmt, uint8_t * r, uint8_t * g, uint8_t * b, uint8_t *a) = 0; -DFhackCExport void SDL_GetRGBA(uint32_t pixel, void * fmt, uint8_t * r, uint8_t * g, uint8_t * b, uint8_t *a) -{ - return _SDL_GetRGBA(pixel, fmt, r, g, b, a); -} - -static int (*_SDL_GL_GetAttribute)(int attr, int * value) = 0; -DFhackCExport int SDL_GL_GetAttribute(int attr, int * value) -{ - return _SDL_GL_GetAttribute(attr,value); -} - -static int (*_SDL_GL_SetAttribute)(int attr, int value) = 0; -DFhackCExport int SDL_GL_SetAttribute(int attr, int value) -{ - return _SDL_GL_SetAttribute(attr,value); -} - -static void (*_SDL_WM_SetCaption)(const char *title, const char *icon) = 0; -DFhackCExport void SDL_WM_SetCaption(const char *title, const char *icon) -{ - //_SDL_WM_SetCaption("DwarfHacked the Fortress of Hacks",icon); - _SDL_WM_SetCaption(title,icon); -} - -static void (*_SDL_WM_SetIcon)(vPtr icon, uint8_t *mask) = 0; -DFhackCExport void SDL_WM_SetIcon(vPtr icon, uint8_t *mask) -{ - _SDL_WM_SetIcon(icon, mask); -} - -static int (*_SDL_FillRect)(vPtr dst, vPtr dstrect, uint32_t color) = 0; -DFhackCExport int SDL_FillRect(vPtr dst, vPtr dstrect, uint32_t color) -{ - return _SDL_FillRect(dst,dstrect,color); -} - -/***** Events and input -SDL_EnableKeyRepeat - int SDLCALL SDL_EnableKeyRepeat(int delay, int interval); -SDL_EnableUNICODE - int SDLCALL SDL_EnableUNICODE(int enable); -SDL_GetKeyState - Uint8 * SDLCALL SDL_GetKeyState(int *numkeys); -SDL_PollEvent - int SDLCALL SDL_PollEvent(SDL_Event *event); -SDL_PushEvent - int SDLCALL SDL_PushEvent(SDL_Event *event); -*/ - -static int (*_SDL_EnableKeyRepeat)(int delay, int interval) = 0; -DFhackCExport int SDL_EnableKeyRepeat(int delay, int interval) -{ - return _SDL_EnableKeyRepeat(delay, interval); -} - -static int (*_SDL_EnableUNICODE)(int enable) = 0; -DFhackCExport int SDL_EnableUNICODE(int enable) -{ - if(!enable) - { - fprintf(stderr, "SDL_EnableUNICODE turned off. Keybindings may break.\n"); - } - return _SDL_EnableUNICODE(enable); -} - -static uint8_t * (*_SDL_GetKeyState)(int* numkeys) = 0; -DFhackCExport uint8_t * SDL_GetKeyState(int* numkeys) -{ - return _SDL_GetKeyState(numkeys); -} - -// called by DF to check input events -static int (*_SDL_PollEvent)(SDL::Event* event) = 0; -DFhackCExport int SDL_PollEvent(SDL::Event* event) -{ - pollevent_again: - // if SDL returns 0 here, it means there are no more events. return 0 - int orig_return = _SDL_PollEvent(event); - if(!orig_return) - return 0; - // otherwise we have an event to filter - else if( event != 0 ) - { - DFHack::Core & c = DFHack::Core::getInstance(); - // if we consume the event, ask SDL for more. - if(!c.DFH_SDL_Event(event)) - goto pollevent_again; - } - return orig_return; -} - -static int (*_SDL_PushEvent)(SDL::Event* event) = 0; -DFhackCExport int SDL_PushEvent(SDL::Event* event) -{ - return _SDL_PushEvent(event); -} - -/***** error handling -SDL_GetError - char * SDLCALL SDL_GetError(void); -SDL_SetError - extern DECLSPEC void SDLCALL SDL_SetError(const char *fmt, ...); -SDL_ClearError - extern DECLSPEC void SDLCALL SDL_ClearError(void); -SDL_Error - extern DECLSPEC void SDLCALL SDL_Error(SDL_errorcode code); -*/ - -static char * (*_SDL_GetError)(void) = 0; -DFhackCExport char * SDL_GetError(void) -{ - return _SDL_GetError(); -} - -static void (*_SDL_SetError)(const char *fmt, ...) = 0; -DFhackCExport void SDL_SetError(const char *fmt, ...) Wformat(printf,1,2) -{ - char buf[1024]; - va_list args; - va_start(args,fmt); - vsnprintf(buf, sizeof(buf) - 1 ,fmt,args); - va_end(args); - _SDL_SetError(buf); -} - -static void (*_SDL_ClearError)(void) = 0; -DFhackCExport void SDL_ClearError(void) -{ - _SDL_ClearError(); -} - -static void (*_SDL_Error)(int code) = 0; -DFhackCExport void SDL_Error(int code) -{ - _SDL_Error(code); -} - -/***** symbol resolution -SDL_LoadFunction - extern DECLSPEC void * SDLCALL SDL_LoadFunction(void *handle, const char *name); -SDL_LoadObject - extern DECLSPEC void * SDLCALL SDL_LoadObject(const char *sofile); -SDL_UnloadObject - extern DECLSPEC void SDLCALL SDL_UnloadObject(void *handle); -*/ - -static void * (*_SDL_LoadFunction)(vPtr handle, const char *name) = 0; -DFhackCExport void * SDL_LoadFunction(vPtr handle, const char *name) -{ - return _SDL_LoadFunction(handle, name); -} - -extern "C" static vPtr (*_SDL_LoadObject)(const char *sofile) = 0; -DFhackCExport vPtr SDL_LoadObject(const char *sofile) -{ - return _SDL_LoadObject(sofile); -} - -static void (*_SDL_UnloadObject)(vPtr handle) = 0; -DFhackCExport void SDL_UnloadObject(vPtr handle) -{ - _SDL_UnloadObject(handle); -} - -/***** r/w -SDL_ReadBE32 - extern DECLSPEC Uint32 SDLCALL SDL_ReadBE32(SDL_RWops *src); -SDL_ReadLE16 - extern DECLSPEC Uint16 SDLCALL SDL_ReadLE16(SDL_RWops *src); -SDL_ReadLE32 - extern DECLSPEC Uint32 SDLCALL SDL_ReadLE32(SDL_RWops *src); -*/ - -static uint32_t (*_SDL_ReadBE32)(vPtr src) = 0; -DFhackCExport uint32_t SDL_ReadBE32(vPtr src) -{ - return _SDL_ReadBE32(src); -} - -static uint16_t (*_SDL_ReadLE16)(vPtr src) = 0; -DFhackCExport uint16_t SDL_ReadLE16(vPtr src) -{ - return _SDL_ReadLE16(src); -} - -static uint32_t (*_SDL_ReadLE32)(vPtr src) = 0; -DFhackCExport uint32_t SDL_ReadLE32(vPtr src) -{ - return _SDL_ReadLE32(src); -} - -/***** Misc -SDL_RWFromFile - SDL_RWops * SDLCALL SDL_RWFromFile(const char *file, const char *mode); -SDL_SetModuleHandle - void SDLCALL SDL_SetModuleHandle(void *hInst); -SDL_ShowCursor - int SDLCALL SDL_ShowCursor(int toggle); -SDL_strlcpy - size_t SDLCALL SDL_strlcpy(char *dst, const char *src, size_t maxlen); -*/ - -static vPtr (*_SDL_RWFromFile)(const char* file, const char *mode) = 0; -DFhackCExport vPtr SDL_RWFromFile(const char* file, const char *mode) -{ - return _SDL_RWFromFile(file, mode); -} - -static void (*_SDL_SetModuleHandle)(vPtr hInst) = 0; -DFhackCExport void SDL_SetModuleHandle(vPtr hInst) -{ - _SDL_SetModuleHandle(hInst); -} - -static int (*_SDL_ShowCursor)(int toggle) = 0; -DFhackCExport int SDL_ShowCursor(int toggle) -{ - return _SDL_ShowCursor(toggle); -} - -static size_t (*_SDL_strlcpy)(char *dst, const char *src, size_t maxlen) = 0; -DFhackCExport size_t SDL_strlcpy(char *dst, const char *src, size_t maxlen) -{ - if(!_SDL_strlcpy) - { - HMODULE realSDLlib = LoadLibrary("SDLreal.dll"); - if(!realSDLlib) - { - exit(-111); - } - _SDL_strlcpy = (size_t (*)(char*, const char*, size_t))GetProcAddress(realSDLlib,"SDL_strlcpy"); - } - return _SDL_strlcpy(dst,src,maxlen); -} - -/***** The real meat of this -SDL_Init -SDL_Quit -SDL_GL_SwapBuffers - void SDLCALL SDL_GL_SwapBuffers(void); -*/ - - -// hook - called at program exit -static void (*_SDL_Quit)(void) = 0; -DFhackCExport void SDL_Quit(void) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - c.Shutdown(); - if(_SDL_Quit) - { - _SDL_Quit(); - } -} -// this is supported from 0.31.04 forward -DFhackCExport int SDL_NumJoysticks(void) -{ - DFHack::Core & c = DFHack::Core::getInstance(); - return c.Update(); -} - -static void (*_SDL_GL_SwapBuffers)(void) = 0; -DFhackCExport void SDL_GL_SwapBuffers(void) -{ - InitSDLPointers(); - _SDL_GL_SwapBuffers(); -} - -// hook - called every tick in the 2D mode of DF -static int (*_SDL_Flip)(void * some_ptr) = 0; -DFhackCExport int SDL_Flip(void * some_ptr) -{ - InitSDLPointers(); - return _SDL_Flip(some_ptr); -} - -static int (*_SDL_Init)(uint32_t flags) = 0; -DFhackCExport int SDL_Init(uint32_t flags) -{ - InitSDLPointers(); - return _SDL_Init(flags); -} - -/* -MORE CRAP -*/ -static void * (*_SDL_CreateSemaphore)(uint32_t initial_value) = 0; -DFhackCExport void *SDL_CreateSemaphore(uint32_t initial_value) -{ - InitSDLPointers(); - return _SDL_CreateSemaphore(initial_value); -} - -static vPtr (*_SDL_CreateThread)(int (*fn)(void *), void *data) = 0; -DFhackCExport vPtr SDL_CreateThread(int (*fn)(void *), void *data) -{ - InitSDLPointers(); - return _SDL_CreateThread(fn,data); -} - - -static void (*_SDL_Delay)(uint32_t ms) = 0; -DFhackCExport void SDL_Delay(uint32_t ms) -{ - InitSDLPointers(); - _SDL_Delay(ms); -} - -static void (*_SDL_DestroySemaphore)(void *sem) = 0; -DFhackCExport void SDL_DestroySemaphore(void *sem) -{ - InitSDLPointers(); - _SDL_DestroySemaphore(sem); -} - -static vPtr (*_SDL_ListModes)(vPtr format, uint32_t flags) = 0; -DFhackCExport vPtr SDL_ListModes(vPtr format, uint32_t flags) -{ - InitSDLPointers(); - return _SDL_ListModes(format, flags); -} - -static uint8_t (*_SDL_GetAppState)(void) = 0; -DFhackCExport uint8_t SDL_GetAppState(void) -{ - InitSDLPointers(); - return _SDL_GetAppState(); -} - -static uint8_t (*_SDL_GetMouseState)(int *, int *) = 0; -DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y) -{ - InitSDLPointers(); - return _SDL_GetMouseState(x,y); -} - -static int (*_SDL_InitSubSystem)(uint32_t flags) = 0; -DFhackCExport int SDL_InitSubSystem(uint32_t flags) -{ - InitSDLPointers(); - return _SDL_InitSubSystem(flags); -} - -static int (*_SDL_SemPost)(void *sem) = 0; -DFhackCExport int SDL_SemPost(void *sem) -{ - InitSDLPointers(); - return _SDL_SemPost(sem); -} - -static int (*_SDL_SemTryWait)(void *sem) = 0; -DFhackCExport int SDL_SemTryWait(void *sem) -{ - InitSDLPointers(); - return _SDL_SemTryWait(sem); -} - -static int (*_SDL_SemWait)(void *sem) = 0; -DFhackCExport int SDL_SemWait(void *sem) -{ - InitSDLPointers(); - return _SDL_SemWait(sem); -} - -static uint32_t (*_SDL_ThreadID)(void) = 0; -DFhackCExport uint32_t SDL_ThreadID(void) -{ - InitSDLPointers(); - return _SDL_ThreadID(); -} - -static char* (*_SDL_getenv)(const char *name) = 0; -DFhackCExport char* SDL_getenv(const char *name) -{ - InitSDLPointers(); - return _SDL_getenv(name); -} - -static size_t (*_SDL_strlcat)(char *dst, const char *src, size_t maxlen) = 0; -DFhackCExport size_t SDL_strlcat(char *dst, const char *src, size_t maxlen) -{ - InitSDLPointers(); - return _SDL_strlcat(dst, src, maxlen); -} - -void FirstCall() -{ - // reroute stdout and stderr - freopen("stdout.log", "w", stdout); - freopen("stderr.log", "w", stderr); - HMODULE realSDLlib = LoadLibrary("SDLreal.dll"); - if(!realSDLlib) - { - MessageBox(0,"Can't load SDLreal.dll\n","Error", MB_OK); - fprintf(stderr, "Can't load SDLreal.dll\n"); - return; - } - fprintf(stderr, "FirstCall()\n"); - // stuff for DF - _SDL_AddTimer = (void*(*)(uint32_t, void*, void*)) GetProcAddress(realSDLlib,"SDL_AddTimer"); - _SDL_CondSignal = (int (*)(vPtr))GetProcAddress(realSDLlib,"SDL_CondSignal"); - _SDL_CondWait = (int (*)(vPtr, vPtr))GetProcAddress(realSDLlib,"SDL_CondWait"); - _SDL_ConvertSurface = (void*(*)(void*, void*, uint32_t))GetProcAddress(realSDLlib,"SDL_ConvertSurface"); - _SDL_CreateCond = (vPtr(*)())GetProcAddress(realSDLlib,"SDL_CreateCond"); - _SDL_CreateMutex = (vPtr(*)())GetProcAddress(realSDLlib,"SDL_CreateMutex"); - _SDL_CreateRGBSurface = (void*(*)(uint32_t, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t))GetProcAddress(realSDLlib,"SDL_CreateRGBSurface"); - _SDL_CreateRGBSurfaceFrom = (void*(*)(void*, int, int, int, int, uint32_t, uint32_t, uint32_t, uint32_t))GetProcAddress(realSDLlib,"SDL_CreateRGBSurfaceFrom"); - _SDL_DestroyCond = (void (*)(vPtr))GetProcAddress(realSDLlib,"SDL_DestroyCond"); - _SDL_DestroyMutex = (void (*)(vPtr))GetProcAddress(realSDLlib,"SDL_DestroyMutex"); - _SDL_EnableKeyRepeat = (int (*)(int, int))GetProcAddress(realSDLlib,"SDL_EnableKeyRepeat"); - _SDL_EnableUNICODE = (int (*)(int))GetProcAddress(realSDLlib,"SDL_EnableUNICODE"); - _SDL_GetVideoSurface = (void*(*)())GetProcAddress(realSDLlib,"SDL_GetVideoSurface"); - _SDL_DisplayFormat = (void * (*) (void *))GetProcAddress(realSDLlib,"SDL_DisplayFormat"); - _SDL_DisplayFormatAlpha = (void * (*) (void *))GetProcAddress(realSDLlib,"SDL_DisplayFormatAlpha"); - _SDL_GetRGBA = (void (*) (uint32_t, void *, uint8_t *, uint8_t *, uint8_t *, uint8_t *))GetProcAddress(realSDLlib,"SDL_GetRGBA"); - _SDL_FreeSurface = (void (*)(void*))GetProcAddress(realSDLlib,"SDL_FreeSurface"); - _SDL_GL_GetAttribute = (int (*)(int, int*))GetProcAddress(realSDLlib,"SDL_GL_GetAttribute"); - _SDL_GL_SetAttribute = (int (*)(int, int))GetProcAddress(realSDLlib,"SDL_GL_SetAttribute"); - _SDL_GL_SwapBuffers = (void (*)())GetProcAddress(realSDLlib,"SDL_GL_SwapBuffers"); - _SDL_GetError = (char*(*)())GetProcAddress(realSDLlib,"SDL_GetError"); - _SDL_GetKeyState = (uint8_t*(*)(int*))GetProcAddress(realSDLlib,"SDL_GetKeyState"); - _SDL_GetTicks = (uint32_t (*)())GetProcAddress(realSDLlib,"SDL_GetTicks"); - _SDL_GetVideoInfo = (void*(*)())GetProcAddress(realSDLlib,"SDL_GetVideoInfo"); - _SDL_Init = (int (*)(uint32_t))GetProcAddress(realSDLlib,"SDL_Init"); - _SDL_Flip = (int (*)( void * )) GetProcAddress(realSDLlib, "SDL_Flip"); - _SDL_LockSurface = (int (*)(void*))GetProcAddress(realSDLlib,"SDL_LockSurface"); - _SDL_MapRGB = (uint32_t (*)(void*, uint8_t, uint8_t, uint8_t))GetProcAddress(realSDLlib,"SDL_MapRGB"); - _SDL_PollEvent = (int (*)(SDL::Event*))GetProcAddress(realSDLlib,"SDL_PollEvent"); - _SDL_PushEvent = (int (*)(SDL::Event*))GetProcAddress(realSDLlib,"SDL_PushEvent"); - _SDL_Quit = (void (*)())GetProcAddress(realSDLlib,"SDL_Quit"); - _SDL_RWFromFile = (void*(*)(const char*, const char*))GetProcAddress(realSDLlib,"SDL_RWFromFile"); - _SDL_RemoveTimer = (bool (*)(void*))GetProcAddress(realSDLlib,"SDL_RemoveTimer"); - _SDL_SaveBMP_RW = (int (*)(void*, void*, int))GetProcAddress(realSDLlib,"SDL_SaveBMP_RW"); - _SDL_SetAlpha = (int (*)(void*, uint32_t, uint8_t))GetProcAddress(realSDLlib,"SDL_SetAlpha"); - _SDL_SetColorKey = (int (*)(void*, uint32_t, uint32_t))GetProcAddress(realSDLlib,"SDL_SetColorKey"); - _SDL_SetModuleHandle = (void (*)(void*))GetProcAddress(realSDLlib,"SDL_SetModuleHandle"); - _SDL_SetVideoMode = (void*(*)(int, int, int, uint32_t))GetProcAddress(realSDLlib,"SDL_SetVideoMode"); - _SDL_ShowCursor = (int (*)(int))GetProcAddress(realSDLlib,"SDL_ShowCursor"); - _SDL_UnlockSurface = (void (*)(void*))GetProcAddress(realSDLlib,"SDL_UnlockSurface"); - _SDL_UpperBlit = (int (*)(DFHack::DFSDL_Surface*, DFHack::DFSDL_Rect*, DFHack::DFSDL_Surface*, DFHack::DFSDL_Rect*))GetProcAddress(realSDLlib,"SDL_UpperBlit"); - _SDL_WM_SetCaption = (void (*)(const char*, const char*))GetProcAddress(realSDLlib,"SDL_WM_SetCaption"); - _SDL_WM_SetIcon = (void (*)(void*, uint8_t*))GetProcAddress(realSDLlib,"SDL_WM_SetIcon"); - _SDL_mutexP = (int (*)(vPtr))GetProcAddress(realSDLlib,"SDL_mutexP"); - _SDL_mutexV = (int (*)(vPtr))GetProcAddress(realSDLlib,"SDL_mutexV"); - _SDL_strlcpy = (size_t (*)(char*, const char*, size_t))GetProcAddress(realSDLlib,"SDL_strlcpy"); - - // stuff for SDL_Image - _SDL_ClearError = (void (*)())GetProcAddress(realSDLlib,"SDL_ClearError"); - _SDL_Error = (void (*)(int))GetProcAddress(realSDLlib,"SDL_Error"); - _SDL_LoadFunction = (void*(*)(vPtr, const char*))GetProcAddress(realSDLlib,"SDL_LoadFunction"); - _SDL_LoadObject = (vPtr(*)(const char*))GetProcAddress(realSDLlib,"SDL_LoadObject"); - _SDL_ReadBE32 = (uint32_t (*)(void*))GetProcAddress(realSDLlib,"SDL_ReadBE32"); - _SDL_ReadLE16 = (uint16_t (*)(void*))GetProcAddress(realSDLlib,"SDL_ReadLE16"); - _SDL_ReadLE32 = (uint32_t (*)(void*))GetProcAddress(realSDLlib,"SDL_ReadLE32"); - _SDL_SetError = (void (*)(const char*, ...))GetProcAddress(realSDLlib,"SDL_SetError"); - _SDL_UnloadObject = (void (*)(vPtr))GetProcAddress(realSDLlib,"SDL_UnloadObject"); - _SDL_FillRect = (int (*)(void*,void*,uint32_t))GetProcAddress(realSDLlib,"SDL_FillRect"); - - // new in DF 0.31.04 - _SDL_CreateSemaphore = (void* (*)(uint32_t))GetProcAddress(realSDLlib,"SDL_CreateSemaphore"); - _SDL_CreateThread = (vPtr (*)(int (*fn)(void *), void *data))GetProcAddress(realSDLlib,"SDL_CreateThread"); - _SDL_Delay = (void (*)(uint32_t))GetProcAddress(realSDLlib,"SDL_Delay"); - _SDL_DestroySemaphore = (void (*)(void *))GetProcAddress(realSDLlib,"SDL_DestroySemaphore"); - _SDL_GetAppState = (uint8_t (*)(void))GetProcAddress(realSDLlib,"SDL_GetAppState"); - _SDL_GetMouseState = (uint8_t (*)(int *, int *))GetProcAddress(realSDLlib,"SDL_GetMouseState"); - _SDL_InitSubSystem = (int (*)(uint32_t))GetProcAddress(realSDLlib,"SDL_InitSubSystem"); - _SDL_SemPost = (int (*)(void *))GetProcAddress(realSDLlib,"SDL_SemPost"); - _SDL_SemTryWait = (int (*)(void *))GetProcAddress(realSDLlib,"SDL_SemTryWait"); - _SDL_SemWait = (int (*)(void *))GetProcAddress(realSDLlib,"SDL_SemWait"); - _SDL_ThreadID = (uint32_t (*)(void))GetProcAddress(realSDLlib,"SDL_ThreadID"); - - // new in DF 0.43.05 - _SDL_getenv = (char* (*)(const char*))GetProcAddress(realSDLlib,"SDL_getenv"); - _SDL_strlcat = (size_t (*)(char*, const char*, size_t))GetProcAddress(realSDLlib,"SDL_strlcat"); - - // new in DF v50.01 - _SDL_ListModes = (void *(*)(void*, uint32_t))GetProcAddress(realSDLlib,"SDL_ListModes"); - - _SDL_EnableUNICODE(1); - - fprintf(stderr,"Initized HOOKS!\n"); -} - -void InitSDLPointers() -{ - std::call_once(inited, [](){ FirstCall(); }); -} diff --git a/library/Hooks.cpp b/library/Hooks.cpp index 36af2617c..b4a865c18 100644 --- a/library/Hooks.cpp +++ b/library/Hooks.cpp @@ -26,8 +26,11 @@ DFhackCExport void dfhooks_prerender() { DFhackCExport bool dfhooks_sdl_event(SDL::Event* event) { return DFHack::Core::getInstance().DFH_SDL_Event(event); } + // called for each utf-8 char read from the ncurses input // key is positive for ncurses keys and negative for everything else +// if true is returned, then the event has been consumed and further processing +// shouldn't happen DFhackCExport bool dfhooks_ncurses_key(int key) { return DFHack::Core::getInstance().DFH_ncurses_key(key); } diff --git a/library/include/Core.h b/library/include/Core.h index 2e022d6ca..386769fcb 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -108,19 +108,6 @@ namespace DFHack // Better than tracking some weird variables all over the place. class DFHACK_EXPORT Core { -#ifdef _DARWIN - friend int ::DFH_SDL_NumJoysticks(void); - friend void ::DFH_SDL_Quit(void); - friend int ::DFH_SDL_PollEvent(SDL::Event *); - friend int ::DFH_SDL_Init(uint32_t flags); - friend int ::DFH_wgetch(WINDOW * w); -#else - friend int ::SDL_NumJoysticks(void); - friend void ::SDL_Quit(void); - friend int ::SDL_PollEvent(SDL::Event *); - friend int ::SDL_Init(uint32_t flags); - friend int ::wgetch(WINDOW * w); -#endif friend void ::dfhooks_init(); friend void ::dfhooks_shutdown(); friend void ::dfhooks_update(); @@ -207,7 +194,7 @@ namespace DFHack bool Init(); int Update (void); int Shutdown (void); - int DFH_SDL_Event(SDL::Event* event); + bool DFH_SDL_Event(SDL::Event* event); bool ncurses_wgetch(int in, int & out); bool DFH_ncurses_key(int key); diff --git a/plugins/title-folder.cpp b/plugins/title-folder.cpp index 4f93a6c7f..2659a094f 100644 --- a/plugins/title-folder.cpp +++ b/plugins/title-folder.cpp @@ -20,7 +20,7 @@ static std::string original_title; static DFLibrary *sdl_handle = NULL; static const std::vector sdl_libs { - "SDLreal.dll", + "SDL.dll", "SDL.framework/Versions/A/SDL", "SDL.framework/SDL", "libSDL-1.2.so.0" From 03d42634f9621c96a8a3dbc03d9af0d170f89caf Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 10:11:40 -0800 Subject: [PATCH 1034/2222] remove unused windows package dir --- package/windows/sdl license.txt | 502 ------------------------------- package/windows/win32/.gitignore | 1 - package/windows/win64/.gitignore | 1 - 3 files changed, 504 deletions(-) delete mode 100644 package/windows/sdl license.txt delete mode 100644 package/windows/win32/.gitignore delete mode 100644 package/windows/win64/.gitignore diff --git a/package/windows/sdl license.txt b/package/windows/sdl license.txt deleted file mode 100644 index e5ab03e12..000000000 --- a/package/windows/sdl license.txt +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/package/windows/win32/.gitignore b/package/windows/win32/.gitignore deleted file mode 100644 index 6a7461313..000000000 --- a/package/windows/win32/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.dll diff --git a/package/windows/win64/.gitignore b/package/windows/win64/.gitignore deleted file mode 100644 index 6a7461313..000000000 --- a/package/windows/win64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.dll From b2f32be117aa09672447c9983134c43227081333 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 10:30:10 -0800 Subject: [PATCH 1035/2222] simplify installation instructions now that we don't overwrite SDL.dll on Windows --- docs/Installing.rst | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/docs/Installing.rst b/docs/Installing.rst index 15a6276bf..8a04b7cb9 100644 --- a/docs/Installing.rst +++ b/docs/Installing.rst @@ -80,28 +80,16 @@ among other things. Some redistributions of Dwarf Fortress may place DF in another folder, so ensure that the ``hack`` folder ends up next to the ``data`` folder, and you'll be fine. -.. note:: - - On Windows, installing DFHack will overwrite ``SDL.dll``. This is - intentional and necessary for DFHack to work, so be sure to choose to - overwrite ``SDL.dll`` if prompted. (If you are not prompted, you may be - installing DFHack in the wrong place.) - Uninstalling DFHack =================== -Manually uninstalling DFHack essentially involves reversing what you did to -install. On Windows, replace ``SDL.dll`` with ``SDLreal.dll`` first. Then, you -can remove any files that were part of the DFHack archive. DFHack does not -currently maintain a list of these files, so if you want to completely remove -them, you should consult the DFHack archive that you installed for a full list. -Generally, any files left behind should not negatively affect DF. +Just renaming or removing the ``dfhooks`` library file is enough to disable +DFHack. If you would like to remove all DFHack files, consult the DFHack install +archive to see the list of files and remove the corresponding files in the Dwarf +Fortress folder. Any DFHack files left behind will not negatively affect DF. On Steam, uninstalling DFHack will cleanly remove everything that was installed -with DFHack, **including** the ``SDL.dll`` file, which will render Dwarf -Fortress inoperative. In your Steam client, open the properties window for -Dwarf Fortress, select "Local Files", and click on "Verify integrity of game -files...". This will get Dwarf Fortress working properly again. +with DFHack, so there is nothing else for you to do. Note that Steam will leave behind the ``dfhack-config`` folder, which contains all your personal DFHack-related settings and data. If you keep this folder, @@ -117,4 +105,4 @@ ensures that files that don't exist in the latest version are properly removed and don't affect your new installation. Then, extract the DFHack release archive into your Dwarf Fortress folder, -overwriting any remaining top-level files (including SDL.dll). +overwriting any remaining top-level files. From 9f605d639696fc94f8ce939b3cff4d51f2c6c9c5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 4 Jan 2023 19:55:46 -0800 Subject: [PATCH 1036/2222] call DF's stubs for SDL semaphore functions --- library/include/df/custom/enabler.methods.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/include/df/custom/enabler.methods.inc b/library/include/df/custom/enabler.methods.inc index 26a23a4dd..b06af2345 100644 --- a/library/include/df/custom/enabler.methods.inc +++ b/library/include/df/custom/enabler.methods.inc @@ -1,6 +1,6 @@ void zoom_display(df::zoom_commands command) { - SDL_SemWait(async_zoom.sem); + DFHack::DFSDL::DFSDL_SemWait(async_zoom.sem); async_zoom.queue.push_back(command); - SDL_SemPost(async_zoom.sem); - SDL_SemPost(async_zoom.sem_fill); + DFHack::DFSDL::DFSDL_SemPost(async_zoom.sem); + DFHack::DFSDL::DFSDL_SemPost(async_zoom.sem_fill); } From 275513319cd5b80a99c5ea63a9d4e7bfa5bcd8d1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 12 Apr 2023 23:14:03 -0700 Subject: [PATCH 1037/2222] remove last reference to SDLReal --- library/modules/DFSDL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/DFSDL.cpp b/library/modules/DFSDL.cpp index 6a3e6af2f..b95b6302a 100644 --- a/library/modules/DFSDL.cpp +++ b/library/modules/DFSDL.cpp @@ -14,7 +14,7 @@ using namespace DFHack; static DFLibrary *g_sdl_handle = nullptr; static DFLibrary *g_sdl_image_handle = nullptr; static const std::vector SDL_LIBS { - "SDLreal.dll", // TODO: change to SDL.dll once we move to dfhooks + "SDL.dll", "SDL.framework/Versions/A/SDL", "SDL.framework/SDL", "libSDL-1.2.so.0" From 6c577fbe26cdbe0fdc2136eb66472359f5812271 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 13 Apr 2023 00:27:20 -0700 Subject: [PATCH 1038/2222] don't initialize Core from the main thread that's too early --- library/Hooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Hooks.cpp b/library/Hooks.cpp index b4a865c18..13f8bd908 100644 --- a/library/Hooks.cpp +++ b/library/Hooks.cpp @@ -3,7 +3,7 @@ // called before main event loop starts DFhackCExport void dfhooks_init() { - DFHack::Core::getInstance().Init(); + // TODO: initialize things we need to do while still in the main thread } // called after main event loops exits From 48c3a2c98738dcf3f8b16c8d50de6904db96d970 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 13 Apr 2023 00:40:10 -0700 Subject: [PATCH 1039/2222] document which thread each call is coming from --- library/Hooks.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/library/Hooks.cpp b/library/Hooks.cpp index 13f8bd908..c241e4875 100644 --- a/library/Hooks.cpp +++ b/library/Hooks.cpp @@ -1,33 +1,35 @@ #include "Core.h" #include "Export.h" -// called before main event loop starts +// called from the main thread before the simulation thread is started +// and the main event loop is initiated DFhackCExport void dfhooks_init() { // TODO: initialize things we need to do while still in the main thread } -// called after main event loops exits +// called from the main thread after the main event loops exits DFhackCExport void dfhooks_shutdown() { DFHack::Core::getInstance().Shutdown(); } -// called in the main event loop +// called from the simulation thread in the main event loop DFhackCExport void dfhooks_update() { DFHack::Core::getInstance().Update(); } -// called just before adding the macro recording/playback overlay +// called from the simulation thread just before adding the macro +// recording/playback overlay DFhackCExport void dfhooks_prerender() { // TODO: render overlay widgets that are not attached to a viewscreen } -// called for each SDL event, if true is returned, then the event has been -// consumed and further processing shouldn't happen +// called from the main thread for each SDL event. if true is returned, then +// the event has been consumed and further processing shouldn't happen DFhackCExport bool dfhooks_sdl_event(SDL::Event* event) { return DFHack::Core::getInstance().DFH_SDL_Event(event); } -// called for each utf-8 char read from the ncurses input +// called from the main thread for each utf-8 char read from the ncurses input // key is positive for ncurses keys and negative for everything else // if true is returned, then the event has been consumed and further processing // shouldn't happen From f13548a47d53eaa45e66da5a932be5c945815784 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 13 Apr 2023 01:16:22 -0700 Subject: [PATCH 1040/2222] continue to ship SDL.dll until DF moves to SDL2 --- CMakeLists.txt | 16 + library/CMakeLists.txt | 4 + package/windows/sdl license.txt | 502 +++++++++++++++++++++++++++++++ package/windows/win32/.gitignore | 1 + package/windows/win64/.gitignore | 1 + 5 files changed, 524 insertions(+) create mode 100644 package/windows/sdl license.txt create mode 100644 package/windows/win32/.gitignore create mode 100644 package/windows/win64/.gitignore diff --git a/CMakeLists.txt b/CMakeLists.txt index 02857f91d..086f054c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,6 +299,22 @@ if(WIN32) DESTINATION ${CMAKE_BINARY_DIR}/depends/) file(COPY ${ZLIB_DOWNLOAD_DIR}/zlib.lib DESTINATION ${CMAKE_BINARY_DIR}/depends/zlib/lib/) + + # Do the same for SDL.dll + # (DFHack doesn't require this at build time, so no need to move it to the build folder) + # TODO: remove SDL.dll from our distribution once DF moves to SDL2. we only + # continue to include it so we don't break Steam players on update by removing + # the SDL.dll that DF needs. + set(SDL_DOWNLOAD_DIR ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH}) + if(${DFHACK_BUILD_ARCH} STREQUAL "64") + download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-SDL.dll" + ${SDL_DOWNLOAD_DIR}/SDL.dll + "1ae242c4b94cb03756a1288122a66faf") + else() + download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-SDL.dll" + ${SDL_DOWNLOAD_DIR}/SDL.dll + "5a09604daca6b2b5ce049d79af935d6a") + endif() endif() if(APPLE) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 418c9b371..0b9f48413 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -432,6 +432,10 @@ if(UNIX) install(TARGETS dfhooks LIBRARY DESTINATION . RUNTIME DESTINATION .) +else() + # On windows, copy SDL.dll so DF can still run. + install(PROGRAMS ${dfhack_SOURCE_DIR}/package/windows/win${DFHACK_BUILD_ARCH}/SDL.dll + DESTINATION ${DFHACK_LIBRARY_DESTINATION}) endif() # install the main lib diff --git a/package/windows/sdl license.txt b/package/windows/sdl license.txt new file mode 100644 index 000000000..e5ab03e12 --- /dev/null +++ b/package/windows/sdl license.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/package/windows/win32/.gitignore b/package/windows/win32/.gitignore new file mode 100644 index 000000000..6a7461313 --- /dev/null +++ b/package/windows/win32/.gitignore @@ -0,0 +1 @@ +*.dll diff --git a/package/windows/win64/.gitignore b/package/windows/win64/.gitignore new file mode 100644 index 000000000..6a7461313 --- /dev/null +++ b/package/windows/win64/.gitignore @@ -0,0 +1 @@ +*.dll From b9d95c5a0bbc68846952c54ed91959ac55835b1f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 13 Apr 2023 18:53:11 -0700 Subject: [PATCH 1041/2222] bump version to 50.08-rc1 --- CMakeLists.txt | 6 +++--- library/xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9f323640..bb5c428a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,9 +191,9 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.07") -set(DFHACK_RELEASE "r1") -set(DFHACK_PRERELEASE FALSE) +set(DF_VERSION "50.08") +set(DFHACK_RELEASE "rc1") +set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/library/xml b/library/xml index 43a89a268..e825025d3 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 43a89a268b825fc05457678b19e551bf632dcd19 +Subproject commit e825025d399d936548c77578d4a40eb23183af0c From 52b869d908aa55410db0574610e64f69bb5502b6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 14 Apr 2023 01:22:12 -0700 Subject: [PATCH 1042/2222] don't read cur_savegame.save_dir when invalid --- library/Core.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index b1fe2d389..e9463ec51 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2083,7 +2083,9 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve if (!df::global::world) return; - std::string rawFolder = "save/" + (df::global::world->cur_savegame.save_dir) + "/init"; + + std::string rawFolder = !isWorldLoaded() ? "" : + "save/" + (df::global::world->cur_savegame.save_dir) + "/init"; auto i = table.find(event); if ( i != table.end() ) { From 5ace09fad0addc0fa02ff26bd27affa573fd39b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 14 Apr 2023 01:38:38 -0700 Subject: [PATCH 1043/2222] use the World module for the data read --- library/Core.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index e9463ec51..c3c7bb7f2 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -2084,8 +2084,7 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve if (!df::global::world) return; - std::string rawFolder = !isWorldLoaded() ? "" : - "save/" + (df::global::world->cur_savegame.save_dir) + "/init"; + std::string rawFolder = !isWorldLoaded() ? "" : "save/" + World::ReadWorldFolder() + "/init"; auto i = table.find(event); if ( i != table.end() ) { From ce6adabbdcf50c4c0a1d071c7f874d697eaeb578 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 14 Apr 2023 05:52:24 -0500 Subject: [PATCH 1044/2222] sync library/xml to 50.08-beta --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index e825025d3..055f9b4ce 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e825025d399d936548c77578d4a40eb23183af0c +Subproject commit 055f9b4cec3bbec8e562f4774754242a14026bd2 From 051baa4e6e2ad27d0a514bbf5da68b31f25ebb96 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 15 Apr 2023 07:12:55 +0000 Subject: [PATCH 1045/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index e825025d3..43059670e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e825025d399d936548c77578d4a40eb23183af0c +Subproject commit 43059670e7d3338d9a164bc23d0c41994187de9c From ef380e9e1c7b4e019b4e0b31c65cf16f234043c7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 15 Apr 2023 15:49:36 -0700 Subject: [PATCH 1046/2222] don't reset planner panel minimized state --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 351e13d76..975caf91f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `buildingplan`: minimized planner panel stays minimized until you change it again ## Documentation diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 3e06ac79d..8f12c695f 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -729,7 +729,6 @@ function PlannerOverlay:onInput(keys) return true end self.selected = 1 - self.minimized = false self.subviews.hollow:setOption(false) self:reset() reset_counts_flag = true From af1d886a27a6f3c2cec8d3de1492c75d528aa74e Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 16 Apr 2023 03:28:37 +0000 Subject: [PATCH 1047/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index ec1a69788..7e7a1034c 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ec1a69788fd6329008672523b622fd8b390fea73 +Subproject commit 7e7a1034c502909fdbb73398865958230da67c69 From a5e2d79e39f0fbaad03e70d16eae7e95be146bbe Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 15 Apr 2023 21:05:05 -0700 Subject: [PATCH 1048/2222] remove stubs for deprecated travis scripts --- travis/authors-rst.py | 14 -------------- travis/buildmaster-rebuild-pr.py | 14 -------------- travis/check-rpc.py | 14 -------------- travis/download-df.sh | 9 --------- travis/get-df-version.sh | 9 --------- travis/lint.py | 14 -------------- travis/run-tests.py | 14 -------------- travis/script-docs.py | 14 -------------- travis/script-syntax.py | 14 -------------- 9 files changed, 116 deletions(-) delete mode 100755 travis/authors-rst.py delete mode 100755 travis/buildmaster-rebuild-pr.py delete mode 100755 travis/check-rpc.py delete mode 100755 travis/download-df.sh delete mode 100755 travis/get-df-version.sh delete mode 100755 travis/lint.py delete mode 100755 travis/run-tests.py delete mode 100755 travis/script-docs.py delete mode 100755 travis/script-syntax.py diff --git a/travis/authors-rst.py b/travis/authors-rst.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/authors-rst.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) diff --git a/travis/buildmaster-rebuild-pr.py b/travis/buildmaster-rebuild-pr.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/buildmaster-rebuild-pr.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) diff --git a/travis/check-rpc.py b/travis/check-rpc.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/check-rpc.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) diff --git a/travis/download-df.sh b/travis/download-df.sh deleted file mode 100755 index aec2d6d99..000000000 --- a/travis/download-df.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -script_name="$(basename "$0")" -new_script_path="$(dirname "$0")/../ci/${script_name}" - -printf >&2 "\nNote: travis/%s is deprecated. Use ci/%s instead.\n\n" "${script_name}" "${script_name}" - -"${new_script_path}" "$@" -exit $? diff --git a/travis/get-df-version.sh b/travis/get-df-version.sh deleted file mode 100755 index aec2d6d99..000000000 --- a/travis/get-df-version.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -script_name="$(basename "$0")" -new_script_path="$(dirname "$0")/../ci/${script_name}" - -printf >&2 "\nNote: travis/%s is deprecated. Use ci/%s instead.\n\n" "${script_name}" "${script_name}" - -"${new_script_path}" "$@" -exit $? diff --git a/travis/lint.py b/travis/lint.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/lint.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) diff --git a/travis/run-tests.py b/travis/run-tests.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/run-tests.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) diff --git a/travis/script-docs.py b/travis/script-docs.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/script-docs.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) diff --git a/travis/script-syntax.py b/travis/script-syntax.py deleted file mode 100755 index cf52385b5..000000000 --- a/travis/script-syntax.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import sys - -script_name = os.path.basename(__file__) -new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name) - -sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name)) -sys.stderr.flush() - -p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:]) -sys.exit(p.returncode) From c596df2bc57585993e7544908a3c1c8643e5d8dc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 15 Apr 2023 21:30:51 -0700 Subject: [PATCH 1049/2222] update new authors from 01 Feb 2023 - 15 Apr 2023 --- docs/about/Authors.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index 75b9eb4f7..020ed5e76 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -45,6 +45,7 @@ cjhammel cjhammel Clayton Hughes Clément Vuchener cvuchener Corey CoreyJ87 +Cubittus Cubittus daedsidog daedsidog Dan Amlund danamlund Daniel Brooks db48x @@ -133,6 +134,7 @@ Milo Christiansen milochristiansen MithrilTuxedo MithrilTuxedo mizipzor mizipzor moversti moversti +mrrho mrrho Murad Beybalaev Erquint Myk Taylor myk002 napagokc napagokc @@ -158,6 +160,7 @@ Petr Mrázek peterix Pfhreak Pfhreak Pierre Lulé plule Pierre-David Bélanger pierredavidbelanger +PopnROFL PopnROFL potato ppaawwll ppaawwll ðŸ‡ðŸ‡ðŸ‡ðŸ‡ Priit Laes plaes @@ -201,6 +204,7 @@ SeerSkye SeerSkye seishuuu seishuuu Seth Woodworth sethwoodworth Shim Panze Shim-Panze +silverflyone silverflyone simon Simon Jackson sizeak Simon Lees simotek @@ -235,6 +239,7 @@ ViTuRaS ViTuRaS Vjek vjek Warmist warmist Wes Malone wesQ3 +Will H TSM-EVO Will Rogers wjrogers WoosterUK WoosterUK XianMaeve XianMaeve From 67f50eafb0cee72a96b2a890023b4859232c90ed Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 15 Apr 2023 22:26:38 -0700 Subject: [PATCH 1050/2222] add alias and keybinding for toggling the keyboard cursor --- data/init/dfhack.keybindings.init | 3 +++ data/init/dfhack.tools.init | 1 + docs/changelog.txt | 1 + 3 files changed, 5 insertions(+) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index 8ce0c3696..0fc596215 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -49,6 +49,9 @@ keybinding add Ctrl-H@dwarfmode autodump-destroy-here # apply blueprints to the map keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort +# toggle keyboard cursor +keybinding add Alt-K@dwarfmode toggle-kbd-cursor + # show information collected by dwarfmonitor #keybinding add Alt-M@dwarfmode/Default "dwarfmonitor prefs" #keybinding add Ctrl-F@dwarfmode/Default "dwarfmonitor stats" diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index aaf0cf277..8fd815559 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -144,3 +144,4 @@ enable \ alias add autounsuspend suspendmanager alias add gui/dig gui/design +alias add toggle-kbd-cursor lua "local flags4 = df.global.d_init.flags4 if flags4.KEYBOARD_CURSOR then flags4.KEYBOARD_CURSOR = false else local guidm = require('gui.dwarfmode') guidm.setCursorPos(guidm.Viewport.get():getCenter()) flags4.KEYBOARD_CURSOR = true end" diff --git a/docs/changelog.txt b/docs/changelog.txt index 351e13d76..3806dd125 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) ## Documentation From 86845c5bbff2b58bd5b12f49d7184a4bafd5c36c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 16 Apr 2023 09:50:57 -0700 Subject: [PATCH 1051/2222] update author names as per feedback --- docs/about/Authors.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index 020ed5e76..cf74c6412 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -12,6 +12,7 @@ Name Github Other 8Z 8Z Abel abstern acwatkins acwatkins +Alex Blamey Cubittus Alexander Collins gearsix Alexander Gavrilov angavrilov ag Amber Brown hawkowl @@ -45,7 +46,6 @@ cjhammel cjhammel Clayton Hughes Clément Vuchener cvuchener Corey CoreyJ87 -Cubittus Cubittus daedsidog daedsidog Dan Amlund danamlund Daniel Brooks db48x @@ -204,7 +204,7 @@ SeerSkye SeerSkye seishuuu seishuuu Seth Woodworth sethwoodworth Shim Panze Shim-Panze -silverflyone silverflyone +Silver silverflyone simon Simon Jackson sizeak Simon Lees simotek From 1ec6d90e463cdb270f6f4828064ff5ce4ddb9b6a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 17 Apr 2023 01:06:59 +0000 Subject: [PATCH 1052/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 7e7a1034c..726e5633f 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 7e7a1034c502909fdbb73398865958230da67c69 +Subproject commit 726e5633fd2aa1e0872a3f1553d8823505fca10e From 2dbfa37f54d74168b6f880d68c43617dadf13dd3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 16 Apr 2023 22:32:50 -0700 Subject: [PATCH 1053/2222] set version number for feedback release --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb5c428a6..239892e76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,8 +191,8 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.08") -set(DFHACK_RELEASE "rc1") +set(DF_VERSION "50.07") +set(DFHACK_RELEASE "r2rc1") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From d1d521fbdb07eb72e57d2918b48d6c7cfd9a37b3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 16 Apr 2023 22:34:29 -0700 Subject: [PATCH 1054/2222] infrastructure for hiding the terminal console on startup --- docs/changelog.txt | 1 + library/Core.cpp | 9 +++++++++ library/lua/dfhack.lua | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index f7a3102cc..3bb16251c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: minimized planner panel stays minimized until you change it again - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) +- `gui/control-panel`: add option for hiding the terminal console by default ## Documentation diff --git a/library/Core.cpp b/library/Core.cpp index c3c7bb7f2..4f06c3d62 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1314,6 +1314,15 @@ static void run_dfhack_init(color_ostream &out, Core *core) // load user overrides std::vector prefixes(1, "dfhack"); loadScriptFiles(core, out, prefixes, CONFIG_PATH + "init"); + + // if the option is set, hide the terminal + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(out, L, "dfhack", "getHideConsoleOnStartup", 0, 1, + Lua::DEFAULT_LUA_LAMBDA, [&](lua_State* L) { + if (lua_toboolean(L, -1)) + core->getConsole().hide(); + }, false); } // Load dfhack.init in a dedicated thread (non-interactive console mode) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index aa33b62d6..bc543fd10 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -51,6 +51,13 @@ if dfhack.is_core_context then SC_UNPAUSED = 8 end +-- User-changeable options + +dfhack.HIDE_CONSOLE_ON_STARTUP = true +function dfhack.getHideConsoleOnStartup() + return dfhack.HIDE_CONSOLE_ON_STARTUP +end + -- Error handling safecall = dfhack.safecall From 16fbea4c1f2bd1e8eac9cbcfc6757eb659be1612 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 17 Apr 2023 05:56:19 +0000 Subject: [PATCH 1055/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 726e5633f..5fe9e423b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 726e5633fd2aa1e0872a3f1553d8823505fca10e +Subproject commit 5fe9e423bbd5cdb7c6e7e8f1051e3aa8d00c853a From a7aded65e44d95c592eb6713e2a1b9c94028c86d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 17 Apr 2023 01:04:03 -0700 Subject: [PATCH 1056/2222] match new gems category --- library/modules/Materials.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index d73b59922..7a1ef249f 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -439,6 +439,7 @@ bool MaterialInfo::matches(const df::dfhack_material_category &cat) const return true; if (cat.bits.milk && linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0) return true; + TEST(gem, IS_GEM); return false; } From 77c2458900b9cc6d04b7cd9cdfaaaad4a0f7a433 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 17 Apr 2023 01:05:25 -0700 Subject: [PATCH 1057/2222] filter by gems (for windows) --- plugins/buildingplan/buildingplan.cpp | 9 +++++++++ plugins/lua/buildingplan.lua | 2 ++ plugins/lua/buildingplan/filterselection.lua | 1 + 3 files changed, 12 insertions(+) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 1d9f58416..e2f964a44 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -150,6 +150,7 @@ static const df::dfhack_material_category stone_cat(df::dfhack_material_category static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood); static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal); static const df::dfhack_material_category glass_cat(df::dfhack_material_category::mask_glass); +static const df::dfhack_material_category gem_cat(df::dfhack_material_category::mask_gem); static const df::dfhack_material_category clay_cat(df::dfhack_material_category::mask_clay); static const df::dfhack_material_category cloth_cat(df::dfhack_material_category::mask_cloth); static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); @@ -169,6 +170,9 @@ static void cache_matched(int16_t type, int32_t index) { } else if (mi.matches(glass_cat)) { DEBUG(status).print("cached glass material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "glass")); + } else if (mi.matches(gem_cat)) { + DEBUG(status).print("cached gem material: %s (%d, %d)\n", mi.toString().c_str(), type, index); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "gem")); } else if (mi.matches(clay_cat)) { DEBUG(status).print("cached clay material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "clay")); @@ -800,6 +804,8 @@ static int setMaterialMaskFilter(lua_State *L) { mask |= metal_cat.whole; else if (cat == "glass") mask |= glass_cat.whole; + else if (cat == "gem") + mask |= gem_cat.whole; else if (cat == "clay") mask |= clay_cat.whole; else if (cat == "cloth") @@ -850,6 +856,7 @@ static int getMaterialMaskFilter(lua_State *L) { ret.emplace("wood", !bits || bits & wood_cat.whole); ret.emplace("metal", !bits || bits & metal_cat.whole); ret.emplace("glass", !bits || bits & glass_cat.whole); + ret.emplace("gem", !bits || bits & gem_cat.whole); ret.emplace("clay", !bits || bits & clay_cat.whole); ret.emplace("cloth", !bits || bits & cloth_cat.whole); ret.emplace("silk", !bits || bits & silk_cat.whole); @@ -897,6 +904,8 @@ static int setMaterialFilter(lua_State *L) { mask.whole |= metal_cat.whole; else if (mat.matches(glass_cat)) mask.whole |= glass_cat.whole; + else if (mat.matches(gem_cat)) + mask.whole |= gem_cat.whole; else if (mat.matches(clay_cat)) mask.whole |= clay_cat.whole; else if (mat.matches(cloth_cat)) diff --git a/plugins/lua/buildingplan.lua b/plugins/lua/buildingplan.lua index 2de467f7c..d64317eb0 100644 --- a/plugins/lua/buildingplan.lua +++ b/plugins/lua/buildingplan.lua @@ -111,6 +111,8 @@ function get_desc(filter) desc = 'Ballista part' elseif desc == 'Catapultpart' then desc = 'Catapult part' + elseif desc == 'Smallgem' then + desc = 'Small, cut gem' end return desc diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index 498c89c1e..968ad88d9 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -452,6 +452,7 @@ function QualityAndMaterialsPage:refresh() make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats), make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats), make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats), + make_cat_choice('Gem', 'gem', 'CUSTOM_SHIFT_E', cats), make_cat_choice('Clay', 'clay', 'CUSTOM_SHIFT_C', cats), make_cat_choice('Cloth', 'cloth', 'CUSTOM_SHIFT_L', cats), make_cat_choice('Silk', 'silk', 'CUSTOM_SHIFT_K', cats), From b26d6a90ca9edd1b8b19185cea285eed4c03803b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 17 Apr 2023 01:06:06 -0700 Subject: [PATCH 1058/2222] update structures head --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 43059670e..1413d3c67 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 43059670e7d3338d9a164bc23d0c41994187de9c +Subproject commit 1413d3c67c5bd85e6afbef45d8b1ade6b8389ede From eb9e40a24d6023470725959f46f2d5fe3c83daf8 Mon Sep 17 00:00:00 2001 From: John Reid Date: Mon, 17 Apr 2023 10:43:14 +0200 Subject: [PATCH 1059/2222] Fix typo in Quickstart.rst Fixes a typo in Quickstart.rst: mangager -> manager --- docs/Quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Quickstart.rst b/docs/Quickstart.rst index 065ba6ecc..f4a022a9c 100644 --- a/docs/Quickstart.rst +++ b/docs/Quickstart.rst @@ -162,7 +162,7 @@ You can run them all from the launcher. First, let's import some useful manager orders to keep your fort stocked with basic necessities. Run ``orders import library/basic``. If you go to your -mangager orders screen, you can see all the orders that have been created for you. +manager orders screen, you can see all the orders that have been created for you. Note that you could have imported the orders directly from this screen as well, using the DFHack `overlay` widget at the bottom of the manager orders panel. From e9f6695aceb9918ff56b3c8596498cd44faee033 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 17 Apr 2023 09:39:15 -0700 Subject: [PATCH 1060/2222] infrastructure for hiding armok tools --- docs/Tags.rst | 2 +- docs/changelog.txt | 3 ++- library/lua/dfhack.lua | 5 +++++ library/lua/helpdb.lua | 17 +++++++++++++++-- plugins/hotkeys.cpp | 39 +++++++++++++++++++++++++++++++-------- plugins/lua/hotkeys.lua | 13 +++++++++++++ 6 files changed, 67 insertions(+), 12 deletions(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index 5ff13d632..c7b284d07 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -21,7 +21,7 @@ for the tag assignment spreadsheet. "why" tags ---------- -- `armok `: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. +- `armok `: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. Players that do not wish to see these tools listed in DFHack command lists can hide them in the ``Preferences`` tab of `gui/control-panel`. - `auto `: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress. - `bugfix `: Tools that fix specific bugs, either permanently or on-demand. - `design `: Tools that help you design your fort. diff --git a/docs/changelog.txt b/docs/changelog.txt index 3bb16251c..553fb3362 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,7 +40,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: minimized planner panel stays minimized until you change it again - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) -- `gui/control-panel`: add option for hiding the terminal console by default +- `gui/control-panel`: add preference option for hiding the terminal console on startup +- `gui/control-panel`: add preference option for hiding "armok" tools in command lists ## Documentation diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index bc543fd10..78d978147 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -58,6 +58,11 @@ function dfhack.getHideConsoleOnStartup() return dfhack.HIDE_CONSOLE_ON_STARTUP end +dfhack.HIDE_ARMOK_TOOLS = false +function dfhack.getHideArmokTools() + return dfhack.HIDE_ARMOK_TOOLS +end + -- Error handling safecall = dfhack.safecall diff --git a/library/lua/helpdb.lua b/library/lua/helpdb.lua index a7ac6226f..4ce078a22 100644 --- a/library/lua/helpdb.lua +++ b/library/lua/helpdb.lua @@ -788,7 +788,11 @@ function ls(filter_str, skip_tags, show_dev_commands, exclude_strs) table.insert(excludes, {str=argparse.stringList(exclude_strs)}) end if not show_dev_commands then - table.insert(excludes, {tag='dev'}) + local dev_tags = {'dev', 'unavailable'} + if dfhack.getHideArmokTools() then + table.insert(dev_tags, 'armok') + end + table.insert(excludes, {tag=dev_tags}) end list_entries(skip_tags, include, excludes) end @@ -813,7 +817,16 @@ function tags(tag) local skip_tags = true local include = {entry_type={ENTRY_TYPES.COMMAND}, tag=tag} - list_entries(skip_tags, include) + + local excludes = {tag={}} + if tag ~= 'unavailable' then + table.insert(excludes.tag, 'unavailable') + end + if tag ~= 'armok' and dfhack.getHideArmokTools() then + table.insert(excludes.tag, 'armok') + end + + list_entries(skip_tags, include, excludes) end return _ENV diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 788a4c02b..136ad7a9d 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -49,7 +49,22 @@ static int cleanupHotkeys(lua_State *) { return 0; } -static void add_binding_if_valid(const string &sym, const string &cmdline, df::viewscreen *screen, bool filtermenu) { +static bool should_hide_armok(color_ostream &out, const string &cmdline) { + bool should_hide = false; + + auto L = Lua::Core::State; + Lua::StackUnwinder top(L); + Lua::CallLuaModuleFunction(out, L, "plugins.hotkeys", "should_hide_armok", 1, 1, + [&](lua_State *L){ + Lua::Push(L, cmdline); + }, [&](lua_State *L){ + should_hide = lua_toboolean(L, -1); + }); + + return should_hide; +} + +static void add_binding_if_valid(color_ostream &out, const string &sym, const string &cmdline, df::viewscreen *screen, bool filtermenu) { if (!can_invoke(cmdline, screen)) return; @@ -59,6 +74,11 @@ static void add_binding_if_valid(const string &sym, const string &cmdline, df::v return; } + if (should_hide_armok(out, cmdline)) { + DEBUG(log).print("filtering out armok keybinding\n"); + return; + } + current_bindings[sym] = cmdline; sorted_keys.push_back(sym); string keyspec = sym + "@" + MENU_SCREEN_FOCUS_STRING; @@ -67,7 +87,7 @@ static void add_binding_if_valid(const string &sym, const string &cmdline, df::v Core::getInstance().AddKeyBinding(keyspec, binding); } -static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { +static void find_active_keybindings(color_ostream &out, df::viewscreen *screen, bool filtermenu) { DEBUG(log).print("scanning for active keybindings\n"); if (valid) cleanupHotkeys(NULL); @@ -103,7 +123,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { string::size_type colon_pos = invoke_cmd->find(":"); // colons at location 0 are for commands like ":lua" if (colon_pos == string::npos || colon_pos == 0) { - add_binding_if_valid(sym, *invoke_cmd, screen, filtermenu); + add_binding_if_valid(out, sym, *invoke_cmd, screen, filtermenu); } else { vector tokens; @@ -111,7 +131,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { string focus = tokens[0].substr(1); if(Gui::matchFocusString(focus)) { auto cmdline = trim(tokens[1]); - add_binding_if_valid(sym, cmdline, screen, filtermenu); + add_binding_if_valid(out, sym, cmdline, screen, filtermenu); } } } @@ -124,7 +144,10 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { } static int getHotkeys(lua_State *L) { - find_active_keybindings(Gui::getCurViewscreen(true), true); + color_ostream *out = Lua::GetOutput(L); + if (!out) + out = &Core::getInstance().getConsole(); + find_active_keybindings(*out, Gui::getCurViewscreen(true), true); Lua::PushVector(L, sorted_keys); Lua::Push(L, current_bindings); return 2; @@ -140,7 +163,7 @@ static void list(color_ostream &out) { DEBUG(log).print("listing active hotkeys\n"); bool was_valid = valid; if (!valid) - find_active_keybindings(Gui::getCurViewscreen(true), false); + find_active_keybindings(out, Gui::getCurViewscreen(true), false); out.print("Valid keybindings for the current focus:\n %s\n", join_strings("\n", Gui::getCurFocus(true)).c_str()); @@ -176,6 +199,8 @@ static command_result hotkeys_cmd(color_ostream &out, vector & paramete return Core::getInstance().runCommand(out, INVOKE_MENU_COMMAND ); } + CoreSuspender guard; + if (parameters[0] == "list") { list(out); return CR_OK; @@ -185,8 +210,6 @@ static command_result hotkeys_cmd(color_ostream &out, vector & paramete if (parameters.size() != 2 || parameters[0] != "invoke") return CR_WRONG_USAGE; - CoreSuspender guard; - int index = string_to_int(parameters[1], -1); if (index < 0) return CR_WRONG_USAGE; diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 8edb70073..ec5699fe4 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -5,6 +5,19 @@ local helpdb = require('helpdb') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') +local function get_command(cmdline) + local first_word = cmdline:trim():split(' +')[1] + if first_word:startswith(':') then first_word = first_word:sub(2) end + return first_word +end + +function should_hide_armok(cmdline) + local first_word = get_command(cmdline) + return dfhack.getHideArmokTools() and + helpdb.is_entry(first_word) and + helpdb.get_entry_tags(first_word).armok +end + -- ----------------- -- -- HotspotMenuWidget -- -- ----------------- -- From f6031e6a799fac2cd7d9b738e99d93d9eb5d2e7e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 17 Apr 2023 13:04:42 -0700 Subject: [PATCH 1061/2222] refactor existing logic to use new function --- plugins/lua/hotkeys.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index ec5699fe4..b162f0c08 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -12,10 +12,10 @@ local function get_command(cmdline) end function should_hide_armok(cmdline) - local first_word = get_command(cmdline) + local command = get_command(cmdline) return dfhack.getHideArmokTools() and - helpdb.is_entry(first_word) and - helpdb.get_entry_tags(first_word).armok + helpdb.is_entry(command) and + helpdb.get_entry_tags(command).armok end -- ----------------- -- @@ -245,10 +245,9 @@ end function Menu:onSelect(_, choice) if not choice or #self.subviews == 0 then return end - local first_word = choice.command:trim():split(' +')[1] - if first_word:startswith(':') then first_word = first_word:sub(2) end - self.subviews.help.text_to_wrap = helpdb.is_entry(first_word) and - helpdb.get_entry_short_help(first_word) or 'Command not found' + local command = get_command(choice.command) + self.subviews.help.text_to_wrap = helpdb.is_entry(command) and + helpdb.get_entry_short_help(command) or 'Command not found' self.subviews.help_panel:updateLayout() end From 3307427718814d7f6109828a9b39a95ce0b6f7aa Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 17 Apr 2023 13:29:58 -0700 Subject: [PATCH 1062/2222] update wording (thanks Ozzatron!) --- docs/Tags.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Tags.rst b/docs/Tags.rst index c7b284d07..ded15c4c2 100644 --- a/docs/Tags.rst +++ b/docs/Tags.rst @@ -21,7 +21,9 @@ for the tag assignment spreadsheet. "why" tags ---------- -- `armok `: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden. Players that do not wish to see these tools listed in DFHack command lists can hide them in the ``Preferences`` tab of `gui/control-panel`. +- `armok `: Tools which give the player god-like powers of any variety, such as control over game events, creating items from thin air, or viewing information the game intentionally keeps hidden. Players that do not wish to see these tools listed in DFHack command lists can hide them in the ``Preferences`` tab of `gui/control-panel`. + + - `auto `: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress. - `bugfix `: Tools that fix specific bugs, either permanently or on-demand. - `design `: Tools that help you design your fort. From c30c59c261e5c64a7573511e676a31d8d9867502 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 18 Apr 2023 07:13:59 +0000 Subject: [PATCH 1063/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 1413d3c67..ae268954d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 1413d3c67c5bd85e6afbef45d8b1ade6b8389ede +Subproject commit ae268954da3cde1c4e08f6d62273a17aae0d94da diff --git a/scripts b/scripts index 5fe9e423b..6fba9905a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5fe9e423bbd5cdb7c6e7e8f1051e3aa8d00c853a +Subproject commit 6fba9905a71f626ecc640fd3de988e147d16cb4e From 495c94127a47f660c5bc94ece7a080d1c3dcef68 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 18 Apr 2023 10:19:25 +0000 Subject: [PATCH 1064/2222] Auto-update submodules depends/xlsxio: dfhack --- depends/xlsxio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/xlsxio b/depends/xlsxio index 439fdbc25..0a9945266 160000 --- a/depends/xlsxio +++ b/depends/xlsxio @@ -1 +1 @@ -Subproject commit 439fdbc259c13f23a3122e68ba35ad5a13bcd97c +Subproject commit 0a994526622c2201756e386ef98b44b193e25f06 From ed87075cb8b3875936c9823c8802d20b06f61e63 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 05:40:51 -0500 Subject: [PATCH 1065/2222] cmake minimum version 3.21 forced by this being the lowest version that supports vs 2022 --- depends/clsocket | 2 +- depends/lua/CMakeLists.txt | 2 +- library/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/depends/clsocket b/depends/clsocket index 6ed8aa464..d5e17c601 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit 6ed8aa46462ea01a1122fc49422840a2facc9757 +Subproject commit d5e17c6012e7eefb0cbe3e130a56c24bd11f0094 diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index c3ff0c16f..efded915f 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -1,5 +1,5 @@ project(lua CXX) -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.21) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLUA_USE_APICHECK") diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index a3fcb8b6f..31cfb007d 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -1,5 +1,5 @@ project(dfapi) -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.21) # prevent CMake warnings about INTERFACE_LINK_LIBRARIES vs LINK_INTERFACE_LIBRARIES cmake_policy(SET CMP0022 NEW) From 649d72e658890246585e23f830b0142595c94e50 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 18 Apr 2023 08:28:00 -0700 Subject: [PATCH 1066/2222] add missed NO_LOGIC_SCREEN to the list adopt_region also kills the top viewscreen on transition --- library/lua/gui.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 64e203d3c..e425dbcf1 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -745,6 +745,7 @@ end local NO_LOGIC_SCREENS = { 'viewscreen_loadgamest', + 'viewscreen_adopt_regionst', 'viewscreen_export_regionst', 'viewscreen_choose_game_typest', 'viewscreen_worldst', From 250f05667b70e20a7e7c55fcfd8704477c0fe487 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 18 Apr 2023 15:50:59 -0700 Subject: [PATCH 1067/2222] add a warning when DT appears to be running --- docs/changelog.txt | 1 + plugins/lua/autolabor.lua | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 553fb3362..af0f208dc 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -42,6 +42,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) - `gui/control-panel`: add preference option for hiding the terminal console on startup - `gui/control-panel`: add preference option for hiding "armok" tools in command lists +- ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running). ## Documentation diff --git a/plugins/lua/autolabor.lua b/plugins/lua/autolabor.lua index 6ef4d7279..50f39b225 100644 --- a/plugins/lua/autolabor.lua +++ b/plugins/lua/autolabor.lua @@ -10,7 +10,7 @@ AutolaborOverlay.ATTRS{ default_enabled=true, viewscreens='dwarfmode/Info/LABOR', frame={w=29, h=5}, - frame_style=gui.MEDIUM_FRAME, + frame_style=gui.THIN_FRAME, frame_background=gui.CLEAR_PEN, } @@ -18,9 +18,20 @@ function AutolaborOverlay:init() self:addviews{ widgets.Label{ frame={t=0, l=0}, - text_pen=COLOR_RED, + text_pen=COLOR_LIGHTRED, + text='DFHack autolabor is active!', + visible=isEnabled, + }, + widgets.Label{ + frame={t=0, l=0}, + text_pen=COLOR_LIGHTRED, + text='Dwarf Therapist is active!', + visible=function() return not isEnabled() end, + }, + widgets.Label{ + frame={t=1, l=0}, + text_pen=COLOR_WHITE, text={ - 'DFHack autolabor is active!', NEWLINE, 'Any changes made on this', NEWLINE, 'screen will have no effect.' }, @@ -29,7 +40,7 @@ function AutolaborOverlay:init() end function AutolaborOverlay:render(dc) - if not isEnabled() then return false end + if df.global.game_extra.external_flag ~= 1 then return end AutolaborOverlay.super.render(self, dc) end From 150708f21db8a401a8e62f98f2c6e8ba8911d416 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 05:00:36 -0500 Subject: [PATCH 1068/2222] build lanchdf with steam sdk --- .gitignore | 3 +++ CMakeLists.txt | 13 ++++++++++++ package/windows/CMakeLists.txt | 10 +++++---- package/windows/{launchdf.c => launchdf.cpp} | 22 ++++++++++++++++++-- 4 files changed, 42 insertions(+), 6 deletions(-) rename package/windows/{launchdf.c => launchdf.cpp} (76%) diff --git a/.gitignore b/.gitignore index e24f11252..a386b260a 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ tags # external plugins /plugins/CMakeLists.custom.txt + +# steam api +depends/steam diff --git a/CMakeLists.txt b/CMakeLists.txt index 239892e76..899063d9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,6 +312,19 @@ if(WIN32) ${SDLREAL_DOWNLOAD_DIR}/SDLreal.dll "5a09604daca6b2b5ce049d79af935d6a") endif() + + # download Steam SDK + set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam/) + download_file("https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" + ${STEAMAPI_DIR}/steamworks_sdk_156.zip + "af5a579990dbe5ae4c1b0689260d001b") + file(ARCHIVE_EXTRACT + INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip + DESTINATION ${STEAMAPI_DIR}) + set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib") + set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam") + set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll") + endif() if(APPLE) diff --git a/package/windows/CMakeLists.txt b/package/windows/CMakeLists.txt index a5877f117..42151e663 100644 --- a/package/windows/CMakeLists.txt +++ b/package/windows/CMakeLists.txt @@ -1,7 +1,9 @@ project(package_windows) if(WIN32) - add_executable(launchdf WIN32 launchdf.c) - install(TARGETS launchdf - DESTINATION ${DFHACK_DATA_DESTINATION}) -endif() + include_directories(${STEAMAPI_SOURCE_DIR}) + link_libraries(${STEAMAPI_LIBRARY}) + add_executable(launchdf WIN32 launchdf.cpp) + install(TARGETS launchdf DESTINATION ${DFHACK_DATA_DESTINATION}) + install(FILES ${STEAMAPI_SHARED_LIBRARY} DESTINATION ${DFHACK_DATA_DESTINATION}) +endif() \ No newline at end of file diff --git a/package/windows/launchdf.c b/package/windows/launchdf.cpp similarity index 76% rename from package/windows/launchdf.c rename to package/windows/launchdf.cpp index 992bf6636..804163960 100644 --- a/package/windows/launchdf.c +++ b/package/windows/launchdf.cpp @@ -1,13 +1,17 @@ #include #include +#include "steam_api.h" + +const uint32 DFHACK_STEAM_APPID = 2346660; static BOOL is_running_on_wine() { - static const char *(CDECL *pwine_get_version)(void); + typedef const char* (CDECL wine_get_version)(void); + static wine_get_version* pwine_get_version; HMODULE hntdll = GetModuleHandle("ntdll.dll"); if(!hntdll) return FALSE; - pwine_get_version = (void *)GetProcAddress(hntdll, "wine_get_version"); + pwine_get_version = (wine_get_version*) GetProcAddress(hntdll, "wine_get_version"); return !!pwine_get_version; } @@ -61,6 +65,20 @@ static BOOL launch_direct() { } int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) { + + if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID)) // Replace with your App ID + { + return 1; + } + + if (!SteamAPI_Init()) + { + printf("Fatal Error - Steam must be running to play this game (SteamAPI_Init() failed).\n"); + return 1; + } + + return 0; + LPCWSTR err = is_running_on_wine() ? launch_via_steam_posix() : launch_via_steam_windows(); if (err && !launch_direct()) { From f336771284b152684cc4eb22cb6856330c649faf Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 08:21:54 -0500 Subject: [PATCH 1069/2222] launchdf rewrite this rewrites launchdf so that the dfhack launcher attempts to linger while df is running --- CMakeLists.txt | 6 +- package/windows/launchdf.cpp | 171 +++++++++++++++++++++++++++++++---- 2 files changed, 155 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 899063d9d..cf5586dfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,10 +314,10 @@ if(WIN32) endif() # download Steam SDK - set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam/) - download_file("https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" + set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam) + file(DOWNLOAD "https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" ${STEAMAPI_DIR}/steamworks_sdk_156.zip - "af5a579990dbe5ae4c1b0689260d001b") + EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b) file(ARCHIVE_EXTRACT INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip DESTINATION ${STEAMAPI_DIR}) diff --git a/package/windows/launchdf.cpp b/package/windows/launchdf.cpp index 804163960..19128ead1 100644 --- a/package/windows/launchdf.cpp +++ b/package/windows/launchdf.cpp @@ -1,8 +1,12 @@ #include #include +#include #include "steam_api.h" +#include + const uint32 DFHACK_STEAM_APPID = 2346660; +const uint32 DF_STEAM_APPID = 975370; static BOOL is_running_on_wine() { typedef const char* (CDECL wine_get_version)(void); @@ -32,7 +36,7 @@ static LPCWSTR launch_via_steam_windows() { si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - WCHAR steamPath[1024]; + WCHAR steamPath[1024] = L""; DWORD datasize = 1024; LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", @@ -43,16 +47,24 @@ static LPCWSTR launch_via_steam_windows() { WCHAR commandLine[1024] = L"steam.exe -applaunch 975370"; - if (CreateProcessW(steamPath, commandLine, - NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0) - return L"Could not launch Dwarf Fortress"; + BOOL res = CreateProcessW(steamPath, commandLine, + NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + + if (res) + { + WaitForSingleObject(pi.hProcess, INFINITE); - return NULL; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return NULL; + } + else + { + return L"Could not launch Dwarf Fortress"; + } } -// this method doesn't properly attribute Steam playtime metrics to DF, -// but that's better than not having DF start at all. -static BOOL launch_direct() { +static LPCWSTR launch_direct() { STARTUPINFOW si; PROCESS_INFORMATION pi; @@ -60,31 +72,152 @@ static BOOL launch_direct() { si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - return CreateProcessW(L"Dwarf Fortress.exe", + BOOL res = CreateProcessW(L"Dwarf Fortress.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + + if (res) + { + WaitForSingleObject(pi.hProcess, INFINITE); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return NULL; + } + + return L"Could not launch via non-steam fallback method"; +} + +DWORD findDwarfFortressProcess() +{ + PROCESSENTRY32W entry; + entry.dwSize = sizeof(PROCESSENTRY32W); + + const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (!Process32FirstW(snapshot, &entry)) + { + CloseHandle(snapshot); + return -1; + } + + do { + std::wstring executableName(entry.szExeFile); + if (executableName == L"Dwarf Fortress.exe") + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } while (Process32NextW(snapshot, &entry)); + + CloseHandle(snapshot); + return -1; } -int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) { +int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) { - if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID)) // Replace with your App ID + // initialize steam context + if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID)) { - return 1; + exit(0); } - if (!SteamAPI_Init()) + if (!SteamAPI_Init()) + { + // could not initialize steam context, attempt fallback launch + LPCWSTR err = launch_direct(); + if (err != NULL) { - printf("Fatal Error - Steam must be running to play this game (SteamAPI_Init() failed).\n"); - return 1; + MessageBoxW(NULL, err, NULL, 0); + exit(1); } + exit(0); + } - return 0; + bool wine = is_running_on_wine(); - LPCWSTR err = is_running_on_wine() ? launch_via_steam_posix() : launch_via_steam_windows(); + if (wine) + { + // attempt launch via steam client + LPCWSTR err = launch_via_steam_posix(); + + if (err != NULL) + // steam client launch failed, attempt fallback launch + err = launch_direct(); - if (err && !launch_direct()) { - MessageBoxW(NULL, err, NULL, 0); + if (err != NULL) + { + MessageBoxW(NULL, err, NULL, 0); + exit(1); + } + exit(0); + } + + // steam detected and not running in wine + + bool df_installed = SteamApps()->BIsAppInstalled(DF_STEAM_APPID); + + if (!df_installed) + { + // Steam DF is not installed. Assume DF is installed in same directory as DFHack and do a fallback launch + LPCWSTR err = launch_direct(); + + if (err != NULL) + { + MessageBoxW(NULL, err, NULL, 0); + exit(1); + } + exit(0); + } + + // obtain DF app path + + char buf[2048] = ""; + + int b = SteamApps()->GetAppInstallDir(DFHACK_STEAM_APPID, (char*)&buf, 2048); + std::string dfhack_install_folder = (b != -1) ? std::string(buf) : ""; + + int b2 = SteamApps()->GetAppInstallDir(DF_STEAM_APPID, (char*)&buf, 2048); + std::string df_install_folder = (b != -1) ? std::string(buf) : ""; + + + if (df_install_folder != dfhack_install_folder) + { + // DF and DFHack are not installed in the same library + MessageBoxW(NULL, L"DFHack and Dwarf Fortress must be installed in the same Steam library.\nAborting.", NULL, 0); exit(1); } + DWORD df_pid = findDwarfFortressProcess(); + + if (df_pid == -1) + { + LPCWSTR err = launch_via_steam_windows(); + if (err != NULL) + { + MessageBoxW(NULL, err, NULL, 0); + exit(1); + } + int counter = 0; + + do { + if (counter++ > 60) + { + MessageBoxW(NULL, L"Dwarf Fortress took too long to launch, aborting", NULL, 0); + exit(1); + } + Sleep(1000); + df_pid = findDwarfFortressProcess(); + } while (df_pid == -1); + } + + HANDLE hDF = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, df_pid); + + // in the future open an IPC connection so that we can proxy SteamAPI calls for the DFSteam module + + // this will eventuallyh need to become a loop with a WaitForMultipleObjects call + WaitForSingleObject(hDF, INFINITE); + + CloseHandle(hDF); + exit(0); } From dae549e3d1cc3dc775260c384eb3bd589981f662 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 08:47:05 -0500 Subject: [PATCH 1070/2222] fix missing end of line --- package/windows/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/windows/CMakeLists.txt b/package/windows/CMakeLists.txt index 42151e663..309613f2e 100644 --- a/package/windows/CMakeLists.txt +++ b/package/windows/CMakeLists.txt @@ -6,4 +6,4 @@ if(WIN32) add_executable(launchdf WIN32 launchdf.cpp) install(TARGETS launchdf DESTINATION ${DFHACK_DATA_DESTINATION}) install(FILES ${STEAMAPI_SHARED_LIBRARY} DESTINATION ${DFHACK_DATA_DESTINATION}) -endif() \ No newline at end of file +endif() From ea01dae88fc3cdc5d1d9673102e2bb2977ff430a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 11:12:29 -0500 Subject: [PATCH 1071/2222] fix minor oops --- package/windows/launchdf.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/windows/launchdf.cpp b/package/windows/launchdf.cpp index 19128ead1..a84465f53 100644 --- a/package/windows/launchdf.cpp +++ b/package/windows/launchdf.cpp @@ -173,11 +173,11 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, char buf[2048] = ""; - int b = SteamApps()->GetAppInstallDir(DFHACK_STEAM_APPID, (char*)&buf, 2048); - std::string dfhack_install_folder = (b != -1) ? std::string(buf) : ""; + int b1 = SteamApps()->GetAppInstallDir(DFHACK_STEAM_APPID, (char*)&buf, 2048); + std::string dfhack_install_folder = (b1 != -1) ? std::string(buf) : ""; int b2 = SteamApps()->GetAppInstallDir(DF_STEAM_APPID, (char*)&buf, 2048); - std::string df_install_folder = (b != -1) ? std::string(buf) : ""; + std::string df_install_folder = (b2 != -1) ? std::string(buf) : ""; if (df_install_folder != dfhack_install_folder) From 337c5eea2adb1be611afca3af7c796e6cdc1ba47 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 18:42:42 -0500 Subject: [PATCH 1072/2222] changes to build process for dflaunch only build dflaunch on steam-specific builds only download steamworks SDK when building dflaunch get steam account from environment when downloading steamworks SDK --- .github/workflows/steam.yml | 4 +++- CMakeLists.txt | 12 ------------ build/win64/generate-MSVC-steam.bat | 4 ++++ package/windows/CMakeLists.txt | 30 ++++++++++++++++++++++++----- 4 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 build/win64/generate-MSVC-steam.bat diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index aae1621af..a89249eab 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -38,7 +38,9 @@ jobs: ccache-win64-cross-msvc - name: Cross-compile win64 artifacts env: - CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1' + CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1' + steam_username: ${{ secrets.STEAM_USERNAME }} + steam_password: ${{ secrets.STEAM_PASSWORD }} run: | cd build bash -x build-win64-from-linux.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index cf5586dfd..fca548ccf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -313,18 +313,6 @@ if(WIN32) "5a09604daca6b2b5ce049d79af935d6a") endif() - # download Steam SDK - set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam) - file(DOWNLOAD "https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" - ${STEAMAPI_DIR}/steamworks_sdk_156.zip - EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b) - file(ARCHIVE_EXTRACT - INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip - DESTINATION ${STEAMAPI_DIR}) - set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib") - set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam") - set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll") - endif() if(APPLE) diff --git a/build/win64/generate-MSVC-steam.bat b/build/win64/generate-MSVC-steam.bat new file mode 100644 index 000000000..007bb1c08 --- /dev/null +++ b/build/win64/generate-MSVC-steam.bat @@ -0,0 +1,4 @@ +IF EXIST DF_PATH.txt SET /P _DF_PATH= Date: Tue, 18 Apr 2023 19:14:37 -0500 Subject: [PATCH 1073/2222] make release channel parameter optional --- .github/workflows/steam.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index a89249eab..cf41f861d 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -14,7 +14,7 @@ on: release_channel: description: Release channel type: string - required: true + required: false default: beta jobs: From 5c541a8317ab2d82bf1744e7fdff2acf6a5d32b2 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 19:33:02 -0500 Subject: [PATCH 1074/2222] pass credentials into container (only when needed of course) --- build/build-win64-from-linux.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 11f4dbbc5..c0559a999 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -41,6 +41,8 @@ fi if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ -e BUILDER_UID=$builder_uid \ -e CCACHE_DIR=/src/build/ccache \ + -e steam_username \ + -e steam_password \ --name dfhack-win \ ghcr.io/dfhack/build-env:msvc \ bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 $CMAKE_EXTRA_ARGS && dfhack-make -j$jobs install" \ From acc408c168093af1b1c51e1a1abe04d8eb97b177 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 21:52:57 -0500 Subject: [PATCH 1075/2222] change sdk download secret --- .github/workflows/steam.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index cf41f861d..71e56b662 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -39,8 +39,8 @@ jobs: - name: Cross-compile win64 artifacts env: CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1' - steam_username: ${{ secrets.STEAM_USERNAME }} - steam_password: ${{ secrets.STEAM_PASSWORD }} + steam_username: ${{ secrets.STEAM_SDK_USERNAME }} + steam_password: ${{ secrets.STEAM_SDK_PASSWORD }} run: | cd build bash -x build-win64-from-linux.sh From 93aaa5d19f1d74cf602744b5ad2dbc9bec42d19f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 18 Apr 2023 21:01:49 -0700 Subject: [PATCH 1076/2222] bump beta version to 50.07-r2rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fca548ccf..caf597088 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "r2rc1") +set(DFHACK_RELEASE "r2rc2") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From c3b077d5e742a578b066d81baebc14ba094c4701 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 23:05:27 -0500 Subject: [PATCH 1077/2222] merge upstream submodules --- depends/clsocket | 2 +- depends/xlsxio | 2 +- scripts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/depends/clsocket b/depends/clsocket index d5e17c601..6ed8aa464 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit d5e17c6012e7eefb0cbe3e130a56c24bd11f0094 +Subproject commit 6ed8aa46462ea01a1122fc49422840a2facc9757 diff --git a/depends/xlsxio b/depends/xlsxio index 0a9945266..439fdbc25 160000 --- a/depends/xlsxio +++ b/depends/xlsxio @@ -1 +1 @@ -Subproject commit 0a994526622c2201756e386ef98b44b193e25f06 +Subproject commit 439fdbc259c13f23a3122e68ba35ad5a13bcd97c diff --git a/scripts b/scripts index 6fba9905a..ec1a69788 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6fba9905a71f626ecc640fd3de988e147d16cb4e +Subproject commit ec1a69788fd6329008672523b622fd8b390fea73 From ac4a068007055426d96ba64f6c4f3b0f96115ab0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 18 Apr 2023 21:08:50 -0700 Subject: [PATCH 1078/2222] add missing changelog for buildingplan --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 553fb3362..5110e24ab 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: minimized planner panel stays minimized until you change it again +- `buildingplan`: can now filter by gems (for gem windows) - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) - `gui/control-panel`: add preference option for hiding the terminal console on startup - `gui/control-panel`: add preference option for hiding "armok" tools in command lists From f45291780e324f3344fe5ab924147cd96119ca09 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 23:19:05 -0500 Subject: [PATCH 1079/2222] update structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 055f9b4ce..34bc84b11 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 055f9b4cec3bbec8e562f4774754242a14026bd2 +Subproject commit 34bc84b1125f32b2f09b20ee0561a24bc3a66e70 From eaf2efee05c719ffda8f45eae5697da486a7d71a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 23:32:54 -0500 Subject: [PATCH 1080/2222] update dfhack version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fca548ccf..1ac75885c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,8 +191,8 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.07") -set(DFHACK_RELEASE "r2rc1") +set(DF_VERSION "50.08b1") +set(DFHACK_RELEASE "beta2") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From ae2bdfad5ff8fb4f9d530f9f1458415a45ed755e Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Wed, 19 Apr 2023 11:28:57 +0200 Subject: [PATCH 1081/2222] moved Slider class from filterselection.lua to widgets.lua --- library/lua/gui/widgets.lua | 137 ++++++++++++++++++ plugins/lua/buildingplan/filterselection.lua | 139 +------------------ 2 files changed, 138 insertions(+), 138 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index b33076cdc..eb4d53757 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2293,4 +2293,141 @@ function TabBar:onInput(keys) end end +-------------------------------- +-- Slider +-- + +Slider = defclass(Slider, Widget) +Slider.ATTRS{ + num_stops=DEFAULT_NIL, + get_left_idx_fn=DEFAULT_NIL, + get_right_idx_fn=DEFAULT_NIL, + on_left_change=DEFAULT_NIL, + on_right_change=DEFAULT_NIL, +} + +function Slider:preinit(init_table) + init_table.frame = init_table.frame or {} + init_table.frame.h = init_table.frame.h or 1 +end + +function Slider:init() + if self.num_stops < 2 then error('too few Slider stops') end + self.is_dragging_target = nil -- 'left', 'right', or 'both' + self.is_dragging_idx = nil -- offset from leftmost dragged tile +end + +local function slider_get_width_per_idx(self) + return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) +end + +function Slider:onInput(keys) + if not keys._MOUSE_L_DOWN then return false end + local x = self:getMousePos() + if not x then return false end + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + local left_pos = width_per_idx*(left_idx-1) + local right_pos = width_per_idx*(right_idx-1) + 4 + if x < left_pos then + self.on_left_change(self.get_left_idx_fn() - 1) + elseif x < left_pos+3 then + self.is_dragging_target = 'left' + self.is_dragging_idx = x - left_pos + elseif x < right_pos then + self.is_dragging_target = 'both' + self.is_dragging_idx = x - left_pos + elseif x < right_pos+3 then + self.is_dragging_target = 'right' + self.is_dragging_idx = x - right_pos + else + self.on_right_change(self.get_right_idx_fn() + 1) + end + return true +end + +local function slider_do_drag(self, width_per_idx) + local x = self.frame_body:localXY(dfhack.screen.getMousePos()) + local cur_pos = x - self.is_dragging_idx + cur_pos = math.max(0, cur_pos) + cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) + local offset = self.is_dragging_target == 'right' and -2 or 1 + local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 + local new_left_idx, new_right_idx + if self.is_dragging_target == 'right' then + new_right_idx = new_idx + else + new_left_idx = new_idx + if self.is_dragging_target == 'both' then + new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() + if new_right_idx > self.num_stops then + return + end + end + end + if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then + self.on_left_change(new_left_idx) + end + if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then + self.on_right_change(new_right_idx) + end +end + +local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} +local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} +local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} +local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} + +function Slider:onRenderBody(dc, rect) + local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() + local width_per_idx = slider_get_width_per_idx(self) + -- draw track + dc:seek(1,0) + dc:char(nil, SLIDER_LEFT_END) + dc:char(nil, SLIDER_TRACK) + for stop_idx=1,self.num_stops-1 do + local track_stop_pen = SLIDER_TRACK_STOP_SELECTED + local track_pen = SLIDER_TRACK_SELECTED + if left_idx > stop_idx or right_idx < stop_idx then + track_stop_pen = SLIDER_TRACK_STOP + track_pen = SLIDER_TRACK + elseif right_idx == stop_idx then + track_pen = SLIDER_TRACK + end + dc:char(nil, track_stop_pen) + for i=2,width_per_idx do + dc:char(nil, track_pen) + end + end + if right_idx >= self.num_stops then + dc:char(nil, SLIDER_TRACK_STOP_SELECTED) + else + dc:char(nil, SLIDER_TRACK_STOP) + end + dc:char(nil, SLIDER_TRACK) + dc:char(nil, SLIDER_RIGHT_END) + -- draw tabs + dc:seek(width_per_idx*(left_idx-1)) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + dc:seek(width_per_idx*(right_idx-1)+4) + dc:char(nil, SLIDER_TAB_LEFT) + dc:char(nil, SLIDER_TAB_CENTER) + dc:char(nil, SLIDER_TAB_RIGHT) + -- manage dragging + if self.is_dragging_target then + slider_do_drag(self, width_per_idx) + end + if df.global.enabler.mouse_lbut == 0 then + self.is_dragging_target = nil + self.is_dragging_idx = nil + end +end + return _ENV diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index 968ad88d9..fde80d4c8 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -12,143 +12,6 @@ local function get_cur_filters() uibs.building_subtype, uibs.custom_type) end --------------------------------- --- Slider --- - -Slider = defclass(Slider, widgets.Widget) -Slider.ATTRS{ - num_stops=DEFAULT_NIL, - get_left_idx_fn=DEFAULT_NIL, - get_right_idx_fn=DEFAULT_NIL, - on_left_change=DEFAULT_NIL, - on_right_change=DEFAULT_NIL, -} - -function Slider:preinit(init_table) - init_table.frame = init_table.frame or {} - init_table.frame.h = init_table.frame.h or 1 -end - -function Slider:init() - if self.num_stops < 2 then error('too few Slider stops') end - self.is_dragging_target = nil -- 'left', 'right', or 'both' - self.is_dragging_idx = nil -- offset from leftmost dragged tile -end - -local function slider_get_width_per_idx(self) - return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) -end - -function Slider:onInput(keys) - if not keys._MOUSE_L_DOWN then return false end - local x = self:getMousePos() - if not x then return false end - local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) - local left_pos = width_per_idx*(left_idx-1) - local right_pos = width_per_idx*(right_idx-1) + 4 - if x < left_pos then - self.on_left_change(self.get_left_idx_fn() - 1) - elseif x < left_pos+3 then - self.is_dragging_target = 'left' - self.is_dragging_idx = x - left_pos - elseif x < right_pos then - self.is_dragging_target = 'both' - self.is_dragging_idx = x - left_pos - elseif x < right_pos+3 then - self.is_dragging_target = 'right' - self.is_dragging_idx = x - right_pos - else - self.on_right_change(self.get_right_idx_fn() + 1) - end - return true -end - -local function slider_do_drag(self, width_per_idx) - local x = self.frame_body:localXY(dfhack.screen.getMousePos()) - local cur_pos = x - self.is_dragging_idx - cur_pos = math.max(0, cur_pos) - cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) - local offset = self.is_dragging_target == 'right' and -2 or 1 - local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 - local new_left_idx, new_right_idx - if self.is_dragging_target == 'right' then - new_right_idx = new_idx - else - new_left_idx = new_idx - if self.is_dragging_target == 'both' then - new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn() - if new_right_idx > self.num_stops then - return - end - end - end - if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then - self.on_left_change(new_left_idx) - end - if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then - self.on_right_change(new_right_idx) - end -end - -local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} -local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK} -local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK} -local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} -local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} -local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} - -function Slider:onRenderBody(dc, rect) - local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) - -- draw track - dc:seek(1,0) - dc:char(nil, SLIDER_LEFT_END) - dc:char(nil, SLIDER_TRACK) - for stop_idx=1,self.num_stops-1 do - local track_stop_pen = SLIDER_TRACK_STOP_SELECTED - local track_pen = SLIDER_TRACK_SELECTED - if left_idx > stop_idx or right_idx < stop_idx then - track_stop_pen = SLIDER_TRACK_STOP - track_pen = SLIDER_TRACK - elseif right_idx == stop_idx then - track_pen = SLIDER_TRACK - end - dc:char(nil, track_stop_pen) - for i=2,width_per_idx do - dc:char(nil, track_pen) - end - end - if right_idx >= self.num_stops then - dc:char(nil, SLIDER_TRACK_STOP_SELECTED) - else - dc:char(nil, SLIDER_TRACK_STOP) - end - dc:char(nil, SLIDER_TRACK) - dc:char(nil, SLIDER_RIGHT_END) - -- draw tabs - dc:seek(width_per_idx*(left_idx-1)) - dc:char(nil, SLIDER_TAB_LEFT) - dc:char(nil, SLIDER_TAB_CENTER) - dc:char(nil, SLIDER_TAB_RIGHT) - dc:seek(width_per_idx*(right_idx-1)+4) - dc:char(nil, SLIDER_TAB_LEFT) - dc:char(nil, SLIDER_TAB_CENTER) - dc:char(nil, SLIDER_TAB_RIGHT) - -- manage dragging - if self.is_dragging_target then - slider_do_drag(self, width_per_idx) - end - if df.global.enabler.mouse_lbut == 0 then - self.is_dragging_target = nil - self.is_dragging_idx = nil - end -end - -------------------------------- -- QualityAndMaterialsPage -- @@ -328,7 +191,7 @@ function QualityAndMaterialsPage:init() enabled=enable_item_quality, on_change=function(val) self:set_max_quality(val+1) end, }, - Slider{ + widgets.Slider{ frame={l=0, t=6}, num_stops=7, get_left_idx_fn=function() From 1620604a8e0d73dd5e0b7528df284aedd11b8572 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Wed, 19 Apr 2023 11:38:22 +0200 Subject: [PATCH 1082/2222] added changelog entry --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5110e24ab..809223e25 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- `widgets`: "Slider" class has been moved from ``filterselection.lua`` into ``widgets.lua`` ## Removed From a0b259bb676f2000eff4e160ae14f4772ab2c82f Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Wed, 19 Apr 2023 11:44:27 +0200 Subject: [PATCH 1083/2222] attempt to fix changelog entry... --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 809223e25..1ad220b0d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,7 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua -- `widgets`: "Slider" class has been moved from ``filterselection.lua`` into ``widgets.lua`` +- ``widgets``: "Slider" class has been moved from ``filterselection.lua`` into ``widgets.lua`` ## Removed From 98a252eab4ca6418b344ac8cbbc05a3273f056e5 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 20 Apr 2023 07:13:34 +0000 Subject: [PATCH 1084/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 6fba9905a..ad2bd2f60 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6fba9905a71f626ecc640fd3de988e147d16cb4e +Subproject commit ad2bd2f60d1760db8ffb80cfa57f8fb868613e81 From 8371aa0b8b0b4bbb2716f22c649b2123e2680f41 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 20 Apr 2023 13:24:47 +0200 Subject: [PATCH 1085/2222] renamed Slider to RangeSlider, and added draft of Lua API.rst entry --- docs/changelog.txt | 2 +- docs/dev/Lua API.rst | 11 +++++++++ library/lua/gui/widgets.lua | 26 ++++++++++---------- plugins/lua/buildingplan/filterselection.lua | 2 +- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1ad220b0d..c6e4386b8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,7 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua -- ``widgets``: "Slider" class has been moved from ``filterselection.lua`` into ``widgets.lua`` +- ``widgets``: "Slider" class has been moved from ``filterselection.lua`` into ``widgets.lua`` and renamed to "RangeSlider" ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index e63bc8a61..212a6e891 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5130,6 +5130,17 @@ widget does not require direct usage of ``Tab``. usage of ``Tab`` in ``TabBar:init()`` for an example. See the default value of ``active_tab_pens`` or ``inactive_tab_pens`` in ``TabBar`` for an example of how to construct pens. +RangeSlider class +----------------- + +This widget implements a mouse-interactable range-slider. The user can move its two handles to set minimum and maximum values to define a range. + +:num_stops: Specifies the amount of "notches" in the range slider, the places where handles can stop. +:get_left_idx_fn: The function used by the RangeSlider to determine what value to display on its left handle. +:get_right_idx_fn: The function used by the RangeSlider to determine what value to display on its right handle. +:on_left_change: Callback executed when a handle is moved leftwards. +:on_right_change: Callback executed when a handle is moved rightwards. + .. _lua-plugins: ======= diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index eb4d53757..a778df639 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2294,11 +2294,11 @@ function TabBar:onInput(keys) end -------------------------------- --- Slider +-- RangeSlider -- -Slider = defclass(Slider, Widget) -Slider.ATTRS{ +RangeSlider = defclass(RangeSlider, Widget) +RangeSlider.ATTRS{ num_stops=DEFAULT_NIL, get_left_idx_fn=DEFAULT_NIL, get_right_idx_fn=DEFAULT_NIL, @@ -2306,27 +2306,27 @@ Slider.ATTRS{ on_right_change=DEFAULT_NIL, } -function Slider:preinit(init_table) +function RangeSlider:preinit(init_table) init_table.frame = init_table.frame or {} init_table.frame.h = init_table.frame.h or 1 end -function Slider:init() - if self.num_stops < 2 then error('too few Slider stops') end +function RangeSlider:init() + if self.num_stops < 2 then error('too few RangeSlider stops') end self.is_dragging_target = nil -- 'left', 'right', or 'both' self.is_dragging_idx = nil -- offset from leftmost dragged tile end -local function slider_get_width_per_idx(self) +local function rangeslider_get_width_per_idx(self) return math.max(5, (self.frame_body.width-7) // (self.num_stops-1)) end -function Slider:onInput(keys) +function RangeSlider:onInput(keys) if not keys._MOUSE_L_DOWN then return false end local x = self:getMousePos() if not x then return false end local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) + local width_per_idx = rangeslider_get_width_per_idx(self) local left_pos = width_per_idx*(left_idx-1) local right_pos = width_per_idx*(right_idx-1) + 4 if x < left_pos then @@ -2346,7 +2346,7 @@ function Slider:onInput(keys) return true end -local function slider_do_drag(self, width_per_idx) +local function rangeslider_do_drag(self, width_per_idx) local x = self.frame_body:localXY(dfhack.screen.getMousePos()) local cur_pos = x - self.is_dragging_idx cur_pos = math.max(0, cur_pos) @@ -2383,9 +2383,9 @@ local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} -function Slider:onRenderBody(dc, rect) +function RangeSlider:onRenderBody(dc, rect) local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn() - local width_per_idx = slider_get_width_per_idx(self) + local width_per_idx = rangeslider_get_width_per_idx(self) -- draw track dc:seek(1,0) dc:char(nil, SLIDER_LEFT_END) @@ -2422,7 +2422,7 @@ function Slider:onRenderBody(dc, rect) dc:char(nil, SLIDER_TAB_RIGHT) -- manage dragging if self.is_dragging_target then - slider_do_drag(self, width_per_idx) + rangeslider_do_drag(self, width_per_idx) end if df.global.enabler.mouse_lbut == 0 then self.is_dragging_target = nil diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index fde80d4c8..4e8b98073 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -191,7 +191,7 @@ function QualityAndMaterialsPage:init() enabled=enable_item_quality, on_change=function(val) self:set_max_quality(val+1) end, }, - widgets.Slider{ + widgets.RangeSlider{ frame={l=0, t=6}, num_stops=7, get_left_idx_fn=function() From 1a2d5dfc8d34df66eb2a76cc1cb97ee79c17c43b Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 21 Apr 2023 07:13:33 +0000 Subject: [PATCH 1086/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index ad2bd2f60..5da969fce 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ad2bd2f60d1760db8ffb80cfa57f8fb868613e81 +Subproject commit 5da969fce69a5b9330f183cc0629798bf9907b69 From e30b86cfd2b25bd10ebe65cf2badf9fdfb1b0744 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 18 Apr 2023 10:19:25 +0000 Subject: [PATCH 1087/2222] Auto-update submodules depends/xlsxio: dfhack --- depends/xlsxio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/xlsxio b/depends/xlsxio index 439fdbc25..0a9945266 160000 --- a/depends/xlsxio +++ b/depends/xlsxio @@ -1 +1 @@ -Subproject commit 439fdbc259c13f23a3122e68ba35ad5a13bcd97c +Subproject commit 0a994526622c2201756e386ef98b44b193e25f06 From 1476e67422b426631247bc8119ff1ffa4cc3c736 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 05:40:51 -0500 Subject: [PATCH 1088/2222] cmake minimum version 3.21 forced by this being the lowest version that supports vs 2022 --- depends/clsocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/clsocket b/depends/clsocket index 6ed8aa464..d5e17c601 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit 6ed8aa46462ea01a1122fc49422840a2facc9757 +Subproject commit d5e17c6012e7eefb0cbe3e130a56c24bd11f0094 From 2b8b6a62e0d984746c386b519c5882b6e81a09be Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Fri, 21 Apr 2023 08:31:17 -0500 Subject: [PATCH 1089/2222] update structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 34bc84b11..98d5f8a55 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 34bc84b1125f32b2f09b20ee0561a24bc3a66e70 +Subproject commit 98d5f8a5553690ef71b9650b28d4aababf21ef5e From 6b86f7c69128e69dc03d07b735da5c68967759d9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 21 Apr 2023 11:20:28 -0700 Subject: [PATCH 1090/2222] planner panel is minimized by default and minimized state is now persisted across reloads --- data/dfhack-config/buildingplan.json | 5 +++ docs/changelog.txt | 2 +- plugins/lua/buildingplan/planneroverlay.lua | 40 ++++++++++++++------- 3 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 data/dfhack-config/buildingplan.json diff --git a/data/dfhack-config/buildingplan.json b/data/dfhack-config/buildingplan.json new file mode 100644 index 000000000..9bb3052b7 --- /dev/null +++ b/data/dfhack-config/buildingplan.json @@ -0,0 +1,5 @@ +{ + "planner": { + "minimized": true + } +} \ No newline at end of file diff --git a/docs/changelog.txt b/docs/changelog.txt index 4794122f6..b53717dd0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,7 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements -- `buildingplan`: minimized planner panel stays minimized until you change it again +- `buildingplan`: planner panel is minimized by default and now remembers minimized state - `buildingplan`: can now filter by gems (for gem windows) - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) - `gui/control-panel`: add preference option for hiding the terminal console on startup diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 8f12c695f..83a58ebd8 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -4,12 +4,15 @@ local itemselection = require('plugins.buildingplan.itemselection') local filterselection = require('plugins.buildingplan.filterselection') local gui = require('gui') local guidm = require('gui.dwarfmode') +local json = require('json') local overlay = require('plugins.overlay') local pens = require('plugins.buildingplan.pens') local utils = require('utils') local widgets = require('gui.widgets') require('dfhack.buildings') +config = config or json.open('dfhack-config/buildingplan.json') + local uibs = df.global.buildreq reset_counts_flag = false @@ -336,14 +339,14 @@ PlannerOverlay.ATTRS{ function PlannerOverlay:init() self.selected = 1 - self.minimized = false + self.state = ensure_key(config.data, 'planner') local main_panel = widgets.Panel{ view_id='main', frame={t=1, l=0, r=0, h=14}, frame_style=gui.INTERIOR_MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, - visible=function() return not self.minimized end, + visible=self:callback('is_not_minimized'), } local minimized_panel = widgets.Panel{ @@ -355,8 +358,8 @@ function PlannerOverlay:init() {text=' show Planner ', pen=pens.MINI_TEXT_PEN, hpen=pens.MINI_TEXT_HPEN}, {text='['..string.char(31)..']', pen=pens.MINI_BUTT_PEN, hpen=pens.MINI_BUTT_HPEN}, }, - visible=function() return self.minimized end, - on_click=function() self.minimized = not self.minimized end, + visible=self:callback('is_minimized'), + on_click=self:callback('toggle_minimized'), }, widgets.Label{ frame={t=0, r=0, h=1}, @@ -364,8 +367,8 @@ function PlannerOverlay:init() {text=' hide Planner ', pen=pens.MINI_TEXT_PEN, hpen=pens.MINI_TEXT_HPEN}, {text='['..string.char(30)..']', pen=pens.MINI_BUTT_PEN, hpen=pens.MINI_BUTT_HPEN}, }, - visible=function() return not self.minimized end, - on_click=function() self.minimized = not self.minimized end, + visible=self:callback('is_not_minimized'), + on_click=self:callback('toggle_minimized'), }, }, } @@ -554,7 +557,7 @@ function PlannerOverlay:init() view_id='divider', frame={t=10, l=0, r=0, h=1}, on_render=self:callback('draw_divider_h'), - visible=function() return not self.minimized end, + visible=self:callback('is_not_minimized'), } local error_panel = widgets.ResizingPanel{ @@ -562,7 +565,7 @@ function PlannerOverlay:init() frame={t=15, l=0, r=0}, frame_style=gui.BOLD_FRAME, frame_background=gui.CLEAR_PEN, - visible=function() return not self.minimized end, + visible=self:callback('is_not_minimized'), } error_panel:addviews{ @@ -609,7 +612,7 @@ function PlannerOverlay:init() frame={t=0, l=1, w=37, h=1}, frame_inset=0, frame_background=gui.CLEAR_PEN, - visible=function() return not self.minimized end, + visible=self:callback('is_not_minimized'), subviews={ prev_next_selector, }, @@ -624,6 +627,19 @@ function PlannerOverlay:init() } end +function PlannerOverlay:is_minimized() + return self.state.minimized +end + +function PlannerOverlay:is_not_minimized() + return not self.state.minimized +end + +function PlannerOverlay:toggle_minimized() + self.state.minimized = not self.state.minimized + config:write() +end + function PlannerOverlay:draw_divider_h(dc) local x2 = dc.width -1 for x=0,x2 do @@ -735,13 +751,13 @@ function PlannerOverlay:onInput(keys) return false end if keys.CUSTOM_ALT_M then - self.minimized = not self.minimized + self:toggle_minimized() return true end if PlannerOverlay.super.onInput(self, keys) then return true end - if self.minimized then return false end + if self:is_minimized() then return false end if keys._MOUSE_L_DOWN then if is_over_options_panel() then return false end local detect_rect = copyall(self.frame_rect) @@ -837,7 +853,7 @@ function PlannerOverlay:onRenderFrame(dc, rect) uibs.building_type, uibs.building_subtype, uibs.custom_type)) end - if self.minimized then return end + if self:is_minimized() then return end local bounds = get_selected_bounds(self.saved_selection_pos, self.saved_pos) if not bounds then return end From 6d9a07355bde4816939177bd70086231398f3564 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 21 Apr 2023 11:54:38 -0700 Subject: [PATCH 1091/2222] allow filtering by yarn --- docs/changelog.txt | 2 +- plugins/buildingplan/buildingplan.cpp | 10 ++++++++++ plugins/lua/buildingplan/filterselection.lua | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4794122f6..935b376b2 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,7 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: minimized planner panel stays minimized until you change it again -- `buildingplan`: can now filter by gems (for gem windows) +- `buildingplan`: can now filter by gems (for gem windows) and yarn (for ropes in wells) - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) - `gui/control-panel`: add preference option for hiding the terminal console on startup - `gui/control-panel`: add preference option for hiding "armok" tools in command lists diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index e2f964a44..05b2c0af6 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -154,6 +154,7 @@ static const df::dfhack_material_category gem_cat(df::dfhack_material_category:: static const df::dfhack_material_category clay_cat(df::dfhack_material_category::mask_clay); static const df::dfhack_material_category cloth_cat(df::dfhack_material_category::mask_cloth); static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk); +static const df::dfhack_material_category yarn_cat(df::dfhack_material_category::mask_yarn); static void cache_matched(int16_t type, int32_t index) { MaterialInfo mi; @@ -182,6 +183,9 @@ static void cache_matched(int16_t type, int32_t index) { } else if (mi.matches(silk_cat)) { DEBUG(status).print("cached silk material: %s (%d, %d)\n", mi.toString().c_str(), type, index); mat_cache.emplace(mi.toString(), std::make_pair(mi, "silk")); + } else if (mi.matches(yarn_cat)) { + DEBUG(status).print("cached yarn material: %s (%d, %d)\n", mi.toString().c_str(), type, index); + mat_cache.emplace(mi.toString(), std::make_pair(mi, "yarn")); } else TRACE(status).print("not matched: %s\n", mi.toString().c_str()); @@ -208,6 +212,7 @@ static void load_material_cache() { load_organic_material_cache(df::organic_mat_category::Wood); load_organic_material_cache(df::organic_mat_category::PlantFiber); load_organic_material_cache(df::organic_mat_category::Silk); + load_organic_material_cache(df::organic_mat_category::Yarn); } static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) { @@ -812,6 +817,8 @@ static int setMaterialMaskFilter(lua_State *L) { mask |= cloth_cat.whole; else if (cat == "silk") mask |= silk_cat.whole; + else if (cat == "yarn") + mask |= yarn_cat.whole; } DEBUG(status,*out).print( "setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n", @@ -860,6 +867,7 @@ static int getMaterialMaskFilter(lua_State *L) { ret.emplace("clay", !bits || bits & clay_cat.whole); ret.emplace("cloth", !bits || bits & cloth_cat.whole); ret.emplace("silk", !bits || bits & silk_cat.whole); + ret.emplace("yarn", !bits || bits & yarn_cat.whole); Lua::Push(L, ret); return 1; } @@ -912,6 +920,8 @@ static int setMaterialFilter(lua_State *L) { mask.whole |= cloth_cat.whole; else if (mat.matches(silk_cat)) mask.whole |= silk_cat.whole; + else if (mat.matches(yarn_cat)) + mask.whole |= yarn_cat.whole; } filter.setMaterialMask(mask.whole); get_item_filters(*out, key).setItemFilter(*out, filter, index); diff --git a/plugins/lua/buildingplan/filterselection.lua b/plugins/lua/buildingplan/filterselection.lua index 968ad88d9..eb364870d 100644 --- a/plugins/lua/buildingplan/filterselection.lua +++ b/plugins/lua/buildingplan/filterselection.lua @@ -456,6 +456,7 @@ function QualityAndMaterialsPage:refresh() make_cat_choice('Clay', 'clay', 'CUSTOM_SHIFT_C', cats), make_cat_choice('Cloth', 'cloth', 'CUSTOM_SHIFT_L', cats), make_cat_choice('Silk', 'silk', 'CUSTOM_SHIFT_K', cats), + make_cat_choice('Yarn', 'yarn', 'CUSTOM_SHIFT_Y', cats), } self.subviews.materials_categories:setChoices(category_choices) From c7bf7eba9062cc7c1aeb2f7a8cbd75e081deea5e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 21 Apr 2023 12:01:33 -0700 Subject: [PATCH 1092/2222] update scripts HEAD in beta branch --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index ec1a69788..5da969fce 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ec1a69788fd6329008672523b622fd8b390fea73 +Subproject commit 5da969fce69a5b9330f183cc0629798bf9907b69 From adeb872725cb6bfdcedbd4265a9c17e5d73d9745 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Sat, 22 Apr 2023 09:44:37 +0200 Subject: [PATCH 1093/2222] modified 's RangeSlider entry --- docs/dev/Lua API.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 212a6e891..02484c745 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5133,13 +5133,16 @@ widget does not require direct usage of ``Tab``. RangeSlider class ----------------- -This widget implements a mouse-interactable range-slider. The user can move its two handles to set minimum and maximum values to define a range. - -:num_stops: Specifies the amount of "notches" in the range slider, the places where handles can stop. -:get_left_idx_fn: The function used by the RangeSlider to determine what value to display on its left handle. -:get_right_idx_fn: The function used by the RangeSlider to determine what value to display on its right handle. -:on_left_change: Callback executed when a handle is moved leftwards. -:on_right_change: Callback executed when a handle is moved rightwards. +This widget implements a mouse-interactable range-slider. The user can move its two handles to set minimum and maximum values +to define a range, or they can drag the bar itself to move both handles at once. +The handles mirror the state of its two parent ``CycleHotkeyLabels``. + +:num_stops: Used to specify the number of "notches" in the range slider, the places where handles can stop. + (this should match the parents' number of options) +:get_left_idx_fn: The function used by the RangeSlider to get the notch index on which to display the left handle. +:get_right_idx_fn: The function used by the RangeSlider to get the notch index on which to display the right handle. +:on_left_change: Callback executed when moving the left handle. +:on_right_change: Callback executed when moving the right handle. .. _lua-plugins: From 29741dff4b012a7a5bdbda8c9dc6f797bb56f2dc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 16:31:41 -0700 Subject: [PATCH 1094/2222] remove toggle-kbd-cursor alias (it's a script now) --- data/init/dfhack.tools.init | 1 - 1 file changed, 1 deletion(-) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 8fd815559..aaf0cf277 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -144,4 +144,3 @@ enable \ alias add autounsuspend suspendmanager alias add gui/dig gui/design -alias add toggle-kbd-cursor lua "local flags4 = df.global.d_init.flags4 if flags4.KEYBOARD_CURSOR then flags4.KEYBOARD_CURSOR = false else local guidm = require('gui.dwarfmode') guidm.setCursorPos(guidm.Viewport.get():getCenter()) flags4.KEYBOARD_CURSOR = true end" From 33142a5dfc6a0cf279570da298b62659b8ce3e07 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 17:26:45 -0700 Subject: [PATCH 1095/2222] add DFHack title version overlay --- docs/changelog.txt | 1 + plugins/lua/overlay.lua | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 76b9ab301..7d95d810a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/control-panel`: add preference option for hiding the terminal console on startup - `gui/control-panel`: add preference option for hiding "armok" tools in command lists - ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running). +- `overlay`: add the DFHack version string to the DF title screen ## Documentation diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index ff8bf7c98..75dc301ad 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -562,4 +562,42 @@ function OverlayWidget:init() self.frame.h = self.frame.h or 1 end +-- ------------------- -- +-- TitleVersionOverlay -- +-- ------------------- -- + +TitleVersionOverlay = defclass(TitleVersionOverlay, OverlayWidget) +TitleVersionOverlay.ATTRS{ + default_pos={x=50, y=-2}, + default_enabled=true, + viewscreens='title', + frame={w=30, h=3}, +} + +function TitleVersionOverlay:init() + local text = {} + table.insert(text, 'DFHack ' .. dfhack.getDFHackVersion() .. + (dfhack.isPrerelease() and (' (%s)'):format(dfhack.getGitCommit():sub(1,7)) or '')) + if #dfhack.getDFHackBuildID() > 0 then + table.insert(text, NEWLINE) + table.insert(text, 'Build ID: ' .. dfhack.getDFHackBuildID()) + end + if dfhack.isPrerelease() then + table.insert(text, NEWLINE) + table.insert(text, {text='Pre-release build', pen=COLOR_LIGHTRED}) + end + + self:addviews{ + widgets.Label{ + frame={b=0, l=0}, + text=text, + text_pen=COLOR_WHITE, + }, + } +end + +OVERLAY_WIDGETS = { + title_version = TitleVersionOverlay, +} + return _ENV From b59bf72cd21c32cf551404ad87ac8a0b4c3d70c4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 17:31:29 -0700 Subject: [PATCH 1096/2222] turn down the title-version plugin --- docs/about/Removed.rst | 6 ++ docs/changelog.txt | 1 + docs/plugins/title-version.rst | 14 ----- plugins/CMakeLists.txt | 1 - plugins/title-version.cpp | 109 --------------------------------- 5 files changed, 7 insertions(+), 124 deletions(-) delete mode 100644 docs/plugins/title-version.rst delete mode 100644 plugins/title-version.cpp diff --git a/docs/about/Removed.rst b/docs/about/Removed.rst index a0365e621..174fc56c4 100644 --- a/docs/about/Removed.rst +++ b/docs/about/Removed.rst @@ -206,6 +206,12 @@ stocksettings Along with ``copystock``, ``loadstock`` and ``savestock``, replaced with the new `stockpiles` API. +.. _title-version: + +title-version +============= +Replaced with an `overlay`. + .. _warn-stuck-trees: warn-stuck-trees diff --git a/docs/changelog.txt b/docs/changelog.txt index 76b9ab301..fa5c15fde 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -52,6 +52,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua ## Removed +- `title-version`: replaced by an `overlay` widget # 50.07-r1 diff --git a/docs/plugins/title-version.rst b/docs/plugins/title-version.rst deleted file mode 100644 index 4d0ef0c0b..000000000 --- a/docs/plugins/title-version.rst +++ /dev/null @@ -1,14 +0,0 @@ -title-version -============= - -.. dfhack-tool:: - :summary: Displays the DFHack version on DF's title screen. - :tags: unavailable interface - :no-command: - -Usage ------ - -:: - - enable title-version diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 53d0296c7..97216ad50 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -162,7 +162,6 @@ dfhack_plugin(strangemood strangemood.cpp) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(title-folder title-folder.cpp) -#dfhack_plugin(title-version title-version.cpp) #dfhack_plugin(trackstop trackstop.cpp) #dfhack_plugin(tubefill tubefill.cpp) #add_subdirectory(tweak) diff --git a/plugins/title-version.cpp b/plugins/title-version.cpp deleted file mode 100644 index 66bb159ad..000000000 --- a/plugins/title-version.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" -#include "modules/Gui.h" -#include "modules/Screen.h" -#include "VTableInterpose.h" -#include "DFHackVersion.h" - -#include "df/graphic.h" -#include "df/viewscreen_optionst.h" -#include "df/viewscreen_titlest.h" -#include "uicommon.h" - -using std::vector; -using std::string; -using namespace DFHack; - -DFHACK_PLUGIN("title-version"); -DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(gps); - -void draw_version(int start_x, int start_y) { - int x = start_x, - y = start_y; - - OutputString(COLOR_WHITE, x, y, string("DFHack ") + DFHACK_VERSION); - if (!DFHACK_IS_RELEASE) - { - OutputString(COLOR_WHITE, x, y, " (dev)"); - x = start_x; y++; - OutputString(COLOR_WHITE, x, y, "Git: "); - OutputString(COLOR_WHITE, x, y, DFHACK_GIT_DESCRIPTION); - } - if (strlen(DFHACK_BUILD_ID)) - { - x = start_x; y++; - OutputString(COLOR_WHITE, x, y, "Build ID: "); - OutputString(COLOR_WHITE, x, y, DFHACK_BUILD_ID); - } - if (DFHACK_IS_PRERELEASE) - { - x = start_x; y++; - OutputString(COLOR_LIGHTRED, x, y, "Pre-release build"); - } -} - -struct title_version_hook : df::viewscreen_titlest { - typedef df::viewscreen_titlest interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - if (!loading) - draw_version(0, 0); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(title_version_hook, render); - -struct options_version_hook : df::viewscreen_optionst { - typedef df::viewscreen_optionst interpose_base; - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - if (!msg_quit && !in_retire_adv && !msg_peasant && - !in_retire_dwf_abandon_adv && !in_abandon_dwf && !ending_game) - draw_version(2, gps->dimy - 6); - } -}; - -IMPLEMENT_VMETHOD_INTERPOSE(options_version_hook, render); - -DFhackCExport command_result plugin_enable (color_ostream &out, bool enable) -{ - if (!gps) - return CR_FAILURE; - - if (enable != is_enabled) - { - if (!INTERPOSE_HOOK(title_version_hook, render).apply(enable) || - !INTERPOSE_HOOK(options_version_hook, render).apply(enable)) - return CR_FAILURE; - - is_enabled = enable; - } - - return CR_OK; -} - -DFhackCExport command_result plugin_init (color_ostream &out, vector &commands) -{ - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown (color_ostream &out) -{ - INTERPOSE_HOOK(title_version_hook, render).remove(); - INTERPOSE_HOOK(options_version_hook, render).remove(); - return CR_OK; -} From 87e67987a926dab0fbe98f06541717d2214eb8d3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 17:41:38 -0700 Subject: [PATCH 1097/2222] add "version" alias to run the help --- data/init/dfhack.tools.init | 1 + docs/changelog.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/data/init/dfhack.tools.init b/data/init/dfhack.tools.init index 8fd815559..cdd2a63a0 100644 --- a/data/init/dfhack.tools.init +++ b/data/init/dfhack.tools.init @@ -145,3 +145,4 @@ enable \ alias add autounsuspend suspendmanager alias add gui/dig gui/design alias add toggle-kbd-cursor lua "local flags4 = df.global.d_init.flags4 if flags4.KEYBOARD_CURSOR then flags4.KEYBOARD_CURSOR = false else local guidm = require('gui.dwarfmode') guidm.setCursorPos(guidm.Viewport.get():getCenter()) flags4.KEYBOARD_CURSOR = true end" +alias add version help diff --git a/docs/changelog.txt b/docs/changelog.txt index fa5c15fde..ecc616669 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `buildingplan`: planner panel is minimized by default and now remembers minimized state - `buildingplan`: can now filter by gems (for gem windows) and yarn (for ropes in wells) - ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K) +- ``version``: add alias to display the DFHack help (including the version number) so something happens when players try to run "version" - `gui/control-panel`: add preference option for hiding the terminal console on startup - `gui/control-panel`: add preference option for hiding "armok" tools in command lists - ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running). From b5459faffea36a6b579f6e7fe957f29d0d7aac72 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 18:20:50 -0700 Subject: [PATCH 1098/2222] format version string in help the same way --- library/Core.cpp | 2 +- library/DFHackVersion.cpp | 7 +++++-- library/include/DFHackVersion.h | 2 +- plugins/lua/overlay.lua | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 4f06c3d62..a1e3b60e5 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -273,7 +273,7 @@ static std::string dfhack_version_desc() if (Version::is_release()) s << "(release)"; else - s << "(development build " << Version::git_description() << ")"; + s << "(git: " << Version::git_commit(true) << ")"; s << " on " << (sizeof(void*) == 8 ? "x86_64" : "x86"); if (strlen(Version::dfhack_build_id())) s << " [build ID: " << Version::dfhack_build_id() << "]"; diff --git a/library/DFHackVersion.cpp b/library/DFHackVersion.cpp index 7746ebece..42aac251e 100644 --- a/library/DFHackVersion.cpp +++ b/library/DFHackVersion.cpp @@ -1,6 +1,8 @@ #define NO_DFHACK_VERSION_MACROS #include "DFHackVersion.h" #include "git-describe.h" +#include + namespace DFHack { namespace Version { int dfhack_abi_version() @@ -27,9 +29,10 @@ namespace DFHack { { return DFHACK_GIT_DESCRIPTION; } - const char *git_commit() + const char* git_commit(bool short_hash) { - return DFHACK_GIT_COMMIT; + static std::string shorty(DFHACK_GIT_COMMIT, 0, 7); + return short_hash ? shorty.c_str() : DFHACK_GIT_COMMIT; } const char *git_xml_commit() { diff --git a/library/include/DFHackVersion.h b/library/include/DFHackVersion.h index 1b69dfe55..fbf2539bf 100644 --- a/library/include/DFHackVersion.h +++ b/library/include/DFHackVersion.h @@ -8,7 +8,7 @@ namespace DFHack { int dfhack_abi_version(); const char *git_description(); - const char *git_commit(); + const char* git_commit(bool short_hash = false); const char *git_xml_commit(); const char *git_xml_expected_commit(); bool git_xml_match(); diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 75dc301ad..6a568fb85 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -571,13 +571,13 @@ TitleVersionOverlay.ATTRS{ default_pos={x=50, y=-2}, default_enabled=true, viewscreens='title', - frame={w=30, h=3}, + frame={w=35, h=3}, } function TitleVersionOverlay:init() local text = {} table.insert(text, 'DFHack ' .. dfhack.getDFHackVersion() .. - (dfhack.isPrerelease() and (' (%s)'):format(dfhack.getGitCommit():sub(1,7)) or '')) + (dfhack.isPrerelease() and (' (git: %s)'):format(dfhack.getGitCommit(true)) or '')) if #dfhack.getDFHackBuildID() > 0 then table.insert(text, NEWLINE) table.insert(text, 'Build ID: ' .. dfhack.getDFHackBuildID()) From 276efc981302646e2cee7b3d5c63dba245cdeeef Mon Sep 17 00:00:00 2001 From: Myk Date: Sun, 23 Apr 2023 18:40:24 -0700 Subject: [PATCH 1099/2222] Apply suggestions from code review --- docs/changelog.txt | 2 +- docs/dev/Lua API.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c6e4386b8..e05227e0d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,7 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua -- ``widgets``: "Slider" class has been moved from ``filterselection.lua`` into ``widgets.lua`` and renamed to "RangeSlider" +- ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 02484c745..078cec828 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5133,9 +5133,9 @@ widget does not require direct usage of ``Tab``. RangeSlider class ----------------- -This widget implements a mouse-interactable range-slider. The user can move its two handles to set minimum and maximum values +This widget implements a mouse-interactable range-slider. The player can move its two handles to set minimum and maximum values to define a range, or they can drag the bar itself to move both handles at once. -The handles mirror the state of its two parent ``CycleHotkeyLabels``. +The parent widget owns the range values, and can control them independently (e.g. with ``CycleHotkeyLabels``). If the range values change, the ``RangeSlider`` appearance will adjust automatically. :num_stops: Used to specify the number of "notches" in the range slider, the places where handles can stop. (this should match the parents' number of options) From 74e1aa70d9aea0bea57573b95797a659d236c1b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 23:28:50 -0700 Subject: [PATCH 1100/2222] fix smoothing job detection --- docs/changelog.txt | 1 + plugins/dig-now.cpp | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 3c1ed7e8f..588c1a6be 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `dig-now`: properly detect and complete smoothing designations that have been converted into active jobs ## Misc Improvements - `buildingplan`: planner panel is minimized by default and now remembers minimized state diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index be431722b..b2af2dbca 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -113,14 +113,10 @@ public: case job_type::CarveUpDownStaircase: td.bits.dig = tile_dig_designation::UpDownStair; break; - case job_type::DetailWall: - case job_type::DetailFloor: { - df::tiletype tt = map.tiletypeAt(job->pos); - if (tileSpecial(tt) != df::tiletype_special::SMOOTH) { - td.bits.smooth = 1; - } + case job_type::SmoothWall: + case job_type::SmoothFloor: + td.bits.smooth = 1; break; - } case job_type::CarveTrack: to.bits.carve_track_north = (job->item_category.whole >> 18) & 1; to.bits.carve_track_south = (job->item_category.whole >> 19) & 1; From 31401b2e199d31027be4fd155c4c91dc88805190 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 23 Apr 2023 23:48:10 -0700 Subject: [PATCH 1101/2222] fixed size limit calculations for rollers --- docs/changelog.txt | 1 + plugins/lua/buildingplan/planneroverlay.lua | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 3c1ed7e8f..0080ab9c3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `buildingplan`: fixed size limit calculations for rollers ## Misc Improvements - `buildingplan`: planner panel is minimized by default and now remembers minimized state diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 83a58ebd8..803e9ae99 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -34,9 +34,10 @@ local function get_selection_size_limits() or btype == df.building_type.RoadPaved or btype == df.building_type.RoadDirt then return {w=31, h=31} - elseif btype == df.building_type.AxleHorizontal - or btype == df.building_type.Rollers then + elseif btype == df.building_type.AxleHorizontal then return uibs.direction == 1 and {w=1, h=31} or {w=31, h=1} + elseif btype == df.building_type.Rollers then + return (uibs.direction == 1 or uibs.direction == 3) and {w=31, h=1} or {w=1, h=31} end end From 5e1117473ae577e009ba4825f81dfcd981105cfd Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 24 Apr 2023 07:14:36 +0000 Subject: [PATCH 1102/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 5da969fce..68f6d354b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 5da969fce69a5b9330f183cc0629798bf9907b69 +Subproject commit 68f6d354b0d815ad0985dbe9b5faa140c980af14 From ab047af1633ed386edef49c7e329d1d34e6b4a92 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 00:31:20 -0700 Subject: [PATCH 1103/2222] demote chatty WARN messages to DEBUG --- docs/changelog.txt | 1 + plugins/autoclothing.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 3c1ed7e8f..54d002f3e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `autoclothing`: eliminate game lag when there are many inventory items in the fort ## Misc Improvements - `buildingplan`: planner panel is minimized by default and now remembers minimized state diff --git a/plugins/autoclothing.cpp b/plugins/autoclothing.cpp index aa114cb84..d404960b4 100644 --- a/plugins/autoclothing.cpp +++ b/plugins/autoclothing.cpp @@ -565,7 +565,7 @@ static void find_needed_clothing_items() if (!item) { - WARN(cycle).print("autoclothing: Invalid inventory item ID: %d\n", ownedItem); + DEBUG(cycle).print("autoclothing: Invalid inventory item ID: %d\n", ownedItem); continue; } @@ -818,7 +818,7 @@ static void generate_report(color_ostream& out) auto item = Items::findItemByID(itemId); if (!item) { - WARN(cycle,out).print("autoclothing: Invalid inventory item ID: %d\n", itemId); + DEBUG(cycle, out).print("autoclothing: Invalid inventory item ID: %d\n", itemId); continue; } if (item->getWear() >= 1) From 6203894c998aef9f9aa3de7c6b85833719032618 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 08:03:11 -0700 Subject: [PATCH 1104/2222] add title screen focus string detection --- library/modules/Gui.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 6ea0a5ff0..44642b5fa 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -84,6 +84,7 @@ using namespace DFHack; #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_titlest.h" #include "df/world.h" const size_t MAX_REPORTS_SIZE = 3000; // DF clears old reports to maintain this vector size @@ -144,6 +145,17 @@ static std::map getFocusStringsHandle ); \ static void getFocusStrings_##screen_type(std::string &baseFocus, std::vector &focusStrings, VIEWSCREEN(screen_type) *screen) +DEFINE_GET_FOCUS_STRING_HANDLER(title) +{ + if (screen->managing_mods) + focusStrings.push_back(baseFocus + "/Mods"); + else if (game->main_interface.settings.open) + focusStrings.push_back(baseFocus + "/Settings"); + + if (focusStrings.empty()) + focusStrings.push_back(baseFocus + "/Default"); +} + DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) { std::string newFocusString; From 65da8ef3c97e9c7b819c0cabf77292c69249dcfc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 08:03:31 -0700 Subject: [PATCH 1105/2222] only display dfhack logo on base title screen to avoid overlapping important widgets on subscreens --- plugins/lua/hotkeys.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index b162f0c08..fca0f1f27 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -39,7 +39,7 @@ HotspotMenuWidget.ATTRS{ -- 'new_region', -- conflicts with vanilla panel layouts 'savegame', 'setupdwarfgame', - 'title', + 'title/Default', 'update_region', 'world' }, From 2686c8f08441088cb6d008c4728c66533e4a2f51 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 08:04:08 -0700 Subject: [PATCH 1106/2222] move DFHack version next to logo and only display on the base title screen --- plugins/lua/overlay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 6a568fb85..81dde32b6 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -570,7 +570,7 @@ TitleVersionOverlay = defclass(TitleVersionOverlay, OverlayWidget) TitleVersionOverlay.ATTRS{ default_pos={x=50, y=-2}, default_enabled=true, - viewscreens='title', + viewscreens='title/Default', frame={w=35, h=3}, } @@ -589,7 +589,7 @@ function TitleVersionOverlay:init() self:addviews{ widgets.Label{ - frame={b=0, l=0}, + frame={t=0, l=0}, text=text, text_pen=COLOR_WHITE, }, From 300e891f8aa8d97fcef4493cc3ac9a0d7bfd7a0c Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 24 Apr 2023 14:15:09 -0700 Subject: [PATCH 1107/2222] Update changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4429e40be..49723ab27 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,7 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `buildingplan`: fixed size limit calculations for rollers -`- `dig-now`: properly detect and complete smoothing designations that have been converted into active jobs +- `dig-now`: properly detect and complete smoothing designations that have been converted into active jobs ## Misc Improvements - `buildingplan`: planner panel is minimized by default and now remembers minimized state From 4ecf125a1aa2eb1c4efe513ba1cca5bbe37278d7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 24 Apr 2023 21:19:02 +0000 Subject: [PATCH 1108/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 68f6d354b..6b4001dc2 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 68f6d354b0d815ad0985dbe9b5faa140c980af14 +Subproject commit 6b4001dc2f9d0e662bb7d06d8ba6fcf343a656aa From 5a4dec35f1a28ad9838a951d3049924622c81166 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 14:28:21 -0700 Subject: [PATCH 1109/2222] reorder template declarations so we can push vectors of maps --- library/include/LuaTools.h | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index b19fc31de..cab3ee9cc 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -342,26 +342,6 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT void PushInterfaceKeys(lua_State *L, const std::set &keys); - template - void PushVector(lua_State *state, const T &pvec, bool addn = false) - { - lua_createtable(state,pvec.size(), addn?1:0); - - if (addn) - { - lua_pushinteger(state, pvec.size()); - lua_setfield(state, -2, "n"); - } - - for (size_t i = 0; i < pvec.size(); i++) - { - Push(state, pvec[i]); - lua_rawseti(state, -2, i+1); - } - } - - DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec, int idx = 1); - DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos); DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos); @@ -412,6 +392,26 @@ namespace DFHack {namespace Lua { lua_settable(state, -3); } + template + void PushVector(lua_State *state, const T &pvec, bool addn = false) + { + lua_createtable(state,pvec.size(), addn?1:0); + + if (addn) + { + lua_pushinteger(state, pvec.size()); + lua_setfield(state, -2, "n"); + } + + for (size_t i = 0; i < pvec.size(); i++) + { + Push(state, pvec[i]); + lua_rawseti(state, -2, i+1); + } + } + + DFHACK_EXPORT void GetVector(lua_State *state, std::vector &pvec, int idx = 1); + DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true); DFHACK_EXPORT bool IsCoreContext(lua_State *state); From 73b5e37f678719c7f4473ebfa5263e8c743484d9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 14:28:47 -0700 Subject: [PATCH 1110/2222] simplify loops with foreach syntax --- library/modules/Buildings.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 7694a696b..b9e61c863 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1115,31 +1115,17 @@ static void createDesign(df::building *bld, bool rough) static int getMaxStockpileId() { - auto &vec = world->buildings.other[buildings_other_id::STOCKPILE]; int max_id = 0; - - for (size_t i = 0; i < vec.size(); i++) - { - auto bld = strict_virtual_cast(vec[i]); - if (bld) - max_id = std::max(max_id, bld->stockpile_number); - } - + for (auto bld : world->buildings.other.STOCKPILE) + max_id = std::max(max_id, bld->stockpile_number); return max_id; } static int getMaxCivzoneId() { - auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE]; int max_id = 0; - - for (size_t i = 0; i < vec.size(); i++) - { - auto bld = strict_virtual_cast(vec[i]); - if (bld) - max_id = std::max(max_id, bld->zone_num); - } - + for (auto bld : world->buildings.other.ANY_ZONE) + max_id = std::max(max_id, bld->zone_num); return max_id; } From 65d7b290a3f7a859d2a75fa44e4c3109684f63b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 14:32:33 -0700 Subject: [PATCH 1111/2222] add ZScreenModal class for modal dialogs --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 7 +++++++ library/lua/gui.lua | 13 +++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a3e9c6f6c..2b3438cc5 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget +- ``gui.ZScreenModal``: ZScreen subclass for modal dialogs ## Removed - `title-version`: replaced by an `overlay` widget diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 078cec828..9c320fc98 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4323,6 +4323,13 @@ Here is an example skeleton for a ZScreen tool window:: view = view and view:raise() or MyScreen{}:show() +ZScreenModal class +------------------ + +A ZScreen convenience subclass that sets the attributes to something +appropriate for modal dialogs. The game is force paused, and no input is passed +through to the underlying viewscreens. + FramedScreen class ------------------ diff --git a/library/lua/gui.lua b/library/lua/gui.lua index e425dbcf1..b2c90d076 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -867,8 +867,17 @@ function ZScreen:onGetSelectedPlant() return zscreen_get_any(self, 'Plant') end --------------------------- --- Framed screen object -- +-- convenience subclass for modal dialogs +ZScreenModal = defclass(ZScreenModal, ZScreen) +ZScreenModal.ATTRS{ + defocusable = false, + force_pause = true, + pass_pause = false, + pass_movement_keys = false, + pass_mouse_clicks = false, +} + +-- Framed screen object -------------------------- -- Plain grey-colored frame. From 54ea391b1daf7059eef570add6e327a2eebaa3b0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 15:26:33 -0700 Subject: [PATCH 1112/2222] bump to 50.07-r2rc3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index caf597088..72314ee6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.07") -set(DFHACK_RELEASE "r2rc2") +set(DFHACK_RELEASE "r2rc3") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From 6c88fa6440b9282595fb2dddf7e6c5ba5bf07336 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 24 Apr 2023 16:12:37 -0700 Subject: [PATCH 1113/2222] don't output git hash on release builds --- plugins/lua/overlay.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 81dde32b6..cf7008a40 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -577,7 +577,7 @@ TitleVersionOverlay.ATTRS{ function TitleVersionOverlay:init() local text = {} table.insert(text, 'DFHack ' .. dfhack.getDFHackVersion() .. - (dfhack.isPrerelease() and (' (git: %s)'):format(dfhack.getGitCommit(true)) or '')) + (dfhack.isRelease() and '' or (' (git: %s)'):format(dfhack.getGitCommit(true)))) if #dfhack.getDFHackBuildID() > 0 then table.insert(text, NEWLINE) table.insert(text, 'Build ID: ' .. dfhack.getDFHackBuildID()) From 4d3a4adf572dd49aab402a72bcd8bd368b0612f4 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 25 Apr 2023 07:13:39 +0000 Subject: [PATCH 1114/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 6b4001dc2..ad1998a00 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6b4001dc2f9d0e662bb7d06d8ba6fcf343a656aa +Subproject commit ad1998a0032ce50e90d05429a4178b668c0840ba From 68a8c687eabbecbf4c39b8045340ee5f76dfdf05 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 25 Apr 2023 16:02:15 -0500 Subject: [PATCH 1115/2222] steam build workflow improvements improve cmake handling for downloading the sdk set up GHA to cache the SDK download --- .github/workflows/steam.yml | 6 ++++++ package/windows/CMakeLists.txt | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 71e56b662..05692c680 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -36,6 +36,12 @@ jobs: restore-keys: | ccache-win64-cross-msvc-develop-${{ github.event.inputs.commit_hash }} ccache-win64-cross-msvc + - name: Restore steam SDK + uses: action/cache@v3 + with: + path: depends/steam/steamworks_sdk_156.zip + key: steam-sdk-156 + enableCrossOsArchive: true - name: Cross-compile win64 artifacts env: CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1' diff --git a/package/windows/CMakeLists.txt b/package/windows/CMakeLists.txt index b74cdfec5..d92d687c4 100644 --- a/package/windows/CMakeLists.txt +++ b/package/windows/CMakeLists.txt @@ -9,13 +9,22 @@ if(WIN32) ${STEAMAPI_DIR}/steamworks_sdk_156.zip EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b USERPWD $ENV{steam_username}:$ENV{steam_password} + STATUS STEAM_SDK_DOWNLOAD_STATUS + SHOW_PROGRESS ) - file(ARCHIVE_EXTRACT - INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip - DESTINATION ${STEAMAPI_DIR}) - set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib") - set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam") - set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll") + list(GET STEAM_SDK_DOWNLOAD_STATUS 0 STEAM_SDK_DL_STATUS_CODE) + list(GET STEAM_SDK_DOWNLOAD_STATUS 1 STEAM_SDK_DL_ERROR_MESSAGE) + if (NOT (${STEAM_SDK_DL_STATUS_CODE} EQUAL 0)) + message(FATAL_ERROR "Steam SDK download: " ${STEAM_SDK_DL_ERROR_MESSAGE}) + else () + message(STATUS "Steam SDK download: " ${STEAM_SDK_DL_ERROR_MESSAGE}) + file(ARCHIVE_EXTRACT + INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip + DESTINATION ${STEAMAPI_DIR}) + set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib") + set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam") + set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll") + endif() else() message(SEND_ERROR "Need to set steam_username and steam_password in environment to download Steamworks SDK") endif() From 17a798d5bc9f0b3d64930663e992275bdddf7d4a Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 25 Apr 2023 16:06:36 -0500 Subject: [PATCH 1116/2222] add missing letter --- .github/workflows/steam.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 05692c680..ca6802446 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -37,7 +37,7 @@ jobs: ccache-win64-cross-msvc-develop-${{ github.event.inputs.commit_hash }} ccache-win64-cross-msvc - name: Restore steam SDK - uses: action/cache@v3 + uses: actions/cache@v3 with: path: depends/steam/steamworks_sdk_156.zip key: steam-sdk-156 From 48ffad2f7145cfe19613bc8e1dba2a04e4bd565e Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Wed, 26 Apr 2023 01:02:38 +0200 Subject: [PATCH 1117/2222] added and attributes to --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 4 ++++ library/lua/gui/widgets.lua | 9 +++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index b85c738a5..80f2797b6 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,6 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget - ``gui.ZScreenModal``: ZScreen subclass for modal dialogs +- ``widgets.CycleHotkeyLabel``: exposed `key_sep` and `val_gap` attributes for improved stylistic control. ## Removed - `title-version`: replaced by an `overlay` widget diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9c320fc98..73538a5a3 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4912,12 +4912,16 @@ It has the following attributes: :key: The hotkey keycode to display, e.g. ``'CUSTOM_A'``. :key_back: Similar to ``key``, but will cycle backwards (optional) +:key_sep: If specified, will be used to customize how the activation key is + displayed. See ``token.key_sep`` in the ``Label`` documentation. :label: The string (or a function that returns a string) to display after the hotkey. :label_width: The number of spaces to allocate to the ``label`` (for use in aligning a column of ``CycleHotkeyLabel`` labels). :label_below: If ``true``, then the option value will apear below the label instead of to the right of it. Defaults to ``false``. +:val_gap: The size of the gap between the label text and the option value. + Default is ``1``. If set to ``0``, there'll be no gap between the strings. :options: A list of strings or tables of ``{label=string or fn, value=val[, pen=pen]}``. String options use the same string for the label and value and use the default pen. The optional ``pen`` diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index a778df639..c7cde698c 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1488,6 +1488,8 @@ CycleHotkeyLabel = defclass(CycleHotkeyLabel, Label) CycleHotkeyLabel.ATTRS{ key=DEFAULT_NIL, key_back=DEFAULT_NIL, + key_sep=': ', + val_gap=1, label=DEFAULT_NIL, label_width=DEFAULT_NIL, label_below=false, @@ -1499,17 +1501,16 @@ CycleHotkeyLabel.ATTRS{ function CycleHotkeyLabel:init() self:setOption(self.initial_option) - local val_gap = 1 if self.label_below then - val_gap = 0 + (self.key_back and 1 or 0) + (self.key and 3 or 0) + self.val_gap = 0 + (self.key_back and 1 or 0) + (self.key and 3 or 0) end self:setText{ self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self:callback('cycle', true)} or {}, - {key=self.key, key_sep=': ', text=self.label, width=self.label_width, + {key=self.key, key_sep=self.key_sep, text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, self.label_below and NEWLINE or '', - {gap=val_gap, text=self:callback('getOptionLabel'), + {gap=self.val_gap, text=self:callback('getOptionLabel'), pen=self:callback('getOptionPen')}, } end From 6e1300458cf4440d3c510fb7007d0925099a9e42 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Wed, 26 Apr 2023 01:26:38 +0200 Subject: [PATCH 1118/2222] fixed some wrong quotation marks in changelog.txt that made the build thingy cry --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 80f2797b6..91f25a323 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -57,7 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget - ``gui.ZScreenModal``: ZScreen subclass for modal dialogs -- ``widgets.CycleHotkeyLabel``: exposed `key_sep` and `val_gap` attributes for improved stylistic control. +- ``widgets.CycleHotkeyLabel``: exposed "key_sep" and "val_gap" attributes for improved stylistic control. ## Removed - `title-version`: replaced by an `overlay` widget From ec659ca1c26bed0ee768421e29d49e3b4038b781 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Wed, 26 Apr 2023 11:26:29 +0200 Subject: [PATCH 1119/2222] added note about val_gap's behaviour when label_below == true --- docs/dev/Lua API.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 73538a5a3..37d85f7f6 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4922,6 +4922,7 @@ It has the following attributes: instead of to the right of it. Defaults to ``false``. :val_gap: The size of the gap between the label text and the option value. Default is ``1``. If set to ``0``, there'll be no gap between the strings. + Note that ``val_gap`` is ignored if ``label_below`` is set to ``true``. :options: A list of strings or tables of ``{label=string or fn, value=val[, pen=pen]}``. String options use the same string for the label and value and use the default pen. The optional ``pen`` From ba06a8f2bb1a41be61543755804999fc4fe48e33 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 26 Apr 2023 13:36:40 -0700 Subject: [PATCH 1120/2222] dump input vars in deploy output --- .github/workflows/steam.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index ca6802446..33aa16d34 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -48,6 +48,10 @@ jobs: steam_username: ${{ secrets.STEAM_SDK_USERNAME }} steam_password: ${{ secrets.STEAM_SDK_PASSWORD }} run: | + echo "commit: ${{ github.event.inputs.commit_hash }}" + echo "version: ${{ github.event.inputs.version }}" + echo "release_channel: ${{ github.event.inputs.release_channel }}" + echo cd build bash -x build-win64-from-linux.sh - name: Steam deploy From 9c447e8d45479c14d5a29113446c85360db70709 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 26 Apr 2023 14:39:13 -0700 Subject: [PATCH 1121/2222] re-add add-spatter plugin (minimal changes) --- docs/changelog.txt | 1 + docs/plugins/add-spatter.rst | 2 +- plugins/CMakeLists.txt | 2 +- plugins/add-spatter.cpp | 62 +++++++++++------------------------- 4 files changed, 21 insertions(+), 46 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 91f25a323..25391f081 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `add-spatter`: allow mods to add poisons and magical effects to weapons ## Fixes - `autoclothing`: eliminate game lag when there are many inventory items in the fort diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 6914690ea..2c43a88ef 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -2,7 +2,7 @@ add-spatter =========== .. dfhack-tool:: - :summary: Make tagged reactions produce contaminants. + :summary: Add poisons and magical effects to weapons. :tags: unavailable adventure fort gameplay items :no-command: diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 97216ad50..8aadd46f2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -74,7 +74,7 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) # see instructions for adding "external" plugins at the end of this file. #dfhack_plugin(3dveins 3dveins.cpp) -#dfhack_plugin(add-spatter add-spatter.cpp) +dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) dfhack_plugin(autoclothing autoclothing.cpp LINK_LIBRARIES lua) diff --git a/plugins/add-spatter.cpp b/plugins/add-spatter.cpp index 451fffabf..3cfefce39 100644 --- a/plugins/add-spatter.cpp +++ b/plugins/add-spatter.cpp @@ -1,51 +1,26 @@ -#include "Core.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "df/item_liquid_miscst.h" +#include "PluginManager.h" +#include "VTableInterpose.h" + +#include "modules/Items.h" +#include "modules/Units.h" + #include "df/item_constructed.h" -#include "df/builtin_mats.h" -#include "df/world.h" #include "df/job.h" #include "df/job_item.h" #include "df/job_item_ref.h" -#include "df/plotinfost.h" -#include "df/report.h" #include "df/reaction.h" #include "df/reaction_reagent_itemst.h" #include "df/reaction_product_item_improvementst.h" -#include "df/reaction_product_improvement_flags.h" -#include "df/matter_state.h" #include "df/spatter.h" -#include "MiscUtils.h" - using std::vector; using std::string; -using std::stack; + using namespace DFHack; using namespace df::enums; DFHACK_PLUGIN("add-spatter"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); -REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(plotinfo); typedef df::reaction_product_item_improvementst improvement_product; @@ -397,18 +372,20 @@ static void enable_hooks(bool enable) INTERPOSE_HOOK(product_hook, produce).apply(enable); } +DFhackCExport command_result plugin_load_data (color_ostream &out) { + if (find_reactions(out)) { + out.print("Detected spatter add reactions - enabling plugin.\n"); + enable_hooks(true); + } + else + enable_hooks(false); + + return CR_OK; +} + DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { switch (event) { - case SC_WORLD_LOADED: - if (find_reactions(out)) - { - out.print("Detected spatter add reactions - enabling plugin.\n"); - enable_hooks(true); - } - else - enable_hooks(false); - break; case SC_WORLD_UNLOADED: enable_hooks(false); reactions.clear(); @@ -423,9 +400,6 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - if (Core::getInstance().isWorldLoaded()) - plugin_onstatechange(out, SC_WORLD_LOADED); - return CR_OK; } From fef7919c080d9d58783e2ab26237450b03fc24c5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 26 Apr 2023 16:19:26 -0700 Subject: [PATCH 1122/2222] bump version to 50.08-beta4 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index beb1f28d9..d6b5d2949 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.08b1") -set(DFHACK_RELEASE "beta3") +set(DFHACK_RELEASE "beta4") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From 1b8fc20ad53fded507407fe0f0a52486d626b6f5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 26 Apr 2023 21:01:21 -0700 Subject: [PATCH 1123/2222] check items for accessibility for dialogs before we only checked when doing the cycle, so if an inaccessible item were manually selected, we'd never be able to build --- docs/changelog.txt | 1 + plugins/buildingplan/buildingplan.cpp | 2 +- plugins/buildingplan/buildingplan.h | 2 +- plugins/buildingplan/buildingplan_cycle.cpp | 42 ++++++++++----------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 91f25a323..66904aa6d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes - `autoclothing`: eliminate game lag when there are many inventory items in the fort - `buildingplan`: fixed size limit calculations for rollers +- `buildingplan`: fixed items not being checked for accessibility in the filter and item selection dialogs - `dig-now`: properly detect and complete smoothing designations that have been converted into active jobs ## Misc Improvements diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index 05b2c0af6..ce424f9c6 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -704,7 +704,7 @@ static int scanAvailableItems(color_ostream &out, df::building_type type, int16_ filter.setMaterials(set()); special.clear(); } - if (itemPassesScreen(item) && matchesFilters(item, jitem, heat, filter, special)) { + if (itemPassesScreen(out, item) && matchesFilters(item, jitem, heat, filter, special)) { if (item_ids) item_ids->emplace_back(item->id); if (counts) { diff --git a/plugins/buildingplan/buildingplan.h b/plugins/buildingplan/buildingplan.h index 5c4f25aa4..9bfd38731 100644 --- a/plugins/buildingplan/buildingplan.h +++ b/plugins/buildingplan/buildingplan.h @@ -54,7 +54,7 @@ void set_config_val(DFHack::PersistentDataItem &c, int index, int value); void set_config_bool(DFHack::PersistentDataItem &c, int index, bool value); std::vector getVectorIds(DFHack::color_ostream &out, const df::job_item *job_item, bool ignore_filters); -bool itemPassesScreen(df::item * item); +bool itemPassesScreen(DFHack::color_ostream& out, df::item* item); df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat); bool matchesFilters(df::item * item, const df::job_item * job_item, HeatSafety heat, const ItemFilter &item_filter, const std::set &special); bool isJobReady(DFHack::color_ostream &out, const std::vector &jitems); diff --git a/plugins/buildingplan/buildingplan_cycle.cpp b/plugins/buildingplan/buildingplan_cycle.cpp index 3213e741d..45cafe474 100644 --- a/plugins/buildingplan/buildingplan_cycle.cpp +++ b/plugins/buildingplan/buildingplan_cycle.cpp @@ -43,10 +43,27 @@ struct BadFlags { } }; -bool itemPassesScreen(df::item * item) { +// This is tricky. we want to choose an item that can be brought to the job site, but that's not +// necessarily the same as job->pos. it could be many tiles off in any direction (e.g. for bridges), or +// up or down (e.g. for stairs). For now, just return if the item is on a walkable tile. +static bool isAccessible(color_ostream& out, df::item* item) { + df::coord item_pos = Items::getPosition(item); + df::map_block* block = Maps::getTileBlock(item_pos); + bool is_walkable = false; + if (block) { + uint16_t walkability_group = index_tile(block->walkable, item_pos); + is_walkable = walkability_group != 0; + TRACE(cycle, out).print("item %d in walkability_group %u at (%d,%d,%d) is %saccessible from job site\n", + item->id, walkability_group, item_pos.x, item_pos.y, item_pos.z, is_walkable ? "(probably) " : "not "); + } + return is_walkable; +} + +bool itemPassesScreen(color_ostream& out, df::item* item) { static const BadFlags bad_flags; return !(item->flags.whole & bad_flags.whole) - && !item->isAssignedToStockpile(); + && !item->isAssignedToStockpile() + && isAccessible(out, item); } df::job_item getJobItemWithHeatSafety(const df::job_item *job_item, HeatSafety heat) { @@ -165,22 +182,6 @@ static df::building * popInvalidTasks(color_ostream &out, Bucket &task_queue, return NULL; } -// This is tricky. we want to choose an item that can be brought to the job site, but that's not -// necessarily the same as job->pos. it could be many tiles off in any direction (e.g. for bridges), or -// up or down (e.g. for stairs). For now, just return if the item is on a walkable tile. -static bool isAccessibleFrom(color_ostream &out, df::item *item, df::job *job) { - df::coord item_pos = Items::getPosition(item); - df::map_block *block = Maps::getTileBlock(item_pos); - bool is_walkable = false; - if (block) { - uint16_t walkability_group = index_tile(block->walkable, item_pos); - is_walkable = walkability_group != 0; - TRACE(cycle,out).print("item %d in walkability_group %u at (%d,%d,%d) is %saccessible from job site\n", - item->id, walkability_group, item_pos.x, item_pos.y, item_pos.z, is_walkable ? "" : "not "); - } - return is_walkable; -} - static void doVector(color_ostream &out, df::job_item_vector_id vector_id, map &buckets, unordered_map &planned_buildings, @@ -195,7 +196,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, item_it != item_vector.rend(); ++item_it) { auto item = *item_it; - if (!itemPassesScreen(item)) + if (!itemPassesScreen(out, item)) continue; for (auto bucket_it = buckets.begin(); bucket_it != buckets.end(); ) { TRACE(cycle,out).print("scanning bucket: %s/%s\n", @@ -218,8 +219,7 @@ static void doVector(color_ostream &out, df::job_item_vector_id vector_id, auto filter_idx = task.second; const int rev_filter_idx = num_filters - (filter_idx+1); auto &pb = planned_buildings.at(id); - if (isAccessibleFrom(out, item, job) - && matchesFilters(item, jitems[filter_idx], pb.heat_safety, + if (matchesFilters(item, jitems[filter_idx], pb.heat_safety, pb.item_filters[rev_filter_idx], pb.specials) && Job::attachJobItem(job, item, df::job_item_ref::Hauled, filter_idx)) From fab05ca887049b1755cca86726f740764ea25a5c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 27 Apr 2023 12:59:06 -0700 Subject: [PATCH 1124/2222] fix position of title overlay; force refresh on resize --- plugins/lua/overlay.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index cf7008a40..b1960989f 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -510,7 +510,7 @@ end local function _render_viewscreen_widgets(vs_name, dc) local vs_widgets = active_viewscreen_widgets[vs_name] - if not vs_widgets then return false end + if not vs_widgets then return end dc = dc or gui.Painter.new() for _,db_entry in pairs(vs_widgets) do local w = db_entry.widget @@ -518,11 +518,18 @@ local function _render_viewscreen_widgets(vs_name, dc) detect_frame_change(w, function() w:render(dc) end) end end + return dc end +local force_refresh + function render_viewscreen_widgets(vs_name) local dc = _render_viewscreen_widgets(vs_name, nil) _render_viewscreen_widgets('all', dc) + if force_refresh then + force_refresh = nil + df.global.gps.force_full_display_count = 1 + end end -- called when the DF window is resized @@ -531,6 +538,7 @@ function reposition_widgets() for _,db_entry in pairs(widget_db) do db_entry.widget:updateLayout(sr) end + force_refresh = true end -- ------------------------------------------------- -- @@ -568,7 +576,7 @@ end TitleVersionOverlay = defclass(TitleVersionOverlay, OverlayWidget) TitleVersionOverlay.ATTRS{ - default_pos={x=50, y=-2}, + default_pos={x=7, y=2}, default_enabled=true, viewscreens='title/Default', frame={w=35, h=3}, From ef140b0dd6d0a7437d5ed9f2cacb533c0bb7f484 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 27 Apr 2023 13:20:25 -0700 Subject: [PATCH 1125/2222] rename and enable plugin --- docs/plugins/{workNow.rst => work-now.rst} | 6 +++--- plugins/CMakeLists.txt | 2 +- plugins/{workNow.cpp => work-now.cpp} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename docs/plugins/{workNow.rst => work-now.rst} (90%) rename plugins/{workNow.cpp => work-now.cpp} (100%) diff --git a/docs/plugins/workNow.rst b/docs/plugins/work-now.rst similarity index 90% rename from docs/plugins/workNow.rst rename to docs/plugins/work-now.rst index cec39c667..7578500e8 100644 --- a/docs/plugins/workNow.rst +++ b/docs/plugins/work-now.rst @@ -1,9 +1,9 @@ -workNow -======= +work-now +======== .. dfhack-tool:: :summary: Reduce the time that dwarves idle after completing a job. - :tags: unavailable fort auto labors + :tags: fort auto jobs After finishing a job, dwarves will wander away for a while before picking up a new job. This plugin will automatically poke the game to assign dwarves to new diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 97216ad50..c9822ea8a 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -166,7 +166,7 @@ dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) #dfhack_plugin(tubefill tubefill.cpp) #add_subdirectory(tweak) #dfhack_plugin(workflow workflow.cpp LINK_LIBRARIES lua) -#dfhack_plugin(workNow workNow.cpp) +dfhack_plugin(work-now work-now.cpp) dfhack_plugin(xlsxreader xlsxreader.cpp LINK_LIBRARIES lua xlsxio_read_STATIC zip expat) #dfhack_plugin(zone zone.cpp) diff --git a/plugins/workNow.cpp b/plugins/work-now.cpp similarity index 100% rename from plugins/workNow.cpp rename to plugins/work-now.cpp From 83fa87b492edd50f1cc98159bae3d12c0fcd0342 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 27 Apr 2023 14:22:45 -0700 Subject: [PATCH 1126/2222] add work-now to the build --- .../init/dfhack.control-panel-system.init | 1 + docs/changelog.txt | 1 + docs/plugins/work-now.rst | 15 +-- plugins/work-now.cpp | 92 ++++++++----------- 4 files changed, 44 insertions(+), 65 deletions(-) diff --git a/data/dfhack-config/init/dfhack.control-panel-system.init b/data/dfhack-config/init/dfhack.control-panel-system.init index 8b1431373..c1ad6a679 100644 --- a/data/dfhack-config/init/dfhack.control-panel-system.init +++ b/data/dfhack-config/init/dfhack.control-panel-system.init @@ -2,3 +2,4 @@ # Please use gui/control-panel to edit this file enable faststart +enable work-now diff --git a/docs/changelog.txt b/docs/changelog.txt index 66904aa6d..a6d72d4ce 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,6 +34,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins +- `work-now`: reduce the time that dwarves are left without a task after completing a job ## Fixes - `autoclothing`: eliminate game lag when there are many inventory items in the fort diff --git a/docs/plugins/work-now.rst b/docs/plugins/work-now.rst index 7578500e8..517378d56 100644 --- a/docs/plugins/work-now.rst +++ b/docs/plugins/work-now.rst @@ -6,17 +6,12 @@ work-now :tags: fort auto jobs After finishing a job, dwarves will wander away for a while before picking up a -new job. This plugin will automatically poke the game to assign dwarves to new -tasks. +new job. This plugin will automatically poke them to pick up a new task quicker. Usage ----- -``workNow`` - Print current plugin status. -``workNow 0`` - Stop monitoring and poking. -``workNow 1`` - Poke the game to assign dwarves to tasks whenever the game is paused. -``workNow 2`` - Poke the game to assign dwarves to tasks whenever a dwarf finishes a job. +:: + + enable work-now + work-now [status] diff --git a/plugins/work-now.cpp b/plugins/work-now.cpp index 2286fee7e..bce2d9a73 100644 --- a/plugins/work-now.cpp +++ b/plugins/work-now.cpp @@ -1,92 +1,74 @@ -#include "Core.h" -#include "Console.h" -#include "Export.h" +#include "Debug.h" #include "PluginManager.h" -#include "DataDefs.h" #include "modules/EventManager.h" -#include "modules/World.h" -#include "df/global_objects.h" - -#include - -using namespace std; +using std::string; +using std::vector; using namespace DFHack; -DFHACK_PLUGIN("workNow"); +DFHACK_PLUGIN("work-now"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); REQUIRE_GLOBAL(process_jobs); REQUIRE_GLOBAL(process_dig); -static int mode = 0; +namespace DFHack { + DBG_DECLARE(worknow, log, DebugCategory::LINFO); +} -DFhackCExport command_result workNow(color_ostream& out, vector& parameters); +DFhackCExport command_result work_now(color_ostream& out, vector& parameters); -void jobCompletedHandler(color_ostream& out, void* ptr); +static void jobCompletedHandler(color_ostream& out, void* ptr); EventManager::EventHandler handler(jobCompletedHandler,1); DFhackCExport command_result plugin_init(color_ostream& out, std::vector &commands) { - if (!process_jobs || !process_dig) - return CR_FAILURE; - commands.push_back(PluginCommand( - "workNow", + "work-now", "Reduce the time that dwarves idle after completing a job.", - workNow)); + work_now)); return CR_OK; } -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { - mode = 0; +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (enable != is_enabled) + { + if (enable) + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handler, plugin_self); + else + EventManager::unregister(EventManager::EventType::JOB_COMPLETED, handler, plugin_self); + + is_enabled = enable; + } + return CR_OK; } +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { + return plugin_enable(out, false); +} + DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event e) { - if ( !mode ) - return CR_OK; - if ( e == DFHack::SC_WORLD_UNLOADED ) { - mode = 0; - return CR_OK; + if (e == SC_PAUSED) { + DEBUG(log,out).print("game paused; poking idlers\n"); + *process_jobs = true; + *process_dig = true; } - if ( e != DFHack::SC_PAUSED ) - return CR_OK; - - *process_jobs = true; - *process_dig = true; return CR_OK; } -DFhackCExport command_result workNow(color_ostream& out, vector& parameters) { - if ( parameters.size() == 0 ) { - out.print("workNow status = %d\n", mode); +DFhackCExport command_result work_now(color_ostream& out, vector& parameters) { + if (parameters.empty() || parameters[0] == "status") { + out.print("work_now is %sactively poking idle dwarves.\n", is_enabled ? "" : "not "); return CR_OK; } - if ( parameters.size() > 1 ) { - return CR_WRONG_USAGE; - } - int32_t a = atoi(parameters[0].c_str()); - - if (a < 0 || a > 2) - return CR_WRONG_USAGE; - if ( a == 2 && mode != 2 ) { - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handler, plugin_self); - } else if ( mode == 2 && a != 2 ) { - EventManager::unregister(EventManager::EventType::JOB_COMPLETED, handler, plugin_self); - } - - mode = a; - out.print("workNow status = %d\n", mode); - - return CR_OK; + return CR_WRONG_USAGE; } -void jobCompletedHandler(color_ostream& out, void* ptr) { - if ( mode < 2 ) - return; - +static void jobCompletedHandler(color_ostream& out, void* ptr) { + DEBUG(log,out).print("job completed; poking idlers\n"); *process_jobs = true; *process_dig = true; } From 3dafdf8f24d0ab9a5e3700ec41f0334548dc7e63 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 27 Apr 2023 14:29:50 -0700 Subject: [PATCH 1127/2222] absorb old sphinx anchor --- docs/plugins/work-now.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/plugins/work-now.rst b/docs/plugins/work-now.rst index 517378d56..5eddf7c9f 100644 --- a/docs/plugins/work-now.rst +++ b/docs/plugins/work-now.rst @@ -1,3 +1,5 @@ +.. _worknow: + work-now ======== From 6b7c90b676ec51001a59da1388e3fa8e35ca52bf Mon Sep 17 00:00:00 2001 From: John Cosker Date: Thu, 27 Apr 2023 22:37:56 -0400 Subject: [PATCH 1128/2222] Working C++ and refactors --- plugins/design.cpp | 423 +++++++++++++++++------------------------ plugins/lua/design.lua | 12 +- 2 files changed, 186 insertions(+), 249 deletions(-) diff --git a/plugins/design.cpp b/plugins/design.cpp index 25f876d3e..88d40a630 100644 --- a/plugins/design.cpp +++ b/plugins/design.cpp @@ -11,6 +11,7 @@ #include "PluginManager.h" #include "df/graphic_viewportst.h" #include "df/world.h" +#include "modules/Gui.h" #include "modules/Persistence.h" #include "modules/Screen.h" #include "modules/World.h" @@ -49,8 +50,10 @@ DFhackCExport command_result plugin_init(color_ostream &out, DEBUG(status, out).print("initializing %s\n", plugin_name); // provide a configuration interface for the plugin - commands.push_back( - PluginCommand(plugin_name, "Designs stuff TBD", do_command)); + commands.push_back(PluginCommand( + plugin_name, + "Plugin to handle performance sensitive functions of gui/design", + do_command)); return CR_OK; } @@ -152,150 +155,77 @@ static command_result do_command(color_ostream &out, static int32_t do_cycle(color_ostream &out, bool force_designate) { return 0; } -// Assuming the existence of a class named Point, similar to the one in Lua -class Point { - public: - int x; - int y; +std::map PENS; - Point(int x, int y) : x(x), y(y) {} +struct DrawingPoint { + uint32_t penKey = 0; + std::pair cursor_coords; - bool operator==(const Point &other) const { - return x == other.x && y == other.y; - } + DrawingPoint() : penKey(0), cursor_coords({-1, -1}) {} }; -// Assuming the existence of a class named Color, similar to the one in Lua -class Color { - public: - // Define your color values here -}; +typedef std::map> ShapeMap; +ShapeMap arr; -// Assuming the existence of a class named Pen, similar to the one in Lua -class Pen { - public: - std::string ch; - int tile; - color_value fg; - // Define your pen properties and methods here +bool has_point(int x, int y) { + return arr.count(x) != 0 && arr.at(x).count(y) != 0; }; -class Design { - public: - std::map PENS; - - enum PEN_MASK { - NORTH = 0, - SOUTH, - EAST, - WEST, - DRAG_POINT, - MOUSEOVER, - INSHAPE, - EXTRA_POINT, - NUM_FLAGS - }; - - // Define the function similar to the Lua version - - uint32_t gen_pen_key(bool n, bool s, bool e, bool w, bool is_corner, - bool is_mouse_over, bool inshape, bool extra_point) { - std::bitset ret; - ret[NORTH] = n; - ret[SOUTH] = s; - ret[EAST] = e; - ret[WEST] = w; - ret[DRAG_POINT] = is_corner; - ret[MOUSEOVER] = is_mouse_over; - ret[INSHAPE] = inshape; - ret[EXTRA_POINT] = extra_point; - - return static_cast(ret.to_ulong()); - } - - Pen Design::get_pen(int x, int y, - const std::map> &arr); - // Define the function similar to the Lua version +// Key tuple is N, W, E, S +typedef std::tuple DirectionKey; +std::map> CURSORS_MAP = { + {{false, false, false, false}, {1, 2}}, // INSIDE + {{true, true, false, false}, {0, 1}}, // NW + {{true, false, false, false}, {1, 1}}, // NORTH + {{true, false, true, false}, {2, 1}}, // NE + {{false, true, false, false}, {0, 2}}, // WEST + {{false, false, true, false}, {2, 2}}, // EAST + {{false, true, false, true}, {0, 3}}, // SW + {{false, false, false, true}, {1, 3}}, // SOUTH + {{false, false, true, true}, {2, 3}}, // SE + {{true, true, true, false}, {3, 2}}, // N_NUB + {{true, false, true, true}, {5, 1}}, // E_NUB + {{true, true, false, true}, {3, 1}}, // W_NUB + {{false, true, true, true}, {4, 2}}, // S_NUB + {{false, true, true, false}, {3, 3}}, // VERT_NS + {{true, false, false, true}, {4, 1}}, // VERT_EW + {{true, true, true, true}, {4, 3}}, // POINT }; -Design design; - -// Add other methods and member variables needed for the class - -static int design_getPen(lua_State *L) { - std::map> arr; - if (lua_istable(L, -1)) { - // Iterate over the outer table - lua_pushnil(L); // First key - while (lua_next(L, -2) != 0) { - int x = lua_tointeger(L, -2); // Convert key to an integer - - if (lua_istable(L, -1)) { - // Iterate over the inner table - lua_pushnil(L); // First key - while (lua_next(L, -2) != 0) { - int y = lua_tointeger(L, -2); // Convert key to an integer - bool value = lua_toboolean(L, -1); - - if (value) { - if (arr.count(x) == 0) arr[x] = {}; - arr[x][y] = value; - } - lua_pop(L, 1); // Remove value, keep the key for the next iteration - } - } - lua_pop(L, 1); // Remove inner table, keep the key for the next iteration - } - } +enum PenMask { + North = 0, + South, + East, + West, + DragPoint, + MouseOver, + InShape, + ExtraPoint, + NumFlags +}; - for (auto x : arr) { - for (auto y : x.second) { - Screen::Pen cur_tile = Screen::readTile(x.first, y.first, true); - // cur_tile.tile = selected_tile_texpos; - Pen pen = design.get_pen(x.first, y.first, arr); - cur_tile.tile = pen.tile; - Screen::paintTile(cur_tile, x.first - *window_x, y.first - *window_y, - true); - } - } - return 0; +uint32_t gen_pen_key(bool n, bool s, bool e, bool w, bool is_drag_point, + bool is_mouse_over, bool inshape, bool extra_point) { + std::bitset(PenMask::NumFlags)> ret; + ret[PenMask::North] = n; + ret[PenMask::South] = s; + ret[PenMask::East] = e; + ret[PenMask::West] = w; + ret[PenMask::DragPoint] = is_drag_point; + ret[PenMask::MouseOver] = is_mouse_over; + ret[PenMask::InShape] = inshape; + ret[PenMask::ExtraPoint] = extra_point; + + return ret.to_ulong(); } -enum class CURSORS { - INSIDE, - NORTH, - N_NUB, - S_NUB, - W_NUB, - E_NUB, - NE, - NW, - WEST, - EAST, - SW, - SOUTH, - SE, - VERT_NS, - VERT_EW, - POINT -}; -std::map> CURSORS_MAP = { - {CURSORS::INSIDE, {1, 2}}, {CURSORS::NORTH, {1, 1}}, - {CURSORS::N_NUB, {3, 2}}, {CURSORS::S_NUB, {4, 2}}, - {CURSORS::W_NUB, {3, 1}}, {CURSORS::E_NUB, {5, 1}}, - {CURSORS::NE, {2, 1}}, {CURSORS::NW, {0, 1}}, - {CURSORS::WEST, {0, 2}}, {CURSORS::EAST, {2, 2}}, - {CURSORS::SW, {0, 3}}, {CURSORS::SOUTH, {1, 3}}, - {CURSORS::SE, {2, 3}}, {CURSORS::VERT_NS, {3, 3}}, - {CURSORS::VERT_EW, {4, 1}}, {CURSORS::POINT, {4, 3}}, -}; -Pen make_pen(const std::pair &direction, bool is_corner, - bool is_mouse_over, bool inshape, bool extra_point) { +Screen::Pen make_pen(const std::pair &direction, bool is_drag_point, + bool is_mouse_over, bool inshape, bool extra_point) { color_value color = COLOR_GREEN; int ycursor_mod = 0; if (!extra_point) { - if (is_corner) { + if (is_drag_point) { color = COLOR_CYAN; ycursor_mod += 6; if (is_mouse_over) { @@ -313,140 +243,141 @@ Pen make_pen(const std::pair &direction, bool is_corner, } } - Pen pen; - pen.ch = inshape ? "X" : "o"; + Screen::Pen pen; + pen.ch = inshape ? 'X' : 'o'; pen.fg = color; int selected_tile_texpos = 0; - Screen::findGraphicsTile("CURSORS", direction.first, direction.second, &selected_tile_texpos); + Screen::findGraphicsTile("CURSORS", direction.first, + direction.second + ycursor_mod, + &selected_tile_texpos); pen.tile = selected_tile_texpos; - // Assuming dfhack.screen.findGraphicsTile is replaced with a custom function - // findGraphicsTile pen.tile = findGraphicsTile("CURSORS", direction.first, - // direction.second + ycursor_mod); - return pen; } -Pen Design::get_pen(int x, int y, - const std::map> &arr) { - auto has_point = [&arr](int _x, int _y) { - return arr.count(_x) != 0 && arr.at(_x).count(_y) != 0 && arr.at(_x).at(_y); - }; - bool get_point = has_point(x, y); - - // Basic shapes are bounded by rectangles and therefore can have corner drag - // points even if they're not real points in the shape if (marks.size() >= - // shape.min_points && shape.basic_shape) { - // Point shape_top_left, shape_bot_right; - // shape.get_point_dims(shape_top_left, shape_bot_right); - - // if (x == shape_top_left.x && y == shape_top_left.y && - // shape.drag_corners.nw) { - // drag_point = true; - // } else if (x == shape_bot_right.x && y == shape_top_left.y && - // shape.drag_corners.ne) { - // drag_point = true; - // } else if (x == shape_top_left.x && y == shape_bot_right.y && - // shape.drag_corners.sw) { - // drag_point = true; - // } else if (x == shape_bot_right.x && y == shape_bot_right.y && - // shape.drag_corners.se) { - // drag_point = true; - // } - // } - - // for (const auto& mark : marks) { - // if (mark == Point(x, y)) { - // drag_point = true; - // } - // } - - // if (mirror_point && *mirror_point == Point(x, y)) { - // drag_point = true; - // } - - // // Check for an extra point - // bool extra_point = false; - // for (const auto& point : extra_points) { - // if (x == point.x && y == point.y) { - // extra_point = true; - // break; - // } - // } - - // // Show center point if both marks are set - // if ((shape.basic_shape && marks.size() == shape.max_points) || - // (!shape.basic_shape && !placing_mark.active && !marks.empty())) { - // int center_x, center_y; - // shape.get_center(center_x, center_y); - - // if (x == center_x && y == center_y) { - // extra_point = true; - // } - // } +Screen::Pen get_pen(int x, int y, ShapeMap &arr, const std::string &type = "") { bool n = false, w = false, e = false, s = false; - if (get_point) { + if (has_point(x, y)) { if (y == 0 || !has_point(x, y - 1)) n = true; if (x == 0 || !has_point(x - 1, y)) w = true; - if (!has_point(x + 1, y)) e = true; - if (!has_point(x, y + 1)) s = true; + if (!has_point(x + 1, y)) e = true; // TODO check map size + if (!has_point(x, y + 1)) s = true; // TODO check map size } - // DEBUG(status).print("jcosker %d %d %d %d\n", n, s, e, w); - // Get the bit field to use as a key for the PENS map - uint32_t pen_key = gen_pen_key(n, s, e, w, false, false, get_point, false); - // DEBUG(status).print("jcosker %zu\n", pen_key); + bool is_drag_point = type == "drag_point"; + bool is_extra = type == "extra_point"; + bool is_in_shape = has_point(x, y); + auto mouse_pos = Gui::getMousePos(); + bool mouse_over = mouse_pos.x == x && mouse_pos.y == y; + + uint32_t pen_key = + gen_pen_key(n, s, e, w, is_drag_point, mouse_over, is_in_shape, is_extra); + + if (CURSORS_MAP.count({n, w, e, s}) > 0 && has_point(x,y)) { + arr[x][y].cursor_coords = CURSORS_MAP.at({n, w, e, s}); + } if (PENS.find(pen_key) == PENS.end()) { std::pair cursor{-1, -1}; - // int cursor = -1; // Assuming -1 is an invalid cursor value - - // Determine the cursor to use based on the input parameters - // The CURSORS enum or equivalent should be defined in your code - if (get_point && !n && !w && !e && !s) - cursor = CURSORS_MAP.at(CURSORS::INSIDE); - else if (get_point && n && w && !e && !s) - cursor = CURSORS_MAP.at(CURSORS::NW); - else if (get_point && n && !w && !e && !s) - cursor = CURSORS_MAP.at(CURSORS::NORTH); - else if (get_point && n && e && !w && !s) - cursor = CURSORS_MAP.at(CURSORS::NE); - else if (get_point && !n && w && !e && !s) - cursor = CURSORS_MAP.at(CURSORS::WEST); - else if (get_point && !n && !w && e && !s) - cursor = CURSORS_MAP.at(CURSORS::EAST); - else if (get_point && !n && w && !e && s) - cursor = CURSORS_MAP.at(CURSORS::SW); - else if (get_point && !n && !w && !e && s) - cursor = CURSORS_MAP.at(CURSORS::SOUTH); - else if (get_point && !n && !w && e && s) - cursor = CURSORS_MAP.at(CURSORS::SE); - else if (get_point && n && w && e && !s) - cursor = CURSORS_MAP.at(CURSORS::N_NUB); - else if (get_point && n && !w && e && s) - cursor = CURSORS_MAP.at(CURSORS::E_NUB); - else if (get_point && n && w && !e && s) - cursor = CURSORS_MAP.at(CURSORS::W_NUB); - else if (get_point && !n && w && e && s) - cursor = CURSORS_MAP.at(CURSORS::S_NUB); - else if (get_point && !n && w && e && !s) - cursor = CURSORS_MAP.at(CURSORS::VERT_NS); - else if (get_point && n && !w && !e && s) - cursor = CURSORS_MAP.at(CURSORS::VERT_EW); - else if (get_point && n && w && e && s) - cursor = CURSORS_MAP.at(CURSORS::POINT); - // else if (drag_point && !get_point) cursor = CURSORS::INSIDE; - // else if (extra_point) cursor = CURSORS::INSIDE; - // Create the pen if the cursor is set - - DEBUG(status).print("jcosker %d, %d\n", cursor.first, cursor.second); - if (cursor.first != -1) { - PENS[pen_key] = make_pen(cursor, false, false, get_point, false); + + if (type != "") { + return make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, mouse_over, + is_in_shape, is_extra); + } + + if (CURSORS_MAP.count({n, w, e, s}) > 0) { + PENS.emplace(pen_key, + make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, + mouse_over, is_in_shape, is_extra)); + if (type == "" && has_point(x,y)) { + arr[x][y].penKey = pen_key; + } } } - // Return the pen for the caller + // DEBUG(status).print("not cached lmao\n"); return PENS.at(pen_key); } -DFHACK_PLUGIN_LUA_COMMANDS{DFHACK_LUA_COMMAND(design_getPen), DFHACK_LUA_END}; +static int design_load_shape(lua_State *L) { + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int x = lua_tointeger(L, -2); + + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int y = lua_tointeger(L, -2); + bool value = lua_toboolean(L, -1); + + if (value) { + arr[x][y] = DrawingPoint(); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + } + } + + return 0; +} + +static int design_clear_shape(lua_State *L) { + arr.clear(); + + return 0; +} + +static int design_draw_shape(lua_State *L) { + if (arr.size() == 0) { + design_load_shape(L); + } + + for (auto x : arr) { + for (auto y : x.second) { + Screen::Pen cur_tile = Screen::readTile(x.first, y.first, true); + Screen::Pen pen = get_pen(x.first, y.first, arr); + cur_tile.tile = pen.tile; + Screen::paintTile(cur_tile, x.first - *window_x, y.first - *window_y, + true); + } + } + + return 0; +} + +static int design_draw_points(lua_State *L) { + if (lua_istable(L, -1)) { + const char *str; + lua_rawgeti(L, -1, 2); + str = lua_tostring(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 1); + int n = luaL_len(L, -1); + for (int i = 1; i <= n; i++) { + lua_rawgeti(L, -1, i); + int x, y; + lua_getfield(L, -1, "y"); + y = lua_tointeger(L, -1); + lua_getfield(L, -2, "x"); + x = lua_tointeger(L, -1); + lua_pop(L, 3); + + Screen::Pen cur_tile = Screen::readTile(x, y, true); + Screen::Pen pen = get_pen(x, y, arr, str); + cur_tile.tile = pen.tile; + Screen::paintTile(cur_tile, x - *window_x, y - *window_y, true); + } + lua_pop(L, 1); + } + + return 0; +} + +DFHACK_PLUGIN_LUA_COMMANDS{DFHACK_LUA_COMMAND(design_draw_shape), + DFHACK_LUA_COMMAND(design_draw_points), + DFHACK_LUA_COMMAND(design_clear_shape), + DFHACK_LUA_END}; diff --git a/plugins/lua/design.lua b/plugins/lua/design.lua index fe916aa41..eb9dd3d9a 100644 --- a/plugins/lua/design.lua +++ b/plugins/lua/design.lua @@ -1,9 +1,15 @@ local _ENV = mkmodule('plugins.design') -view2 = {design_window = { name = "hello"}} +function draw_shape(arr) + design_draw_shape(arr) +end + +function draw_points(points_obj) + design_draw_points(points_obj) +end -function getPen(hi) - design_getPen(hi) +function clear_shape(arr) + design_clear_shape(arr) end return _ENV From f61d25bdebc3ee2b93eaf78d0e2102139a3bf907 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 29 Apr 2023 18:46:13 -0700 Subject: [PATCH 1129/2222] bump to 50.08-r1 --- CMakeLists.txt | 6 +++--- library/xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6b5d2949..4228f45cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,9 +191,9 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl endif() # set up versioning. -set(DF_VERSION "50.08b1") -set(DFHACK_RELEASE "beta4") -set(DFHACK_PRERELEASE TRUE) +set(DF_VERSION "50.08") +set(DFHACK_RELEASE "r1") +set(DFHACK_PRERELEASE FALSE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/library/xml b/library/xml index 98d5f8a55..113f5e40b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 98d5f8a5553690ef71b9650b28d4aababf21ef5e +Subproject commit 113f5e40b471c67b5414380f477d1c7be9b635ee From 923f84b2f296c6a6f4fa00af37766b05fe3485e8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 29 Apr 2023 18:58:34 -0700 Subject: [PATCH 1130/2222] bump to 50.08-r1 --- docs/changelog.txt | 20 ++++++++++++++++---- library/xml | 2 +- scripts | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 66904aa6d..e3ec3ca40 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -35,6 +35,22 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Lua + +## Removed + +# 50.08-r1 + +## New Plugins + ## Fixes - `autoclothing`: eliminate game lag when there are many inventory items in the fort - `buildingplan`: fixed size limit calculations for rollers @@ -51,10 +67,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running). - `overlay`: add the DFHack version string to the DF title screen -## Documentation - -## API - ## Lua - ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget - ``gui.ZScreenModal``: ZScreen subclass for modal dialogs diff --git a/library/xml b/library/xml index 113f5e40b..6a620feac 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 113f5e40b471c67b5414380f477d1c7be9b635ee +Subproject commit 6a620feacc85224742d8a97f633c9106ff7d81e8 diff --git a/scripts b/scripts index ad1998a00..70788484b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit ad1998a0032ce50e90d05429a4178b668c0840ba +Subproject commit 70788484b4cf797aa71e5c6737cb77838407a677 From ffa9f79f941e1e58f591f9d45d715e7114c40a50 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 29 Apr 2023 22:30:42 -0700 Subject: [PATCH 1131/2222] rightsize ccache, protect steam sdk from eviction --- .github/workflows/build.yml | 8 ++++++++ .github/workflows/steam.yml | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f03d8b3b0..39be29f9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,12 @@ jobs: # - name: Download DF # run: | # sh ci/download-df.sh + - name: Restore steam SDK + uses: actions/cache@v3 + with: + path: depends/steam + key: steam-sdk-156 + enableCrossOsArchive: true - name: Configure DFHack env: CC: gcc-${{ matrix.gcc }} @@ -94,6 +100,8 @@ jobs: - name: Build DFHack run: | ninja -C build-ci install + ccache --max-size 100M + ccache --cleanup ccache --show-stats - name: Run cpp unit tests id: run_tests_cpp diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 33aa16d34..4c275e032 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -14,8 +14,8 @@ on: release_channel: description: Release channel type: string - required: false - default: beta + required: true + default: staging jobs: deploy-to-steam: @@ -34,12 +34,12 @@ jobs: path: build/win64-cross/ccache key: ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} restore-keys: | - ccache-win64-cross-msvc-develop-${{ github.event.inputs.commit_hash }} + ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} ccache-win64-cross-msvc - name: Restore steam SDK uses: actions/cache@v3 with: - path: depends/steam/steamworks_sdk_156.zip + path: depends/steam key: steam-sdk-156 enableCrossOsArchive: true - name: Cross-compile win64 artifacts From b69aff4d6eed64fe244255c7134ca65b54a3047a Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 29 Apr 2023 23:19:02 -0700 Subject: [PATCH 1132/2222] reduce cache for msvc builds too; remove ruby --- .github/workflows/build.yml | 18 ++++-------------- .github/workflows/steam.yml | 3 +++ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39be29f9c..034c8c7cf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,7 +100,7 @@ jobs: - name: Build DFHack run: | ninja -C build-ci install - ccache --max-size 100M + ccache --max-size 200M ccache --cleanup ccache --show-stats - name: Run cpp unit tests @@ -155,6 +155,9 @@ jobs: run: | cd build bash -x build-win64-from-linux.sh + ccache -d build/win64-cross/ccache --max-size 200M + ccache -d build/win64-cross/ccache --cleanup + ccache -d build/win64-cross/ccache --show-stats - name: Format artifact name id: artifactname run: | @@ -182,11 +185,6 @@ jobs: - name: Build docs run: | sphinx-build -W --keep-going -j auto --color . docs/html - - name: Upload docs - uses: actions/upload-artifact@v1 - with: - name: docs - path: docs/html lint: runs-on: ubuntu-22.04 @@ -195,10 +193,6 @@ jobs: uses: actions/setup-python@v4 with: python-version: 3 - - name: Set up Ruby 2.7 - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7 - name: Install Lua run: | sudo apt-get update @@ -223,10 +217,6 @@ jobs: if: success() || failure() run: | python ci/script-syntax.py --ext=lua --cmd="luac5.3 -p" --github-actions - - name: Check Ruby syntax - if: success() || failure() - run: | - python ci/script-syntax.py --ext=rb --cmd="ruby -c" --github-actions check-pr: runs-on: ubuntu-latest diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 4c275e032..057ba3fd5 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -54,6 +54,9 @@ jobs: echo cd build bash -x build-win64-from-linux.sh + ccache -d build/win64-cross/ccache --max-size 200M + ccache -d build/win64-cross/ccache --cleanup + ccache -d build/win64-cross/ccache --show-stats - name: Steam deploy uses: game-ci/steam-deploy@v2 with: From 17a423bcd2eaae7c823e94e94da295597e32824d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 29 Apr 2023 23:31:44 -0700 Subject: [PATCH 1133/2222] ensure msvc builds have ccache --- .github/workflows/build.yml | 4 ++++ .github/workflows/steam.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 034c8c7cf..a8495cf8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -136,6 +136,10 @@ jobs: name: Build MSVC win64 runs-on: ubuntu-22.04 steps: + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install ccache - name: Clone DFHack uses: actions/checkout@v3 with: diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 057ba3fd5..dda9b1618 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -22,6 +22,10 @@ jobs: name: Deploy to Steam runs-on: ubuntu-22.04 steps: + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install ccache - name: Clone DFHack uses: actions/checkout@v3 with: From 7aa5692fef32fa88dff776f801db90cb091d7db8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 29 Apr 2023 23:46:06 -0700 Subject: [PATCH 1134/2222] reduce linux ccache size; fix msvc ccache path --- .github/workflows/build.yml | 8 ++++---- .github/workflows/steam.yml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8495cf8a..131c21743 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,7 +100,7 @@ jobs: - name: Build DFHack run: | ninja -C build-ci install - ccache --max-size 200M + ccache --max-size 50M ccache --cleanup ccache --show-stats - name: Run cpp unit tests @@ -159,9 +159,9 @@ jobs: run: | cd build bash -x build-win64-from-linux.sh - ccache -d build/win64-cross/ccache --max-size 200M - ccache -d build/win64-cross/ccache --cleanup - ccache -d build/win64-cross/ccache --show-stats + ccache -d win64-cross/ccache --max-size 200M + ccache -d win64-cross/ccache --cleanup + ccache -d win64-cross/ccache --show-stats - name: Format artifact name id: artifactname run: | diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index dda9b1618..5210b3a11 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -58,9 +58,9 @@ jobs: echo cd build bash -x build-win64-from-linux.sh - ccache -d build/win64-cross/ccache --max-size 200M - ccache -d build/win64-cross/ccache --cleanup - ccache -d build/win64-cross/ccache --show-stats + ccache -d win64-cross/ccache --max-size 200M + ccache -d win64-cross/ccache --cleanup + ccache -d win64-cross/ccache --show-stats - name: Steam deploy uses: game-ci/steam-deploy@v2 with: From cfcb1cd937fc2f513ffde0a81ab2357bce517cb5 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Sun, 30 Apr 2023 10:18:15 +0200 Subject: [PATCH 1135/2222] make val_sep useful if label_below == true --- docs/dev/Lua API.rst | 4 ++-- library/lua/gui/widgets.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 37d85f7f6..8ebb752c9 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4918,11 +4918,11 @@ It has the following attributes: hotkey. :label_width: The number of spaces to allocate to the ``label`` (for use in aligning a column of ``CycleHotkeyLabel`` labels). -:label_below: If ``true``, then the option value will apear below the label +:label_below: If ``true``, then the option value will appear below the label instead of to the right of it. Defaults to ``false``. :val_gap: The size of the gap between the label text and the option value. Default is ``1``. If set to ``0``, there'll be no gap between the strings. - Note that ``val_gap`` is ignored if ``label_below`` is set to ``true``. + If ``label_below`` == ``true``, negative values will shift the value leftwards. :options: A list of strings or tables of ``{label=string or fn, value=val[, pen=pen]}``. String options use the same string for the label and value and use the default pen. The optional ``pen`` diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index c7cde698c..210b71f7b 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1502,7 +1502,7 @@ function CycleHotkeyLabel:init() self:setOption(self.initial_option) if self.label_below then - self.val_gap = 0 + (self.key_back and 1 or 0) + (self.key and 3 or 0) + self.val_gap = self.val_gap + (self.key_back and 1 or 0) + (self.key and 2 or 0) end self:setText{ From 0d6c5869f49f7cdec4443c0754c6fdc45e27182b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 30 Apr 2023 16:40:03 -0700 Subject: [PATCH 1136/2222] fix logo disappearing when hovered on title screen --- docs/changelog.txt | 1 + plugins/lua/overlay.lua | 25 +++++++++++++------------ plugins/overlay.cpp | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index e3ec3ca40..d0855dbc8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `hotkeys`: DFHack logo no longer disappears when hovered on the title screen ## Misc Improvements diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index b1960989f..84c2b4f96 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -447,7 +447,7 @@ function update_hotspot_widgets() end end -local function matches_focus_strings(db_entry, vs_name) +local function matches_focus_strings(db_entry, vs_name, vs) if not db_entry.focus_strings then return true end local matched = true local simple_vs_name = simplify_viewscreen_name(vs_name) @@ -465,9 +465,10 @@ end local function _update_viewscreen_widgets(vs_name, vs, now_ms) local vs_widgets = active_viewscreen_widgets[vs_name] if not vs_widgets then return end + local is_all = vs_name == 'all' now_ms = now_ms or dfhack.getTickCount() for name,db_entry in pairs(vs_widgets) do - if matches_focus_strings(db_entry, vs_name) and + if (is_all or matches_focus_strings(db_entry, vs_name, vs)) and do_update(name, db_entry, now_ms, vs) then return end @@ -483,12 +484,12 @@ function update_viewscreen_widgets(vs_name, vs) end end -local function _feed_viewscreen_widgets(vs_name, keys) +local function _feed_viewscreen_widgets(vs_name, vs, keys) local vs_widgets = active_viewscreen_widgets[vs_name] if not vs_widgets then return false end for _,db_entry in pairs(vs_widgets) do local w = db_entry.widget - if matches_focus_strings(db_entry, vs_name) and + if (not vs or matches_focus_strings(db_entry, vs_name, vs)) and detect_frame_change(w, function() return w:onInput(keys) end) then return true end @@ -496,9 +497,9 @@ local function _feed_viewscreen_widgets(vs_name, keys) return false end -function feed_viewscreen_widgets(vs_name, keys) - if not _feed_viewscreen_widgets(vs_name, keys) and - not _feed_viewscreen_widgets('all', keys) then +function feed_viewscreen_widgets(vs_name, vs, keys) + if not _feed_viewscreen_widgets(vs_name, vs, keys) and + not _feed_viewscreen_widgets('all', nil, keys) then return false end gui.markMouseClicksHandled(keys) @@ -508,13 +509,13 @@ function feed_viewscreen_widgets(vs_name, keys) return true end -local function _render_viewscreen_widgets(vs_name, dc) +local function _render_viewscreen_widgets(vs_name, vs, dc) local vs_widgets = active_viewscreen_widgets[vs_name] if not vs_widgets then return end dc = dc or gui.Painter.new() for _,db_entry in pairs(vs_widgets) do local w = db_entry.widget - if matches_focus_strings(db_entry, vs_name) then + if not vs or matches_focus_strings(db_entry, vs_name, vs) then detect_frame_change(w, function() w:render(dc) end) end end @@ -523,9 +524,9 @@ end local force_refresh -function render_viewscreen_widgets(vs_name) - local dc = _render_viewscreen_widgets(vs_name, nil) - _render_viewscreen_widgets('all', dc) +function render_viewscreen_widgets(vs_name, vs) + local dc = _render_viewscreen_widgets(vs_name, vs, nil) + _render_viewscreen_widgets('all', nil, dc) if force_refresh then force_refresh = nil df.global.gps.force_full_display_count = 1 diff --git a/plugins/overlay.cpp b/plugins/overlay.cpp index 8e27c8308..c02c960ba 100644 --- a/plugins/overlay.cpp +++ b/plugins/overlay.cpp @@ -72,9 +72,10 @@ struct viewscreen_overlay : T { bool input_is_handled = false; // don't send input to the overlays if there is a modal dialog up if (!world->status.popups.size()) - call_overlay_lua(NULL, "feed_viewscreen_widgets", 2, 1, + call_overlay_lua(NULL, "feed_viewscreen_widgets", 3, 1, [&](lua_State *L) { Lua::Push(L, T::_identity.getName()); + Lua::Push(L, this); Lua::PushInterfaceKeys(L, *input); }, [&](lua_State *L) { input_is_handled = lua_toboolean(L, -1); From 3f51f24f9161ecc3417b52acc6244822e9f0a44b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 30 Apr 2023 17:12:43 -0700 Subject: [PATCH 1137/2222] fix errors when dragging a scrollbar and the mouse leaves the window --- library/lua/gui/widgets.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index c7cde698c..7eeb00f1f 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -836,7 +836,9 @@ function Scrollbar:update(top_elem, elems_per_page, num_elems) end local function scrollbar_do_drag(scrollbar) - local _,y = scrollbar.frame_body:localXY(dfhack.screen.getMousePos()) + local x,y = dfhack.screen.getMousePos() + if not y then return end + x,y = scrollbar.frame_body:localXY(x, y) local cur_pos = y - scrollbar.is_dragging local max_top = scrollbar.num_elems - scrollbar.elems_per_page + 1 local max_pos = scrollbar_get_max_pos_and_height(scrollbar) From 528dc466e2d2f6e8114282c7d23eb2e0090a66e1 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 1 May 2023 14:20:53 -0400 Subject: [PATCH 1138/2222] address review comments --- plugins/CMakeLists.txt | 2 +- plugins/design.cpp | 122 ++--------------------------------------- plugins/lua/design.lua | 12 ---- 3 files changed, 6 insertions(+), 130 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index c880a89ff..943bc311c 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -77,8 +77,8 @@ set_source_files_properties( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) #dfhack_plugin(add-spatter add-spatter.cpp) dfhack_plugin(autobutcher autobutcher.cpp LINK_LIBRARIES lua) dfhack_plugin(autochop autochop.cpp LINK_LIBRARIES lua) +dfhack_plugin(autoclothing autoclothing.cpp LINK_LIBRARIES lua) dfhack_plugin(design design.cpp LINK_LIBRARIES lua) -dfhack_plugin(autoclothing autoclothing.cpp) dfhack_plugin(autodump autodump.cpp) dfhack_plugin(autofarm autofarm.cpp) #dfhack_plugin(autogems autogems.cpp LINK_LIBRARIES jsoncpp_static) diff --git a/plugins/design.cpp b/plugins/design.cpp index 88d40a630..1abd61842 100644 --- a/plugins/design.cpp +++ b/plugins/design.cpp @@ -30,131 +30,19 @@ using namespace df::enums; enum ConfigValues { CONFIG_IS_ENABLED = 0, }; -namespace DFHack { -// // for configuration-related logging -DBG_DECLARE(design, status, DebugCategory::LDEBUG); -// for logging during the periodic scan -DBG_DECLARE(design, cycle, DebugCategory::LDEBUG); -} // namespace DFHack -static const std::string CONFIG_KEY = std::string(plugin_name) + "/config"; -static PersistentDataItem config; -static const int32_t CYCLE_TICKS = 1200; -static int32_t cycle_timestamp = 0; // world->frame_counter at last cycle - -static command_result do_command(color_ostream &out, - std::vector ¶meters); -static int32_t do_cycle(color_ostream &out, bool force_designate = false); - -DFhackCExport command_result plugin_init(color_ostream &out, - std::vector &commands) { - DEBUG(status, out).print("initializing %s\n", plugin_name); - - // provide a configuration interface for the plugin - commands.push_back(PluginCommand( - plugin_name, - "Plugin to handle performance sensitive functions of gui/design", - do_command)); - - return CR_OK; -} -static int get_config_val(PersistentDataItem &c, int index) { - if (!c.isValid()) return -1; - return c.ival(index); -} - -static bool get_config_bool(PersistentDataItem &c, int index) { - return get_config_val(c, index) == 1; -} - -static void set_config_val(PersistentDataItem &c, int index, int value) { - if (c.isValid()) c.ival(index) = value; +namespace DFHack { + DBG_DECLARE(pathable, log, DebugCategory::LINFO); } -static void set_config_bool(PersistentDataItem &c, int index, bool value) { - set_config_val(c, index, value ? 1 : 0); -} - -DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { - if (!Core::getInstance().isWorldLoaded()) { - out.printerr("Cannot enable %s without a loaded world.\n", plugin_name); - - return CR_FAILURE; - } - - if (enable != is_enabled) { - is_enabled = enable; - DEBUG(status, out) - .print("%s from the API; persisting\n", - is_enabled ? "enabled" : "disabled"); - set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); - if (enable) do_cycle(out, true); - } else { - DEBUG(status, out) - .print("%s from the API, but already %s; no action\n", - is_enabled ? "enabled" : "disabled", - is_enabled ? "enabled" : "disabled"); - } - return CR_OK; +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { + return CR_OK; } DFhackCExport command_result plugin_shutdown(color_ostream &out) { - DEBUG(status, out).print("shutting down %s\n", plugin_name); - - return CR_OK; + return CR_OK; } -DFhackCExport command_result plugin_load_data(color_ostream &out) { - cycle_timestamp = 0; - config = World::GetPersistentData(CONFIG_KEY); - - if (!config.isValid()) { - DEBUG(status, out).print("no config found in this save; initializing\n"); - config = World::AddPersistentData(CONFIG_KEY); - set_config_bool(config, CONFIG_IS_ENABLED, is_enabled); - } - - // we have to copy our enabled flag into the global plugin variable, but - // all the other state we can directly read/modify from the persistent - // data structure. - is_enabled = get_config_bool(config, CONFIG_IS_ENABLED); - DEBUG(status, out) - .print("loading persisted enabled state: %s\n", - is_enabled ? "true" : "false"); - - return CR_OK; -} - -DFhackCExport command_result plugin_onstatechange(color_ostream &out, - state_change_event event) { - if (event == DFHack::SC_WORLD_UNLOADED) { - if (is_enabled) { - DEBUG(status, out).print("world unloaded; disabling %s\n", plugin_name); - is_enabled = false; - } - } - - return CR_OK; -} - -DFhackCExport command_result plugin_onupdate(color_ostream &out) { - if (is_enabled && world->frame_counter - cycle_timestamp >= CYCLE_TICKS) { - int32_t ret = do_cycle(out); - } - - return CR_OK; -} -int selected_tile_texpos = 0; -const static bool hi = - Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos); - -static command_result do_command(color_ostream &out, - std::vector ¶meters) { - return CR_OK; -} - -static int32_t do_cycle(color_ostream &out, bool force_designate) { return 0; } - std::map PENS; struct DrawingPoint { diff --git a/plugins/lua/design.lua b/plugins/lua/design.lua index eb9dd3d9a..ec3b4c79b 100644 --- a/plugins/lua/design.lua +++ b/plugins/lua/design.lua @@ -1,15 +1,3 @@ local _ENV = mkmodule('plugins.design') -function draw_shape(arr) - design_draw_shape(arr) -end - -function draw_points(points_obj) - design_draw_points(points_obj) -end - -function clear_shape(arr) - design_clear_shape(arr) -end - return _ENV From 0eb04f1b73d61752245a4113d89183840c55cfa2 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Mon, 1 May 2023 14:32:09 -0400 Subject: [PATCH 1139/2222] changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 66904aa6d..e1e45f799 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -50,6 +50,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/control-panel`: add preference option for hiding "armok" tools in command lists - ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running). - `overlay`: add the DFHack version string to the DF title screen +- `gui/design`: Improved performance for drawing shapes ## Documentation From ef0c8950bd60818b5fa807609f95fc330cc5e8c0 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Tue, 2 May 2023 16:46:46 +0200 Subject: [PATCH 1140/2222] renamed val_gap to option_gap, updated docs/changelog --- docs/changelog.txt | 2 +- docs/dev/Lua API.rst | 2 +- library/lua/gui/widgets.lua | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index e3ec3ca40..792d09167 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -70,7 +70,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget - ``gui.ZScreenModal``: ZScreen subclass for modal dialogs -- ``widgets.CycleHotkeyLabel``: exposed "key_sep" and "val_gap" attributes for improved stylistic control. +- ``widgets.CycleHotkeyLabel``: exposed "key_sep" and "option_gap" attributes for improved stylistic control. ## Removed - `title-version`: replaced by an `overlay` widget diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 8ebb752c9..85e51d11d 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4920,7 +4920,7 @@ It has the following attributes: aligning a column of ``CycleHotkeyLabel`` labels). :label_below: If ``true``, then the option value will appear below the label instead of to the right of it. Defaults to ``false``. -:val_gap: The size of the gap between the label text and the option value. +:option_gap: The size of the gap between the label text and the option value. Default is ``1``. If set to ``0``, there'll be no gap between the strings. If ``label_below`` == ``true``, negative values will shift the value leftwards. :options: A list of strings or tables of diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 2e1732f11..7aa79cbc3 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1491,7 +1491,7 @@ CycleHotkeyLabel.ATTRS{ key=DEFAULT_NIL, key_back=DEFAULT_NIL, key_sep=': ', - val_gap=1, + option_gap=1, label=DEFAULT_NIL, label_width=DEFAULT_NIL, label_below=false, @@ -1504,7 +1504,7 @@ function CycleHotkeyLabel:init() self:setOption(self.initial_option) if self.label_below then - self.val_gap = self.val_gap + (self.key_back and 1 or 0) + (self.key and 2 or 0) + self.option_gap = self.option_gap + (self.key_back and 1 or 0) + (self.key and 2 or 0) end self:setText{ @@ -1512,7 +1512,7 @@ function CycleHotkeyLabel:init() {key=self.key, key_sep=self.key_sep, text=self.label, width=self.label_width, on_activate=self:callback('cycle')}, self.label_below and NEWLINE or '', - {gap=self.val_gap, text=self:callback('getOptionLabel'), + {gap=self.option_gap, text=self:callback('getOptionLabel'), pen=self:callback('getOptionPen')}, } end From c1a0d1ad8665acbbe87538518545b635050c1b54 Mon Sep 17 00:00:00 2001 From: Myk Date: Tue, 2 May 2023 09:55:35 -0700 Subject: [PATCH 1141/2222] Update changelog.txt --- docs/changelog.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d0855dbc8..e3ec3ca40 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,7 +36,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- `hotkeys`: DFHack logo no longer disappears when hovered on the title screen ## Misc Improvements From d21a6c6432b14a12070387db1f2ca4c2003efba7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 2 May 2023 14:34:45 -0700 Subject: [PATCH 1142/2222] update symbols for 50.08 itch and classic --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 6a620feac..22d9bc0bc 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 6a620feacc85224742d8a97f633c9106ff7d81e8 +Subproject commit 22d9bc0bc1847def8a6c62893104f36262e63e98 From 8b6d20ae9e8e25feed9c851bcb68e041aaf84ece Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 3 May 2023 07:14:37 +0000 Subject: [PATCH 1143/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 70788484b..8c0624942 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 70788484b4cf797aa71e5c6737cb77838407a677 +Subproject commit 8c0624942ed11b369be805b2c0b5bc5ffd14f13d From bebf3584badfd753ed7f9ad5dd70dcf604ee9d94 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 2 May 2023 21:06:46 -0700 Subject: [PATCH 1144/2222] maybe fix terminal in foreground issue --- docs/changelog.txt | 1 + library/Console-windows.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2b800fc42..924adbd31 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- Terminal console no longer appears in front of the game window on startup ## Documentation diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index 515d89911..dd8ce9d84 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -508,6 +508,7 @@ bool Console::init(bool) inited = true; // DOESN'T WORK - locks up DF! // ForceForegroundWindow(d->MainWindow); + SetForegroundWindow(d->MainWindow); return true; } // FIXME: looks awfully empty, doesn't it? @@ -608,6 +609,7 @@ void Console::msleep (unsigned int msec) bool Console::hide() { ShowWindow( GetConsoleWindow(), SW_HIDE ); + SetForegroundWindow(d->MainWindow); return true; } From ad5a0d41f5c4a56d955032c1e456527fe93bb5cc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 3 May 2023 14:34:48 -0700 Subject: [PATCH 1145/2222] only show the terminal if requested --- library/Console-windows.cpp | 3 +-- library/Core.cpp | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index dd8ce9d84..20d943b18 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -508,7 +508,7 @@ bool Console::init(bool) inited = true; // DOESN'T WORK - locks up DF! // ForceForegroundWindow(d->MainWindow); - SetForegroundWindow(d->MainWindow); + hide(); return true; } // FIXME: looks awfully empty, doesn't it? @@ -609,7 +609,6 @@ void Console::msleep (unsigned int msec) bool Console::hide() { ShowWindow( GetConsoleWindow(), SW_HIDE ); - SetForegroundWindow(d->MainWindow); return true; } diff --git a/library/Core.cpp b/library/Core.cpp index f1bb28925..9ff6976d5 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1304,10 +1304,6 @@ static void run_dfhack_init(color_ostream &out, Core *core) return; } - // if we're running on Steam Deck, hide the terminal by default - if (DFSteam::DFIsSteamRunningOnSteamDeck()) - core->getConsole().hide(); - // load baseline defaults core->loadScriptFile(out, CONFIG_PATH + "init/default.dfhack.init", false); @@ -1315,13 +1311,13 @@ static void run_dfhack_init(color_ostream &out, Core *core) std::vector prefixes(1, "dfhack"); loadScriptFiles(core, out, prefixes, CONFIG_PATH + "init"); - // if the option is set, hide the terminal + // show the terminal if requested auto L = Lua::Core::State; Lua::StackUnwinder top(L); Lua::CallLuaModuleFunction(out, L, "dfhack", "getHideConsoleOnStartup", 0, 1, Lua::DEFAULT_LUA_LAMBDA, [&](lua_State* L) { - if (lua_toboolean(L, -1)) - core->getConsole().hide(); + if (!lua_toboolean(L, -1)) + core->getConsole().show(); }, false); } From 8f5c454b398e2c12e223ca083a90444ad0ed5d63 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 3 May 2023 15:52:01 -0700 Subject: [PATCH 1146/2222] add focus strings for new_region --- library/modules/Gui.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 44642b5fa..d03bcce09 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -84,6 +84,7 @@ using namespace DFHack; #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_new_regionst.h" #include "df/viewscreen_titlest.h" #include "df/world.h" @@ -156,6 +157,19 @@ DEFINE_GET_FOCUS_STRING_HANDLER(title) focusStrings.push_back(baseFocus + "/Default"); } +DEFINE_GET_FOCUS_STRING_HANDLER(new_region) +{ + if (screen->doing_mods) + focusStrings.push_back(baseFocus + "/Mods"); + else if (screen->doing_simple_params) + focusStrings.push_back(baseFocus + "/Basic"); + else if (screen->doing_params) + focusStrings.push_back(baseFocus + "/Advanced"); + + if (focusStrings.empty()) + focusStrings.push_back(baseFocus); +} + DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) { std::string newFocusString; From 58e11b01cba07e3721ea1dc795b3f9258354bfc0 Mon Sep 17 00:00:00 2001 From: John Cosker Date: Wed, 3 May 2023 19:37:44 -0400 Subject: [PATCH 1147/2222] Comments --- docs/changelog.txt | 1 + plugins/design.cpp | 313 ++++++++++++++++++++++----------------------- 2 files changed, 157 insertions(+), 157 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a7ce7c25f..0a37bb3b9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -41,6 +41,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - Terminal console no longer appears in front of the game window on startup +- `gui/design`: Improved performance for drawing shapes ## Documentation diff --git a/plugins/design.cpp b/plugins/design.cpp index 1abd61842..37ee32cbd 100644 --- a/plugins/design.cpp +++ b/plugins/design.cpp @@ -17,7 +17,6 @@ #include "modules/World.h" DFHACK_PLUGIN("design"); -DFHACK_PLUGIN_IS_ENABLED(is_enabled); using DFHack::color_value; REQUIRE_GLOBAL(window_x); @@ -27,36 +26,37 @@ REQUIRE_GLOBAL(plotinfo); using namespace DFHack; using namespace df::enums; -enum ConfigValues { - CONFIG_IS_ENABLED = 0, -}; - namespace DFHack { - DBG_DECLARE(pathable, log, DebugCategory::LINFO); +DBG_DECLARE(design, log, DebugCategory::LINFO); } -DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { +DFhackCExport command_result plugin_init(color_ostream &out, + std::vector &commands) { return CR_OK; } -DFhackCExport command_result plugin_shutdown(color_ostream &out) { +std::map PENS; + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { + if (event == DFHack::SC_WORLD_UNLOADED) { + DEBUG(log,out).print("clearing PENS\n"); + PENS.clear(); + } return CR_OK; } -std::map PENS; - struct DrawingPoint { - uint32_t penKey = 0; - std::pair cursor_coords; + uint32_t penKey = 0; + std::pair cursor_coords; - DrawingPoint() : penKey(0), cursor_coords({-1, -1}) {} + DrawingPoint() : penKey(0), cursor_coords({-1, -1}) {} }; typedef std::map> ShapeMap; ShapeMap arr; bool has_point(int x, int y) { - return arr.count(x) != 0 && arr.at(x).count(y) != 0; + return arr.count(x) != 0 && arr.at(x).count(y) != 0; }; // Key tuple is N, W, E, S @@ -81,188 +81,187 @@ std::map> CURSORS_MAP = { }; enum PenMask { - North = 0, - South, - East, - West, - DragPoint, - MouseOver, - InShape, - ExtraPoint, - NumFlags + North = 0, + South, + East, + West, + DragPoint, + MouseOver, + InShape, + ExtraPoint, + NumFlags }; uint32_t gen_pen_key(bool n, bool s, bool e, bool w, bool is_drag_point, bool is_mouse_over, bool inshape, bool extra_point) { - std::bitset(PenMask::NumFlags)> ret; - ret[PenMask::North] = n; - ret[PenMask::South] = s; - ret[PenMask::East] = e; - ret[PenMask::West] = w; - ret[PenMask::DragPoint] = is_drag_point; - ret[PenMask::MouseOver] = is_mouse_over; - ret[PenMask::InShape] = inshape; - ret[PenMask::ExtraPoint] = extra_point; - - return ret.to_ulong(); + std::bitset(PenMask::NumFlags)> ret; + ret[PenMask::North] = n; + ret[PenMask::South] = s; + ret[PenMask::East] = e; + ret[PenMask::West] = w; + ret[PenMask::DragPoint] = is_drag_point; + ret[PenMask::MouseOver] = is_mouse_over; + ret[PenMask::InShape] = inshape; + ret[PenMask::ExtraPoint] = extra_point; + + return ret.to_ulong(); } Screen::Pen make_pen(const std::pair &direction, bool is_drag_point, bool is_mouse_over, bool inshape, bool extra_point) { - color_value color = COLOR_GREEN; - int ycursor_mod = 0; - - if (!extra_point) { - if (is_drag_point) { - color = COLOR_CYAN; - ycursor_mod += 6; - if (is_mouse_over) { - color = COLOR_MAGENTA; - ycursor_mod += 3; - } - } - } else { - ycursor_mod += 15; - color = COLOR_LIGHTRED; + color_value color = COLOR_GREEN; + int ycursor_mod = 0; + + if (!extra_point) { + if (is_drag_point) { + color = COLOR_CYAN; + ycursor_mod += 6; + if (is_mouse_over) { + color = COLOR_MAGENTA; + ycursor_mod += 3; + } + } + } else { + ycursor_mod += 15; + color = COLOR_LIGHTRED; - if (is_mouse_over) { - color = COLOR_RED; - ycursor_mod += 3; + if (is_mouse_over) { + color = COLOR_RED; + ycursor_mod += 3; + } } - } - - Screen::Pen pen; - pen.ch = inshape ? 'X' : 'o'; - pen.fg = color; - int selected_tile_texpos = 0; - Screen::findGraphicsTile("CURSORS", direction.first, - direction.second + ycursor_mod, - &selected_tile_texpos); - pen.tile = selected_tile_texpos; - - return pen; + + Screen::Pen pen; + pen.ch = inshape ? 'X' : 'o'; + pen.fg = color; + int selected_tile_texpos = 0; + Screen::findGraphicsTile("CURSORS", direction.first, + direction.second + ycursor_mod, + &selected_tile_texpos); + pen.tile = selected_tile_texpos; + + return pen; } Screen::Pen get_pen(int x, int y, ShapeMap &arr, const std::string &type = "") { - bool n = false, w = false, e = false, s = false; - if (has_point(x, y)) { - if (y == 0 || !has_point(x, y - 1)) n = true; - if (x == 0 || !has_point(x - 1, y)) w = true; - if (!has_point(x + 1, y)) e = true; // TODO check map size - if (!has_point(x, y + 1)) s = true; // TODO check map size - } - - bool is_drag_point = type == "drag_point"; - bool is_extra = type == "extra_point"; - bool is_in_shape = has_point(x, y); - auto mouse_pos = Gui::getMousePos(); - bool mouse_over = mouse_pos.x == x && mouse_pos.y == y; - - uint32_t pen_key = - gen_pen_key(n, s, e, w, is_drag_point, mouse_over, is_in_shape, is_extra); - - if (CURSORS_MAP.count({n, w, e, s}) > 0 && has_point(x,y)) { - arr[x][y].cursor_coords = CURSORS_MAP.at({n, w, e, s}); - } - - if (PENS.find(pen_key) == PENS.end()) { - std::pair cursor{-1, -1}; - - if (type != "") { - return make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, mouse_over, - is_in_shape, is_extra); + bool n = false, w = false, e = false, s = false; + if (has_point(x, y)) { + if (y == 0 || !has_point(x, y - 1)) n = true; + if (x == 0 || !has_point(x - 1, y)) w = true; + if (!has_point(x + 1, y)) e = true; // TODO check map size + if (!has_point(x, y + 1)) s = true; // TODO check map size + } + + bool is_drag_point = type == "drag_point"; + bool is_extra = type == "extra_point"; + bool is_in_shape = has_point(x, y); + auto mouse_pos = Gui::getMousePos(); + bool mouse_over = mouse_pos.x == x && mouse_pos.y == y; + + uint32_t pen_key = gen_pen_key(n, s, e, w, is_drag_point, mouse_over, + is_in_shape, is_extra); + + if (CURSORS_MAP.count({n, w, e, s}) > 0 && has_point(x, y)) { + arr[x][y].cursor_coords = CURSORS_MAP.at({n, w, e, s}); } - if (CURSORS_MAP.count({n, w, e, s}) > 0) { - PENS.emplace(pen_key, - make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, - mouse_over, is_in_shape, is_extra)); - if (type == "" && has_point(x,y)) { - arr[x][y].penKey = pen_key; - } + if (PENS.find(pen_key) == PENS.end()) { + std::pair cursor{-1, -1}; + + if (type != "") { + return make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, + mouse_over, is_in_shape, is_extra); + } + + if (CURSORS_MAP.count({n, w, e, s}) > 0) { + PENS.emplace(pen_key, + make_pen(CURSORS_MAP.at({n, w, e, s}), is_drag_point, + mouse_over, is_in_shape, is_extra)); + if (type == "" && has_point(x, y)) { + arr[x][y].penKey = pen_key; + } + } } - } - // DEBUG(status).print("not cached lmao\n"); - return PENS.at(pen_key); + return PENS.at(pen_key); } static int design_load_shape(lua_State *L) { - if (lua_istable(L, -1)) { - lua_pushnil(L); - while (lua_next(L, -2) != 0) { - int x = lua_tointeger(L, -2); - - if (lua_istable(L, -1)) { + if (lua_istable(L, -1)) { lua_pushnil(L); while (lua_next(L, -2) != 0) { - int y = lua_tointeger(L, -2); - bool value = lua_toboolean(L, -1); - - if (value) { - arr[x][y] = DrawingPoint(); - } - lua_pop(L, 1); + int x = lua_tointeger(L, -2); + + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int y = lua_tointeger(L, -2); + bool value = lua_toboolean(L, -1); + + if (value) { + arr[x][y] = DrawingPoint(); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); } - } - lua_pop(L, 1); } - } - return 0; + return 0; } static int design_clear_shape(lua_State *L) { - arr.clear(); + arr.clear(); - return 0; + return 0; } static int design_draw_shape(lua_State *L) { - if (arr.size() == 0) { - design_load_shape(L); - } - - for (auto x : arr) { - for (auto y : x.second) { - Screen::Pen cur_tile = Screen::readTile(x.first, y.first, true); - Screen::Pen pen = get_pen(x.first, y.first, arr); - cur_tile.tile = pen.tile; - Screen::paintTile(cur_tile, x.first - *window_x, y.first - *window_y, - true); + if (arr.size() == 0) { + design_load_shape(L); + } + + for (auto x : arr) { + for (auto y : x.second) { + Screen::Pen cur_tile = Screen::readTile(x.first, y.first, true); + Screen::Pen pen = get_pen(x.first, y.first, arr); + cur_tile.tile = pen.tile; + Screen::paintTile(cur_tile, x.first - *window_x, + y.first - *window_y, true); + } } - } - return 0; + return 0; } static int design_draw_points(lua_State *L) { - if (lua_istable(L, -1)) { - const char *str; - lua_rawgeti(L, -1, 2); - str = lua_tostring(L, -1); - lua_pop(L, 1); - - lua_rawgeti(L, -1, 1); - int n = luaL_len(L, -1); - for (int i = 1; i <= n; i++) { - lua_rawgeti(L, -1, i); - int x, y; - lua_getfield(L, -1, "y"); - y = lua_tointeger(L, -1); - lua_getfield(L, -2, "x"); - x = lua_tointeger(L, -1); - lua_pop(L, 3); - - Screen::Pen cur_tile = Screen::readTile(x, y, true); - Screen::Pen pen = get_pen(x, y, arr, str); - cur_tile.tile = pen.tile; - Screen::paintTile(cur_tile, x - *window_x, y - *window_y, true); + if (lua_istable(L, -1)) { + const char *str; + lua_rawgeti(L, -1, 2); + str = lua_tostring(L, -1); + lua_pop(L, 1); + + lua_rawgeti(L, -1, 1); + int n = luaL_len(L, -1); + for (int i = 1; i <= n; i++) { + lua_rawgeti(L, -1, i); + int x, y; + lua_getfield(L, -1, "y"); + y = lua_tointeger(L, -1); + lua_getfield(L, -2, "x"); + x = lua_tointeger(L, -1); + lua_pop(L, 3); + + Screen::Pen cur_tile = Screen::readTile(x, y, true); + Screen::Pen pen = get_pen(x, y, arr, str); + cur_tile.tile = pen.tile; + Screen::paintTile(cur_tile, x - *window_x, y - *window_y, true); + } + lua_pop(L, 1); } - lua_pop(L, 1); - } - return 0; + return 0; } DFHACK_PLUGIN_LUA_COMMANDS{DFHACK_LUA_COMMAND(design_draw_shape), From aa0721edee40396c3aa11ec933f3beacdb85bc3d Mon Sep 17 00:00:00 2001 From: John Cosker Date: Wed, 3 May 2023 20:01:17 -0400 Subject: [PATCH 1148/2222] Changelog --- docs/changelog.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0a37bb3b9..f6524dc58 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -68,7 +68,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/control-panel`: add preference option for hiding "armok" tools in command lists - ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running). - `overlay`: add the DFHack version string to the DF title screen -- `gui/design`: Improved performance for drawing shapes ## Lua - ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget From 07c8f035c7c133da150edeccead7dc4bc3dcda2b Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 4 May 2023 07:14:05 +0000 Subject: [PATCH 1149/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 8c0624942..43a74b6c8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 8c0624942ed11b369be805b2c0b5bc5ffd14f13d +Subproject commit 43a74b6c8679807a24028d56c1b157b28af59e73 From bfb6f3aa2eb1ed4834758f5506658955f9490579 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 5 May 2023 07:13:16 +0000 Subject: [PATCH 1150/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 43a74b6c8..54bd83727 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 43a74b6c8679807a24028d56c1b157b28af59e73 +Subproject commit 54bd83727df5f7048f5a7251b99ada1930e18ad9 From e664f4aa7b06e1bf78283773c0b1f7dc4cc61674 Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 9 May 2023 21:56:07 -0400 Subject: [PATCH 1151/2222] CMake: fix find_package(Python) when inactive pyenv Python installations are present `find_package(Python)`'s default behavior is to attempt to use any `python3.x` executables it finds before `python3`. This is problematic when using tools such as pyenv, where `python3.x` executables may be present in a user's PATH but not be functional. Setting the `Python_FIND_UNVERSIONED_NAMES` hint to `FIRST` causes `python3` to be attempted first, if it exists. Note that this option was introduced in CMake 3.20: https://cmake.org/cmake/help/latest/module/FindPython.html#hints --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4228f45cd..6d1d4c562 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,6 +376,9 @@ endif() #### expose depends #### +# fix for pyenv: default to `python3` before `python3.x` +set(Python_FIND_UNVERSIONED_NAMES FIRST) + if(UNIX) # Rescan for pthread and zlib if the build arch changed if(NOT "${DFHACK_BUILD_ARCH}" STREQUAL "${DFHACK_BUILD_ARCH_PREV}") From cb2db50a649ee737ea0f79c4e355cb2ec6e7c5bf Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 9 May 2023 22:13:49 -0400 Subject: [PATCH 1152/2222] Re-enable build of several plugins Likely to still work, and were probably just disabled due to v50 changes: - changeitem - createitem - deramp - flows - lair and a couple that don't access DF data at all: - luasocket - title-folder --- plugins/CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1967c370a..43b949d60 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -90,7 +90,7 @@ dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) #dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) #dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) add_subdirectory(buildingplan) -#dfhack_plugin(changeitem changeitem.cpp) +dfhack_plugin(changeitem changeitem.cpp) dfhack_plugin(changelayer changelayer.cpp) dfhack_plugin(changevein changevein.cpp) add_subdirectory(channel-safely) @@ -98,10 +98,10 @@ dfhack_plugin(cleanconst cleanconst.cpp) dfhack_plugin(cleaners cleaners.cpp) dfhack_plugin(cleanowned cleanowned.cpp) dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) -#dfhack_plugin(createitem createitem.cpp) +dfhack_plugin(createitem createitem.cpp) dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) -#dfhack_plugin(deramp deramp.cpp) +dfhack_plugin(deramp deramp.cpp) dfhack_plugin(debug debug.cpp LINK_LIBRARIES jsoncpp_static) dfhack_plugin(dig dig.cpp) dfhack_plugin(dig-now dig-now.cpp LINK_LIBRARIES lua) @@ -117,7 +117,7 @@ dfhack_plugin(faststart faststart.cpp) dfhack_plugin(filltraffic filltraffic.cpp) #dfhack_plugin(fix-unit-occupancy fix-unit-occupancy.cpp) #dfhack_plugin(fixveins fixveins.cpp) -#dfhack_plugin(flows flows.cpp) +dfhack_plugin(flows flows.cpp) #dfhack_plugin(follow follow.cpp) #dfhack_plugin(forceequip forceequip.cpp) #dfhack_plugin(generated-creature-renamer generated-creature-renamer.cpp) @@ -126,9 +126,9 @@ dfhack_plugin(hotkeys hotkeys.cpp LINK_LIBRARIES lua) #dfhack_plugin(infiniteSky infiniteSky.cpp) #dfhack_plugin(isoworldremote isoworldremote.cpp PROTOBUFS isoworldremote) #dfhack_plugin(jobutils jobutils.cpp) -#dfhack_plugin(lair lair.cpp) +dfhack_plugin(lair lair.cpp) dfhack_plugin(liquids liquids.cpp Brushes.h LINK_LIBRARIES lua) -#dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) +dfhack_plugin(luasocket luasocket.cpp LINK_LIBRARIES clsocket lua dfhack-tinythread) #dfhack_plugin(manipulator manipulator.cpp) #dfhack_plugin(map-render map-render.cpp LINK_LIBRARIES lua) dfhack_plugin(misery misery.cpp LINK_LIBRARIES lua) @@ -162,7 +162,7 @@ add_subdirectory(stockpiles) dfhack_plugin(strangemood strangemood.cpp) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) -#dfhack_plugin(title-folder title-folder.cpp) +dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(trackstop trackstop.cpp) #dfhack_plugin(tubefill tubefill.cpp) #add_subdirectory(tweak) From f9a946190d56ac5f64c8d740b53367b95fbca8ca Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 10 May 2023 23:31:00 -0400 Subject: [PATCH 1153/2222] Disable building title-folder again Hangs when enabled in dfhack.init, at least under wine --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 43b949d60..3ae78d320 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -162,7 +162,7 @@ add_subdirectory(stockpiles) dfhack_plugin(strangemood strangemood.cpp) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua) -dfhack_plugin(title-folder title-folder.cpp) +#dfhack_plugin(title-folder title-folder.cpp) #dfhack_plugin(trackstop trackstop.cpp) #dfhack_plugin(tubefill tubefill.cpp) #add_subdirectory(tweak) From 94e56bf4c74e46f8b6d50c9ea051255f8308d59e Mon Sep 17 00:00:00 2001 From: lethosor Date: Wed, 10 May 2023 23:49:02 -0400 Subject: [PATCH 1154/2222] Update changelog and docs for readded plugins Also mark reinstated plugins in the changelog, including for previous v50 releases --- docs/changelog.txt | 14 ++++++++++---- docs/plugins/changeitem.rst | 4 ++-- docs/plugins/createitem.rst | 2 +- docs/plugins/deramp.rst | 2 +- docs/plugins/flows.rst | 2 +- docs/plugins/lair.rst | 2 +- docs/plugins/luasocket.rst | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index f6524dc58..fc706cdba 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,8 +34,14 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins -- `add-spatter`: allow mods to add poisons and magical effects to weapons -- `work-now`: reduce the time that dwarves are left without a task after completing a job +- `add-spatter`: reinstated: allow mods to add poisons and magical effects to weapons +- `changeitem`: reinstated: change item material, quality, and subtype +- `createitem`: reinstated: create arbitrary items, from the command line +- `deramp`: reinstated: removes all ramps designated for removal from the map +- `flows`: reinstated: counts map blocks with flowing liquids +- `lair`: reinstated: mark the map as a monster lair (this avoids item scatter when the fortress is abandoned) +- `luasocket`: reinstated: provides a Lua API for accessing network sockets +- `work-now`: reinstated, renamed from ``workNow``: reduce the time that dwarves are left without a task after completing a job ## Fixes @@ -112,8 +118,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # 50.07-beta2 ## New Plugins -- `getplants`: designate trees for chopping and shrubs for gathering according to type -- `prospector`: get stone, ore, gem, and other tile property counts in fort mode. +- `getplants`: reinstated: designate trees for chopping and shrubs for gathering according to type +- `prospector`: reinstated: get stone, ore, gem, and other tile property counts in fort mode. ## Fixes -@ `buildingplan`: filters are now properly applied to planned stairs diff --git a/docs/plugins/changeitem.rst b/docs/plugins/changeitem.rst index 788b49247..f452f6a74 100644 --- a/docs/plugins/changeitem.rst +++ b/docs/plugins/changeitem.rst @@ -2,8 +2,8 @@ changeitem ========== .. dfhack-tool:: - :summary: Change item material or base quality. - :tags: unavailable adventure fort armok items + :summary: Change item material, quality, and subtype. + :tags: adventure fort armok items By default, a change is only allowed if the existing and desired item materials are of the same subtype (for example wood -> wood, stone -> stone, etc). But diff --git a/docs/plugins/createitem.rst b/docs/plugins/createitem.rst index b287042c7..b29e41521 100644 --- a/docs/plugins/createitem.rst +++ b/docs/plugins/createitem.rst @@ -3,7 +3,7 @@ createitem .. dfhack-tool:: :summary: Create arbitrary items. - :tags: unavailable adventure fort armok items + :tags: adventure fort armok items You can create new items of any type and made of any material. A unit must be selected in-game to use this command. By default, items created are spawned at diff --git a/docs/plugins/deramp.rst b/docs/plugins/deramp.rst index 4a1f34548..fdc0f7619 100644 --- a/docs/plugins/deramp.rst +++ b/docs/plugins/deramp.rst @@ -3,7 +3,7 @@ deramp .. dfhack-tool:: :summary: Removes all ramps designated for removal from the map. - :tags: unavailable fort armok map + :tags: fort armok map It also removes any "floating" down ramps that can remain after a cave-in. diff --git a/docs/plugins/flows.rst b/docs/plugins/flows.rst index 56840d999..bbb2c6661 100644 --- a/docs/plugins/flows.rst +++ b/docs/plugins/flows.rst @@ -3,7 +3,7 @@ flows .. dfhack-tool:: :summary: Counts map blocks with flowing liquids. - :tags: unavailable fort inspection map + :tags: fort inspection map If you suspect that your magma sea leaks into HFS, you can use this tool to be sure without revealing the map. diff --git a/docs/plugins/lair.rst b/docs/plugins/lair.rst index 82c5e211c..9bded57ab 100644 --- a/docs/plugins/lair.rst +++ b/docs/plugins/lair.rst @@ -3,7 +3,7 @@ lair .. dfhack-tool:: :summary: Mark the map as a monster lair. - :tags: unavailable fort armok map + :tags: fort armok map This avoids item scatter when the fortress is abandoned. diff --git a/docs/plugins/luasocket.rst b/docs/plugins/luasocket.rst index 1aa320ed7..4b5b18540 100644 --- a/docs/plugins/luasocket.rst +++ b/docs/plugins/luasocket.rst @@ -3,7 +3,7 @@ luasocket .. dfhack-tool:: :summary: Provides a Lua API for accessing network sockets. - :tags: unavailable dev + :tags: dev :no-command: See `luasocket-api` for details. From b6723e4fdbd0a72d766d12d8322ca240632d0ba4 Mon Sep 17 00:00:00 2001 From: lethosor Date: Thu, 11 May 2023 00:26:19 -0400 Subject: [PATCH 1155/2222] Remove "unavailable" tag from add-spatter and channel-safely docs These plugins are currently being built --- docs/plugins/add-spatter.rst | 2 +- docs/plugins/channel-safely.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/add-spatter.rst b/docs/plugins/add-spatter.rst index 2c43a88ef..e8a39f781 100644 --- a/docs/plugins/add-spatter.rst +++ b/docs/plugins/add-spatter.rst @@ -3,7 +3,7 @@ add-spatter .. dfhack-tool:: :summary: Add poisons and magical effects to weapons. - :tags: unavailable adventure fort gameplay items + :tags: adventure fort gameplay items :no-command: Give some use to all those poisons that can be bought from caravans! The plugin diff --git a/docs/plugins/channel-safely.rst b/docs/plugins/channel-safely.rst index c24f352b0..c5dbc37f6 100644 --- a/docs/plugins/channel-safely.rst +++ b/docs/plugins/channel-safely.rst @@ -3,7 +3,7 @@ channel-safely .. dfhack-tool:: :summary: Auto-manage channel designations to keep dwarves safe. - :tags: unavailable fort auto + :tags: fort auto Multi-level channel projects can be dangerous, and managing the safety of your dwarves throughout the completion of such projects can be difficult and time From 1a703c344f810ec289a42dddba9add8277c5ff75 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 15 May 2023 17:33:57 -0700 Subject: [PATCH 1156/2222] support disabling DFHack with --disable-dfhack --- docs/Core.rst | 19 ++++++++++++++++++- docs/changelog.txt | 1 + library/Core.cpp | 30 +++++++++++++++++------------- library/Hooks.cpp | 22 +++++++++++++++++++++- library/include/Core.h | 3 ++- 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/docs/Core.rst b/docs/Core.rst index 5decd668d..62b91c19c 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -53,7 +53,7 @@ double quotes. To include a double quote character, use ``\"``. If the first non-whitespace character is ``:``, the command is parsed in an alternative mode. The non-whitespace characters following the ``:`` are the command name, and the remaining part of the line is used verbatim as -the first argument. This is very useful for the `lua` and `rb` commands. +the first argument. This is very useful for the `lua` command. As an example, the following two command lines are exactly equivalent:: :foo a b "c d" e f @@ -306,6 +306,23 @@ the root DF folder. Note that ``script-paths.txt`` is only read at startup, but the paths can also be modified programmatically at any time through the `Lua API `. +Commandline options +=================== + +In addition to `Using an OS terminal`_ to execute commands on startup, DFHack +also recognizes a single commandline option that can be specified on the +commandline: + +- ``--disable-dfhack``: If this option is passed on the Dwarf Fortress + commandline, then DFHack will be disabled for the session. You will have to + restart Dwarf Fortress without specifying this option in order to use DFHack. + If you are launching Dwarf Fortress from Steam, you can enter the option in + the "Launch Options" text box in the properties for the Dwarf Fortress app. + Note that if you do this, DFHack will be disabled regardless of whether you + run Dwarf Fortress from its own app or DFHack's. You will have to clear the + DF Launch Options in order to use DFHack again. Note that even if DFHack is + disabled, :file:`stdout.txt` and :file:`stderr.txt` will still be redirected + to :file:`stdout.log` and :file:`stderr.log`, respectively. .. _env-vars: diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..d602ed93a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,6 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - Terminal console no longer appears in front of the game window on startup - `gui/design`: Improved performance for drawing shapes +- ``Core``: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline to disable DFHack for the session. ## Documentation diff --git a/library/Core.cpp b/library/Core.cpp index 9ff6976d5..256493dce 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1471,16 +1471,8 @@ std::string Core::getHackPath() #endif } -bool Core::Init() -{ - if(started) - return true; - if(errorstate) - return false; - - // Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown - // Core::Update will temporary unlock when there is any commands queued - MainThread::suspend().lock(); +bool Core::InitMainThread() { + Filesystem::init(); // Re-route stdout and stderr again - DF seems to set up stdout and // stderr.txt on Windows as of 0.43.05. Also, log before switching files to @@ -1496,8 +1488,6 @@ bool Core::Init() if (!freopen("stderr.log", "w", stderr)) std::cerr << "Could not redirect stderr to stderr.log" << std::endl; - Filesystem::init(); - std::cerr << "DFHack build: " << Version::git_description() << "\n" << "Starting with working directory: " << Filesystem::getcwd() << std::endl; @@ -1566,6 +1556,20 @@ bool Core::Init() // Init global object pointers df::global::InitGlobals(); + return true; +} + +bool Core::InitSimulationThread() +{ + if(started) + return true; + if(errorstate) + return false; + + // Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown + // Core::Update will temporary unlock when there is any commands queued + MainThread::suspend().lock(); + std::cerr << "Initializing Console.\n"; // init the console. bool is_text_mode = (init && init->display.flag.is_set(init_display_flags::TEXT)); @@ -1965,7 +1969,7 @@ int Core::Update() if(!started) { // Initialize the core - Init(); + InitSimulationThread(); if(errorstate) return -1; } diff --git a/library/Hooks.cpp b/library/Hooks.cpp index c241e4875..1976d60d7 100644 --- a/library/Hooks.cpp +++ b/library/Hooks.cpp @@ -1,31 +1,49 @@ #include "Core.h" #include "Export.h" +#include "df/gamest.h" + +static bool disabled = false; + // called from the main thread before the simulation thread is started // and the main event loop is initiated DFhackCExport void dfhooks_init() { - // TODO: initialize things we need to do while still in the main thread + if (!DFHack::Core::getInstance().InitMainThread() || !df::global::game) + return; + const std::string & cmdline = df::global::game->command_line.original; + if (cmdline.find("--disable-dfhack") != std::string::npos) { + fprintf(stdout, "dfhack: --disable-dfhack specified on commandline; disabling\n"); + disabled = true; + } } // called from the main thread after the main event loops exits DFhackCExport void dfhooks_shutdown() { + if (disabled) + return; DFHack::Core::getInstance().Shutdown(); } // called from the simulation thread in the main event loop DFhackCExport void dfhooks_update() { + if (disabled) + return; DFHack::Core::getInstance().Update(); } // called from the simulation thread just before adding the macro // recording/playback overlay DFhackCExport void dfhooks_prerender() { + if (disabled) + return; // TODO: render overlay widgets that are not attached to a viewscreen } // called from the main thread for each SDL event. if true is returned, then // the event has been consumed and further processing shouldn't happen DFhackCExport bool dfhooks_sdl_event(SDL::Event* event) { + if (disabled) + return false; return DFHack::Core::getInstance().DFH_SDL_Event(event); } @@ -34,5 +52,7 @@ DFhackCExport bool dfhooks_sdl_event(SDL::Event* event) { // if true is returned, then the event has been consumed and further processing // shouldn't happen DFhackCExport bool dfhooks_ncurses_key(int key) { + if (disabled) + return false; return DFHack::Core::getInstance().DFH_ncurses_key(key); } diff --git a/library/include/Core.h b/library/include/Core.h index 386769fcb..696be4ead 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -191,7 +191,8 @@ namespace DFHack struct Private; std::unique_ptr d; - bool Init(); + bool InitMainThread(); + bool InitSimulationThread(); int Update (void); int Shutdown (void); bool DFH_SDL_Event(SDL::Event* event); From 910b7c2ae945b4babe2b7304b0500ea2f189dcfb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 15 May 2023 17:52:20 -0700 Subject: [PATCH 1157/2222] fix autolabor warning appearing on inappropriate screens --- docs/changelog.txt | 1 + plugins/lua/autolabor.lua | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..9b6868540 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `work-now`: reinstated, renamed from ``workNow``: reduce the time that dwarves are left without a task after completing a job ## Fixes +- `autolabor`: work detail override warning now only appears on the work details screen ## Misc Improvements - Terminal console no longer appears in front of the game window on startup diff --git a/plugins/lua/autolabor.lua b/plugins/lua/autolabor.lua index 50f39b225..a468b30e1 100644 --- a/plugins/lua/autolabor.lua +++ b/plugins/lua/autolabor.lua @@ -8,9 +8,9 @@ AutolaborOverlay = defclass(AutolaborOverlay, overlay.OverlayWidget) AutolaborOverlay.ATTRS{ default_pos={x=7,y=-13}, default_enabled=true, - viewscreens='dwarfmode/Info/LABOR', + viewscreens='dwarfmode/Info/LABOR/WORK_DETAILS', frame={w=29, h=5}, - frame_style=gui.THIN_FRAME, + frame_style=gui.MEDIUM_FRAME, frame_background=gui.CLEAR_PEN, } From 9f997eaade3776997379a440af0018c6980aa8f2 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 15 May 2023 17:54:45 -0700 Subject: [PATCH 1158/2222] Update docs/changelog.txt Co-authored-by: Alan --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index d602ed93a..529eebfff 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,7 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - Terminal console no longer appears in front of the game window on startup - `gui/design`: Improved performance for drawing shapes -- ``Core``: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline to disable DFHack for the session. +- Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline to disable DFHack for the session. ## Documentation From a62993b90b6c21f0f1f93dbd685e3fd19f158b73 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 15 May 2023 18:15:46 -0700 Subject: [PATCH 1159/2222] add DFHACK_DISABLE env var --- docs/Core.rst | 5 +++++ library/Hooks.cpp | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/docs/Core.rst b/docs/Core.rst index 62b91c19c..0e333702f 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -336,6 +336,11 @@ on UNIX-like systems: DFHACK_SOME_VAR=1 ./dfhack +- ``DFHACK_DISABLE``: if set, DFHack will not initialize, not even to redirect + :file:`stdout.txt` or :file:`stderr.txt`. This is provided as an alternative + to the ``--disable-dfhack`` commandline parameter above for when environment + variables are more convenient. + - ``DFHACK_PORT``: the port to use for the RPC server (used by ``dfhack-run`` and `remotefortressreader` among others) instead of the default ``5000``. As with the default, if this port cannot be used, the server is not started. diff --git a/library/Hooks.cpp b/library/Hooks.cpp index 1976d60d7..4e339e768 100644 --- a/library/Hooks.cpp +++ b/library/Hooks.cpp @@ -8,6 +8,13 @@ static bool disabled = false; // called from the main thread before the simulation thread is started // and the main event loop is initiated DFhackCExport void dfhooks_init() { + if (getenv("DFHACK_DISABLE")) { + fprintf(stdout, "dfhack: DFHACK_DISABLE detected in environment; disabling\n"); + disabled = true; + return; + } + + // we need to init DF globals before we can check the commandline if (!DFHack::Core::getInstance().InitMainThread() || !df::global::game) return; const std::string & cmdline = df::global::game->command_line.original; From b845ea15b8050758797c0694380dc163106d85d5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 15 May 2023 18:34:43 -0700 Subject: [PATCH 1160/2222] update changelog --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 529eebfff..80d07eff4 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,7 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - Terminal console no longer appears in front of the game window on startup - `gui/design`: Improved performance for drawing shapes -- Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline to disable DFHack for the session. +- Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. ## Documentation From 6b2c805d5f813b193f959758afdb5431f42fedc9 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 15 May 2023 22:12:12 -0700 Subject: [PATCH 1161/2222] Update docs/Core.rst Co-authored-by: Alan --- docs/Core.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Core.rst b/docs/Core.rst index 0e333702f..763858b61 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -337,7 +337,7 @@ on UNIX-like systems: DFHACK_SOME_VAR=1 ./dfhack - ``DFHACK_DISABLE``: if set, DFHack will not initialize, not even to redirect - :file:`stdout.txt` or :file:`stderr.txt`. This is provided as an alternative + standard output or standard error. This is provided as an alternative to the ``--disable-dfhack`` commandline parameter above for when environment variables are more convenient. From 81918a89a33d55f2ebdea2e6fe6e66fb3b59410e Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 16 May 2023 07:13:50 +0000 Subject: [PATCH 1162/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 54bd83727..1ec3602f9 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 54bd83727df5f7048f5a7251b99ada1930e18ad9 +Subproject commit 1ec3602f9d896c9dc76b26b3be845a5aa7544093 From f05fe333071a4d28cfdc643047376fa8a9bb24c6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 11:26:47 -0700 Subject: [PATCH 1163/2222] fix crash on malformed json (again) --- docs/changelog.txt | 1 + library/RemoteClient.cpp | 6 +++++- library/RemoteServer.cpp | 19 +++++++++++-------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..50d015c4d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `work-now`: reinstated, renamed from ``workNow``: reduce the time that dwarves are left without a task after completing a job ## Fixes +- RemoteServer: fix crash on malformed json in ``dfhack-config/remote-server.json`` ## Misc Improvements - Terminal console no longer appears in front of the game window on startup diff --git a/library/RemoteClient.cpp b/library/RemoteClient.cpp index 6a8becaae..0aa68eb51 100644 --- a/library/RemoteClient.cpp +++ b/library/RemoteClient.cpp @@ -150,7 +150,11 @@ int RemoteClient::GetDefaultPort() if (in_file) { Json::Value config; - in_file >> config; + try { + in_file >> config; + } catch (const std::exception & e) { + std::cerr << "Error reading remote server config file: " << filename << ": " << e.what() << std::endl; + } in_file.close(); if (config.isMember("port")) { port = config["port"].asInt(); diff --git a/library/RemoteServer.cpp b/library/RemoteServer.cpp index 734b80702..77510d63a 100644 --- a/library/RemoteServer.cpp +++ b/library/RemoteServer.cpp @@ -420,17 +420,20 @@ ServerMainImpl::ServerMainImpl(std::promise promise, int port) : Json::Value configJson; - std::ifstream inFile(filename, std::ios_base::in); - bool allow_remote = false; - if (inFile.is_open()) - { - inFile >> configJson; - inFile.close(); - - allow_remote = configJson.get("allow_remote", "false").asBool(); + std::ifstream inFile(filename, std::ios_base::in); + try { + if (inFile.is_open()) + { + inFile >> configJson; + allow_remote = configJson.get("allow_remote", "false").asBool(); + } + } catch (const std::exception & e) { + std::cerr << "Error reading remote server config file: " << filename << ": " << e.what() << std::endl; + std::cerr << "Reverting to remote server config to defaults" << std::endl; } + inFile.close(); // rewrite/normalize config file configJson["allow_remote"] = allow_remote; From a76bed0ed511848484bfeb0d3b94a0a8ac9af211 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 11:41:28 -0700 Subject: [PATCH 1164/2222] rename overlay.reload to overlay.rescan --- docs/changelog.txt | 1 + docs/dev/overlay-dev-guide.rst | 4 ++-- plugins/lua/overlay.lua | 4 ++-- plugins/overlay.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..4fab1aa28 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -54,6 +54,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API ## Lua +- ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. ## Removed diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index 1a53e7f65..4ec226d30 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -183,7 +183,7 @@ Scripts #. If the script is not in your `script-paths`, install your script (see the `modding-guide` for help setting up a dev environment so that you don't need to reinstall your scripts after every edit). -#. Call ``:lua require('plugins.overlay').reload()`` to reload your overlay +#. Call ``:lua require('plugins.overlay').rescan()`` to reload your overlay widget Plugins @@ -194,7 +194,7 @@ Plugins :file:`hack/lua/plugins/` #. If you have changed the compiled plugin, `reload` it #. If you have changed the lua code, run ``:lua reload('plugins.mypluginname')`` -#. Call ``:lua require('plugins.overlay').reload()`` to reload your overlay +#. Call ``:lua require('plugins.overlay').rescan()`` to reload your overlay widget Troubleshooting diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 84c2b4f96..9f41cb035 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -292,7 +292,7 @@ local function load_widgets(env_name, env) end -- called directly from cpp on plugin enable -function reload() +function rescan() reset() for _,plugin in ipairs(dfhack.internal.listPlugins()) do @@ -317,7 +317,7 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) return end -- pick up widgets from active mods - reload() + rescan() end local function dump_widget_config(name, widget) diff --git a/plugins/overlay.cpp b/plugins/overlay.cpp index c02c960ba..784e17129 100644 --- a/plugins/overlay.cpp +++ b/plugins/overlay.cpp @@ -128,7 +128,7 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { if (enable) { screenSize = Screen::getWindowSize(); - call_overlay_lua(&out, "reload"); + call_overlay_lua(&out, "rescan"); } DEBUG(control).print("%sing interpose hooks\n", enable ? "enabl" : "disabl"); From 217be6b58d16004192ed3191d9154ee87be11586 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 12:36:58 -0700 Subject: [PATCH 1165/2222] make full text search configurable for list filters --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 8 ++++++++ library/lua/gui/widgets.lua | 19 ++++++++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..f301a1055 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - Terminal console no longer appears in front of the game window on startup +- `gui/control-panel`: new preference for whether filters in lists search for substrings in the middle of words (e.g. if set to true, then "ee" will match "steel") - `gui/design`: Improved performance for drawing shapes ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 85e51d11d..6215fd250 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5106,6 +5106,14 @@ The widget implements: Same as with an ordinary list. +Filter behavior: + +By default, the filter matches substrings that start at the beginning of a word +(or after any punctuation). You can instead configure filters to match any +substring with a command like:: + + :lua require('gui.widgets').FILTER_FULL_TEXT=true + TabBar class ------------ diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 7aa79cbc3..e5d808903 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1932,6 +1932,8 @@ end -- Filtered List -- ------------------- +FILTER_FULL_TEXT = false + FilteredList = defclass(FilteredList, Widget) FilteredList.ATTRS { @@ -2102,19 +2104,22 @@ function FilteredList:setFilter(filter, pos) end for _,key in ipairs(tokens) do key = key:escape_pattern() - -- start matches at non-space or non-punctuation. this allows - -- punctuation itself to be matched if that is useful (e.g. - -- filenames or parameter names) if key ~= '' then if not self.case_sensitive then search_key = string.lower(search_key) key = string.lower(key) end - if not search_key:match('%f[^%p\x00]'..key) and - not search_key:match('%f[^%s\x00]'..key) then - ok = false - break + -- the separate checks for non-space or non-punctuation allows + -- punctuation itself to be matched if that is useful (e.g. + -- filenames or parameter names) + if not FILTER_FULL_TEXT and not search_key:match('%f[^%p\x00]'..key) + and not search_key:match('%f[^%s\x00]'..key) then + ok = false + break + elseif FILTER_FULL_TEXT and not search_key:find(key) then + ok = false + break end end end From b6b65d4bf69a564b2ac2b02c0cf782080b17e42b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 15:37:00 -0700 Subject: [PATCH 1166/2222] update library military orders move preference for silver below steel leather cloaks -> silk cloaks leather shield -> wood shield adjust target quantities for archer uniforms --- data/orders/military.json | 410 +- .../military_include_artifact_materials.json | 4971 ----------------- docs/plugins/orders.rst | 16 +- 3 files changed, 198 insertions(+), 5199 deletions(-) delete mode 100644 data/orders/military_include_artifact_materials.json diff --git a/data/orders/military.json b/data/orders/military.json index 0e53747b6..abf2675f9 100644 --- a/data/orders/military.json +++ b/data/orders/military.json @@ -92,15 +92,16 @@ [ { "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", + "flags" : + [ + "silk" + ], + "item_type" : "CLOTH", + "min_dimension" : 10000, "value" : 10 }, { "condition" : "AtMost", - "flags" : - [ - "leather" - ], "item_subtype" : "ITEM_ARMOR_CLOAK", "item_type" : "ARMOR", "value" : 10 @@ -110,7 +111,7 @@ "job" : "MakeArmor", "material_category" : [ - "leather" + "silk" ] }, { @@ -152,25 +153,21 @@ [ { "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 + "item_type" : "WOOD", + "value" : 50 }, { "condition" : "AtMost", - "flags" : - [ - "leather" - ], "item_subtype" : "ITEM_SHIELD_SHIELD", "item_type" : "SHIELD", - "value" : 1 + "value" : 10 } ], "item_subtype" : "ITEM_SHIELD_SHIELD", "job" : "MakeShield", "material_category" : [ - "leather" + "wood" ] }, { @@ -191,7 +188,7 @@ "condition" : "AtMost", "item_subtype" : "ITEM_ARMOR_LEATHER", "item_type" : "ARMOR", - "value" : 1 + "value" : 10 } ], "item_subtype" : "ITEM_ARMOR_LEATHER", @@ -223,7 +220,7 @@ ], "item_subtype" : "ITEM_HELM_HELM", "item_type" : "HELM", - "value" : 1 + "value" : 10 } ], "item_subtype" : "ITEM_HELM_HELM", @@ -255,7 +252,7 @@ ], "item_subtype" : "ITEM_SHOES_BOOTS", "item_type" : "SHOES", - "value" : 2 + "value" : 20 } ], "item_subtype" : "ITEM_SHOES_BOOTS", @@ -287,7 +284,7 @@ ], "item_subtype" : "ITEM_PANTS_LEGGINGS", "item_type" : "PANTS", - "value" : 1 + "value" : 10 } ], "item_subtype" : "ITEM_PANTS_LEGGINGS", @@ -319,7 +316,7 @@ ], "item_subtype" : "ITEM_GLOVES_GLOVES", "item_type" : "GLOVES", - "value" : 2 + "value" : 20 } ], "item_subtype" : "ITEM_GLOVES_GLOVES", @@ -571,37 +568,6 @@ "is_active" : false, "is_validated" : false, "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:NATIVE_PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:NATIVE_PLATINUM" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 19, - "is_active" : false, - "is_validated" : false, - "item_conditions" : [ { "condition" : "AtLeast", @@ -629,7 +595,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 20, + "id" : 19, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -660,7 +626,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 21, + "id" : 20, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -691,7 +657,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 22, + "id" : 21, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -722,7 +688,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 23, + "id" : 22, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -759,7 +725,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 24, + "id" : 23, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -808,7 +774,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 25, + "id" : 24, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -845,7 +811,7 @@ "amount_left" : 4, "amount_total" : 4, "frequency" : "Daily", - "id" : 26, + "id" : 25, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -888,7 +854,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 27, + "id" : 26, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -924,7 +890,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 28, + "id" : 27, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -967,7 +933,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 29, + "id" : 28, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1015,7 +981,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 30, + "id" : 29, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1070,79 +1036,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 35, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 35, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 37, + "id" : 30, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1175,7 +1069,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 38, + "id" : 31, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1208,7 +1102,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 39, + "id" : 32, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1241,7 +1135,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 40, + "id" : 33, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1274,7 +1168,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 41, + "id" : 34, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1307,7 +1201,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 42, + "id" : 35, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1347,7 +1241,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 43, + "id" : 36, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1387,7 +1281,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 44, + "id" : 37, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1404,12 +1298,6 @@ "material" : "COAL", "value" : 100 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "item_subtype" : "ITEM_WEAPON_MACE", @@ -1426,7 +1314,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 45, + "id" : 38, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1443,12 +1331,6 @@ "material" : "COAL", "value" : 100 }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, { "condition" : "AtMost", "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", @@ -1465,7 +1347,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 46, + "id" : 39, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1498,7 +1380,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 47, + "id" : 40, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1531,7 +1413,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 48, + "id" : 41, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1564,7 +1446,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 49, + "id" : 42, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1597,7 +1479,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 50, + "id" : 43, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1630,7 +1512,103 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 51, + "id" : 44, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_MACE", + "job" : "MakeWeapon", + "material" : "INORGANIC:SILVER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 45, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "INORGANIC:SILVER", + "value" : 5 + }, + { + "condition" : "AtLeast", + "item_type" : "BAR", + "material" : "COAL", + "value" : 100 + }, + { + "condition" : "LessThan", + "item_type" : "BOULDER", + "reaction_class" : "FLUX", + "value" : 5 + }, + { + "condition" : "AtMost", + "flags" : + [ + "metal" + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "item_type" : "WEAPON", + "value" : 10 + }, + { + "condition" : "LessThan", + "item_type" : "BAR", + "material" : "INORGANIC:STEEL", + "value" : 10 + } + ], + "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", + "job" : "MakeWeapon", + "material" : "INORGANIC:SILVER" + }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 46, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1678,7 +1656,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 52, + "id" : 47, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1726,7 +1704,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 53, + "id" : 48, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1774,7 +1752,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 54, + "id" : 49, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1822,7 +1800,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 55, + "id" : 50, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1870,7 +1848,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 56, + "id" : 51, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1928,7 +1906,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 57, + "id" : 52, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -1986,7 +1964,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 58, + "id" : 53, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2040,7 +2018,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 59, + "id" : 54, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2094,7 +2072,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 60, + "id" : 55, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2142,7 +2120,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 61, + "id" : 56, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2190,7 +2168,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 62, + "id" : 57, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2238,7 +2216,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 63, + "id" : 58, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2286,7 +2264,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 64, + "id" : 59, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2334,7 +2312,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 79, + "id" : 74, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2382,7 +2360,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 80, + "id" : 75, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2430,7 +2408,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 81, + "id" : 76, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2478,7 +2456,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 82, + "id" : 77, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2526,7 +2504,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 83, + "id" : 78, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2574,7 +2552,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 84, + "id" : 79, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2632,7 +2610,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 85, + "id" : 80, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2690,7 +2668,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 72, + "id" : 67, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2735,7 +2713,7 @@ "item_type" : "BAR", "material" : "INORGANIC:SILVER", "value" : 5 - }, + } ], "item_subtype" : "ITEM_WEAPON_MACE", "job" : "MakeWeapon", @@ -2745,7 +2723,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 87, + "id" : 82, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2799,7 +2777,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 88, + "id" : 83, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2847,7 +2825,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 89, + "id" : 84, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2895,7 +2873,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 90, + "id" : 85, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2943,7 +2921,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 91, + "id" : 86, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -2991,7 +2969,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 92, + "id" : 87, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3039,7 +3017,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 79, + "id" : 74, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3093,7 +3071,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 80, + "id" : 75, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3147,7 +3125,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 81, + "id" : 76, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3201,7 +3179,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 82, + "id" : 77, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3255,7 +3233,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 83, + "id" : 78, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3309,7 +3287,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 84, + "id" : 79, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3373,7 +3351,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 85, + "id" : 80, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3437,7 +3415,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 86, + "id" : 81, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3497,7 +3475,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 87, + "id" : 82, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3557,7 +3535,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 88, + "id" : 83, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3611,7 +3589,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 89, + "id" : 84, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3665,7 +3643,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 90, + "id" : 85, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3719,7 +3697,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 91, + "id" : 86, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3773,7 +3751,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 92, + "id" : 87, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3827,7 +3805,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 93, + "id" : 88, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3887,7 +3865,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 94, + "id" : 89, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -3947,7 +3925,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 95, + "id" : 90, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4007,7 +3985,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 96, + "id" : 91, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4067,7 +4045,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 97, + "id" : 92, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4127,7 +4105,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 98, + "id" : 93, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4197,7 +4175,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 99, + "id" : 94, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4267,7 +4245,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 100, + "id" : 95, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4333,7 +4311,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 101, + "id" : 96, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4399,7 +4377,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 102, + "id" : 97, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4459,7 +4437,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 103, + "id" : 98, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4519,7 +4497,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 104, + "id" : 99, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4579,7 +4557,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 105, + "id" : 100, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -4639,7 +4617,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 106, + "id" : 101, "is_active" : false, "is_validated" : false, "item_conditions" : diff --git a/data/orders/military_include_artifact_materials.json b/data/orders/military_include_artifact_materials.json deleted file mode 100644 index 536b2cd7a..000000000 --- a/data/orders/military_include_artifact_materials.json +++ /dev/null @@ -1,4971 +0,0 @@ -[ - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 0, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 10 - }, - { - "condition" : "AtMost", - "item_type" : "BACKPACK", - "value" : 10 - } - ], - "job" : "MakeBackpack", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 1, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 10 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_type" : "FLASK", - "value" : 10 - } - ], - "job" : "MakeFlask", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 2, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 10 - }, - { - "condition" : "AtMost", - "item_type" : "QUIVER", - "value" : 10 - } - ], - "job" : "MakeQuiver", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 3, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 10 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_subtype" : "ITEM_ARMOR_CLOAK", - "item_type" : "ARMOR", - "value" : 10 - } - ], - "item_subtype" : "ITEM_ARMOR_CLOAK", - "job" : "MakeArmor", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 4, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "WOOD", - "value" : 50 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material_category" : - [ - "wood" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 5, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "item_type" : "SHIELD", - "value" : 1 - } - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "job" : "MakeShield", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 6, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_ARMOR_LEATHER", - "item_type" : "ARMOR", - "value" : 1 - } - ], - "item_subtype" : "ITEM_ARMOR_LEATHER", - "job" : "MakeArmor", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 7, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_subtype" : "ITEM_HELM_HELM", - "item_type" : "HELM", - "value" : 1 - } - ], - "item_subtype" : "ITEM_HELM_HELM", - "job" : "MakeHelm", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 8, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "item_type" : "SHOES", - "value" : 2 - } - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "job" : "MakeShoes", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 9, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_subtype" : "ITEM_PANTS_LEGGINGS", - "item_type" : "PANTS", - "value" : 1 - } - ], - "item_subtype" : "ITEM_PANTS_LEGGINGS", - "job" : "MakePants", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 10, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "SKIN_TANNED", - "value" : 25 - }, - { - "condition" : "AtMost", - "flags" : - [ - "leather" - ], - "item_subtype" : "ITEM_GLOVES_GLOVES", - "item_type" : "GLOVES", - "value" : 2 - } - ], - "item_subtype" : "ITEM_GLOVES_GLOVES", - "job" : "MakeGloves", - "material_category" : - [ - "leather" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 11, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "flags" : - [ - "unrotten", - "bone", - "body_part" - ], - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "bone" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 1000 - } - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "job" : "MakeAmmo", - "material_category" : - [ - "bone" - ] - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 12, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "WOOD", - "value" : 150 - }, - { - "condition" : "AtMost", - "flags" : - [ - "bone" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 200 - }, - { - "condition" : "AtMost", - "flags" : - [ - "plant" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 1000 - } - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "job" : "MakeAmmo", - "material_category" : - [ - "wood" - ] - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 13, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:CASSITERITE", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:TIN", - "value" : 20 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:CASSITERITE" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 14, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:HEMATITE", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 40 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:HEMATITE" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 15, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:HORN_SILVER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 10 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:HORN_SILVER" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 16, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:LIMONITE", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 40 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:LIMONITE" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 17, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:NATIVE_COPPER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 40 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:NATIVE_COPPER" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 18, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:NATIVE_PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:NATIVE_PLATINUM" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 19, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:NATIVE_SILVER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 10 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:NATIVE_SILVER" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 20, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:MAGNETITE", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 40 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:MAGNETITE" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 21, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:MALACHITE", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 40 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:MALACHITE" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 22, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "material" : "INORGANIC:TETRAHEDRITE", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 40 - } - ], - "job" : "SmeltOre", - "material" : "INORGANIC:TETRAHEDRITE" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 23, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "bearing" : "TIN", - "condition" : "AtLeast", - "item_type" : "BOULDER", - "value" : 5 - }, - { - "bearing" : "COPPER", - "condition" : "AtLeast", - "item_type" : "BOULDER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 40 - } - ], - "job" : "CustomReaction", - "reaction" : "BRONZE_MAKING" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 24, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:TIN", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 40 - }, - { - "bearing" : "TIN", - "condition" : "AtMost", - "item_type" : "BOULDER", - "value" : 5 - }, - { - "bearing" : "COPPER", - "condition" : "AtMost", - "item_type" : "BOULDER", - "value" : 5 - } - ], - "job" : "CustomReaction", - "reaction" : "BRONZE_MAKING2" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 25, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:PIG_IRON", - "value" : 10 - } - ], - "job" : "CustomReaction", - "reaction" : "PIG_IRON_MAKING" - }, - { - "amount_left" : 4, - "amount_total" : 4, - "frequency" : "Daily", - "id" : 26, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PIG_IRON", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 40 - } - ], - "job" : "CustomReaction", - "reaction" : "STEEL_MAKING" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 27, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 1000 - } - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "job" : "MakeAmmo", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 28, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "min_dimension" : 150, - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 1000 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "job" : "MakeAmmo", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 29, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 1000 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "job" : "MakeAmmo", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 30, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "min_dimension" : 150, - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "item_type" : "AMMO", - "value" : 1000 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - } - ], - "item_subtype" : "ITEM_AMMO_BOLTS", - "job" : "MakeAmmo", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 31, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:PLATINUM" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 32, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:PLATINUM" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 64, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "material" : "INORGANIC:PLATINUM", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:PLATINUM" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 35, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 35, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 64, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:SILVER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 37, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_SHIELD_SHIELD", - "item_type" : "SHIELD", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "job" : "MakeShield", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 38, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "job" : "MakeArmor", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 39, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_HELM_HELM", - "item_type" : "HELM", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_HELM_HELM", - "job" : "MakeHelm", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 40, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_SHOES_BOOTS", - "item_type" : "SHOES", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "job" : "MakeShoes", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 41, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "item_type" : "GLOVES", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "job" : "MakeGloves", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 42, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_PANTS_GREAVES", - "item_type" : "PANTS", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "material" : "INORGANIC:STEEL", - "value" : 5 - } - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "job" : "MakePants", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 43, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "item_type" : "ARMOR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "material" : "INORGANIC:STEEL", - "value" : 5 - } - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "job" : "MakeArmor", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 44, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 45, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 46, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_SPEAR", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 47, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 48, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 49, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_PICK", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 50, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:STEEL" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 51, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "item_type" : "SHIELD", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "job" : "MakeShield", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 52, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "job" : "MakeArmor", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 53, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_HELM_HELM", - "item_type" : "HELM", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_HELM_HELM", - "job" : "MakeHelm", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 54, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "item_type" : "SHOES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "job" : "MakeShoes", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 55, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "item_type" : "GLOVES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "job" : "MakeGloves", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 56, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "item_type" : "PANTS", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - } - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "job" : "MakePants", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 57, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - } - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "job" : "MakeArmor", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 58, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 59, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 60, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 61, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 62, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 63, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - } - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 64, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BOULDER", - "reaction_class" : "FLUX", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:IRON" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 79, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "item_type" : "SHIELD", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "job" : "MakeShield", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 80, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "job" : "MakeArmor", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 81, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_HELM_HELM", - "item_type" : "HELM", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_HELM_HELM", - "job" : "MakeHelm", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 82, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "item_type" : "SHOES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "job" : "MakeShoes", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 83, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "item_type" : "GLOVES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - } - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "job" : "MakeGloves", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 84, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "item_type" : "PANTS", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - } - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "job" : "MakePants", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 85, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - } - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "job" : "MakeArmor", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 72, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "min_dimension" : 150, - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 87, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 88, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 89, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 90, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 91, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - } - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 92, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:BISMUTH_BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 79, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "item_type" : "SHIELD", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "job" : "MakeShield", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 80, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "job" : "MakeArmor", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 81, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_HELM_HELM", - "item_type" : "HELM", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_HELM_HELM", - "job" : "MakeHelm", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 82, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "item_type" : "SHOES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "job" : "MakeShoes", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 83, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "item_type" : "GLOVES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "job" : "MakeGloves", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 84, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "item_type" : "PANTS", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "job" : "MakePants", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 85, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "job" : "MakeArmor", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 86, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 87, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 88, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 89, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 90, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 91, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 30 - } - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 92, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 30 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:BRONZE" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 93, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "item_type" : "SHIELD", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHIELD_SHIELD", - "job" : "MakeShield", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 94, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "job" : "MakeArmor", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 95, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_HELM_HELM", - "item_type" : "HELM", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_HELM_HELM", - "job" : "MakeHelm", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 96, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "item_type" : "SHOES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_SHOES_BOOTS", - "job" : "MakeShoes", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 97, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "item_type" : "GLOVES", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - } - ], - "item_subtype" : "ITEM_GLOVES_GAUNTLETS", - "job" : "MakeGloves", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 98, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "item_type" : "PANTS", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - } - ], - "item_subtype" : "ITEM_PANTS_GREAVES", - "job" : "MakePants", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 99, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 20 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "item_type" : "ARMOR", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 20 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 20 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_ARMOR_MAIL_SHIRT", - "item_type" : "ARMOR", - "value" : 5 - } - ], - "item_subtype" : "ITEM_ARMOR_BREASTPLATE", - "job" : "MakeArmor", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 100, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_MACE", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 101, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "item_type" : "WEAPON", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_HAMMER_WAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 102, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SPEAR", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 103, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_SWORD_SHORT", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 104, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 10 - } - ], - "item_subtype" : "ITEM_WEAPON_AXE_BATTLE", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 105, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 10 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 30 - } - ], - "item_subtype" : "ITEM_WEAPON_PICK", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - }, - { - "amount_left" : 1, - "amount_total" : 1, - "frequency" : "Daily", - "id" : 106, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "INORGANIC:COPPER", - "value" : 30 - }, - { - "condition" : "AtLeast", - "item_type" : "BAR", - "material" : "COAL", - "value" : 100 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BRONZE", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:IRON", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:STEEL", - "value" : 30 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:SILVER", - "value" : 5 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:PLATINUM", - "value" : 5 - }, - { - "condition" : "AtMost", - "flags" : - [ - "metal" - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "item_type" : "WEAPON", - "value" : 10 - }, - { - "condition" : "LessThan", - "item_type" : "BAR", - "material" : "INORGANIC:BISMUTH_BRONZE", - "value" : 30 - } - ], - "item_subtype" : "ITEM_WEAPON_CROSSBOW", - "job" : "MakeWeapon", - "material" : "INORGANIC:COPPER" - } -] diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 08e05c6ca..7082d2434 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -104,13 +104,15 @@ Orders are missing for plaster powder until DF :bug:`11803` is fixed. This collection adds high-volume smelting jobs for military-grade metal ores and produces weapons and armor: -- leather backpacks/waterskins/cloaks/quivers/armor +- leather backpacks/waterskins/quivers/armor +- silk cloaks - bone/wooden bolts - smelting for platinum, silver, steel, bronze, bismuth bronze, and copper (and their dependencies) - bronze/bismuth bronze/copper bolts -- silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, +- steel/silver/iron/bismuth bronze/bronze/copper weapons and armor, with checks to ensure only the best available materials are being used +- wooden shields (if metal isn't available) If you set a stockpile to take weapons and armor of less than masterwork quality and turn on `automelt` (like what `dreamfort` provides on its industry level), @@ -120,16 +122,6 @@ Make sure you have a lot of fuel (or magma forges and furnaces) before you turn This file should only be imported, of course, if you need to equip a military. -:source:`library/military_include_artifact_materials ` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As above, but this collection will also allow creation of platinum blunt weapons. -Normally these are only created by artifact moods, work orders can't be created -manually for them. - -- platinum/silver/steel/iron/bismuth bronze/bronze/copper weapons and armor, - with checks to ensure only the best available materials are being used - :source:`library/smelting ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From f43bfed7f21406429fd53976d1fc2f8cf90d1a9c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 15:53:11 -0700 Subject: [PATCH 1167/2222] remove easy meals; add bins; document jugs --- data/orders/basic.json | 99 ++++++++++++++++++++--------------------- docs/plugins/orders.rst | 7 ++- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/data/orders/basic.json b/data/orders/basic.json index 16b05e0ba..8e271ba04 100644 --- a/data/orders/basic.json +++ b/data/orders/basic.json @@ -1,31 +1,9 @@ [ - { - "amount_left" : 150, - "amount_total" : 150, - "frequency" : "Monthly", - "id" : 0, - "is_active" : false, - "is_validated" : false, - "item_conditions" : - [ - { - "condition" : "LessThan", - "flags" : - [ - "unrotten" - ], - "item_type" : "FOOD", - "value" : 400 - } - ], - "job" : "PrepareMeal", - "meal_ingredients" : 2 - }, { "amount_left" : 10, "amount_total" : 10, "frequency" : "Daily", - "id" : 1, + "id" : 0, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -47,7 +25,7 @@ "unrotten", "cookable" ], - "value" : 500 + "value" : 80 }, { "condition" : "AtMost", @@ -57,15 +35,6 @@ ], "item_type" : "FOOD", "value" : 3500 - }, - { - "condition" : "AtLeast", - "flags" : - [ - "unrotten" - ], - "item_type" : "FOOD", - "value" : 400 } ], "job" : "PrepareMeal", @@ -75,7 +44,7 @@ "amount_left" : 2, "amount_total" : 2, "frequency" : "Daily", - "id" : 2, + "id" : 1, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -112,7 +81,7 @@ "amount_left" : 2, "amount_total" : 2, "frequency" : "Daily", - "id" : 3, + "id" : 2, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -149,7 +118,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 4, + "id" : 3, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -170,7 +139,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 5, + "id" : 4, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -205,7 +174,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 6, + "id" : 5, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -237,7 +206,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 7, + "id" : 6, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -268,7 +237,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 8, + "id" : 7, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -290,7 +259,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 9, + "id" : 8, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -322,7 +291,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 10, + "id" : 9, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -353,7 +322,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 11, + "id" : 10, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -386,7 +355,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 12, + "id" : 11, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -426,7 +395,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 13, + "id" : 12, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -452,7 +421,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 14, + "id" : 13, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -487,7 +456,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 15, + "id" : 14, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -522,7 +491,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 16, + "id" : 15, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -557,7 +526,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 17, + "id" : 16, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -587,7 +556,7 @@ "amount_left" : 1, "amount_total" : 1, "frequency" : "Daily", - "id" : 18, + "id" : 17, "is_active" : false, "is_validated" : false, "item_conditions" : @@ -618,6 +587,36 @@ "job" : "MakeTool", "material" : "INORGANIC" }, + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 18, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "item_type" : "WOOD", + "value" : 50 + }, + { + "condition" : "AtMost", + "flags" : + [ + "empty" + ], + "item_type" : "BIN", + "value" : 5 + } + ], + "job" : "ConstructBin", + "material_category" : + [ + "wood" + ] + }, { "amount_left" : 1, "amount_total" : 1, diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 7082d2434..46a004081 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -67,7 +67,7 @@ This collection of orders handles basic fort necessities: - prepared meals and food products (and by-products like oil) - booze/mead - thread/cloth/dye -- pots/jugs/buckets/mugs +- pots/bins/jugs/buckets/mugs - bags of leather, cloth, silk, and yarn - crafts, totems, and shleggings from otherwise unusable by-products - mechanisms/cages @@ -80,7 +80,10 @@ This collection of orders handles basic fort necessities: You should import it as soon as you have enough dwarves to perform the tasks. Right after the first migration wave is usually a good time. -Armok's note: shleggings? Yes, `shleggings `__. +Note that the jugs are specifically made out of wood. This is so, as long as you don't may any other "Tools" out of wood, you can have a stockpile just for jugs by restricting a finished goods stockpile to only take wooden tools. + +Armok's additional note: "shleggings? Yes, +`shleggings `__." :source:`library/furnace ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 04b1b0f84d69ae41890aeda7d71d36eac0e69eb9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 16:04:33 -0700 Subject: [PATCH 1168/2222] update changelog --- docs/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..f2cbeb0af 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `orders`: update orders in orders library for prepared meals, bins, archer uniforms, and weapons - Terminal console no longer appears in front of the game window on startup - `gui/design`: Improved performance for drawing shapes @@ -56,6 +57,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua ## Removed +- `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. # 50.08-r1 From 368a9fbc2eec722c1eaf47ffe99ab3c17a89732e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 16:34:22 -0700 Subject: [PATCH 1169/2222] update dreamfort traffic patterns --- data/blueprints/dreamfort.csv | 223 ++++++++++++++++++++++++++++------ docs/changelog.txt | 1 + 2 files changed, 184 insertions(+), 40 deletions(-) diff --git a/data/blueprints/dreamfort.csv b/data/blueprints/dreamfort.csv index c5566920b..66ce693b8 100644 --- a/data/blueprints/dreamfort.csv +++ b/data/blueprints/dreamfort.csv @@ -1890,7 +1890,7 @@ Workshops: - 1x Dyer - 1x Loom - 1x Clothier -"" + "" Manual steps you have to take: - Assign minecarts to your quantum stockpile hauling routes @@ -1954,9 +1954,46 @@ Industry Walkthrough: "#meta label(industry2) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) build workshops and stockpiles, configure stockpiles" +traffic/industry_traffic build/industry_build place/industry_place query/industry_query +#dig label(industry_traffic) start(18; 18; central stairs) hidden() + + +,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,oh,ol,ol,ol,oh,ol,ol,ol,oh,ol,ol,ol,oh +,,,,oh,oh,oh,oh,oh,oh,oh,oh,ol,ol,ol,oh,ol,ol,ol,oh,ol,ol,ol,oh,oh,oh,oh,oh,oh,oh,oh +,,,,oh,`,`,`,`,`,`,oh,ol,ol,ol,oh,ol,ol,ol,oh,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,oh,`,`,`,`,`,`,oh,ol,ol,ol,oh,oh,oh,oh,oh,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,oh,`,`,`,`,`,`,oh,ol,ol,ol,oh,oh,`,oh,oh,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,oh,`,`,`,`,`,`,oh,ol,ol,ol,oh,oh,`,oh,oh,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,oh,`,`,`,`,`,`,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,oh,`,`,`,`,`,`,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,oh,oh,oh,oh,oh,oh,oh,oh,` +,,`,ol,ol,ol,ol,ol,ol,ol,ol,ol,oh,oh,oh,`,`,`,`,`,oh,oh,oh,ol,ol,ol,ol,ol,ol,ol,ol,ol,` +,,`,ol,ol,ol,ol,ol,ol,ol,ol,ol,oh,oh,oh,`,`,`,`,`,oh,oh,oh,ol,ol,ol,ol,ol,ol,ol,ol,ol,` +,,`,ol,ol,ol,ol,ol,ol,ol,ol,ol,oh,oh,,,`,,`,,,oh,oh,ol,ol,ol,ol,ol,ol,ol,ol,ol,` +,,`,oh,oh,oh,oh,oh,oh,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,oh,oh,oh,oh,oh,oh,` +,,`,ol,ol,ol,oh,oh,oh,`,`,`,`,`,`,`,,,,`,`,`,`,`,`,`,oh,oh,oh,ol,ol,ol,` +,,`,ol,ol,ol,oh,`,`,`,`,`,`,`,,`,,`,,`,,`,`,`,`,`,`,`,oh,ol,ol,ol,` +,,`,ol,ol,ol,oh,oh,oh,`,`,`,`,`,`,`,,,,`,`,`,`,`,`,`,oh,oh,oh,ol,ol,ol,` +,,`,oh,oh,oh,oh,oh,oh,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,oh,oh,oh,oh,oh,oh,` +,,`,ol,ol,ol,ol,ol,ol,ol,ol,ol,oh,oh,,,`,,`,,,oh,oh,ol,ol,ol,ol,ol,ol,ol,ol,ol,` +,,`,ol,ol,ol,ol,ol,ol,ol,ol,ol,oh,oh,oh,`,`,`,`,`,oh,oh,oh,ol,ol,ol,ol,ol,ol,ol,ol,ol,` +,,`,ol,ol,ol,ol,ol,ol,ol,ol,ol,oh,oh,oh,`,`,`,`,`,oh,oh,oh,ol,ol,ol,ol,ol,ol,ol,ol,ol,` +,,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,oh,oh,oh,oh,oh,oh,oh,oh,` +,,,,`,`,`,`,`,`,`,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,`,`,`,`,`,`,`,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,`,`,`,`,`,`,`,oh,ol,ol,ol,oh,oh,`,oh,oh,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,oh,oh,oh,oh,oh,oh,oh,oh,ol,ol,ol,`,oh,`,oh,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,`,`,`,`,`,`,`,oh,ol,ol,ol,`,`,`,`,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,`,`,`,`,`,`,`,oh,ol,ol,ol,`,ol,ol,ol,`,ol,ol,ol,oh,`,`,`,`,`,`,oh +,,,,`,`,`,`,`,`,`,oh,ol,ol,ol,`,ol,ol,ol,`,ol,ol,ol,oh,oh,oh,oh,oh,oh,oh,oh +,,,,,,,,,,,oh,ol,ol,ol,oh,ol,ol,ol,oh,ol,ol,ol,oh +,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` + + #build label(industry_build) start(18; 18) hidden() @@ -2052,7 +2089,7 @@ query/industry_query ,,~,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,` ,,~,`,`,`,`,`,`,"{givename name=""wood feeder""}",~,"{givename name=""goods feeder""}",nocontainers,~,,`,`,`,`,`,,craftrefuse,,,,~,`,`,`,`,`,`,` ,,t{Right 5}{Down}&,`,`,`,`,`,`,~,~,{tallow}{permitwax},~,~,`,`,,,,`,`,"{givename name=""cloth/bones feeder""}",g{Up 3}{Right 5}&,~,~,~,`,`,`,`,`,`,` -,,`,`,~,`,`,"{quantum name=""goods/wood quantum""}g{Up 13}{Right 10}&","{quantumstop name=""Goods/Wood quantum"" sp_links=""{sp_link move={Right} move_back={Left}}{sp_link move=""""{Right 5}"""" move_back=""""{Left 5}""""}{sp_link move=""""{Down}{Right 5}"""" move_back=""""{Left 5}{Up}""""}""}{givename name=""goods/wood dumper""}",~,~,{forbidcrafts}{forbidgoblets},~,~,,`,,`,,`,,nocontainers,~,~,~,~,"{quantumstopfromwest name=""Clothier/Bones quantum""}{givename name=""cloth/bones dumper""}","{quantum name=""cloth/bones quantum""}g{Up 4}&",`,`,~,`,` +,,`,`,~,`,`,"{quantum name=""goods/wood quantum""}g{Up 13}{Right 10}&","{quantumstop name=""Goods/Wood quantum"" sp_links=""{sp_link move={Right} move_back={Left}}{sp_link move=""""{Right 5}"""" move_back=""""{Left 5}""""}{sp_link move=""""{Down}{Right 5}"""" move_back=""""{Left 5}{Up}""""}""}{givename name=""goods/wood dumper""}",~,~,{forbidcrafts}{forbidgoblets},~,~,,`,,`,,`,,nocontainers,~,~,~,~,"{quantumstopfromwest name=""Clothier/Bones quantum""}{givename name=""cloth/bones dumper""}","{quantum name=""cloth/bones quantum""}g{Up 4}{Right 3}&",`,`,~,`,` ,,miscliquid,`,`,`,`,`,`,~,~,"{givename name=""furniture feeder""}",~,~,`,`,,,,`,`,forbidadamantinethread,~,~,~,~,`,`,`,`,`,`,` ,,"{givename name=""miscliquid""}",`,`,`,`,`,`,~,~,forbidsand,~,~,,`,`,`,`,`,,dye,~,~,~,~,`,`,`,`,`,`,` ,,~,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,` @@ -2206,6 +2243,7 @@ Services Walkthrough: "#meta label(services2) start(central stairs) message(Remember to enqueue manager orders for this blueprint. Once furniture has been placed, continue with /services3.) dining hall anchors, stockpiles, hospital, garbage dump" +traffic/services_traffic zones/services_zones build/services_build place/services_place @@ -2221,6 +2259,108 @@ build2/services_build2 build3/services_build3 place_jail/services_place_jail query_jail/services_query_jail +#dig label(services_traffic) start(18; 18) hidden() keep lollygaggers out of the cisterns + +,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,,or,,or,or,or +,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,or,or,or,or,or,or +,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,,or,,or,or,or +,,ol,,,,ol,,,,ol,,,,,,,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,,or,or,or,,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,,or,or,or,,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,,,,or,,,,,,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,,,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,,or,,or,,or +,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,,,or,,or,,,,,or,,or,,or,,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,oh,oh,oh,oh,oh,,,,or,or,or,or,or,or,or,or,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,or,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,`,,oh,`,`,`,oh,,`,,oh,oh,oh,`,oh,or,or,or,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,or,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,oh,oh,oh,oh,oh,,,,or,or,or,or,or,or,or,or,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,,,or,,,,,,or,,or,,or,,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,,,,,,,,,or,,or,,or,,or +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh +,,,,oh,oh,,oh,oh +,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh +,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh + +#> + +,,,,,,,,,,,,,or,or,or,,or,,or,or,or +,,,,,,,,,,,,,or,or,or,or,or,or,or,or,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,or,or,or,,or,,or,or,or +,,,,,,,,,,,,,or,or,or,or,or,or,or,or,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,,,,,or +,,,,,,,,,,,,,,,,,or,or,or,or,or,or,or,or,or,or +,,,,,,,,,,,,,,,,,or,,,,,,,,,or +,,,,,,,,,,,,,,,,,or,,,,,,,,,or +,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,,or +,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or,or +,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or,or +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,` + +#> + +,,,,,,,,,,,,,or,or,or,,,,or,or,or + + + +,,,,,,,,,,,,,or,or,or,,,,or,or,or + + + + + + + + + +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or,or +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,` + +#> + +,,,,,,,,,,,,,,or,or,,,,or,or + + + +,,,,,,,,,,,,,,or,or,,,,or,or + + + + + + + + + +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or +,,,,,,,,,,,,,,,`,`,`,`,` +,,,,,,,,,,,,,,,`,`,`,`,` + "#zone label(services_zones) start(18; 18) hidden() message(If you'd like to fill your wells via bucket brigade, activate the inactive pond zones one level down from where the wells will be built.) garbage dump, hospital, and pond zones" ,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` @@ -2575,42 +2715,6 @@ query_jail/services_query_jail ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` -#dig label(services_traffic) start(18; 18) hidden() promote the tavern as the place to eat - -,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,,or,,or,or,or -,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,or,or,or,or,or,or -,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,,or,,or,or,or -,,ol,,,,ol,,,,ol,,,,,,,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,,,,or,,,,,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,,,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,,or,,or,,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,,,or,,or,,,,,or,,or,,or,,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,oh,oh,oh,oh,oh,,,,or,or,or,or,or,or,or,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,`,,oh,`,`,`,oh,,`,,oh,oh,oh,`,oh,or,or,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,oh,oh,oh,oh,oh,,,,or,or,or,or,or,or,or,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,,,`,,,,,,or,,or,,or,,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,,,,,,,,,or,,or,,or,,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,,,,oh,oh,,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh - "#build label(services_build3) start(18; 18) hidden() jail, statues" ,~,~,~,,~,~,~,,~,~,~,,t,l,b,,`,,t,l,b @@ -2946,8 +3050,47 @@ Apartments Walkthrough: ,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d -"#build label(suites2) start(18; 18; central ramp) message(Remember to enqueue manager orders for this blueprint. -bedrooms are left unconfigured so you can assign them to specific nobles)" +"#meta label(suites2) start(central ramp) message(Remember to enqueue manager orders for this blueprint. +bedrooms are left unconfigured so you can assign them to specific nobles.) build furniture and set traffic patterns" +traffic_suites/suites_traffic +build_suites/suites_build +#dig label(suites_traffic) start(18; 18; central ramp) hidden() + +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,,,,or,,,,,,or,,,,`,,`,,,,or,,,,,,or,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,or,,,,,,or,,,,`,`,`,,,,or,,,,,,or,,,,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,,,,or,,,,,,or,,,,`,`,`,,,,or,,,,,,or,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,or,,,,,,or,,,,`,,`,,,,or,,,,,,or,,,,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` + +#build label(suites_build) start(18; 18; central ramp) hidden() ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..75050ff73 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,6 +48,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - Terminal console no longer appears in front of the game window on startup - `gui/design`: Improved performance for drawing shapes +- Dreamfort: improve traffic patterns throughout the fortress (stockpiles and zones are still not working, pending updates in `quickfort`) ## Documentation From 11361975f903e4cb424b95e4b9e5497ef45df991 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 17:12:44 -0700 Subject: [PATCH 1170/2222] look up texpos values instead of assuming they're constant --- docs/changelog.txt | 1 + library/lua/gui.lua | 2 +- library/lua/gui/widgets.lua | 43 +++++++++++++++++++------------------ library/modules/Screen.cpp | 2 +- plugins/pathable.cpp | 6 ++++-- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fc706cdba..14c2d7620 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -44,6 +44,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `work-now`: reinstated, renamed from ``workNow``: reduce the time that dwarves are left without a task after completing a job ## Fixes +- DFHack screen backgrounds now use appropriate tiles in DF Classic ## Misc Improvements - Terminal console no longer appears in front of the game window on startup diff --git a/library/lua/gui.lua b/library/lua/gui.lua index b2c90d076..7e991e1df 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -9,7 +9,7 @@ local getval = utils.getval local to_pen = dfhack.pen.parse -CLEAR_PEN = to_pen{tile=909, ch=32, fg=0, bg=0, write_to_lower=true} +CLEAR_PEN = to_pen{tile=df.global.init.texpos_border_interior, ch=32, fg=0, bg=0, write_to_lower=true} TRANSPARENT_PEN = to_pen{tile=0, ch=0} KEEP_LOWER_PEN = to_pen{ch=32, fg=0, bg=0, keep_lower=true} diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 7aa79cbc3..5653dff94 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -853,7 +853,7 @@ local function scrollbar_is_visible(scrollbar) return scrollbar.elems_per_page < scrollbar.num_elems end -local SBSO = 922 --Scroll Bar Spritesheet Offset / change this to point to a different spritesheet (ui themes, anyone? :p) +local SBSO = df.global.init.scrollbar_texpos[0] --Scroll Bar Spritesheet Offset / change this to point to a different spritesheet (ui themes, anyone? :p) local SCROLLBAR_UP_LEFT_PEN = to_pen{tile=SBSO+0, ch=47, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_UP_RIGHT_PEN = to_pen{tile=SBSO+1, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} local SCROLLBAR_DOWN_LEFT_PEN = to_pen{tile=SBSO+24, ch=92, fg=COLOR_CYAN, bg=COLOR_BLACK} @@ -2144,34 +2144,35 @@ function FilteredList:onFilterChar(char, text) return true end +local TSO = df.global.init.tabs_texpos[0] -- tab spritesheet offset local DEFAULT_ACTIVE_TAB_PENS = { text_mode_tab_pen=to_pen{fg=COLOR_YELLOW}, text_mode_label_pen=to_pen{fg=COLOR_WHITE}, - lt=to_pen{tile=1005, write_to_lower=true}, - lt2=to_pen{tile=1006, write_to_lower=true}, - t=to_pen{tile=1007, fg=COLOR_BLACK, write_to_lower=true, top_of_text=true}, - rt2=to_pen{tile=1008, write_to_lower=true}, - rt=to_pen{tile=1009, write_to_lower=true}, - lb=to_pen{tile=1015, write_to_lower=true}, - lb2=to_pen{tile=1016, write_to_lower=true}, - b=to_pen{tile=1017, fg=COLOR_BLACK, write_to_lower=true, bottom_of_text=true}, - rb2=to_pen{tile=1018, write_to_lower=true}, - rb=to_pen{tile=1019, write_to_lower=true}, + lt=to_pen{tile=TSO+5, write_to_lower=true}, + lt2=to_pen{tile=TSO+6, write_to_lower=true}, + t=to_pen{tile=TSO+7, fg=COLOR_BLACK, write_to_lower=true, top_of_text=true}, + rt2=to_pen{tile=TSO+8, write_to_lower=true}, + rt=to_pen{tile=TSO+9, write_to_lower=true}, + lb=to_pen{tile=TSO+15, write_to_lower=true}, + lb2=to_pen{tile=TSO+16, write_to_lower=true}, + b=to_pen{tile=TSO+17, fg=COLOR_BLACK, write_to_lower=true, bottom_of_text=true}, + rb2=to_pen{tile=TSO+18, write_to_lower=true}, + rb=to_pen{tile=TSO+19, write_to_lower=true}, } local DEFAULT_INACTIVE_TAB_PENS = { text_mode_tab_pen=to_pen{fg=COLOR_BROWN}, text_mode_label_pen=to_pen{fg=COLOR_DARKGREY}, - lt=to_pen{tile=1000, write_to_lower=true}, - lt2=to_pen{tile=1001, write_to_lower=true}, - t=to_pen{tile=1002, fg=COLOR_WHITE, write_to_lower=true, top_of_text=true}, - rt2=to_pen{tile=1003, write_to_lower=true}, - rt=to_pen{tile=1004, write_to_lower=true}, - lb=to_pen{tile=1010, write_to_lower=true}, - lb2=to_pen{tile=1011, write_to_lower=true}, - b=to_pen{tile=1012, fg=COLOR_WHITE, write_to_lower=true, bottom_of_text=true}, - rb2=to_pen{tile=1013, write_to_lower=true}, - rb=to_pen{tile=1014, write_to_lower=true}, + lt=to_pen{tile=TSO+0, write_to_lower=true}, + lt2=to_pen{tile=TSO+1, write_to_lower=true}, + t=to_pen{tile=TSO+2, fg=COLOR_WHITE, write_to_lower=true, top_of_text=true}, + rt2=to_pen{tile=TSO+3, write_to_lower=true}, + rt=to_pen{tile=TSO+4, write_to_lower=true}, + lb=to_pen{tile=TSO+10, write_to_lower=true}, + lb2=to_pen{tile=TSO+11, write_to_lower=true}, + b=to_pen{tile=TSO+12, fg=COLOR_WHITE, write_to_lower=true, bottom_of_text=true}, + rb2=to_pen{tile=TSO+13, write_to_lower=true}, + rb=to_pen{tile=TSO+14, write_to_lower=true}, } --------- diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index dcb18dd91..6ccf246aa 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -209,7 +209,7 @@ static bool doSetTile_default(const Pen &pen, int x, int y, bool map) } } else if (pen.ch) { screen[0] = uint8_t(pen.ch); - *texpos_lower = 909; // basic black background + *texpos_lower = df::global::init->texpos_border_interior; // basic black background } auto rgb_fg = &gps->uccolor[fg][0]; diff --git a/plugins/pathable.cpp b/plugins/pathable.cpp index dd26a712f..06394f838 100644 --- a/plugins/pathable.cpp +++ b/plugins/pathable.cpp @@ -7,6 +7,8 @@ #include "LuaTools.h" #include "PluginManager.h" +#include "df/init.h" + using namespace DFHack; DFHACK_PLUGIN("pathable"); @@ -37,8 +39,8 @@ static void paintScreen(df::coord target, bool skip_unrevealed = false) { int selected_tile_texpos = 0; Screen::findGraphicsTile("CURSORS", 4, 3, &selected_tile_texpos); - long pathable_tile_texpos = 779; - long unpathable_tile_texpos = 782; + long pathable_tile_texpos = df::global::init->load_bar_texpos[1]; + long unpathable_tile_texpos = df::global::init->load_bar_texpos[4]; long on_off_texpos = Textures::getOnOffTexposStart(); if (on_off_texpos > 0) { pathable_tile_texpos = on_off_texpos + 0; From 899422aaf8291e7028d8b30d7316b1d1f3e90704 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 17:26:16 -0700 Subject: [PATCH 1171/2222] give widgets.TabBar default hotkeys --- docs/dev/Lua API.rst | 4 +++- library/lua/gui/widgets.lua | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 85e51d11d..db5a9d59c 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -5113,7 +5113,9 @@ This widget implements a set of one or more tabs to allow navigation between gro the width of the window and will continue rendering on the next line(s) if all tabs cannot fit on a single line. :key: Specifies a keybinding that can be used to switch to the next tab. -:key_back: Specifies a keybinding that can be used to switch to the previous tab. + Defaults to ``CUSTOM_CTRL_T``. +:key_back: Specifies a keybinding that can be used to switch to the previous + tab. Defaults to ``CUSTOM_CTRL_Y``. :labels: A table of strings; entry representing the label text for a single tab. The order of the entries determines the order the tabs will appear in. :on_select: Callback executed when a tab is selected. It receives the selected tab index as an argument. The provided function diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 7aa79cbc3..aa18d9fa9 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2241,8 +2241,8 @@ TabBar.ATTRS{ active_tab_pens=DEFAULT_ACTIVE_TAB_PENS, inactive_tab_pens=DEFAULT_INACTIVE_TAB_PENS, get_pens=DEFAULT_NIL, - key=DEFAULT_NIL, - key_back=DEFAULT_NIL, + key='CUSTOM_CTRL_T', + key_back='CUSTOM_CTRL_Y', } function TabBar:init() From 1d24f812dd36ad4b43dd439e30931d1481d87416 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 17 May 2023 07:13:14 +0000 Subject: [PATCH 1172/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 1ec3602f9..2554b931b 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 1ec3602f9d896c9dc76b26b3be845a5aa7544093 +Subproject commit 2554b931bfc23564de5bc7cb28eed673e82d06d3 From de2e29a2e3679b560edcec9bf7dec766bc0a43ba Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 17 May 2023 02:34:19 -0700 Subject: [PATCH 1173/2222] add github action to clean up PR caches after merge --- .github/workflows/clean-cache.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/clean-cache.yml diff --git a/.github/workflows/clean-cache.yml b/.github/workflows/clean-cache.yml new file mode 100644 index 000000000..309fae105 --- /dev/null +++ b/.github/workflows/clean-cache.yml @@ -0,0 +1,30 @@ +name: Clean up PR caches +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + REPO=${{ github.repository }} + BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" + + echo "Fetching list of cache keys" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1) + + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR; do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ffe8de12928e9786431947a08bbd7f63b4bc067f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 17 May 2023 11:10:20 -0700 Subject: [PATCH 1174/2222] clean caches in root context, not the PR --- .github/workflows/clean-cache.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clean-cache.yml b/.github/workflows/clean-cache.yml index 309fae105..d3e12959d 100644 --- a/.github/workflows/clean-cache.yml +++ b/.github/workflows/clean-cache.yml @@ -1,6 +1,6 @@ name: Clean up PR caches on: - pull_request: + pull_request_target: types: - closed From 9d8a825eb4af1de095f425b4286c95186442bd18 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 15 May 2023 15:17:06 -0700 Subject: [PATCH 1175/2222] adjust usage to game structure reunification --- plugins/autolabor/autolabor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 53a01a6a6..72bb4d84e 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -34,8 +34,8 @@ #include #include #include +#include #include -#include #include @@ -51,7 +51,7 @@ using namespace df::enums; DFHACK_PLUGIN("autolabor"); REQUIRE_GLOBAL(plotinfo); REQUIRE_GLOBAL(world); -REQUIRE_GLOBAL(game_extra); +REQUIRE_GLOBAL(game); #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) @@ -414,7 +414,7 @@ static void enable_plugin(color_ostream &out) cleanup_state(); init_state(); - df::global::game_extra->external_flag |= 1; // shut down DF's work detail system + game->external_flag |= 1; // shut down DF's work detail system } DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) @@ -1084,7 +1084,7 @@ DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) enable_autolabor = false; setOptionEnabled(CF_ENABLED, false); - df::global::game_extra->external_flag &= ~1; // reenable DF's work detail system + game->external_flag &= ~1; // reenable DF's work detail system out << "Autolabor is disabled." << std::endl; } From aa6baae834537db062f05fac2846af2c818afbae Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 17 May 2023 11:14:00 -0700 Subject: [PATCH 1176/2222] update structures ref --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 22d9bc0bc..9891be326 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 22d9bc0bc1847def8a6c62893104f36262e63e98 +Subproject commit 9891be32663435f2fe875e27c70010d5618de735 From 5c7d9f228d47a7c1a34a099309254703fcb916e5 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 17 May 2023 18:55:27 +0000 Subject: [PATCH 1177/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 2554b931b..2a646f9c7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 2554b931bfc23564de5bc7cb28eed673e82d06d3 +Subproject commit 2a646f9c7204253cae70bbfb5ed194182e0ac373 From 6aede45135c5b14d0c8cbfefb8540fd846d75e9c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 16 May 2023 12:05:53 -0700 Subject: [PATCH 1178/2222] add links to quickstart-guide and gui/control-panel to the title screen --- docs/changelog.txt | 1 + docs/plugins/overlay.rst | 7 +++++++ plugins/lua/overlay.lua | 17 ++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c9f9fca89..59974909e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/design`: Improved performance for drawing shapes - Dreamfort: improve traffic patterns throughout the fortress (stockpiles and zones are still not working, pending updates in `quickfort`) - Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. +- `overlay`: added links to the quickstart guide and the control panel on the DF title screen ## Documentation diff --git a/docs/plugins/overlay.rst b/docs/plugins/overlay.rst index 313220fd4..fd196158f 100644 --- a/docs/plugins/overlay.rst +++ b/docs/plugins/overlay.rst @@ -66,3 +66,10 @@ For easy reference, the corners can be found at the following coordinates: :(-1, 1): top right corner :(1, -1): lower left corner :(-1, -1): lower right corner + +Overlay +------- + +The `overlay` plugin also provides a standard overlay itself: +``title_version``, which displays the DFHack version on the DF title screen, +along with quick links to `quickstart-guide` and `gui/control-panel`. diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 9f41cb035..fda1edc95 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -580,7 +580,8 @@ TitleVersionOverlay.ATTRS{ default_pos={x=7, y=2}, default_enabled=true, viewscreens='title/Default', - frame={w=35, h=3}, + frame={w=35, h=5}, + autoarrange_subviews=1, } function TitleVersionOverlay:init() @@ -602,6 +603,20 @@ function TitleVersionOverlay:init() text=text, text_pen=COLOR_WHITE, }, + widgets.HotkeyLabel{ + frame={l=0}, + label='Quickstart guide', + auto_width=true, + key='STRING_A063', + on_activate=function() dfhack.run_script('quickstart-guide') end, + }, + widgets.HotkeyLabel{ + frame={l=0}, + label='Control panel', + auto_width=true, + key='STRING_A047', + on_activate=function() dfhack.run_script('gui/control-panel') end, + }, } end From d4f41141ef907f336c56197a9ac3be62e751eda7 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 17 May 2023 16:45:35 -0700 Subject: [PATCH 1179/2222] fix game_extra reference --- plugins/lua/autolabor.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/autolabor.lua b/plugins/lua/autolabor.lua index a468b30e1..a2c0e88cd 100644 --- a/plugins/lua/autolabor.lua +++ b/plugins/lua/autolabor.lua @@ -40,7 +40,7 @@ function AutolaborOverlay:init() end function AutolaborOverlay:render(dc) - if df.global.game_extra.external_flag ~= 1 then return end + if df.global.game.external_flag ~= 1 then return end AutolaborOverlay.super.render(self, dc) end From b9afb94ba6f5405d681ccb289419bf1e72f5d02e Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 18 May 2023 07:12:59 +0000 Subject: [PATCH 1180/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 2a646f9c7..97557b923 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 2a646f9c7204253cae70bbfb5ed194182e0ac373 +Subproject commit 97557b9239a5ad8297f6a32651b78fd9dbb333ec From eb742a603dcc945a09181662c9ea9138b78f81ba Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 11:22:58 +0200 Subject: [PATCH 1181/2222] renamed XYZ_FRAME to FRAME_XYZ --- library/include/modules/Textures.h | 10 ++++---- library/lua/gui.lua | 33 +++++++++++++++--------- library/modules/Textures.cpp | 40 +++++++++++++++--------------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/library/include/modules/Textures.h b/library/include/modules/Textures.h index 95e628d5a..1664f6081 100644 --- a/library/include/modules/Textures.h +++ b/library/include/modules/Textures.h @@ -54,11 +54,11 @@ DFHACK_EXPORT long getControlPanelTexposStart(); /** * Get the first texpos for the DFHack borders. Each is a 7x3 grid. */ -DFHACK_EXPORT long getThinBordersTexposStart(); -DFHACK_EXPORT long getMediumBordersTexposStart(); -DFHACK_EXPORT long getBoldBordersTexposStart(); -DFHACK_EXPORT long getPanelBordersTexposStart(); -DFHACK_EXPORT long getWindowBordersTexposStart(); +DFHACK_EXPORT long getBorderThinTexposStart(); +DFHACK_EXPORT long getBorderMediumTexposStart(); +DFHACK_EXPORT long getBorderBoldTexposStart(); +DFHACK_EXPORT long getBorderPanelTexposStart(); +DFHACK_EXPORT long getBorderWindowTexposStart(); } } diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 7e991e1df..311edc1f3 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -905,7 +905,7 @@ local BASE_FRAME = { } local function make_frame(name, double_line) - local texpos = dfhack.textures['get'..name..'BordersTexposStart']() + local texpos = dfhack.textures['getBorder'..name..'TexposStart']() local tp = function(offset) if texpos == -1 then return nil end return texpos + offset @@ -923,17 +923,26 @@ local function make_frame(name, double_line) return frame end -WINDOW_FRAME = make_frame('Window', true) -PANEL_FRAME = make_frame('Panel', false) -MEDIUM_FRAME = make_frame('Medium', false) -BOLD_FRAME = make_frame('Bold', true) -INTERIOR_FRAME = make_frame('Thin', false) -INTERIOR_FRAME.signature_pen = false -INTERIOR_MEDIUM_FRAME = copyall(MEDIUM_FRAME) -INTERIOR_MEDIUM_FRAME.signature_pen = false +FRAME_WINDOW = make_frame('Window', true) +FRAME_PANEL = make_frame('Panel', false) +FRAME_MEDIUM = make_frame('Medium', false) +FRAME_BOLD = make_frame('Bold', true) +FRAME_INTERIOR = make_frame('Thin', false) +FRAME_INTERIOR.signature_pen = false +FRAME_INTERIOR_MEDIUM = copyall(FRAME_MEDIUM) +FRAME_INTERIOR_MEDIUM.signature_pen = false -- for compatibility with pre-steam code -GREY_LINE_FRAME = WINDOW_FRAME +GREY_LINE_FRAME = FRAME_PANEL + +-- for compatibility with deprecated frame naming scheme +WINDOW_FRAME = FRAME_WINDOW +PANEL_FRAME = FRAME_PANEL +MEDIUM_FRAME = FRAME_MEDIUM +BOLD_FRAME = FRAME_BOLD +INTERIOR_FRAME = FRAME_INTERIOR +INTERIOR_MEDIUM_FRAME = FRAME_INTERIOR_MEDIUM + function paint_frame(dc,rect,style,title,inactive,pause_forced,resizable) local pen = style.frame_pen @@ -942,8 +951,8 @@ function paint_frame(dc,rect,style,title,inactive,pause_forced,resizable) dscreen.paintTile(style.rt_frame_pen or pen, x2, y1) dscreen.paintTile(style.lb_frame_pen or pen, x1, y2) local rb_frame_pen = style.rb_frame_pen - if rb_frame_pen == WINDOW_FRAME.rb_frame_pen and not resizable then - rb_frame_pen = PANEL_FRAME.rb_frame_pen + if rb_frame_pen == FRAME_WINDOW.rb_frame_pen and not resizable then + rb_frame_pen = FRAME_PANEL.rb_frame_pen end dscreen.paintTile(rb_frame_pen or pen, x2, y2) dscreen.fillRect(style.t_frame_pen or style.h_frame_pen or pen,x1+1,y1,x2-1,y1) diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index a30f879a0..40339a616 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -24,11 +24,11 @@ static long g_red_pin_texpos_start = -1; static long g_icons_texpos_start = -1; static long g_on_off_texpos_start = -1; static long g_control_panel_texpos_start = -1; -static long g_thin_borders_texpos_start = -1; -static long g_medium_borders_texpos_start = -1; -static long g_bold_borders_texpos_start = -1; -static long g_panel_borders_texpos_start = -1; -static long g_window_borders_texpos_start = -1; +static long g_border_thin_texpos_start = -1; +static long g_border_medium_texpos_start = -1; +static long g_border_bold_texpos_start = -1; +static long g_border_panel_texpos_start = -1; +static long g_border_window_texpos_start = -1; // Converts an arbitrary Surface to something like the display format // (32-bit RGBA), and converts magenta to transparency if convert_magenta is set @@ -132,15 +132,15 @@ void Textures::init(color_ostream &out) { g_num_dfhack_textures += load_textures(out, "hack/data/art/control-panel.png", &g_control_panel_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-thin.png", - &g_thin_borders_texpos_start); + &g_border_thin_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-medium.png", - &g_medium_borders_texpos_start); + &g_border_medium_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-bold.png", - &g_bold_borders_texpos_start); + &g_border_bold_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-panel.png", - &g_panel_borders_texpos_start); + &g_border_panel_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-window.png", - &g_window_borders_texpos_start); + &g_border_window_texpos_start); DEBUG(textures,out).print("loaded %ld textures\n", g_num_dfhack_textures); @@ -197,22 +197,22 @@ long Textures::getControlPanelTexposStart() { return g_control_panel_texpos_start; } -long Textures::getThinBordersTexposStart() { - return g_thin_borders_texpos_start; +long Textures::getBorderThinTexposStart() { + return g_border_thin_texpos_start; } -long Textures::getMediumBordersTexposStart() { - return g_medium_borders_texpos_start; +long Textures::getBorderMediumTexposStart() { + return g_border_medium_texpos_start; } -long Textures::getBoldBordersTexposStart() { - return g_bold_borders_texpos_start; +long Textures::getBorderBoldTexposStart() { + return g_border_bold_texpos_start; } -long Textures::getPanelBordersTexposStart() { - return g_panel_borders_texpos_start; +long Textures::getBorderPanelTexposStart() { + return g_border_panel_texpos_start; } -long Textures::getWindowBordersTexposStart() { - return g_window_borders_texpos_start; +long Textures::getBorderWindowTexposStart() { + return g_border_window_texpos_start; } From 90fe6e7ae34132a8aa1670ae6d0cb7189a1cfd6c Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 11:30:21 +0200 Subject: [PATCH 1182/2222] swapped any instance of 'border' being after 'xyz', everywhere --- docs/dev/Lua API.rst | 16 ++++++++-------- library/LuaApi.cpp | 10 +++++----- plugins/lua/buildingplan/pens.lua | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 1d70647f7..6fb2aca2c 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4347,32 +4347,32 @@ A framed screen has the following attributes: There are the following predefined frame style tables: -* ``WINDOW_FRAME`` +* ``FRAME_WINDOW`` A frame suitable for a draggable, optionally resizable window. -* ``PANEL_FRAME`` +* ``FRAME_PANEL`` A frame suitable for a static (non-draggable, non-resizable) panel. -* ``MEDIUM_FRAME`` +* ``FRAME_MEDIUM`` A frame suitable for overlay widget panels. -* ``BOLD_FRAME`` +* ``FRAME_BOLD`` A frame suitable for a non-draggable panel meant to capture the user's focus, like an important notification, confirmation dialog or error message. -* ``INTERIOR_FRAME`` +* ``FRAME_INTERIOR`` A frame suitable for light interior accent elements. This frame does *not* have a visible ``DFHack`` signature on it, so it must not be used as the most external frame for a DFHack-owned UI. -* ``INTERIOR_MEDIUM_FRAME`` +* ``FRAME_INTERIOR_MEDIUM`` - A copy of ``MEDIUM_FRAME`` that lacks the ``DFHack`` signature. Suitable for + A copy of ``FRAME_MEDIUM`` that lacks the ``DFHack`` signature. Suitable for panels that are part of a larger widget cluster. Must *not* be used as the most external frame for a DFHack-owned UI. @@ -4509,7 +4509,7 @@ Has attributes: by 1. The attributes are identical to what is defined in the `FramedScreen class`_. When using the predefined frame styles in the ``gui`` module, remember to ``require`` the gui module and prefix the identifier with - ``gui.``, e.g. ``gui.GREY_LINE_FRAME``. + ``gui.``, e.g. ``gui.FRAME_GREY_LINE``. Has functions: diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 08903c7bd..d21ce29e5 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1716,11 +1716,11 @@ static const LuaWrapper::FunctionReg dfhack_textures_module[] = { WRAPM(Textures, getIconsTexposStart), WRAPM(Textures, getOnOffTexposStart), WRAPM(Textures, getControlPanelTexposStart), - WRAPM(Textures, getThinBordersTexposStart), - WRAPM(Textures, getMediumBordersTexposStart), - WRAPM(Textures, getBoldBordersTexposStart), - WRAPM(Textures, getPanelBordersTexposStart), - WRAPM(Textures, getWindowBordersTexposStart), + WRAPM(Textures, getBorderThinTexposStart), + WRAPM(Textures, getBorderMediumTexposStart), + WRAPM(Textures, getBorderBoldTexposStart), + WRAPM(Textures, getBorderPanelTexposStart), + WRAPM(Textures, getBorderWindowTexposStart), { NULL, NULL } }; diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index e69a4c210..c68e12eae 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -18,12 +18,12 @@ function reload_pens() GOOD_TILE_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} BAD_TILE_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} - local tb_texpos = dfhack.textures.getThinBordersTexposStart() + local tb_texpos = dfhack.textures.getBorderThinTexposStart() VERT_TOP_PEN = to_pen{tile=tp(tb_texpos, 10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=193, fg=COLOR_GREY, bg=COLOR_BLACK} - local mb_texpos = dfhack.textures.getMediumBordersTexposStart() + local mb_texpos = dfhack.textures.getBorderMediumTexposStart() HORI_LEFT_PEN = to_pen{tile=tp(mb_texpos, 12), ch=195, fg=COLOR_GREY, bg=COLOR_BLACK} HORI_MID_PEN = to_pen{tile=tp(mb_texpos, 5), ch=196, fg=COLOR_GREY, bg=COLOR_BLACK} HORI_RIGHT_PEN = to_pen{tile=tp(mb_texpos, 13), ch=180, fg=COLOR_GREY, bg=COLOR_BLACK} From c52b1cc950b3e55b27050740fb3f037d45918c09 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 12:46:21 +0200 Subject: [PATCH 1183/2222] reverted to just renaming FRAMES in gui.lua --- library/LuaApi.cpp | 10 ++++---- library/include/modules/Textures.h | 10 ++++---- library/lua/gui.lua | 2 +- library/modules/Textures.cpp | 40 +++++++++++++++--------------- plugins/lua/buildingplan/pens.lua | 4 +-- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d21ce29e5..08903c7bd 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1716,11 +1716,11 @@ static const LuaWrapper::FunctionReg dfhack_textures_module[] = { WRAPM(Textures, getIconsTexposStart), WRAPM(Textures, getOnOffTexposStart), WRAPM(Textures, getControlPanelTexposStart), - WRAPM(Textures, getBorderThinTexposStart), - WRAPM(Textures, getBorderMediumTexposStart), - WRAPM(Textures, getBorderBoldTexposStart), - WRAPM(Textures, getBorderPanelTexposStart), - WRAPM(Textures, getBorderWindowTexposStart), + WRAPM(Textures, getThinBordersTexposStart), + WRAPM(Textures, getMediumBordersTexposStart), + WRAPM(Textures, getBoldBordersTexposStart), + WRAPM(Textures, getPanelBordersTexposStart), + WRAPM(Textures, getWindowBordersTexposStart), { NULL, NULL } }; diff --git a/library/include/modules/Textures.h b/library/include/modules/Textures.h index 1664f6081..95e628d5a 100644 --- a/library/include/modules/Textures.h +++ b/library/include/modules/Textures.h @@ -54,11 +54,11 @@ DFHACK_EXPORT long getControlPanelTexposStart(); /** * Get the first texpos for the DFHack borders. Each is a 7x3 grid. */ -DFHACK_EXPORT long getBorderThinTexposStart(); -DFHACK_EXPORT long getBorderMediumTexposStart(); -DFHACK_EXPORT long getBorderBoldTexposStart(); -DFHACK_EXPORT long getBorderPanelTexposStart(); -DFHACK_EXPORT long getBorderWindowTexposStart(); +DFHACK_EXPORT long getThinBordersTexposStart(); +DFHACK_EXPORT long getMediumBordersTexposStart(); +DFHACK_EXPORT long getBoldBordersTexposStart(); +DFHACK_EXPORT long getPanelBordersTexposStart(); +DFHACK_EXPORT long getWindowBordersTexposStart(); } } diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 311edc1f3..d511cd51f 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -905,7 +905,7 @@ local BASE_FRAME = { } local function make_frame(name, double_line) - local texpos = dfhack.textures['getBorder'..name..'TexposStart']() + local texpos = dfhack.textures['get'..name..'BordersTexposStart']() local tp = function(offset) if texpos == -1 then return nil end return texpos + offset diff --git a/library/modules/Textures.cpp b/library/modules/Textures.cpp index 40339a616..a30f879a0 100644 --- a/library/modules/Textures.cpp +++ b/library/modules/Textures.cpp @@ -24,11 +24,11 @@ static long g_red_pin_texpos_start = -1; static long g_icons_texpos_start = -1; static long g_on_off_texpos_start = -1; static long g_control_panel_texpos_start = -1; -static long g_border_thin_texpos_start = -1; -static long g_border_medium_texpos_start = -1; -static long g_border_bold_texpos_start = -1; -static long g_border_panel_texpos_start = -1; -static long g_border_window_texpos_start = -1; +static long g_thin_borders_texpos_start = -1; +static long g_medium_borders_texpos_start = -1; +static long g_bold_borders_texpos_start = -1; +static long g_panel_borders_texpos_start = -1; +static long g_window_borders_texpos_start = -1; // Converts an arbitrary Surface to something like the display format // (32-bit RGBA), and converts magenta to transparency if convert_magenta is set @@ -132,15 +132,15 @@ void Textures::init(color_ostream &out) { g_num_dfhack_textures += load_textures(out, "hack/data/art/control-panel.png", &g_control_panel_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-thin.png", - &g_border_thin_texpos_start); + &g_thin_borders_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-medium.png", - &g_border_medium_texpos_start); + &g_medium_borders_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-bold.png", - &g_border_bold_texpos_start); + &g_bold_borders_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-panel.png", - &g_border_panel_texpos_start); + &g_panel_borders_texpos_start); g_num_dfhack_textures += load_textures(out, "hack/data/art/border-window.png", - &g_border_window_texpos_start); + &g_window_borders_texpos_start); DEBUG(textures,out).print("loaded %ld textures\n", g_num_dfhack_textures); @@ -197,22 +197,22 @@ long Textures::getControlPanelTexposStart() { return g_control_panel_texpos_start; } -long Textures::getBorderThinTexposStart() { - return g_border_thin_texpos_start; +long Textures::getThinBordersTexposStart() { + return g_thin_borders_texpos_start; } -long Textures::getBorderMediumTexposStart() { - return g_border_medium_texpos_start; +long Textures::getMediumBordersTexposStart() { + return g_medium_borders_texpos_start; } -long Textures::getBorderBoldTexposStart() { - return g_border_bold_texpos_start; +long Textures::getBoldBordersTexposStart() { + return g_bold_borders_texpos_start; } -long Textures::getBorderPanelTexposStart() { - return g_border_panel_texpos_start; +long Textures::getPanelBordersTexposStart() { + return g_panel_borders_texpos_start; } -long Textures::getBorderWindowTexposStart() { - return g_border_window_texpos_start; +long Textures::getWindowBordersTexposStart() { + return g_window_borders_texpos_start; } diff --git a/plugins/lua/buildingplan/pens.lua b/plugins/lua/buildingplan/pens.lua index c68e12eae..e69a4c210 100644 --- a/plugins/lua/buildingplan/pens.lua +++ b/plugins/lua/buildingplan/pens.lua @@ -18,12 +18,12 @@ function reload_pens() GOOD_TILE_PEN = to_pen{ch='o', fg=COLOR_GREEN, tile=dfhack.screen.findGraphicsTile('CURSORS', 1, 2)} BAD_TILE_PEN = to_pen{ch='X', fg=COLOR_RED, tile=dfhack.screen.findGraphicsTile('CURSORS', 3, 0)} - local tb_texpos = dfhack.textures.getBorderThinTexposStart() + local tb_texpos = dfhack.textures.getThinBordersTexposStart() VERT_TOP_PEN = to_pen{tile=tp(tb_texpos, 10), ch=194, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_MID_PEN = to_pen{tile=tp(tb_texpos, 4), ch=179, fg=COLOR_GREY, bg=COLOR_BLACK} VERT_BOT_PEN = to_pen{tile=tp(tb_texpos, 11), ch=193, fg=COLOR_GREY, bg=COLOR_BLACK} - local mb_texpos = dfhack.textures.getBorderMediumTexposStart() + local mb_texpos = dfhack.textures.getMediumBordersTexposStart() HORI_LEFT_PEN = to_pen{tile=tp(mb_texpos, 12), ch=195, fg=COLOR_GREY, bg=COLOR_BLACK} HORI_MID_PEN = to_pen{tile=tp(mb_texpos, 5), ch=196, fg=COLOR_GREY, bg=COLOR_BLACK} HORI_RIGHT_PEN = to_pen{tile=tp(mb_texpos, 13), ch=180, fg=COLOR_GREY, bg=COLOR_BLACK} From ff498fcc586c37e9464f5e682db0bcc32b4a9114 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 12:50:25 +0200 Subject: [PATCH 1184/2222] fixed overly aggressive FRAME renaming in docs --- docs/dev/Lua API.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 6fb2aca2c..52ca4d935 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4509,7 +4509,7 @@ Has attributes: by 1. The attributes are identical to what is defined in the `FramedScreen class`_. When using the predefined frame styles in the ``gui`` module, remember to ``require`` the gui module and prefix the identifier with - ``gui.``, e.g. ``gui.FRAME_GREY_LINE``. + ``gui.``, e.g. ``gui.GREY_LINE_FRAME``. Has functions: From 58519890b6eda1f29b9f1dc8ddc3c03164ebe38e Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 13:13:56 +0200 Subject: [PATCH 1185/2222] added changelog entry --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index c9f9fca89..cc717d0bf 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,6 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. +- `gui.lua`: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. From c224a534355154173f8b73b6d44cfaa47c0e5da8 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 13:21:18 +0200 Subject: [PATCH 1186/2222] fixed changelog entry... --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index cc717d0bf..4baa5e54f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,7 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. -- `gui.lua`: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) +- ``gui.lua``: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. From 69612bde0f76cdb0656a6b30e0850a5de966022b Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 13:27:27 +0200 Subject: [PATCH 1187/2222] ATTEMPTING to fix changelog entry... --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 4baa5e54f..9495658d9 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,7 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. -- ``gui.lua``: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) +- `gui`: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. From e7eb664eadde01c29e18c42ae13a80a0044d6a04 Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 13:29:26 +0200 Subject: [PATCH 1188/2222] ATTEMPTING to fix changelog entry... AGAIN --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 9495658d9..3d9ca0356 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,7 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. -- `gui`: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) +- gui.lua: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. From 4cf88a1f8e68b03bf71de050a96567913aa235bb Mon Sep 17 00:00:00 2001 From: Taxi Service Date: Thu, 18 May 2023 13:52:07 +0200 Subject: [PATCH 1189/2222] actually fixed changelog entry..? --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 3d9ca0356..c8ccdec15 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,7 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. -- gui.lua: changed frame naming scheme to `FRAME_X` rather than `X_FRAME`, and added aliases for backwards compatibility. (for example `BOLD_FRAME` is now called `FRAME_BOLD`) +- ``gui.lua``: changed frame naming scheme to ``FRAME_X`` rather than ``X_FRAME``, and added aliases for backwards compatibility. (for example ``BOLD_FRAME`` is now called ``FRAME_BOLD``) ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. From afcbee48ddf1b67073993a9223e0c22a1152ffe9 Mon Sep 17 00:00:00 2001 From: TaxiService Date: Thu, 18 May 2023 19:28:30 +0200 Subject: [PATCH 1190/2222] Update docs/changelog.txt Co-authored-by: Myk --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c8ccdec15..832a81c56 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -62,7 +62,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. -- ``gui.lua``: changed frame naming scheme to ``FRAME_X`` rather than ``X_FRAME``, and added aliases for backwards compatibility. (for example ``BOLD_FRAME`` is now called ``FRAME_BOLD``) +- ``gui``: changed frame naming scheme to ``FRAME_X`` rather than ``X_FRAME``, and added aliases for backwards compatibility. (for example ``BOLD_FRAME`` is now called ``FRAME_BOLD``) ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. From 9928ea874abe18ca577d3214378f69259f4e4298 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 17 May 2023 17:26:33 -0700 Subject: [PATCH 1191/2222] transfer Ctrl-H keybinding to gui/autodump --- data/init/dfhack.keybindings.init | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/init/dfhack.keybindings.init b/data/init/dfhack.keybindings.init index 0fc596215..f677ca301 100644 --- a/data/init/dfhack.keybindings.init +++ b/data/init/dfhack.keybindings.init @@ -43,8 +43,8 @@ keybinding add Ctrl-C spotclean # destroy the selected item keybinding add Ctrl-K@dwarfmode autodump-destroy-item -# destroy items designated for dump in the selected tile -keybinding add Ctrl-H@dwarfmode autodump-destroy-here +# bring up the autodump UI +keybinding add Ctrl-H@dwarfmode gui/autodump # apply blueprints to the map keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort From ee5553358e536bc97be7b9f7aa82a376a3e29626 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 17 May 2023 17:28:35 -0700 Subject: [PATCH 1192/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 832a81c56..64d491906 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,6 +55,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/design`: Improved performance for drawing shapes - Dreamfort: improve traffic patterns throughout the fortress (stockpiles and zones are still not working, pending updates in `quickfort`) - Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. +- `gui/autodump`: fort-mode keybinding: Ctrl-H ## Documentation From 796eb331ff4992de64e53c8021506631f178295c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 11:01:16 -0700 Subject: [PATCH 1193/2222] update scripts head --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 97557b923..0f02ade81 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 97557b9239a5ad8297f6a32651b78fd9dbb333ec +Subproject commit 0f02ade81771a7c24a4bbd6cfd7969704288f94b From 73f6c66c3e484451335cb4c266031d3776cf22bb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 11:13:47 -0700 Subject: [PATCH 1194/2222] bump version to 50.08-r2rc1 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d1d4c562..c1560c630 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,8 +192,8 @@ endif() # set up versioning. set(DF_VERSION "50.08") -set(DFHACK_RELEASE "r1") -set(DFHACK_PRERELEASE FALSE) +set(DFHACK_RELEASE "r2rc1") +set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") From 1212ee0ef8bd60cacd2b05fc8f3afb96ef1d11fb Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 14:06:00 -0700 Subject: [PATCH 1195/2222] retrieve steam SDK from a private repo --- .github/workflows/build.yml | 15 +++++---- .github/workflows/steam.yml | 13 ++++---- package/windows/CMakeLists.txt | 60 +++++++++++++++------------------- 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 131c21743..94aa49452 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,12 +72,6 @@ jobs: # - name: Download DF # run: | # sh ci/download-df.sh - - name: Restore steam SDK - uses: actions/cache@v3 - with: - path: depends/steam - key: steam-sdk-156 - enableCrossOsArchive: true - name: Configure DFHack env: CC: gcc-${{ matrix.gcc }} @@ -145,6 +139,13 @@ jobs: with: submodules: true fetch-depth: 0 + - name: Get 3rd party SDKs + uses: actions/checkout@v3 + with: + repository: DFHack/3rdparty + ref: main + token: ${{ secrets.DFHACK_3RDPARTY_TOKEN }} + path: depends - name: Fetch ccache uses: actions/cache@v3 with: @@ -155,7 +156,7 @@ jobs: ccache-win64-cross-msvc - name: Cross-compile win64 artifacts env: - CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1' + CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1' run: | cd build bash -x build-win64-from-linux.sh diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 5210b3a11..4f785969a 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -32,6 +32,13 @@ jobs: submodules: true fetch-depth: 0 ref: ${{ github.event.inputs.commit_hash }} + - name: Get 3rd party SDKs + uses: actions/checkout@v3 + with: + repository: DFHack/3rdparty + ref: main + token: ${{ secrets.DFHACK_3RDPARTY_TOKEN }} + path: depends - name: Fetch ccache uses: actions/cache@v3 with: @@ -40,12 +47,6 @@ jobs: restore-keys: | ccache-win64-cross-msvc-${{ github.event.inputs.commit_hash }} ccache-win64-cross-msvc - - name: Restore steam SDK - uses: actions/cache@v3 - with: - path: depends/steam - key: steam-sdk-156 - enableCrossOsArchive: true - name: Cross-compile win64 artifacts env: CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1' diff --git a/package/windows/CMakeLists.txt b/package/windows/CMakeLists.txt index d92d687c4..ad5469c6b 100644 --- a/package/windows/CMakeLists.txt +++ b/package/windows/CMakeLists.txt @@ -1,38 +1,32 @@ project(package_windows) -if(WIN32) - if (BUILD_DFLAUNCH) - if ((DEFINED ENV{steam_username}) AND (DEFINED ENV{steam_password})) - # download Steam SDK - set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam) - file(DOWNLOAD "https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" - ${STEAMAPI_DIR}/steamworks_sdk_156.zip - EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b - USERPWD $ENV{steam_username}:$ENV{steam_password} - STATUS STEAM_SDK_DOWNLOAD_STATUS - SHOW_PROGRESS - ) - list(GET STEAM_SDK_DOWNLOAD_STATUS 0 STEAM_SDK_DL_STATUS_CODE) - list(GET STEAM_SDK_DOWNLOAD_STATUS 1 STEAM_SDK_DL_ERROR_MESSAGE) - if (NOT (${STEAM_SDK_DL_STATUS_CODE} EQUAL 0)) - message(FATAL_ERROR "Steam SDK download: " ${STEAM_SDK_DL_ERROR_MESSAGE}) - else () - message(STATUS "Steam SDK download: " ${STEAM_SDK_DL_ERROR_MESSAGE}) - file(ARCHIVE_EXTRACT - INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip - DESTINATION ${STEAMAPI_DIR}) - set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib") - set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam") - set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll") - endif() - else() - message(SEND_ERROR "Need to set steam_username and steam_password in environment to download Steamworks SDK") - endif() +option(BUILD_DFLAUNCH "Whether to build the Steam launcher exectuable (requires Steam SDK)." OFF) - include_directories(${STEAMAPI_SOURCE_DIR}) - link_libraries(${STEAMAPI_LIBRARY}) - add_executable(launchdf WIN32 launchdf.cpp) - install(TARGETS launchdf DESTINATION ${DFHACK_DATA_DESTINATION}) - install(FILES ${STEAMAPI_SHARED_LIBRARY} DESTINATION ${DFHACK_DATA_DESTINATION}) +if(WIN32 AND BUILD_DFLAUNCH) + # builder must manually download Steam SDK + set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam) + set (STEAMAPI_VER 156) + set (STEAMAPI_ZIP_EXPECTED_HASH af5a579990dbe5ae4c1b0689260d001b) + set (STEAMSDK_ZIP ${STEAMAPI_DIR}/steamworks_sdk_${STEAMAPI_VER}.zip) + + set (STEAM_SDK_HASH "NOT FOUND") + file(MD5 ${STEAMSDK_ZIP} STEAM_SDK_HASH) + if (NOT (${STEAM_SDK_HASH} STREQUAL ${STEAMAPI_ZIP_EXPECTED_HASH})) + message(FATAL_ERROR "You need the Steamworks SDK at ${STEAMSDK_ZIP} to build launchdf.exe. Please disable the BUILD_DFLAUNCH CMake option or download the Steam SDK from: https://partner.steamgames.com/downloads/steamworks_sdk_${STEAMAPI_VER}.zip") + endif() + if (${STEAMSDK_ZIP} IS_NEWER_THAN ${STEAMAPI_DIR}/sdk) + file(ARCHIVE_EXTRACT + INPUT ${STEAMSDK_ZIP} + DESTINATION ${STEAMAPI_DIR}) endif() + + set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib") + set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam") + set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll") + + include_directories(${STEAMAPI_SOURCE_DIR}) + link_libraries(${STEAMAPI_LIBRARY}) + add_executable(launchdf WIN32 launchdf.cpp) + install(TARGETS launchdf DESTINATION ${DFHACK_DATA_DESTINATION}) + install(FILES ${STEAMAPI_SHARED_LIBRARY} DESTINATION ${DFHACK_DATA_DESTINATION}) endif() From 14bbff85376cbdf412aadde12dcc1a750c0eb57f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 14:39:49 -0700 Subject: [PATCH 1196/2222] secrets can't be accessed from PRs; remove from build action --- .github/workflows/build.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94aa49452..9aed03c17 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -139,13 +139,6 @@ jobs: with: submodules: true fetch-depth: 0 - - name: Get 3rd party SDKs - uses: actions/checkout@v3 - with: - repository: DFHack/3rdparty - ref: main - token: ${{ secrets.DFHACK_3RDPARTY_TOKEN }} - path: depends - name: Fetch ccache uses: actions/cache@v3 with: @@ -156,7 +149,7 @@ jobs: ccache-win64-cross-msvc - name: Cross-compile win64 artifacts env: - CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1' + CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1' run: | cd build bash -x build-win64-from-linux.sh From b6ee0d5c02cb620cf6f466fa2f860abfa600ca19 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 14:59:08 -0700 Subject: [PATCH 1197/2222] follow advice in actions/checkout#664 --- .github/workflows/steam.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 4f785969a..5ca1ebe8d 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -32,6 +32,7 @@ jobs: submodules: true fetch-depth: 0 ref: ${{ github.event.inputs.commit_hash }} + persist-credentials: false - name: Get 3rd party SDKs uses: actions/checkout@v3 with: From 09621809812812238057b2913766ef4141b90f8f Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 15:10:15 -0700 Subject: [PATCH 1198/2222] attempting as an ssh-key instead of a token --- .github/workflows/steam.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 5ca1ebe8d..7f4c58afe 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -32,13 +32,12 @@ jobs: submodules: true fetch-depth: 0 ref: ${{ github.event.inputs.commit_hash }} - persist-credentials: false - name: Get 3rd party SDKs uses: actions/checkout@v3 with: repository: DFHack/3rdparty ref: main - token: ${{ secrets.DFHACK_3RDPARTY_TOKEN }} + ssh-key: ${{ secrets.DFHACK_3RDPARTY_TOKEN }} path: depends - name: Fetch ccache uses: actions/cache@v3 From b2102d66fa3668eeb3e62f3ef07433553291bf7b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 16:16:24 -0700 Subject: [PATCH 1199/2222] check out repo directly in steam dir so we don't bork the rest of depends --- .github/workflows/steam.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 7f4c58afe..46dd27270 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -38,7 +38,7 @@ jobs: repository: DFHack/3rdparty ref: main ssh-key: ${{ secrets.DFHACK_3RDPARTY_TOKEN }} - path: depends + path: depends/steam - name: Fetch ccache uses: actions/cache@v3 with: From d4e8d3399b34f84b22508baf752e9139e936cdd5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 18 May 2023 16:56:22 -0700 Subject: [PATCH 1200/2222] remove cursor guard from autodump so autodump destroy can work --- docs/changelog.txt | 1 + plugins/autodump.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 64d491906..eeeaf269e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -49,6 +49,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `autolabor`: work detail override warning now only appears on the work details screen ## Misc Improvements +- `autodump`: no longer checks for a keyboard cursor before executing, so ``autodump destroy`` (which doesn't require a cursor) can still function - `orders`: update orders in orders library for prepared meals, bins, archer uniforms, and weapons - Terminal console no longer appears in front of the game window on startup - `gui/control-panel`: new preference for whether filters in lists search for substrings in the middle of words (e.g. if set to true, then "ee" will match "steel") diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 2514dd91a..839b3b727 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -280,8 +280,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector Date: Fri, 19 May 2023 12:12:52 -0700 Subject: [PATCH 1201/2222] Update widgets.lua --- library/lua/gui/widgets.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 6610dcedb..26c949805 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1664,9 +1664,9 @@ function List:setChoices(choices, selected) -- Check if page_top needs to be adjusted if #self.choices - self.page_size < 0 then self.page_top = 1 - elseif self.selected <= math.floor(self.page_size / 2) then + elseif self.selected <= (self.page_size // 2) then self.page_top = 1 - elseif self.selected >= #self.choices - math.floor(self.page_size / 2) then + elseif self.selected >= #self.choices - (self.page_size // 2) then self.page_top = #self.choices - self.page_size + 1 end end @@ -1709,7 +1709,7 @@ local function update_list_scrollbar(list) end function List:postComputeFrame(body) - local row_count = math.floor(body.height / self.row_height) + local row_count = body.height // self.row_height self.page_size = math.max(1, row_count) local num_choices = #self.choices From 3729d7daa4ccba9b5772382df7ac35209276843d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 19 May 2023 19:41:02 -0700 Subject: [PATCH 1202/2222] encode transmitted names in utf-8 --- docs/changelog.txt | 1 + .../remotefortressreader.cpp | 34 +++++++++---------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 76105e9b5..60d858551 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -47,6 +47,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - DFHack screen backgrounds now use appropriate tiles in DF Classic - RemoteServer: fix crash on malformed json in ``dfhack-config/remote-server.json`` - `autolabor`: work detail override warning now only appears on the work details screen +- `RemoteFortressReader`: ensured names are transmitted in UTF-8 instead of CP437 ## Misc Improvements - `autodump`: no longer checks for a keyboard cursor before executing, so ``autodump destroy`` (which doesn't require a cursor) can still function diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index 061cf2698..ec478c632 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -808,8 +808,6 @@ static command_result GetMaterialList(color_ostream &stream, const EmptyMessage return CR_OK; } - - df::world_raws *raws = &world->raws; // df::world_history *history = &world->history; MaterialInfo mat; @@ -820,7 +818,7 @@ static command_result GetMaterialList(color_ostream &stream, const EmptyMessage mat_def->mutable_mat_pair()->set_mat_type(0); mat_def->mutable_mat_pair()->set_mat_index(i); mat_def->set_id(mat.getToken()); - mat_def->set_name(mat.toString()); //find the name at cave temperature; + mat_def->set_name(DF2UTF(mat.toString())); //find the name at cave temperature; if (size_t(raws->inorganics[i]->material.state_color[GetState(&raws->inorganics[i]->material)]) < raws->descriptors.colors.size()) { ConvertDFColorDescriptor(raws->inorganics[i]->material.state_color[GetState(&raws->inorganics[i]->material)], mat_def->mutable_state_color()); @@ -838,7 +836,7 @@ static command_result GetMaterialList(color_ostream &stream, const EmptyMessage mat_def->mutable_mat_pair()->set_mat_type(i); mat_def->mutable_mat_pair()->set_mat_index(j); mat_def->set_id(mat.getToken()); - mat_def->set_name(mat.toString()); //find the name at cave temperature; + mat_def->set_name(DF2UTF(mat.toString())); //find the name at cave temperature; if (size_t(raws->mat_table.builtin[i]->state_color[GetState(raws->mat_table.builtin[i])]) < raws->descriptors.colors.size()) { ConvertDFColorDescriptor(raws->mat_table.builtin[i]->state_color[GetState(raws->mat_table.builtin[i])], mat_def->mutable_state_color()); @@ -855,7 +853,7 @@ static command_result GetMaterialList(color_ostream &stream, const EmptyMessage mat_def->mutable_mat_pair()->set_mat_type(j + 19); mat_def->mutable_mat_pair()->set_mat_index(i); mat_def->set_id(mat.getToken()); - mat_def->set_name(mat.toString()); //find the name at cave temperature; + mat_def->set_name(DF2UTF(mat.toString())); //find the name at cave temperature; if (size_t(creature->material[j]->state_color[GetState(creature->material[j])]) < raws->descriptors.colors.size()) { ConvertDFColorDescriptor(creature->material[j]->state_color[GetState(creature->material[j])], mat_def->mutable_state_color()); @@ -872,7 +870,7 @@ static command_result GetMaterialList(color_ostream &stream, const EmptyMessage mat_def->mutable_mat_pair()->set_mat_type(j + 419); mat_def->mutable_mat_pair()->set_mat_index(i); mat_def->set_id(mat.getToken()); - mat_def->set_name(mat.toString()); //find the name at cave temperature; + mat_def->set_name(DF2UTF(mat.toString())); //find the name at cave temperature; if (size_t(plant->material[j]->state_color[GetState(plant->material[j])]) < raws->descriptors.colors.size()) { ConvertDFColorDescriptor(plant->material[j]->state_color[GetState(plant->material[j])], mat_def->mutable_state_color()); @@ -903,7 +901,7 @@ static command_result GetGrowthList(color_ostream &stream, const EmptyMessage *i continue; MaterialDefinition * basePlant = out->add_material_list(); basePlant->set_id(pp->id + ":BASE"); - basePlant->set_name(pp->name); + basePlant->set_name(DF2UTF(pp->name)); basePlant->mutable_mat_pair()->set_mat_type(-1); basePlant->mutable_mat_pair()->set_mat_index(i); #if DF_VERSION_INT > 40001 @@ -916,7 +914,7 @@ static command_result GetGrowthList(color_ostream &stream, const EmptyMessage *i { MaterialDefinition * out_growth = out->add_material_list(); out_growth->set_id(pp->id + ":" + growth->id + +":" + growth_locations[l]); - out_growth->set_name(growth->name); + out_growth->set_name(DF2UTF(growth->name)); out_growth->mutable_mat_pair()->set_mat_type(g * 10 + l); out_growth->mutable_mat_pair()->set_mat_index(i); } @@ -1951,8 +1949,8 @@ static command_result GetWorldMapCenter(color_ostream &stream, const EmptyMessag out->set_center_x(pos.x); out->set_center_y(pos.y); out->set_center_z(pos.z); - out->set_name(Translation::TranslateName(&(data->name), false)); - out->set_name_english(Translation::TranslateName(&(data->name), true)); + out->set_name(DF2UTF(Translation::TranslateName(&(data->name), false))); + out->set_name_english(DF2UTF(Translation::TranslateName(&(data->name), true))); out->set_cur_year(World::ReadCurrentYear()); out->set_cur_year_tick(World::ReadCurrentTick()); return CR_OK; @@ -1977,8 +1975,8 @@ static command_result GetWorldMap(color_ostream &stream, const EmptyMessage *in, int height = data->world_height; out->set_world_width(width); out->set_world_height(height); - out->set_name(Translation::TranslateName(&(data->name), false)); - out->set_name_english(Translation::TranslateName(&(data->name), true)); + out->set_name(DF2UTF(Translation::TranslateName(&(data->name), false))); + out->set_name_english(DF2UTF(Translation::TranslateName(&(data->name), true))); auto poles = data->flip_latitude; #if DF_VERSION_INT > 34011 switch (poles) @@ -2126,8 +2124,8 @@ static command_result GetWorldMapNew(color_ostream &stream, const EmptyMessage * int height = data->world_height; out->set_world_width(width); out->set_world_height(height); - out->set_name(Translation::TranslateName(&(data->name), false)); - out->set_name_english(Translation::TranslateName(&(data->name), true)); + out->set_name(DF2UTF(Translation::TranslateName(&(data->name), false))); + out->set_name_english(DF2UTF(Translation::TranslateName(&(data->name), true))); #if DF_VERSION_INT > 34011 auto poles = data->flip_latitude; switch (poles) @@ -2797,8 +2795,8 @@ static command_result GetPartialCreatureRaws(color_ostream &stream, const ListRe auto send_tissue = send_creature->add_tissues(); send_tissue->set_id(orig_tissue->id); - send_tissue->set_name(orig_tissue->tissue_name_singular); - send_tissue->set_subordinate_to_tissue(orig_tissue->subordinate_to_tissue); + send_tissue->set_name(DF2UTF(orig_tissue->tissue_name_singular)); + send_tissue->set_subordinate_to_tissue(DF2UTF(orig_tissue->subordinate_to_tissue)); CopyMat(send_tissue->mutable_material(), orig_tissue->mat_type, orig_tissue->mat_index); } @@ -2831,7 +2829,7 @@ static command_result GetPartialPlantRaws(color_ostream &stream, const ListReque plant_remote->set_index(i); plant_remote->set_id(plant_local->id); - plant_remote->set_name(plant_local->name); + plant_remote->set_name(DF2UTF(plant_local->name)); if (!plant_local->flags.is_set(df::plant_raw_flags::TREE)) plant_remote->set_tile(plant_local->tiles.shrub_tile); else @@ -2843,7 +2841,7 @@ static command_result GetPartialPlantRaws(color_ostream &stream, const ListReque TreeGrowth * growth_remote = plant_remote->add_growths(); growth_remote->set_index(j); growth_remote->set_id(growth_local->id); - growth_remote->set_name(growth_local->name); + growth_remote->set_name(DF2UTF(growth_local->name)); for (size_t k = 0; k < growth_local->prints.size(); k++) { df::plant_growth_print* print_local = growth_local->prints[k]; From 3cb13152f802ad59c3a211388d67ebde56395d14 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 20 May 2023 07:12:20 +0000 Subject: [PATCH 1203/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 0f02ade81..86b083cdf 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 0f02ade81771a7c24a4bbd6cfd7969704288f94b +Subproject commit 86b083cdf5847128e88197834eb953c1716c50a8 From a5a6b70a5186484563abb0c21c454a41c9a48841 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 19 May 2023 16:51:39 -0700 Subject: [PATCH 1204/2222] launch DFHack through steam if DF is run from steam --- library/Core.cpp | 6 +- library/include/modules/DFSteam.h | 4 +- library/modules/DFSteam.cpp | 118 +++++++++++++++++++++++++----- package/windows/launchdf.cpp | 59 ++++++++------- 4 files changed, 138 insertions(+), 49 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 256493dce..431cd2c9f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1682,8 +1682,10 @@ bool Core::InitSimulationThread() fatal("cannot bind SDL libraries"); return false; } - if (DFSteam::init(con)) + if (DFSteam::init(con)) { std::cerr << "Found Steam.\n"; + DFSteam::launchSteamDFHackIfNecessary(con); + } std::cerr << "Initializing textures.\n"; Textures::init(con); // create mutex for syncing with interactive tasks @@ -2291,7 +2293,7 @@ int Core::Shutdown ( void ) allModules.clear(); Textures::cleanup(); DFSDL::cleanup(); - DFSteam::cleanup(); + DFSteam::cleanup(getConsole()); memset(&(s_mods), 0, sizeof(s_mods)); d.reset(); return -1; diff --git a/library/include/modules/DFSteam.h b/library/include/modules/DFSteam.h index 3144830da..e604294f8 100644 --- a/library/include/modules/DFSteam.h +++ b/library/include/modules/DFSteam.h @@ -24,9 +24,9 @@ bool init(DFHack::color_ostream& out); /** * Call this when DFHack is being unloaded. */ -void cleanup(); +void cleanup(DFHack::color_ostream& out); -DFHACK_EXPORT bool DFIsSteamRunningOnSteamDeck(); +DFHACK_EXPORT void launchSteamDFHackIfNecessary(DFHack::color_ostream& out); } } diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index b27cc1744..c61d996d1 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -12,6 +12,9 @@ DBG_DECLARE(core, dfsteam, DebugCategory::LINFO); using namespace DFHack; +static const int DFHACK_STEAM_APPID = 2346660; + +static bool g_steam_initialized = false; static DFLibrary* g_steam_handle = nullptr; static const std::vector STEAM_LIBS { "steam_api64.dll", @@ -21,8 +24,32 @@ static const std::vector STEAM_LIBS { bool (*g_SteamAPI_Init)() = nullptr; void (*g_SteamAPI_Shutdown)() = nullptr; +int (*g_SteamAPI_GetHSteamUser)() = nullptr; +bool (*g_SteamAPI_RestartAppIfNecessary)(uint32_t unOwnAppID) = nullptr; void* (*g_SteamInternal_FindOrCreateUserInterface)(int, const char*) = nullptr; -bool (*g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck)(void*) = nullptr; +bool (*g_SteamAPI_ISteamApps_BIsAppInstalled)(void *iSteamApps, uint32_t appID) = nullptr; + + +static void bind_all(color_ostream& out, DFLibrary* handle) { +#define bind(name) \ + if (!handle) { \ + g_##name = nullptr; \ + } else { \ + g_##name = (decltype(g_##name))LookupPlugin(handle, #name); \ + if (!g_##name) { \ + WARN(dfsteam, out).print("steam library function not found: " #name "\n"); \ + } \ + } + + bind(SteamAPI_Init); + bind(SteamAPI_Shutdown); + bind(SteamAPI_GetHSteamUser); + bind(SteamInternal_FindOrCreateUserInterface); + bind(SteamAPI_RestartAppIfNecessary); + bind(SteamInternal_FindOrCreateUserInterface); + bind(SteamAPI_ISteamApps_BIsAppInstalled); +#undef bind +} bool DFSteam::init(color_ostream& out) { for (auto& lib_str : STEAM_LIBS) { @@ -34,30 +61,19 @@ bool DFSteam::init(color_ostream& out) { return false; } -#define bind(handle, name) \ - g_##name = (decltype(g_##name))LookupPlugin(handle, #name); \ - if (!g_##name) { \ - WARN(dfsteam, out).print("steam library function not found: " #name "\n"); \ - } - - bind(g_steam_handle, SteamAPI_Init); - bind(g_steam_handle, SteamAPI_Shutdown); + bind_all(out, g_steam_handle); - // TODO: can we remove this initialization of the Steam API once we move to dfhooks? if (!g_SteamAPI_Init || !g_SteamAPI_Shutdown || !g_SteamAPI_Init()) { DEBUG(dfsteam, out).print("steam detected but cannot be initialized\n"); return false; } - bind(g_steam_handle, SteamInternal_FindOrCreateUserInterface); - bind(g_steam_handle, SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck); -#undef bind - DEBUG(dfsteam, out).print("steam library linked\n"); + g_steam_initialized = true; return true; } -void DFSteam::cleanup() { +void DFSteam::cleanup(color_ostream& out) { if (!g_steam_handle) return; @@ -66,16 +82,78 @@ void DFSteam::cleanup() { ClosePlugin(g_steam_handle); g_steam_handle = nullptr; + + bind_all(out, nullptr); + g_steam_initialized = false; } -bool DFSteam::DFIsSteamRunningOnSteamDeck() { - if (!g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck) +#ifdef WIN32 +#include +static bool is_running_on_wine() { + typedef const char* (CDECL wine_get_version)(void); + static wine_get_version* pwine_get_version; + HMODULE hntdll = GetModuleHandle("ntdll.dll"); + if(!hntdll) return false; - if (!g_SteamInternal_FindOrCreateUserInterface) + pwine_get_version = (wine_get_version*) GetProcAddress(hntdll, "wine_get_version"); + return !!pwine_get_version; +} + +static bool launchDFHack(color_ostream& out) { + if (is_running_on_wine()) { + DEBUG(dfsteam, out).print("not attempting to re-launch DFHack on wine\n"); return false; + } + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); - void* SteamUtils = g_SteamInternal_FindOrCreateUserInterface(0, "SteamUtils010"); + // note that the enviornment must be explicitly zeroed out and not NULL, + // otherwise the launched process will inherit this process's environment, + // and the Steam API in the launchdf process will think it is in DF's context. + BOOL res = CreateProcessW(L"hack/launchdf.exe", + NULL, NULL, NULL, FALSE, 0, "\0", NULL, &si, &pi); + + return !!res; +} +#else +static bool launchDFHack(color_ostream& out) { + // TODO once we have a non-Windows build to work with + return false; +} +#endif + +void DFSteam::launchSteamDFHackIfNecessary(color_ostream& out) { + if (!g_steam_initialized || + !g_SteamAPI_GetHSteamUser || + !g_SteamInternal_FindOrCreateUserInterface || + !g_SteamAPI_ISteamApps_BIsAppInstalled) { + DEBUG(dfsteam, out).print("required Steam API calls are unavailable\n"); + return; + } + + if (strncmp(getenv("SteamClientLaunch"), "1", 2)) { + DEBUG(dfsteam, out).print("not launched from Steam client\n"); + return; + } + + void* iSteamApps = g_SteamInternal_FindOrCreateUserInterface(g_SteamAPI_GetHSteamUser(), "STEAMAPPS_INTERFACE_VERSION008"); + if (!iSteamApps) { + DEBUG(dfsteam, out).print("cannot obtain iSteamApps interface\n"); + return; + } + + bool isDFHackInstalled = g_SteamAPI_ISteamApps_BIsAppInstalled(iSteamApps, DFHACK_STEAM_APPID); + if (!isDFHackInstalled) { + DEBUG(dfsteam, out).print("player has not installed DFHack through Steam\n"); + return; + } - return g_SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck(SteamUtils); + bool ret = launchDFHack(out); + DEBUG(dfsteam, out).print("launching DFHack via Steam: %s\n", ret ? "successful" : "unsuccessful"); } diff --git a/package/windows/launchdf.cpp b/package/windows/launchdf.cpp index a84465f53..8e6536743 100644 --- a/package/windows/launchdf.cpp +++ b/package/windows/launchdf.cpp @@ -113,6 +113,24 @@ DWORD findDwarfFortressProcess() return -1; } +bool waitForDF() { + DWORD df_pid = findDwarfFortressProcess(); + + if (df_pid == -1) + return false; + + HANDLE hDF = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, df_pid); + + // in the future open an IPC connection so that we can proxy SteamAPI calls for the DFSteam module + + // this will eventuallyh need to become a loop with a WaitForMultipleObjects call + WaitForSingleObject(hDF, INFINITE); + + CloseHandle(hDF); + + return true; +} + int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) { // initialize steam context @@ -133,6 +151,9 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, exit(0); } + if (waitForDF()) + exit(0); + bool wine = is_running_on_wine(); if (wine) @@ -187,37 +208,25 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, exit(1); } - DWORD df_pid = findDwarfFortressProcess(); + if (waitForDF()) + exit(0); - if (df_pid == -1) + LPCWSTR err = launch_via_steam_windows(); + if (err != NULL) { - LPCWSTR err = launch_via_steam_windows(); - if (err != NULL) + MessageBoxW(NULL, err, NULL, 0); + exit(1); + } + + int counter = 0; + while (!waitForDF()) { + if (counter++ > 60) { - MessageBoxW(NULL, err, NULL, 0); + MessageBoxW(NULL, L"Dwarf Fortress took too long to launch, aborting", NULL, 0); exit(1); } - int counter = 0; - - do { - if (counter++ > 60) - { - MessageBoxW(NULL, L"Dwarf Fortress took too long to launch, aborting", NULL, 0); - exit(1); - } - Sleep(1000); - df_pid = findDwarfFortressProcess(); - } while (df_pid == -1); + Sleep(1000); } - HANDLE hDF = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, df_pid); - - // in the future open an IPC connection so that we can proxy SteamAPI calls for the DFSteam module - - // this will eventuallyh need to become a loop with a WaitForMultipleObjects call - WaitForSingleObject(hDF, INFINITE); - - CloseHandle(hDF); - exit(0); } From 8c01f3efe03918f8308029a9f4ee7141a3f38a99 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 19 May 2023 20:41:52 -0700 Subject: [PATCH 1205/2222] don't relaunch launchdf if it's already running --- library/modules/DFSteam.cpp | 31 +++++++++++++++++++++++++++++++ package/windows/launchdf.cpp | 6 +++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index c61d996d1..289c22e27 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -88,7 +88,9 @@ void DFSteam::cleanup(color_ostream& out) { } #ifdef WIN32 +#include #include +#include static bool is_running_on_wine() { typedef const char* (CDECL wine_get_version)(void); static wine_get_version* pwine_get_version; @@ -100,12 +102,41 @@ static bool is_running_on_wine() { return !!pwine_get_version; } +static DWORD findProcess(LPWSTR name) { + PROCESSENTRY32W entry; + entry.dwSize = sizeof(PROCESSENTRY32W); + + const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + + if (!Process32FirstW(snapshot, &entry)) { + CloseHandle(snapshot); + return -1; + } + + do { + std::wstring executableName(entry.szExeFile); + if (executableName == name) { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } + while (Process32NextW(snapshot, &entry)); + + CloseHandle(snapshot); + return -1; +} + static bool launchDFHack(color_ostream& out) { if (is_running_on_wine()) { DEBUG(dfsteam, out).print("not attempting to re-launch DFHack on wine\n"); return false; } + if (findProcess(L"launchdf.exe") != -1) { + DEBUG(dfsteam, out).print("launchdf.exe already running\n"); + return true; + } + STARTUPINFOW si; PROCESS_INFORMATION pi; diff --git a/package/windows/launchdf.cpp b/package/windows/launchdf.cpp index 8e6536743..f9ed11f66 100644 --- a/package/windows/launchdf.cpp +++ b/package/windows/launchdf.cpp @@ -139,6 +139,9 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, exit(0); } + if (waitForDF()) + exit(0); + if (!SteamAPI_Init()) { // could not initialize steam context, attempt fallback launch @@ -151,9 +154,6 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, exit(0); } - if (waitForDF()) - exit(0); - bool wine = is_running_on_wine(); if (wine) From 9dffba68431c70d84a0951519540ec8e7802ecbc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 20 May 2023 04:38:03 -0700 Subject: [PATCH 1206/2222] amend #2914 so lists don't jump around on resize --- library/lua/gui/widgets.lua | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 26c949805..5c8094d28 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -1664,9 +1664,7 @@ function List:setChoices(choices, selected) -- Check if page_top needs to be adjusted if #self.choices - self.page_size < 0 then self.page_top = 1 - elseif self.selected <= (self.page_size // 2) then - self.page_top = 1 - elseif self.selected >= #self.choices - (self.page_size // 2) then + elseif self.page_top > #self.choices - self.page_size + 1 then self.page_top = #self.choices - self.page_size + 1 end end @@ -1719,14 +1717,8 @@ function List:postComputeFrame(body) return end - local max_page_top = math.max(1, num_choices - row_count + 1) - - if self.selected > num_choices - row_count then - self.page_top = max_page_top - elseif self.selected < self.page_top then - self.page_top = self.selected - else - self.page_top = math.max(1, self.selected - row_count + 1) + if self.page_top > num_choices - self.page_size + 1 then + self.page_top = math.max(1, num_choices - self.page_size + 1) end update_list_scrollbar(self) From c782873f868099fab121038ec6bf8999604d7b5d Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 20 May 2023 04:58:08 -0700 Subject: [PATCH 1207/2222] Update changelog.txt --- docs/changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 60d858551..fdd9ca795 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -64,6 +64,9 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API +## Internal +- ``dfhack.internal``: added memory analysis functions: ``msizeAddress``, ``getHeapState``, ``heapTakeSnapshot``, ``isAddressInHeap``, ``isAddressActiveInHeap``, ``isAddressUsedAfterFreeInHeap``, ``getAddressSizeInHeap``, and ``getRootAddressOfHeapObject`` + ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. - ``gui``: changed frame naming scheme to ``FRAME_X`` rather than ``X_FRAME``, and added aliases for backwards compatibility. (for example ``BOLD_FRAME`` is now called ``FRAME_BOLD``) From 8d73385aaf5babc6a47b8c0ab0b1539d4fe77ed7 Mon Sep 17 00:00:00 2001 From: Myk Date: Sat, 20 May 2023 05:01:07 -0700 Subject: [PATCH 1208/2222] Update changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index fdd9ca795..2e5b482f0 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -64,7 +64,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## API -## Internal +## Internals - ``dfhack.internal``: added memory analysis functions: ``msizeAddress``, ``getHeapState``, ``heapTakeSnapshot``, ``isAddressInHeap``, ``isAddressActiveInHeap``, ``isAddressUsedAfterFreeInHeap``, ``getAddressSizeInHeap``, and ``getRootAddressOfHeapObject`` ## Lua From af1ba12031928fea804f016c86812fcc3c10a688 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 21 May 2023 07:12:22 +0000 Subject: [PATCH 1209/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 86b083cdf..40e517eb8 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 86b083cdf5847128e88197834eb953c1716c50a8 +Subproject commit 40e517eb844779b4ac1ba6fe6d226f2d613d0b8c From d06118ad8ef99927b31487fec2f6f7cb5a761bd3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 20 May 2023 16:00:59 -0700 Subject: [PATCH 1210/2222] support recording stockpiles in blueprints --- docs/changelog.txt | 3 ++- plugins/blueprint.cpp | 26 +++++++++----------------- plugins/lua/blueprint.lua | 12 ++++++------ plugins/lua/stockpiles.lua | 4 ++-- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 2e5b482f0..df00cf34a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -55,7 +55,8 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Terminal console no longer appears in front of the game window on startup - `gui/control-panel`: new preference for whether filters in lists search for substrings in the middle of words (e.g. if set to true, then "ee" will match "steel") - `gui/design`: Improved performance for drawing shapes -- Dreamfort: improve traffic patterns throughout the fortress (stockpiles and zones are still not working, pending updates in `quickfort`) +- Dreamfort: improve traffic patterns throughout the fortress +- `gui/blueprint`: recording of stockpile layouts and categories is now supported. note that detailed stockpile configurations will *not* be saved (yet) - Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. - `overlay`: added links to the quickstart guide and the control panel on the DF title screen - `gui/autodump`: fort-mode keybinding: Ctrl-H diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index 39847935d..d5a06cc5c 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -99,9 +99,9 @@ struct blueprint_options { bool construct = false; bool build = false; bool place = false; - bool zone = false; - bool query = false; - bool rooms = false; + // bool zone = false; + // bool query = false; + // bool rooms = false; static struct_identity _identity; }; @@ -125,9 +125,9 @@ static const struct_field_info blueprint_options_fields[] = { { struct_field_info::PRIMITIVE, "construct", offsetof(blueprint_options, construct), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "build", offsetof(blueprint_options, build), &df::identity_traits::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "place", offsetof(blueprint_options, place), &df::identity_traits::identity, 0, 0 }, - { struct_field_info::PRIMITIVE, "zone", offsetof(blueprint_options, zone), &df::identity_traits::identity, 0, 0 }, - { struct_field_info::PRIMITIVE, "query", offsetof(blueprint_options, query), &df::identity_traits::identity, 0, 0 }, - { struct_field_info::PRIMITIVE, "rooms", offsetof(blueprint_options, rooms), &df::identity_traits::identity, 0, 0 }, + // { struct_field_info::PRIMITIVE, "zone", offsetof(blueprint_options, zone), &df::identity_traits::identity, 0, 0 }, + // { struct_field_info::PRIMITIVE, "query", offsetof(blueprint_options, query), &df::identity_traits::identity, 0, 0 }, + // { struct_field_info::PRIMITIVE, "rooms", offsetof(blueprint_options, rooms), &df::identity_traits::identity, 0, 0 }, { struct_field_info::END } }; struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::allocator_fn, NULL, "blueprint_options", NULL, blueprint_options_fields); @@ -855,7 +855,6 @@ static const char * get_tile_build(const df::coord &pos, return add_expansion_syntax(ctx, keys); } -/* TODO: understand how this changes for v50 static const char * get_place_keys(const tile_context &ctx) { df::building_stockpilest* sp = virtual_cast(ctx.b); @@ -908,6 +907,7 @@ static const char * get_tile_place(const df::coord &pos, return add_expansion_syntax(ctx, get_place_keys(ctx)); } +/* TODO: understand how this changes for v50 static bool hospital_maximums_eq(const df::hospital_supplies &a, const df::hospital_supplies &b) { return a.max_thread == b.max_thread && @@ -1333,23 +1333,15 @@ static bool do_transform(color_ostream &out, get_tile_construct, ensure_building); add_processor(processors, opts, "build", "build", opts.build, get_tile_build, ensure_building); -/* TODO: understand how this changes for v50 add_processor(processors, opts, "place", "place", opts.place, get_tile_place, ensure_building); +/* TODO: understand how this changes for v50 add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone); add_processor(processors, opts, "query", "query", opts.query, get_tile_query, ensure_building); add_processor(processors, opts, "query", "rooms", opts.rooms, get_tile_rooms, ensure_building); -*/ if (opts.place) - out.printerr("'place' blueprints are not yet supported for the current version of DF\n"); - if (opts.zone) - out.printerr("'zone' blueprints are not yet supported for the current version of DF\n"); - if (opts.query) - out.printerr("'query' blueprints are not yet supported for the current version of DF\n"); - if (opts.rooms) - out.printerr("'rooms' blueprints are not yet supported for the current version of DF\n"); - +*/ if (processors.empty()) { out.printerr("no phases requested! nothing to do!\n"); return false; diff --git a/plugins/lua/blueprint.lua b/plugins/lua/blueprint.lua index 375cfc1b4..57dee31fc 100644 --- a/plugins/lua/blueprint.lua +++ b/plugins/lua/blueprint.lua @@ -9,17 +9,17 @@ local valid_phase_list = { 'construct', 'build', 'place', - 'zone', - 'query', - 'rooms', + -- 'zone', + -- 'query', + -- 'rooms', } valid_phases = utils.invert(valid_phase_list) local meta_phase_list = { 'build', 'place', - 'zone', - 'query', + -- 'zone', + -- 'query', } meta_phases = utils.invert(meta_phase_list) @@ -167,7 +167,7 @@ end function parse_commandline(opts, ...) local positionals = process_args(opts, {...}) - if opts.help then return end + if not positionals or opts.help then return end local width, height = tonumber(positionals[1]), tonumber(positionals[2]) if is_bad_dim(width) or is_bad_dim(height) then diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 9e17cbe2b..379f2185a 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -81,7 +81,7 @@ local included_elements = { types=8, } -local function export_stockpile(name, opts) +function export_stockpile(name, opts) assert_safe_name(name) name = STOCKPILES_DIR .. '/' .. name @@ -101,7 +101,7 @@ local function export_stockpile(name, opts) stockpiles_export(name, get_sp_id(opts), includedElements) end -local function import_stockpile(name, opts) +function import_stockpile(name, opts) local is_library = false if name:startswith('library/') then name = name:sub(9) From 848556158bd3ffd198cfb98099a0ee247efbe424 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 21 May 2023 09:45:37 -0700 Subject: [PATCH 1211/2222] update HEADs --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 9891be326..c0ae38c0c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9891be32663435f2fe875e27c70010d5618de735 +Subproject commit c0ae38c0c60f5c12112d89bfd8facb7a0359fba2 diff --git a/scripts b/scripts index 40e517eb8..c96ffb334 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 40e517eb844779b4ac1ba6fe6d226f2d613d0b8c +Subproject commit c96ffb334e87a7c6098426bdbefca8ae8b51b748 From 6c9c44c71a97075dc9fef3886062fe6081bdcf57 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 21 May 2023 10:34:17 -0700 Subject: [PATCH 1212/2222] "develop" works as a ref; makes good default --- .github/workflows/steam.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 46dd27270..90f046b40 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -4,11 +4,12 @@ on: workflow_dispatch: inputs: commit_hash: - description: Commit hash + description: Branch or commit hash type: string required: true + default: develop version: - description: Version + description: Version or build description type: string required: true release_channel: From eeebce7e0f03c98289f41bc66ea11065d9ef3495 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 22 May 2023 07:13:59 +0000 Subject: [PATCH 1213/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index c96ffb334..d305ede50 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit c96ffb334e87a7c6098426bdbefca8ae8b51b748 +Subproject commit d305ede5067e9f8bb1cfeab6d746ac39a659a590 From 9e4c71318008e5fd364f24c8e836e53b7bad73d7 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 23 May 2023 07:13:27 +0000 Subject: [PATCH 1214/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index d305ede50..596b4e32a 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d305ede5067e9f8bb1cfeab6d746ac39a659a590 +Subproject commit 596b4e32a72ee6ef71445ffae94988c8896d0805 From f3ce8059606ad1f2e65e31deea691f7c5cec5c4e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 23 May 2023 12:26:44 -0700 Subject: [PATCH 1215/2222] scroll mouse wheel to focus window under cursor --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 10 +++++----- library/lua/gui.lua | 16 +++++++++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index df00cf34a..ab8ca1439 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -60,6 +60,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. - `overlay`: added links to the quickstart guide and the control panel on the DF title screen - `gui/autodump`: fort-mode keybinding: Ctrl-H +- Window behavior: if you have multiple DFHack tool windows open, scrolling the mouse wheel while over an unfocused window will focus it and raise it to the top ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 1c1d75358..44304a54e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4240,11 +4240,11 @@ input skips all unfocused ZScreens under that ZScreen and is passed directly to the first non-ZScreen viewscreen. There are class attributes that can be set to control what kind of unhandled input is passed to the lower layers. -If multiple ZScreens are visible and the player left or right clicks on a -visible element of a non-focused ZScreen, that ZScreen will be given focus. This -allows multiple DFHack GUI tools to be usable at the same time. If the mouse is -clicked away from the ZScreen widgets, that ZScreen loses focus. If no ZScreen -has focus, all input is passed directly through to the first underlying +If multiple ZScreens are visible and the player scrolls or left/right clicks on +a visible element of a non-focused ZScreen, that ZScreen will be given focus. +This allows multiple DFHack GUI tools to be usable at the same time. If the +mouse is clicked away from the ZScreen widgets, that ZScreen loses focus. If no +ZScreen has focus, all input is passed directly through to the first underlying non-ZScreen viewscreen. For a ZScreen with keyboard focus, if :kbd:`Esc` or the right mouse button is diff --git a/library/lua/gui.lua b/library/lua/gui.lua index d511cd51f..9d9d39a8e 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -787,7 +787,9 @@ end function ZScreen:onInput(keys) local has_mouse = self:isMouseOver() if not self:hasFocus() then - if (keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN) and has_mouse then + if has_mouse and + (keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or + keys.CONTEXT_SCROLL_UP or keys.CONTEXT_SCROLL_DOWN) then self:raise() else self:sendInputToParent(keys) @@ -818,10 +820,14 @@ function ZScreen:onInput(keys) end local passit = self.pass_pause and keys.D_PAUSE if not passit and self.pass_mouse_clicks then - for key in pairs(MOUSE_KEYS) do - if keys[key] then - passit = true - break + if keys.CONTEXT_SCROLL_UP or keys.CONTEXT_SCROLL_DOWN then + passit = true + else + for key in pairs(MOUSE_KEYS) do + if keys[key] then + passit = true + break + end end end end From 6f49a0eb3dc4055ca5b77fd7d040d25f6a4fbbad Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 23 May 2023 15:17:01 -0700 Subject: [PATCH 1216/2222] allow dragging by frame edge for non-resizable windows --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 4 ++-- library/lua/gui/widgets.lua | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index df00cf34a..c21facea1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -59,6 +59,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/blueprint`: recording of stockpile layouts and categories is now supported. note that detailed stockpile configurations will *not* be saved (yet) - Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. - `overlay`: added links to the quickstart guide and the control panel on the DF title screen +- Window behavior: non-resizable windows now allow dragging by their frame edges by default - `gui/autodump`: fort-mode keybinding: Ctrl-H ## Documentation diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 1c1d75358..538fa08d8 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -4503,7 +4503,7 @@ Has attributes: Called from ``postComputeFrame``. * ``draggable = bool`` (default: ``false``) -* ``drag_anchors = {}`` (default: ``{title=true, frame=false, body=false}``) +* ``drag_anchors = {}`` (default: ``{title=true, frame=false/true, body=true}``) * ``drag_bound = 'frame' or 'body'`` (default: ``'frame'``) * ``on_drag_begin = function()`` (default: ``nil``) * ``on_drag_end = function(bool)`` (default: ``nil``) @@ -4511,7 +4511,7 @@ Has attributes: If ``draggable`` is set to ``true``, then the above attributes come into play when the panel is dragged around the screen, either with the mouse or the keyboard. ``drag_anchors`` sets which parts of the panel can be clicked on - with the left mouse button to start dragging. ``drag_bound`` configures + with the left mouse button to start dragging. The frame is a drag anchor by default only if ``resizable`` (below) is ``false``. ``drag_bound`` configures whether the frame of the panel (if any) can be dragged outside the containing parent's boundary. The body will never be draggable outside of the parent, but you can allow the frame to cross the boundary by setting ``drag_bound`` to diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 5c8094d28..a58ac228c 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -87,7 +87,7 @@ Panel.ATTRS { function Panel:init(args) if not self.drag_anchors then - self.drag_anchors = {title=true, frame=false, body=true} + self.drag_anchors = {title=true, frame=not self.resizable, body=true} end if not self.resize_anchors then self.resize_anchors = {t=false, l=true, r=true, b=true} From f6d9af5725ee15fcdc3a2527413f451ad8522ab6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 23 May 2023 15:20:09 -0700 Subject: [PATCH 1217/2222] also set focus on shift-scrolling --- library/lua/gui.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 9d9d39a8e..e22ddae20 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -789,7 +789,8 @@ function ZScreen:onInput(keys) if not self:hasFocus() then if has_mouse and (keys._MOUSE_L_DOWN or keys._MOUSE_R_DOWN or - keys.CONTEXT_SCROLL_UP or keys.CONTEXT_SCROLL_DOWN) then + keys.CONTEXT_SCROLL_UP or keys.CONTEXT_SCROLL_DOWN or + keys.CONTEXT_SCROLL_PAGEUP or keys.CONTEXT_SCROLL_PAGEDOWN) then self:raise() else self:sendInputToParent(keys) @@ -820,7 +821,8 @@ function ZScreen:onInput(keys) end local passit = self.pass_pause and keys.D_PAUSE if not passit and self.pass_mouse_clicks then - if keys.CONTEXT_SCROLL_UP or keys.CONTEXT_SCROLL_DOWN then + if keys.CONTEXT_SCROLL_UP or keys.CONTEXT_SCROLL_DOWN or + keys.CONTEXT_SCROLL_PAGEUP or keys.CONTEXT_SCROLL_PAGEDOWN then passit = true else for key in pairs(MOUSE_KEYS) do From 4ba2c807b2b63ba8dccde08a759b289fc5795e73 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Tue, 23 May 2023 22:26:06 +0000 Subject: [PATCH 1218/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index c0ae38c0c..c0bf8aa61 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c0ae38c0c60f5c12112d89bfd8facb7a0359fba2 +Subproject commit c0bf8aa6166b8e7a9672fd67edccfa4e78c945cd diff --git a/scripts b/scripts index 596b4e32a..d9a4d888e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 596b4e32a72ee6ef71445ffae94988c8896d0805 +Subproject commit d9a4d888e3af40ef2f1f8765c8b4d45532129dc4 From 44340dfb75759eac7102465158cea0b2864133f9 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 24 May 2023 07:12:57 +0000 Subject: [PATCH 1219/2222] Auto-update submodules library/xml: master scripts: master plugins/stonesense: master --- library/xml | 2 +- plugins/stonesense | 2 +- scripts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index c0bf8aa61..29801b004 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c0bf8aa6166b8e7a9672fd67edccfa4e78c945cd +Subproject commit 29801b0043b884d04a7e63a995258ebf2b67cbaf diff --git a/plugins/stonesense b/plugins/stonesense index b7073b664..8a407a048 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit b7073b664310b909989ebe68de36a164e452825a +Subproject commit 8a407a0485d63ce399314afd2f0c623a7dfa4dfa diff --git a/scripts b/scripts index d9a4d888e..04e1e09a7 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit d9a4d888e3af40ef2f1f8765c8b4d45532129dc4 +Subproject commit 04e1e09a75e4ce3d9b7a2b98ab99a0cb45eff3b9 From 87775317a525bcbdede9d9d6b48e458a9faff5ef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 24 May 2023 12:41:02 -0700 Subject: [PATCH 1220/2222] don't throw if json is unreadable just act like the file didn't exist (unless strict is set) --- docs/changelog.txt | 1 + library/lua/json.lua | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index df00cf34a..42ce711eb 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -51,6 +51,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `autodump`: no longer checks for a keyboard cursor before executing, so ``autodump destroy`` (which doesn't require a cursor) can still function +- Settings: recover gracefully when settings files become corrupted (e.g. by CTD) - `orders`: update orders in orders library for prepared meals, bins, archer uniforms, and weapons - Terminal console no longer appears in front of the game window on startup - `gui/control-panel`: new preference for whether filters in lists search for substrings in the middle of words (e.g. if set to true, then "ee" will match "steel") diff --git a/library/lua/json.lua b/library/lua/json.lua index 2b4b3a66a..6ba8483dd 100644 --- a/library/lua/json.lua +++ b/library/lua/json.lua @@ -59,7 +59,13 @@ function _file:read(strict) end else self.exists = true - self.data = decode_file(self.path) + local ok, err = pcall(function() self.data = decode_file(self.path) end) + if not ok then + if strict then + error(('cannot decode file: %s: %s'):format(self.path, err)) + end + self.data = {} + end end return self.data end From a9843912bec83bf2607928ee150d09638c7c7433 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 25 May 2023 07:13:13 +0000 Subject: [PATCH 1221/2222] Auto-update submodules scripts: master plugins/stonesense: master --- plugins/stonesense | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/stonesense b/plugins/stonesense index 8a407a048..d7fa20079 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 8a407a0485d63ce399314afd2f0c623a7dfa4dfa +Subproject commit d7fa20079e89cc6516a0f5406a5ad112436066bb diff --git a/scripts b/scripts index 04e1e09a7..439356811 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 04e1e09a75e4ce3d9b7a2b98ab99a0cb45eff3b9 +Subproject commit 4393568117eaa5b74872c4d98e07dee1d58f3e48 From 732e042a543d618da913deaf1aa9907a6d8ecd6a Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Fri, 26 May 2023 00:57:44 +0000 Subject: [PATCH 1222/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 439356811..a26e72b6e 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 4393568117eaa5b74872c4d98e07dee1d58f3e48 +Subproject commit a26e72b6e1da16d6fcaadc71518314fb8aaae193 From 27ee0ae3960625bd4fd1c25136344dd52b05f5eb Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sat, 27 May 2023 07:12:17 +0000 Subject: [PATCH 1223/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 29801b004..1cc81c0fa 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 29801b0043b884d04a7e63a995258ebf2b67cbaf +Subproject commit 1cc81c0faf7aa9fd1c18fbc6ef8b2298f31ab1f9 From 0918fbb0041e74765624aea4625c33bf38d8ac97 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 27 May 2023 03:16:51 -0700 Subject: [PATCH 1224/2222] add ensure_keys utility function --- docs/changelog.txt | 1 + docs/dev/Lua API.rst | 5 +++++ library/lua/dfhack.lua | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index c193442c6..b2ee30767 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -74,6 +74,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Lua - ``overlay.reload()``: has been renamed to ``overlay.rescan()`` so as not to conflict with the global ``reload()`` function. If you are developing an overlay, please take note of the new function name for reloading your overlay during development. - ``gui``: changed frame naming scheme to ``FRAME_X`` rather than ``X_FRAME``, and added aliases for backwards compatibility. (for example ``BOLD_FRAME`` is now called ``FRAME_BOLD``) +- ``ensure_keys``: walks a series of keys, creating new tables for any missing values ## Removed - `orders`: ``library/military_include_artifact_materials`` library file removed since recent research indicates that platinum blunt weapons and silver crossbows are not more effective than standard steel. the alternate military orders file was also causing unneeded confusion. diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9f0a40ba8..e5167a531 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -3019,6 +3019,11 @@ environment by the mandatory init file dfhack.lua: set to the value of ``default_value``, which defaults to ``{}`` if not set. The new or existing value of ``t[key]`` is then returned. +* ``ensure_keys(t, key...)`` + + Walks a series of keys, creating any missing keys as empty tables. The new or + existing table from the last specified key is returned from the function. + .. _lua-string: String class extensions diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 78d978147..8ea5e9dac 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -404,6 +404,14 @@ function ensure_key(t, key, default_value) return t[key] end +function ensure_keys(t, key, ...) + t = ensure_key(t, key) + if select('#', ...) > 0 then + return ensure_keys(t, ...) + end + return t +end + -- String class extentions -- prefix is a literal string, not a pattern From 8b3eef699f4d2d6d5776ca4156085afd4220356c Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 27 May 2023 05:37:31 -0700 Subject: [PATCH 1225/2222] adjust findCivzonesAt to v50 semantics --- library/modules/Buildings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index b9e61c863..dca8010da 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -465,7 +465,7 @@ bool Buildings::findCivzonesAt(std::vector *pvec, df::coord pos) { pvec->clear(); - for (df::building_civzonest* zone : world->buildings.other.ACTIVITY_ZONE) + for (df::building_civzonest* zone : world->buildings.other.ANY_ZONE) { if (pos.z != zone->z) continue; From dbcba3d548a8b8e18eb8ba385f243a38413354dd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 28 May 2023 02:26:06 -0700 Subject: [PATCH 1226/2222] refactor to allow interacting with route stop settings --- plugins/lua/stockpiles.lua | 18 +- plugins/stockpiles/StockpileSerializer.cpp | 456 +++++++++++---------- plugins/stockpiles/StockpileSerializer.h | 54 ++- plugins/stockpiles/stockpiles.cpp | 48 +++ 4 files changed, 342 insertions(+), 234 deletions(-) diff --git a/plugins/lua/stockpiles.lua b/plugins/lua/stockpiles.lua index 379f2185a..3e127de4d 100644 --- a/plugins/lua/stockpiles.lua +++ b/plugins/lua/stockpiles.lua @@ -101,7 +101,7 @@ function export_stockpile(name, opts) stockpiles_export(name, get_sp_id(opts), includedElements) end -function import_stockpile(name, opts) +local function normalize_name(name) local is_library = false if name:startswith('library/') then name = name:sub(9) @@ -109,11 +109,19 @@ function import_stockpile(name, opts) end assert_safe_name(name) if not is_library and dfhack.filesystem.exists(STOCKPILES_DIR .. '/' .. name .. '.dfstock') then - name = STOCKPILES_DIR .. '/' .. name - else - name = STOCKPILES_LIBRARY_DIR .. '/' .. name + return STOCKPILES_DIR .. '/' .. name end - stockpiles_import(name, get_sp_id(opts), opts.mode, table.concat(opts.filters, ',')) + return STOCKPILES_LIBRARY_DIR .. '/' .. name +end + +function import_stockpile(name, opts) + name = normalize_name(name) + stockpiles_import(name, get_sp_id(opts), opts.mode, table.concat(opts.filters or {}, ',')) +end + +function import_route(name, route_id, stop_id, mode, filters) + name = normalize_name(name) + stockpiles_route_import(name, route_id, stop_id, mode, table.concat(filters or {}, ',')) end local valid_includes = {general=true, categories=true, types=true} diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index 529e402bd..fcb0cee3e 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -150,12 +150,17 @@ static struct OtherMatsWeaponsArmor { } } mOtherMatsWeaponsArmor; +StockpileSettingsSerializer::StockpileSettingsSerializer(df::stockpile_settings *settings) + : mSettings(settings) { } + +StockpileSettingsSerializer::~StockpileSettingsSerializer() { } + StockpileSerializer::StockpileSerializer(df::building_stockpilest* stockpile) - : mPile(stockpile) { } + : StockpileSettingsSerializer(&stockpile->settings), mPile(stockpile) { } StockpileSerializer::~StockpileSerializer() { } -bool StockpileSerializer::serialize_to_ostream(std::ostream* output, uint32_t includedElements) { +bool StockpileSettingsSerializer::serialize_to_ostream(std::ostream* output, uint32_t includedElements) { if (output->fail()) return false; mBuffer.Clear(); @@ -168,7 +173,7 @@ bool StockpileSerializer::serialize_to_ostream(std::ostream* output, uint32_t in return output->good(); } -bool StockpileSerializer::serialize_to_file(const string& file, uint32_t includedElements) { +bool StockpileSettingsSerializer::serialize_to_file(const string& file, uint32_t includedElements) { std::fstream output(file, std::ios::out | std::ios::binary | std::ios::trunc); if (output.fail()) { WARN(log).print("ERROR: failed to open file for writing: '%s'\n", @@ -178,7 +183,7 @@ bool StockpileSerializer::serialize_to_file(const string& file, uint32_t include return serialize_to_ostream(&output, includedElements); } -bool StockpileSerializer::parse_from_istream(std::istream* input, DeserializeMode mode, const vector& filters) { +bool StockpileSettingsSerializer::parse_from_istream(std::istream* input, DeserializeMode mode, const vector& filters) { if (input->fail()) return false; mBuffer.Clear(); @@ -190,7 +195,7 @@ bool StockpileSerializer::parse_from_istream(std::istream* input, DeserializeMod return res; } -bool StockpileSerializer::unserialize_from_file(const string& file, DeserializeMode mode, const vector& filters) { +bool StockpileSettingsSerializer::unserialize_from_file(const string& file, DeserializeMode mode, const vector& filters) { std::fstream input(file, std::ios::in | std::ios::binary); if (input.fail()) { WARN(log).print("failed to open file for reading: '%s'\n", @@ -655,9 +660,7 @@ static void write_cat(const char *name, bool include_types, uint32_t cat_flags, } } -void StockpileSerializer::write(uint32_t includedElements) { - if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) - write_containers(); +void StockpileSettingsSerializer::write(uint32_t includedElements) { if (includedElements & INCLUDED_ELEMENTS_GENERAL) write_general(); @@ -665,100 +668,106 @@ void StockpileSerializer::write(uint32_t includedElements) { return; DEBUG(log).print("GROUP SET %s\n", - bitfield_to_string(mPile->settings.flags).c_str()); + bitfield_to_string(mSettings->flags).c_str()); bool include_types = 0 != (includedElements & INCLUDED_ELEMENTS_TYPES); write_cat("ammo", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_ammo, + mSettings->flags.whole, + mSettings->flags.mask_ammo, std::bind(&StockpileSettings::mutable_ammo, &mBuffer), - std::bind(&StockpileSerializer::write_ammo, this, _1)); + std::bind(&StockpileSettingsSerializer::write_ammo, this, _1)); write_cat("animals", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_animals, + mSettings->flags.whole, + mSettings->flags.mask_animals, std::bind(&StockpileSettings::mutable_animals, &mBuffer), - std::bind(&StockpileSerializer::write_animals, this, _1)); + std::bind(&StockpileSettingsSerializer::write_animals, this, _1)); write_cat("armor", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_armor, + mSettings->flags.whole, + mSettings->flags.mask_armor, std::bind(&StockpileSettings::mutable_armor, &mBuffer), - std::bind(&StockpileSerializer::write_armor, this, _1)); + std::bind(&StockpileSettingsSerializer::write_armor, this, _1)); write_cat("bars_blocks", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_bars_blocks, + mSettings->flags.whole, + mSettings->flags.mask_bars_blocks, std::bind(&StockpileSettings::mutable_barsblocks, &mBuffer), - std::bind(&StockpileSerializer::write_bars_blocks, this, _1)); + std::bind(&StockpileSettingsSerializer::write_bars_blocks, this, _1)); write_cat("cloth", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_cloth, + mSettings->flags.whole, + mSettings->flags.mask_cloth, std::bind(&StockpileSettings::mutable_cloth, &mBuffer), - std::bind(&StockpileSerializer::write_cloth, this, _1)); + std::bind(&StockpileSettingsSerializer::write_cloth, this, _1)); write_cat("coin", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_coins, + mSettings->flags.whole, + mSettings->flags.mask_coins, std::bind(&StockpileSettings::mutable_coin, &mBuffer), - std::bind(&StockpileSerializer::write_coins, this, _1)); + std::bind(&StockpileSettingsSerializer::write_coins, this, _1)); write_cat("finished_goods", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_finished_goods, + mSettings->flags.whole, + mSettings->flags.mask_finished_goods, std::bind(&StockpileSettings::mutable_finished_goods, &mBuffer), - std::bind(&StockpileSerializer::write_finished_goods, this, _1)); + std::bind(&StockpileSettingsSerializer::write_finished_goods, this, _1)); write_cat("food", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_food, + mSettings->flags.whole, + mSettings->flags.mask_food, std::bind(&StockpileSettings::mutable_food, &mBuffer), - std::bind(&StockpileSerializer::write_food, this, _1)); + std::bind(&StockpileSettingsSerializer::write_food, this, _1)); write_cat("furniture", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_furniture, + mSettings->flags.whole, + mSettings->flags.mask_furniture, std::bind(&StockpileSettings::mutable_furniture, &mBuffer), - std::bind(&StockpileSerializer::write_furniture, this, _1)); + std::bind(&StockpileSettingsSerializer::write_furniture, this, _1)); write_cat("gems", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_gems, + mSettings->flags.whole, + mSettings->flags.mask_gems, std::bind(&StockpileSettings::mutable_gems, &mBuffer), - std::bind(&StockpileSerializer::write_gems, this, _1)); + std::bind(&StockpileSettingsSerializer::write_gems, this, _1)); write_cat("leather", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_leather, + mSettings->flags.whole, + mSettings->flags.mask_leather, std::bind(&StockpileSettings::mutable_leather, &mBuffer), - std::bind(&StockpileSerializer::write_leather, this, _1)); + std::bind(&StockpileSettingsSerializer::write_leather, this, _1)); write_cat("corpses", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_corpses, + mSettings->flags.whole, + mSettings->flags.mask_corpses, std::bind(&StockpileSettings::mutable_corpses_v50, &mBuffer), - std::bind(&StockpileSerializer::write_corpses, this, _1)); + std::bind(&StockpileSettingsSerializer::write_corpses, this, _1)); write_cat("refuse", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_refuse, + mSettings->flags.whole, + mSettings->flags.mask_refuse, std::bind(&StockpileSettings::mutable_refuse, &mBuffer), - std::bind(&StockpileSerializer::write_refuse, this, _1)); + std::bind(&StockpileSettingsSerializer::write_refuse, this, _1)); write_cat("sheet", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_sheet, + mSettings->flags.whole, + mSettings->flags.mask_sheet, std::bind(&StockpileSettings::mutable_sheet, &mBuffer), - std::bind(&StockpileSerializer::write_sheet, this, _1)); + std::bind(&StockpileSettingsSerializer::write_sheet, this, _1)); write_cat("stone", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_stone, + mSettings->flags.whole, + mSettings->flags.mask_stone, std::bind(&StockpileSettings::mutable_stone, &mBuffer), - std::bind(&StockpileSerializer::write_stone, this, _1)); + std::bind(&StockpileSettingsSerializer::write_stone, this, _1)); write_cat("weapons", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_weapons, + mSettings->flags.whole, + mSettings->flags.mask_weapons, std::bind(&StockpileSettings::mutable_weapons, &mBuffer), - std::bind(&StockpileSerializer::write_weapons, this, _1)); + std::bind(&StockpileSettingsSerializer::write_weapons, this, _1)); write_cat("wood", include_types, - mPile->settings.flags.whole, - mPile->settings.flags.mask_wood, + mSettings->flags.whole, + mSettings->flags.mask_wood, std::bind(&StockpileSettings::mutable_wood, &mBuffer), - std::bind(&StockpileSerializer::write_wood, this, _1)); + std::bind(&StockpileSettingsSerializer::write_wood, this, _1)); } -void StockpileSerializer::read(DeserializeMode mode, const vector& filters) { +void StockpileSerializer::write(uint32_t includedElements) { + if (includedElements & INCLUDED_ELEMENTS_CONTAINERS) + write_containers(); + + StockpileSettingsSerializer::write(includedElements); +} + +void StockpileSettingsSerializer::read(DeserializeMode mode, const vector& filters) { DEBUG(log).print("==READ==\n"); - read_containers(mode); read_general(mode); read_ammo(mode, filters); read_animals(mode, filters); @@ -786,6 +795,11 @@ void StockpileSerializer::read(DeserializeMode mode, const vector& filte read_wood(mode, filters); } +void StockpileSerializer::read(DeserializeMode mode, const vector& filters) { + read_containers(mode); + StockpileSettingsSerializer::read(mode, filters); +} + void StockpileSerializer::write_containers() { DEBUG(log).print("writing container settings\n"); mBuffer.set_max_bins(mPile->max_bins); @@ -854,53 +868,61 @@ void StockpileSerializer::read_containers(DeserializeMode mode) { mPile->max_wheelbarrows); } -void StockpileSerializer::write_general() { +void StockpileSettingsSerializer::write_general() { DEBUG(log).print("writing general settings\n"); + mBuffer.set_allow_inorganic(mSettings->allow_inorganic); + mBuffer.set_allow_organic(mSettings->allow_organic); +} + +void StockpileSerializer::write_general() { + StockpileSettingsSerializer::write_general(); mBuffer.set_use_links_only(mPile->use_links_only); - mBuffer.set_allow_inorganic(mPile->settings.allow_inorganic); - mBuffer.set_allow_organic(mPile->settings.allow_organic); } -void StockpileSerializer::read_general(DeserializeMode mode) { - read_elem("use_links_only", mode, - std::bind(&StockpileSettings::has_use_links_only, mBuffer), - std::bind(&StockpileSettings::use_links_only, mBuffer), - mPile->use_links_only); +void StockpileSettingsSerializer::read_general(DeserializeMode mode) { read_elem("allow_inorganic", mode, std::bind(&StockpileSettings::has_allow_inorganic, mBuffer), std::bind(&StockpileSettings::allow_inorganic, mBuffer), - mPile->settings.allow_inorganic); + mSettings->allow_inorganic); read_elem("allow_organic", mode, std::bind(&StockpileSettings::has_allow_organic, mBuffer), std::bind(&StockpileSettings::allow_organic, mBuffer), - mPile->settings.allow_organic); + mSettings->allow_organic); +} + +void StockpileSerializer::read_general(DeserializeMode mode) { + StockpileSettingsSerializer::read_general(mode); + read_elem("use_links_only", mode, + std::bind(&StockpileSettings::has_use_links_only, mBuffer), + std::bind(&StockpileSettings::use_links_only, mBuffer), + mPile->use_links_only); } static bool ammo_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { +bool StockpileSettingsSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { bool all = serialize_list_itemdef( [&](const string& token) { ammo->add_type(token); }, - mPile->settings.ammo.type, + mSettings->ammo.type, vector(world->raws.itemdefs.ammo.begin(), world->raws.itemdefs.ammo.end()), item_type::AMMO); all = serialize_list_material( ammo_mat_is_allowed, [&](const string& token) { ammo->add_mats(token); }, - mPile->settings.ammo.mats) && all; + mSettings->ammo.mats) && all; - if (mPile->settings.ammo.other_mats.size() > 2) { + if (mSettings->ammo.other_mats.size() > 2) { WARN(log).print("ammo other materials > 2: %zd\n", - mPile->settings.ammo.other_mats.size()); + mSettings->ammo.other_mats.size()); } size_t num_other_mats = std::min(size_t(2), - mPile->settings.ammo.other_mats.size()); + mSettings->ammo.other_mats.size()); for (size_t i = 0; i < num_other_mats; ++i) { - if (!mPile->settings.ammo.other_mats.at(i)) { + if (!mSettings->ammo.other_mats.at(i)) { all = false; continue; } @@ -911,22 +933,22 @@ bool StockpileSerializer::write_ammo(StockpileSettings::AmmoSet* ammo) { all = serialize_list_quality( [&](const string& token) { ammo->add_quality_core(token); }, - mPile->settings.ammo.quality_core) && all; + mSettings->ammo.quality_core) && all; all = serialize_list_quality( [&](const string& token) { ammo->add_quality_total(token); }, - mPile->settings.ammo.quality_total) && all; + mSettings->ammo.quality_total) && all; return all; } -void StockpileSerializer::read_ammo(DeserializeMode mode, const vector& filters) { - auto & pammo = mPile->settings.ammo; +void StockpileSettingsSerializer::read_ammo(DeserializeMode mode, const vector& filters) { + auto & pammo = mSettings->ammo; read_category("ammo", mode, std::bind(&StockpileSettings::has_ammo, mBuffer), std::bind(&StockpileSettings::ammo, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_ammo, + mSettings->flags.whole, + mSettings->flags.mask_ammo, [&]() { pammo.type.clear(); pammo.mats.clear(); @@ -970,8 +992,8 @@ void StockpileSerializer::read_ammo(DeserializeMode mode, const vector& }); } -bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { - auto & panimals = mPile->settings.animals; +bool StockpileSettingsSerializer::write_animals(StockpileSettings::AnimalsSet* animals) { + auto & panimals = mSettings->animals; bool all = panimals.empty_cages && panimals.empty_traps; animals->set_empty_cages(panimals.empty_cages); @@ -982,13 +1004,13 @@ bool StockpileSerializer::write_animals(StockpileSettings::AnimalsSet* animals) panimals.enabled) && all; } -void StockpileSerializer::read_animals(DeserializeMode mode, const vector& filters) { - auto & panimals = mPile->settings.animals; +void StockpileSettingsSerializer::read_animals(DeserializeMode mode, const vector& filters) { + auto & panimals = mSettings->animals; read_category("animals", mode, std::bind(&StockpileSettings::has_animals, mBuffer), std::bind(&StockpileSettings::animals, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_animals, + mSettings->flags.whole, + mSettings->flags.mask_animals, [&]() { panimals.empty_cages = false; panimals.empty_traps = false; @@ -1010,9 +1032,9 @@ static bool armor_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && mi.material->flags.is_set(material_flags::IS_METAL); } -bool StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { +bool StockpileSettingsSerializer::write_armor(StockpileSettings::ArmorSet* armor) { - auto & parmor = mPile->settings.armor; + auto & parmor = mSettings->armor; bool all = parmor.unusable && parmor.usable; armor->set_unusable(parmor.unusable); @@ -1082,13 +1104,13 @@ bool StockpileSerializer::write_armor(StockpileSettings::ArmorSet* armor) { return all; } -void StockpileSerializer::read_armor(DeserializeMode mode, const vector& filters) { - auto & parmor = mPile->settings.armor; +void StockpileSettingsSerializer::read_armor(DeserializeMode mode, const vector& filters) { + auto & parmor = mSettings->armor; read_category("armor", mode, std::bind(&StockpileSettings::has_armor, mBuffer), std::bind(&StockpileSettings::armor, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_armor, + mSettings->flags.whole, + mSettings->flags.mask_armor, [&]() { parmor.unusable = false; parmor.usable = false; @@ -1160,35 +1182,35 @@ static bool blocks_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { +bool StockpileSettingsSerializer::write_bars_blocks(StockpileSettings::BarsBlocksSet* bars_blocks) { bool all = serialize_list_material( bars_mat_is_allowed, [&](const string& token) { bars_blocks->add_bars_mats(token); }, - mPile->settings.bars_blocks.bars_mats); + mSettings->bars_blocks.bars_mats); all = serialize_list_material( blocks_mat_is_allowed, [&](const string& token) { bars_blocks->add_blocks_mats(token); }, - mPile->settings.bars_blocks.blocks_mats) && all; + mSettings->bars_blocks.blocks_mats) && all; all = serialize_list_other_mats( mOtherMatsBars.mats, [&](const string& token) { bars_blocks->add_bars_other_mats(token); }, - mPile->settings.bars_blocks.bars_other_mats) && all; + mSettings->bars_blocks.bars_other_mats) && all; all = serialize_list_other_mats( mOtherMatsBlocks.mats, [&](const string& token) { bars_blocks->add_blocks_other_mats(token); }, - mPile->settings.bars_blocks.blocks_other_mats) && all; + mSettings->bars_blocks.blocks_other_mats) && all; return all; } -void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const vector& filters) { - auto & pbarsblocks = mPile->settings.bars_blocks; +void StockpileSettingsSerializer::read_bars_blocks(DeserializeMode mode, const vector& filters) { + auto & pbarsblocks = mSettings->bars_blocks; read_category("bars_blocks", mode, std::bind(&StockpileSettings::has_barsblocks, mBuffer), std::bind(&StockpileSettings::barsblocks, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_bars_blocks, + mSettings->flags.whole, + mSettings->flags.mask_bars_blocks, [&]() { pbarsblocks.bars_mats.clear(); pbarsblocks.bars_other_mats.clear(); @@ -1219,51 +1241,51 @@ void StockpileSerializer::read_bars_blocks(DeserializeMode mode, const vectoradd_thread_silk(token); }, - &mPile->settings.cloth.thread_silk, organic_mat_category::Silk) && all; + &mSettings->cloth.thread_silk, organic_mat_category::Silk) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_thread_plant(token); }, - &mPile->settings.cloth.thread_plant, organic_mat_category::PlantFiber) && all; + &mSettings->cloth.thread_plant, organic_mat_category::PlantFiber) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_thread_yarn(token); }, - &mPile->settings.cloth.thread_yarn, organic_mat_category::Yarn) && all; + &mSettings->cloth.thread_yarn, organic_mat_category::Yarn) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_thread_metal(token); }, - &mPile->settings.cloth.thread_metal, organic_mat_category::MetalThread) && all; + &mSettings->cloth.thread_metal, organic_mat_category::MetalThread) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_cloth_silk(token); }, - &mPile->settings.cloth.cloth_silk, organic_mat_category::Silk) && all; + &mSettings->cloth.cloth_silk, organic_mat_category::Silk) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_cloth_plant(token); }, - &mPile->settings.cloth.cloth_plant, organic_mat_category::PlantFiber) && all; + &mSettings->cloth.cloth_plant, organic_mat_category::PlantFiber) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_cloth_yarn(token); }, - &mPile->settings.cloth.cloth_yarn, organic_mat_category::Yarn) && all; + &mSettings->cloth.cloth_yarn, organic_mat_category::Yarn) && all; all = serialize_list_organic_mat( [&](const string& token) { cloth->add_cloth_metal(token); }, - &mPile->settings.cloth.cloth_metal, organic_mat_category::MetalThread) && all; + &mSettings->cloth.cloth_metal, organic_mat_category::MetalThread) && all; return all; } -void StockpileSerializer::read_cloth(DeserializeMode mode, const vector& filters) { - auto & pcloth = mPile->settings.cloth; +void StockpileSettingsSerializer::read_cloth(DeserializeMode mode, const vector& filters) { + auto & pcloth = mSettings->cloth; read_category("cloth", mode, std::bind(&StockpileSettings::has_cloth, mBuffer), std::bind(&StockpileSettings::cloth, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_cloth, + mSettings->flags.whole, + mSettings->flags.mask_cloth, [&]() { pcloth.thread_silk.clear(); pcloth.thread_yarn.clear(); @@ -1315,20 +1337,20 @@ static bool coins_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid(); } -bool StockpileSerializer::write_coins(StockpileSettings::CoinSet* coins) { +bool StockpileSettingsSerializer::write_coins(StockpileSettings::CoinSet* coins) { return serialize_list_material( coins_mat_is_allowed, [&](const string& token) { coins->add_mats(token); }, - mPile->settings.coins.mats); + mSettings->coins.mats); } -void StockpileSerializer::read_coins(DeserializeMode mode, const vector& filters) { - auto & pcoins = mPile->settings.coins; +void StockpileSettingsSerializer::read_coins(DeserializeMode mode, const vector& filters) { + auto & pcoins = mSettings->coins; read_category("coin", mode, std::bind(&StockpileSettings::has_coin, mBuffer), std::bind(&StockpileSettings::coin, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_coins, + mSettings->flags.whole, + mSettings->flags.mask_coins, [&]() { pcoins.mats.clear(); }, @@ -1378,37 +1400,37 @@ static bool finished_goods_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_GEM) || mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { +bool StockpileSettingsSerializer::write_finished_goods(StockpileSettings::FinishedGoodsSet* finished_goods) { bool all = serialize_list_item_type( finished_goods_type_is_allowed, [&](const string& token) { finished_goods->add_type(token); }, - mPile->settings.finished_goods.type); + mSettings->finished_goods.type); all = serialize_list_material( finished_goods_mat_is_allowed, [&](const string& token) { finished_goods->add_mats(token); }, - mPile->settings.finished_goods.mats) && all; + mSettings->finished_goods.mats) && all; all = serialize_list_other_mats( mOtherMatsFinishedGoods.mats, [&](const string& token) { finished_goods->add_other_mats(token); }, - mPile->settings.finished_goods.other_mats) && all; + mSettings->finished_goods.other_mats) && all; all = serialize_list_quality([&](const string& token) { finished_goods->add_quality_core(token); }, - mPile->settings.finished_goods.quality_core) && all; + mSettings->finished_goods.quality_core) && all; all = serialize_list_quality([&](const string& token) { finished_goods->add_quality_total(token); }, - mPile->settings.finished_goods.quality_total) && all; + mSettings->finished_goods.quality_total) && all; return all; } -void StockpileSerializer::read_finished_goods(DeserializeMode mode, const vector& filters) { - auto & pfinished_goods = mPile->settings.finished_goods; +void StockpileSettingsSerializer::read_finished_goods(DeserializeMode mode, const vector& filters) { + auto & pfinished_goods = mSettings->finished_goods; read_category("finished_goods", mode, std::bind(&StockpileSettings::has_finished_goods, mBuffer), std::bind(&StockpileSettings::finished_goods, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_finished_goods, + mSettings->flags.whole, + mSettings->flags.mask_finished_goods, [&]() { pfinished_goods.type.clear(); pfinished_goods.other_mats.clear(); @@ -1441,7 +1463,7 @@ void StockpileSerializer::read_finished_goods(DeserializeMode mode, const vector }); } -food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_category cat) { +food_pair StockpileSettingsSerializer::food_map(organic_mat_category::organic_mat_category cat) { using df::enums::organic_mat_category::organic_mat_category; switch (cat) { @@ -1451,7 +1473,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_meat(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().meat(idx); }; - return food_pair("meat", setter, &mPile->settings.food.meat, getter, mBuffer.food().meat_size()); + return food_pair("meat", setter, &mSettings->food.meat, getter, mBuffer.food().meat_size()); } case organic_mat_category::Fish: { @@ -1459,7 +1481,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_fish(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().fish(idx); }; - return food_pair("fish/prepared", setter, &mPile->settings.food.fish, getter, mBuffer.food().fish_size()); + return food_pair("fish/prepared", setter, &mSettings->food.fish, getter, mBuffer.food().fish_size()); } case organic_mat_category::UnpreparedFish: { @@ -1467,7 +1489,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_unprepared_fish(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().unprepared_fish(idx); }; - return food_pair("fish/unprepared", setter, &mPile->settings.food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); + return food_pair("fish/unprepared", setter, &mSettings->food.unprepared_fish, getter, mBuffer.food().unprepared_fish_size()); } case organic_mat_category::Eggs: { @@ -1475,7 +1497,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_egg(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().egg(idx); }; - return food_pair("egg", setter, &mPile->settings.food.egg, getter, mBuffer.food().egg_size()); + return food_pair("egg", setter, &mSettings->food.egg, getter, mBuffer.food().egg_size()); } case organic_mat_category::Plants: { @@ -1483,7 +1505,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_plants(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().plants(idx); }; - return food_pair("plants", setter, &mPile->settings.food.plants, getter, mBuffer.food().plants_size()); + return food_pair("plants", setter, &mSettings->food.plants, getter, mBuffer.food().plants_size()); } case organic_mat_category::PlantDrink: { @@ -1491,7 +1513,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_drink_plant(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().drink_plant(idx); }; - return food_pair("drink/plant", setter, &mPile->settings.food.drink_plant, getter, mBuffer.food().drink_plant_size()); + return food_pair("drink/plant", setter, &mSettings->food.drink_plant, getter, mBuffer.food().drink_plant_size()); } case organic_mat_category::CreatureDrink: { @@ -1499,7 +1521,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_drink_animal(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().drink_animal(idx); }; - return food_pair("drink/animal", setter, &mPile->settings.food.drink_animal, getter, mBuffer.food().drink_animal_size()); + return food_pair("drink/animal", setter, &mSettings->food.drink_animal, getter, mBuffer.food().drink_animal_size()); } case organic_mat_category::PlantCheese: { @@ -1507,7 +1529,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_cheese_plant(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().cheese_plant(idx); }; - return food_pair("cheese/plant", setter, &mPile->settings.food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); + return food_pair("cheese/plant", setter, &mSettings->food.cheese_plant, getter, mBuffer.food().cheese_plant_size()); } case organic_mat_category::CreatureCheese: { @@ -1515,7 +1537,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_cheese_animal(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().cheese_animal(idx); }; - return food_pair("cheese/animal", setter, &mPile->settings.food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); + return food_pair("cheese/animal", setter, &mSettings->food.cheese_animal, getter, mBuffer.food().cheese_animal_size()); } case organic_mat_category::Seed: { @@ -1523,7 +1545,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_seeds(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().seeds(idx); }; - return food_pair("seeds", setter, &mPile->settings.food.seeds, getter, mBuffer.food().seeds_size()); + return food_pair("seeds", setter, &mSettings->food.seeds, getter, mBuffer.food().seeds_size()); } case organic_mat_category::Leaf: { @@ -1531,7 +1553,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_leaves(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().leaves(idx); }; - return food_pair("leaves", setter, &mPile->settings.food.leaves, getter, mBuffer.food().leaves_size()); + return food_pair("leaves", setter, &mSettings->food.leaves, getter, mBuffer.food().leaves_size()); } case organic_mat_category::PlantPowder: { @@ -1539,7 +1561,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_powder_plant(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().powder_plant(idx); }; - return food_pair("powder/plant", setter, &mPile->settings.food.powder_plant, getter, mBuffer.food().powder_plant_size()); + return food_pair("powder/plant", setter, &mSettings->food.powder_plant, getter, mBuffer.food().powder_plant_size()); } case organic_mat_category::CreaturePowder: { @@ -1547,7 +1569,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_powder_creature(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().powder_creature(idx); }; - return food_pair("powder/animal", setter, &mPile->settings.food.powder_creature, getter, mBuffer.food().powder_creature_size()); + return food_pair("powder/animal", setter, &mSettings->food.powder_creature, getter, mBuffer.food().powder_creature_size()); } case organic_mat_category::Glob: { @@ -1555,7 +1577,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_glob(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().glob(idx); }; - return food_pair("glob", setter, &mPile->settings.food.glob, getter, mBuffer.food().glob_size()); + return food_pair("glob", setter, &mSettings->food.glob, getter, mBuffer.food().glob_size()); } case organic_mat_category::PlantLiquid: { @@ -1563,7 +1585,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_liquid_plant(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().liquid_plant(idx); }; - return food_pair("liquid/plant", setter, &mPile->settings.food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); + return food_pair("liquid/plant", setter, &mSettings->food.liquid_plant, getter, mBuffer.food().liquid_plant_size()); } case organic_mat_category::CreatureLiquid: { @@ -1571,7 +1593,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_liquid_animal(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().liquid_animal(idx); }; - return food_pair("liquid/animal", setter, &mPile->settings.food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); + return food_pair("liquid/animal", setter, &mSettings->food.liquid_animal, getter, mBuffer.food().liquid_animal_size()); } case organic_mat_category::MiscLiquid: { @@ -1579,7 +1601,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_liquid_misc(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().liquid_misc(idx); }; - return food_pair("liquid/misc", setter, &mPile->settings.food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); + return food_pair("liquid/misc", setter, &mSettings->food.liquid_misc, getter, mBuffer.food().liquid_misc_size()); } case organic_mat_category::Paste: @@ -1588,7 +1610,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_glob_paste(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().glob_paste(idx); }; - return food_pair("paste", setter, &mPile->settings.food.glob_paste, getter, mBuffer.food().glob_paste_size()); + return food_pair("paste", setter, &mSettings->food.glob_paste, getter, mBuffer.food().glob_paste_size()); } case organic_mat_category::Pressed: { @@ -1596,7 +1618,7 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego mBuffer.mutable_food()->add_glob_pressed(id); }; FuncReadImport getter = [&](size_t idx) -> string { return mBuffer.food().glob_pressed(idx); }; - return food_pair("pressed", setter, &mPile->settings.food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); + return food_pair("pressed", setter, &mSettings->food.glob_pressed, getter, mBuffer.food().glob_pressed_size()); } case organic_mat_category::Leather: case organic_mat_category::Silk: @@ -1623,8 +1645,8 @@ food_pair StockpileSerializer::food_map(organic_mat_category::organic_mat_catego return food_pair(); } -bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { - auto & pfood = mPile->settings.food; +bool StockpileSettingsSerializer::write_food(StockpileSettings::FoodSet* food) { + auto & pfood = mSettings->food; bool all = pfood.prepared_meals; food->set_prepared_meals(pfood.prepared_meals); @@ -1642,16 +1664,16 @@ bool StockpileSerializer::write_food(StockpileSettings::FoodSet* food) { return all; } -void StockpileSerializer::read_food(DeserializeMode mode, const vector& filters) { +void StockpileSettingsSerializer::read_food(DeserializeMode mode, const vector& filters) { using df::enums::organic_mat_category::organic_mat_category; using traits = df::enum_traits; - auto & pfood = mPile->settings.food; + auto & pfood = mSettings->food; read_category("food", mode, std::bind(&StockpileSettings::has_food, mBuffer), std::bind(&StockpileSettings::food, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_food, + mSettings->flags.whole, + mSettings->flags.mask_food, [&]() { pfood.prepared_meals = false; for (int32_t mat_category = traits::first_item_value; mat_category < traits::last_item_value; ++mat_category) { @@ -1682,11 +1704,11 @@ static bool furniture_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furniture) { +bool StockpileSettingsSerializer::write_furniture(StockpileSettings::FurnitureSet* furniture) { using df::enums::furniture_type::furniture_type; using type_traits = df::enum_traits; - auto & pfurniture = mPile->settings.furniture; + auto & pfurniture = mSettings->furniture; bool all = true; for (size_t i = 0; i < pfurniture.type.size(); ++i) { @@ -1717,13 +1739,13 @@ bool StockpileSerializer::write_furniture(StockpileSettings::FurnitureSet* furni return all; } -void StockpileSerializer::read_furniture(DeserializeMode mode, const vector& filters) { - auto & pfurniture = mPile->settings.furniture; +void StockpileSettingsSerializer::read_furniture(DeserializeMode mode, const vector& filters) { + auto & pfurniture = mSettings->furniture; read_category("furniture", mode, std::bind(&StockpileSettings::has_furniture, mBuffer), std::bind(&StockpileSettings::furniture, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_furniture, + mSettings->flags.whole, + mSettings->flags.mask_furniture, [&]() { pfurniture.type.clear(); pfurniture.other_mats.clear(); @@ -1786,10 +1808,10 @@ static bool gem_other_mat_is_allowed(MaterialInfo& mi) { return mi.isValid() && (mi.getToken() == "GLASS_GREEN" || mi.getToken() == "GLASS_CLEAR" || mi.getToken() == "GLASS_CRYSTAL"); } -bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { +bool StockpileSettingsSerializer::write_gems(StockpileSettings::GemsSet* gems) { MaterialInfo mi; - auto & pgems = mPile->settings.gems; + auto & pgems = mSettings->gems; bool all = serialize_list_material( gem_mat_is_allowed, @@ -1830,13 +1852,13 @@ bool StockpileSerializer::write_gems(StockpileSettings::GemsSet* gems) { return all; } -void StockpileSerializer::read_gems(DeserializeMode mode, const vector& filters) { - auto & pgems = mPile->settings.gems; +void StockpileSettingsSerializer::read_gems(DeserializeMode mode, const vector& filters) { + auto & pgems = mSettings->gems; read_category("gems", mode, std::bind(&StockpileSettings::has_gems, mBuffer), std::bind(&StockpileSettings::gems, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_gems, + mSettings->flags.whole, + mSettings->flags.mask_gems, [&]() { pgems.cut_other_mats.clear(); pgems.cut_mats.clear(); @@ -1887,19 +1909,19 @@ void StockpileSerializer::read_gems(DeserializeMode mode, const vector& }); } -bool StockpileSerializer::write_leather(StockpileSettings::LeatherSet* leather) { +bool StockpileSettingsSerializer::write_leather(StockpileSettings::LeatherSet* leather) { return serialize_list_organic_mat( [&](const string& id) { leather->add_mats(id); }, - &mPile->settings.leather.mats, organic_mat_category::Leather); + &mSettings->leather.mats, organic_mat_category::Leather); } -void StockpileSerializer::read_leather(DeserializeMode mode, const vector& filters) { - auto & pleather = mPile->settings.leather; +void StockpileSettingsSerializer::read_leather(DeserializeMode mode, const vector& filters) { + auto & pleather = mSettings->leather; read_category("leather", mode, std::bind(&StockpileSettings::has_leather, mBuffer), std::bind(&StockpileSettings::leather, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_leather, + mSettings->flags.whole, + mSettings->flags.mask_leather, [&]() { pleather.mats.clear(); }, @@ -1912,19 +1934,19 @@ void StockpileSerializer::read_leather(DeserializeMode mode, const vectoradd_corpses(token); }, - mPile->settings.corpses.corpses); + mSettings->corpses.corpses); } -void StockpileSerializer::read_corpses(DeserializeMode mode, const vector& filters) { - auto & pcorpses = mPile->settings.corpses; +void StockpileSettingsSerializer::read_corpses(DeserializeMode mode, const vector& filters) { + auto & pcorpses = mSettings->corpses; read_category("corpses", mode, std::bind(&StockpileSettings::has_corpses_v50, mBuffer), std::bind(&StockpileSettings::corpses_v50, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_corpses, + mSettings->flags.whole, + mSettings->flags.mask_corpses, [&]() { pcorpses.corpses.clear(); }, @@ -1945,8 +1967,8 @@ static bool refuse_type_is_allowed(item_type::item_type type) { return true; } -bool StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { - auto & prefuse = mPile->settings.refuse; +bool StockpileSettingsSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { + auto & prefuse = mSettings->refuse; bool all = prefuse.fresh_raw_hide && prefuse.rotten_raw_hide; refuse->set_fresh_raw_hide(prefuse.fresh_raw_hide); @@ -1984,13 +2006,13 @@ bool StockpileSerializer::write_refuse(StockpileSettings::RefuseSet* refuse) { return all; } -void StockpileSerializer::read_refuse(DeserializeMode mode, const vector& filters) { - auto & prefuse = mPile->settings.refuse; +void StockpileSettingsSerializer::read_refuse(DeserializeMode mode, const vector& filters) { + auto & prefuse = mSettings->refuse; read_category("refuse", mode, std::bind(&StockpileSettings::has_refuse, mBuffer), std::bind(&StockpileSettings::refuse, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_refuse, + mSettings->flags.whole, + mSettings->flags.mask_refuse, [&]() { prefuse.fresh_raw_hide = false; prefuse.rotten_raw_hide = false; @@ -2042,25 +2064,25 @@ void StockpileSerializer::read_refuse(DeserializeMode mode, const vector } -bool StockpileSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { +bool StockpileSettingsSerializer::write_sheet(StockpileSettings::SheetSet* sheet) { bool all = serialize_list_organic_mat( [&](const string& token) { sheet->add_paper(token); }, - &mPile->settings.sheet.paper, organic_mat_category::Paper); + &mSettings->sheet.paper, organic_mat_category::Paper); all = serialize_list_organic_mat( [&](const string& token) { sheet->add_parchment(token); }, - &mPile->settings.sheet.parchment, organic_mat_category::Parchment) && all; + &mSettings->sheet.parchment, organic_mat_category::Parchment) && all; return all; } -void StockpileSerializer::read_sheet(DeserializeMode mode, const vector& filters) { - auto & psheet = mPile->settings.sheet; +void StockpileSettingsSerializer::read_sheet(DeserializeMode mode, const vector& filters) { + auto & psheet = mSettings->sheet; read_category("sheet", mode, std::bind(&StockpileSettings::has_sheet, mBuffer), std::bind(&StockpileSettings::sheet, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_sheet, + mSettings->flags.whole, + mSettings->flags.mask_sheet, [&]() { psheet.paper.clear(); psheet.parchment.clear(); @@ -2086,20 +2108,20 @@ static bool stone_is_allowed(const MaterialInfo& mi) { return is_allowed_soil || is_allowed_stone; } -bool StockpileSerializer::write_stone(StockpileSettings::StoneSet* stone) { +bool StockpileSettingsSerializer::write_stone(StockpileSettings::StoneSet* stone) { return serialize_list_material( stone_is_allowed, [&](const string& token) { stone->add_mats(token); }, - mPile->settings.stone.mats); + mSettings->stone.mats); } -void StockpileSerializer::read_stone(DeserializeMode mode, const vector& filters) { - auto & pstone = mPile->settings.stone; +void StockpileSettingsSerializer::read_stone(DeserializeMode mode, const vector& filters) { + auto & pstone = mSettings->stone; read_category("stone", mode, std::bind(&StockpileSettings::has_stone, mBuffer), std::bind(&StockpileSettings::stone, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_stone, + mSettings->flags.whole, + mSettings->flags.mask_stone, [&]() { pstone.mats.clear(); }, @@ -2116,8 +2138,8 @@ static bool weapons_mat_is_allowed(const MaterialInfo& mi) { return mi.isValid() && mi.material && (mi.material->flags.is_set(material_flags::IS_METAL) || mi.material->flags.is_set(material_flags::IS_STONE)); } -bool StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) { - auto & pweapons = mPile->settings.weapons; +bool StockpileSettingsSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) { + auto & pweapons = mSettings->weapons; bool all = pweapons.unusable && pweapons.usable; weapons->set_unusable(pweapons.unusable); @@ -2156,13 +2178,13 @@ bool StockpileSerializer::write_weapons(StockpileSettings::WeaponsSet* weapons) return all; } -void StockpileSerializer::read_weapons(DeserializeMode mode, const vector& filters) { - auto & pweapons = mPile->settings.weapons; +void StockpileSettingsSerializer::read_weapons(DeserializeMode mode, const vector& filters) { + auto & pweapons = mSettings->weapons; read_category("weapons", mode, std::bind(&StockpileSettings::has_weapons, mBuffer), std::bind(&StockpileSettings::weapons, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_weapons, + mSettings->flags.whole, + mSettings->flags.mask_weapons, [&]() { pweapons.unusable = false; pweapons.usable = false; @@ -2209,10 +2231,10 @@ static bool wood_mat_is_allowed(const df::plant_raw* plant) { return plant && plant->flags.is_set(plant_raw_flags::TREE); } -bool StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { +bool StockpileSettingsSerializer::write_wood(StockpileSettings::WoodSet* wood) { bool all = true; - for (size_t i = 0; i < mPile->settings.wood.mats.size(); ++i) { - if (!mPile->settings.wood.mats.at(i)) { + for (size_t i = 0; i < mSettings->wood.mats.size(); ++i) { + if (!mSettings->wood.mats.at(i)) { all = false; continue; } @@ -2225,13 +2247,13 @@ bool StockpileSerializer::write_wood(StockpileSettings::WoodSet* wood) { return all; } -void StockpileSerializer::read_wood(DeserializeMode mode, const vector& filters) { - auto & pwood = mPile->settings.wood; +void StockpileSettingsSerializer::read_wood(DeserializeMode mode, const vector& filters) { + auto & pwood = mSettings->wood; read_category("wood", mode, std::bind(&StockpileSettings::has_wood, mBuffer), std::bind(&StockpileSettings::wood, mBuffer), - mPile->settings.flags.whole, - mPile->settings.flags.mask_wood, + mSettings->flags.whole, + mSettings->flags.mask_wood, [&]() { pwood.mats.clear(); }, diff --git a/plugins/stockpiles/StockpileSerializer.h b/plugins/stockpiles/StockpileSerializer.h index f0e1a62e2..4798fba5e 100644 --- a/plugins/stockpiles/StockpileSerializer.h +++ b/plugins/stockpiles/StockpileSerializer.h @@ -4,6 +4,7 @@ #include "df/itemdef.h" #include "df/organic_mat_category.h" +#include "df/stockpile_settings.h" #include "proto/stockpiles.pb.h" @@ -57,14 +58,14 @@ struct food_pair { /** * Class for serializing the stockpile_settings structure into a Google protobuf */ -class StockpileSerializer { +class StockpileSettingsSerializer { public: /** - * @param stockpile stockpile to read or write settings to + * @param settings settings to read or write to */ - StockpileSerializer(df::building_stockpilest* stockpile); + StockpileSettingsSerializer(df::stockpile_settings *settings); - ~StockpileSerializer(); + ~StockpileSettingsSerializer(); /** * Since we depend on protobuf-lite, not the full lib, we copy this function from @@ -88,20 +89,20 @@ public: */ bool unserialize_from_file(const std::string& file, DeserializeMode mode, const std::vector& filters); -private: - df::building_stockpilest* mPile; +protected: dfstockpiles::StockpileSettings mBuffer; // read memory structures and serialize to protobuf - void write(uint32_t includedElements); + virtual void write(uint32_t includedElements); // parse serialized data into ui indices - void read(DeserializeMode mode, const std::vector& filters); + virtual void read(DeserializeMode mode, const std::vector& filters); - void write_containers(); - void read_containers(DeserializeMode mode); - void write_general(); - void read_general(DeserializeMode mode); + virtual void write_general(); + virtual void read_general(DeserializeMode mode); + +private: + df::stockpile_settings *mSettings; bool write_ammo(dfstockpiles::StockpileSettings::AmmoSet* ammo); void read_ammo(DeserializeMode mode, const std::vector& filters); @@ -139,3 +140,32 @@ private: bool write_wood(dfstockpiles::StockpileSettings::WoodSet* wood); void read_wood(DeserializeMode mode, const std::vector& filters); }; + +/** + * Class for serializing a stockpile into a Google protobuf + */ +class StockpileSerializer : public StockpileSettingsSerializer { +public: + /** + * @param stockpile stockpile to read or write settings to + */ + StockpileSerializer(df::building_stockpilest* stockpile); + + ~StockpileSerializer(); + +protected: + // read memory structures and serialize to protobuf + virtual void write(uint32_t includedElements); + + // parse serialized data into ui indices + virtual void read(DeserializeMode mode, const std::vector& filters); + + virtual void write_general(); + virtual void read_general(DeserializeMode mode); + +private: + df::building_stockpilest* mPile; + + void write_containers(); + void read_containers(DeserializeMode mode); +}; diff --git a/plugins/stockpiles/stockpiles.cpp b/plugins/stockpiles/stockpiles.cpp index d1ce46e9c..de57598c4 100644 --- a/plugins/stockpiles/stockpiles.cpp +++ b/plugins/stockpiles/stockpiles.cpp @@ -8,6 +8,8 @@ #include "df/building.h" #include "df/building_stockpilest.h" +#include "df/hauling_route.h" +#include "df/hauling_stop.h" #include #include @@ -147,8 +149,54 @@ static bool stockpiles_import(color_ostream& out, string fname, int id, string m return true; } +static bool stockpiles_route_import(color_ostream& out, string fname, int route_id, int stop_id, string mode_str, string filter) { + auto route = df::hauling_route::find(route_id); + if (!route) { + out.printerr("Specified hauling route not found: %d.\n", route_id); + return false; + } + + df::hauling_stop *stop = binsearch_in_vector(route->stops, &df::hauling_stop::id, stop_id); + if (!stop) { + out.printerr("Specified hauling stop not found in route %d: %d.\n", route_id, stop_id); + return false; + } + + if (!is_dfstockfile(fname)) + fname += ".dfstock"; + + if (!Filesystem::exists(fname)) { + out.printerr("ERROR: file doesn't exist: '%s'\n", fname.c_str()); + return false; + } + + DeserializeMode mode = DESERIALIZE_MODE_SET; + if (mode_str == "enable") + mode = DESERIALIZE_MODE_ENABLE; + else if (mode_str == "disable") + mode = DESERIALIZE_MODE_DISABLE; + + vector filters; + split_string(&filters, filter, ",", true); + + try { + StockpileSettingsSerializer cereal(&stop->settings); + if (!cereal.unserialize_from_file(fname, mode, filters)) { + out.printerr("deserialization failed: '%s'\n", fname.c_str()); + return false; + } + } + catch (std::exception& e) { + out.printerr("deserialization failed: protobuf exception: %s\n", e.what()); + return false; + } + + return true; +} + DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_LUA_FUNCTION(stockpiles_export), DFHACK_LUA_FUNCTION(stockpiles_import), + DFHACK_LUA_FUNCTION(stockpiles_route_import), DFHACK_LUA_END }; From 20ce4a1612feea8998326f07cc02b7d6d8f0fc15 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 28 May 2023 02:28:45 -0700 Subject: [PATCH 1227/2222] add "everything" stockpile settings file useful for setting hauling route settings --- data/stockpiles/everything.dfstock | Bin 0 -> 82 bytes docs/plugins/stockpiles.rst | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 data/stockpiles/everything.dfstock diff --git a/data/stockpiles/everything.dfstock b/data/stockpiles/everything.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..c0fcb60519dbc8f39b9aab50b01767baa370a8a9 GIT binary patch literal 82 zcmd;LQeYHfUctyH#bm*##gxEk#3aDz#ALwe1*C(RG#I0RI0?wl0`iNPJQ%BhbQ2?x b(Z$G=!8n1DaRwvf4n~Gkj7%Jimp}vnsd5XU literal 0 HcmV?d00001 diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 560ae2497..685bdcac3 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -134,6 +134,9 @@ entire category, or with a filter, any matchable subset thereof:: cat_weapons cat_wood +There is also an ``everything`` file that includes all the above categories, +including refuse and corpses. + For many of the categories, there are also flags and subcategory prefixes that you can match with filters and convenient pre-made settings files that manipulate interesting category subsets. From 760cd0cbcb02d7b7b3cd2a0b894219ac66ac95b0 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 28 May 2023 05:37:29 -0700 Subject: [PATCH 1228/2222] implement tameable property filtering --- data/stockpiles/all.dfstock | Bin 0 -> 73 bytes docs/plugins/stockpiles.rst | 29 ++++++++++++++++----- plugins/stockpiles/StockpileSerializer.cpp | 11 ++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 data/stockpiles/all.dfstock diff --git a/data/stockpiles/all.dfstock b/data/stockpiles/all.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..eace9c53dd7de0601ca25e16759cb90993b5f4bd GIT binary patch literal 73 zcmd;LQeYHfUctyH#bm)~#3aDz#ALwe1*C(RG#I0RI0?wl0`iNPJQ%BhbQ2?x(Z$G= U!8n1DaRwvf4n~Gcj7%Ji07w%GJ^%m! literal 0 HcmV?d00001 diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 685bdcac3..ae77e5cf2 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -134,12 +134,14 @@ entire category, or with a filter, any matchable subset thereof:: cat_weapons cat_wood -There is also an ``everything`` file that includes all the above categories, -including refuse and corpses. +In addition, there are files for ``all``, which includes all categories except +refuse and corpses (mirroring the "all" configuration in-game), and +``everything``, which really includes all categories. -For many of the categories, there are also flags and subcategory prefixes that -you can match with filters and convenient pre-made settings files that -manipulate interesting category subsets. +For many of the categories, there are also flags, subcategory prefixes, and +item properties that you can match with filters. In addition, there are +normally at least a few convenient pre-made settings files that manipulate +interesting category subsets. Ammo stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -172,6 +174,10 @@ Flags:: cages traps +Properties:: + + tameable + Settings files:: cages @@ -274,6 +280,13 @@ Notes: * ``thread`` and ``cloth`` settings files set all materials that are not adamantine. +Corpse stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Properties:: + + tameable + Finished goods stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -295,7 +308,7 @@ Settings files:: Example commands for a toy stockpile:: - stockpiles import cat_furniture -f mats/,other/,core/,total/ + stockpiles import cat_finished_goods -f mats/,other/,core/,total/ stockpiles import -m enable toys Food stockpile adjustments @@ -404,6 +417,10 @@ Flags and subcategory prefixes:: teeth/ horns/ +Properties:: + + tameable + Settings files:: rawhides diff --git a/plugins/stockpiles/StockpileSerializer.cpp b/plugins/stockpiles/StockpileSerializer.cpp index fcb0cee3e..b650ec618 100644 --- a/plugins/stockpiles/StockpileSerializer.cpp +++ b/plugins/stockpiles/StockpileSerializer.cpp @@ -11,6 +11,7 @@ // df #include "df/building_stockpilest.h" #include "df/creature_raw.h" +#include "df/caste_raw.h" #include "df/inorganic_raw.h" #include "df/item_quality.h" #include @@ -611,6 +612,12 @@ static bool serialize_list_creature(FuncWriteExport add_value, const vectorcaste.size() || !r->caste[0]->flags.is_set(df::enums::caste_raw_flags::PET)) + return r->name[0]; + return r->name[0] + "/tameable"; +} + static void unserialize_list_creature(const char* subcat, bool all, char val, const vector& filters, FuncReadImport read_value, int32_t list_size, vector& pile_list) { size_t num_elems = world->raws.creatures.all.size(); @@ -618,7 +625,7 @@ static void unserialize_list_creature(const char* subcat, bool all, char val, co if (all) { for (size_t idx = 0; idx < num_elems; ++idx) { auto r = find_creature(idx); - set_filter_elem(subcat, filters, val, r->name[0], r->creature_id, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, get_filter_string(r), r->creature_id, pile_list.at(idx)); } return; } @@ -631,7 +638,7 @@ static void unserialize_list_creature(const char* subcat, bool all, char val, co continue; } auto r = find_creature(idx); - set_filter_elem(subcat, filters, val, r->name[0], r->creature_id, pile_list.at(idx)); + set_filter_elem(subcat, filters, val, get_filter_string(r), r->creature_id, pile_list.at(idx)); } } From 2edfe151d339f8ab4304798fb6c73fcad0888dab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 28 May 2023 20:42:36 -0700 Subject: [PATCH 1229/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b2ee30767..4708711c1 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -63,6 +63,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Window behavior: non-resizable windows now allow dragging by their frame edges by default - `gui/autodump`: fort-mode keybinding: Ctrl-H - Window behavior: if you have multiple DFHack tool windows open, scrolling the mouse wheel while over an unfocused window will focus it and raise it to the top +- `stockpiles`: allow filtering creatures by tameability ## Documentation From 7dad5be7dc30343b75fe9422d1a7114bfbc85593 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Mon, 29 May 2023 07:13:57 +0000 Subject: [PATCH 1230/2222] Auto-update submodules library/xml: master --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 1cc81c0fa..17e77412c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 1cc81c0faf7aa9fd1c18fbc6ef8b2298f31ab1f9 +Subproject commit 17e77412c040dab658a39a34eb0e21cb014c3824 From acca228ac87fa2db27c97912757ced0781dc58d4 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 31 May 2023 07:13:26 +0000 Subject: [PATCH 1231/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index a26e72b6e..6a746d6d5 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit a26e72b6e1da16d6fcaadc71518314fb8aaae193 +Subproject commit 6a746d6d53dcff667c9105579db0f4f1c7f7c9b0 From 974a6155c06071293f7a51bab4d902d9dae76bc3 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 31 May 2023 18:48:08 -0700 Subject: [PATCH 1232/2222] reinstated Buildings.setOwner --- docs/dev/Lua API.rst | 4 ++-- library/include/modules/Buildings.h | 4 ++-- library/modules/Buildings.cpp | 29 ++++++++++++++--------------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index e5167a531..d07cb045e 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -1944,9 +1944,9 @@ General Searches for a specific_ref with the given type. -* ``dfhack.buildings.setOwner(item,unit)`` +* ``dfhack.buildings.setOwner(civzone,unit)`` - Replaces the owner of the building. If unit is *nil*, removes ownership. + Replaces the owner of the civzone. If unit is *nil*, removes ownership. Returns *false* in case of error. * ``dfhack.buildings.getSize(building)`` diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 66745abe6..78163108e 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -108,9 +108,9 @@ DFHACK_EXPORT df::general_ref *getGeneralRef(df::building *building, df::general DFHACK_EXPORT df::specific_ref *getSpecificRef(df::building *building, df::specific_ref_type type); /** - * Sets the owner unit for the building. + * Sets the owner unit for the zone. */ -DFHACK_EXPORT bool setOwner(df::building *building, df::unit *owner); +DFHACK_EXPORT bool setOwner(df::building_civzonest *building, df::unit *owner); /** * Find the building located at the specified tile. diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index dca8010da..3d0d89c49 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -350,46 +350,45 @@ df::specific_ref *Buildings::getSpecificRef(df::building *building, df::specific return findRef(building->specific_refs, type); } -bool Buildings::setOwner(df::building *bld, df::unit *unit) +bool Buildings::setOwner(df::building_civzonest *bld, df::unit *unit) { CHECK_NULL_POINTER(bld); -/* TODO: understand how this changes for v50 - if (!bld->is_room) - return false; - if (bld->owner == unit) + + if (bld->assigned_unit == unit) return true; - if (bld->owner) + df::building * pbld = dynamic_cast(bld); + + if (bld->assigned_unit) { - auto &blist = bld->owner->owned_buildings; - vector_erase_at(blist, linear_index(blist, bld)); + auto &blist = bld->assigned_unit->owned_buildings; + vector_erase_at(blist, linear_index(blist, pbld)); - if (auto spouse = df::unit::find(bld->owner->relationship_ids[df::unit_relationship_type::Spouse])) + if (auto spouse = df::unit::find(bld->assigned_unit->relationship_ids[df::unit_relationship_type::Spouse])) { auto &blist = spouse->owned_buildings; - vector_erase_at(blist, linear_index(blist, bld)); + vector_erase_at(blist, linear_index(blist, pbld)); } } - bld->owner = unit; + bld->assigned_unit = unit; if (unit) { - bld->owner_id = unit->id; + bld->assigned_unit_id = unit->id; unit->owned_buildings.push_back(bld); if (auto spouse = df::unit::find(unit->relationship_ids[df::unit_relationship_type::Spouse])) { auto &blist = spouse->owned_buildings; - if (bld->canUseSpouseRoom() && linear_index(blist, bld) < 0) + if (bld->canUseSpouseRoom() && linear_index(blist, pbld) < 0) blist.push_back(bld); } } else { - bld->owner_id = -1; + bld->assigned_unit_id = -1; } -*/ return true; } From 5c914280c3cbfaf88fc9fb00d92b97033839081b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 1 Jun 2023 10:53:08 -0700 Subject: [PATCH 1233/2222] change dynamic_cast -> virtual_cast --- library/modules/Buildings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 3d0d89c49..78c7b00ed 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -357,7 +357,7 @@ bool Buildings::setOwner(df::building_civzonest *bld, df::unit *unit) if (bld->assigned_unit == unit) return true; - df::building * pbld = dynamic_cast(bld); + df::building * pbld = virtual_cast(bld); if (bld->assigned_unit) { From 6ba830f2cfdc72b382e319344ad6e560477b429b Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 1 Jun 2023 17:54:12 +0000 Subject: [PATCH 1234/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 6a746d6d5..6430e8ed0 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6a746d6d53dcff667c9105579db0f4f1c7f7c9b0 +Subproject commit 6430e8ed0facbe152e98f5ff1f274febfd6b5772 From ecf82471cfc8886d2424ecb43e1ea81c487c460d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Thu, 1 Jun 2023 11:20:08 -0700 Subject: [PATCH 1235/2222] update for 50.08-r2 --- CMakeLists.txt | 4 ++-- docs/changelog.txt | 49 +++++++++++++++++++++++++++++----------------- library/xml | 2 +- scripts | 2 +- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1560c630..3ea16ce29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,8 +192,8 @@ endif() # set up versioning. set(DF_VERSION "50.08") -set(DFHACK_RELEASE "r2rc1") -set(DFHACK_PRERELEASE TRUE) +set(DFHACK_RELEASE "r2") +set(DFHACK_PRERELEASE FALSE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index 4708711c1..bd1b97e6e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -34,14 +34,32 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: # Future ## New Plugins -- `add-spatter`: reinstated: allow mods to add poisons and magical effects to weapons -- `changeitem`: reinstated: change item material, quality, and subtype -- `createitem`: reinstated: create arbitrary items, from the command line -- `deramp`: reinstated: removes all ramps designated for removal from the map -- `flows`: reinstated: counts map blocks with flowing liquids -- `lair`: reinstated: mark the map as a monster lair (this avoids item scatter when the fortress is abandoned) -- `luasocket`: reinstated: provides a Lua API for accessing network sockets -- `work-now`: reinstated, renamed from ``workNow``: reduce the time that dwarves are left without a task after completing a job + +## Fixes + +## Misc Improvements + +## Documentation + +## API + +## Internals + +## Lua + +## Removed + +# 50.08-r2 + +## New Plugins +- `add-spatter`: (reinstated) allow mods to add poisons and magical effects to weapons +- `changeitem`: (reinstated) change item material, quality, and subtype +- `createitem`: (reinstated) create arbitrary items from the command line +- `deramp`: (reinstated) removes all ramps designated for removal from the map +- `flows`: (reinstated) counts map blocks with flowing liquids +- `lair`: (reinstated) mark the map as a monster lair (this avoids item scatter when the fortress is abandoned) +- `luasocket`: (reinstated) provides a Lua API for accessing network sockets +- `work-now`: (reinstated, renamed from ``workNow``) prevent dwarves from wandering aimlessly with "No job" after completing a task ## Fixes - DFHack screen backgrounds now use appropriate tiles in DF Classic @@ -51,24 +69,19 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `autodump`: no longer checks for a keyboard cursor before executing, so ``autodump destroy`` (which doesn't require a cursor) can still function -- Settings: recover gracefully when settings files become corrupted (e.g. by CTD) -- `orders`: update orders in orders library for prepared meals, bins, archer uniforms, and weapons -- Terminal console no longer appears in front of the game window on startup +- Settings: recover gracefully when settings files become corrupted (e.g. by DF CTD) +- `orders`: update orders in library for prepared meals, bins, archer uniforms, and weapons - `gui/control-panel`: new preference for whether filters in lists search for substrings in the middle of words (e.g. if set to true, then "ee" will match "steel") - `gui/design`: Improved performance for drawing shapes - Dreamfort: improve traffic patterns throughout the fortress - `gui/blueprint`: recording of stockpile layouts and categories is now supported. note that detailed stockpile configurations will *not* be saved (yet) -- Core: For debugging purposes, you can now pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. -- `overlay`: added links to the quickstart guide and the control panel on the DF title screen +- Core: new commandline flag/environment var: pass ``--disable-dfhack`` on the Dwarf Fortress commandline or specify ``DFHACK_DISABLE=1`` in the environment to disable DFHack for the current session. +- `overlay`: add links to the quickstart guide and the control panel on the DF title screen - Window behavior: non-resizable windows now allow dragging by their frame edges by default -- `gui/autodump`: fort-mode keybinding: Ctrl-H +- `gui/autodump`: fort-mode keybinding: Ctrl-H (when ``armok`` tools are enabled in `gui/control-panel`) - Window behavior: if you have multiple DFHack tool windows open, scrolling the mouse wheel while over an unfocused window will focus it and raise it to the top - `stockpiles`: allow filtering creatures by tameability -## Documentation - -## API - ## Internals - ``dfhack.internal``: added memory analysis functions: ``msizeAddress``, ``getHeapState``, ``heapTakeSnapshot``, ``isAddressInHeap``, ``isAddressActiveInHeap``, ``isAddressUsedAfterFreeInHeap``, ``getAddressSizeInHeap``, and ``getRootAddressOfHeapObject`` diff --git a/library/xml b/library/xml index 17e77412c..51fe3ca11 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 17e77412c040dab658a39a34eb0e21cb014c3824 +Subproject commit 51fe3ca11f9587bc50ed09b768022cd9688250d8 diff --git a/scripts b/scripts index 6430e8ed0..315c7afc2 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 6430e8ed0facbe152e98f5ff1f274febfd6b5772 +Subproject commit 315c7afc20651ac0fe1700d720f71796190f198e From 4a2b97105c76998320f0c0f73b5d4c3d2a6839bc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 5 Jun 2023 14:18:55 -0700 Subject: [PATCH 1236/2222] only initialize steam if launched from steam --- docs/changelog.txt | 1 + library/modules/DFSteam.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bd1b97e6e..c36597047 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- Fix crash for some players when they launch DF outside of the Steam client ## Misc Improvements diff --git a/library/modules/DFSteam.cpp b/library/modules/DFSteam.cpp index 289c22e27..1fd064d56 100644 --- a/library/modules/DFSteam.cpp +++ b/library/modules/DFSteam.cpp @@ -52,6 +52,12 @@ static void bind_all(color_ostream& out, DFLibrary* handle) { } bool DFSteam::init(color_ostream& out) { + char *steam_client_launch = getenv("SteamClientLaunch"); + if (!steam_client_launch || strncmp(steam_client_launch, "1", 2) != 0) { + DEBUG(dfsteam, out).print("not launched from Steam client; not initializing steam\n"); + return false; + } + for (auto& lib_str : STEAM_LIBS) { if ((g_steam_handle = OpenPlugin(lib_str.c_str()))) break; @@ -168,11 +174,6 @@ void DFSteam::launchSteamDFHackIfNecessary(color_ostream& out) { return; } - if (strncmp(getenv("SteamClientLaunch"), "1", 2)) { - DEBUG(dfsteam, out).print("not launched from Steam client\n"); - return; - } - void* iSteamApps = g_SteamInternal_FindOrCreateUserInterface(g_SteamAPI_GetHSteamUser(), "STEAMAPPS_INTERFACE_VERSION008"); if (!iSteamApps) { DEBUG(dfsteam, out).print("cannot obtain iSteamApps interface\n"); From d8c1e64c5c7684f5508ca1e5b505d833c4f65709 Mon Sep 17 00:00:00 2001 From: UnFaventia <135673143+UnFaventia@users.noreply.github.com> Date: Tue, 6 Jun 2023 10:38:24 +1000 Subject: [PATCH 1237/2222] Update autonick.txt --- data/dfhack-config/autonick.txt | 864 ++++++++++++++++++++++++++++++++ 1 file changed, 864 insertions(+) diff --git a/data/dfhack-config/autonick.txt b/data/dfhack-config/autonick.txt index e7de79f54..2c1120888 100644 --- a/data/dfhack-config/autonick.txt +++ b/data/dfhack-config/autonick.txt @@ -24,6 +24,36 @@ Jay Crow Raven Sparrow +Platypus +House Mouse +Pangolin +Funnel Web +Weasel +Meerkats +Rat +Ant +Gopher +Fennec +Groundhog +Aardvark +Rabbit +Olm +Chipmunk +Bilbie +Vole +Nile Croc +Wombat +Worm +Gerbil +Armodillo +Thrinaxodon +Binky +Bandicoot +Bettongs +Potoroos +Antechinus +Jerboas +Numbat # colours Flash @@ -35,11 +65,435 @@ Indigo Jade Umber Silver +Bistre +Black +Black bean +Noir +Charcoal +Ebony +Eerie +Jet +Licorice +Midnight +Night +Onyx +Space +Raisin +Rich +Russ violet +Smoky +Taupe +Aero +Alice +Argent Lue +Azure +Azul +Baby blue +Berkeley +Bice +Bleu +Bondi +Brandeis Byzant +Cambridge +Carolina +Celestial +Celtic +Cerulean +Chefchaouen +Chrysler +Cobalt +Columbia +Cornflower +Sky +Delft +Denim +Dodger +Duke +Federal +Glaucous +Electrindigo +Eclipse +Ice +Illini +Klein +Jordy +Lapis Lazuli +Majorelle +Marian +Maya +Slate +Munsell +Navy +Neon blue +Oxford +Palatinate +Penn +Periwinkle +Phthalo +Picton +Poly +Powder +Prussia +Royal +Ruddy +Sapphire +Honolulu +Savoy +Silver Lake +Space cadet +Steel +Tang +Tufts +Ultramarine +Uranian +Vista +Yale +Zaffre +Auburn +Almond +Beaver +Beige +Bole +Bone +Bronze +Buff +Burgundy +Sienna +Umber +Camel +Caput mortuum +Caramel +Chamoisee +Chestnut +Chocolate +Citron +Cocoa +Coffee +Copper +Cordovan +Coyote +Desert +Drab +Dun +Earth +Ecru +Fallow +Fawn +Field +Fulvous +Goldenrod +Harvest +Khaki +Kobicha +Lion +Liver +Mahogany +Maroon +Ochre +Redwood +Rufous +Russet +Rust +Sand +Satin +Sheen +Seal +Sepia +Sinopia +Tan +Taupe +Tawny +Titian +Van Dyke +Walnut +Wenge +Wheat +Aqua +Aquamarine +Capri +Caribbean +Celeste +Cyprus +Fluorescent +Jungle +Keppel +Ice +Sea +Moonstone +Myrtle +Pacific +Pine +Robin +Skobeloff +Teal +Turquoise +Verdigris +Vivid +Zomp +Platinum +Timberwolf +Ash +Rose quartz +Cinereous +Cadet +Cool +Davys +Paynes +Glaucous +Gunmetal +Feldgrau +Apple +Asparagus +Avocado +Brunswick +Cal Poly +Castleton +Celadon +Chartreuse +Moss +Pastel +Dartmouth +Emerald +Erin +Fern +Forest +Harlequin +Honeydew +Hunter +Jade +Kelly green +Lawn +Lime +Malachite +Mantis +Mindaro +Neon +Olivine +Paris +Pear +Pigment +Pine +Pistachio +Reseda +Rifle +Sage +Screamin' +Shamrock +Bud +Amaranth +Baker-Miller +Cerise +Crimson +Carmine +Magenta +Eggplant +Fandango +Finn +Fuchsia +Haze +Mulberry +Orchid +Plum +Pizzazz +Quinacridone +Raspberry +Razzle dazzle +Rose +Shocking +Telemagenta +Aerospace +Alloy +Amber +Apricot +Atomic +Tangerine +Burnt +Butterscotch +Carrot +Champagne +Chrome +Coral +Flame +Gold +Hunyadi +Melon +Peel +Papaya +Peach +Persimmon +Princeton +Pumpkin +Rust +Safety orange +Saffron +Tangelo +Tigers Eye +Titian +Xanthous +Blush +Brilliant +Brink +Carnation +Cherry +Cordovan +Cyclamen +Dogwood +Folly +Heliotrope +Hollywood +Hot +Lavender +Mimi +Misty +Mountbatten +Orchid +Phlox +Pompadour +Puce +Raspberry +Razzmatazz +Bonbon +Quartz +Taupe +Vale +Rosewood +Rosy +Salmon +Seashell +Tea +Tickle +Thulian +Ultra +Burgundy +Byzantium +Caput mortuum +Eminence +Grape +Iris +Mardi Gras +Mauve +Mauveine +Mulberry +Murrey +Palatinate +Pale +Pomp +Power +Purpureus +Tekhelet +Thistle +Tropical +Tyrian +Wisteria +Barn +Bittersweet +Shimmer +Blood +Candy apple +Cantaloupe +Cardinal +Carmine +Chili +Cosmos +Cinnabar +Claret +Coquelicot +Cordovan +Cornell +Crimson +Falu +Brick +Engine +Folly +Garnet +Imperial +Jasper +Penn +Poppy +Redwood +Rojo +Rust +Rusty +Scarlet +Syracuse +Tomato +Turkey +Vermilion +Wine +Alabaster +Antique +Cornsilk +Latte +Cream +Eggshell +Flax +Floral +Ghost +Isabelline +Ivory +Lemon +chiffon +Linen +Navajo +Nyanza +Lace +Parchment +Pearl +Seasalt +Seashell +Vanilla +Smoke +Amber +Apricot +Arylide +Aureolin +Buff +Canary +Citron +Ecru +Gamboge +Icterine +Jonquil +Maize +Mikado +Mindaro +Mustard +Selective +Stil de grain +Straw +Sunglow +Sunset +Wheat # planets Mars Jupiter Saturn +Pluto +Neptune +Europa + +# charites +Damia +Auxesia +Cleta +Phaenna +Hegemone +Peitho +Paregoros +Pasithea +Charis +Kale +Antheia +Eudaimonia +Euthymia +Eutychia +Paidia +Pandaisia +Pannychis +Aglaea +Euphrosyne +Thalia # nature Blaze @@ -150,3 +604,413 @@ Wilder Wisdom Wyatt Zephyr + +# Gemstones +Actinolite +Nephrite +Adamite +Aegirine +Afghanite +Agrellite +Algodonite +Alunite +Amblygonite +Analcime +Anatase +Andalusite +Chiastolite +Andesine +Anglesite +Anhydrite +Annabergite +Anorthite +Antigorite +Bowenite +Apatite +Apophyllite +Aragonite +Asbestos +Astrophyllite +Augelite +Austinite +Ferro +Magnes +Mangan +Tinzenite +Azurmalachite +Azurite +Baryte +Bast +Bayldonite +Benitoite +Beryl +Aquamarine +Maxixe +Emerald +Goshenite +Golden beryl +Heliodor +Morganite +Red beryl +Beryllonite +Beudantite +Bismutot +Biotit +Bole +Boracite +Bornite +Brazilianite +Brookite +Brucite +Bustam +Bytown +Calcite +Caledonite +Canasite +Cancrin +Vishnev +Carleton +Carnall +Cassiterite +Cataplei +Cavans +Celestite +Ceruleite +Cerussite +Chabaz +Chalcopyr +Chambers +Charlesite +Charoite +Childrenite +Chiolite +Chrysoberyl +Alexandrite +Cymophane +Chromite +Chrysocolla +Cinnabar +Clinochlore +Clinohumite +Clintonite +Cobaltite +Coleman +Cordierite +Iolite +Cornwallite +Corundum +Ruby +Sapphire +Padparadscha +Covell +Creedite +Crocite +Cuprite +Danburite +Datolite +Descloiz +Diamond +Bort +Ballas +Diaspore +Dickinsonite +Diopside +Dioptase +Dolomite +Dumortier +Ekanite +Elbaite +Emerald +Trapiche +Enstatite +Bronzite +Hypersthene +Eosphorite +Epidote +Piemont +Erythrite +Esperite +Ettring +Eudialyte +Faya +Feldspar +Andesine +Albite +Anorth +Anorthoc +Amazon +Celsian +Microcline +Moonstone +Adularia +Rainbow +Ortho +Unakite +Plagioclase +Labradorite +Oligoclase +Sanidine +Sunstone +Oregon Sunstone +Rainbow Lattice +Fergusonite +Ferroaxin +Fluora +Fluorapophyl +Fluorite +Forster +Friedelite +Gadolin +Gahnite +Gahnospinel +Garnet +Pyralspite +Almandine +Pyrope +Spessartine +Ugrand +Demantoid +Melanite +Topazolita +Grossular +Hessonite +Hydrogrossular +Tsavorite +Pyrope +Rhodolite +Mali garnet +Malaia +Spessar +Umbal +Gaspe +Gayluss +Gibbsite +Glaucophane +Goeth +Goosecreek +Grandidier +Gypsum +Gyro +Halite +Hambergite +Hanksite +Hardystonite +Helenite +Hematite +Herder +Hexagonite +Hibonite +Hidden +Hodgkinsonite +Holtite +Howlite +Huebnerite +Humite +Hurlbut +Ilmenite +Inderite +Jade +Jadeite +Jasper +Jeremejevite +Kainite +Kämmerer +Kaolin +Kornerup +Kurnakov +Kyanite +Langbein +Lawsonite +Lazulite +Lazurite +Legrandite +Lepidolite +Leucite +Leucophan +Linarite +Lizardite +Londonite +Ludlamite +Ludwigite +Malachite +Maria-meionite +Werner +Marcasite +Meliphanite +Mellite +Mesolite +Milar +Millerite +Mime +Monazite +Mordenite +Mottram +Muscovite +Fuchsite +Nambul +Natrolite +Nepheline +Neptunite +Nickeline +Niccolite +Nosean +Nuumm +Olivine +Opal +Fire opal +Moss opal +Painite +Papagoite +Pargas +Parisite +Pectol +Larimar +Pentland +Peridot +Periclase +Perthite +Petal +Castor +Pezzottaite +Phena +Phosgen +Phospho +Piemontite +Realgar +Rhodizite +Rhodochros +Rhodon +Richter +Riebeck +Crocidolite +Rosasite +Rutile +Samarskite +Sanidine +Sapphirine +Sarcol +Scapol +Marialite +Meionite +Scheel +Schizol +Scorod +Selenite +Sella +Senarmon +Sepio +Meerschaum +Sérandite +Seraph +Serendibite +Serpentine +Bowen +Stich +Shattuck +Shiga +Shortite +Shung +Siderite +Silliman +Simpsonite +Sinhal +Smalt +Smithsonite +Sodalite +Hackman +Sogdian +Sperry +Spessar +Sphaler +Spinel +Ceylon +Spodumene +Triphane +Spurrite +Stauro +Strontian +Titanate +Sulfur +Bustamite +Sylvite +Taaffeita +Talc +Tantalite +Tektites +Tephroite +Thomsonite +Thaumasite +Topaz +Tourmaline +Achroite +Chrome +Dravite +Elbaite +Indicol +Olenite +Paraiba +Rossman +Rubellite +Tremol +Triphyl +Triplite +Tugtup +Turquoise +Ulex +Ussing +Vanadinite +Variscite +Vesuvianite +Californite +Villiaum +Vivianite +Vlasov +Wardite +Wavell +Welogan +Whewell +Wilkeite +Willemite +Wither +Wollastone +Wulfenite +Wurtzite +Xonot +Yugawara +Zektzer +Zeolites +Analcime +Chabaz +Steller +Stilbite +Zinc +Zinnwald +Zircon +Jacinth +Zoisite +Tanzan +Thulite +Zultan +Zany +Lapis lazuli +Desert glass +Llanite +Maw sit-sit +Obsidian +Tears +Pallas +Peridot +Soapstone +Tact +Unakite +Bauxite +Concretions +Bloodstone +Heliotrope +Eilat stone +Epidos +Glimmer +Goldstone +Hawks eye +Iddings +Lampro From b4a804ca0cc70c8bbb541a58b4a87d1d98bac463 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Mon, 5 Jun 2023 17:42:35 -0700 Subject: [PATCH 1238/2222] bump to 50.08-r3 --- CMakeLists.txt | 2 +- docs/changelog.txt | 6 +++++- library/xml | 2 +- scripts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ea16ce29..1abc91861 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ endif() # set up versioning. set(DF_VERSION "50.08") -set(DFHACK_RELEASE "r2") +set(DFHACK_RELEASE "r3") set(DFHACK_PRERELEASE FALSE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/docs/changelog.txt b/docs/changelog.txt index c36597047..fbc74bad8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,7 +36,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes -- Fix crash for some players when they launch DF outside of the Steam client ## Misc Improvements @@ -50,6 +49,11 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Removed +# 50.08-r3 + +## Fixes +- Fix crash for some players when they launch DF outside of the Steam client + # 50.08-r2 ## New Plugins diff --git a/library/xml b/library/xml index 51fe3ca11..413f1c037 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 51fe3ca11f9587bc50ed09b768022cd9688250d8 +Subproject commit 413f1c0371094a7933db0cd524ff9fad64fb0fe3 diff --git a/scripts b/scripts index 315c7afc2..c356aebcd 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 315c7afc20651ac0fe1700d720f71796190f198e +Subproject commit c356aebcde32199fa7c1d32b65ed5c96d008c1f9 From c5e68f42944df6ba0ddcec97c68fc904c28c42c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 01:10:44 +0000 Subject: [PATCH 1239/2222] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/python-jsonschema/check-jsonschema: 0.22.0 → 0.23.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.22.0...0.23.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c305de56c..efa59812d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: args: ['--fix=lf'] - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.22.0 + rev: 0.23.1 hooks: - id: check-github-workflows - repo: https://github.com/Lucas-C/pre-commit-hooks From 9c19b229b4628ab5e65d8ac718310195d3335fba Mon Sep 17 00:00:00 2001 From: UnFaventia <135673143+UnFaventia@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:17:18 +1000 Subject: [PATCH 1240/2222] Update autonick.txt Fixed white spaces & name doubles --- data/dfhack-config/autonick.txt | 459 +++++++++++++++----------------- 1 file changed, 213 insertions(+), 246 deletions(-) diff --git a/data/dfhack-config/autonick.txt b/data/dfhack-config/autonick.txt index 2c1120888..4e24fd61e 100644 --- a/data/dfhack-config/autonick.txt +++ b/data/dfhack-config/autonick.txt @@ -66,314 +66,298 @@ Jade Umber Silver Bistre -Black +Black Black bean Noir Charcoal -Ebony +Ebony Eerie Jet Licorice Midnight -Night -Onyx -Space -Raisin +Night +Onyx +Space +Raisin Rich Russ violet Smoky -Taupe Aero -Alice -Argent Lue -Azure -Azul -Baby blue -Berkeley +Alice +Argent Lue +Azure +Azul +Baby blue +Berkeley Bice -Bleu -Bondi +Bleu +Bondi Brandeis Byzant -Cambridge -Carolina -Celestial -Celtic +Cambridge +Carolina +Celestial +Celtic Cerulean -Chefchaouen -Chrysler -Cobalt -Columbia -Cornflower -Sky -Delft -Denim -Dodger +Chefchaouen +Chrysler +Cobalt +Columbia +Cornflower +Sky +Delft +Denim +Dodger Duke Federal Glaucous Electrindigo Eclipse -Ice -Illini -Klein -Jordy +Illini +Klein +Jordy Lapis Lazuli Majorelle -Marian -Maya -Slate +Marian +Maya +Slate Munsell -Navy +Navy Neon blue Oxford -Palatinate -Penn +Palatinate +Penn Periwinkle -Phthalo -Picton +Phthalo +Picton Poly Powder Prussia -Royal -Ruddy -Sapphire -Honolulu -Savoy +Royal +Ruddy +Honolulu +Savoy Silver Lake Space cadet -Steel -Tang -Tufts +Steel +Tang +Tufts Ultramarine Uranian -Vista -Yale +Vista +Yale Zaffre Auburn Almond Beaver Beige -Bole -Bone +Bole +Bone Bronze -Buff -Burgundy -Sienna -Umber -Camel -Caput mortuum -Caramel +Burgundy +Sienna +Umber +Camel +Caput mortuum +Caramel Chamoisee -Chestnut -Chocolate -Citron -Cocoa -Coffee -Copper -Cordovan -Coyote -Desert +Chestnut +Chocolate +Citron +Cocoa +Coffee +Copper +Coyote +Desert Drab -Dun -Earth -Ecru -Fallow -Fawn -Field +Dun +Earth +Fallow +Fawn +Field Fulvous Goldenrod Harvest -Khaki -Kobicha -Lion -Liver -Mahogany -Maroon -Ochre +Khaki +Kobicha +Lion +Liver +Mahogany +Maroon +Ochre Redwood -Rufous -Russet -Rust -Sand -Satin -Sheen -Seal -Sepia +Rufous +Russet +Rust +Sand +Satin +Sheen +Seal +Sepia Sinopia -Tan -Taupe -Tawny -Titian +Tan +Taupe +Tawny +Titian Van Dyke -Walnut -Wenge -Wheat -Aqua -Aquamarine -Capri +Walnut +Wenge +Aqua +Aquamarine +Capri Caribbean -Celeste +Celeste Cyprus -Fluorescent -Jungle +Fluorescent +Jungle Keppel -Ice +Ice Sea -Moonstone -Myrtle +Myrtle Pacific -Pine -Robin +Pine +Robin Skobeloff -Teal -Turquoise -Verdigris -Vivid +Teal +Turquoise +Verdigris +Vivid Zomp Platinum Timberwolf -Ash -Rose quartz +Ash +Rose quartz Cinereous -Cadet +Cadet Cool Davys Paynes -Glaucous -Gunmetal +Glaucous +Gunmetal Feldgrau -Apple -Asparagus -Avocado -Brunswick -Cal Poly -Castleton +Apple +Asparagus +Avocado +Brunswick +Cal Poly +Castleton Celadon -Chartreuse -Moss -Pastel -Dartmouth +Chartreuse +Moss +Pastel +Dartmouth Emerald -Erin +Erin Fern Forest -Harlequin +Harlequin Honeydew Hunter -Jade -Kelly green -Lawn -Lime +Kelly green +Lawn +Lime Malachite Mantis Mindaro -Neon +Neon Olivine -Paris +Paris Pear -Pigment -Pine +Pigment Pistachio Reseda -Rifle +Rifle Sage Screamin' Shamrock Bud Amaranth -Baker-Miller +Baker-Miller Cerise -Crimson Carmine Magenta Eggplant -Fandango -Finn +Fandango +Finn Fuchsia -Haze -Mulberry -Orchid -Plum +Haze +Orchid +Plum Pizzazz Quinacridone -Raspberry -Razzle dazzle +Razzle dazzle Rose -Shocking +Shocking Telemagenta Aerospace -Alloy -Amber +Alloy +Amber Apricot -Atomic +Atomic Tangerine -Burnt +Burnt Butterscotch -Carrot +Carrot Champagne -Chrome -Coral -Flame -Gold -Hunyadi +Chrome +Coral +Flame +Gold +Hunyadi Melon Peel Papaya -Peach -Persimmon +Peach +Persimmon Princeton Pumpkin Rust Safety orange Saffron Tangelo -Tigers Eye +Tigers Eye Titian Xanthous -Blush +Blush Brilliant -Brink -Carnation -Cherry +Brink +Carnation +Cherry Cordovan Cyclamen Dogwood -Folly -Heliotrope -Hollywood -Hot +Heliotrope +Hollywood +Hot Lavender Mimi Misty -Mountbatten -Orchid -Phlox +Mountbatten +Orchid +Phlox Pompadour -Puce +Puce Raspberry Razzmatazz Bonbon Quartz -Taupe -Vale +Taupe +Vale Rosewood Rosy Salmon -Seashell -Tea +Tea Tickle Thulian Ultra Burgundy Byzantium -Caput mortuum Eminence -Grape +Grape Iris Mardi Gras -Mauve -Mauveine -Mulberry +Mauve +Mauveine +Mulberry Murrey Palatinate Pale @@ -383,16 +367,15 @@ Purpureus Tekhelet Thistle Tropical -Tyrian +Tyrian Wisteria Barn -Bittersweet +Bittersweet Shimmer Blood -Candy apple +Candy apple Cantaloupe Cardinal -Carmine Chili Cosmos Cinnabar @@ -402,65 +385,61 @@ Cordovan Cornell Crimson Falu -Brick +Brick Engine -Folly -Garnet +Folly Imperial -Jasper +Jaspar Penn Poppy -Redwood -Rojo -Rust -Rusty +Rojo +Rusty Scarlet -Syracuse +Syracuse Tomato -Turkey -Vermilion +Turkey +Vermilion Wine Alabaster -Antique +Antique Cornsilk -Latte +Latte Cream Eggshell -Flax +Flax Floral Ghost Isabelline Ivory -Lemon +Lemon chiffon -Linen -Navajo +Linen +Navajo Nyanza Lace Parchment -Pearl +Pearl Seasalt Seashell Vanilla Smoke -Amber -Apricot -Arylide +Apricot +Arylide Aureolin -Buff +Buff Canary Citron -Ecru +Ecru Gamboge Icterine Jonquil -Maize -Mikado -Mindaro +Maize +Mikado +Mindaro Mustard Selective -Stil de grain -Straw +Stil de grain +Straw Sunglow Sunset Wheat @@ -475,22 +454,22 @@ Europa # charites Damia -Auxesia -Cleta -Phaenna -Hegemone -Peitho -Paregoros -Pasithea -Charis -Kale -Antheia -Eudaimonia -Euthymia -Eutychia -Paidia -Pandaisia -Pannychis +Auxesia +Cleta +Phaenna +Hegemone +Peitho +Paregoros +Pasithea +Charis +Kale +Antheia +Eudaimonia +Euthymia +Eutychia +Paidia +Pandaisia +Pannychis Aglaea Euphrosyne Thalia @@ -619,7 +598,6 @@ Analcime Anatase Andalusite Chiastolite -Andesine Anglesite Anhydrite Annabergite @@ -677,7 +655,6 @@ Cavans Celestite Ceruleite Cerussite -Chabaz Chalcopyr Chambers Charlesite @@ -689,7 +666,6 @@ Alexandrite Cymophane Chromite Chrysocolla -Cinnabar Clinochlore Clinohumite Clintonite @@ -719,8 +695,6 @@ Dioptase Dolomite Dumortier Ekanite -Elbaite -Emerald Trapiche Enstatite Bronzite @@ -745,11 +719,10 @@ Moonstone Adularia Rainbow Ortho -Unakite +Kite Plagioclase Labradorite Oligoclase -Sanidine Sunstone Oregon Sunstone Rainbow Lattice @@ -766,7 +739,6 @@ Gahnospinel Garnet Pyralspite Almandine -Pyrope Spessartine Ugrand Demantoid @@ -780,7 +752,6 @@ Pyrope Rhodolite Mali garnet Malaia -Spessar Umbal Gaspe Gayluss @@ -832,7 +803,6 @@ Lizardite Londonite Ludlamite Ludwigite -Malachite Maria-meionite Werner Marcasite @@ -851,11 +821,10 @@ Nambul Natrolite Nepheline Neptunite -Nickeline +Nickeline Niccolite Nosean Nuumm -Olivine Opal Fire opal Moss opal @@ -866,10 +835,9 @@ Parisite Pectol Larimar Pentland -Peridot Periclase Perthite -Petal +Petal Castor Pezzottaite Phena @@ -898,7 +866,7 @@ Scorod Selenite Sella Senarmon -Sepio +Sepio Meerschaum Sérandite Seraph @@ -979,7 +947,6 @@ Xonot Yugawara Zektzer Zeolites -Analcime Chabaz Steller Stilbite From 0f224e60da2123ce45c37d4980f2e17f485a782b Mon Sep 17 00:00:00 2001 From: UnFaventia <135673143+UnFaventia@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:21:44 +1000 Subject: [PATCH 1241/2222] Update changelog.txt --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index c36597047..ebefbdd4e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -82,6 +82,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/autodump`: fort-mode keybinding: Ctrl-H (when ``armok`` tools are enabled in `gui/control-panel`) - Window behavior: if you have multiple DFHack tool windows open, scrolling the mouse wheel while over an unfocused window will focus it and raise it to the top - `stockpiles`: allow filtering creatures by tameability +- Autonick: additional nicknames based on borrowwing animals, colours, gems and minerals added ## Internals - ``dfhack.internal``: added memory analysis functions: ``msizeAddress``, ``getHeapState``, ``heapTakeSnapshot``, ``isAddressInHeap``, ``isAddressActiveInHeap``, ``isAddressUsedAfterFreeInHeap``, ``getAddressSizeInHeap``, and ``getRootAddressOfHeapObject`` From d9dab857c6756197e67523a5e746f2fc7b4092ee Mon Sep 17 00:00:00 2001 From: UnFaventia <135673143+UnFaventia@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:09:53 +1000 Subject: [PATCH 1242/2222] Update autonick.txt --- data/dfhack-config/autonick.txt | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/data/dfhack-config/autonick.txt b/data/dfhack-config/autonick.txt index 4e24fd61e..12fbe65e6 100644 --- a/data/dfhack-config/autonick.txt +++ b/data/dfhack-config/autonick.txt @@ -63,7 +63,6 @@ Blue Shadow Indigo Jade -Umber Silver Bistre Black @@ -103,13 +102,11 @@ Chrysler Cobalt Columbia Cornflower -Sky Delft Denim Dodger Duke Federal -Glaucous Electrindigo Eclipse Illini @@ -153,7 +150,6 @@ Beige Bole Bone Bronze -Burgundy Sienna Umber Camel @@ -195,9 +191,7 @@ Seal Sepia Sinopia Tan -Taupe Tawny -Titian Van Dyke Walnut Wenge @@ -214,17 +208,14 @@ Ice Sea Myrtle Pacific -Pine Robin Skobeloff Teal -Turquoise Verdigris Vivid Zomp Platinum Timberwolf -Ash Rose quartz Cinereous Cadet @@ -234,7 +225,6 @@ Paynes Glaucous Gunmetal Feldgrau -Apple Asparagus Avocado Brunswick @@ -246,7 +236,6 @@ Moss Pastel Dartmouth Emerald -Erin Fern Forest Harlequin @@ -254,10 +243,8 @@ Honeydew Hunter Kelly green Lawn -Lime Malachite Mantis -Mindaro Neon Olivine Paris @@ -280,7 +267,6 @@ Fandango Finn Fuchsia Haze -Orchid Plum Pizzazz Quinacridone @@ -291,14 +277,12 @@ Telemagenta Aerospace Alloy Amber -Apricot Atomic Tangerine Burnt Butterscotch Carrot Champagne -Chrome Coral Flame Gold @@ -310,7 +294,6 @@ Peach Persimmon Princeton Pumpkin -Rust Safety orange Saffron Tangelo @@ -322,10 +305,8 @@ Brilliant Brink Carnation Cherry -Cordovan Cyclamen Dogwood -Heliotrope Hollywood Hot Lavender @@ -359,7 +340,6 @@ Mauve Mauveine Mulberry Murrey -Palatinate Pale Pomp Power @@ -390,7 +370,6 @@ Engine Folly Imperial Jaspar -Penn Poppy Rojo Rusty @@ -428,7 +407,6 @@ Arylide Aureolin Buff Canary -Citron Ecru Gamboge Icterine @@ -622,9 +600,7 @@ Bast Bayldonite Benitoite Beryl -Aquamarine Maxixe -Emerald Goshenite Golden beryl Heliodor @@ -634,7 +610,6 @@ Beryllonite Beudantite Bismutot Biotit -Bole Boracite Bornite Brazilianite @@ -780,7 +755,6 @@ Humite Hurlbut Ilmenite Inderite -Jade Jadeite Jasper Jeremejevite From 2de77d3f57615d50c205ee99b72bf602626f8088 Mon Sep 17 00:00:00 2001 From: UnFaventia <135673143+UnFaventia@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:11:10 +1000 Subject: [PATCH 1243/2222] Update changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ebefbdd4e..c724c9235 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -39,6 +39,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - Fix crash for some players when they launch DF outside of the Steam client ## Misc Improvements +- `Autonick`: additional nicknames based on burrowing animals, colours, gems and minerals added ## Documentation @@ -82,7 +83,6 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: - `gui/autodump`: fort-mode keybinding: Ctrl-H (when ``armok`` tools are enabled in `gui/control-panel`) - Window behavior: if you have multiple DFHack tool windows open, scrolling the mouse wheel while over an unfocused window will focus it and raise it to the top - `stockpiles`: allow filtering creatures by tameability -- Autonick: additional nicknames based on borrowwing animals, colours, gems and minerals added ## Internals - ``dfhack.internal``: added memory analysis functions: ``msizeAddress``, ``getHeapState``, ``heapTakeSnapshot``, ``isAddressInHeap``, ``isAddressActiveInHeap``, ``isAddressUsedAfterFreeInHeap``, ``getAddressSizeInHeap``, and ``getRootAddressOfHeapObject`` From e50fe7eb995832ff44097e615c2432dd304b04af Mon Sep 17 00:00:00 2001 From: UnFaventia <135673143+UnFaventia@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:17:53 +1000 Subject: [PATCH 1244/2222] Update changelog.txt --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 580bdc2bd..8ee4abf2c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,7 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements -- `Autonick`: additional nicknames based on burrowing animals, colours, gems and minerals added +- `autonick`: additional nicknames based on burrowing animals, colours, gems and minerals added ## Documentation From efc4f277d93b323334a3015b758b16a058612be6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 6 Jun 2023 08:06:56 -0700 Subject: [PATCH 1245/2222] clear item occupancy flags for channeled tiles --- docs/changelog.txt | 1 + plugins/dig-now.cpp | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 8ee4abf2c..b010f55b5 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -36,6 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## New Plugins ## Fixes +- `dig-now`: clear item occupancy flags for channeled tiles that had items on them ## Misc Improvements - `autonick`: additional nicknames based on burrowing animals, colours, gems and minerals added diff --git a/plugins/dig-now.cpp b/plugins/dig-now.cpp index b2af2dbca..94d8c761f 100644 --- a/plugins/dig-now.cpp +++ b/plugins/dig-now.cpp @@ -968,10 +968,20 @@ static void post_process_dug_tiles(color_ostream &out, } if (to.bits.item) { - for (auto item : world->items.other.IN_PLAY) { - if (item->pos == pos && item->flags.bits.on_ground) - item->moveToGround( - resting_pos.x, resting_pos.y, resting_pos.z); + std::vector items; + if (auto b = Maps::ensureTileBlock(pos)) { + for (auto item_id : b->items) { + auto item = df::item::find(item_id); + if (item && item->pos == pos) + items.emplace_back(item); + } + } + if (!items.empty()) { + // fresh MapCache since tile properties are being actively changed + MapExtras::MapCache mc; + for (auto item : items) + Items::moveToGround(mc, item, resting_pos); + mc.WriteAll(); } } } From abc1a6a56965794acff928c223d3ad7ef84fafab Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 23 May 2023 17:03:36 -0700 Subject: [PATCH 1246/2222] clean up some cmake --- CMakeLists.txt | 127 +++++++++++++++++++------------------------------ 1 file changed, 48 insertions(+), 79 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1abc91861..56cfa0b95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,21 +1,19 @@ # main project file. use it from a build sub-folder, see COMPILE for details ## some generic CMake magic -cmake_minimum_required(VERSION 3.6 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) cmake_policy(SET CMP0048 NEW) +cmake_policy(SET CMP0074 NEW) project(dfhack) -if("${CMAKE_GENERATOR}" STREQUAL Ninja) - if("${CMAKE_VERSION}" VERSION_LESS 3.9) - message(WARNING "You are using an old version of CMake (${CMAKE_VERSION}) with Ninja. This may result in ninja errors - see docs/Compile.rst for more details. Upgrading your CMake version is recommended.") - endif() -endif() +# set up versioning. +set(DF_VERSION "50.08") +set(DFHACK_RELEASE "r2rc1") +set(DFHACK_PRERELEASE TRUE) -if(NOT("${CMAKE_VERSION}" VERSION_LESS 3.12)) - # make ZLIB_ROOT work in CMake >= 3.12 - # https://cmake.org/cmake/help/git-stage/policy/CMP0074.html - cmake_policy(SET CMP0074 NEW) -endif() +set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") +set(DFHACK_ABI_VERSION 1) +set(DFHACK_BUILD_ID "" CACHE STRING "Build ID (should be specified on command line)") # Set up build types if(CMAKE_CONFIGURATION_TYPES) @@ -123,7 +121,7 @@ elseif("${DFHACK_BUILD_ARCH}" STREQUAL "64") set(DFHACK_SETARCH "x86_64") add_definitions(-DDFHACK64) else() - message(SEND_ERROR "Invalid build architecture (should be 32/64): ${DFHACK_BUILD_ARCH}") + message(SEND_ERROR "Invalid build architecture (should be 32 or 64): ${DFHACK_BUILD_ARCH}") endif() if(CMAKE_CROSSCOMPILING) @@ -182,30 +180,24 @@ endif() if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhack_SOURCE_DIR}/scripts/CMakeLists.txt OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/clsocket/CMakeLists.txt + OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/jsoncpp-sub/CMakeLists.txt OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/libexpat/expat/CMakeLists.txt OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/libzip/CMakeLists.txt OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/xlsxio/CMakeLists.txt + OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/googletest/CMakeLists.txt OR NOT EXISTS ${dfhack_SOURCE_DIR}/depends/luacov/src ) - message(SEND_ERROR "One or more required submodules could not be found! Run 'git submodule update --init' from the root DFHack directory. (See the section 'Getting the Code' in docs/Compile.rst)") + message(SEND_ERROR "One or more required submodules could not be found! Run 'git submodule update --init' from the root DFHack directory. (See the section 'Getting the Code' in docs/dev/compile/Compile.rst)") endif() -# set up versioning. -set(DF_VERSION "50.08") -set(DFHACK_RELEASE "r3") -set(DFHACK_PRERELEASE FALSE) - -set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") - -set(DFHACK_ABI_VERSION 1) - -set(DFHACK_BUILD_ID "" CACHE STRING "Build ID (should be specified on command line)") +# dfhack data goes here: +set(DFHACK_DATA_DESTINATION hack) ## where to install things (after the build is done, classic 'make install' or package structure) # the dfhack libraries will be installed here: if(UNIX) # put the lib into DF/hack - set(DFHACK_LIBRARY_DESTINATION hack) + set(DFHACK_LIBRARY_DESTINATION ${DFHACK_DATA_DESTINATION}) else() # windows is crap, therefore we can't do nice things with it. leave the libs on a nasty pile... set(DFHACK_LIBRARY_DESTINATION .) @@ -213,31 +205,26 @@ endif() # external tools will be installed here: set(DFHACK_BINARY_DESTINATION .) -# dfhack data goes here: -set(DFHACK_DATA_DESTINATION hack) # plugin libs go here: -set(DFHACK_PLUGIN_DESTINATION hack/plugins) -# dfhack header files go here: -set(DFHACK_INCLUDES_DESTINATION hack/include) +set(DFHACK_PLUGIN_DESTINATION ${DFHACK_DATA_DESTINATION}/plugins) # dfhack lua files go here: -set(DFHACK_LUA_DESTINATION hack/lua) +set(DFHACK_LUA_DESTINATION ${DFHACK_DATA_DESTINATION}/lua) # the windows .lib file goes here: -set(DFHACK_DEVLIB_DESTINATION hack) +set(DFHACK_DEVLIB_DESTINATION ${DFHACK_DATA_DESTINATION}) # user documentation goes here: -set(DFHACK_USERDOC_DESTINATION hack) +set(DFHACK_USERDOC_DESTINATION ${DFHACK_DATA_DESTINATION}) # developer documentation goes here: -set(DFHACK_DEVDOC_DESTINATION hack) +set(DFHACK_DEVDOC_DESTINATION ${DFHACK_DATA_DESTINATION}) # some options for the user/developer to play with -option(BUILD_LIBRARY "Build the library that goes into DF." ON) -option(BUILD_PLUGINS "Build the plugins." ON) +option(BUILD_LIBRARY "Build the DFHack library." ON) +option(BUILD_PLUGINS "Build the DFHack plugins." ON) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) if(UNIX) ## flags for GCC # default to hidden symbols - # build 32bit # ensure compatibility with older CPUs # enable C++11 features add_definitions(-DLINUX_BUILD) @@ -254,7 +241,7 @@ if(UNIX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -march=i686") endif() string(REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - set(CMAKE_INSTALL_RPATH "hack") + set(CMAKE_INSTALL_RPATH ${DFHACK_LIBRARY_DESTINATION}) elseif(MSVC) # for msvc, tell it to always use 8-byte pointers to member functions to avoid confusion set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /vmg /vmm /MP") @@ -276,30 +263,37 @@ elseif(WIN32) add_definitions(-DWIN32) endif() -#### download depends #### +#### dependencies #### + +# fix for pyenv: default to `python3` before `python3.x` +set(Python_FIND_UNVERSIONED_NAMES FIRST) include(CMake/DownloadFile.cmake) if(WIN32) - # Download zlib on Windows - set(ZLIB_DOWNLOAD_DIR ${dfhack_SOURCE_DIR}/depends/zlib/lib/win${DFHACK_BUILD_ARCH}) - if(${DFHACK_BUILD_ARCH} STREQUAL "64") - download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-zlib.lib" - ${ZLIB_DOWNLOAD_DIR}/zlib.lib - "a3b2fc6b68efafa89b0882e354fc8418") - else() - download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-zlib.lib" - ${ZLIB_DOWNLOAD_DIR}/zlib.lib - "f4ebaa21d9de28566e88b1edfcdff901") + set(ZLIB_FILE win64-zlib.lib) + set(ZLIB_PATH ${CMAKE_BINARY_DIR}/depends/zlib/) + set(ZLIB_MD5 a3b2fc6b68efafa89b0882e354fc8418) + file(COPY ${dfhack_SOURCE_DIR}/depends/zlib/ DESTINATION ${ZLIB_PATH}) + download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/${ZLIB_FILE}" + ${ZLIB_PATH}${ZLIB_FILE} + ${ZLIB_MD5}) + set(ZLIB_ROOT ${ZLIB_PATH}) +else() + # Rescan for pthread and zlib if the build arch changed + if(NOT "${DFHACK_BUILD_ARCH}" STREQUAL "${DFHACK_BUILD_ARCH_PREV}") + unset(ZLIB_LIBRARY CACHE) + unset(CMAKE_HAVE_PTHREAD_H CACHE) endif() - # Move zlib to the build folder so possible 32 and 64-bit builds - # in the same source tree don't conflict - file(COPY ${dfhack_SOURCE_DIR}/depends/zlib - DESTINATION ${CMAKE_BINARY_DIR}/depends/) - file(COPY ${ZLIB_DOWNLOAD_DIR}/zlib.lib - DESTINATION ${CMAKE_BINARY_DIR}/depends/zlib/lib/) + if(NOT APPLE AND DFHACK_BUILD_32) + set(ZLIB_ROOT /usr/lib/i386-linux-gnu) + endif() +endif() +find_package(ZLIB REQUIRED) +include_directories(${ZLIB_INCLUDE_DIRS}) +if(WIN32) # Do the same for SDL.dll # (DFHack doesn't require this at build time, so no need to move it to the build folder) # TODO: remove SDL.dll from our distribution once DF moves to SDL2. we only @@ -315,7 +309,6 @@ if(WIN32) ${SDL_DOWNLOAD_DIR}/SDL.dll "5a09604daca6b2b5ce049d79af935d6a") endif() - endif() if(APPLE) @@ -376,29 +369,6 @@ endif() #### expose depends #### -# fix for pyenv: default to `python3` before `python3.x` -set(Python_FIND_UNVERSIONED_NAMES FIRST) - -if(UNIX) - # Rescan for pthread and zlib if the build arch changed - if(NOT "${DFHACK_BUILD_ARCH}" STREQUAL "${DFHACK_BUILD_ARCH_PREV}") - unset(ZLIB_LIBRARY CACHE) - unset(CMAKE_HAVE_PTHREAD_H CACHE) - endif() -endif() - -# find and make available libz -if(NOT UNIX) # Windows - # zlib is in here so 32-bit and 64-bit builds in the same source tree are possible - set(ZLIB_ROOT ${CMAKE_BINARY_DIR}/depends/zlib/) -else() - if(NOT APPLE AND DFHACK_BUILD_32) - # 32-bit Linux - set(ZLIB_ROOT /usr/lib/i386-linux-gnu) - endif() -endif() - -find_package(ZLIB REQUIRED) include_directories(depends/protobuf) include_directories(depends/lua/include) include_directories(depends/md5) @@ -420,7 +390,6 @@ endif() include_directories(depends/lodepng) include_directories(depends/tthread) -include_directories(${ZLIB_INCLUDE_DIRS}) include_directories(depends/clsocket/src) include_directories(depends/xlsxio/include) add_subdirectory(depends) From aa3ca94de45328fb52fd00d2cc4d698e540c37cc Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 23 May 2023 17:15:47 -0700 Subject: [PATCH 1247/2222] fix paths --- CMakeLists.txt | 6 +++--- depends/zlib/lib/win32/.gitignore | 1 - depends/zlib/lib/win64/.gitignore | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 depends/zlib/lib/win32/.gitignore delete mode 100644 depends/zlib/lib/win64/.gitignore diff --git a/CMakeLists.txt b/CMakeLists.txt index 56cfa0b95..50ac8f183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,12 +271,12 @@ set(Python_FIND_UNVERSIONED_NAMES FIRST) include(CMake/DownloadFile.cmake) if(WIN32) - set(ZLIB_FILE win64-zlib.lib) + set(ZLIB_FILE zlib.lib) set(ZLIB_PATH ${CMAKE_BINARY_DIR}/depends/zlib/) set(ZLIB_MD5 a3b2fc6b68efafa89b0882e354fc8418) file(COPY ${dfhack_SOURCE_DIR}/depends/zlib/ DESTINATION ${ZLIB_PATH}) - download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/${ZLIB_FILE}" - ${ZLIB_PATH}${ZLIB_FILE} + download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-${ZLIB_FILE}" + ${ZLIB_PATH}lib/${ZLIB_FILE} ${ZLIB_MD5}) set(ZLIB_ROOT ${ZLIB_PATH}) else() diff --git a/depends/zlib/lib/win32/.gitignore b/depends/zlib/lib/win32/.gitignore deleted file mode 100644 index 683bf139f..000000000 --- a/depends/zlib/lib/win32/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.lib diff --git a/depends/zlib/lib/win64/.gitignore b/depends/zlib/lib/win64/.gitignore deleted file mode 100644 index 683bf139f..000000000 --- a/depends/zlib/lib/win64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.lib From 9aecedb9e2ac5c90faab8a3623f3b25f224dce21 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 24 May 2023 03:04:33 -0700 Subject: [PATCH 1248/2222] move zlib download back to src dir --- CMakeLists.txt | 5 ++--- depends/zlib/lib/win64/.gitignore | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 depends/zlib/lib/win64/.gitignore diff --git a/CMakeLists.txt b/CMakeLists.txt index 50ac8f183..dfc8efb4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,11 +272,10 @@ include(CMake/DownloadFile.cmake) if(WIN32) set(ZLIB_FILE zlib.lib) - set(ZLIB_PATH ${CMAKE_BINARY_DIR}/depends/zlib/) + set(ZLIB_PATH ${dfhack_SOURCE_DIR}/depends/zlib/) set(ZLIB_MD5 a3b2fc6b68efafa89b0882e354fc8418) - file(COPY ${dfhack_SOURCE_DIR}/depends/zlib/ DESTINATION ${ZLIB_PATH}) download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-${ZLIB_FILE}" - ${ZLIB_PATH}lib/${ZLIB_FILE} + ${ZLIB_PATH}lib/win64/${ZLIB_FILE} ${ZLIB_MD5}) set(ZLIB_ROOT ${ZLIB_PATH}) else() diff --git a/depends/zlib/lib/win64/.gitignore b/depends/zlib/lib/win64/.gitignore new file mode 100644 index 000000000..683bf139f --- /dev/null +++ b/depends/zlib/lib/win64/.gitignore @@ -0,0 +1 @@ +*.lib From 394db656e6877d8c389b1f77acc8512b8236a2ed Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 24 May 2023 03:04:43 -0700 Subject: [PATCH 1249/2222] remove obsolete BUILD_DEVEL option and logic --- library/CMakeLists.txt | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 371d3cd14..8681c9c90 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -5,7 +5,6 @@ cmake_minimum_required(VERSION 3.21) cmake_policy(SET CMP0022 NEW) # build options -option(BUILD_DEVEL "Install/package files required for development(For SDK)." OFF) if(UNIX) option(CONSOLE_NO_CATCH "Make the console not catch 'CTRL+C' events for easier debugging." OFF) endif() @@ -459,17 +458,4 @@ install(DIRECTORY ${dfhack_SOURCE_DIR}/patches DESTINATION ${DFHACK_DATA_DESTINATION} FILES_MATCHING PATTERN "*.dif") -# Unused for so long that it's not even relevant now... -if(BUILD_DEVEL) - if(WIN32) - install(TARGETS dfhack - ARCHIVE DESTINATION ${DFHACK_DEVLIB_DESTINATION}) - endif() - # note the ending '/'. This means *contents* of the directory are installed - # without the '/', the directory itself is installed - install(DIRECTORY include/ - DESTINATION ${DFHACK_INCLUDES_DESTINATION} - FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" ) -endif() - add_subdirectory(xml) From 73f8186ab7561e20f837c999fad67b5ea0423cd6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 24 May 2023 12:15:52 -0700 Subject: [PATCH 1250/2222] use standard ROOT path structure --- CMakeLists.txt | 2 +- depends/zlib/lib/{win64 => }/.gitignore | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename depends/zlib/lib/{win64 => }/.gitignore (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index dfc8efb4e..0b4c6027f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,7 +275,7 @@ if(WIN32) set(ZLIB_PATH ${dfhack_SOURCE_DIR}/depends/zlib/) set(ZLIB_MD5 a3b2fc6b68efafa89b0882e354fc8418) download_file("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-${ZLIB_FILE}" - ${ZLIB_PATH}lib/win64/${ZLIB_FILE} + ${ZLIB_PATH}lib/${ZLIB_FILE} ${ZLIB_MD5}) set(ZLIB_ROOT ${ZLIB_PATH}) else() diff --git a/depends/zlib/lib/win64/.gitignore b/depends/zlib/lib/.gitignore similarity index 100% rename from depends/zlib/lib/win64/.gitignore rename to depends/zlib/lib/.gitignore From 130b42d041270ed4917c81ca3832f342bef54e6d Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Wed, 7 Jun 2023 07:13:20 +0000 Subject: [PATCH 1251/2222] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index c356aebcd..0119ab1b6 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit c356aebcde32199fa7c1d32b65ed5c96d008c1f9 +Subproject commit 0119ab1b6d540e192c77f114c5ea7f7aeab9b39a From 05db72c7b39d0fa45265e705bebaf8f6fc4d968d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Fri, 2 Jun 2023 13:15:21 -0700 Subject: [PATCH 1252/2222] add organic stockpile preset --- data/stockpiles/organic.dfstock | Bin 0 -> 9 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/stockpiles/organic.dfstock diff --git a/data/stockpiles/organic.dfstock b/data/stockpiles/organic.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..70f0496ca3b21fb297da244a8ac729b839d0fe58 GIT binary patch literal 9 QcmbQh$T)+MVFx1v01D>; Date: Mon, 5 Jun 2023 04:17:13 -0700 Subject: [PATCH 1253/2222] add barrels stockpile preset --- data/stockpiles/barrels.dfstock | 2 ++ docs/plugins/stockpiles.rst | 1 + 2 files changed, 3 insertions(+) create mode 100644 data/stockpiles/barrels.dfstock diff --git a/data/stockpiles/barrels.dfstock b/data/stockpiles/barrels.dfstock new file mode 100644 index 000000000..70a723903 --- /dev/null +++ b/data/stockpiles/barrels.dfstock @@ -0,0 +1,2 @@ + +BARREL \ No newline at end of file diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index ae77e5cf2..922c3a418 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -369,6 +369,7 @@ Subcategory prefixes:: Settings files:: pots + barrels bags buckets sand From 8c1e761f867f6259152cd1dbdfd3417e3544358d Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 7 Jun 2023 01:28:15 -0700 Subject: [PATCH 1254/2222] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index b010f55b5..cdfe29650 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,6 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `autonick`: additional nicknames based on burrowing animals, colours, gems and minerals added +- `stockpiles`: added ``barrels`` and ``organic`` stockpile presets ## Documentation From 321941385e94171142c24e1550bc797bafc8041e Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 7 Jun 2023 11:57:16 -0700 Subject: [PATCH 1255/2222] add artifacts and masterworks presets --- data/stockpiles/artifacts.dfstock | Bin 0 -> 107 bytes data/stockpiles/masterworks.dfstock | Bin 0 -> 112 bytes docs/changelog.txt | 2 +- docs/plugins/stockpiles.rst | 15 +++++++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 data/stockpiles/artifacts.dfstock create mode 100644 data/stockpiles/masterworks.dfstock diff --git a/data/stockpiles/artifacts.dfstock b/data/stockpiles/artifacts.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..1c7315dfe83ce1f673768a743418c10baa1124fa GIT binary patch literal 107 zcmd;L;b0J85MrLe$RNd~#Nk*}l9`s6T%yGiz!1S;#NdP^=EV@i5XF#$B$CBY#8Ac1 e#K@-w*J#1uz|h6W=LHvtU`Sv%#mI1pkpTehwHf6A literal 0 HcmV?d00001 diff --git a/data/stockpiles/masterworks.dfstock b/data/stockpiles/masterworks.dfstock new file mode 100644 index 0000000000000000000000000000000000000000..fa94c86a936d8852d061758b28f67c069c8d48c5 GIT binary patch literal 112 zcmd;L;b0J85MrLe$RNe7#Oa$@T#{OpR+^*562K6_V8q~rEa=4$#1O@hge;WBP{dHh f(8S2Eh0trk;K0zu$nS*^iC{=zIK{|tiID*Sw*(xB literal 0 HcmV?d00001 diff --git a/docs/changelog.txt b/docs/changelog.txt index cdfe29650..b15f5565e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -40,7 +40,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `autonick`: additional nicknames based on burrowing animals, colours, gems and minerals added -- `stockpiles`: added ``barrels`` and ``organic`` stockpile presets +- `stockpiles`: added ``barrels``, ``organic``, ``artifacts``, and ``masterworks`` stockpile presets ## Documentation diff --git a/docs/plugins/stockpiles.rst b/docs/plugins/stockpiles.rst index 922c3a418..cc1f3c016 100644 --- a/docs/plugins/stockpiles.rst +++ b/docs/plugins/stockpiles.rst @@ -143,6 +143,21 @@ item properties that you can match with filters. In addition, there are normally at least a few convenient pre-made settings files that manipulate interesting category subsets. +Cross-category stockpile adjustments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Settings files:: + + artifacts + masterworks + +Example command for a meltable weapons stockpile:: + + stockpiles import cat_weapons + stockpiles import -m disable cat_weapons -f other/ + stockpiles import -m disable artifacts + stockpiles import -m disable masterworks + Ammo stockpile adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 873000df806d806dfa8d7e992818e97e2bffe96c Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Thu, 8 Jun 2023 07:13:15 +0000 Subject: [PATCH 1256/2222] Auto-update submodules library/xml: master scripts: master --- library/xml | 2 +- scripts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index 413f1c037..1f22dd8b8 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 413f1c0371094a7933db0cd524ff9fad64fb0fe3 +Subproject commit 1f22dd8b8aa767609ea13bf1d2da8907001e0ce2 diff --git a/scripts b/scripts index 0119ab1b6..13a822532 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 0119ab1b6d540e192c77f114c5ea7f7aeab9b39a +Subproject commit 13a82253226854a5da8d01e6deeb5cc6192c770f From c98525d6683869fc72a5cbf27ea659a9fb4934b1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 7 Jun 2023 03:40:34 -0700 Subject: [PATCH 1257/2222] update dreamfort to new quickfort syntax and capabilities --- data/blueprints/dreamfort.csv | 2675 ++++++++++++++------------------- 1 file changed, 1170 insertions(+), 1505 deletions(-) diff --git a/data/blueprints/dreamfort.csv b/data/blueprints/dreamfort.csv index 66ce693b8..e93d082e3 100644 --- a/data/blueprints/dreamfort.csv +++ b/data/blueprints/dreamfort.csv @@ -1,16 +1,17 @@ #notes label(help) run me for the dreamfort walkthrough "Welcome to Dreamfort! These blueprints will help you build a functional, secure, fully self-sustaining fortress that you can use as-is or extend to build the fortress of your dreams!" "" -"It can be difficult to apply a set of blueprints that you did not write yourself. This walkthrough will guide you through the high-level steps of building Dreamfort. Run ""quickfort run library/dreamfort.csv -n /checklist"" (or, if you're looking at the online version, switch to the ""checklist"" sheet) for a compact list of the commands you'll be running. Each level also has its own mini-walkthrough with more details." +"It can be difficult to apply a set of blueprints that you did not write yourself. This walkthrough will guide you through the high-level steps of building Dreamfort. Run ""quickfort run library/dreamfort.csv -n /checklist"" (or, if you're looking at the online version, switch to the ""checklist"" sheet) for a compact list of the blueprints you'll be applying. Each level also has its own mini-walkthrough with more details." "" "The final fort will have a walled-in area on the surface for livestock, trading, aboveground farming, and military training. One z-level down is the farming level, with related workshops and vents up to the surface for miasma prevention. The farming level also has a miniature dining hall and dormitory for use until you get the services and housing levels set up." "" "Beyond those two, the other layers can be built in any order, at any z-level, according to your preference and the layout peculiarities of your embark site:" "- The industry level has a compact, but complete set of workshops and stockpiles (minus what is already provided on the farming level)." -"- The services level has dining, hospital, marksman barracks, and justice services, plus a well system. It is 4 z-levels deep." -"- The guildhall level has large, empty rooms for building libraries, temples, and guildhalls." -- The suites level has fancy rooms for your nobles. +"- The services level has dining, hospital, marksman barracks, and justice services. It has a well system and is 4 z-levels deep." +"- The guildhall level has large rooms for building libraries, temples, and guildhalls, with optional furniture layouts." +- The suites level has fancy rooms for your nobles with the furniture that they require. - The apartments levels have small but well-furnished bedrooms for your other dwarves. +- The crypt level houses your dead. "" "Run each level's ""help"" blueprint (e.g. ""quickfort run library/dreamfort.csv -n /surface_help"") for more details." "" @@ -20,25 +21,20 @@ "" "There are some tasks common to all forts that Dreamfort doesn't specifically handle for you. For example, Dreamfort sets up a barracks, but managing squads is up to you. Here are some other common tasks that may need to be done manually (or with some other tool):" - Exploratory mining for specific resources like iron -- Filling the well system with water -- Bringing magma up to the industry level to power magma forges/furnaces +"- Filling the well system with water (if you have a light aquifer, see library/aquifer_tap.csv for help with this)" +- Bringing magma up to the industry level to power magma forges/furnaces (see library/pump_stack.csv for help with this) - Manufacturing trade goods -- Assigning skilled labors to migrants (although the example professions included with DFHack will help with this) "- Custom stockpile setups to assist with, for example, encrusting only high-quality items" "" -"Dreamfort works best at an embark site that is flat and has at least one soil layer. New players should avoid embarks with aquifers if they are not prepared to deal with them. Bring picks for mining, an axe for woodcutting, and an anvil for a forge. Bring a few blocks to speed up initial workshop construction as well. That's all you really need, but see the example embark profile in the online spreadsheets for a more complete setup." +"Dreamfort works best at an embark site that is relatively flat and has at least one soil layer. New players should avoid embarks with aquifers if they are not prepared to deal with them. Bring picks for mining, an axe for woodcutting, and an anvil for a forge. Bring a few blocks to speed up initial workshop construction as well. That's all you really need, but see the example embark profile in the online spreadsheets for a more complete setup." "" -"Other DFHack commands also work very well with Dreamfort, such as autofarm, autonestbox, prioritize, seedwatch, tailor, and, of course, buildingplan. An init file that gets everything configured for you is distributed with DFHack as hack/examples/init/onMapLoad_dreamfort.init." -Put that file in your dfhack-config/init/ directory -- the same directory that has dfhack.init. +"Other DFHack tools also work very well with Dreamfort, such as autofarm and all of the other DFHack auto tools. Turn them on in the DFHack gui/control-panel." "" -"Also check out https://docs.dfhack.org/en/stable/docs/Plugins.html#professions for more information on the default labor professions that are distributed with DFHack, including suggestions on how many dwarves of each profession you are likely to need at each stage of fort maturity." +"Once you have your starting surface workshops up and running, you might want to configure buildingplan to only use blocks for constructions so it won't use your precious wood, boulders, and bars to build floors and walls. If you bring at least 7 blocks with you on embark, you can set this to be your default in the Autostart tab of gui/control-panel." "" -"Once you have your starting surface workshops up and running, you might want to configure buildingplan (in its global settings, accessible from any building placement screen, e.g.: b-a-G) to only use blocks for constructions so it won't use your precious wood, boulders, and bars to build floors and walls. If you bring at least 7 blocks with you on embark, you can even set this in your dfhack-config/init/onMapLoad.init file like this:" -on-new-fortress buildingplan set boulders false; buildingplan set logs false +"Directly after embark, run ""quickfort run library/dreamfort.csv -n /setup_help"" to get some advice on initial settings, and get started building your fort with ""quickfort run library/dreamfort.csv -n /surface1"" on the surface (see /surface_help for how to select a good spot). Read the walkthroughs for each level to understand what's going on and follow the checklist to keep track of where you are in the building process. Good luck, and have fun building an awesome Dreamfort-based fort!" "" -"Directly after embark, run ""quickfort run library/dreamfort.csv -n /setup"" with your cursor on your wagon to set settings, and get started building your fort with ""quickfort run library/dreamfort.csv -n /surface1"" on the surface (see /surface_help for how to select a good spot). Read the walkthroughs for each level to understand what's going on and follow the checklist to keep track of where you are in the building process. Good luck, and have fun building an awesome Dreamfort-based fort!" -"" -"The dreamfort.csv file distributed with DFHack is generated from online spreadsheet files. If you want to look at how these blueprints are put together, it is easier to look at the online spreadsheets than the giant .csv. You can view them at: https://drive.google.com/drive/folders/1iS90EEVqUkxTeZiiukVj1pLloZqabKuP" +"The dreamfort.csv file distributed with DFHack is generated from online spreadsheet files. If you want to look at how these blueprints are put together, it is easier to look at the online spreadsheets than the giant .csv. You can view them at: https://drive.google.com/drive/folders/1dsmvnzbOKsyFS3DCj0F8ibSnMhVHEjdV" You are welcome to copy the Dreamfort spreadsheets and make your own modifications! "" "If you like, you can download a fully built Dreamfort-based fort from https://dffd.bay12games.com/file.php?id=15434 and explore it @@ -46,105 +42,98 @@ interactively." "# The dreamfort.csv distributed with DFHack is generated with the following command: for fname in dreamfort*.xlsx; do xlsx2csv -a -p '' ""$fname""; done | sed 's/,*$//'" #notes label(checklist) command checklist -"Here is the recommended order for Dreamfort commands. You can copy/paste the command lines directly into the DFHack terminal, or, if you prefer, you can run the blueprints in the UI with gui/quickfort. See the walkthroughs (the ""help"" blueprints) for context and details. Also remember to read the messages the blueprints print out after you run them so you don't miss any important manual steps." +"Here is the recommended order for Dreamfort commands. You can either copy/paste the command lines directly into the DFHack terminal or run the blueprints in the UI with gui/quickfort. See the walkthroughs (the ""help"" blueprints) for context and details. Also remember to read the messages the blueprints print out after you run them so you don't miss any important manual steps!" "" -- Preparation (before you embark!) -- -Copy hack/examples/init/onMapLoad_dreamfort.init to your dfhack-config/init directory inside your DF installation -Optionally copy the premade Dreamfort embark profile from the online spreadsheets to the data/init/embark_profiles.txt file +Optionally copy the premade Dreamfort embark profile from the online spreadsheets to the prefs/embark_profiles.txt file. +Run gui/control-panel and enable settings on the Autostart tab. See the /setup_help notes for details. "" -- Set settings and preload initial orders -- -quickfort run library/dreamfort.csv -n /setup,# Run before making any manual adjustments to settings! Run the /setup_help blueprint for details on what this blueprint does. -"quickfort orders library/dreamfort.csv -n ""/surface2, /farming2, /surface3, /farming3, /industry2, /surface4""","# Queue up orders required to get the fort minimally functional and secure. You can remove the order for the anvil (you brought one with you, right?)." +quickfort run library/dreamfort.csv -n /setup_help,# For advice on how to do initial setup for success. +"quickfort orders library/dreamfort.csv -n ""/surface2, /farming2, /surface3, /industry2, /surface4""","# Queue up orders required to get the fort minimally functional and secure. You can remove the order for the anvil (you brought one with you, right?)." "" -- Find a good starting spot on the surface -- gui/quickfort library/dreamfort.csv -n /perimeter,# Run at embark. Don't actually apply the blueprint. Just use the preview shadow to find a good spot on the surface. "" -- Dig -- -quickfort run library/dreamfort.csv -n /surface1,# Run when you find your center tile. -quickfort run library/dreamfort.csv -n /dig_all,"# Run when you find a suitable rock layer for the industry level. It designates digging for industry, services, guildhall, suites, and apartments all in one go. This list does not include the farming level, which we'll dig in the uppermost soil layer a bit later. Note that it is more efficient for your miners if you designate your digging before they dig the central stairs past that level since the stairs are dug at a low priority. This keeps your miners focused on one level at a time. If you need to designate your levels individually due to caverns interrupting the sequence or just because it is your preference, run the level-specific dig blueprints (i.e. /industry1, /services1, /guildhall1, /suites1, and 5 levels of /apartments1) instead of running /dig_all." +quickfort run library/dreamfort.csv -n /surface1,# Run when you find your center tile. Deconstruct your wagon if it is in the way. +quickfort run library/dreamfort.csv -n /dig_all,"# Run when you find a suitable rock layer for the industry level. It designates digging for industry, services, guildhall, suites, apartments, and the crypt all in one go. This list does not include the farming level, which we'll dig in the uppermost soil layer a bit later. Note that it is more efficient for your miners if you designate your digging before they dig the central stairs past that level since the stairs are dug at a low priority. This keeps your miners focused on one level at a time. If you need to designate your levels individually due to caverns interrupting the sequence or just because it is your preference, run the level-specific dig blueprints (i.e. /industry1, /services1, /guildhall1, /suites1, 3 levels of /apartments1, and /crypt1) instead of running /dig_all." "" -- Core fort (should finish at about the third migration wave) -- quickfort run library/dreamfort.csv -n /surface2,"# Run after initial trees are cleared. If you are deconstructing the wagon now, wait until after the wagon is deconstructed so the jobs that depend on wagon contents (e.g. blocks) don't get canceled later." -quickfort run library/dreamfort.csv -n /farming1,# Run when channels are dug and the additional designated trees are cleared. +quickfort run library/dreamfort.csv -n /farming1,# Run when channels are dug and the additional designated trees on the surface are cleared. quickfort run library/dreamfort.csv -n /farming2,# Run when the farming level has been dug out. quickfort run library/dreamfort.csv -n /surface3,# Run right after /farming2. -quickfort run library/dreamfort.csv -n /farming3,# Run when furniture has been placed. quickfort run library/dreamfort.csv -n /industry2,# Run when the industry level has been dug out. -prioritize ConstructBuilding,# To get those workshops up and running ASAP. You may have to run this several times as the materials for the building construction jobs become ready. +prioritize ConstructBuilding,"# To get those workshops up and running ASAP. You may have to run this several times as the materials for the building construction jobs become ready. As industry workshops are built, you can remove the temporary workshops and stockpiles on the surface. **Be sure that there are no items attached to jobs left in the workshops before deconstructing them, otherwise you'll get canceled jobs!**" quickfort run library/dreamfort.csv -n /surface4,"# Run after the walls and floors are built on the surface. Even if /surface3 is finished before you run /industry2, though, wait until after /industry2 to run this blueprint so that surface walls, floors, and roofing don't prevent your workshops from being built (due to lack of blocks)." -"quickfort orders,run library/dreamfort.csv -n /services2",# Run when the services levels have been dug out. Feel free to remove the orders for the ropes if you already brought them with you. -orders import library/basic,"# Run after the first migration wave, so you have dorfs to do all the basic tasks. Note that this is the ""orders"" plugin, not the ""quickfort orders"" command." +"quickfort orders,run library/dreamfort.csv -n /services2","# Run when the services levels have been dug out. Feel free to remove the orders for the ropes if you already brought them with you. If you are filling your wells from an aquifer or stream, now is also a good time to start digging the plumbing." +orders import library/basic,"# Run after the first migration wave, so you have dwarves to do all the basic tasks. Note that this is the ""orders"" plugin, not the ""quickfort orders"" command." "quickfort orders,run library/dreamfort.csv -n /surface5","# Run when all marked trees on the surface are chopped down and walls and floors have been constructed, including the roof section over the future barracks." prioritize ConstructBuilding,# Run when you see the bridges ready to be built so the busy masons come and build them. -"quickfort orders,run library/dreamfort.csv -n /surface6",# Run when at least the beehives and weapon rack are constructed and you have linked all levers to their respective bridges. +"quickfort orders,run library/dreamfort.csv -n /surface6",# Run once you have linked all levers to their respective bridges. "quickfort orders,run library/dreamfort.csv -n /surface7",# Run after the surface walls are completed and any marked trees are chopped down. "" -- Plumbing -- -"This is a good time to fill your well cisterns, either with a bucket brigade or by routing water from a freshwater stream or an aquifer (see the library/aquifer_tap.csv blueprint for help with this)." -"Also consider bringing magma up to your services level so you can replace the forge and furnaces on your industry level with more powerful magma versions. This is especially important if your embark has insufficient trees to convert into charcoal. Keep in mind that moving magma is a tricky process and can take a long time. Don't forget to continue making progress through the checklist! If you choose to use magma, I suggest getting it in place before importing the military and smelting automation orders since they make heavy use of furnaces and forges." +"If you haven't done it already, this is a good time to fill your well cisterns, either with a bucket brigade or by routing water from a freshwater stream or an aquifer (see the library/aquifer_tap.csv blueprint for help with this)." +"Also consider bringing magma up to your services level so you can replace the forge and furnaces on your industry level with more powerful magma versions. This is especially important if your embark has insufficient trees to convert into charcoal. Keep in mind that moving magma is a tricky process and can take a long time. Don't forget to continue making progress through this checklist! If you choose to use magma, try to get it in place before importing the military and smelting automation orders since they make heavy use of furnaces and forges." "" -- Mature fort (third migration wave onward) -- +The order of steps in this section is less important. Feel free to reorder as per the needs of your fort. orders import library/furnace,# Automated production of basic furnace-related items. Don't forget to create a sand collection zone (or remove the sand- and glass-related orders if you have no sand). +"quickfort orders,run library/dreamfort.csv -n /guildhall2_default",# Run when the guildhall level has been dug out. "quickfort orders,run library/dreamfort.csv -n /suites2",# Run when the suites level has been dug out. -"quickfort orders,run library/dreamfort.csv -n /surface8","# Run if/when you need longer trap corridors on the surface for larger sieges, anytime after you run /surface7." +"quickfort orders,run library/dreamfort.csv -n /services3",# Run when your population grows to about 20. "quickfort orders,run library/dreamfort.csv -n /apartments2",# Run when the first apartment level has been dug out. -"quickfort orders,run library/dreamfort.csv -n /services3","# Run after the dining table and chair, weapon rack, and archery targets have been constructed. Also wait until after you complete /surface7, though, because surface defenses are more important than a grand dining hall." -"quickfort orders,run library/dreamfort.csv -n /guildhall2",# Run when the guildhall level has been dug out. -"quickfort orders,run library/dreamfort.csv -n ""/guildhall3, /guildhall4""",# Optionally run after /guildhall2 to build default furnishings and declare a library and temple. -"quickfort orders,run library/dreamfort.csv -n /apartments3",# Run when all beds have been constructed on the first apartments level. -"quickfort orders,run library/dreamfort.csv -n /farming4",# Run once you have a cache of potash. +"quickfort orders,run library/dreamfort.csv -n /crypt2",# Run when the crypt level has been dug out. +"quickfort orders,run library/dreamfort.csv -n /surface8","# Run if/when you need longer trap corridors on the surface for larger sieges, anytime after you run /surface7." +"quickfort orders,run library/dreamfort.csv -n /farming3",# Add in all the doors we couldn't afford to build earlier. orders import library/military,# Automated production of military equipment. Turn on automelt in the meltables piles on the industry level to automatically upgrade all metal military equipment to masterwork quality. These orders are optional if you are not using a military. orders import library/smelting,# Automated production of all types of metal bars. -"quickfort orders,run library/dreamfort.csv -n /services4","# Run when you need a jail, anytime after the restraints are placed from /services3." -orders import library/rockstock,# Maintains a small stock of all types of rock furniture. +"quickfort orders,run library/dreamfort.csv -n /services4",# Run when you need a full dining room and/or jail. +orders import library/rockstock,# Maintains a small stock of all types of rock furniture. Useful for filling out future guildhalls. orders import library/glassstock,# Maintains a small stock of all types of glass furniture and parts (only import if you have sand). +"quickfort orders,run library/dreamfort.csv -n /apartments2",# Repeat as needed as your fort grows. +"quickfort orders,run library/dreamfort.csv -n /crypt3",# Run when the crypt is starting to run out of space. "" --- Repeat for each remaining apartments level as needed -- -"quickfort orders,run library/dreamfort.csv -n /apartments2",# Run when the apartment level has been dug out. -"quickfort orders,run library/dreamfort.csv -n /apartments3",# Run when all beds have been constructed. -burial -pets,# Run once the coffins are placed to set them to allow for burial. - See this checklist online at https://docs.google.com/spreadsheets/d/13PVZ2h3Mm3x_G1OXQvwKd7oIR2lK4A1Ahf6Om1kFigw/edit#gid=1459509569 #notes label(setup_help) -Makes common initial adjustments to in-game settings. -"" -"The /setup blueprint is intended to be run once at the start of the game, before anything else is changed. Players are welcome to make any further customizations after this blueprint is run. Please be sure to run the /setup blueprint before making any other changes, though, so your settings are not overwritten!" -"" -The following settings are changed: -"- The manager, chief medical dwarf, broker, and bookkeeper noble roles are assigned to the first suggested dwarf. This is likely to be the expedition leader, but the game could suggest others if they have relevant skills. Bookkeeping is set to the highest precision." -"" -- Standing orders are set to: - - only farmers harvest - - gather refuse from outside (incl. vermin) - - no autoloom (so the hospital always has thread -- we'll be managing cloth production with automated orders) -"" -"- A burrow named ""Inside"" is created, but it's up to the player to define the area as the fort expands. It is intended for use in getting your civilians to safety during sieges. A military alert named ""Siege"" is also created and associated with the ""Inside"" burrow." -"" -- Military uniforms get the following modifications: - - all default uniforms set to replace clothing -" - in the ""Metal armor"" uniform, the ""metal armor"" item is removed and replaced by ""metal breastplate"" and ""metal mail shirt""" -" - in the ""Metal armor"" uniform, the ""metal legwear"" item is removed and replaced by ""metal greaves""" -"" -"All default uniforms should also have a leather cloak added, but the position of the cloak in the equipment list changes for every embark. We suggest manually adding a leather cloak to your uniforms after running the /setup blueprint." -"" -- Hotkeys are created for the 8 most interesting levels. Only the names are set -- it's up to the player to adjust them to actual locations in the (H) menu since the blueprint can't know where the levels will eventually be built. Feel free to replace any hotkeys with locations that *you* think are interesting : ) -"" -These are all set for your convenience. Nothing in Dreamfort depends on these settings staying as they are. Feel free to change any setting to your personal preference. -"" -#aliases -startnobles: n{Down 4}{togglesequence 8}{Up}s{Up}&^^ -startorders: ohrov^Wl^^ -startburrows: wa&nInside&^^ -metalarmorsetup: A{Right 2}{Down 2}&{Down}&{Left}&M{Right}{Down}&{Left}{Down}{Right}{Down}&{Left}{Down 2}L{Right}{Down 2}&{Left}{Down 3}&M{Right}{Down}&{Left 2} -startmilitary: mnr{Down}r{metalarmorsetup}{Down}racNSiege&{Right}&^ -sethotkey: {fkey}n{name}& -starthotkeys: H{sethotkey fkey={F2} name=Farming}{sethotkey fkey={F3} name=Industry}{sethotkey fkey={F4} name=Services}{sethotkey fkey={F5} name=Guildhall}{sethotkey fkey={F6} name=Quarry}{sethotkey fkey={F7} name=Cavern}{sethotkey fkey={F8} name=Magma}^ -"" -"#config label(setup) message(Please set the zoom targets of the hotkeys (the 'H' menu) according to where you actually end up digging the levels. -As you build your fort, expand the ""Inside"" burrow to include new civilian-safe areas. -Optionally, add a leather cloak to your military uniforms to enhance their protection rating. -Nothing in Dreamfort depends on these settings staying as they are. Feel free to change any setting to your personal preference.) assign nobles, set standing orders, create burrows, make adjustments to military uniforms, and set hotkey names" -{startnobles}{startorders}{startburrows}{startmilitary}{starthotkeys} +These are Dreamfort's suggestions for adjustments to settings and initial setup. +"" +"- Assign dwarves to manager, chief medical dwarf, sheriff, broker, and bookkeeper noble roles (they can all be the same dwarf)" +"" +- On the Work details screen (Labor -> Work details) + - Specialize your miners (click the hammer-lock button so it turns red) and make your miners also engravers (they'll need something to do once the mining is done) +"" +- In standing orders (Labor -> Standing orders): +" - Change ""Automatically weave all thread"" to ""No automatic weaving"" so the hospital always has thread -- we'll be managing cloth production with automated orders" +"" +"- Create a burrow named ""Inside"" and register it as a civilian alert burrow in gui/civ-alert so you can use it to get your civilians to safety during sieges. You will have to periodically expand the burrow area as the fort expands." +"" +"Beyond the bugfix tools that are enabled by default, we recommend enabling the following DFHack tools in gui/control-panel (but they are not required if you prefer to do these things manually):" +"On the ""Autostart"" tab, additionally enable:" +- autobutcher +- autobutcher target 50 50 14 2 BIRD_GOOSE +- autochop +- autofarm +- autofish +- autonestbox +- autoslab +- ban-cooking all +- buildingplan set boulders false +- buildingplan set logs false +- nestboxes +- prioritize +- seedwatch +- suspendmanager +- tailor +"Note that if you've already started your fort and have missed the ""new fort"" trigger, you can enable these tools on the ""Fort"" tab instead. You can run the one-time commands (like ban-cooking all) manually from gui/launcher." +"" +"On the ""Maintenance"" tab, enable:" +- everything +"" +"On the ""System"" tab, additionally enable:" +- work-now "#meta label(dig_all) start(central stairs on industry level) dig industry, services, guildhall, suites, and apartments levels. does not include farming." # Note that this blueprint will only work for the unified dreamfort.csv. It won't work for the individual .xlsx files (since #meta blueprints can't cross file boundaries). "" @@ -156,35 +145,35 @@ Nothing in Dreamfort depends on these settings staying as they are. Feel free to #> /suites1 #> -/apartments1 repeat(down 5) +/apartments1 repeat(down 3) +#>3 +/crypt1 #ignore -"Here are the minimal labors needed for essential tasks in getting Dreamfort up and running, along with suggestions for which dwarves to assign them to. You can enable additional labors as you wish. Skills with an asterisk (*) are especially worth putting points into on the embark preparation screen." +"Here are the most important skills for getting Dreamfort up and running, along with suggestions for how to distribute them." "" -Manager / Bookkeeper / Broker,Miner,Miner,Mason,Mason,Outdoorsdwarf,Farmer -Mechanic (*),Miner (*),Miner (*),Mason (*),Mason (*),Carpenter (*),Grower (*) -Stonecrafter,,,,,Wood Cutter (*) -Wood Cutter,,,,,Bee Keeper -Architect -Misc. labors needed for constructing workshops +Manager / Bookkeeper / Broker,Miner,Miner,Mason,Smith,Outdoorsdwarf,Farmer +Mechanic,Miner,Miner,Stonecutter,Weaponsmith,Carpenter,Stonecutter +Stonecrafting,Engraver,Engraver,Mason,Armorsmith,Wood Cutter,Planter "" -"The most time-consuming tasks in early Dreamfort are: mining, chopping down trees, and making blocks. Starting with at least two miners, two woodcutters (assuming your embark has trees), and two masons helps in keeping the fort from stalling." +"The most time-consuming tasks in early Dreamfort are: mining, chopping down trees, and making blocks. Starting with at least two miners, two woodcutters (assuming your embark has trees), and two stonecutters helps keep the fort from stalling. If you have to smooth walls to prevent a light aquifer from flooding your fort, starting with a few engravers saves a ton of time." "" We suggest bringing at least: 2 picks,for the two miners -2 battleaxes,for the two woodcutters +2 battleaxes,for two active woodcutters 1 anvil,for the forge food and seeds,as per usual 4 ropes,"for the hospital well and traction benches. you could build the ropes out of raw materials, but dwarves are usually too busy to do textile work at the start of the game." 7 blocks,for starting workshops and the temporary trade depot. necessary if you have buildingplan configured for blocks only. -many boulders,for quickly turning into more blocks. blocks are the limiting factor in the early stages. +many boulders,for quickly turning into more blocks while your miners are digging in dirt or dealing with aquifers. blocks are the limiting factor in the early stages. dogs and cats,for protection and vermin control -geese,"for bones and leather. bring at least 1 male and 2 females for the 2 early nestboxes. autobutcher settings in the included onMapLoad_dreamfort.init file are optimized for raising geese. if you prefer another bird, be sure to adjust the autobutcher settings." +geese,for bones and leather. bring at least 1 male and 2 females for the 2 early nestboxes. "" Also bring logs for beds if embarking in an area without many trees. "" See ldog's Dreamfort embark profile for a more advanced (and more thoroughly explained!) approach: https://drive.google.com/file/d/1Et42JTzeYK23iI5wrPMsFJ7lUXwVBQob/view?usp=sharing "#ignore Add these lines to the bottom of your ""data/init/embark_profiles.txt"" file to make the ""Dreamfort"" profile available in-game. Also see ldog's dreamfort embark profile for a more advanced, dwarfy approach." +[PROFILE] [TITLE:Dreamfort] [SKILL:1:STONECRAFT:1] [SKILL:1:MECHANICS:5] @@ -193,36 +182,40 @@ https://drive.google.com/file/d/1Et42JTzeYK23iI5wrPMsFJ7lUXwVBQob/view?usp=shari [SKILL:1:ORGANIZATION:1] [SKILL:1:RECORD_KEEPING:1] [SKILL:2:MINING:5] -[SKILL:2:DETAILSTONE:2] +[SKILL:2:ENGRAVE_STONE:4] [SKILL:2:SWIMMING:1] [SKILL:3:MINING:5] -[SKILL:3:DETAILSTONE:2] +[SKILL:3:ENGRAVE_STONE:4] [SKILL:3:SWIMMING:1] -[SKILL:4:MASONRY:5] -[SKILL:5:MASONRY:5] +[SKILL:4:MASONRY:2] +[SKILL:4:MILITARY_TACTICS:5] +[SKILL:4:CUT_STONE:3] +[SKILL:5:FORGE_WEAPON:5] +[SKILL:5:FORGE_ARMOR:5] [SKILL:6:WOODCUTTING:5] [SKILL:6:CARPENTRY:5] -[SKILL:7:PLANT:3] -[ITEM:2:WEAPON:ITEM_WEAPON_PICK:INORGANIC:COPPER] -[ITEM:2:WEAPON:ITEM_WEAPON_AXE_BATTLE:INORGANIC:COPPER] +[SKILL:7:PLANT:5] +[SKILL:7:CUT_STONE:5] +[ITEM:10:CLOTH:NONE:CREATURE_MAT:SPIDER_CAVE:SILK] +[ITEM:100:WOOD:NONE:PLANT_MAT:WILLOW:WOOD] +[ITEM:30:BLOCKS:NONE:INORGANIC:QUARTZITE] +[ITEM:21:SEEDS:NONE:PLANT_MAT:MUSHROOM_HELMET_PLUMP:SEED] +[ITEM:21:SEEDS:NONE:PLANT_MAT:GRASS_TAIL_PIG:SEED] +[ITEM:21:SEEDS:NONE:PLANT_MAT:GRASS_WHEAT_CAVE:SEED] +[ITEM:21:SEEDS:NONE:PLANT_MAT:POD_SWEET:SEED] +[ITEM:20:SEEDS:NONE:PLANT_MAT:BUSH_QUARRY:SEED] +[ITEM:20:SEEDS:NONE:PLANT_MAT:MUSHROOM_CUP_DIMPLE:SEED] [ITEM:1:ANVIL:NONE:INORGANIC:IRON] -[ITEM:7:DRINK:NONE:PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK] -[ITEM:26:DRINK:NONE:PLANT_MAT:GRASS_TAIL_PIG:DRINK] -[ITEM:26:DRINK:NONE:PLANT_MAT:GRASS_WHEAT_CAVE:DRINK] -[ITEM:26:DRINK:NONE:PLANT_MAT:POD_SWEET:DRINK] -[ITEM:10:SEEDS:NONE:PLANT_MAT:MUSHROOM_HELMET_PLUMP:SEED] -[ITEM:10:SEEDS:NONE:PLANT_MAT:GRASS_TAIL_PIG:SEED] -[ITEM:10:SEEDS:NONE:PLANT_MAT:GRASS_WHEAT_CAVE:SEED] -[ITEM:10:SEEDS:NONE:PLANT_MAT:POD_SWEET:SEED] -[ITEM:10:SEEDS:NONE:PLANT_MAT:BUSH_QUARRY:SEED] -[ITEM:10:SEEDS:NONE:PLANT_MAT:MUSHROOM_CUP_DIMPLE:SEED] -[ITEM:25:PLANT:NONE:PLANT_MAT:MUSHROOM_HELMET_PLUMP:STRUCTURAL] +[ITEM:2:WEAPON:ITEM_WEAPON_AXE_BATTLE:INORGANIC:COPPER] +[ITEM:2:WEAPON:ITEM_WEAPON_PICK:INORGANIC:COPPER] +[ITEM:20:DRINK:NONE:PLANT_MAT:GRASS_TAIL_PIG:DRINK] +[ITEM:20:DRINK:NONE:PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK] +[ITEM:20:DRINK:NONE:PLANT_MAT:POD_SWEET:DRINK] +[ITEM:20:DRINK:NONE:PLANT_MAT:GRASS_WHEAT_CAVE:DRINK] +[ITEM:30:PLANT:NONE:PLANT_MAT:MUSHROOM_HELMET_PLUMP:STRUCTURAL] +[ITEM:45:BOULDER:NONE:INORGANIC:QUARTZITE] +[ITEM:20:THREAD:NONE:CREATURE_MAT:SPIDER_CAVE:SILK] [ITEM:4:CHAIN:NONE:CREATURE_MAT:SPIDER_CAVE:SILK] -[ITEM:5:CLOTH:NONE:CREATURE_MAT:SPIDER_CAVE:SILK] -[ITEM:5:THREAD:NONE:CREATURE_MAT:SPIDER_CAVE:SILK] -[ITEM:50:WOOD:NONE:PLANT_MAT:WILLOW:WOOD] -[ITEM:40:BOULDER:NONE:INORGANIC:QUARTZITE] -[ITEM:10:BLOCKS:NONE:INORGANIC:QUARTZITE] [PET:2:DOG:FEMALE:STANDARD] [PET:1:DOG:MALE:STANDARD] [PET:2:CAT:FEMALE:STANDARD] @@ -268,7 +261,7 @@ Features: - Barracks (with prisoner processing quantum dump) - Trap-filled hallways for invaders - Optional extended trap hallways (to handle larger sieges) -- Protected trade depot +"- Protected trade depot, with separate trade goods stockpiles for organics and inorganics (for easy elven trading)" - A grid of 1x1 farm plots (intended to be managed by DFHack autofarm) "" Manual steps you have to take: @@ -279,11 +272,11 @@ Manual steps you have to take: Be sure to choose an embark site that has a flat area on the surface large enough to use these blueprints! "" Surface Walkthrough: -"1) Choose a tile for your central fortress stairs. The terrain around that tile should be perfectly flat. Trees are ok, but no slopes, rivers, or lakes. To be sure that the tile you've chosen is in a good spot, set the cursor over that tile and run ""quickfort run library/dreamfort.csv -n /perimeter"". This will show you the eventual boundaries of the fort. Some wall segments might be missing due to existing trees, but that's ok. Make sure the area within the exterior wall is flat. Run ""quickfort undo library/dreamfort.csv -n /perimeter"" to clean up." +"1) Choose a tile for your central fortress stairs. The terrain around that tile should be perfectly flat. Trees are ok, but no slopes, rivers, or lakes. To be sure that the tile you've chosen is in a good spot, run ""gui/quickfort library/dreamfort.csv -n /perimeter"". This will show you the eventual boundaries of the fort. Some wall segments might be missing due to existing trees, but that's ok. Make sure the area within the exterior wall is flat. Cancel out of the preview. You don't actually need to apply this blueprint." "" "2) With the cursor on the chosen tile, run /surface1 to clear surrounding trees and set up your pastures. Deconstruct your wagon to get it out of the way of our upcoming walls and floors. Remember to assign your dogs to the pasture around the staircase and your grazing animals to the large pasture. Your egg-layers will automatically get assigned to nestbox zones once the nestboxes are built, so you don't need to worry about them. You can let your cats roam free to chase vermin." "" -"3) Once the marked trees have been cleared, run /surface2 to setup starting workshops/stockpiles, channel out the miasma vents for the farming level, and start clearing trees from a larger area. If you haven't done it already, now is a good time to configure buildingplan to only build buildings with blocks, not logs or raw boulders. Do this by entering buildingplan's global configuration (""baG"") and ensuring the only generic building material allowed is ""blocks"". Run ""quickfort orders"" for /surface2." +"3) Once the marked trees have been cleared, run /surface2 to setup starting workshops/stockpiles, channel out the miasma vents for the farming level, and start clearing trees from a larger area. If you haven't done it already, now is a good time to configure buildingplan to only build buildings with blocks, not logs or raw boulders. Run ""quickfort orders"" for /surface2." "" "4) Once the channels are dug out and the trees are cleared, start digging the farming level one z-level down. Once you have run /farming2, come back to the surface and run /surface3 to cover the vents and build an enclosure around your central stairs. Although the vents will be covered with flooring, they will still work to prevent miasma on the farming level. Run ""quickfort orders"" for /surface3." "" @@ -291,39 +284,39 @@ Surface Walkthrough: "" "6) Once walls and floors have been constructed (including the small roof segment one z-level up over the barracks), run /surface5 to build furniture, gates, and the permanent trade depot. Remember to deconstruct the temporary trade depot once nobody is using it. Run ""quickfort orders"" for /surface5." "" -"7) Once at least the beehives and weapon rack are built, run /surface6 to configure the rooms and build the remaining walls and floors. Run ""quickfort orders"" for /surface6." +"7) Once all marked trees are cleared, run /surface6 to build the remaining walls and floors. Run ""quickfort orders"" for /surface6." "" "8) Once you have enough dwarves to do a lot of building without starving other important tasks, run /surface7 to build the roof. Run ""quickfort orders"" for /surface7." "" "9) For extra security, you can run /surface8 any time after /surface7 to extend the trap corridors. Run ""quickfort orders"" for /surface8." "" -"10) Once your industry and farming levels are set up and running, you can disassemble the surface workshops and remove the surface stockpiles. Disassembling a workshop scatters the items stored within it and cancels any pending jobs that happen to use those items. In order to avoid job cancellations, first set the surface workshops to not accept general work orders. Do this by entering query mode (""q""), selecting a workshop, entering the workshop profile (""P""), moving to work orders (right arrow), and hitting Enter. Then enter view mode (""t"") and check to see if any items in a workshop are marked with ""TSK"". Once no items in the workshop have that marker, you are free to disassemble that workshop." +"10) Once your industry and farming levels are set up and running, you can disassemble the surface workshops and remove the surface stockpiles. Disassembling a workshop scatters the items stored within it and cancels any pending jobs that happen to use those items. In order to avoid job cancellations, first set the surface workshops to not accept general work orders. Then check to see if any items in a workshop are marked as being part of an active job. Once no items in the workshop have that marker, you are free to disassemble that workshop." "" Sieges and Prisoner Processing: Here are some tips and procedures for handling seiges -- including how to clean up afterwards! "" "- Ensure your ""Inside"" burrow includes only below-ground areas and safe surface areas of your fort. In particular, don't include the ""atrium"" area (where the ""siege bait"" zone is) or the trapped hallways." "" -"- When a siege begins, set the civilian alert to ""Siege"" to ensure all your civilians stay out of danger. Immediately pull the lever to close the outer main gate. It is also wise to close the trade depot and inner main gate as well. That way, if enemies get past the traps, they'll have to go through the soldiers in your barracks." +"- When a siege begins, set your civilian alert (attach the alert to your ""Inside"" burrow if it isn't already) to ensure all your civilians stay out of danger. Immediately pull the lever to close the outer main gate. It is also wise to close the trade depot and inner main gate as well. That way, if enemies get past the traps, they'll have to go through the soldiers in your barracks." "" "- During a siege, use the levers to control how attackers path through the trapped corridors. If there are more enemies than cage traps, time your lever pulling so that the inner gates snap closed before your last cage trap is sprung. Then the remaining attackers will have to backtrack and go through the other trap-filled hallway." "" -"- If your cage traps fill up, ensure your hallways are free of uncaged attackers, then close the trap hallway outer gates and open the inner gates. Unset the civilian alert and allow your dwarves to reset all the traps -- make some extra cages in preparation for this! Then re-set the civilian alert to ""Siege"" and open the trap hallway outer gates." +"- If your cage traps fill up, ensure your hallways are free of uncaged attackers, then close the trap hallway outer gates and open the inner gates. Clear the civilian alert and allow your dwarves to reset all the traps -- make some extra cages in preparation for this! Then re-enable the civilian alert and open the trap hallway outer gates." "" "- Once the last attacker is caged, open all the gates and unset the civilian alert. Life is normal again!" "" "After a siege, you can use the caged prisoners to safely train your military. Here's how:" "" -"- Once the prisoners are hauled to the ""prisoner quantum"" stockpile, run ""unforbid all"" and ""stripcaged all"" in the DFHack console (or GUI launcher)." +"- Once the prisoners are hauled to the ""prisoner quantum"" stockpile, run ""stripcaged all"" in the DFHack gui/launcher." "" "- After all the prisoners' items have been confiscated, bring your military dwarves to the barracks (if they aren't already there)." "" -- Use the zone (i) menu to assign a group prisoners to the pasture that overlaps the prisoner quantum stockpile +- Assign a group prisoners to the pasture that overlaps the prisoner quantum stockpile "" "- Hauler dwarves will come and release prisoners one by one. Your military dwarves will immediately pounce on the released prisoner and chop them to bits, saving the hauler dwarves from being attacked. Repeat until all prisoners have been ""processed""." "" -"Only common hostile creatures are accepted by the prisoner hauling route by default. You can add additional creature types by configuring the hauling route stop in the 'h' menu. Note that generated creature types, like necromancer experiments, can't be explicitly added. You have to (at least temporarily) accept all animals to include them." -#meta label(perimeter) start(central stairs) message(Run quickfort undo on this blueprint to clean up.) show the eventual perimeter of the surface fort; useful for location scouting +"Only common hostile creatures are accepted by the prisoner hauling route by default. You can add additional creature types by configuring the hauling route stop. Note that generated creature types, like necromancer experiments, can't be explicitly added. You have to (at least temporarily) accept all animals to include them." +"#meta label(perimeter) start(central stairs) message(If you accidentally applied this blueprint to the map, run quickfort undo on this blueprint to clean up.) show the eventual perimeter of the surface fort; useful for location scouting" walls/surface_walls corridor/surface_corridor "" @@ -333,15 +326,13 @@ If your wagon is within the fort perimeter, deconstruct it to get it out of the Once the marked trees are all chopped down (if any), continue with /surface2.) clear trees and set up pastures" clear_small/surface_clear_small zones/surface_zones -name_zones/surface_name_zones #> central_stairs/central_stairs repeat(down 10) "" "#meta label(surface2) start(central stairs) message(Remember to enqueue manager orders for this blueprint. Once the channels are dug out and the marked trees are cleared, continue with /surface3.) set up starting workshops/stockpiles, channel miasma vents, and clear more trees" -build_start/surface_build_start place_start/surface_place_start -query_start/surface_query_start +build_start/surface_build_start channel/surface_channel clear/surface_clear "" @@ -357,16 +348,14 @@ pre_building/surface_pre_building "" "#meta label(surface5) start(central stairs) message(Remember to enqueue manager orders for this blueprint. Disassemble the temporary trade depot in the pasture once the new one is constructed (and no merchants are using the old one). -Once the marked trees are cleared and at least the beehives and weapon rack have been constructed, continue with /surface6.) build gates, furniture, and trade stockpile/depot" +Once the marked trees are cleared, continue with /surface6.) build gates, furniture, and trade stockpile/depot" +traffic/surface_traffic place/surface_place build/surface_build -query/surface_query -traffic/surface_traffic clear_large/surface_clear_large "" "#meta label(surface6) start(central stairs) message(Remember to enqueue manager orders for this blueprint. -Continue with /surface7 sometime after the walls are completed and any marked trees are chopped down, whenever you have enough dwarves to build the roof without starving other important construction tasks.) configure hives and barracks, build traps and remaining walls/floors" -query2/surface_query2 +Continue with /surface7 sometime after the walls are completed and any marked trees are chopped down, whenever you have enough dwarves to build the roof without starving other important construction tasks.) build traps and remaining walls/floors" walls/surface_walls floors/surface_floors traps/surface_traps @@ -384,17 +373,26 @@ roof4/surface_roof4 corridor_gates/surface_corridor_gates corridor/surface_corridor corridor_traps/surface_corridor_traps -query_corridor/surface_query_corridor -#dig label(central_stairs_odd) start(2;2) hidden() spiral stairs odd levels +#dig label(central_stairs_odd) start(2;2) hidden() carved spiral stairs odd levels `,j6,` u,`,u `,j6,` -#meta label(central_stairs_even) hidden() spiral stairs even levels +#meta label(central_stairs_even) hidden() carved spiral stairs even levels /central_stairs_odd transform(cw) -#meta label(central_stairs) two levels of spiral stairs (use --repeat down) +#meta label(central_stairs) two levels of carved spiral stairs (repeat down as needed) /central_stairs_odd #> /central_stairs_even +#build label(central_stairs_odd_constructed) start(2;2) hidden() constructed spiral stairs odd levels +`,Cd,` +Cu,`,Cu +`,Cd,` +#meta label(central_stairs_even_constructed) hidden() constructed spiral stairs even levels +/central_stairs_odd_constructed transform(cw) +#meta label(central_stairs_constructed) two levels of constructed spiral stairs (repeat down as needed) +/central_stairs_odd_constructed +#> +/central_stairs_even_constructed #dig label(surface_clear_small) start(19; 19) hidden() clear trees for starting workshops and stockpiles @@ -417,7 +415,7 @@ u,`,u ,,,`,,`,,,,,,,,,,`,t1,j,t1,j,t1,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,t1,t1,t1,t1,t1,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,t1,t1,t1,t1,t1,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,t1,,,,,,t1,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,t1,t1,,,,,,t1,t1,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,t1,t1,,t1,t1,t1,,t1,t1,,,,,,,,,`,,` @@ -435,98 +433,30 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,nmt(25x11),,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,n(1x1),n(1x1),n(1x1),n(1x1),n(1x1),n(1x1),n(1x1),`,,,,,,`,nt(9x5),,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,nt,nt,nt,nt,nt,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,`,nt,`,~,`,nt,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,nt,nt,nt,nt,nt,,,,,,,,,,,`,,` -,,,`,,`,,`,n(1x1),n(1x1),n(1x1),n(1x1),n(1x1),n(1x1),n(1x1),`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,n,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,n,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` - - - -#query label(surface_name_zones) start(19; 19) hidden() - - - -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,"{namezone name=""main pasture""}",,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,"{namezone name=""nestbox1""}","{namezone name=""nestbox2""}","{namezone name=""nestbox3""}","{namezone name=""nestbox4""}","{namezone name=""nestbox5""}","{namezone name=""nestbox6""}","{namezone name=""nestbox7""}",`,,,,,,`,"{namezone name=""taming area""}",,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,"{namezone name=""guard dogs""}",,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,`,"{namezone name=""nestbox8""}","{namezone name=""nestbox9""}","{namezone name=""nestbox10""}","{namezone name=""nestbox11""}","{namezone name=""nestbox12""}","{namezone name=""nestbox13""}","{namezone name=""nestbox14""}",`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,"{namezone name=""siege bait""}",,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,"{namezone name=""prisoner processing""}",,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` - - - -#build label(surface_build_start) start(19; 19) hidden() message(There is room to the left of the carpenter's workshop to build one more workshop of any type if you need it.) starting workshops - - - -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` +,,,`,,`,"n{name=""Main pasture""}(25x11)",,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,"t{name=""Pet training area""}(9x5)",,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,wc,,,,wr,,,,wm,,,,wt,,,,,D,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,N,N,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` +,,,`,,`,,`,"n{name=""Nestbox 1""}(1x1)","n{name=""Nestbox 2""}(1x1)","n{name=""Nestbox 3""}(1x1)","n{name=""Nestbox 4""}(1x1)","n{name=""Nestbox 5""}(1x1)","n{name=""Nestbox 6""}(1x1)","n{name=""Nestbox 7""}(1x1)",`,,,,,,`,,,,,,,,,,`,,` +,,,`,,`,,`,,,,,,,,,"n/guarddogs{name=""Guard dogs""}(5x1)",~,~,~,~,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` +,,,`,,`,,`,,,,,,,,,n/guarddogs(5x1),~,~,~,~,,,,,,,,,,,`,,` +,,,`,,`,,`,"n{name=""Nestbox 8""}(1x1)","n{name=""Nestbox 9""}(1x1)","n{name=""Nestbox 10""}(1x1)","n{name=""Nestbox 11""}(1x1)","n{name=""Nestbox 12""}(1x1)","n{name=""Nestbox 13""}(1x1)","n{name=""Nestbox 14""}(1x1)",`,,,,,,`,,,,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` +,,,`,,`,"B{name=""Surface barracks""}(8x5)",,,,,,,,`,,,,,,,,`,"m{name=""Welcome area/wagon parking lot""}(8x5)",,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,,,,"n{name=""Siege bait pasture""}",,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` +,,,`,,`,,,,,,,"t{name=""Caged wildlife taming area""}(1x1)",,`,,,,,,,,`,,,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,"n{name=""Prisoner processing pen""}(1x-2)",`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` ,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` @@ -537,9 +467,9 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` +,,,`,,`,,,"gunzSpd{name=""Starting misc"" containers=0}(8x2)",,,,,,,,"hlr{name=""Starting cloth/trash"" containers=0}(8x2)",,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,f(8x4),,,,,,,,w(4x4),,,,s2(5x4),,,,,gunzSpd(4x4),,,,hlr(4x4),,,,`,,` +,,,`,,`,"f{name=""Starting food""}(8x4)",,,,,,,,"w{name=""Starting wood""}(4x4)",,,,"s2{name=""Starting stone""}:=otherstone(6x4)",,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -554,7 +484,7 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -566,29 +496,29 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway -#query label(surface_query_start) start(19; 19) hidden() config stockpiles +#build label(surface_build_start) start(19; 19) hidden() message(There is room to the left of the carpenter's workshop to build one more workshop of any type if you need it.) starting workshops ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,`,`,`,`,`,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,`,`,`,`,`,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,`,`,"D{name=""Starter trade depot""}",`,`,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,`,`,`,`,`,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,`,`,`,`,`,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,"{givename name=""starting food""}",,,,"{givename name=""starting wood""}",,,,,"{givename name=""starting stone""}",,,,"{givename name=""starting misc""}",,,,"{givename name=""starting cloth/trash""}",`,,` -,,,`,,`,,,,,,,,,,,,,otherstone,,,,,nocontainers,,,,nocontainers,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,,,,,,,,,,`,,` +,,,`,,`,`,"wt{name=""Starter mechanic's""}",`,,`,"wc{name=""Starter carpenter""}",`,,`,"wr{name=""Starter craftsdwarf's""}",`,,`,"wm{name=""Starter stoneworker's""}",`,,,,,,,,,,,`,,` +,,,`,,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` +,,,`,,`,,`,N,N,,,,,,`,,,,,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -622,7 +552,7 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,h1,,,,,,h1,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,h1,h1,,,,,,h1,h1,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,h1,h1,,h1,h1,h1,,h1,h1,,,,,,,,,`,,` @@ -656,7 +586,7 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,t1,t1 -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,t1,t1 +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,t1,t1 ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,t1,t1 ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,t1,t1 ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,t1,t1 @@ -690,7 +620,7 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,Cf,,,,,,Cf,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,Cf,Cf,,,,,,Cf,Cf,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,Cf,Cf,,Cf,Cf,Cf,,Cf,Cf,,,,,,,,,`,,` @@ -724,7 +654,7 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,Cw,,H,~,H,,Cw,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,Cf,,`,`,`,,Cf,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,Cw,,,,,,Cw,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,Cw,Cw,Cf,Cw,Cf,Cw,Cw,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,Cw,Cw,Cf,Cw,Cf,Cw,Cw,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -792,7 +722,7 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,d,,`,`,`,,d,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,d,`,d,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,d,`,d,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -825,14 +755,14 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,`,,,,,,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,~,~,~,~,~,~,~,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,~,~,~,~,~,~,~,Cf,Cf,Cf,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,~,~,~,~,~,`,,,Cf,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,~,`,~,`,`,`,`,Cf,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,Cf,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,Cf,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,Cf,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,Cf,`,,` +,,,`,,`,,`,,,,,Cf,Cf,Cf,~,~,~,~,~,~,~,,,,,,,,,,`,,` +,,,`,,`,,`,,,,,Cf,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,Cf,`,`,`,`,~,`,~,`,`,`,`,,,,,,,,`,,` +,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,,`,,,,,,,,`,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` ,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` @@ -856,24 +786,24 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,Cf,,Cf,,Cf,`,,,,,,,,,,`,,` -,,,`,,`,,Cw,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,Cf,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` -,,,`,,`,,Cw,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,Cf,Cf,Cf,Cf,Cf,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,Cw,Cf,Cw,`,`,`,,`,,`,`,`,Cw,Cf,Cw,`,`,`,`,`,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,~,,,,,,~,`,,,,,,,Cf,Cf,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,~,Cf,Cf,Cf,Cf,Cf,~,~,,,,,,,,Cf,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,~,,~,~,~,,~,~,,,,,Cf,Cf,,Cf,`,,` -,,,`,,`,,Cf,,Cf,Cf,Cf,Cf,Cf,,Cf,,~,~,~,,Cf,,,Cf,,,,,,Cf,`,,` -,,,`,,`,,Cf,,Cf,Cf,Cf,Cf,Cf,`,,,~,~,~,,,`,,Cf,,,,,Cf,Cf,`,,` +,,,`,,`,,`,,,,,,,,`,Cf,,Cf,,Cf,`,Cf,Cf,,Cf,Cf,Cf,Cf,Cf,,`,,` +,,,`,,`,,Cw,,,,,,,,,,`,`,`,,,Cf,Cf,,Cf,Cf,Cf,Cf,Cf,,`,,` +,,,`,,`,,Cf,,,,,,,,`,,`,`,`,,`,Cf,Cf,,Cf,Cf,Cf,Cf,Cf,,`,,` +,,,`,,`,,Cw,,,,,,,,,,`,`,`,,,Cf,Cf,,Cf,Cf,Cf,Cf,Cf,,`,,` +,,,`,,`,,`,,,,,,,,`,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,,Cf,Cf,Cf,Cf,Cf,,`,,` +,,,`,,`,`,`,`,`,`,Cw,Cf,Cw,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` +,,,`,,`,Cf,Cf,,Cf,Cf,,,,`,~,Cf,Cf,Cf,Cf,Cf,~,`,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,,,~,~,Cf,Cf,Cf,Cf,Cf,~,~,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,,,~,~,,~,~,~,,~,~,,,,,,,,,`,,` +,,,`,,`,Cf,,,,,,Cf,,Cf,Cf,,~,~,~,,Cf,Cf,,,,,,,,,`,,` +,,,`,,`,Cf,Cf,,,,,Cf,,`,,,~,~,~,,,`,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,Cf,,,,,,,,,Cf,,,,,~,,,,,Cf,,,,,,,,,Cf,` +,,,`,Cf,Cf,,,,,,,,Cf,Cf,,,,~,,,,Cf,Cf,,,,,,,,Cf,Cf,` ,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` -#place label(surface_place) start(19; 19) hidden() remaining surface stockpiles +#dig label(surface_traffic) start(19; 19) hidden() set traffic designations @@ -890,64 +820,26 @@ Feel free to assign an unimportant animal to the pasture in the main entranceway ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,a5(9x5),,,,,,,,,`,,` +,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,g(3x3),,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,c,,,,,,,`,,,,,,,,`,,c,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,or,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` +,,,`,,`,,,,,,,,or,`,or,or,ol,ol,ol,ol,,`,,,,,,,,,`,,` +,,,`,,`,,,,,,,,or,ol,ol,or,ol,ol,ol,ol,ol,ol,,,,,,,,,`,,` +,,,`,,`,,,,,,,,or,ol,ol,or,,,,,ol,ol,,,,,,,,,`,,` +,,,`,,`,,,,,,,,or,ol,ol,or,,,,,ol,ol,,,,,,,,,`,,` +,,,`,,`,,,,,,,,or,`,or,or,,,,,,`,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` - - - -"#build label(surface_build) start(19; 19) hidden() message(Use autofarm to manage farm crop selection. -Remember to connect the levers to the gates once they are built.) gates, barracks, farm area, and trade area" - - - -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,~h,`,~,~,N,N,N,N,N,`,Tl,,Tl,,Tl,`,,,,,,,,,,`,,` -,,,`,,`,~h,`,p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,~h,d,p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),`,,`,`,`,,`,,,,,,,,,,`,,` -,,,`,,`,~h,`,p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,~h,`,N,N,N,N,N,N,N,`,Tl,Tl,Tl,Tl,Tl,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,d,`,`,`,`,,`,,`,`,`,`,d,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,gw,gw,gw,gw,gw,,`,,,,,,,h,b,`,,` -,,,`,,`,,,,,,,,,ga,ga,gw,gw,gw,gw,gw,gd,gd,,,,,,,,b,`,,` -,,,`,,`,,,,,,D,,,ga,ga,,,,,,gd,gd,,,,,a,r,,b,`,,` -,,,`,,`,,trackstopS,,,,,,,ga,ga,,,,,,gd,gd,,trackstopS,,,,,,b,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,h,b,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,gd,gd,,,,,,,,gd,gd,,,,,,,,ga,ga,,,,,,,,ga,ga,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,gw,gw,gw,gw,gw,gw,gw,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,gw,gw,gw,gw,gw,gw,gw -,,,,,,,,,,,,,,,gw,gw,gw,gw,gw,gw,gw - -#aliases -"prisoner_route_enable: {enableanimals}{cages}{permittraps}{animalsprefix}{Right}{permitsearch search="" men""}{permitsearch search=dwarves}{permitsearch search=elves}{permitsearch search=humans}{permitsearch search=kobolds}{permitsearch search=gremlins}{permitsearch search=giants}{permitsearch search=goblins}{permitsearch search=ettins}{permitsearch search=cyclopes}{permitsearch search=ogres}{permitsearch search=eyes}{permitsearch search=reachers}{permitsearch search=gorlaks}{permitsearch search=trolls}{permitsearch search=minotaurs}^" +,,,`,`,`,`,`,`,`,`,`,`,`,`,ol,ol,ol,ol,ol,ol,ol,`,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,,,,,ol,ol,ol,ol,ol,ol,ol +,,,,,,,,,,,,,,,ol,ol,ol,ol,ol,ol,ol -"#query label(surface_query) start(19; 19) hidden() message(Remember to assign minecarts to the trade goods and prisoner processing quantum stockpiles (run ""assign-minecarts all""). +"#place label(surface_place) start(19; 19) hidden() message(Remember to assign minecarts to the trade goods and prisoner processing quantum stockpiles (run ""assign-minecarts all""). Feel free to adjust the configuration of the ""trade goods"" feeder stockpile so it accepts the item types you want to trade away. If those items types are also accepted by other stockpiles, configure those stockpiles to give to the ""trade goods"" stockpile. -You might also want to set the ""trade goods quantum"" stockpile to Auto Trade if you have the autotrade DFHack plugin enabled.)" +You might also want to set the ""trade goods quantum"" stockpile to autotrade.) remaining surface stockpiles" @@ -958,30 +850,32 @@ You might also want to set the ""trade goods quantum"" stockpile to Auto Trade i ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,"a{name=""Pets/Prisoner feeder""}(9x5)",,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,"{givename name=""trade depo gate""}",,"{givename name=""inner main gate""}",,"{givename name=""barracks gate""}",`,"{givename name=""prison/training area""}",,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` +,,,`,,`,,`,,,,,,,,`,,,,,,`,,"c{name=""Organic trade goods quantum"" quantum=true}:+all",,,,,,,,`,,` +,,,`,,`,,`,,,,,,,,,,`,`,`,,,"g{name=""Trade goods"" containers=0}:-cat_finished_goods/type/,core/artifact+crafts(2x3)",,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,"{givename name=""left outer gate""}","{givename name=""left inner gate""}","{givename name=""outer main gate""}","{givename name=""right inner gate""}","{givename name=""right outer gate""}",`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,nocontainers,crafts,,,,,,,`,,,,"{givename name=""inner main gate""}",,,,`,,,,,,,,,`,,` -,,,`,,`,"{givename name=""trade goods""}",,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,{forbidmasterworkfinishedgoods}{forbidartifactfinishedgoods},,,,,,,,"{givename name=""trade depo gate""}",,,,,,,,"{givename name=""barracks gate""}",,,,,,,,,`,,` -,,,`,,`,,"{quantumstopfromnorth name=""Trade Goods quantum""}",,,,,,,,,,,,,,,,,"{quantumstop name=""Prisoner/Cage quantum"" move={Up 5} move_back={Down 5} route_enable={prisoner_route_enable}}{givename name=""prisoner/cage dumper""}",,,,,,,`,,` -,,,`,,`,,"{quantum name=""trade goods quantum""}",,,,,,,`,,,,,,,,`,,"{quantum name=""prisoner/cage quantum"" quantum_enable={enableanimals}}",,,,,,,`,,` +,,,`,,`,,`,,,,,,,,`,,,,,,`,,"c{name=""Inorganic trade goods quantum"" quantum=true}:+all-organic",,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` +,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,,,,,,,"a{name=""Prisoner/cage quantum"" quantum=true}",,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,"{givename name=""left outer gate""}",,,,,,,,,"{givename name=""left inner gate""}",,,,,,,,"{givename name=""right inner gate""}",,,,,,,,,"{givename name=""right outer gate""}",,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,"{givename name=""outer main gate""}",,,,`,`,`,`,`,`,`,`,`,`,`,` +,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` +,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` -#dig label(surface_traffic) start(19; 19) hidden() set traffic designations +#aliases +"#build label(surface_build) start(19; 19) hidden() message(Use autofarm to manage farm crop selection. +Remember to connect the levers to the gates once they are built.) gates, barracks, farm area, and trade area" @@ -998,22 +892,22 @@ You might also want to set the ""trade goods quantum"" stockpile to Auto Trade i ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,or,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,ol,ol,ol,ol,ol,,`,or,,,,,,,,`,,` -,,,`,,`,,,,,,,,,ol,ol,ol,ol,ol,ol,ol,ol,ol,or,,,,,,,,`,,` -,,,`,,`,,,,,,,,,ol,ol,,,,,,ol,ol,or,,,,,,,,`,,` -,,,`,,`,,,,,,,,,ol,ol,,,,,,ol,ol,or,,,,,,,,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,or,,,,,,,,`,,` +,,,`,,`,~h{do_install=true do_gather=true}(1x1),`,~,~,N,N,N,N,N,`,"Tl{name=""Barracks gate""}",,"Tl{name=""Inner main gate""}",,"Tl{name=""Trade depo gate""}",`,"trackstopE{name=""Organic trade goods dumper"" take_from=""Trade goods"" route=""Organic trade goods quantum""}",,,~,~,~,~,~,,`,,` +,,,`,,`,~h{do_install=true do_gather=true}(1x1),`,p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),,,`,`,`,,,,,,~,~,~,~,~,,`,,` +,,,`,,`,"~h{name=""reserved for splitting"" do_install=true}(1x1)",d,p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),`,,`,`,`,,`,,,,~,~,D,~,~,,`,,` +,,,`,,`,~h{do_install=true do_gather=true}(1x1),`,p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),p(1x1),,,`,`,`,,,,,,~,~,~,~,~,,`,,` +,,,`,,`,~h{do_install=true do_gather=true}(1x1),`,N,N,N,N,N,N,N,`,"Tl{name=""Left outer gate""}(1x1)","Tl{name=""Left inner gate""}(1x1)","Tl{name=""Outer main gate""}(1x1)","Tl{name=""Right inner gate""}(1x1)","Tl{name=""Right outer gate""}(1x1)",`,"trackstopE{name=""Inorganic trade goods dumper"" take_from=""Trade goods,Organic trade goods quantum"" route=""Inorganic trade goods quantum""}:-organic",,,~,~,~,~,~,,`,,` +,,,`,,`,`,`,`,`,`,`,d,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` +,,,`,,`,b,h,,a,r,,,,`,,"gw{name=""Inner main gate""}",gw,gw,gw,gw,,`,,,,,,,,,`,,` +,,,`,,`,b,,,,,,,,"ga{name=""Barracks gate""}",ga,gw,gw,gw,gw,gw,"gd{name=""Trade depo gate""}",gd,,,,,,,,,`,,` +,,,`,,`,b,,,,,,,,ga,ga,,,,,,gd,gd,,,,,,,,,`,,` +,,,`,,`,b,,,,,,"trackstopS{name=""Prisoner/cage dumper"" take_from=""Pets/Prisoner feeder"" route=""Prisoner/cage quantum""}:-cat_animals/tameable",,ga,ga,,,,,,gd,gd,,,,,,,,,`,,` +,,,`,,`,b,h,,,,,,,`,,,,,,,,`,s,,s,,,s,,s,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,ol,ol,ol,ol,ol,ol,ol,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,ol,ol,ol,ol,ol,ol,ol -,,,,,,,,,,,,,,,ol,ol,ol,ol,ol,ol,ol +,,,`,"gd{name=""Left outer gate""}",gd,,,,,,,,"gd{name=""Left inner gate""}",gd,,,,,,,,"ga{name=""Right inner gate""}",ga,,,,,,,,"ga{name=""Right outer gate""}",ga,` +,,,`,`,`,`,`,`,`,`,`,`,`,`,"gw{name=""Outer main gate""}",gw,gw,gw,gw,gw,gw,`,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,,,,,gw,gw,gw,gw,gw,gw,gw +,,,,,,,,,,,,,,,gw,gw,gw,gw,gw,gw,gw #dig label(surface_clear_large) start(19; 19) hidden() clear wider area of trees t1(37x33) @@ -1031,51 +925,17 @@ t1(37x33) ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,`,`,,`,`,,` +,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,,`,~,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` - - - -#query label(surface_query2) start(19; 19) hidden() - - - -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,cg,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,cg,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,c,,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` -,,,`,,`,cg,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,cg,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,r&,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` @@ -1105,7 +965,7 @@ t1(37x33) ,,,`,,Cw,,,,,,,,,,~,,`,`,`,,~,,,,,,,,,,Cw,,` ,,,`,,Cw,,~,,,,,,,,,,`,`,`,,,,,,,,,,,,Cw,,` ,,,`,,Cw,,Cw,,,,,,,,~,,,,,,~,,,,,,,,,,Cw,,` -,,,`,,Cw,Cw,Cw,Cw,Cw,Cw,~,,~,Cw,~,~,,~,,~,~,Cw,~,,~,Cw,Cw,Cw,Cw,Cw,Cw,,` +,,,`,,Cw,Cw,Cw,Cw,Cw,Cw,~,,~,Cw,~,~,,~,,~,~,Cw,Cw,,,,,,,,Cw,,` ,,,`,,Cw,,,,,,,,,Cw,,,,,,,,Cw,,,,,,,,,Cw,,` ,,,`,,Cw,,,,,,,,,,,,,,,,,,,,,,,,,,Cw,,` ,,,`,,Cw,,,,,,,,,,,,,,,,,,,,,,,,,,Cw,,` @@ -1134,17 +994,17 @@ t1(37x33) ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,~,`,~,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,~,Cf,~,Cf,~,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,~,Cf,Cf,Cf,Cf,Cf,~,,,,,,,,,,`,,` -,,,`,,`,,~,,,,,,,,`,Cf,`,Cf,`,Cf,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,~,Cf,Cf,Cf,Cf,Cf,~,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,`,`,`,`,`,`,`,,` -,,,`,,`,~,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,`,Cf,Cf,Cf,Cf,Cf,Cf,~,~,`,,` -,,,`,,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,`,,` -,,,`,,`,~,~,~,~,~,~,~,~,~,~,Cf,~,~,~,Cf,~,~,Cf,Cf,Cf,Cf,~,~,Cf,~,`,,` -,,,`,,`,Cf,~,Cf,~,~,~,~,~,~,~,Cf,~,~,~,Cf,~,~,Cf,~,Cf,Cf,Cf,Cf,Cf,~,`,,` -,,,`,,`,Cf,~,Cf,~,~,~,~,~,`,Cf,Cf,~,~,~,Cf,Cf,`,Cf,~,Cf,Cf,Cf,Cf,~,~,`,,` +,,,`,,`,,`,,,,,,,,`,~,Cf,~,Cf,~,`,~,~,Cf,~,~,~,~,~,Cf,`,,` +,,,`,,`,,`,,,,,,,,~,Cf,Cf,Cf,Cf,Cf,~,~,~,Cf,~,~,~,~,~,Cf,`,,` +,,,`,,`,,~,,,,,,,,`,Cf,`,Cf,`,Cf,`,~,~,Cf,~,~,~,~,~,Cf,`,,` +,,,`,,`,,`,,,,,,,,~,Cf,Cf,Cf,Cf,Cf,~,~,~,Cf,~,~,~,~,~,Cf,`,,` +,,,`,,`,,`,,,,,,,,`,~,~,~,~,~,`,~,~,Cf,~,~,~,~,~,Cf,`,,` +,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,~,Cf,~,~,Cf,Cf,Cf,`,~,~,~,~,~,~,~,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,~,~,~,~,~,~,~,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,~,Cf,~,~,~,Cf,~,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,~,Cf,~,~,Cf,~,~,~,Cf,~,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,~,Cf,Cf,Cf,Cf,~,Cf,`,Cf,Cf,~,~,~,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,`,`,`,`,`,`,`,`,`,,` ,,,`,~,,,,,,,,,,,Cf,Cf,Cf,~,Cf,Cf,Cf,,,,,,,,,,,~,` ,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` @@ -1153,8 +1013,8 @@ t1(37x33) #build label(surface_traps) start(19; 19) hidden() -,,,,,Tc,Tc,,,,,,,,,,,,,,,,,,,,,,,,Tc,Tc -,,,,,Tc,Tc,,,,,,,,,,,,,,,,,,,,,,,,Tc,Tc +,,,,,Tc,,,,,,,,,,,,,,,,,,,,,,,,,,Tc +,,,,,Tc,,,,,,,,,,,,,,,,,,,,,,,,,,Tc ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -1173,7 +1033,7 @@ t1(37x33) ,,,`,,`,,,,,,,,,,`,,`,`,`,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -1205,14 +1065,14 @@ t1(37x33) ,,,`,,`,,`,,,,,,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,~,~,~,~,~,~,~,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,~,~,~,~,~,~,~,~,~,~,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,~,~,~,~,~,`,,,~,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,~,`,~,`,`,`,`,~,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,~,~,~,~,~,~,~,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,~,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,~,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,~,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,~,`,,` +,,,`,,`,,`,,,,,~,~,~,~,~,~,~,~,~,~,,,,,,,,,,`,,` +,,,`,,`,,`,,,,,~,,,`,~,~,~,~,~,`,,,,,,,,,,`,,` +,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,,,,,,,,`,,` +,,,`,,`,~,~,~,~,~,~,~,,`,,,,,,,,`,,,,,,,,,`,,` +,,,`,,`,~,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,~,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,~,,,,,,,,,,,,,,,,,,,,,,,,,`,,` +,,,`,,`,~,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` ,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` @@ -1239,14 +1099,14 @@ t1(37x33) ,,,`,,`,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,~,~,~,~,~,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` ,,,`,,`,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,~,~,~,~,~,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` ,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,~,~,~,~,~,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` -,,,`,,`,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,~,~,~,~,~,~,~,~,~,Cf,Cf,Cf,Cf,Cf,Cf,`,,` -,,,`,,`,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,~,~,~,~,~,`,Cf,Cf,~,Cf,Cf,Cf,Cf,Cf,Cf,`,,` -,,,`,,`,`,`,`,`,`,`,Cf,`,`,`,`,~,`,~,`,`,`,`,~,`,`,`,`,`,`,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,~,~,~,~,~,~,~,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,`,,` -,,,`,,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,~,`,,` +,,,`,,`,Cf,`,Cf,Cf,Cf,Cf,~,~,~,~,~,~,~,~,~,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,Cf,`,Cf,Cf,Cf,Cf,~,Cf,Cf,`,~,~,~,~,~,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,~,~,~,~,~,~,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` +,,,`,,`,~,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,,` ,,,`,,`,`,`,`,`,`,`,`,`,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,Cf,,` ,,,`,`,`,`,`,`,`,`,`,`,`,`,Cf,Cf,Cf,Cf,Cf,Cf,Cf,`,`,`,`,`,`,`,`,`,`,`,` @@ -1275,7 +1135,7 @@ t1(37x33) ,,,`,,`,~,~,~,~,~,~,~,~,~,`,~,~,~,~,~,`,~,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,`,~,~,~,~,~,~,~,`,~,~,~,~,~,`,~,~,~,~,~,~,~,~,~,`,,` -,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,,` @@ -1309,7 +1169,7 @@ t1(37x33) ,,,`,,`,~,~,~,~,~,~,~,~,~,`,~,~,~,~,~,`,~,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,`,~,~,~,~,~,~,~,`,~,~,~,~,~,`,~,~,~,~,~,~,~,~,~,`,,` -,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,,` ,,,`,,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,,` @@ -1321,10 +1181,10 @@ t1(37x33) -#build label(surface_corridor_gates) start(19; 19) hidden() gates for the longer trap hallways +#build label(surface_corridor_gates) start(19; 19) hidden() message(Remember to connect the levers to the new external trap gates.) gates for the longer trap hallways -,,,,gx,,,,,,,,,,,,,,,,,,,,,,,,,,,,gx +,,,,"gx{name=""Left trap gate""}",,,,,,,,,,,,,,,,,,,,,,,,,,,,"gx{name=""Right trap gate""}" ,,,`,gx,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,gx,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -1340,10 +1200,10 @@ t1(37x33) ,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,`,Tl,`,`,`,Tl,`,,,,,,,,,,`,,` +,,,`,,`,,,,,,,,,,`,"Tl{name=""Left trap gate""}",`,`,`,"Tl{name=""Right trap gate""}",`,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` ,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` +,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,` ,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` ,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` @@ -1377,7 +1237,7 @@ t1(37x33) ,,,Cw,,`,,,,,,,,,,`,~,`,`,`,~,`,,,,,,,,,,`,,Cw ,,,Cw,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,Cw ,,,Cw,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,Cw -,,,Cw,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,Cw +,,,Cw,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,,Cw ,,,Cw,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,Cw ,,,Cw,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,Cw ,,,Cw,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,Cw @@ -1411,7 +1271,7 @@ t1(37x33) ,,,`,Cf,`,~,~,~,~,~,~,~,~,~,`,~,~,~,~,~,`,~,~,~,~,~,~,~,~,~,`,Cf,` ,,,`,Cf,`,~,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,Cf,` ,,,`,Cf,`,~,`,~,~,~,~,~,~,~,`,~,~,~,~,~,`,~,~,~,~,~,~,~,~,~,`,Cf,` -,,,`,Cf,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,`,`,`,`,`,`,`,Cf,` +,,,`,Cf,`,`,`,`,`,`,`,~,`,`,`,`,~,`,~,`,`,`,`,~,~,~,~,~,~,~,`,Cf,` ,,,`,Cf,`,~,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,`,~,~,~,~,~,~,~,~,`,Cf,` ,,,`,Cf,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,Cf,` ,,,`,Cf,`,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,`,Cf,` @@ -1426,8 +1286,8 @@ t1(37x33) #build label(surface_corridor_traps) start(19; 19) hidden() traps for the longer trap hallways -,,,,~,,,,,,,,,,,,,,,,,,,,,,,,,,,,~ -,,,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,~,` +,,Tc,Tc,~,,,,,,,,,,,,,,,,,,,,,,,,,,,,~,Tc,Tc +,,Tc,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,Tc ,,,`,Tc,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,Tc,` ,,,`,Tc,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,Tc,` ,,,`,Tc,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,Tc,` @@ -1445,7 +1305,7 @@ t1(37x33) ,,,`,Tc,`,,,,,,,,,,`,~,`,`,`,~,`,,,,,,,,,,`,Tc,` ,,,`,Tc,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,Tc,` ,,,`,Tc,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,Tc,` -,,,`,Tc,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,Tc,` +,,,`,Tc,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,,,,,,,`,Tc,` ,,,`,Tc,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,Tc,` ,,,`,Tc,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,Tc,` ,,,`,Tc,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,Tc,` @@ -1453,42 +1313,8 @@ t1(37x33) ,,,`,Tc,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,Tc,` ,,,`,Tc,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,Tc,` ,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` - - - -#query label(surface_query_corridor) start(19; 19) hidden() (Remember to connect the levers to the new external trap gates.) configure barracks and name outer levers/gates - - - -,,,`,"{givename name=""left trap gate""}",`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,"{givename name=""right trap gate""}",` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,`,"{givename name=""left trap gate""}",`,`,`,"{givename name=""right trap gate""}",`,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,,,`,`,`,,,,,,,,,,,,`,,` -,,,`,,`,,`,,,,,,,,`,,,,,,`,,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,,`,`,`,`,,`,,`,`,`,`,,`,`,`,`,`,`,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,,,,,,,,,,,,,,,,,,`,,` -,,,`,,`,,,,,,,,,`,,,,,,,,`,,,,,,,,,`,,` -,,,`,,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,,` -,,,`,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` -,,,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` - +,,Tc,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,Tc +,,Tc,Tc,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Tc,Tc #notes label(farming_help) @@ -1497,7 +1323,7 @@ Screenshot: https://drive.google.com/file/d/1fBC3G5Y888l4tVe5REAyAd_zeojADVme "" Features: - Pairs with the surface blueprints for vents that prevent miasma -- Farm plots (intended to be managed by DFHack autofarm) +- Farm plots (can be managed by DFHack autofarm) - Plentiful food storage - Refuse/corpse quantum stockpile - Small dormitory and dining room for post-embark needs @@ -1514,25 +1340,21 @@ Workshops: - Screw Press "" Manual steps you have to take: -- Check to make sure the lower office is assigned to your manager and assign the upper office to your bookkeeper (if different from your manager) "- Assign a minecart to your refuse quantum stockpile hauling route (you can run ""assign-minecarts all"" at the DFHack prompt to do this)" -"- If the industry level is already built, configure the jugs, pots, and bags stockpiles to take from the ""Goods"" quantum stockpile (the one on the left) on the industry level" "" Farming Walkthrough: "1) Wait until you have channeled the miasma vents and cleared trees on the surface before digging out the farming level on the z-level below the surface, otherwise you will end up with extra ramps on the farming level and unprotected holes through the surface when you later chop down trees growing above empty space." "" -"2) Start digging with /farming1 and get started on manufacturing furniture by running ""quickfort orders"" on /farming2 and /farming3." +"2) Start digging with /farming1 and get started on manufacturing furniture by running ""quickfort orders"" on /farming2" "" -"3) Once the level is dug out, run /farming2 to build workshops, stockpiles, and the furniture we need to anchor the rooms. Remember to assign a minecart to the newly-designated quantum refuse dump. There are also jugs, pots, and bags stockpiles on this level that should be configured to ""take"" from the industry level stockpile once we get the industry level built." +"3) Once the level is dug out, run /farming2 to designate zones, build workshops and stockpiles, and place furniture. Remember to assign a minecart to the newly-designated quantum refuse dump." "" -"4) When the furniture is in place, run /farming3 to designate your starter dining room and dormitory and build the farm plots and remaining furniture. The blueprint also attempts to assign the lower office to your manager, but double-check this assignment in case your dwarves are in an unexpected order." +"4) Once your fort has enough free time to build the remaining doors, run /farming3. Run ""quickfort orders"" for /farming3." "" -"5) Once your fort has enough free time to build the remaining doors, run /farming4. This will also enable seasonal fertilization for your farm plots. Run ""quickfort orders"" for /farming4." -"" -"6) You can disassemble the dining room and dormitory once the services and apartments levels are up and running, if you like." +"5) You can disassemble the dining room and dormitory once the services and apartments levels are up and running, if you like." "#dig label(farming1) start(16; 18; central stairs) message(Once the area is dug out, continue with /farming2.)" -# this level is dug at priority 2 since it is dug in soil. it's worth the miner's time to stop digging the industry level and -# quickly dig out this one. +# this level is dug at priority 2 since it is dug in soil. it's worth the miner's time to +# stop digging the industry level and quickly dig out this one. ,,,,,,,,,2,2,2,,2,2,2,2,2,,2,2,2,2 ,,,,,,,,,2,2,2,,2,2,2,2,2,,2,2,2,2 ,,,,,,,,,2,2,2,,2,2,2,2,2,,2,2,2,2 @@ -1563,35 +1385,29 @@ Farming Walkthrough: "#meta label(farming2) start(central stairs) message(Remember to enqueue manager orders for this blueprint. Once furniture has been placed, continue with /farming3.) workshops, stockpiles, and important furniture" -build/farming_build +zone/farming_zone place/farming_place +build/farming_build traffic/farming_traffic -query_stockpiles/farming_query_stockpiles -link_stockpiles/farming_link -"" -#meta label(farming3) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) configure rooms and build farm plots and more furniture -query_rooms/farming_rooms -build2/farming_build2 "" -#meta label(farming4) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) configure farm plots and build remaining furniture -query_plots/farming_query_plots -build3/farming_build3 -#build label(farming_build) start(16; 18) hidden() workshops and important furniture +#meta label(farming3) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) build remaining doors +doors/farming_doors +#zone label(farming_zone) start(16; 18) hidden() rooms -,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,,,c,t,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,t,c +,,,,,,,,,T{pets=true}(1x1),`,`,,`,`,`,`,`,,"h{name=""Starter dining hall""}(4x6)",,,` +,,,,,,,,,T{pets=true}(1x1),`,`,,`,`,`,`,`,,`,`,`,` +,,,,,,,,,T{pets=true}(1x1),`,`,,`,`,`,`,`,,`,`,`,` ,,,,,,,,,,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,c,t,`,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,` -,,,,,,,,,,,`,,`,`,`,`,`,,`,,`,`,b +,,,,,,,"o{name=""Manager's office"" assigned_unit=manager}(3x1)",,`,,`,,`,`,`,`,`,,`,`,`,` +,,,,,,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,` +,,,,,,,"o{name=""Bookkeeper's office"" assigned_unit=bookkeeper}(3x1)",,`,,`,,`,`,`,`,`,,` +,,,,,,,,,,,`,,`,`,`,`,`,,`,,"D{name=""Starter dormitory""}(3x3)",,` ,,,,,,`,`,`,`,,`,,`,`,`,`,`,,`,`,`,`,` -,,,,,,`,wl,`,`,,`,,`,`,`,`,`,,`,,`,`,` +,,,,,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,` ,,,`,`,,`,`,`,`,,`,,`,`,`,`,`,,` -,,wq,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,wp,`,`,`,`,ww,`,`,`,`,,,`,,`,,,`,`,`,wu,`,`,`,wz,` +,,`,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` +,,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` ,,,`,`,,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` ,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` ,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,` @@ -1602,40 +1418,71 @@ build3/farming_build3 ,,`,`,`,`,`,`,`,`,,`,`,,,`,,,`,`,,`,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,,`,`,`,,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,wh,`,,`,`,`,,`,wn,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,,trackstopS,,,`,`,`,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,`,`,`,,,`,,,`,`,`,,`,`,`,`,`,`,` ,,,,,,,,,,,,,,,` -#place label(farming_place) start(16; 18) hidden() stockpiles +"#place label(farming_place) start(16; 18) hidden() message(remember to assign a minecart to the refuse quantum stockpile (run ""assign-minecarts all"")" -,,,,,,,,,`,`,`,,`,`,`,f10(1x9),b(1x12),,f(4x2),,,` +,,,,,,,,,`,`,`,,`,`,`,"c{name=""Seeds"" barrels=10 links_only=true take_from=""Starting food""}:+seeds(1x9)","c{name=""Potash""}:+potash(1x12)",,"c{name=""booze"" barrels=-1 take_from=""Starting food""}:+booze(4x2)",,,` ,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` ,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` ,,,,,,,,,,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,`,f(3x2),,` +,,,,,,,`,`,`,,`,,`,`,`,`,`,,`,"c{name=""Prepared food"" take_from=""Starting food""}:+preparedmeals(3x2)",,` ,,,,,,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,` ,,,,,,,`,`,`,,`,,`,`,`,`,`,,` ,,,,,,,,,,,`,,`,`,`,`,`,,`,,`,`,` -,,,,,,`,`,`,u,,`,,`,`,`,`,`,,`,`,`,`,` -,,,,,,`,`,`,u,,`,,f(4x3),,,`,`,,`,,`,`,` -,,,u,u,,`,`,`,u,,`,,`,`,`,`,`,,` -,,`,u,u,,`,`,`,u,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,`,g,g,`,`,`,`,u,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` -,,,g,g,,`,`,`,u,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` +,,,,,,`,`,`,"u{name=""Pots"" take_from=""Starting misc""}:-cat_furniture/type+pots(1x4)",,`,,`,`,`,`,`,,`,`,`,`,` +,,,,,,`,`,`,~,,`,,"c{name=""Seeds feeder"" give_to=""Seeds"" take_from=""Starting food""}:+seeds(4x3)",,,,`,,`,,`,`,` +,,,"u{name=""Bags"" take_from=""Starting misc""}:-cat_furniture/type+bags(2x2)",~,,`,`,`,~,,`,,`,`,`,`,`,,` +,,`,~,~,,`,`,`,~,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` +,,`,"c{name=""Jugs"" take_from=""Starting misc""}:+cat_finished_goods/core,total+woodtools(2x2)",~,`,`,`,`,"u{name=""Barrels"" take_from=""Starting misc,Starting food""}:-cat_furniture/type+barrels(1x2)",`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` +,,,~,~,,`,`,`,~,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` +,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` +,,"c{name=""Plants"" barrels=-1 take_from=""Starting food""}:+plants",c,c,c,c,c,c,c,c,c,,`,`,~,`,`,,"c{name=""Cookable food"" barrels=-1 take_from=""Starting food""}:+cat_food/meat/,fish/prepared/,egg/,cheese/,leaves/,powder/,glob/,liquid/plant/,paste/,pressed/,milk,royal_jelly-dye-cat_food/tallow,thread,liquid/misc/",c,c,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,c,c,c,`,`,`,`,`,`,`,c,c,c,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,c,c,c,,`,`,`,`,`,,c,c,c,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,c,c,,,,`,,`,,,,c,c,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,c,,,c,`,`,`,`,`,c,,,c,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,c,,"c{name=""Unprepared fish"" take_from=""Starting food""}:+unpreparedfish",c,,,`,,,"c{name=""Rawhides"" take_from=""Starting cloth/trash""}:+rawhides",c,,c,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,,,c,c,,"c{name=""Refuse feeder"" give_to=""Rawhides"" take_from=""Starting cloth/trash""}:+cat_refuse/type(1x3)","y{name=""Corpse feeder"" take_from=""Starting cloth/trash""}:+cat_refuse/corpses,bodyparts(2x3)",~,,c,c,,,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,,`,`,`,,~,~,~,,`,`,`,,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,,`,`,`,,~,~,~,,`,`,`,,c,c,c,c,c,c,c +,,c,c,c,c,c,c,c,,`,`,`,,,`,,,`,`,`,,c,c,c,c,c,c,c +,,,,,,,,,,,,,,,"ry{name=""Refuse/corpse quantum"" give_to=""Rawhides"" quantum=true}" + + +#build label(farming_build) start(16; 18) hidden() workshops and important furniture + + +,,,,,,,,,n,`,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,`,`,`,` +,,,,,,,,,n,`,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,`,`,`,` +,,,,,,,,,n,`,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,c,t,t,c +,,,,,,,,,,,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,c,t,t,c +,,,,,,,t,c,`,,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,`,`,`,` +,,,,,,,`,`,`,`,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,`,`,`,` +,,,,,,,t,c,`,,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,` +,,,,,,,,,,,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,`,,b,b,b +,,,,,,`,`,`,`,,`,,p{seasonal_fertilize=true}(3x1),,,`,`,,`,`,`,`,h +,,,,,,`,wl,`,`,,`,,`,`,`,`,`,,`,,b,b,b +,,,`,`,,`,`,`,`,,`,,`,`,`,`,`,,` +,,wq,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` +,,wp,`,`,`,`,ww,`,`,`,`,,,`,,`,,,`,`,`,wu,`,`,`,wz,` +,,,`,`,,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` ,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` -,,f,f,f,f,f,f,f,f,f,f,,`,`,~,`,`,,f,f,f,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,f,f,f,`,`,`,`,`,`,`,f,f,f,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,f,f,f,,`,`,`,`,`,,f,f,f,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,f,f,,,,`,,`,,,,f,f,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,f,,,f,`,`,`,`,`,r,,,f,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,f,,f,f,,,`,,,r,r,,f,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,,,f,f,,r(2x3),,ry2(1x3),,r,r,,,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,,`,`,`,,`,`,`,,`,`,`,,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,,`,`,`,,`,`,`,,`,`,`,,f,f,f,f,f,f,f -,,f,f,f,f,f,f,f,,`,`,`,,,`,,,`,`,`,,f,f,f,f,f,f,f -,,,,,,,,,,,,,,,ry +,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,,,,`,,`,,,,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,,,`,d,`,`,`,d,`,,,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,,`,`,,,d,,,`,`,,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,,`,`,`,,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,`,wh,`,,`,`,`,,`,wn,`,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,`,`,`,,,"trackstopS{name=""Refuse/corpse dumper"" take_from=""Refuse feeder,Corpse feeder"" route=""Refuse/corpse quantum""}",,,`,`,`,,`,`,`,`,`,`,` +,,,,,,,,,,,,,,,` #dig label(farming_traffic) start(16; 18) hidden() keep hungry dwarves away from the crops and food stores so they prefer the prepared meals @@ -1652,181 +1499,24 @@ build3/farming_build3 ,,,,,,ol,ol,ol,ol,,ol,,or,or,or,or,or,,ol,ol,ol,ol,ol ,,,,,,ol,ol,ol,ol,,ol,,or,or,or,or,or,,ol,,ol,ol,ol ,,,ol,ol,,ol,ol,ol,ol,,ol,,or,or,or,or,or,,ol -,,ol,ol,ol,,ol,ol,ol,ol,,ol,,or,or,or,or,or,,ol,,ol,ol,ol,,ol,ol,ol -,,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,or,,or,,,ol,ol,ol,ol,ol,ol,ol,ol,ol -,,,ol,ol,,ol,ol,ol,ol,,ol,ol,ol,ol,ol,ol,ol,ol,ol,,ol,ol,ol,,ol,ol,ol +,,ol,ol,ol,,ol,ol,ol,ol,,ol,,or,or,or,or,or,,ol,,or,or,or,,or,or,or +,,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,or,,or,,,ol,or,or,or,or,ol,or,or,or +,,,ol,ol,,ol,ol,ol,ol,,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,or,or ,,,,or,,,or,,,,,,ol,`,`,`,ol,,,,,or,,,,or ,,or,or,or,or,or,or,or,or,or,or,,ol,`,~,`,ol,,or,or,or,or,or,or,or,or,or,or ,,or,or,or,or,or,or,or,or,or,or,or,ol,`,`,`,ol,or,or,or,or,or,or,or,or,or,or,or ,,or,or,or,or,or,or,or,or,or,or,,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,or,or,,,,ol,,ol,,,,or,or,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,or,,,ol,ol,ol,ol,ol,ol,ol,,,or,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,or,,ol,ol,,,ol,,,ol,ol,,or,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,,,ol,ol,,ol,ol,ol,,ol,ol,,,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,or,or,or,or -,,or,or,or,or,or,or,or,,ol,ol,ol,,,ol,,,ol,ol,ol,,or,or,or,or,or,or,or -,,,,,,,,,,,,,,,ol - +,,or,or,or,or,or,or,or,or,or,,,,or,,or,,,,or,or,or,or,or,or,or,or,or +,,or,or,or,or,or,or,or,or,,,or,or,or,or,or,or,or,,,or,or,or,or,or,or,or,or +,,or,or,or,or,or,or,or,or,,or,or,,,or,,,or,or,,or,or,or,or,or,or,or,or +,,or,or,or,or,or,or,or,,,or,or,,or,or,or,,or,or,,,or,or,or,or,or,or,or +,,or,or,or,or,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or,or,or,or,or +,,or,or,or,or,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or,or,or,or,or +,,or,or,or,or,or,or,or,,or,or,or,,,or,,,or,or,or,,or,or,or,or,or,or,or +,,,,,,,,,,,,,,,or -"#query label(farming_query_stockpiles) start(16; 18) hidden() message(remember to: -- assign a minecart to the refuse quantum stockpile (run ""assign-minecarts all"") -- if the industry level is already built, configure the jugs, pots, and bags stockpiles to take from the ""Goods"" quantum stockpile on the industry level) config stockpiles" - -,,,,,,,,,`,`,`,,`,`,`,seeds,potash,,booze,,,` -,,,,,,,,,`,`,`,,`,`,`,linksonly,nocontainers,,"{givename name=""booze""}",`,`,` -,,,,,,,,,`,`,`,,`,`,`,"{givename name=""seeds""}","{givename name=""potash""}",,`,`,`,` -,,,,,,,,,,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,`,preparedfood -,,,,,,,`,`,`,`,`,,`,`,`,`,`,,`,"{givename name=""prepared food""}",`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,` -,,,,,,,,,,,`,,`,`,`,`,`,,`,,`,`,` -,,,,,,`,`,`,pots,,`,,`,`,`,`,`,,`,`,`,`,` -,,,,,,`,`,`,"{givename name=""pots""}",,`,,seeds,nocontainers,"{givename name=""seeds feeder""}",give2up,`,,`,,`,`,` -,,,bags,,,`,`,`,`,,`,,`,`,`,`,`,,` -,,`,nocontainers,"{givename name=""bags""}",,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,`,woodentools,,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` -,,,nocontainers,"{givename name=""jugs""}",,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` -,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` -,,plants,,,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,,,,,,,"{givename name=""cookable food""}" -,,`,,,,"{givename name=""plants""}",`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,forbidplants,,,,,`,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,forbidtallow,,,,,`,` -,,`,`,`,`,`,`,`,`,`,,,,`,,`,,,,`,`,forbiddye,,,,`,`,` -,,`,`,`,`,`,`,`,`,,,unpreparedfish,,,,,,rawhides,,,`,forbidunpreparedfish,,,,,,` -,,`,`,`,`,`,`,`,`,,`,nocontainers,,,`,,,"{givename name=""rawhides""}",`,,`,forbidmiscliquid,,,,,,` -,,`,`,`,`,`,`,`,,,`,"{givename name=""unprepared fish""}",,forbidcraftrefuse,"{givename name=""refuse feeder""}",corpses,,t{Left 3}{Down 4}&,`,,,forbidpreparedfood,,,,,,` -,,`,`,`,`,`,`,`,,`,`,`,,forbidcorpses,"{give move=""{Right 3}{Up}""}","{givename name=""corpse feeder""}",,`,`,`,,forbidbooze,,,,,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,forbidseeds,,,,,`,` -,,`,`,`,`,`,`,`,,`,`,`,,,"{quantumstop name=""Refuse/Corpse quantum"" sp_links=""{sp_link move={Up} move_back={Down}}{sp_link move=""""{Right}{Up}"""" move_back=""""{Down}{Left}""""}""}{givename name=""refuse/corpse dumper""}",,,`,`,`,,forbidwax,,,,`,`,` -,,,,,,,,,,,,,,,"{quantum name=""refuse/corpse quantum""}" - - -#query label(farming_link) start(16; 18) hidden() set farming stockpiles to take from starting surface stockpiles - - -,,,,,,,,,`,`,`,,`,`,`,t{Down 6}{Left 10}<&,`,,t{Down 6}{Left 13}<&,`,`,` -,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,,,,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,`,t{Down 2}{Left 14}<&,`,` -,,,,,,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,` -,,,,,,,,,,,`,,`,`,`,`,`,,`,,`,`,` -,,,,,,`,`,`,t{Up 2}{Right 11}<&,,`,,`,`,`,`,`,,`,`,`,`,` -,,,,,,`,`,`,`,,`,,t{Up 3}{Left 7}<&,`,`,`,`,,`,,`,`,` -,,,t{Up 4}{Right 17}<&,`,,`,`,`,`,,`,,`,`,`,`,`,,` -,,`,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,`,t{Up 6}{Right 17}<&,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` -,,,`,`,,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` -,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` -,,t{Up 9}{Right 4}<&,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,t{Up 9}{Left 13}<&,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,,,,`,,`,,,,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,,t{Up 13}{Left 6}<&,`,`,`,`,`,`,,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,`,`,,,`,,,`,`,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,,t{Up 15}{Right 10}<&,`,`,,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,,`,,,`,`,`,,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,` - - -#query label(farming_rooms) start(16; 18) hidden() message(Check to ensure the lower office got assigned to your manager and assign the upper office to your bookkeeper (if different from your manager).) configure rooms - - -,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,,,r&a+&,,,,`,`,`,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,r+&h -,,,,,,,,,,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,r&a+&,,,`,`,,`,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,`,`,`,`,`,,` -,,,,,,,,,,,`,,`,`,`,`,`,,`,,`,`,r&d -,,,,,,`,`,`,`,,`,,`,`,`,`,`,,`,`,`,`,` -,,,,,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,` -,,,`,`,,`,`,`,`,,`,,`,`,`,`,`,,` -,,`,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` -,,,`,`,,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` -,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,,,,`,,`,,,,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,`,`,,,`,,,`,`,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,,`,`,`,,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,,`,,,`,`,`,,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,` - - -"#build label(farming_build2) start(16; 18) hidden() farm plots, remaining furniture, and important doors" - - -,,,,,,,,,`,`,`,,p(3x1),,,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,p(3x1),,,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,p(3x1),,,`,`,,c,t,~,~ -,,,,,,,,,,,`,,p(3x1),,,`,`,,c,t,t,c -,,,,,,,`,`,`,,`,,p(3x1),,,`,`,,`,`,`,` -,,,,,,,`,`,`,`,`,,p(3x1),,,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,p(3x1),,,`,`,,` -,,,,,,,,,,,`,,p(3x1),,,`,`,,`,,b,b,~ -,,,,,,`,`,`,`,,`,,p(3x1),,,`,`,,`,`,`,`,h -,,,,,,`,`,`,`,,`,,`,`,`,`,`,,`,,b,b,b -,,,`,`,,`,`,`,`,,`,,`,`,`,`,`,,` -,,`,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` -,,,`,`,,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` -,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,,,,`,,`,,,,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,,`,d,`,`,`,d,`,,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,`,`,,,d,,,`,`,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,,`,`,`,,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,,`,,,`,`,`,,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,` - - -#query label(farming_query_plots) start(16; 18) hidden() configure farm plots for seasonal fertilization - - -,,,,,,,,,`,`,`,,s,`,`,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,s,`,`,`,`,,`,`,`,` -,,,,,,,,,`,`,`,,s,`,`,`,`,,`,`,`,` -,,,,,,,,,,,`,,s,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,s,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,`,`,,s,`,`,`,`,,`,`,`,` -,,,,,,,`,`,`,,`,,s,`,`,`,`,,` -,,,,,,,,,,,`,,s,`,`,`,`,,`,,`,`,` -,,,,,,`,`,`,`,,`,,s,`,`,`,`,,`,`,`,`,` -,,,,,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,` -,,,`,`,,`,`,`,`,,`,,`,`,`,`,`,,` -,,`,`,`,,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,` -,,,`,`,,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,,`,`,` -,,,,`,,,`,,,,,,`,`,`,`,`,,,,,`,,,,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,,,,`,,`,,,,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,,`,`,,,`,,,`,`,,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,,`,`,`,,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,`,`,,,`,,,`,`,`,,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,` - - -#build label(farming_build3) start(16; 18) hidden() remaining doors +#build label(farming_doors) start(16; 18) hidden() remaining doors ,,,,,,,,,`,`,`,,`,`,`,`,`,,`,`,`,` @@ -1862,18 +1552,18 @@ Sets up workshops for all non-farming industries Screenshot: https://drive.google.com/file/d/1emMaHHCaUPcdRbkLQqvr-0ZCs2tdM5X7 "" Features: -- Space-efficient layout for all workshops +- Compact layout that covers all workshops - Manager orders that automate basic fortress maintenance "- Space available underneath the forge and smelters for magma, allowing the starting forge and smelters to be eventually replaced by magma versions." - Quantum stockpiles for compact storage with separate stockpiles for: - A reserve of uncut gems for strange moods that the jeweler's workshop cannot take from -"- Wood, steel bars, and coal so you can see at a glance if you're low on stock" -- Items that cannot be quantum stockpiled (e.g. lye and sand bags) +"- Wood, iron bars, steel bars, flux, and coal so you can see at a glance if you're low on stock" +"- Items that cannot be quantum stockpiled (e.g. lye, dye, and sand bags)" - Meltable weapons and armor "" Workshops: -- 2x Mason +- 2x Stonecutter - 4x Craftsdwarf - 1x Jeweler - 1x Mechanic @@ -1890,33 +1580,29 @@ Workshops: - 1x Dyer - 1x Loom - 1x Clothier - "" Manual steps you have to take: -- Assign minecarts to your quantum stockpile hauling routes -"- Give from the ""Goods"" quantum stockpile to the jugs, pots, and bags stockpiles on the farming level" +- Assign minecarts to your quantum stockpile hauling routes. "" Optional manual steps you can take: -- Restrict the Mechanic's workshop to only allow skilled workers so unskilled trap-resetters won't be tasked to build mechanisms. -"- Restrict the Craftsdwarf's workshops to only allow labors that take from the adjacent stockpiles. That is, only allow Woodcrafting for the Craftsdwarf's workshop on the left near the wood stockpile, Stonecrafting and Strand Extraction for the Craftsdwarf's workshop near the Mason's workshops, and Bonecrafting for one of the Craftsdwarf's workshop near the Clothier's workshop. The last Craftdwarf's workshop can hold all the remaining labors, or it can be a secondary workshop for a labor that you want more dwarves working on." "- To encourage your masons to concentrate on building blocks and other high-volume orders, it helps to set one of your Mason's workshops to service a maximum of 2 manager orders at a time." "- Once you have enough haulers, you can increase the rate of stone and ore hauling jobs by building more wheelbarrows and adding them to the stone and ore feeder stockpiles." -"- If desired, set one or both stockpiles in the bottom left to auto-melt. This results in melting all weapons and armor that are inferior to masterwork. This is great for upgrading your military, but it takes a *lot* of fuel unless you have first replaced the forge and smelters with magma versions. If you enable automelt and you don't have magma forges and magma smelters, be sure to be in a heavily forested area, enable auto-chop, and keep your coal stocks high." +"- If desired, set one or both stockpiles in the bottom left to auto-melt. This results in melting all weapons and armor that are inferior to masterwork. This is great for upgrading your military, but it takes a *lot* of fuel unless you have first replaced the forge and smelters with magma versions. If you enable automelt and you don't have magma forges and magma smelters, be sure to be in a heavily forested area, set up autochop, and keep your coal stocks high." "" Industry Walkthrough: -"1) Start digging out /industry1 as soon as you find a stone layer at least two layers beneath the surface so the boulders can be used by your starting workshops. The services level is intended to be dug beneath this one, and there is space on that level to route magma underneath your furnaces so you can replace the furnaces on this level with magma-powered equivalents." +"1) Start digging out /industry1 as soon as you find a non-aquifer stone layer at least two layers beneath the surface so the boulders can be used by your starting workshops. The services level is intended to be dug beneath this one, and there is space on that level to route magma underneath your furnaces so you can replace the furnaces on this level with magma-powered equivalents." "" -"2) Queue up manufacturing by running ""quickfort orders"" on /industry2. You brought an anvil with you (right??), so you can remove the unneeded anvil work order from the manager orders screen (j-m). Note that stockpiles that accept containers may claim the barrels you need to build the Dyer's Workshop and Ashery. If you see those two buildings not being constructed, build a few extra barrels or run combine-plants and combine-drinks to free up some existing ones." +"2) Queue up manufacturing by running ""quickfort orders"" on /industry2. You brought an anvil with you (right??), so you can remove the unneeded anvil work order from the manager orders screen. Note that stockpiles that accept containers may claim the barrels you need to build the Dyer's Workshop and Ashery. If you see those two buildings not being constructed, build a few extra barrels or run combine all to free up some existing ones." "" -"3) Once the area is dug out, run /industry2. Remember to assign minecarts to to your quantum stockpile hauling routes, and if the farming level is already built, give from the ""Goods"" quantum stockpile (the one on the left) to the jugs, pots, and bags stockpiles on the farming level." +"3) Once the area is dug out, run /industry2. Remember to assign minecarts to to your quantum stockpile hauling routes." "" -"4) Once you have enough dwarves to do maintenance tasks (that is, after the first or second migration wave), run ""orders import library/basic"" to use the provided basic.json to take care of your fort's basic needs, such as food, booze, and raw material processing." +"4) Once you have enough dwarves to do maintenance tasks (that is, after the first or second migration wave), run ""orders import library/basic"" to use the provided manager orders to take care of your fort's basic needs, such as food, booze, and raw material processing." "" "5) If you want to automatically melt goblinite and other low-quality weapons and armor, mark the south-east stockpiles for auto-melt. If you don't have a high density of trees to make into charcoal, though, be sure to route magma to the level beneath this one and replace the forge and furnaces with magma equivalents." "" "6) Once you have magma furnaces (or abundant fuel) and more dwarves, run ""orders import library/furnace"", ""orders import library/military"", and ""orders import library/smelting"" to import the remaining fort automation orders. The military orders are optional if you are not planning to have a military, of course." "" -"7) At any time, feel free to build extra workshops or designate custom stockpiles in the unused space in the top and bottom right. The space is there for you to use!" +"7) At any time, feel free to build extra workshops or designate custom stockpiles in the remaining open space. The space is there for you to use! Note the area in the bottom right can be used for additional magma-powered furnaces and workshops since there is space reserved for magma in those spots in the level beneath." "#dig label(industry1) start(18; 18; central stairs) message(Once the area is dug out, continue with /industry2.)" @@ -1953,12 +1639,12 @@ Industry Walkthrough: ,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d,d -"#meta label(industry2) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) build workshops and stockpiles, configure stockpiles" +#meta label(industry2) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) workshops and stockpiles traffic/industry_traffic -build/industry_build place/industry_place -query/industry_query -#dig label(industry_traffic) start(18; 18; central stairs) hidden() +build/industry_build +build2/industry_build2 +#dig label(industry_traffic) start(18; 18; central stairs) hidden() traffic patterns ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` @@ -1994,117 +1680,115 @@ query/industry_query ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` -#build label(industry_build) start(18; 18) hidden() +"#place label(industry_place) start(18; 18) hidden() message(remember to: +- assign minecarts to to your quantum stockpile hauling routes (use ""assign-minecarts all"") +- if you want to automatically melt goblinite and other low-quality weapons and armor, mark the south-east stockpiles for auto-melt +- once you have enough dwarves, run ""orders import library/basic"" to automate your fort's basic needs (see /industry_help for more info on this file)) industry stockpiles" + + +,,,,,,,,,,,"e{name=""Rough gems for moods"" containers=0 take_from=""Stoneworker quantum,Gem feeder""}:=roughgems",e,e,e,e,e,e,e,e,e,e,e,e +,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"se{name=""Stoneworker quantum"" quantum=true}",`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,"s10{name=""Stone feeder""}:=otherstone(5x4)",,,~,~,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,` +,,"w{name=""Wood"" take_from=""Goods/wood quantum,Wood feeder""}",`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,"c{name=""Dye"" barrels=-1 }:+dye" +,,w,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,c +,,w,`,`,`,`,`,`,`,`,`,`,`,`,"e{name=""Gem feeder"" containers=0}(5x1)",,,~,~,`,`,`,`,`,`,`,`,`,`,`,`,c +,,w,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,c +,,w,`,`,`,`,`,`,"w{name=""Wood feeder""}(2x5)",,"g{name=""Goods feeder"" containers=0}:+cat_food/tallow+wax-crafts-goblets(3x3)",,`,,`,`,`,`,`,,"hlS{name=""Cloth/bones feeder"" containers=0}:+cat_refuse/skulls/,bones/,hair/,shells/,teeth/,horns/-adamantinethread(5x5)",,,~,~,`,`,`,`,`,`,c +,,w,`,`,`,`,`,`,~,~,~,~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,c +,,`,`,`,`,`,"c{name=""Goods/wood quantum"" quantum=true give_to=Pots,Barrels,Jugs,Bags}:+all",`,~,~,~,~,~,,`,,`,,`,,~,~,~,~,~,`,"r{name=""Cloth/bones quantum"" quantum=true}:+all",`,`,`,`,c +,,"c{name=""Lye"" barrels=2}:+miscliquid",`,`,`,`,`,`,~,~,"u2{name=""Furniture feeder""}:-sand(3x2)",~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,c +,,c,`,`,`,`,`,`,~,~,~,~,~,,`,`,`,`,`,,~,~,~,~,~,`,`,`,`,`,`,c +,,c,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,c +,,c,`,`,`,`,`,`,`,`,`,`,`,`,"bnpdz{name=""Bar/military feeder"" containers=0}:-potash+adamantinethread(5x3)",,,,~,`,`,`,`,`,`,`,`,`,`,`,`,c +,,c,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,c +,,c,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,c +,,,,"pd{name=""Meltable steel/bronze"" take_from=""Metalworker quantum,Bar/military feeder""}:-cat_weapons/mats/,other/,core/masterful,core/artifact-cat_armor/mats/,other/,core/masterful,core/artifact+bronzeweapons+bronzearmor+steelweapons+steelarmor(7x3)",,,~,~,~,~,`,`,`,`,"s5{name=""Ore/clay feeder""}:-otherstone(5x2)",,,~,~,`,`,`,`,`,`,`,`,`,`,` +,,,,~,~,~,~,~,~,~,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,` +,,,,~,~,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,"c{name=""Coal"" containers=0 take_from=""Metalworker quantum,Bar/military feeder""}:+coal",`,"c{name=""Metalworker quantum"" quantum=true}:+all",`,"c{name=""Iron"" containers=0 take_from=""Metalworker quantum,Bar/military feeder""}:+ironbars",`,`,`,`,`,`,`,`,`,`,` +,,,,"pd{name=""Other meltables"" take_from=""Metalworker quantum,Bar/military feeder""}:-cat_weapons/other/,core/masterful,core/artifact-cat_armor/other/,core/masterful,core/artifact-bronzeweapons-bronzearmor-steelweapons-steelarmor(7x3)",,,~,~,~,~,`,`,`,`,c,"c{name=Flux take_from=""Metalworker quantum,Ore/clay feeder""}:+flux(3x1)",,,c,`,`,`,`,`,`,`,`,`,`,` +,,,,~,~,~,~,~,~,~,`,`,`,`,c,`,`,`,c,`,`,`,`,`,`,`,`,`,`,` +,,,,~,~,~,~,~,~,~,`,`,`,`,c,`,`,`,c,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,"u{name=""Sand bags""}:-cat_furniture/type+sand",u,u,u,u,u,`,"c{name=""Steel"" containers=0 take_from=""Metalworker quantum,Bar/military feeder""}:+steelbars",c,c,c,c,c + + +#build label(industry_build) start(18; 18) hidden() workshops to build first ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,wj,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,wr,`,`,`,`,`,`,`,wt,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,trackstopN,`,`,`,`,`,`,`,`,ws,`,`,`,` -,,,,`,`,wS,`,`,wb,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,"wr{name=""Stone craftsdwarf""}",`,`,`,`,`,`,`,wt,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"trackstopN{name=""Stone/gem dumper"" take_from=""Stone feeder,Gem feeder"" route=""Stone/gem quantum""}",`,`,`,`,`,`,`,`,~,`,`,`,` +,,,,`,`,~,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,wm,`,`,`,`,`,`,`,wm,`,`,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,wy,`,`,ew,`,`,ew,`,`,`,`,`,`,`,`,`,`,`,`,`,wk,`,`,wo,`,`,wd,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,,,d,,d,,,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,~,`,`,~,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,~,`,`,~,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,,,~,,~,,,`,`,`,`,`,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,d,`,,,,`,d,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,wc,`,`,`,trackstopW,`,`,`,`,`,,`,,`,,`,,`,`,`,`,`,trackstopE,`,`,`,we,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,d,`,,,,`,d,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,~,`,,,,`,~,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,wc,`,`,`,"trackstopW{name=""Goods/wood dumper"" take_from=""Wood feeder,Goods feeder,Furniture feeder"" route=""Goods/wood quantum""}",`,`,`,`,`,,`,,`,,`,,`,`,`,`,`,"trackstopE{name=""Cloth/bones dumper"" take_from=""Cloth/bones feeder"" route=""Cloth/bones quantum""}",`,`,`,~,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,~,`,,,,`,~,`,`,`,`,`,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,,,d,,d,,,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,wr,`,`,ew,`,`,ew,`,`,`,`,`,`,`,`,`,`,`,`,`,wr,`,`,wr,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,,,~,,~,,,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,~,`,`,~,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,~,`,`,`,`,` ,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,trackstopS,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"trackstopS{name=""Metalworker dumper"" take_from=""Bar/military feeder,Ore/clay feeder"" route=""Metalworker quantum""}",`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,eg,`,`,`,wf,`,`,`,ek,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,~,`,`,`,~,`,`,`,~,`,`,`,`,`,`,`,`,` ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` -#place label(industry_place) start(18; 18) hidden() +#build label(industry_build2) start(18; 18) hidden() remaining workshops -,,,,,,,,,,,e,e,e,e,e,e,e,e,e,e,e,e,e ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"wj{name=""Encruster"" take_from=""Goods/wood quantum,Stoneworker quantum,Gem feeder""}",`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,ws,`,`,`,` +,,,,`,`,wS,`,`,wb,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,wy,`,`,ew,`,`,ew,`,`,`,`,`,`,`,`,`,`,`,`,`,wk,`,`,wo,`,`,"wd{take_from=""Cloth/bones feeder,Cloth/bones quantum,Dye""}",`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,,,d,,d,,,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,d,`,,,,`,d,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,~,`,`,`,~,`,`,`,`,`,,`,,`,,`,,`,`,`,`,`,~,`,`,`,we,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,d,`,,,,`,d,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,,,d,,d,,,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,"wr{name=""Wood craftsdwarf""}",`,`,ew,`,`,ew,`,`,`,`,`,`,`,`,`,`,`,`,`,"wr{name=""Misc craftsdwarf""}",`,`,"wr{name=""Bone craftsdwarf""}",`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,c,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,s4(5x4),,,~,~,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,` -,,w,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,w,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,w,`,`,`,`,`,`,`,`,`,`,`,`,e(5x1),,,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,w,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,` -,,w,`,`,`,`,`,`,w(2x5),,fg(3x3),,`,,`,`,`,`,`,,frhlS(5x5),,,~,~,`,`,`,`,`,`,` -,,w,`,`,`,`,`,`,~,~,~,~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,` -,,`,`,`,`,`,c,`,~,~,~,~,~,,`,,`,,`,,~,~,~,~,~,`,r,`,`,`,`,` -,,f3,`,`,`,`,`,`,~,~,u2(3x2),~,~,`,`,,,,`,`,~,~,~,~,~,`,`,`,`,`,`,` -,,f3,`,`,`,`,`,`,~,~,~,~,~,,`,`,`,`,`,,~,~,~,~,~,`,`,`,`,`,`,` -,,f3,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,` -,,f3,`,`,`,`,`,`,`,`,`,`,`,`,bnpdhz(5x3),,,,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,f3,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,f3,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,pd(7x3),,,~,~,~,~,`,`,`,`,s2(5x2),,,~,~,`,`,`,`,`,`,`,`,`,`,` -,,,,~,~,~,~,~,~,~,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,` -,,,,~,~,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,b,`,c,`,b,`,`,`,`,`,`,`,`,`,`,` -,,,,pd(7x3),,,~,~,~,~,`,`,`,`,b,s,s,s,b,`,`,`,`,`,`,`,`,`,`,` -,,,,~,~,~,~,~,~,~,`,`,`,`,b,`,`,`,b,`,`,`,`,`,`,`,`,`,`,` -,,,,~,~,~,~,~,~,~,`,`,`,`,b,`,`,`,b,`,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,u,u,u,u,u,u,`,b,b,b,b,b,b - - -"#query label(industry_query) start(18; 18) hidden() message(remember to: -- assign minecarts to to your quantum stockpile hauling routes (use ""assign-minecarts all"") -- if the farming level is already built, give from the ""Goods"" quantum stockpile to the jugs, pots, and bags stockpiles on the farming level -- if you want to automatically melt goblinite and other low-quality weapons and armor, mark the south-east stockpiles for auto-melt -- once you have enough dwarves, run ""orders import library/basic"" to automate your fort's basic needs (see /industry_help for more info on this file) -- optionally, restrict the labors for your Craftsdwarf's and Mechanic's workshops as per the guidance in /industry_help)" - - -,,,,,,,,,,,roughgems,,,,nocontainers,"{givename name=""rough gems for moods""}",t{Down 5}&,,,,,~,~ -,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,es,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,"{givename name=""stone craftsdwarf""}",`,`,`,"{quantum name=""stoneworker quantum""}g{Up 3}&",`,`,`,~,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,"{quantumstop name=""Stoneworker quantum"" sp_links=""{sp_link move={Down} move_back={Up}}{sp_link move=""""{Down 5}"""" move_back=""""{Up 5}""""}""}{givename name=""stoneworker dumper""}",`,`,`,`,`,`,`,`,~,`,`,`,` -,,,,`,`,~,`,`,~,`,`,`,`,`,otherstone,,,,~,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,~,`,"{givename name=""stone feeder""}",~,~,~,~,`,~,`,`,`,`,`,`,`,`,` -,,"{givename name=""wood""}",`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,~,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,~,`,~,`,`,~,`,`,~,`,`,`,`,nocontainers,"{givename name=""gem feeder""}",~,~,~,`,`,`,`,~,`,`,~,`,`,~,`,` -,,~,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,` -,,~,`,`,`,`,`,`,"{givename name=""wood feeder""}",~,"{givename name=""goods feeder""}",nocontainers,~,,`,`,`,`,`,,craftrefuse,,,,~,`,`,`,`,`,`,` -,,t{Right 5}{Down}&,`,`,`,`,`,`,~,~,{tallow}{permitwax},~,~,`,`,,,,`,`,"{givename name=""cloth/bones feeder""}",g{Up 3}{Right 5}&,~,~,~,`,`,`,`,`,`,` -,,`,`,~,`,`,"{quantum name=""goods/wood quantum""}g{Up 13}{Right 10}&","{quantumstop name=""Goods/Wood quantum"" sp_links=""{sp_link move={Right} move_back={Left}}{sp_link move=""""{Right 5}"""" move_back=""""{Left 5}""""}{sp_link move=""""{Down}{Right 5}"""" move_back=""""{Left 5}{Up}""""}""}{givename name=""goods/wood dumper""}",~,~,{forbidcrafts}{forbidgoblets},~,~,,`,,`,,`,,nocontainers,~,~,~,~,"{quantumstopfromwest name=""Clothier/Bones quantum""}{givename name=""cloth/bones dumper""}","{quantum name=""cloth/bones quantum""}g{Up 4}{Right 3}&",`,`,~,`,` -,,miscliquid,`,`,`,`,`,`,~,~,"{givename name=""furniture feeder""}",~,~,`,`,,,,`,`,forbidadamantinethread,~,~,~,~,`,`,`,`,`,`,` -,,"{givename name=""miscliquid""}",`,`,`,`,`,`,~,~,forbidsand,~,~,,`,`,`,`,`,,dye,~,~,~,~,`,`,`,`,`,`,` -,,~,`,`,`,`,`,`,`,`,`,`,`,,,`,,`,,,`,`,`,`,`,`,`,`,`,`,`,` -,,~,`,"{givename name=""wood craftsdwarf""}",`,`,~,`,`,~,`,`,`,`,forbidpotash,nocontainers,"{givename name=""bar/military feeder""}",~,~,`,`,`,`,"{givename name=""misc craftsdwarf""}",`,`,"{givename name=""bone craftsdwarf""}",`,`,`,`,` -,,~,`,`,`,`,`,`,`,`,`,`,`,`,adamantinethread,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,~,`,`,`,`,`,`,`,`,`,`,`,`,~,~,~,~,~,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,nocontainers,t{Right 12}{Up 3}&,t{Right 11}{Down 3}&,"{givename name=""meltable steel/brnze""}",,,,`,`,~,`,forbidotherstone,,,,,`,~,`,`,`,`,`,`,`,`,` -,,,,{bronzeweapons}{permitsteelweapons}{forbidmasterworkweapons}{forbidartifactweapons},,,,,,,`,`,`,`,"{givename name=""ore/clay feeder""}",~,~,~,~,`,`,`,`,`,`,`,`,`,`,` -,,,,{bronzearmor}{permitsteelarmor}{forbidmasterworkarmor}{forbidartifactarmor},,,,,,,`,`,`,`,`,`,"{quantumstop name=""Metalworker quantum"" sp_links=""{sp_link move={Up} move_back={Down}}{sp_link move=""""{Up 5}"""" move_back=""""{Down 5}""""}""}{givename name=""metalworker dumper""}",`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,`,`,`,`,`,`,`,~,`,t{Right 2}&,`,"{quantum name=""metalworker quantum""}",`,t{Left 2}&,`,~,`,`,`,`,`,`,`,`,` -,,,,nocontainers,t{Right 12}{Up 7}&,t{Right 11}{Up 1}&,"{givename name=""other meltables""}",,,,`,`,`,`,coal,flux,t{Up}&,"{givename name=""flux""}",ironbars,`,`,`,`,`,`,`,`,`,`,` -,,,,{metalweapons}{forbidbronzeweapons}{forbidsteelweapons}{forbidmasterworkweapons}{forbidartifactweapons},,,,,,,`,`,`,`,"{givename name=""coal""}",`,`,`,"{givename name=""iron""}",`,`,`,`,`,`,`,`,`,`,` -,,,,{metalarmor}{forbidbronzearmor}{forbidsteelarmor}{forbidmasterworkarmor}{forbidartifactarmor},,,,,,,`,`,~,`,nocontainers,`,~,`,nocontainers,`,~,`,`,`,`,`,`,`,`,` +,,,,`,`,`,`,`,`,`,`,`,eg,`,`,`,wf,`,`,`,ek,`,`,`,`,`,`,`,`,` +,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` ,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,sand,"{givename name=""sand bags""}",nocontainers,~,~,~,`,t{Up 5}{Left}&,,,nocontainers,steelbars,"{givename name=""steel""}" #notes label(services_help) @@ -2113,53 +1797,55 @@ Screenshot: https://drive.google.com/file/d/13vDIkTVOZGkM84tYf4O5nmRs4VZdE1gh "" Features: - Spacious dining room/tavern (tavern is restricted to residents-only by default) -- Prepared food and drink stockpiles +- Large prepared food and drink stockpiles - Well cistern system (bring your own water) -- Hospital with a well for washing +- Hospital (also restricted to residents by default) with a well for washing +- Well-appointed jail cells +- Bolt-recycling archery range for your marksdwarves +- Interrogation room for your Captain of the Guard - Garbage dump - Empty space for magma to power forges and smelters in the industry level above "" -Note the hospital also has animal training enabled so it can be used with the dwarfvet plugin if it's enabled. +Note the hospital is attached to an animal training zone so it can be used with the dwarfvet plugin if it's enabled. "" Manual steps you have to take: -"- If you want to tavern to attract visitors, change the restriction in the (l)ocation menu." -"- Fill the cisterns with water, either with a bucket brigade or by plumbing flowing water. Fill so that there are two z-levels of 7-depth water to prevent muddiness. If you want to fill with buckets, designate a pond zone on the level below the main floor. If you feel adventurous and are experienced with water pressure, you can instead route (depressurized!) water to the second-to-bottom level (the one above the up staircases)." +"- If you want to tavern to attract visitors, change the restriction in the location configuration screen." +"- Fill the cisterns with water, either with a bucket brigade or by plumbing flowing water. Fill so that there are two z-levels of 7-depth water to prevent muddiness. If you want to fill with buckets, you can use the pre-designated pond zones on the level below the main floor. If you feel adventurous and are experienced with water pressure, you can instead route (depressurized!) water to the second-to-bottom level (the one above the up staircases)." "- If you are filling the wells with a bucket brigade and at least one well is already constructed, you'll run into issues with the dwarves stealing water from one well to fill another. Temporarily deconstruct the wells or temporarily build grates beneath the wells to block the buckets to avoid this issue. Remember to rebuild the wells or remove the grates afterwards, though!" -- Assign the office to the right of the barracks to your Sheriff/Captain of the Guard to use it as an interrogation room. "" Services Walkthrough: -1) Start this level when your fort grows to about 50 dwarves so everyone has a place to eat. +1) Start this level when your fort grows to about 30 dwarves so everyone has a place to eat. "" -2) Start digging with /services1. Note that this digs out the main level and three levels below for the wells. +2) Start digging with /services1. Note that this digs out the main level and three levels below for the well cisterns. "" -"3) Once the area is dug out, set up important furniture, stockpiles, hospital zone and garbage dump zone with /services2. Run ""quickfort orders"" for /services2." +"3) Once the area is dug out, set up zones/locations and start populating the rooms with /services2. Run ""quickfort orders"" for /services2." "" -"4) When the table and chair have been placed in the dining room, the beds are placed in the rented rooms, and the weapon rack and archery targets are constructed in the barracks, run /services3 to build the rest of the furniture and configure your dining room/tavern and barracks. Run ""quickfort orders"" for /services3." +4) Fill the wells with either bucket brigades or by carefully routing flowing water. "" -5) Fill the wells with either bucket brigades or by carefully routing flowing water. +"5) When your fort has grown some more, or you start needing a jail cell, run /services3 to extend the rooms a bit more. Run ""quickfort orders"" for /services3." "" -"6) When your fort is mature enough to need jail cells, run /services4 to set those up, anytime after the restraints are built in the jail cell block. You also get some decorative statues to increase the value of your dining hall. Assign the office to the right of the barracks to your Sheriff/Captain of the Guard to use it as an interrogation room. Run ""quickfort orders"" for /services4." +"6) When your fort is more mature and you need to expand the rooms, run /services4 to finish everything up. If you didn't have a Sheriff/Captain of the Guard to automatically assign to the interrogation room when you ran /services2, you can manually assign the office now. Run ""quickfort orders"" for /services4." "#dig label(services1) start(18; 18; central stairs) message(Once the area is dug out, continue with /services2.)" -,d,d,d,,d,d,d,,d,d,d,,d,h,d,,j,,d,h,d -,d,d,d,,d,d,d,,d,d,d,,d,d,d,d,d,d,d,d,d -,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,,d,d,d -,,d,,,,d,,,,d,,,,,,,d -,d,d,d,d,d,d,d,d,d,d,d,,d,h,d,,d,,d,h,d,,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,,d,,d,d,d,,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,,,,,d,,,,,,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,,,,d -,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,d,d +,d,d,d,,d,d,d,,d,d,d,,d,h,d,,d,h,d,,d,h,d,,d,h,d,,d,h,d +,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d +,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d +,,d,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d +,d,d,d,d,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,,,,d +,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,,d,,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,h,h,h,h,h,h,h,h,h,h,,d,,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,,d,,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,d,,j,,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,d,d,,d,d,d,d,d,d,d,d,d,,d,,d,,d,,d ,d,d,d,d,d,d,d,d,d,d,d,,,,,d,,d,,,,,d,,d,,d,,d -,d,d,d,d,d,d,d,d,d,d,d,,,,d,d,d,d,d,,,,d,d,d,d,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,d,,d,`,`,`,d,,d,,d,d,d,h,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d -,d,d,d,d,d,d,d,d,d,d,d,,,,d,d,d,d,d,,,,d,d,d,d,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,,,d,d,d,d,d,,,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,d,d,d,d,d,h,d,d,d,d,d,,d,,d,`,`,`,d,,d,,d,d,d,h,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,,,,d,d,d,d,d,,,,d,,d,,d,,d ,d,d,d,d,d,d,d,d,d,d,d,,,,,,d,,,,,,d,,d,,d,,d ,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,,,d,,d,,d,,d ,d,d,d,d,d,d,d,d,d,d,d @@ -2177,33 +1863,33 @@ Services Walkthrough: #> -,,,,,,,,,,,,,j5,h5,j,,u,,j,h5,j5 -,,,,,,,,,,,,,5,5,5,5,d,5,5,5,5 -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,j5,h5,j,,d,,j,h5,j5 -,,,,,,,,,,,,,5,5,5,5,d,5,5,5,5 -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,,,,,d -,,,,,,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d -,,,,,,,,,,,,,,,,,6,,,,,,,,,d -,,,,,,,,,,,,,,,,,d,,,,,,,,,d -,,,,,,,,,,,,,,,d,d,d,d,d,,,,,,,d -,,,,,,,,,,,,,,,d,`,`,`,d,,,,,,5,5,5 -,,,,,,,,,,,,,,,d,`,`,`,d,,,,,,j,h5,j5 +,,,,,,,,,,,,,j5,h5,j,,j5,h5,j,,j5,h5,j,,j5,h5,j,,j5,h5,j +,,,,,,,,,,,,,5,5,5,,5,5,5,,5,5,5,,5,5,5,,5,5,5 +,,,,,,,,,,,,,,d,,,,d,,,,d,,,,d,,,,d +,,,,,,,,,,,,,,d,,,,d,,,,d,,,,d,,,,d +,,,,,,,,,,,,,,d,,,,d,,,,d,,,,d,,,,d +,,,,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,,,,,,,,,,u,d,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,,,,,,,,,,,,d +,,,,,,d,,,,,,,,,d,d,d,d,d,,,,,,,d +,,,,,5,5,5,,,,,,,,d,`,`,`,d,,,,,,5,5,5 +,,,,,j5,h5,j,,,,,,,,d,`,`,`,d,,,,,,j5,h5,j ,,,,,,,,,,,,,,,d,`,`,`,d ,,,,,,,,,,,,,,,d,d,d,d,d #> -,,,,,,,,,,,,,u5,h5,i,,,,i,h5,u5 +,,,,,,,,,,,,,u5,h5,i,,u5,h5,i,,u5,h5,i,,u5,h5,i,,u5,h5,i + -,,,,,,,,,,,,,u5,h5,i,,,,i,h5,u5 @@ -2215,17 +1901,17 @@ Services Walkthrough: ,,,,,,,,,,,,,,,d,d,d,d,d ,,,,,,,,,,,,,,,d,`,`,`,d -,,,,,,,,,,,,,,,d,`,`,`,d,,,,,,i,h5,u5 +,,,,,u5,h5,i,,,,,,,,d,`,`,`,d,,,,,,u5,h5,i ,,,,,,,,,,,,,,,d,`,`,`,d ,,,,,,,,,,,,,,,d,d,d,d,d #> -,,,,,,,,,,,,,,d,u,,,,u,d +,,,,,,,,,,,,,,d,u,,,d,u,,,d,u,,,d,u,,,d,u + -,,,,,,,,,,,,,,d,u,,,,u,d @@ -2237,93 +1923,88 @@ Services Walkthrough: ,,,,,,,,,,,,,,,d,d,d,d,d ,,,,,,,,,,,,,,,d,`,`,`,d -,,,,,,,,,,,,,,,d,`,`,`,d,,,,,,u,d +,,,,,,d,u,,,,,,,,d,`,`,`,d,,,,,,,d,u ,,,,,,,,,,,,,,,d,`,`,`,d ,,,,,,,,,,,,,,,d,d,d,d,d -"#meta label(services2) start(central stairs) message(Remember to enqueue manager orders for this blueprint. -Once furniture has been placed, continue with /services3.) dining hall anchors, stockpiles, hospital, garbage dump" +"#meta label(services2) start(central stairs) message(Once furniture has been built, continue with /services3.) zones and minimally functional hospital and dining hall" traffic/services_traffic zones/services_zones -build/services_build place/services_place -name_zones/services_name_zones -query_stockpiles/services_query_stockpiles +build/services_build "" -"#meta label(services3) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) configure dining room/tavern, build dining hall and hospital furniture" -query_dining/services_query_dining -query_rented_rooms/services_query_rented_rooms +"#meta label(services3) start(central stairs) message(Once furniture has been built, continue with /services4.) expand furnishings" +place2/services_place2 build2/services_build2 "" -#meta label(services4) start(central stairs) message(Remember to enqueue manager orders for this blueprint.) declare and furnish jail and build decorative furniture +#meta label(services4) start(central stairs) complete furnishings +place3/services_place3 build3/services_build3 -place_jail/services_place_jail -query_jail/services_query_jail -#dig label(services_traffic) start(18; 18) hidden() keep lollygaggers out of the cisterns - -,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,,or,,or,or,or -,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,or,or,or,or,or,or -,ol,ol,ol,,ol,ol,ol,,ol,ol,ol,,or,or,or,,or,,or,or,or -,,ol,,,,ol,,,,ol,,,,,,,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,,or,,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,,,,or,,,,,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,,,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,or,or,or,or,or,or,or,or,or,,or,,or,,or,,or -,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,ol,,,,,or,,or,,,,,or,,or,,or,,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,oh,oh,oh,oh,oh,,,,or,or,or,or,or,or,or,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,`,,oh,`,`,`,oh,,`,,oh,oh,oh,`,oh,or,or,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,oh,oh,oh,oh,oh,,,,or,or,or,or,or,or,or,or,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,,,or,,,,,,or,,or,,or,,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,,,,,,,,,,,,or,,or,,or,,or -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh -,,,,oh,oh,,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh -,oh,oh,oh,oh,oh,,oh,oh,oh,oh,oh +#dig label(services_traffic) start(18; 18) hidden() keep lollygaggers out of the cisterns and justice areas + +,`,`,`,,`,`,`,,`,`,`,,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or +,`,`,`,,`,`,`,,`,`,`,,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or +,`,`,`,,`,`,`,,`,`,`,,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or +,,`,,,,`,,,,`,,,,or,,,,or,,,,or,,,,or,,,,or +,`,`,`,`,`,`,`,`,`,`,`,,,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or,or,,or,,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or,or,,or,,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or,or,,or,,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or,or,,or,,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,or,or,or,or,or,or,or,or,or,,or,,or,,or,,or +,`,`,`,`,`,`,`,`,`,`,`,,,,,or,,or,,,,,or,,or,,or,,or +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,or,,or,,or,,or +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,ol,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,oh,oh,oh,oh,oh,ol,or +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,ol,or,or,or,or +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,or,,or,,or,,or +,`,`,`,`,`,`,`,`,`,`,`,,,,,,or,,,,,,or,,or,,or,,or +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,or,,or,,or,,or +,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,` +,,,,`,`,,`,` +,`,`,`,`,`,,`,`,`,`,` +,`,`,`,`,`,,`,`,`,`,` +,`,`,`,`,`,,`,`,`,`,` +,`,`,`,`,`,,`,`,`,`,` +,`,`,`,`,`,,`,`,`,`,` #> -,,,,,,,,,,,,,or,or,or,,or,,or,or,or -,,,,,,,,,,,,,or,or,or,or,or,or,or,or,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,or,or,or,,or,,or,or,or -,,,,,,,,,,,,,or,or,or,or,or,or,or,or,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,,,,,or -,,,,,,,,,,,,,,,,,or,or,or,or,or,or,or,or,or,or -,,,,,,,,,,,,,,,,,or,,,,,,,,,or -,,,,,,,,,,,,,,,,,or,,,,,,,,,or -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,,or -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or,or -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or,or +,,,,,,,,,,,,,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or +,,,,,,,,,,,,,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or +,,,,,,,,,,,,,,or,,,,or,,,,or,,,,or,,,,or +,,,,,,,,,,,,,,or,,,,or,,,,or,,,,or,,,,or +,,,,,,,,,,,,,,or,,,,or,,,,or,,,,or,,,,or +,,,,,,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,,,,,,,,,,or,or,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,,,,,,,,,,,,or +,,,,,,or,,,,,,,,,`,`,`,`,`,,,,,,,or +,,,,,or,or,or,,,,,,,,`,`,`,`,`,,,,,,or,or,or +,,,,,or,or,or,,,,,,,,`,`,`,`,`,,,,,,or,or,or ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` #> -,,,,,,,,,,,,,or,or,or,,,,or,or,or +,,,,,,,,,,,,,or,or,or,,or,or,or,,or,or,or,,or,or,or,,or,or,or + -,,,,,,,,,,,,,or,or,or,,,,or,or,or @@ -2335,17 +2016,17 @@ query_jail/services_query_jail ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or,or +,,,,,or,or,or,,,,,,,,`,`,`,`,`,,,,,,or,or,or ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` #> -,,,,,,,,,,,,,,or,or,,,,or,or +,,,,,,,,,,,,,,or,or,,,or,or,,,or,or,,,or,or,,,or,or + -,,,,,,,,,,,,,,or,or,,,,or,or @@ -2357,33 +2038,33 @@ query_jail/services_query_jail ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,or,or +,,,,,,or,or,,,,,,,,`,`,`,`,`,,,,,,,or,or ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` -"#zone label(services_zones) start(18; 18) hidden() message(If you'd like to fill your wells via bucket brigade, activate the inactive pond zones one level down from where the wells will be built.) garbage dump, hospital, and pond zones" - -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,,,,,,d,,,,,ht,ht,ht,ht,ht,ht,ht,ht,ht,ht -,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,ht,ht,ht,ht,ht,ht,ht,ht,ht +"#zone label(services_zones) start(18; 18) hidden() message(If you'd like to fill your wells via bucket brigade, activate the inactive pond zones one level down from where the wells will be built.) garbage dump, hospital, taverrn, barracks, archery range, and pond zones" +,,,,,,,,,,,,"j{name=""Jail 1""}(5x5)",,,,"j{name=""Jail 2""}(5x5)",,,,"j{name=""Jail 3""}(5x5)",,,,"j{name=""Jail 4""}(5x5)",,,,"j{name=""Jail 5""}(5x5)" +,"b{location=tavern/bigpub name=""Rented room 1""}(1x3)","b{location=tavern/bigpub name=""Rented room 2""}(1x3)","b{location=tavern/bigpub name=""Rented room 3""}(1x3)",,"b{location=tavern/bigpub name=""Rented room 4""}(1x3)","b{location=tavern/bigpub name=""Rented room 5""}(1x3)","b{location=tavern/bigpub name=""Rented room 6""}(1x3)",,"b{location=tavern/bigpub name=""Rented room 7""}(1x3)","b{location=tavern/bigpub name=""Rented room 8""}(1x3)","b{location=tavern/bigpub name=""Rented room 9""}(1x3)",,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +"h{location=tavern/bigpub allow=residents name=""Grand hall tavern""}(13x31)",,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,`,"o{name=""Interrogation room"" assigned_unit=sheriff}(7x7)" +,`,`,`,`,`,`,`,`,`,`,`,,"B{name=""Marksdwarf barracks""}a{name=""Shooting gallery"" shoot_from=""south""}",Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba +,`,`,`,`,`,`,`,`,`,`,`,,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,Ba,,m{location=hospital name=Hospital allow=residents},,m,,m,,m +,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,m,,m,,m,,m +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,m,,m,,m,,m +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,m,m,m,m,m,m,m,m,m,m +,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,m,m,m,m,m,m,m +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,m,m,m,m,m,m,m,m,m,m +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,m,,m,,m,,m +,`,`,`,`,`,`,`,`,`,`,`,,,,,,"d{name=""Garbage dump""}",,,,,,m,,m,,m,,m +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,m,,m,,m,,m ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` @@ -2399,84 +2080,48 @@ query_jail/services_query_jail #> -,,,,,,,,,,,,,`,apPf,`,,`,,`,apPf,` -,,,,,,,,,,,,,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,`,apPf,`,,`,,`,apPf,` -,,,,,,,,,,,,,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,`,,,,,,,,,` -,,,,,,,,,,,,,,,,,`,,,,,,,,,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,`,apPf,` +,,,,,,,,,,,,,`,"p{name=""Jail 1 cistern"" pond=true active=false}",`,,`,"p{name=""Jail 2 cistern"" pond=true active=false}",`,,`,"p{name=""Jail 3 cistern"" pond=true active=false}",`,,`,"p{name=""Jail 4 cistern"" pond=true active=false}",`,,`,"p{name=""Jail 5 cistern"" pond=true active=false}",` +,,,,,,,,,,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,,,,,,,,,,,,,,`,,,,`,,,,`,,,,`,,,,` +,,,,,,,,,,,,,,`,,,,`,,,,`,,,,`,,,,` +,,,,,,,,,,,,,,`,,,,`,,,,`,,,,`,,,,` +,,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,,,,,,,,,,`,`,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,,,,,,,,,,,,` +,,,,,,`,,,,,,,,,`,`,`,`,`,,,,,,,` +,,,,,`,`,`,,,,,,,,`,`,`,`,`,,,,,,`,`,` +,,,,,`,"p{name=""Tavern cistern"" pond=true active=false}",`,,,,,,,,`,`,`,`,`,,,,,,`,"p{name=""Hospital cistern"" pond=true active=false}",` ,,,,,,,,,,,,,,,`,`,`,`,` ,,,,,,,,,,,,,,,`,`,`,`,` -#build label(services_build) start(18; 18) hidden() build basic hospital and dining room anchor - -,b,b,b,,b,b,b,,b,b,b,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,d,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,A,A,A,`,A,A,A,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,r,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,d,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,trackstopN,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,,`,,`,,b -,`,`,`,`,`,`,`,`,`,`,`,,,,,d,,d,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,t,`,`,`,`,R -,`,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,h -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,l,`,`,`,`,R -,`,`,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,t,c,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,R -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,h,`,`,`,`,` -,,,,`,`,,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` - #place label(services_place) start(18; 18) hidden() -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,c,`,`,`,`,`,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,"z{name=""Training quantum"" quantum=true}",`,`,`,`,`,,`,,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,z(7x1),,,`,`,`,`,`,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,"z{name=""Training bolts feeder"" containers=0 take_from=""Metalworker quantum""}:-cat_ammo/type/,mats/,other/+bolts+woodammo+boneammo(9x1)",,,,,,,,`,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,c,,,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,"c{name=""Garbage dump"" links_only=true}",,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` @@ -2485,41 +2130,42 @@ query_jail/services_query_jail ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,,`,` -,f(5x5),,,`,`,,f(5x5),,,`,` +,"c{name=""Prepared food"" barrels=-1}:+preparedmeals(5x5)",,,,`,,"c{name=""Booze"" barrels=-1}:+booze(5x5)",,,,` ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` -#query label(services_name_zones) start(18; 18) hidden() - -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,"{namezone name=""hospital""}" -,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,"{namezone name=""garbage dump""}" +"#build label(services_build) start(18; 18) hidden() message(Remember to enqueue manager orders for this blueprint. +Assign a minecart to the training ammo quantum dump with ""assign-minecarts all"") build basic hospital, dining room, and barracks" + +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,A,A,A,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,h,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,b,`,`,`,"trackstopN{name=""Training bolts dumper"" take_from=""Training bolts feeder"" route=""Training bolts quantum""}",`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,,`,,`,,b +,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,d +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,t,`,`,d,R +,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,l,`,`,h +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,d,R +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` +,`,`,`,`,`,`,c,t,t,c,` +,`,`,`,`,`,`,c,t,t,c,` +,`,`,`,`,`,`,c,t,t,c,` +,`,`,`,`,`,`,c,t,t,c,` ,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,h,`,`,`,`,` ,,,,`,`,,`,` ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` @@ -2527,50 +2173,28 @@ query_jail/services_query_jail ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` -#> - -,,,,,,,,,,,,,`,"{namezone name=""jail3 well""}",`,,`,,`,"{namezone name=""jail4 well""}",` -,,,,,,,,,,,,,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,`,"{namezone name=""jail1 well""}",`,,`,,`,"{namezone name=""jail2 well""}",` -,,,,,,,,,,,,,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,`,,,,,,,,,` -,,,,,,,,,,,,,,,,,`,,,,,,,,,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,`,"{namezone name=""hospital well""}",` -,,,,,,,,,,,,,,,`,`,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,` - -"#query label(services_query_stockpiles) start(18; 18) hidden() message(Configure the training ammo stockpile to take from the metalworker quantum on the industry level. Assign a minecart to the training ammo quantum dump with ""assign-minecarts all"") configure stockpiles" - -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,"{quantum name=""training quantum""}",`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,"{quantumstopfromsouth name=""Training quantum""}{givename name=""training dumper""}",`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,nocontainers,{bolts}{forbidmetalbolts}{forbidartifactammo},"{givename name=""training bolts""}",`,`,`,`,`,,`,,`,,`,,` +#place label(services_place2) start(18; 18) hidden() jail food and booze + +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,"c{name=""Jail booze"" barrels=-1 take_from=""Booze""}:+booze(1x2)",,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,"c{name=""Jail food"" take_from=""Prepared food""}:+preparedmeals(2x1)",,`,,`,`,`,,`,`,` +,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,~,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,~,~,~,~,~,~,~,~,~,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,"{givename name=""garbage dump""}" +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,~,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` @@ -2579,41 +2203,41 @@ query_jail/services_query_jail ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,,`,` -,preparedfood,"{givename name=""prepared food""}",`,`,`,,booze,"{givename name=""booze""}",`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` - -"#query label(services_query_dining) start(18; 18) hidden() message(The tavern is restricted to residents only by default. If you'd like your tavern to attract vistors, please go to the (l)ocation menu and change the restriction.) set up dining room/tavern and barracks" - -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,r+&w,r+&w,r+&w,`,r+&w,r+&w,r+&w,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,r{+ 2}&,,,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,"r{+ 12}&h{givename name=""grand hall""}lai^l{Up}r^q",,,,,,,,,,,,,,,,,,,,`,`,`,`,`,`,`,` +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ + +#build label(services_build2) start(18; 18) hidden() message(Remember to enqueue manager orders for this blueprint.) expand each room + +,`,b,`,,`,b,`,,`,b,`,,`,`,`,,`,`,`,,t,l,b,,`,`,`,,`,`,` +,`,h,`,,`,h,`,,`,h,`,,`,`,`,,`,`,`,,c,v,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,,`,,,,`,,,,`,,,,`,,,,`,,,,d,,,,`,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,A,A,A,~,~,~,A,A,A,A,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,c,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,t,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,c,`,` +,`,`,`,`,`,`,`,`,`,`,`,,~,`,`,`,`,`,`,`,h,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,~,`,`,`,~,`,`,`,b +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,b,,b,,b,,~ +,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,d,,d,,d,,~ +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,~,`,`,~,~ +,`,`,`,`,`,l,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,~,`,`,~ +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,~,~ +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` +,`,c,t,t,c,`,~,~,~,~,` +,`,c,t,t,c,`,~,~,~,~,` +,`,c,t,t,c,`,~,~,~,~,` +,`,c,t,t,c,`,~,~,~,~,` ,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` +,h,`,`,`,`,~,`,`,`,`,h ,,,,`,`,,`,` ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` @@ -2621,28 +2245,28 @@ query_jail/services_query_jail ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` -#query label(services_query_rented_rooms) start(18; 18) hidden() attach rented rooms to tavern - -,r&l-&,r&l-&,r&l-&,,r&l-&,r&l-&,r&l-&,,r&l-&,r&l-&,r&l-&,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` +#place label(services_place3) start(18; 18) hidden() remaining jail food and booze + +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,`,`,`,,`,`,`,,`,`,`,,`,`,"c{name=""Jail booze"" barrels=-1 take_from=""Booze""}:+booze(1x2)",,`,`,"c{name=""Jail booze"" barrels=-1 take_from=""Booze""}:+booze(1x2)",,`,`,~,,`,`,"c{name=""Jail booze"" barrels=-1 take_from=""Booze""}:+booze(1x2)",,`,`,"c{name=""Jail booze"" barrels=-1 take_from=""Booze""}:+booze(1x2)" +,`,`,`,,`,`,`,,`,`,`,,"c{name=""Jail food"" take_from=""Prepared food""}:+preparedmeals(2x1)",,`,,"c{name=""Jail food"" take_from=""Prepared food""}:+preparedmeals(2x1)",,`,,~,`,`,,"c{name=""Jail food"" take_from=""Prepared food""}:+preparedmeals(2x1)",,`,,"c{name=""Jail food"" take_from=""Prepared food""}:+preparedmeals(2x1)",,` +,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,~,`,`,`,`,`,,`,,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,~,~,~,~,~,~,~,~,~,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,~,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` @@ -2651,172 +2275,42 @@ query_jail/services_query_jail ,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,` ,,,,`,`,,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` - -"#build label(services_build2) start(18; 18) hidden() build rest of hospital and dining room, doors, prep for jail" - -,~,~,~,,~,~,~,,~,~,~,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,v,`,d,`,d,`,v,` -,f,`,h,,f,`,h,,f,`,h,,`,`,`,,`,,`,`,` -,,d,,,,d,,,,d,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,v,`,d,`,d,`,v,`,,`,`,c,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,~,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,b,~,~,~,`,~,~,~,h,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,b,`,`,`,~,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,b,`,`,`,`,`,`,`,`,~,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,b,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,b,`,`,`,`,`,`,`,a,,b,,b,,b,,~ -,`,`,`,`,`,`,`,`,`,`,`,,,,,~,,~,,,,,d,,d,,d,,d -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,~,`,`,`,d,~ -,`,c,t,t,c,`,c,t,t,c,`,~,`,d,`,`,`,`,`,d,`,d,`,`,`,`,`,`,~ -,`,c,t,t,c,`,c,t,t,c,`,,`,,`,`,`,`,`,,`,,`,`,`,~,`,`,`,d,~ -,`,c,t,t,c,`,c,t,t,c,`,~,`,d,`,`,`,`,`,d,`,d,`,`,`,`,`,`,f -,`,c,t,~,~,`,c,t,t,c,`,,,,`,`,`,`,`,,,,`,`,`,t,`,`,`,d,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ +,~,~,~,~,~,,~,~,~,~,~ + +#build label(services_build3) start(18; 18) hidden() message(Remember to enqueue manager orders for this blueprint.) finalize furniture + +,b,~,b,,b,~,b,,b,~,b,,t,l,b,,t,l,b,,~,~,~,,t,l,b,,t,l,b +,h,~,h,,h,~,h,,h,~,h,,c,v,`,,c,v,`,,~,~,`,,c,v,`,,c,v,` +,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` +,,d,,,,d,,,,d,,,,d,,,,d,,,,~,,,,d,,,,d +,`,`,`,s,`,`,`,s,`,`,`,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,,` +,s,`,`,`,`,`,`,`,`,`,s,,~,~,~,~,~,~,~,~,~,~,,`,,j,`,`,`,j +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,,`,,`,`,~,`,` +,s,`,`,`,`,`,`,`,`,`,s,,`,`,`,`,`,`,`,`,`,`,,`,,j,`,~,`,j +,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,d,`,d,`,`,~,`,` +,s,`,`,`,`,`,`,`,`,`,s,,~,h,h,`,`,`,h,h,~,`,,`,,j,`,`,`,j +,`,`,`,`,`,`,`,`,`,`,`,,~,b,b,`,~,`,b,b,~ +,s,`,`,`,`,`,`,`,`,`,s,,`,`,`,`,`,`,`,`,`,,~,,~,,~,,~ +,`,`,`,`,`,`,`,`,`,`,`,,,,,d,,d,,,,,~,,~,,~,,~ +,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,,`,,`,,` +,`,`,`,`,`,`,`,`,`,`,`,d,`,d,`,`,`,`,`,d,`,d,`,`,`,`,`,~,`,`,~,~ +,`,c,t,`,`,~,`,`,t,c,`,,s,,`,`,`,`,`,,s,,`,`,`,~,`,`,~ +,`,c,t,`,`,`,`,`,t,c,`,d,`,d,`,`,`,`,`,d,`,d,`,`,`,`,`,t,`,`,~,~ +,`,c,t,t,c,`,c,t,t,c,`,,,,`,`,`,`,`,,,,`,,`,,`,,` ,`,c,t,t,c,`,c,t,t,c,`,,,,,,`,,,,,,d,,d,,d,,d ,`,c,t,t,c,`,c,t,t,c,`,,,,,,,,,,,,b,,b,,b,,b -,`,c,t,t,c,`,c,t,t,c,` -,`,c,t,t,c,`,c,t,t,c,` -,`,c,t,t,c,`,c,t,t,c,` -,`,c,t,t,c,`,c,t,t,c,` -,`,`,`,`,`,`,`,`,`,`,` -,h,`,`,`,`,~,`,`,`,`,h -,,,,d,d,,d,d -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` - -#> - -,,,,,,,,,,,,,`,`,`,,`,,`,`,` -,,,,,,,,,,,,,`,`,`,d,`,d,`,`,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,`,`,`,,`,,`,`,` -,,,,,,,,,,,,,`,`,`,d,`,d,`,`,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,` -,,,,,,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,` -,,,,,,,,,,,,,,,,,`,,,,,,,,,` -,,,,,,,,,,,,,,,,,d,,,,,,,,,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,,d -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,`,,,,,,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,` -,,,,,,,,,,,,,,,`,`,`,`,` - -"#build label(services_build3) start(18; 18) hidden() jail, statues" - -,~,~,~,,~,~,~,,~,~,~,,t,l,b,,`,,t,l,b -,`,`,`,,`,`,`,,`,`,`,,c,~,`,~,`,~,c,~,` -,~,`,~,,~,`,~,,~,`,~,,`,`,`,,`,,`,`,` -,,~,,,,~,,,,~,,,,,,,` -,`,`,`,s,`,`,`,s,`,`,`,,t,l,b,,`,,t,l,b,,j,`,`,`,j -,`,`,`,`,`,`,`,`,`,`,`,,c,~,`,~,`,~,c,~,`,,`,`,~,`,` -,s,`,`,`,`,`,`,`,`,`,s,,`,`,`,,`,,`,`,`,,v,`,t,`,v -,`,`,`,`,`,`,`,`,`,`,`,,,,,,~,,,,,,`,`,c,`,` -,s,`,`,`,`,`,`,`,`,`,s,,~,~,~,~,`,~,~,~,~,,j,`,`,`,j -,`,`,`,`,`,`,`,`,`,`,`,,~,`,`,`,~,`,`,`,`,,,,d -,s,`,`,`,`,`,`,`,`,`,s,,~,`,`,`,`,`,`,`,`,~,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,~,`,`,`,`,`,`,`,` -,s,`,`,`,`,`,`,`,`,`,s,,~,`,`,`,`,`,`,`,~,,~,,~,,~,,~ -,`,`,`,`,`,`,`,`,`,`,`,,,,,~,,~,,,,,~,,~,,~,,~ -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,s,`,~,`,`,`,~,~ -,`,~,~,~,~,`,~,~,~,~,`,~,`,~,`,`,`,`,`,~,`,~,`,`,`,`,`,`,~ -,`,~,~,~,~,`,~,~,~,~,`,,s,,`,`,`,`,`,,s,,`,`,`,~,`,`,`,~,~ -,`,~,~,~,~,`,~,~,~,~,`,~,`,~,`,`,`,`,`,~,`,~,`,`,`,`,`,`,~ -,`,~,~,~,~,`,~,~,~,~,`,,,,`,`,`,`,`,,,,`,s,`,~,`,`,`,~,~ -,`,~,~,~,~,`,~,~,~,~,`,,,,,,`,,,,,,~,,~,,~,,~ -,`,~,~,~,~,`,~,~,~,~,`,,,,,,,,,,,,~,,~,,~,,~ ,`,~,~,~,~,`,~,~,~,~,` ,`,~,~,~,~,`,~,~,~,~,` ,`,~,~,~,~,`,~,~,~,~,` ,`,~,~,~,~,`,~,~,~,~,` ,`,`,`,`,`,`,`,`,`,`,` ,~,`,s,`,`,~,`,`,s,`,~ -,,,,~,~,,~,~ -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` - -#place label(services_place_jail) start(18; 18) hidden() - -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,`,f(1x2),`,`,`,`,`,f(1x2) -,`,`,`,,`,`,`,,`,`,`,,f(2x1),`,`,,`,,f(2x1),`,` -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,f(1x2),`,`,`,`,`,f(1x2),,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,f(2x1),`,`,,`,,f(2x1),`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,,`,`,`,`,` - -#query label(services_query_jail) start(18; 18) hidden() message(Assign the office to the right of the barracks to your Sheriff/Captain of the Guard to use it as your interrogation room) set up barracks and jail - -,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,,`,`,` -,`,`,`,,`,`,`,,`,`,`,,`,"r--&j{givename name=""jail3""}","{booze}{givename name=""booze""}",`,`,`,`,"r--&j{givename name=""jail4""}","{booze}{givename name=""booze""}" -,`,`,`,,`,`,`,,`,`,`,,"{preparedfood}{givename name=""prepared food""}",t{Down 4}&,t{Down 4}&,,`,,"{preparedfood}{givename name=""prepared food""}",t{Down 4}&,t{Down 4}& -,,`,,,,`,,,,`,,,,,,,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,,`,,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,"r--&j{givename name=""jail1""}","{booze}{givename name=""booze""}",`,`,`,`,"r--&j{givename name=""jail2""}","{booze}{givename name=""booze""}",,`,`,"r+&{givename name=""sheriff's office""}",`,` -,`,`,`,`,`,`,`,`,`,`,`,,"{preparedfood}{givename name=""prepared food""}",t{Down 22}{Left 10}&,t{Down 22}{Left 4}&,,`,,"{preparedfood}{givename name=""prepared food""}",t{Left 6}&,t{Left 6}&,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,,,"{givename name=""interrogation""}" -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,`,`,`,`,`,`,`,`,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,`,,`,`,`,`,`,,`,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,`,,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,,,`,,`,,`,,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,`,`,`,`,`,`,`,`,`,`,` -,,,,`,`,,`,` +,,,,d,d,,d,d ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` ,`,`,`,`,`,,`,`,`,`,` @@ -2828,14 +2322,14 @@ query_jail/services_query_jail Screenshot: https://drive.google.com/file/d/17jHiCKeZm6FSS-CI4V0r0GJZh09nzcO_ "" Features: -"- Big rooms, optionally pre-furnished. Double-thick walls to ensure engravings add value to the ""correct"" side. Declare locations from the pre-created meeting zones as needed." +"- Big rooms, optionally pre-furnished. Double-thick walls to ensure engravings add maximum value. Declare locations from the pre-created meeting zones as needed." "" Guildhall Walkthrough: 1) Dig out the rooms with /guildhall1. "" -"2) Once the area is dug out, pre-create the zones and add doors and a few statues with /guildhall2. Run ""quickfort orders"" for /guildhall2." +"2) Once the area is dug out, choose a /guildhall2 variant based on your needs. For your first copy of this level, you probably want /guildhall2_default, which includes temple and library zones. For later copies you probably want to create all locations yourself, so you should choose /guildhall2_no_locations if you want the default furnishings or /guildhall2_custom for a full blank slate. Run ""quickfort orders"" for your chosen variant." "" -"3) Furnish individual rooms manually, or get default furnishings for a variety of room types by running /guildhall3. If you use the default furnishings, also run ""quickfort orders"" for /guildhall3. Declare appropriate locations from the pre-created zones as you need guildhalls, libraries, and temples. If you'd like a ""no specific diety"" temple declared for the top room and a library declared for the bottom room, run /guildhall4. Both locations will be ""Residents only"" by default, but you can change this in the (l)ocation menu if you want them to attract visitors. If you need more rooms, you can dig another /guildhall1 in an unused z-level." +"Note that the default temple and library are created ""Residents only"", but you can change this in the location configuration screen if you want them to attract visitors. If you need more rooms, you can dig another /guildhall1 in an unused z-level." "#dig label(guildhall1) start(15; 15; central stairs) message(Once the area is dug out, continue with /guildhall2.)" @@ -2866,33 +2360,44 @@ Guildhall Walkthrough: ,,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d,,,d,d,d,d,d,d,d -#meta label(guildhall2) +#meta label(guildhall2_default) furnished with default temple and library +locations/guildhall_locations doors/guildhall_doors +furnish/guildhall_furnish +"" +#meta label(guildhall2_no_locations) fully furnished and zoned but no locations zones/guildhall_zones -"#build label(guildhall_doors) start(15; 15; central stairs) hidden() message(Remember to enqueue manager orders for this blueprint. -Smooth/engrave tiles, furnish rooms, and declare locations as required.) build doors" - +doors/guildhall_doors +furnish/guildhall_furnish +"" +#meta label(guildhall2_custom) only zones and doors +zones/guildhall_zones +doors/guildhall_doors +"" +"" +"#zone label(guildhall_locations) start(15; 15; central stairs) hidden() message(The library and temple are restricted to residents only by default. If you'd like them to attract vistors, please go to the location screen and change the restrictions.) declare a library and temple" +,m(9x9),,,,,,,,,"m{location=temple name=""All-inclusive temple"" allow=residents}(9x9)",,,,,,,,,m(9x9) ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,~,~,`,`,`,`,`,`,`,~,~,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,,,,,,d,,,,,,d,,d,,,,,,d -,,,,,,,d,,,,,,`,s,`,,,,,,d -,,`,`,`,`,`,`,`,,,,,d,,d,,,,,`,`,`,`,`,`,` +,,,,,,,~,,,,,,~,,~,,,,,,~ +,m(9x9),,,,,,~,,,,,,`,~,`,,,,m(9x9),,~ +,,`,`,`,`,`,`,`,,,,,~,,~,,,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,d,`,d,`,,,,`,d,`,d,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,s,,`,,`,,`,,s,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,d,`,d,`,,,,`,d,`,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,~,`,~,`,,,,`,~,`,~,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,~,,`,,`,,`,,~,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,~,`,~,`,,,,`,~,`,~,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,,,d,,d,,,,,`,`,`,`,`,`,` -,,,,,,,d,,,,,,`,s,`,,,,,,d -,,,,,,,d,,,,,,d,,d,,,,,,d +,,`,`,`,`,`,`,`,,,,,~,,~,,,,,`,`,`,`,`,`,` +,,,,,,,~,,,,,,`,~,`,,,,,,~ +,m(9x9),,,,,,~,,,m{location=library name=Library allow=residents}(9x9),,,~,,~,,,,m(9x9),,~ ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,~,~,`,`,`,`,`,`,`,~,~,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` @@ -2900,7 +2405,7 @@ Smooth/engrave tiles, furnish rooms, and declare locations as required.) build d ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -#zone label(guildhall_zones) start(15; 15; central stairs) hidden() designate zones +"#zone label(guildhall_zones) start(15; 15; central stairs) hidden() designate zones, ready for custom locations to be assigned" ,m(9x9),,,,,,,,,m(9x9),,,,,,,,,m(9x9) ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` @@ -2930,7 +2435,37 @@ Smooth/engrave tiles, furnish rooms, and declare locations as required.) build d ,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -"#build label(guildhall3) start(15; 15; central stairs) message(Remember to enqueue manager orders for this blueprint.) furnish 4 guildhalls, 3 temples, and a library" +#build label(guildhall_doors) start(15; 15; central stairs) hidden() message(Remember to enqueue manager orders for this blueprint.) build doors + + +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,,,,,,d,,,,,,d,,d,,,,,,d +,,,,,,,d,,,,,,`,s,`,,,,,,d +,,`,`,`,`,`,`,`,,,,,d,,d,,,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,d,`,d,`,,,,`,d,`,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,s,,`,,`,,`,,s,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,d,`,d,`,,,,`,d,`,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,,,d,,d,,,,,`,`,`,`,`,`,` +,,,,,,,d,,,,,,`,s,`,,,,,,d +,,,,,,,d,,,,,,d,,d,,,,,,d +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,`,d,d,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` + + +"#build label(guildhall_furnish) start(15; 15; central stairs) hidden() furnish 4 guildhalls, 3 temples, and a library" ,,`,`,`,`,s,`,`,,,`,s,`,c,`,`,f,,,`,`,s,`,`,`,` @@ -2960,36 +2495,6 @@ Smooth/engrave tiles, furnish rooms, and declare locations as required.) build d ,,`,`,`,`,s,`,`,,,h,~c,~c,s,~c,~c,h,,,`,`,s,`,`,`,` -"#query label(guildhall4) start(15; 15; central stairs) message(The library and temple are restricted to residents only by default. If you'd like them to attract vistors, please go to the (l)ocation menu and change the restriction.) declare a library and temple" - - -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,^ilat&^l{Up}r^q,,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,,,,,,`,,,,,,`,,`,,,,,,` -,,,,,,,`,,,,,,`,`,`,,,,,,` -,,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,`,,`,,`,,`,,`,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,,`,`,`,`,`,,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,,,`,,`,,,,,`,`,`,`,`,`,` -,,,,,,,`,,,,,,`,`,`,,,,,,` -,,,,,,,`,,,,,,`,,`,,,,,,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,^ilal&^l{Up}r^q,,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` -,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,`,,,`,`,`,`,`,`,` - - #notes label(beds_help) Suites for nobles and apartments for the teeming masses Suites screenshot: https://drive.google.com/file/d/1IBqCf6fF3lw7sHiBE_15Euubysl5AAiS @@ -2998,23 +2503,17 @@ Apt. screenshot: https://drive.google.com/file/d/1mDQQXG8BnXqasRGFC9R5N6xNALiswE Features: - Well-appointed suites to satisfy nobles - Apartments with beds and storage to keep dwarves happy and the fortress clean -- Apartments also serve as burial chambers since dwarves like looking at coffins -- Meta blueprint included for designating 5 levels of apartments for a full 200+ dwarves "" Suites Walkthrough: 1) Dig out the suites layer with /suites1. "" -"2) Once the area is dug out, furnish the suites with /suites2. The rooms are left unconfigured so you can assign them to specific nobles. Each room can serve as a bedroom, a dining hall, an office, and/or a tomb. Run ""quickfort orders"" for /suites2." +"2) Once the area is dug out, furnish the suites with /suites2. The rooms are left unzoned so you can configure them for specific nobles. Each room can serve as a bedroom, a dining hall, an office, and/or a tomb. Run ""quickfort orders"" for /suites2." "" Apartments Walkthrough: -"1) Dig out one layer of apartments with /apartments1, or 5 layers at once (enough for 200 dwarves) with by adding ""--repeat down,5"" to the quickfort command." +"1) Dig out one layer of apartments with /apartments1, or 5 layers at once (enough for 200 dwarves) with by adding ""--repeat down,5"" to the quickfort command or adjusting the settings in the gui/quickfort UI." "" -"2) Once a layer is dug out, build beds with /apartments2. Run ""quickfort orders"" for /apartments2." -"" -"3) Once the beds are built, configure the rooms and build the remaining furniture with /apartments3. Run ""quickfort orders"" for /apartments3." -"" -"4) Once the coffins are all in place, run ""burial -pets"" to set them all to accept burials." -"#dig label(suites1) start(18; 18; central ramp) message(Once the area is dug out, run /suites2) noble suites" +"2) Once a layer is dug out, build furniture with /apartments2. Run ""quickfort orders"" for /apartments2." +"#dig label(suites1) start(18; 18; central stairs) message(Once the area is dug out, run /suites2) noble suites" ,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d @@ -3050,47 +2549,47 @@ Apartments Walkthrough: ,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d -"#meta label(suites2) start(central ramp) message(Remember to enqueue manager orders for this blueprint. -bedrooms are left unconfigured so you can assign them to specific nobles.) build furniture and set traffic patterns" +"#meta label(suites2) start(central stairs) message(Remember to enqueue manager orders for this blueprint. +rooms are left unzoned so you can configure them for specific nobles.) build furniture and set traffic patterns" traffic_suites/suites_traffic build_suites/suites_build -#dig label(suites_traffic) start(18; 18; central ramp) hidden() +#dig label(suites_traffic) start(18; 18; central stairs) hidden() don't path through other dwarves' rooms ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,,,,or,,,,,,or,,,,`,,`,,,,or,,,,,,or,,,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,,,or,,,,,,or,,,,`,`,`,,,,or,,,,,,or,,,,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,,,,or,,,,,,or,,,,oh,,oh,,,,or,,,,,,or,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,or,,,,,,or,,,,oh,`,oh,,,,or,,,,,,or,,,,`,` +,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,` ,`,`,,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,,`,` -,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,`,`,,,,or,,,,,,or,,,,`,`,`,,,,or,,,,,,or,,,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,,,,,,,,,,,,,`,`,`,,,,,,,,,,,,,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,`,`,,`,`,`,`,`,,`,`,`,`,`,,`,` -,`,`,,,,or,,,,,,or,,,,`,,`,,,,or,,,,,,or,,,,`,` +,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,`,`,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,oh,`,` +,`,`,,,,or,,,,,,or,,,,oh,`,oh,,,,or,,,,,,or,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,,,,,,,,,,,oh,`,oh,,,,,,,,,,,,,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,or,`,`,`,`,`,,`,`,`,`,`,or,oh,`,oh,or,`,`,`,`,`,,`,`,`,`,`,or,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,`,`,`,`,`,,`,`,`,`,`,,oh,`,oh,,`,`,`,`,`,,`,`,`,`,`,,`,` +,`,`,,,,or,,,,,,or,,,,oh,,oh,,,,or,,,,,,or,,,,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -#build label(suites_build) start(18; 18; central ramp) hidden() +#build label(suites_build) start(18; 18; central stairs) hidden() ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` @@ -3126,150 +2625,316 @@ build_suites/suites_build ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -"#dig label(apartments1) start(18; 18; central ramp) message(Once the area is dug out, continue with /apartments2.) apartment complex" - -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d -,,,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d -,,,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d -,,,,,d,,,,d,,,,d,,,d,d,d,,,d,,,,d,,,,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d +"#dig label(apartments1) start(18; 18; central stairs) message(Once the area is dug out, continue with /apartments2.) apartment complex" + +,,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,,,,d,,,d,,,d,,,d,,,d,,d,,,d,,,d,,,d,,,d +,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,,d,,,d,,,d,,,d,,d,d,d,,d,,,d,,,d,,,d,,,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d ,,,,,,,,,,,,,,,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,,d,,d,d,d,,d,d,d,,d,d,d -,,,,,d,,,,d,,,,d,,,d,d,d,,,d,,,,d,,,,d -,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,,d,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,,,d,,,d,,,d,,,d,,,d,d,d,,,d,,,d,,,d,,,d,,,d +,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d ,d,d,d,d,d,d,d,d,d,d,d,d,d,,d,`,~,`,d,,d,d,d,d,d,d,d,d,d,d,d,d,d -,d,d,d,,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,,d,d,d -,,,,,d,,,,d,,,,d,,,d,d,d,,,d,,,,d,,,,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d +,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,`,`,`,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,d,,,d,,,d,,,d,,,d,,,d,d,d,,,d,,,d,,,d,,,d,,,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d ,,,,,,,,,,,,,,,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,,d,,,,d,,,,d,,,d,d,d,,,d,,,,d,,,,d -,,,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d -,,,,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d -,,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d -,,,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d,,d,d,d - -"#build label(apartments2) start(18; 18; central ramp) message(Remember to enqueue manager orders for this blueprint. -Once beds have been placed, continue with /apartments3.) build beds" - -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,b,`,,`,b,`,,`,b,`,,`,b,`,,`,b,`,,`,b,`,,`,b,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,b,`,,`,b,`,,`,b,`,,`,`,`,,`,b,`,,`,b,`,,`,b,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,,,,,,,,,,,,`,`,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,b,`,,`,b,`,,`,b,`,,`,`,`,,`,b,`,,`,b,`,,`,b,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,,`,,`,`,`,,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,` -,`,b,`,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,`,b,` -,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,b,`,,`,b,`,,`,b,`,,`,`,`,,`,b,`,,`,b,`,,`,b,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,,,,,,,,,,,,`,`,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,b,`,,`,b,`,,`,b,`,,`,`,`,,`,b,`,,`,b,`,,`,b,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,b,`,,`,b,`,,`,b,`,,`,b,`,,`,b,`,,`,b,`,,`,b,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` - -#meta label(apartments3) start(central ramp) message(Remember to enqueue manager orders for this blueprint.) configure rooms and build remaining furniture -query_apartments/apartments_rooms -build2_apartments/apartments_build2 -#query label(apartments_rooms) start(18; 18) hidden() configure rooms - -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,`,`,,`,r-&,`,,`,r-&,`,,`,r-&,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,,,,,,,,,,,,`,`,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,`,`,,`,r-&,`,,`,r-&,`,,`,r-&,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,,`,,`,`,`,,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,` -,`,r-&,`,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,`,r-&,` -,`,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,`,`,,`,r-&,`,,`,r-&,`,,`,r-&,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,,,,,,,,,,,,`,`,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,`,`,,`,r-&,`,,`,r-&,`,,`,r-&,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,,`,,,,`,,,,`,,,`,`,`,,,`,,,,`,,,,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,,,,`,,,,`,,,,`,,,,`,,,,`,,,,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` -,,,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,`,,`,r-&,` -,,,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,`,,`,`,` - -"#build label(apartments_build2) start(18; 18) hidden() message(Coffins should be configured with DFHack ""burial"" script) build remaining furniture" - -,,,,n,`,h,,n,`,h,,n,`,h,,n,`,h,,n,`,h,,n,`,h,,n,`,h -,,,,`,~,`,,`,~,`,,`,~,`,,`,~,`,,`,~,`,,`,~,`,,`,~,` -,,,,n,`,f,,n,`,f,,n,`,f,,n,`,f,,n,`,f,,n,`,f,,n,`,f -,,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,s,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,d,,,,d,,,,d,,,`,`,`,,,d,,,,d,,,,d -,,,,n,`,h,,n,`,h,,n,`,h,,`,`,`,,n,`,h,,n,`,h,,n,`,h -,,,,`,~,`,,`,~,`,,`,~,`,,`,s,`,,`,~,`,,`,~,`,,`,~,` -,,,,n,`,f,,n,`,f,,n,`,f,,`,`,`,,n,`,f,,n,`,f,,n,`,f +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,,d,,,d,,,d,,,d,,,d,,d,d,d,,d,,,d,,,d,,,d,,,d +,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d +,,,,d,,,d,,,d,,,d,,,d,,d,,,d,,,d,,,d,,,d +,,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d +,,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d,,d,d + +#meta label(apartments2) start(central stairs) zone rooms and build furniture +zone_apartments/apartments_rooms +build_apartments/apartments_build +#zone label(apartments_rooms) start(18; 18; central stairs) hidden() zone rooms +,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5) +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,,`,,,`,,,`,,,`,,,`,,`,,,`,,,`,,,`,,,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),,`,,`,`,`,b(4x5),`,,b(4x5),`,,b(4x5),`,,b(4x5),`,,b(4x5),` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,,`,`,`,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5) +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,,,`,,,`,,,`,,,`,,,`,`,`,,,`,,,`,,,`,,,`,,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +b(4x5),`,,b(4x5),`,,b(4x5),`,,b(4x5),`,,b(4x5),`,,,`,`,`,b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,,`,`,`,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5),,,b(4x5) +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,`,,,`,,,`,,,`,,,`,,`,`,`,,`,,,`,,,`,,,`,,,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),,`,b(4x5),`,,b(4x5),`,,b(4x5),`,,b(4x5),`,,b(4x5),` +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` + +#build label(apartments_build) start(18; 18; central stairs) hidden() message(Remember to enqueue manager orders for this blueprint.) build furniture + +,,,f,h,,f,h,,f,h,,f,h,,f,h,,h,f,,h,f,,h,f,,h,f,,h,f +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,b,`,,b,`,,b,`,,b,`,,b,`,,`,b,,`,b,,`,b,,`,b,,`,b +,,,,`,,,`,,,`,,,`,,,`,,`,,,`,,,`,,,`,,,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,,,`,,,`,,,`,,,`,,`,`,`,,`,,,`,,,`,,,`,,,` +,b,`,,b,`,,b,`,,b,`,,b,`,,`,`,`,,`,b,,`,b,,`,b,,`,b,,`,b +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,f,h,,f,h,,f,h,,f,h,,f,h,,`,`,`,,h,f,,h,f,,h,f,,h,f,,h,f ,,,,,,,,,,,,,,,,`,`,` -,,,,n,`,h,,n,`,h,,n,`,h,,`,s,`,,n,`,h,,n,`,h,,n,`,h -,,,,`,~,`,,`,~,`,,`,~,`,,`,`,`,,`,~,`,,`,~,`,,`,~,` -,,,,n,`,f,,n,`,f,,n,`,f,,d,,d,,n,`,f,,n,`,f,,n,`,f -,,,,,d,,,,d,,,,d,,,`,`,`,,,d,,,,d,,,,d -,n,`,h,,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,,n,`,h -,`,~,`,d,`,s,`,`,s,`,`,s,`,,`,`,~,`,`,,`,s,`,`,s,`,`,s,`,d,`,~,` -,n,`,f,,`,`,`,`,`,`,`,`,`,d,`,`,`,`,`,d,`,`,`,`,`,`,`,`,`,,n,`,f -,,,,,d,,,,d,,,,d,,,`,`,`,,,d,,,,d,,,,d -,,,,n,`,h,,n,`,h,,n,`,h,,d,,d,,n,`,h,,n,`,h,,n,`,h -,,,,`,~,`,,`,~,`,,`,~,`,,`,`,`,,`,~,`,,`,~,`,,`,~,` -,,,,n,`,f,,n,`,f,,n,`,f,,`,s,`,,n,`,f,,n,`,f,,n,`,f +,h,f,,h,f,,h,f,,h,f,,h,f,,`,`,`,,f,h,,f,h,,f,h,,f,h,,f,h +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,`,b,,`,b,,`,b,,`,b,,`,b,,`,,`,,b,`,,b,`,,b,`,,b,`,,b,` +,`,,,`,,,`,,,`,,,`,,,`,`,`,,,`,,,`,,,`,,,`,,,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,,`,`,~,`,`,,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,`,,,`,,,`,,,`,,,`,,,`,`,`,,,`,,,`,,,`,,,`,,,` +,`,b,,`,b,,`,b,,`,b,,`,b,,`,,`,,b,`,,b,`,,b,`,,b,`,,b,` +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,h,f,,h,f,,h,f,,h,f,,h,f,,`,`,`,,f,h,,f,h,,f,h,,f,h,,f,h ,,,,,,,,,,,,,,,,`,`,` -,,,,n,`,h,,n,`,h,,n,`,h,,`,`,`,,n,`,h,,n,`,h,,n,`,h -,,,,`,~,`,,`,~,`,,`,~,`,,`,s,`,,`,~,`,,`,~,`,,`,~,` -,,,,n,`,f,,n,`,f,,n,`,f,,`,`,`,,n,`,f,,n,`,f,,n,`,f -,,,,,d,,,,d,,,,d,,,`,`,`,,,d,,,,d,,,,d -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,s,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` -,,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d,,,,d -,,,,n,`,h,,n,`,h,,n,`,h,,n,`,h,,n,`,h,,n,`,h,,n,`,h -,,,,`,~,`,,`,~,`,,`,~,`,,`,~,`,,`,~,`,,`,~,`,,`,~,` -,,,,n,`,f,,n,`,f,,n,`,f,,n,`,f,,n,`,f,,n,`,f,,n,`,f +,f,h,,f,h,,f,h,,f,h,,f,h,,`,`,`,,h,f,,h,f,,h,f,,h,f,,h,f +,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,b,`,,b,`,,b,`,,b,`,,b,`,,`,`,`,,`,b,,`,b,,`,b,,`,b,,`,b +,,`,,,`,,,`,,,`,,,`,,`,`,`,,`,,,`,,,`,,,`,,,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,` +,,,,`,,,`,,,`,,,`,,,`,,`,,,`,,,`,,,`,,,` +,,,b,`,,b,`,,b,`,,b,`,,b,`,,`,b,,`,b,,`,b,,`,b,,`,b +,,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,`,,`,` +,,,f,h,,f,h,,f,h,,f,h,,f,h,,h,f,,h,f,,h,f,,h,f,,h,f + +#notes label(crypt_help) +Places to rest your dead +Screenshot: https://drive.google.com/file/d/1IBqCf6fF3lw7sHiBE_15Euubysl5AAiS +"" +Features: +"- Staged crypt expansion that grows with your, um, need" +- 52 tombs in stage 1 +- 160 additional tombs in stage 2 (212 tombs total) +- Extendable in any direction by applying the blueprints multiple times +"" +Crypt Walkthrough: +1) Dig out the layer with /crypt1. +"" +"2) Once the area is dug out, add initial tombs with /crypt2." +"" +"3) If/when you need additional tombs, apply /crypt3." +"#dig label(crypt1) start(18; 18; central stairs) message(Once the area is dug out, continue with /crypt2.) crypt complex" + +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,d,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,d,d,d,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,d,d,d,,,,d,d,d,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,,~,,d,d,d,d +,,d,,d,,d,,d,,d,,d,d,d,d,,,,d,d,d,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,d,d,d,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,d,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,d,d,d,d,d,d,d,d,d,d,d,d,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d,d +,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d,,d +,,,,,,,,,,,,d,d,d,d,d,d,d,d,d,d,d + +#meta label(crypt2) start(central stairs) small crypt that can be extended later +zone/crypt_zone +build/crypt_build +"" +#meta label(crypt3) start(central stairs) crypt extension +zone_extended/crypt_extended_zone +build_extended/crypt_extended_build +#zone label(crypt_zone) start(18; 18; central stairs) hidden() zone tombs + + + + + + + + + +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,`,`,`,,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,`,`,`,,,,`,`,`,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,,~,,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,`,`,`,,,,`,`,`,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,`,`,`,,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,` +,,,,,,,,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` + + + + + + + + + +#build label(crypt_build) start(18; 18; central stairs) hidden() message(Remember to enqueue manager orders for this blueprint.) build urns + + + + + + + + + +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n +,,,,,,,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,` +,,,,,,,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,n,,n,,`,,n,,`,s,`,,n,,`,,n,,n +,,,,,,,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,` +,,,,,,,,n,,n,,`,`,`,`,,,,`,`,`,`,,n,,n +,,,,,,,,,,,,`,`,s,`,,~,,`,s,`,` +,,,,,,,,n,,n,,`,`,`,`,,,,`,`,`,`,,n,,n +,,,,,,,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,` +,,,,,,,,n,,n,,`,,n,,`,s,`,,n,,`,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,,,,,,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n +,,,,,,,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,` +,,,,,,,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` + + + + + + + + + +#zone label(crypt_extended_zone) start(18; 18; central stairs) hidden() zone more tombs + +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,,~,,`,`,`,,~,,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,`,`,`,,,,`,`,`,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,,~,,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,`,`,`,,,,`,`,`,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,,~,,`,`,`,,~,,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,`,,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1),,T{pets=true}(1x1) +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` + +#build label(crypt_extended_build) start(18; 18; central stairs) hidden() message(Remember to enqueue manager orders for this blueprint.) build more urns + +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,,~,,`,~,`,,~,,`,,~,,~,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,`,`,`,,,,`,`,`,`,,~,,~,,n,,n,,n +,,,,,,,,,,,,`,`,~,`,,~,,`,~,`,` +,,n,,n,,n,,~,,~,,`,`,`,`,,,,`,`,`,`,,~,,~,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,`,`,`,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,,~,,`,~,`,,~,,`,,~,,~,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,~,,~,,`,,~,,~,,~,,~,,`,,~,,~,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,`,`,`,`,`,`,`,`,`,`,`,`,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,`,` +,,n,,n,,n,,n,,n,,`,,n,,n,,n,,n,,`,,n,,n,,n,,n,,n +,,,,,,,,,,,,`,`,`,`,`,`,`,`,`,`,` From 25530d6e7be72a8839ea9975e2dc74789a23d934 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 7 Jun 2023 12:26:49 -0700 Subject: [PATCH 1258/2222] guide update - first draft --- docs/guides/quickfort-user-guide.rst | 1362 ++++++++++++++++---------- library/xml | 2 +- scripts | 2 +- 3 files changed, 822 insertions(+), 544 deletions(-) diff --git a/docs/guides/quickfort-user-guide.rst b/docs/guides/quickfort-user-guide.rst index ec416f18f..a06413d40 100644 --- a/docs/guides/quickfort-user-guide.rst +++ b/docs/guides/quickfort-user-guide.rst @@ -4,12 +4,12 @@ Quickfort blueprint creation guide ================================== -`Quickfort ` is a DFHack script that helps you build fortresses from +`Quickfort ` is a DFHack tool that helps you build fortresses from "blueprint" .csv and .xlsx files. Many applications exist to edit these files, such as MS Excel and `Google Sheets `__. Most layout and building-oriented DF commands are supported through the use of multiple files or -spreadsheets, each describing a different phase of DF construction: designation, -building, placing stockpiles/zones, and setting configuration. +spreadsheets, each describing a different phase of DF construction: designating +digging, defining zones, placing stockpiles, and building. The original idea came from :wiki:`Valdemar's ` auto-designation macro. Joel Thornton reimplemented the core logic in Python and extended its @@ -21,51 +21,44 @@ interacts with Dwarf Fortress memory structures directly, allowing for instantaneous blueprint application, error checking and recovery, and many other advanced features. -This guide focuses on DFHack Quickfort's capabilities and teaches players how -to understand and create blueprint files. Some of the text was originally -written by Joel Thornton, reused here with his permission. +This guide focuses on DFHack Quickfort's capabilities and blueprint syntax, and +teaches players how to understand and create blueprint files. Some of the text +was originally written by Joel Thornton, reused here with his permission. -For those just looking to apply existing blueprints, check out the `quickfort -command's documentation ` for syntax. There are also many -ready-to-use blueprints available in the ``hack/data/blueprints`` subfolder in -your DFHack installation. Browse them on your computer or -:source:`online `, or run `gui/quickfort` to browse -and apply them to your fort! +If you are just looking to apply existing blueprints to your fort, check out +`gui/quickfort` (or `quickfort` for the commandline version). There are many +ready-to-use blueprints available in the `quickfort-library-guide` distributed +with DFHack. Before you become an expert at writing blueprints, though, you should know that the easiest way to make a quickfort blueprint is to build your plan "for real" -in Dwarf Fortress and then export your map using the DFHack `blueprint` plugin. -You can apply those blueprints as-is in your next fort, or you can fine-tune -them with additional features from this guide. +in Dwarf Fortress and then export your map using `gui/blueprint`. You can apply +those blueprints as-is in your next fort, or you can fine-tune them with +additional features from this guide. See the `Links`_ section for more information and online resources. - .. contents:: Table of Contents :local: :depth: 2 - Features -------- - General - - Manages blueprints to handle all phases of DF construction - - Supports .csv and multi-worksheet .xlsx blueprint files + - Blueprint modes for all phases of fort design + - Read blueprints from .csv or multi-worksheet .xlsx files - Near-instant application, even for very large and complex blueprints - Blueprints can span multiple z-levels - - You can package all blueprints and keystroke aliases needed for an entire - fortress in a single file for easy sharing - - "meta" blueprints that simplify the application of sequences of blueprints - - Undo functionality for dig, build, place, and zone blueprints - - Rotate blueprints or flip them around to your preference when you apply - them to the map - - Automatic cropping of blueprints so you don't get errors if the blueprint - extends off the map - - Can generate manager orders for everything required by a build blueprint + - Easy sharing of blueprints with multi-blueprint files + - Scripted application of sequences of blueprints + - Easy undo + - Rotate blueprints or flip them around + - Automatic cropping of blueprints that extend off the map + - Generate manager orders for items required by a blueprint - Includes a library of ready-to-use blueprints - - Blueprint debugging features + - Blueprint debugging - Dig mode @@ -76,58 +69,44 @@ Features - Handles carving arbitrarily complex minecart tracks, including tracks that cross other tracks +- Zone and place modes + + - Define zones and stockpiles of any shape, not just rectangles + - Configurable numbers of bins, barrels and wheelbarrows assigned to created + stockpiles + - Automatic splitting of stockpiles that exceed maximum dimension limits + - Create and attach locations to zones + - Full control over stockpile configuration based on the `stockpiles` + settings library + - Configurable zone/location settings, such as the pit/pond toggle or + hospital supply quantities + - Build mode - - Fully integrated with DFHack buildingplan: you can place buildings before - manufacturing building materials and you can use the buildingplan UI for - setting materials preferences + - Integrated with DFHack `buildingplan`: you can place buildings before + manufacturing building materials and you can use the `buildingplan` UI + for setting materials and quality preferences - Designate entire constructions in mid-air without having to wait for each tile to become supported - Automatic expansion of building footprints to their minimum dimensions, so only the center tile of a multi-tile building needs to be recorded in the blueprint - Tile occupancy and validity checking so, for example, buildings that - cannot be placed on a target tile will be skipped instead of messing up - the blueprint. Blueprints that are only partially applied for any reason - (e.g. you need to dig out some more tiles) can be safely reapplied to - build the remaining buildings. + cannot be placed on a target tile will be skipped instead of causing + errors and interrupting the blueprint. Blueprints that are only partially + applied for any reason (e.g. you need to dig out some more tiles) can be + safely reapplied to build the remaining buildings. - Relaxed rules for farm plot and road placement: you can still place the building even if an invalid tile (e.g. stone tiles for farm plots) splits the designated area into two disconnected parts - Intelligent boundary detection for adjacent buildings of the same type (e.g. a 6x6 block of ``wj`` cells will be correctly split into 4 jeweler's workshops) + - Set building properties (such as a name) + - Attach track stops to hauling routes -- Place and zone modes - - - Define stockpiles and zones of any shape, not just rectangles - - Configurable numbers of bins, barrels and wheelbarrows assigned to created - stockpiles - - Automatic splitting of stockpiles and zones that exceed maximum dimension - limits - - Fully configurable zone settings, such as pit/pond and hospital supply - counts - -- Query mode - - - Send arbitrary keystroke sequences to the UI -- *anything* you can do - through the UI is supported - - Supports aliases to simplify frequent keystroke combos - - Includes a library of pre-made and tested aliases to simplify most common - tasks, such as configuring stockpiles for important item types or creating - hauling routes for quantum stockpiles. - - Supports expanding aliases in other aliases for easy management of common - subsequences - - Supports repeating key sequences a specified number of times - - Skips sending keys when the cursor is over a tile that does not have a - stockpile or building, so missing buildings won't desynchronize your - blueprint - - Instant halting of query blueprint application when keystroke errors are - detected, such as when a mistake in a key sequence leaves us stuck in a - submenu, to make query blueprints easier to debug - -Creating blueprints -------------------- +Introduction to blueprints +-------------------------- We recommend using a spreadsheet editor such as Excel, `Google Sheets `__, or `LibreOffice `__ @@ -138,29 +117,27 @@ line (or upper-left cell) of the spreadsheet should look like this:: #dig -The keyword ``dig`` tells Quickfort we are going to be using the Designations -menu in DF. The following "mode" keywords are understood: +The keyword ``dig`` tells Quickfort we are going to be specifying designations. +The following "mode" keywords are understood: ============== =========== Blueprint mode Description ============== =========== -dig Designations menu (:kbd:`d`) -build Build menu (:kbd:`b`) -place Place stockpiles menu (:kbd:`p`) -zone Activity zones menu (:kbd:`i`) -query Set building tasks/prefs menu (:kbd:`q`) +dig Designations (digging, traffic, dumping, etc.) +build Constructions, buildings, and furniture +place Stockpiles +zone Activity zones ============== =========== If no modeline appears in the first cell, Quickfort assumes that it's looking at a ``#dig`` blueprint. There are also other modes that don't directly correspond to Dwarf Fortress -menus, but we'll talk about those `later `. +design operations, but we'll talk about those `later `. If you like, you may enter a comment after the mode keyword. This comment will -appear in the output of ``quickfort list`` when run from the ``DFHack#`` prompt -or in the dialog window when running `gui/quickfort`. You can use this space for -explanations, attribution, etc.:: +appear in the output of ``quickfort list`` or in the dialog window when running +`gui/quickfort`. You can use this space for explanations, attribution, etc.:: #dig grand dining room @@ -176,12 +153,31 @@ readability, but a real .csv file would have commas):: d d d d # # # # # # +The letter ``d`` here stands for "dig". The character sequences in these +blueprints are based on the old (pre-v50) keyboard shortcuts for the various DF +menus. Please see the `quickfort_guide_appendix` below for a full listing. + Note the :kbd:`#` symbols at the right end of each row and below the last row. These are completely optional, but can be helpful to make the row and column positions clear. -Once the dwarves have that dug out, let's build a walled-in bedroom within our -dug-out area:: +Once the dwarves have that dug out, let's zone it as a bedroom:: + + #zone + b b b b # + b b b b # + b b b b # + b b b b # + # # # # # + +This looks very similar to the ``#dig`` blueprint above, but with ``b``s +instead of ``d``s. The ``b``s mark the area for a ``b``edroom zone just like +the ``#dig`` blueprint marked the area for digging. It's important to wait +until after the area is completely dug out before applying further blueprints +since zones can't be applied to hidden tiles and furniture can't be built in +undug walls. + +Now, let's add some walls and furniture:: #build Cw Cw Cw Cw # @@ -190,17 +186,17 @@ dug-out area:: Cw Cw Cw # # # # # # -Note my generosity -- in addition to the bed (:kbd:`b`) I've built a container -(:kbd:`h`) here for the dwarf as well. You must use the full series of keys -needed to build something in each cell, e.g. :kbd:`C`:kbd:`w` indicates we -should enter DF's constructions submenu (:kbd:`C`) and select walls (:kbd:`w`). +The :kbd:`C`:kbd:`w` cells represent the constructed walls, leaving space for a +door that we might want to add later. And note my generosity -- in addition to +the bed (:kbd:`b`) I've built a container (:kbd:`h`) here for the dwarf as +well. -I'd also like to place a booze stockpile in the 2 unoccupied tiles in the room:: +Finally, let's place a booze stockpile in the 2 unoccupied tiles in the room:: - #place Place a food stockpile + #place personal booze stockpile ` ` ` ` # ` ~ ~ ` # - ` f f ` # + ` f f{name="bedroom booze"}:=booze ` ` ` # # # # # # @@ -213,45 +209,19 @@ multilayer or fortress-wide blueprint layouts as "chalk lines". QF is smart enough to recognize this as a 2x1 food stockpile, and creates it as such rather than as two 1x1 food stockpiles. Quickfort treats any connected region of identical designations as a single entity. The tiles can be connected -orthogonally or diagonally, just as long as they are touching. +orthogonally or diagonally, just as long as they are touching. You can also +treat disconnected segments as belonging to the same stockpile, but we'll get +into that later. -Lastly, let's turn the bed into a bedroom and set the food stockpile to hold -only booze. +Now what's all that business attached to the second ``f``? The part between the +curly brackets specifies properties, in this case the name that we want to give +the stockpile. The remaining part, from the colon (``:``) onward, applies the +``booze`` preset from the `stockpiles` library. That will configure the +stockpile to accept only booze. You can use presets (along with other options +that we'll go over later) to configure stockpiles however you want, directly +from the ``#place`` blueprint. -:: - - #query - ` ` ` ` # - ` r& ` # - ` booze # - ` ` ` ` # - # # # # # - -In row 2, column 2 we have ``r&``. This sends the :kbd:`r` key to DF when the -cursor is over the bed, causing us to "make room" and :kbd:`Enter`, represented -by special ``&`` alias, to indicate that we're done setting the size (the -default room size is fine here). - -In column 2, row 3 we have ``booze``. This is one of many alias keywords defined -in the included :source:`aliases library `. -This particular alias sets a food stockpile to accept only booze. It sends the -keys needed to navigate DF's stockpile settings menu, and then it sends an -Escape character to exit back to the map. It is important to exit out of any -menus that you enter while in query mode so that the cursor can move to the next -tile when it is done with the current tile. - -If there weren't an alias named ``booze`` then the literal characters -:kbd:`b`:kbd:`o`:kbd:`o`:kbd:`z`:kbd:`e` would have been sent, so be sure to -spell those aliases correctly! - -You can save a lot of time and effort by using aliases instead of adding all -key sequences directly to your blueprints. For more details, check out the -`quickfort-alias-guide`. You can also see examples of aliases being used in the -query blueprints in the -:source:`DFHack blueprint library `. You can create -your own aliases by adding them to :source:`dfhack-config/quickfort/aliases.txt` -in your DFHack folder or you can package them -`together with your blueprint files `. +And that's it! You now have a series of blueprints that you can "stamp" across your fort to quickly build new bedrooms. Area expansion syntax ~~~~~~~~~~~~~~~~~~~~~ @@ -273,7 +243,7 @@ In Quickfort, the following blueprints are equivalent:: The second example uses Quickfort's "area expansion syntax", which takes the form:: - keys(WxH) + text(WxH) Note that area expansion syntax can only specify rectangular areas. If you want to create extent-based structures (e.g. farm plots or stockpiles) in different @@ -333,6 +303,36 @@ blueprint could also be written as:: ga(4x-2) ` # # # # # # +Property syntax +~~~~~~~~~~~~~~~ + +Many things you can designate with `quickfort` are configurable. All buildings, +stockpiles, and zones, for example, can be named. These configuration elements +are expressed as properties. + +Properties are written between curly brackets (``{}``). There can be multiple +properties defined between those brackets, separated by spaces. Each property +has a name and a value, with an equal sign to connect them. If a property value +has a space within it, it should be surrounded by double quotes (``"``). + +If you have defined the area of something over multiple spreadsheet cells, you +can specify properties in just one of those cells and they will apply to the +whole object. You can even split properties up among multiple cells if that is +more convenient. If you are using expansion syntax, the expansion part always +goes last. + +Here's an example of a seed stockpile that is configured to take from a seed feeder stockpile:: + + #place + f{name=Seeds links_only=true}:=seeds(3x2) + + f + f{name="Seeds feeder" give_to=Seeds}:=seeds + f{containers=0} + +Different modes and different types may have different properties that you can +configure. See the `quickfort_guide_appendix` for a full list. + Automatic area expansion ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -406,15 +406,21 @@ You can go up or down multiple levels by adding a number after the ``<`` or #>2 r(10x10) +#dig mode +--------- + +``#dig`` blueprints are normally the first step in any design. They define the +boundaries and layouts for the blueprints for later stages of construction. Despite their name, ``#dig``` blueprints are for more than just digging. They also handle smoothing, carving, traffic designations, and marking items on the ground for dumping, forbidding, or other similar tags. See the full list of supported designations in the `#dig mode reference`_. + .. _quickfort-dig-priorities: Dig priorities ~~~~~~~~~~~~~~ -DF designation priorities are supported for ``#dig`` blueprints. The full syntax -is ``[letter][number][expansion]``, where if the ``letter`` is not specified, +DF designation priorities are supported in ``#dig`` blueprints. The full syntax +is ``[symbol][number][expansion]``, where if the ``symbol`` is not specified, ``d`` is assumed, and if ``number`` is not specified, ``4`` is assumed (the -default priority). So each of these blueprints is equivalent:: +default priority). So all of these blueprints are equivalent:: #dig dig the interior of the room at high priority d d d d d # @@ -457,222 +463,24 @@ center of the room marked for digging later:: d d d d d # # # # # # # -Then you can use "Toggle Standard/Marking" (:kbd:`d`:kbd:`M`) to convert the -center tiles to regular designations at your leisure. +Then you can use DF's "Toggle Standard/Marking" icons (DF calls them +"blueprints", but hopefully that won't get too confusing in this context) to +convert the center tiles to regular designations at your leisure. To apply an entire dig blueprint in marker mode, regardless of what the blueprint itself says, you can set the global quickfort setting -``force_marker_mode`` to ``true`` before you apply the blueprint. +``force_marker_mode`` to ``true`` before you apply the blueprint by running +``quickfort set force_marker_mode true``. -Note that the in-game UI setting "Standard/Marker Only" (:kbd:`d`:kbd:`m`) does -not have any effect on quickfort. +Note that the state of the in-game vanilla button that you use to draw +designations in either Standard or "Blueprint" mode does not have any effect on +`quickfort`. -Stockpiles and zones -~~~~~~~~~~~~~~~~~~~~ - -It is very common to have stockpiles that accept multiple categories of items or -zones that permit more than one activity. Although it is perfectly valid to -declare a single-purpose stockpile or zone and then modify it with a ``#query`` -blueprint, quickfort also supports directly declaring all the types in the -``#place`` and ``#zone`` blueprints. For example, to declare a 20x10 stockpile -that accepts both corpses and refuse, you could write:: - - #place refuse heap - yr(20x10) - -And similarly, to declare a zone that is a pasture, a fruit picking area, and a -meeting area all at once:: - - #zone main pasture and picnic area - nmg(10x10) - -The order of the individual letters doesn't matter. If you want to configure the -stockpile from scratch in a ``#query`` blueprint, you can place unconfigured -"custom" stockpiles with (:kbd:`c`). It is more efficient, though, to place -stockpiles using the keys that represent the categories of items that you want -to store, and then only use a ``#query`` blueprint if you need fine-grained -customization. - -.. _quickfort-place-containers: - -Stockpile bins, barrels, and wheelbarrows -````````````````````````````````````````` - -Quickfort has global settings for default values for the number of bins, -barrels, and wheelbarrows assigned to stockpiles, but these numbers can be set -for individual stockpiles as well. - -To set the number of bins, barrels, or wheelbarrows, just add a number after the -letter that indicates what type of stockpile it is. For example:: - - #place a stone stockpile with 5 wheelbarrows - s5(3x3) - - #place a bar, ammo, weapon, and armor stockpile with 20 bins - bzpd20(5x5) - -If the specified number exceeds the number of available stockpile tiles, the -number of available tiles is used. For wheelbarrows, that limit is reduced by 1 -to ensure there is at least one non-wheelbarrow tile available in the stockpile. -Otherwise no stone would ever be brought to the stockpile since all tiles would -be occupied by wheelbarrows! - -Quickfort figures out which container type is being set by looking at the letter -that comes just before the number. For example ``zf10`` means 10 barrels in a -stockpile that accepts both ammo and food, whereas ``z10f`` means 10 bins. If -the stockpile category doesn't usually use any container type, like refuse or -corpses, wheelbarrows are assumed:: - - #place a corpse stockpile with 3 wheelbarrows - y3(3x3) - -Note that if you are not using expansion syntax, each tile of the stockpile must -have the same text. Otherwise the stockpile boundaries will not be detected -properly:: - - #place a non-rectangular animal stockpile with 5 wheelbarrows - a5,a5,a5,a5 - a5, , ,a5 - a5, , ,a5 - a5,a5,a5,a5 - -Running ``quickfort orders`` on a ``#place`` blueprint with explicitly set -container/wheelbarrow counts will enqueue manager orders for the specified -number of containers or wheelbarrows, even if that number exceeds the in-game -size of the stockpile. For example, ``quickfort orders`` on the following -blueprint will enqueue 10 rock pots, even though the stockpile only has 9 -tiles:: - - #place - f10(3x3) - -Zone detailed configuration -``````````````````````````` - -Detailed configuration for zones, such as the pit/pond toggle, can also be set -by mimicking the hotkeys used to set them. Note that gather flags default to -true, so specifying them in a blueprint will turn the toggles off. If you need -to set configuration from multiple zone subscreens, separate the key sections -with :kbd:`^`. Note the special syntax for setting hospital supply levels, which -have no in-game hotkeys:: - - #zone a combination hospital and shrub (but not fruit) gathering zone - gGtf^hH{hospital buckets=5 splints=20}(10x10) - -The valid hospital settings (and their maximum values) are:: - - thread (1500000) - cloth (1000000) - splints (100) - crutches (100) - plaster (15000) - buckets (100) - soap (15000) - -To toggle the ``active`` flag for zones, add an :kbd:`a` character to the -string. For example, to create a *disabled* pond zone (that you later intend to -carefully fill with 3-depth water for a dwarven bathtub):: - - #zone disabled pond zone - apPf(1x3) - -Minecart tracks -~~~~~~~~~~~~~~~ - -There are two ways to produce minecart tracks, and they are handled very -differently by the game. You can carve them into hard natural floors or you can -construct them out of building materials. Constructed tracks are conceptually -simpler, so we'll start with them. - -Constructed tracks -`````````````````` - -Quickfort supports the designation of track stops and rollers in ``#build`` -blueprints. You can build a track stop with :kbd:`C`:kbd:`S` and some number of -:kbd:`d` and :kbd:`a` characters for selecting dump direction and friction. You -can build a roller with :kbd:`M`:kbd:`r` and some number of :kbd:`s` and -:kbd:`q` characters for direction and speed. However, this can get confusing -very quickly and is very difficult to read in a blueprint. Moreover, constructed -track segments don't even have keys associated with them at all! - -To solve this problem, Quickfort provides the following keywords for use in -build blueprints:: - - -- Track segments -- - trackN - trackS - trackE - trackW - trackNS - trackNE - trackNW - trackSE - trackSW - trackEW - trackNSE - trackNSW - trackNEW - trackSEW - trackNSEW - - -- Track/ramp segments -- - trackrampN - trackrampS - trackrampE - trackrampW - trackrampNS - trackrampNE - trackrampNW - trackrampSE - trackrampSW - trackrampEW - trackrampNSE - trackrampNSW - trackrampNEW - trackrampSEW - trackrampNSEW - - -- Horizontal and vertical roller segments -- - rollerH - rollerV - rollerNS - rollerSN - rollerEW - rollerWE - - Note: append up to four 'q' characters to roller keywords to set roller - speed. E.g. a roller that propels from East to West at the slowest speed can - be specified with 'rollerEWqqqq'. - - -- Track stops that (optionally) dump to the N/S/E/W -- - trackstop - trackstopN - trackstopS - trackstopE - trackstopW - - Note: append up to four 'a' characters to trackstop keywords to set friction - amount. E.g. a stop that applies the smallest amount of friction can be - specified with 'trackstopaaaa'. - -As an example, you can create an E-W track with stops at each end that dump to -their outside directions with the following blueprint:: - - #build Example track - trackstopW trackEW trackEW trackEW trackstopE - -Note that the **only** way to build track and track/ramp segments is with the -keywords. The UI method of using :kbd:`+` and :kbd:`-` keys to select the track -type from a list does not work since DFHack Quickfort doesn't actually send keys -to the UI to build buildings. The text in your spreadsheet cells is mapped -directly onto DFHack API calls. Only ``#query`` blueprints send actual keycodes -to the UI. - -Carved tracks -````````````` +Carved minecart tracks +~~~~~~~~~~~~~~~~~~~~~~ In the game, you carve a minecart track by specifying a beginning and ending -tile and the game "adds" the designation to the tiles in between. You cannot +tile, and the game "adds" the designation to the tiles in between. You cannot designate single tiles because DF needs a multi-tile track to figure out which direction the track should go on each tile. For example to carve two track segments that cross each other, you might use the cursor to designate a line of @@ -701,8 +509,8 @@ track of the form:: Quickfort supports both styles of specification for carving tracks with ``#dig`` blueprints. You can use the "additive" style to carve tracks in segments or you -can use the aliases to specify the track tile by tile. To designate track -segments, use area expansion syntax with a height or width of 1:: +can use the ``track`` aliases to specify the track tile by tile. To designate +track segments, use area expansion syntax with a height or width of 1:: #dig ` T(1x3) ` # @@ -757,10 +565,239 @@ masterwork engravings for destruction (unless forced to by a commandline parameter). You would run (and let your dwarves complete the jobs for) the sequence of blueprints until no tiles are designated by the "erase" blueprint. +#zone mode +---------- + +Zones define how regions of your fort should be treated. They are also the anchor point for "locations" like taverns and hospitals. Unlike stockpiles or buildings, zones can overlap, which can lead to some interesting layouts. + +Zone designation syntax +~~~~~~~~~~~~~~~~~~~~~~~ + +A zone is declared with a symbol followed by optional properties:: + + #zone a single tile garbage dump zone + d + + #zone a single tile garbage dump zone named "The Dump" + d{name="The Dump"} + + #zone interrogation room + o{name=Interrogation assigned_unit=sheriff} + + #zone a small inactive pond zone + p{name="Fill me" pond=true active=false}(3x3) + +If you want multiple zones that have the same footprint, they can be declared +from the same cell:: + + #zone pasture and training area + n{name="Main pasture"}t{name="Pet training area"}(14x10) + +or from different corners of the same rectangle:: + + #zone pasture and training area + n{name="Main pasture"}(10x2) + t{name="Pet training area"}(10x-2) + +and you can use this technique to achieve partial overlap, of course. The only configuration that can't be specified in a single blueprint is multiple non-rectangular zones that are partially overlapping. You will have to use multiple ``#zone`` blueprints to achieve that. + +Locations, locations, locations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Hospitals, guildhalls, taverns, libraries, and temples are locations. You can +declare a location in the properties for a zone:: + + #zone metalcrafter hall + m{location=guildhall profession=metalcrafter}(7x7) + +You can attach multiple zones to a single location by giving the location a +label (not a name -- you can name zones, but you can't directly name locations) +and then using that label for each of the zones you want to attach:: + + #zone tavern and rented room + b{location=tavern/bigpub name="Rent me"}(3x1) + h{location=tavern/bigpub name="Central pub" allow=residents}(25x40) + +Note that the label ("bigpub" in this case) will never appear in-game. It is only used in the context of the blueprint to identify a common location. + +#place mode +----------- + +``#place`` mode is dedicated to stockpiles, which are a major design element in any fortress. + +Stockpile designation syntax +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Just like zones, stockpiles can have properties like names or lists of other stockpiles to take from. Unlike zones, stockpiles can have configuration specifiers for exactly what types of items to accept. The full syntax looks like this:: + + types{properties}:configuration(expansion) + +You're already familiar with `Property syntax`_ and `Area expansion syntax`_, so let's focus in on the remaining elements. + +Stockpile types +~~~~~~~~~~~~~~~ + +The type of stockpile corresponds to the category of items it accepts. Some types will cause the stockpile to accept bins or barrels. See the full list in the `#place mode reference`_. + +It is very common to have stockpiles that accept multiple categories of items. +Although it is perfectly valid to declare a single-purpose stockpile, +`quickfort` also supports directly declaring all the categories at once. For +example, to declare a 20x10 stockpile that accepts both corpses and refuse, you +could write:: + + #place refuse heap + yr(20x10) + +The order of the individual letters doesn't matter. If you want to configure the +stockpile from scratch, you can place unconfigured "custom" stockpiles with (:kbd:`c`). It is more efficient, though, to place +stockpiles using the keys that represent the categories of items that you want +to store, and then only use a ``#query`` blueprint if you need fine-grained +customization. + +.. _quickfort-place-containers: + +Bins, barrels, and wheelbarrows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Quickfort has global settings for default values for the number of bins, +barrels, and wheelbarrows assigned to stockpiles, but these numbers can be set +for individual stockpiles as well. + +To set the number of bins, barrels, or wheelbarrows, just add a number after the +letter that indicates what type of stockpile it is. For example:: + + #place a stone stockpile with 5 wheelbarrows + s5(3x3) + + #place a bar, ammo, weapon, and armor stockpile with 20 bins + bzpd20(5x5) + +If the specified number exceeds the number of available stockpile tiles, the +number of available tiles is used. For wheelbarrows, that limit is reduced by 1 +to ensure there is at least one non-wheelbarrow tile available in the stockpile. +Otherwise no stone would ever be brought to the stockpile since all tiles would +be occupied by wheelbarrows! + +Quickfort figures out which container type is being set by looking at the letter +that comes just before the number. For example ``zf10`` means 10 barrels in a +stockpile that accepts both ammo and food, whereas ``z10f`` means 10 bins. If +the stockpile category doesn't usually use any container type, like refuse or +corpses, wheelbarrows are assumed:: + + #place a corpse stockpile with 3 wheelbarrows + y3(3x3) + +Note that if you are not using expansion syntax, each tile of the stockpile must +have the same text. Otherwise the stockpile boundaries will not be detected +properly:: + + #place a non-rectangular animal stockpile with 5 wheelbarrows + a5,a5,a5,a5 + a5, , ,a5 + a5, , ,a5 + a5,a5,a5,a5 + +Running ``quickfort orders`` on a ``#place`` blueprint with explicitly set +container/wheelbarrow counts will enqueue manager orders for the specified +number of containers or wheelbarrows, even if that number exceeds the in-game +size of the stockpile. For example, ``quickfort orders`` on the following +blueprint will enqueue 10 rock pots, even though the stockpile only has 9 +tiles:: + + #place + f10(3x3) + +#build mode +----------- + +``#build`` mode handles buildings, furniture (which are also "buildings" +according to DF), constructions (including constructed tracks), and hauling +routes. + +Building designation syntax +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Other than names, most buildings do not have any extra properties. See the +`#build mode reference`_ for those that do. + +The syntax otherwise looks just like stockpiles, except that it only makes +sense to have a single symbol to indicate what to build on that tile:: + + symbol{properties}:configuration(expansion) + +Here's an example of a simple 5x5 square of flooring:: + + #build + Cf(5x5) + +or a named Jeweler's workshop that takes from specific stockpiles:: + + #build + wj{name="Encrusting center" take_from="Furniture,Gem storage"} + +The ``:configuration`` part is only relevant for hauling routes, which we'll go +over in the next section. + +Hauling route definitions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Hauling routes are defined by properties and configuration attached to track +stops. You can define a single-stop hauling route for a quantum stockpile as +easily as a multi-stop stone quarry transportation line. The stockpile-like +``:configuration`` part of the syntax controls which item types are considered +"desired" for the hauling route stop. If it's not specified, then all item +types are accepted. This is the most common case since most hauling route +contents are filtered by the stockpiles that the stops take from, but the +flexibility is there for when multiple stops take different items from the same +stockpile, or when a stop only wants a subset of items from a stockpile. + +Here is a common setup for a quantum stone stockpile:: + + #place + s{name="Stone quantum" quantum=true} ~ s5{name="Stone feeder"}(3x3) + #build + ~ trackstopW{take_from="Stone feeder" route="Stone dumper"} + +This sets up the quantum stockpile and the feeder stockpile in the ``#place`` +blueprint, followed by the trackstop and the hauling route configuration in the +``#build`` blueprint. The ``route`` property is the name of the hauling route +to create (or attach to if it already exists). If you are applying a quantum +stockpile blueprint more than once in a fort, be sure to *avoid* defining the +``route`` property so that each application of the blueprint creates a unique +hauling route. Two quantum stockpiles on the same route will not function +propertly. + +Let's look at a slightly more complicated setup where we sort the stone into +different output quantum stockpiles:: + + #place + s{name="Other stone quantum" quantum=true} ~ s5e{name="Rock feeder"}(3x3) + s{name="Ore/clay stone quantum" quantum=true} ~ + s{name="Gem quantum" quantum=true} ~ + #build + ~ trackstopW{take_from="Rock feeder" route="Other stone"}:=otherstone + ~ trackstopW{take_from="Rock feeder" route="Ore/clay"}:=cat_stone-otherstone + ~ trackstopW{take_from="Rock feeder" route="Gems"}:=cat_gems + +You can see how we make use of the stockpile-style configuration syntax to +fine-tune the items desired by the hauling route stop. + +Finally, let's make a series of stops on a common hauling route. There is +nothing particularly special about this example. If the ``route`` property +names an existing route, the stop will be added to that route:: + + #dig + trackE trackEW trackEW trackW + #build + trackstop{route="Tick tock"} ~ ~ trackstop{route="Tick tock"} + +These two track stops (which do not dump their contents) simply exist on a +common route at the ends of a connected carved track. + .. _quickfort-modeline: Modeline markers -~~~~~~~~~~~~~~~~ +---------------- The modeline has some additional optional components that we haven't talked about yet. You can: @@ -774,7 +811,7 @@ about yet. You can: The full modeline syntax, when all optional elements are specified, is:: - #mode label(mylabel) start(X;Y;STARTCOMMENT) hidden() message(mymessage) comment + #mode label(mylabel) start(X;Y;startcomment) hidden() message(mymessage) comment Note that all elements are optional except for the initial ``#mode`` (though, as mentioned in the first section, if a modeline doesn't appear at all in the first @@ -783,16 +820,15 @@ no optional markers). Here are a few examples of modelines with optional elements before we discuss them in more detail:: #dig start(3; 3; Center tile of a 5-tile square) Regular blueprint comment - #build label(noblebedroom) start(10;15) - #query label(configstockpiles) No explicit 'start()' means cursor is at upper left corner + #build label(noblebedroom) No explicit 'start()' so cursor is in upper left #meta label(digwholefort) start(center of stairs on surface) - #dig label(digdining) hidden() called by the digwholefort meta blueprint + #dig label(dig_dining) hidden() called by the digwholefort meta blueprint #zone label(pastures) message(remember to assign animals to the new pastures) .. _quickfort-label: Blueprint labels -```````````````` +~~~~~~~~~~~~~~~~ Labels are displayed in the ``quickfort list`` output and are used for addressing specific blueprints when there are multiple blueprints in a single @@ -807,7 +843,7 @@ user-defined labels. .. _quickfort-start: Start positions -``````````````` +~~~~~~~~~~~~~~~ Start positions specify a cursor offset for a particular blueprint, simplifying the task of blueprint alignment. This is very helpful for blueprints that are @@ -842,7 +878,7 @@ You can use semicolons, commas, or spaces to separate the elements of the .. _quickfort-hidden: Hiding blueprints -````````````````` +~~~~~~~~~~~~~~~~~ A blueprint with a ``hidden()`` marker won't appear in ``quickfort list`` output unless the ``--hidden`` flag is specified. The primary reason for hiding a @@ -853,7 +889,7 @@ managed by a `meta blueprint `. .. _quickfort-message: Messages -```````` +~~~~~~~~ A blueprint with a ``message()`` marker will display a message after the blueprint is applied with ``quickfort run``. This is useful for reminding @@ -870,27 +906,26 @@ quotes automatically when they save/export the file. .. _quickfort-meta: -Meta blueprints -~~~~~~~~~~~~~~~ +#meta mode +---------- -Meta blueprints are blueprints that control how other blueprints are applied. -For example, meta blueprints can bundle a group of other blueprints so that they -can be run with a single command. They can also encode logic, like rotating the -blueprint or duplicating it across a specified number of z-levels. +``#meta`` blueprints are blueprints that control how other blueprints are +applied. For example, meta blueprints can bundle a group of other blueprints so +that they can be run with a single command. They can also encode logic, like +rotating the blueprint or duplicating it across a specified number of z-levels. A common scenario where meta blueprints are useful is when you have several phases to link together. For example you might: 1. Apply a dig blueprint to designate dig areas #. Wait for miners to dig +#. **Apply another dig blueprint** to designate traffic costs +#. **Apply a zone blueprint** to designate zones +#. **Apply a place buildprint** to designate and configure stockpiles #. **Apply a build buildprint** to designate buildings -#. **Apply a place buildprint** to designate stockpiles -#. **Apply a query blueprint** to configure stockpiles -#. Wait for buildings to get built -#. Apply a different query blueprint to configure rooms -Those three "apply"s in the middle might as well get done in one command instead -of three. A ``#meta`` blueprint can help with that. A meta blueprint refers to +Those last four "apply"s might as well get done in one command instead of four. +A ``#meta`` blueprint can help with that. A meta blueprint refers to other blueprints in the same file by their label (see the `Modeline markers`_ section above) in the same format used by the `quickfort` command: ``/