diff --git a/CMake/DownloadFile.cmake b/CMake/DownloadFile.cmake index 8f9938f90..fe9e4e05e 100644 --- a/CMake/DownloadFile.cmake +++ b/CMake/DownloadFile.cmake @@ -9,10 +9,31 @@ function(file_md5_if_exists FILE VAR) endif() endfunction() +function(search_downloads FILE_MD5 VAR) + set(${VAR} "" PARENT_SCOPE) + file(GLOB FILES ${CMAKE_SOURCE_DIR}/CMake/downloads/*) + foreach(FILE ${FILES}) + file(MD5 "${FILE}" CUR_MD5) + if("${CUR_MD5}" STREQUAL "${FILE_MD5}") + set(${VAR} ${FILE} PARENT_SCOPE) + return() + endif() + endforeach() +endfunction() + function(download_file URL DEST EXPECTED_MD5) get_filename_component(FILENAME "${URL}" NAME) file_md5_if_exists("${DEST}" CUR_MD5) + search_downloads(${EXPECTED_MD5} DLPATH) + if(NOT("${DLPATH}" STREQUAL "")) + message("* Copying ${FILENAME} from ${DLPATH}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E copy + "${DLPATH}" + "${DEST}") + return() + endif() + if(NOT "${EXPECTED_MD5}" STREQUAL "${CUR_MD5}") message("* Downloading ${FILENAME}") file(DOWNLOAD "${URL}" "${DEST}" EXPECTED_MD5 "${EXPECTED_MD5}" SHOW_PROGRESS) diff --git a/CMake/downloads/.gitignore b/CMake/downloads/.gitignore new file mode 100644 index 000000000..ff7b252fe --- /dev/null +++ b/CMake/downloads/.gitignore @@ -0,0 +1,3 @@ +* +!README.txt +!.gitignore diff --git a/CMake/downloads/README.txt b/CMake/downloads/README.txt new file mode 100644 index 000000000..40b504725 --- /dev/null +++ b/CMake/downloads/README.txt @@ -0,0 +1,3 @@ +This folder exists as an alternate location for downloaded files. Files will +ordinarily not be downloaded here, but CMake will look for them here anyway to +facilitate offline builds. diff --git a/CMakeLists.txt b/CMakeLists.txt index 3602655b4..c7c0835a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,7 +137,7 @@ endif() # set up versioning. set(DF_VERSION "0.43.05") -SET(DFHACK_RELEASE "alpha4") +SET(DFHACK_RELEASE "beta1") SET(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/NEWS.rst b/NEWS.rst index 2fdbfc561..4229da17f 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -64,6 +64,7 @@ Ruby New Plugins ----------- - `dwarfvet` enables animal caretaking +- `generated-creature-renamer`: Renames generated creature IDs for use with graphics packs - `labormanager` (formerly autolabor2): a more advanced alternative to `autolabor` - `misery`: re-added and updated for the 0.4x series - `title-folder`: shows DF folder name in window title bar when enabled @@ -80,6 +81,7 @@ Fixes ----- - The DF path on OS X can now contain spaces and ``:`` characters - Buildings::setOwner() changes now persist properly when saved +- ``ls`` now lists scripts in folders other than ``hack/scripts``, when applicable - `add-thought`: fixed support for emotion names - `autofarm`: Made surface farms detect local biome - `devel/find-offsets`: fixed a crash when vtables used by globals aren't available @@ -88,6 +90,10 @@ Fixes - Fixed crash when selecting a profession from an empty list - Custom professions are now sorted alphabetically more reliably +- `modtools/create-unit`: stopped permanently overwriting the creature creation + menu in arena mode +- `title-version`: now hidden when loading an arena + Misc Improvements ----------------- - Documented all default keybindings (from :file:`dfhack.init-example`) in the @@ -101,6 +107,8 @@ Misc Improvements - site towers, world buildings - surface material +- `title-version`: Added a prerelease indicator + DFHack 0.43.03-r1 ================= diff --git a/build/win32/generate-MSVC-all-breakfast.bat b/build/win32/generate-MSVC-all-breakfast.bat deleted file mode 100644 index ac26c4f75..000000000 --- a/build/win32/generate-MSVC-all-breakfast.bat +++ /dev/null @@ -1,9 +0,0 @@ -@echo off -IF EXIST DF_PATH.txt SET /P _DF_PATH=`_. - -You should also install the Visual Studio 2010 SP1 update. - -You can confirm whether you have SP1 by opening the Visual Studio 2010 IDE -and selecting About from the Help menu. If you have SP1 it will have *SP1Rel* -at the end of the version number, for example: *Version 10.0.40219.1 SP1Rel* - -Use of pre-SP1 releases has been reported to cause issues and is therefore not -supported by DFHack. Please ensure you are using SP1 before raising any Issues. +* Python (for documentation; optional, except for release builds) -If your Windows Update is configured to receive updates for all Microsoft -Products, not just Windows, you will receive the SP1 update automatically -through Windows Update (you will probably need to trigger a manual check.) +Microsoft Visual Studio 2015 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DFHack has to be compiled with the Microsoft Visual C++ 2015 toolchain; other +versions won't work against Dwarf Fortress due to ABI and STL incompatibilities. -If not, you can download it directly `from this Microsoft Download link `_. +At present, the only way to obtain the MSVC C++ 2015 toolchain is to install a +full copy of Microsoft Visual Studio 2015. The free Community version is +sufficient. Additional dependencies: installing with the Chocolatey Package Manager ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The remainder of dependencies - Git, CMake and StrawberryPerl - can be most -easily installed using the Chocolatey Package Manger. Chocolatey is a + +The remainder of dependencies - Git, CMake, StrawberryPerl, and Python - can be +most easily installed using the Chocolatey Package Manger. 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 preferred way of installing the required dependencies -as it's quicker, less effort and will install known-good utilities +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: @@ -482,8 +467,10 @@ install XML::LibXML and XML::LibXSLT for it using CPAN. Build ----- -There are several different batch files in the ``build`` folder along -with a script that's used for picking the DF path. +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 +to build for. First, run ``set_df_path.vbs`` and point the dialog that pops up at a suitable DF installation which is of the appropriate version for the DFHack @@ -501,6 +488,9 @@ solution file(s): in, then hit configure, then generate. More options can appear after the configure step. * ``minimal`` will create a minimal solution with just the bare necessities - the main library and standard plugins. +* ``release`` will create a solution with everything that should be included in + 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: @@ -548,9 +538,10 @@ files as detailed above. Building/installing from the Visual Studio IDE: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -After running the CMake generate script you will have a new folder called VC2010. -Open the file ``dfhack.sln`` inside that folder. If you have multiple versions of -Visual Studio installed, make sure you open with Visual Studio 2010. +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. @@ -674,3 +665,52 @@ Chocolatey as outlined in the `Windows section `:: Then close that Admin ``cmd.exe``, re-open another Admin ``cmd.exe``, and run:: pip install sphinx + +Misc. Notes +=========== + +.. _note-offline-builds: + +Note on 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. + +First, you must locate the files you will need. These can be found in the +`dfhack-bin repo `_. Look for the +most recent version number *before or equal to* the DF version which you are +building for. For example, suppose "0.43.05" and "0.43.07" are listed. You should +choose "0.43.05" if you are building for 0.43.05 or 0.43.06, and "0.43.07" if +you are building for 0.43.07 or 0.43.08. + +Then, download all of the files you need, and save them to ``/CMake/downloads/``. The destination filename you choose +does not matter, as long as the files end up in the ``CMake/downloads`` folder. +You need to download all of the files for the architecture(s) you are building +for. For example, if you are building for 32-bit Linux and 64-bit Windows, +download all files starting with ``linux32`` and ``win64``. GitHub should sort +files alphabetically, so all the files you need should be next to each other. + +It is recommended that you create a build folder and run CMake to verify that +you have downloaded everything at this point, assuming your download machine has +CMake installed. This involves running a "generate" batch script on Windows, or +a command starting with ``cmake ..`` on Linux and OS X. CMake should +automatically locate files that you placed in ``CMake/downloads``, and use them +instead of attempting to download them. + +.. _note-old-git-and-dfhack: + +Note on using very old git versions with pre-0.43.03 DFHack versions +-------------------------------------------------------------------- + +If you are using git 1.8.0 or older, and cloned DFHack before commit 85a920d +(around DFHack v0.43.03-alpha1), you may run into fatal git errors when updating +submodules after switching branches. This is due to those versions of git being +unable to handle our change from "scripts/3rdparty/name" submodules to a single +"scripts" submodule. This may be fixable by renaming .git/modules/scripts to +something else and re-running ``git submodule update --init`` on the branch with +the single scripts submodule (and running it again when switching back to the +one with multiple submodules, if necessary), but it is usually much simpler to +upgrade your git version. diff --git a/docs/NEWS-dev.rst b/docs/NEWS-dev.rst index 9920e3433..df74735c0 100644 --- a/docs/NEWS-dev.rst +++ b/docs/NEWS-dev.rst @@ -37,6 +37,38 @@ Development Changelog .. contents:: :depth: 2 +DFHack 0.43.05-beta1 +==================== + +Fixes +----- +- Fixed various crashes on 64-bit Windows related to DFHack screens, notably `manipulator` +- Fixed addresses of next_id globals on 64-bit Linux (fixes an `automaterial`/box-select crash) +- ``ls`` now lists scripts in folders other than ``hack/scripts``, when applicable +- `modtools/create-unit`: stopped permanently overwriting the creature creation + menu in arena mode +- `season-palette`: fixed an issue where only part of the screen was redrawn + after changing the color scheme +- `title-version`: now hidden when loading an arena + +Structures +---------- +- ``file_compressorst``: fixed field sizes on x64 +- ``historical_entity``: fixed alignment on x64 +- ``ui_sidebar_menus.command_line``: fixed field sizes on x64 +- ``viewscreen_choose_start_sitest``: added 3 missing fields, renamed ``in_embark_only_warning`` +- ``viewscreen_layer_arena_creaturest``: identified more fields +- ``world.math``: identified +- ``world.murky_pools``: identified + +Additions/Removals +------------------ +- `generated-creature-renamer`: Renames generated creature IDs for use with graphics packs + +Other Changes +------------- +- `title-version`: Added a prerelease indicator + DFHack 0.43.05-alpha4 ===================== diff --git a/docs/Plugins.rst b/docs/Plugins.rst index b3e68459d..e2b829020 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -2453,6 +2453,24 @@ armor onto a war animal or to add unusual items (such as crowns) to any unit. For more information run ``forceequip help``. See also `modtools/equip-item`. +.. _generated-creature-renamer: + +generated-creature-renamer +========================== +Automatically renames generated creatures, such as forgotten beasts, titans, +etc, to have raw token names that match the description given in-game. + +The ``list-generated`` command can be used to list the token names of all +generated creatures in a given save, with an optional ``detailed`` argument +to show the accompanying description. + +The ``save-generated-raws`` command will save a sample creature graphics file in +the Dwarf Fortress root directory, to use as a start for making a graphics set +for generated creatures using the new names that they get with this plugin. + +The new names are saved with the save, and the plugin, when enabled, only runs once +per save, unless there's an update. + .. _lair: lair @@ -2522,7 +2540,7 @@ Options: :-unit: Make the strange mood strike the selected unit instead of picking one randomly. Unit eligibility is still enforced. :-type : Force the mood to be of a particular type instead of choosing randomly based on happiness. - Valid values for Tare "fey", "secretive", "possessed", "fell", and "macabre". + Valid values for T are "fey", "secretive", "possessed", "fell", and "macabre". :-skill S: 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", "gemcutter", "gemsetter", diff --git a/library/Core.cpp b/library/Core.cpp index 6afe907aa..198e49df4 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -279,19 +279,24 @@ static void listScripts(PluginManager *plug_mgr, std::map &pset, std::vector files; Filesystem::listdir(path, files); + path += '/'; for (size_t i = 0; i < files.size(); i++) { if (hasEnding(files[i], ".lua")) { - std::string help = getScriptHelp(path + files[i], "--"); - - pset[prefix + files[i].substr(0, files[i].size()-4)] = help; + string help = getScriptHelp(path + files[i], "--"); + string key = prefix + files[i].substr(0, files[i].size()-4); + if (pset.find(key) == pset.end()) { + pset[key] = help; + } } else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && hasEnding(files[i], ".rb")) { - std::string help = getScriptHelp(path + files[i], "#"); - - pset[prefix + files[i].substr(0, files[i].size()-3)] = help; + string help = getScriptHelp(path + files[i], "#"); + string key = prefix + files[i].substr(0, files[i].size()-3); + if (pset.find(key) == pset.end()) { + pset[key] = help; + } } else if (all && !files[i].empty() && files[i][0] != '.') { @@ -300,6 +305,14 @@ static void listScripts(PluginManager *plug_mgr, std::map &pset, } } +static void listAllScripts(map &pset, bool all) +{ + vector paths; + Core::getInstance().getScriptPaths(&paths); + for (string path : paths) + listScripts(Core::getInstance().getPluginManager(), pset, path, all); +} + namespace { struct ScriptArgs { const string *pcmd; @@ -418,7 +431,7 @@ static bool try_autocomplete(color_ostream &con, const std::string &first, std:: bool all = (first.find('/') != std::string::npos); std::map scripts; - listScripts(plug_mgr, scripts, Core::getInstance().getHackPath() + "scripts/", all); + listAllScripts(scripts, all); for (auto iter = scripts.begin(); iter != scripts.end(); ++iter) if (iter->first.substr(0, first.size()) == first) possible.push_back(iter->first); @@ -907,7 +920,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v con.reset_color(); } std::map scripts; - listScripts(plug_mgr, scripts, getHackPath() + "scripts/", all); + listAllScripts(scripts, all); if (!scripts.empty()) { con.print("\nscripts:\n"); @@ -918,8 +931,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v } else if (builtin == "plug") { - const char *header_format = "%25s %10s %4s %8s\n"; - const char *row_format = "%25s %10s %4i %8s\n"; + const char *header_format = "%30s %10s %4s %8s\n"; + const char *row_format = "%30s %10s %4i %8s\n"; con.print(header_format, "Name", "State", "Cmds", "Enabled"); plug_mgr->refresh(); @@ -1043,7 +1056,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v << " 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, or F1-F9, or Enter)." << 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: " diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b8a6fe53d..a2446c78a 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1386,6 +1386,16 @@ static std::string getOSType() } } +static int getArchitecture() +{ + return sizeof(void*) * 8; +} + +static std::string getArchitectureName() +{ + return getArchitecture() == 64 ? "x86_64" : "x86"; +} + static std::string getDFVersion() { return Core::getInstance().vinfo->getVersion(); } static uint32_t getTickCount() { return Core::getInstance().p->getTickCount(); } @@ -1403,6 +1413,8 @@ static std::string df2console(std::string s) { return DF2CONSOLE(s); } static const LuaWrapper::FunctionReg dfhack_module[] = { WRAP(getOSType), + WRAP(getArchitecture), + WRAP(getArchitectureName), WRAP(getDFVersion), WRAP(getDFPath), WRAP(getTickCount), @@ -2557,6 +2569,9 @@ static int internal_memscan(lua_State *L) for (int i = 0; i <= hcount; i++) { uint8_t *p = haystack + i*hstep; + if (p + nsize > haystack + (hcount * hstep)) { + break; + } if (memcmp(p, needle, nsize) == 0) { lua_pushinteger(L, i); lua_pushinteger(L, (lua_Integer)p); diff --git a/library/Process-windows.cpp b/library/Process-windows.cpp index cb2e16bd2..3a1da0ac4 100644 --- a/library/Process-windows.cpp +++ b/library/Process-windows.cpp @@ -342,12 +342,14 @@ int Process::adjustOffset(int offset, bool to_file) return -1; } - string Process::doReadClassName (void * vptr) { char * rtti = readPtr((char *)vptr - sizeof(void*)); #ifdef DFHACK64 - char * typeinfo = d->base + readDWord(rtti + 0xC); + void *base; + if (!RtlPcToFileHeader(rtti, &base)) + return "dummy"; + char * typeinfo = (char *)base + readDWord(rtti + 0xC); string raw = readCString(typeinfo + 0x10+4); // skips the .?AV #else char * typeinfo = readPtr(rtti + 0xC); diff --git a/library/include/df/custom/viewscreen.methods.inc b/library/include/df/custom/viewscreen.methods.inc index c5d277716..b1f07242e 100644 --- a/library/include/df/custom/viewscreen.methods.inc +++ b/library/include/df/custom/viewscreen.methods.inc @@ -1 +1,6 @@ friend struct df::interfacest; +void feed_key(df::interface_key key) { + std::set input; + input.insert(key); + feed(&input); +} diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 603c7ab44..b83691acf 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -354,6 +354,10 @@ function Painter:key(code,pen,...) ) end +function Painter:key_string(code, text, ...) + return self:key(code):string(': '):string(text, ...) +end + -------------------------- -- Abstract view object -- -------------------------- diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 9d672cd58..55f0869a4 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1182,52 +1182,52 @@ void Buildings::clearBuildings(color_ostream& out) { void Buildings::updateBuildings(color_ostream& out, void* ptr) { - // int32_t id = (int32_t)ptr; - // auto building = df::building::find(id); - - // if (building) - // { - // // Already cached -> weird, so bail out - // if (corner1.count(id)) - // return; - // // Civzones cannot be cached because they can - // // overlap each other and normal buildings. - // if (!building->isSettingOccupancy()) - // return; - - // 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); - - // 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 (containsTile(building, pt, false)) - // locationToBuilding[pt] = id; - // } - // } - // } - // else if (corner1.count(id)) - // { - // //existing building: destroy it - // df::coord p1 = corner1[id]; - // df::coord p2 = corner2[id]; - - // 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,p1.z); - - // auto cur = locationToBuilding.find(pt); - // if (cur != locationToBuilding.end() && cur->second == id) - // locationToBuilding.erase(cur); - // } - // } - - // corner1.erase(id); - // corner2.erase(id); - // } + int32_t id = *((int32_t*)ptr); + auto building = df::building::find(id); + + if (building) + { + // Already cached -> weird, so bail out + if (corner1.count(id)) + return; + // Civzones cannot be cached because they can + // overlap each other and normal buildings. + if (!building->isSettingOccupancy()) + return; + + 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); + + 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 (containsTile(building, pt, false)) + locationToBuilding[pt] = id; + } + } + } + else if (corner1.count(id)) + { + //existing building: destroy it + df::coord p1 = corner1[id]; + df::coord p2 = corner2[id]; + + 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,p1.z); + + auto cur = locationToBuilding.find(pt); + if (cur != locationToBuilding.end() && cur->second == id) + locationToBuilding.erase(cur); + } + } + + corner1.erase(id); + corner2.erase(id); + } } void Buildings::getStockpileContents(df::building_stockpilest *stockpile, std::vector *items) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index dfb978022..e2baf9bfb 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -273,7 +273,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } for ( size_t a = 0; a < df::global::world->buildings.all.size(); a++ ) { df::building* b = df::global::world->buildings.all[a]; - Buildings::updateBuildings(out, (void*)b); + Buildings::updateBuildings(out, (void*)&(b->id)); buildings.insert(b->id); } lastSyndromeTime = -1; @@ -609,7 +609,7 @@ static void manageBuildingEvent(color_ostream& out) { buildings.insert(a); for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler bob = (*b).second; - bob.eventHandler(out, (void*)intptr_t(a)); + bob.eventHandler(out, (void*)&a); } } nextBuilding = *df::global::building_next_id; @@ -625,7 +625,7 @@ static void manageBuildingEvent(color_ostream& out) { for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler bob = (*b).second; - bob.eventHandler(out, (void*)intptr_t(id)); + bob.eventHandler(out, (void*)&id); } a = buildings.erase(a); } diff --git a/library/xml b/library/xml index 34331ef67..a292304c6 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 34331ef67f3d163f4ab66121449a8217eb1cfb47 +Subproject commit a292304c6097a499dfa749d5cc8ab24e69e004f5 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4611009c3..f7586f03d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -122,6 +122,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(follow follow.cpp) DFHACK_PLUGIN(forceequip forceequip.cpp) DFHACK_PLUGIN(fortplan fortplan.cpp LINK_LIBRARIES buildingplan-lib) + DFHACK_PLUGIN(generated-creature-renamer generated-creature-renamer.cpp) DFHACK_PLUGIN(getplants getplants.cpp) DFHACK_PLUGIN(hotkeys hotkeys.cpp) DFHACK_PLUGIN(infiniteSky infiniteSky.cpp) diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index d2bdf4909..274f8cad7 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -128,7 +128,7 @@ public: if (input->count(df::interface_key::SETUP_EMBARK)) { cancel = true; - screen->in_embark_normal = 1; + screen->in_embark_only_warning = 1; } }; }; diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp index 14f1ec26e..365542314 100644 --- a/plugins/eventful.cpp +++ b/plugins/eventful.cpp @@ -183,8 +183,8 @@ void ev_mng_invasion(color_ostream& out, void* ptr) } static void ev_mng_building(color_ostream& out, void* ptr) { - int32_t myId=*(int32_t*)&ptr; - onBuildingCreatedDestroyed(out,myId); + int32_t id = *((int32_t*)ptr); + onBuildingCreatedDestroyed(out, id); } static void ev_mng_inventory(color_ostream& out, void* ptr) { @@ -289,7 +289,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(furnace_hook, fillSidebarMenu); struct product_hook : item_product { typedef item_product interpose_base; - + DEFINE_VMETHOD_INTERPOSE( void, produce, (df::unit *unit, diff --git a/plugins/generated-creature-renamer.cpp b/plugins/generated-creature-renamer.cpp new file mode 100644 index 000000000..49444eaf4 --- /dev/null +++ b/plugins/generated-creature-renamer.cpp @@ -0,0 +1,274 @@ +#include "Console.h" +#include "Core.h" +#include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" +#include "df/world.h" +#include "df/world_raws.h" +#include "df/creature_raw.h" +#include "df/caste_raw.h" +#include "modules/World.h" +#include "MemAccess.h" + +//#include "df/world.h" + +using namespace DFHack; + +DFHACK_PLUGIN("generated-creature-renamer"); +REQUIRE_GLOBAL(world); + +#define RENAMER_VERSION 3 + +command_result list_creatures(color_ostream &out, std::vector & parameters); +command_result save_generated_raw(color_ostream &out, std::vector & parameters); + +DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "list-generated", + "Prints a list of generated creature tokens.", + list_creatures, + false, //allow non-interactive use + "Use \"list-generated detailed\" to show descriptions." + )); + commands.push_back(PluginCommand( + "save-generated-raws", + "Saves a graphics raw file to use with the renamed generated creatures.", + save_generated_raw, + false, //allow non-interactive use + "Creates graphics_procedural_creatures.txt, with a full set of creature graphics definitions for all possible generated beasts. Modify the resulting file to suit your needs." + )); return CR_OK; +} + +DFhackCExport command_result plugin_shutdown(color_ostream &out) +{ + return CR_OK; +} + +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + is_enabled = enable; + return CR_OK; +} + +std::vector descriptors = { + "blob", "quadruped", "humanoid", "silverfish", "mayfly", "dragonfly", "damselfly", "stonefly", + "earwig", "grasshopper", "cricket", "stick insect", "cockroach", "termite", "mantis", "louse", + "thrips", "aphid", "cicada", "assassin bug", "wasp", "hornet", "tiger beetle", "ladybug", + "weevil", "darkling beetle", "click beetle", "firefly", "scarab beetle", "stag beetle", "dung beetle", "rhinoceros beetle", + "rove beetle", "snakefly", "lacewing", "antlion larva", "mosquito", "flea", "scorpionfly", "caddisfly", + "butterfly", "moth", "caterpillar", "maggot", "spider", "tarantula", "scorpion", "tick", + "mite", "shrimp", "lobster", "crab", "nematode", "snail", "slug", "earthworm", + "leech", "bristleworm", "ribbon worm", "flat worm", "toad", "frog", "salamander", "newt", + "alligator", "crocodile", "lizard", "chameleon", "iguana", "gecko", "skink", "gila monster", + "monitor", "serpent", "viper", "rattlesnake", "cobra", "python", "anaconda", "turtle", + "tortoise", "pterosaur", "dimetrodon", "sauropod", "theropod", "iguanodont", "hadrosaurid", "stegosaurid", + "ceratopsid", "ankylosaurid", "duck", "goose", "swan", "turkey", "grouse", "chicken", + "quail", "pheasant", "gull", "loon", "grebe", "albatross", "petrel", "penguin", + "pelican", "stork", "vulture", "flamingo", "falcon", "kestrel", "condor", "osprey", + "buzzard", "eagle", "harrier", "kite", "crane", "dove", "pigeon", "parrot", + "cockatoo", "cuckoo", "nightjar", "swift", "hummingbird", "kingfisher", "hornbill", "quetzal", + "toucan", "woodpecker", "lyrebird", "thornbill", "honeyeater", "oriole", "fantail", "shrike", + "crow", "raven", "magpie", "kinglet", "lark", "swallow", "martin", "bushtit", + "warbler", "thrush", "oxpecker", "starling", "mockingbird", "wren", "nuthatch", "sparrow", + "tanager", "cardinal", "bunting", "finch", "titmouse", "chickadee", "waxwing", "flycatcher", + "opossum", "koala", "wombat", "kangaroo", "sloth", "anteater", "armadillo", "squirrel", + "marmot", "beaver", "gopher", "mouse", "porcupine", "chinchilla", "cavy", "capybara", + "rabbit", "hare", "lemur", "loris", "monkey", "hedgehog", "shrew", "mole", + "fruit bat", "wolf", "coyote", "jackal", "raccoon", "coati", "weasel", "otter", + "badger", "skunk", "bear", "panda", "panther", "mongoose", "hyena", "civet", + "walrus", "pangolin", "elephant", "mammoth", "horse", "zebra", "tapir", "rhinoceros", + "warthog", "hippopotamus", "camel", "llama", "giraffe", "deer", "moose", "antelope", + "sheep", "goat", "bison", "buffalo", "bull", "ape", "ant", "bat", + "owl", "pig", "bee", "fly", "hawk", "jay", "rat", "fox", + "cat", "ass", "elk" +}; + +std::vector prefixes = { + "FORGOTTEN_BEAST_", + "TITAN_", + "DEMON_", + "NIGHT_CREATURE_", + "HF" +}; + + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + if (!is_enabled) + return CR_OK; + + if (event != DFHack::SC_WORLD_LOADED) + return CR_OK; + + CoreSuspender suspend; + + std::vector descriptorCount = std::vector(descriptors.size()); + + auto version = World::GetPersistentData("AlreadyRenamedCreatures"); + if (version.isValid() && version.ival(1) >= RENAMER_VERSION) + { + return CR_OK; + } + + int creatureCount = 0; + + for (int i = 0; i < world->raws.creatures.all.size(); i++) + { + auto creatureRaw = world->raws.creatures.all[i]; + if (!creatureRaw->flags.is_set(df::enums::creature_raw_flags::GENERATED)) + continue; + size_t minPos = std::string::npos; + size_t foundIndex = -1; + size_t prefixIndex = -1; + + for (size_t j = 0; j < prefixes.size(); j++) + { + if (creatureRaw->creature_id.compare(0, prefixes[j].length(), prefixes[j]) == 0) + { + prefixIndex = j; + } + } + + if (prefixIndex < 0) + continue; //unrecognized generaed type. + + for (size_t j = 0; j < descriptors.size(); j++) + { + size_t pos = creatureRaw->caste[0]->description.find(" " + descriptors[j]); + if (pos < minPos) + { + minPos = pos; + foundIndex = j; + } + } + + if (foundIndex < 0) + continue; //can't find a match. + + auto descriptor = descriptors[foundIndex]; + + for (int j = 0; j < descriptor.size(); j++) + { + if (descriptor[j] == ' ') + descriptor[j] = '_'; + else + descriptor[j] = toupper(descriptor[j]); + } + + auto prefix = prefixes[prefixIndex]; + if (prefix[prefix.length() - 1] != '_') + prefix.append("_"); + + creatureRaw->creature_id = prefixes[prefixIndex] + descriptor; + + if (descriptorCount[foundIndex] > 0) + { + creatureRaw->creature_id.append("_" + std::to_string(descriptorCount[foundIndex])); + } + descriptorCount[foundIndex]++; + creatureCount++; + } + + version = World::AddPersistentData("AlreadyRenamedCreatures"); + version.ival(1) = RENAMER_VERSION; + + out << "Renamed " << creatureCount << " generated creatures to have sensible names." << endl; + + + return CR_OK; +} + +command_result list_creatures(color_ostream &out, std::vector & parameters) +{ + bool detailed = false; + if (!parameters.empty()) + { + if (parameters.size() > 1) + return CR_WRONG_USAGE; + if(parameters[0].compare("detailed") != 0) + return CR_WRONG_USAGE; + detailed = true; + } + + CoreSuspender suspend; + for (int i = 0; i < world->raws.creatures.all.size(); i++) + { + auto creatureRaw = world->raws.creatures.all[i]; + if (!creatureRaw->flags.is_set(df::enums::creature_raw_flags::GENERATED)) + continue; + out.print(creatureRaw->creature_id.c_str()); + if (detailed) + { + out.print("\t"); + out.print(creatureRaw->caste[0]->description.c_str()); + } + out.print("\n"); + } + return CR_OK; +} + +command_result save_generated_raw(color_ostream &out, std::vector & parameters) +{ +#ifdef LINUX_BUILD + std::string pathSep = "/"; +#else + std::string pathSep = "\\"; +#endif + int pageWidth = 16; + int pageHeight = (descriptors.size() / pageWidth) + ((descriptors.size() % pageWidth > 0) ? 1 : 0); + int tileWidth = 24; + int tileHeight = 24; + std::string fileName = "graphics_procedural_creatures"; + std::string pageName = "PROCEDURAL_FRIENDLY"; + int repeats = 128; + + std::ofstream outputFile(fileName + ".txt", std::ios::out | std::ios::trunc); + + outputFile << fileName << endl << endl; + + outputFile << "[OBJECT:GRAPHICS]" << endl << endl; + + outputFile << "[TILE_PAGE:" << pageName << "]" << endl; + outputFile << " [FILE:procedural_friendly.png]" << endl; + outputFile << " [TILE_DIM:" << tileWidth << ":" << tileHeight << "]" << endl; + outputFile << " [PAGE_DIM:" << pageWidth << ":" << pageHeight << "]" << endl << endl; + + for (size_t descIndex = 0; descIndex < descriptors.size(); descIndex++) + { + for (size_t prefIndex = 0; prefIndex < prefixes.size(); prefIndex++) + { + for (size_t rep = 0; rep < repeats; rep++) + { + auto descriptor = descriptors[descIndex]; + + for (int j = 0; j < descriptor.size(); j++) + { + if (descriptor[j] == ' ') + descriptor[j] = '_'; + else + descriptor[j] = toupper(descriptor[j]); + } + + auto prefix = prefixes[prefIndex]; + if (prefix[prefix.length() - 1] != '_') + prefix.append("_"); + + std::string token = prefix + descriptor; + if (rep > 0) + token.append("_" + std::to_string(rep)); + + outputFile << "[CREATURE_GRAPHICS:" << token << "]" << endl; + outputFile << " [DEFAULT:" << pageName << ":" << descIndex % pageWidth << ":" << descIndex / pageWidth << ":ADD_COLOR]" << endl; + } + outputFile << endl; + } + outputFile << endl; + } + + outputFile.close(); + + return CR_OK; +} diff --git a/plugins/proto/RemoteFortressReader.proto b/plugins/proto/RemoteFortressReader.proto index fdfc25d99..af25c9a4b 100644 --- a/plugins/proto/RemoteFortressReader.proto +++ b/plugins/proto/RemoteFortressReader.proto @@ -322,8 +322,8 @@ message UnitDefinition optional int32 blood_max = 14; optional int32 blood_count = 15; optional UnitAppearance appearance = 16; - optional int32 profession_id = 17; - repeated string noble_positions = 18; + optional int32 profession_id = 17; + repeated string noble_positions = 18; } message UnitList @@ -756,4 +756,4 @@ message DigCommand message SingleBool { optional bool Value = 1; -} \ No newline at end of file +} diff --git a/scripts b/scripts index 20eb2146f..7e8daa168 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 20eb2146fb93912b07034f3192dde1cf95a9ec7f +Subproject commit 7e8daa16822cc67f8e84adab7607694b622fe434